diff options
Diffstat (limited to 'drivers')
632 files changed, 24472 insertions, 10859 deletions
diff --git a/drivers/acpi/apei/apei-base.c b/drivers/acpi/apei/apei-base.c index 00a783661d0b..46f80e2c92f7 100644 --- a/drivers/acpi/apei/apei-base.c +++ b/drivers/acpi/apei/apei-base.c @@ -590,6 +590,9 @@ static int apei_check_gar(struct acpi_generic_address *reg, u64 *paddr, if (bit_width == 32 && bit_offset == 0 && (*paddr & 0x03) == 0 && *access_bit_width < 32) *access_bit_width = 32; + else if (bit_width == 64 && bit_offset == 0 && (*paddr & 0x07) == 0 && + *access_bit_width < 64) + *access_bit_width = 64; if ((bit_width + bit_offset) > *access_bit_width) { pr_warning(FW_BUG APEI_PFX diff --git a/drivers/acpi/glue.c b/drivers/acpi/glue.c index 95af6f674a6c..35da18113216 100644 --- a/drivers/acpi/glue.c +++ b/drivers/acpi/glue.c @@ -297,7 +297,7 @@ static int acpi_platform_notify(struct device *dev) if (!ret) { struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL }; - acpi_get_name(dev->acpi_handle, ACPI_FULL_PATHNAME, &buffer); + acpi_get_name(ACPI_HANDLE(dev), ACPI_FULL_PATHNAME, &buffer); DBG("Device %s -> %s\n", dev_name(dev), (char *)buffer.pointer); kfree(buffer.pointer); } else diff --git a/drivers/acpi/osl.c b/drivers/acpi/osl.c index 3ff267861541..bd22f8667eed 100644 --- a/drivers/acpi/osl.c +++ b/drivers/acpi/osl.c @@ -250,7 +250,7 @@ acpi_physical_address __init acpi_os_get_root_pointer(void) return acpi_rsdp; #endif - if (efi_enabled) { + if (efi_enabled(EFI_CONFIG_TABLES)) { if (efi.acpi20 != EFI_INVALID_TABLE_ADDR) return efi.acpi20; else if (efi.acpi != EFI_INVALID_TABLE_ADDR) diff --git a/drivers/acpi/processor_idle.c b/drivers/acpi/processor_idle.c index f1a5da44591d..ed9a1cc690be 100644 --- a/drivers/acpi/processor_idle.c +++ b/drivers/acpi/processor_idle.c @@ -958,6 +958,9 @@ static int acpi_processor_setup_cpuidle_cx(struct acpi_processor *pr) return -EINVAL; } + if (!dev) + return -EINVAL; + dev->cpu = pr->id; if (max_cstate == 0) @@ -1149,6 +1152,7 @@ int acpi_processor_cst_has_changed(struct acpi_processor *pr) } /* Populate Updated C-state information */ + acpi_processor_get_power_info(pr); acpi_processor_setup_cpuidle_states(pr); /* Enable all cpuidle devices */ diff --git a/drivers/acpi/processor_perflib.c b/drivers/acpi/processor_perflib.c index 836bfe069042..53e7ac9403a7 100644 --- a/drivers/acpi/processor_perflib.c +++ b/drivers/acpi/processor_perflib.c @@ -340,6 +340,13 @@ static void amd_fixup_frequency(struct acpi_processor_px *px, int i) if ((boot_cpu_data.x86 == 0x10 && boot_cpu_data.x86_model < 10) || boot_cpu_data.x86 == 0x11) { rdmsr(MSR_AMD_PSTATE_DEF_BASE + index, lo, hi); + /* + * MSR C001_0064+: + * Bit 63: PstateEn. Read-write. If set, the P-state is valid. + */ + if (!(hi & BIT(31))) + return; + fid = lo & 0x3f; did = (lo >> 6) & 7; if (boot_cpu_data.x86 == 0x10) diff --git a/drivers/ata/ahci.c b/drivers/ata/ahci.c index 7862d17976b7..497912732566 100644 --- a/drivers/ata/ahci.c +++ b/drivers/ata/ahci.c @@ -53,6 +53,7 @@ enum { AHCI_PCI_BAR_STA2X11 = 0, + AHCI_PCI_BAR_ENMOTUS = 2, AHCI_PCI_BAR_STANDARD = 5, }; @@ -410,6 +411,9 @@ static const struct pci_device_id ahci_pci_tbl[] = { { PCI_VDEVICE(ASMEDIA, 0x0611), board_ahci }, /* ASM1061 */ { PCI_VDEVICE(ASMEDIA, 0x0612), board_ahci }, /* ASM1062 */ + /* Enmotus */ + { PCI_DEVICE(0x1c44, 0x8000), board_ahci }, + /* Generic, PCI class code for AHCI */ { PCI_ANY_ID, PCI_ANY_ID, PCI_ANY_ID, PCI_ANY_ID, PCI_CLASS_STORAGE_SATA_AHCI, 0xffffff, board_ahci }, @@ -1098,9 +1102,11 @@ static int ahci_init_one(struct pci_dev *pdev, const struct pci_device_id *ent) dev_info(&pdev->dev, "PDC42819 can only drive SATA devices with this driver\n"); - /* The Connext uses non-standard BAR */ + /* Both Connext and Enmotus devices use non-standard BARs */ if (pdev->vendor == PCI_VENDOR_ID_STMICRO && pdev->device == 0xCC06) ahci_pci_bar = AHCI_PCI_BAR_STA2X11; + else if (pdev->vendor == 0x1c44 && pdev->device == 0x8000) + ahci_pci_bar = AHCI_PCI_BAR_ENMOTUS; /* acquire resources */ rc = pcim_enable_device(pdev); diff --git a/drivers/ata/libahci.c b/drivers/ata/libahci.c index 320712a7b9ea..6cd7805e47ca 100644 --- a/drivers/ata/libahci.c +++ b/drivers/ata/libahci.c @@ -1951,13 +1951,13 @@ static void ahci_set_aggressive_devslp(struct ata_port *ap, bool sleep) /* Use the nominal value 10 ms if the read MDAT is zero, * the nominal value of DETO is 20 ms. */ - if (dev->sata_settings[ATA_LOG_DEVSLP_VALID] & + if (dev->devslp_timing[ATA_LOG_DEVSLP_VALID] & ATA_LOG_DEVSLP_VALID_MASK) { - mdat = dev->sata_settings[ATA_LOG_DEVSLP_MDAT] & + mdat = dev->devslp_timing[ATA_LOG_DEVSLP_MDAT] & ATA_LOG_DEVSLP_MDAT_MASK; if (!mdat) mdat = 10; - deto = dev->sata_settings[ATA_LOG_DEVSLP_DETO]; + deto = dev->devslp_timing[ATA_LOG_DEVSLP_DETO]; if (!deto) deto = 20; } else { diff --git a/drivers/ata/libata-core.c b/drivers/ata/libata-core.c index 9e8b99af400d..46cd3f4c6aaa 100644 --- a/drivers/ata/libata-core.c +++ b/drivers/ata/libata-core.c @@ -2325,24 +2325,28 @@ int ata_dev_configure(struct ata_device *dev) } } - /* check and mark DevSlp capability */ - if (ata_id_has_devslp(dev->id)) - dev->flags |= ATA_DFLAG_DEVSLP; - - /* Obtain SATA Settings page from Identify Device Data Log, - * which contains DevSlp timing variables etc. - * Exclude old devices with ata_id_has_ncq() + /* Check and mark DevSlp capability. Get DevSlp timing variables + * from SATA Settings page of Identify Device Data Log. */ - if (ata_id_has_ncq(dev->id)) { + if (ata_id_has_devslp(dev->id)) { + u8 sata_setting[ATA_SECT_SIZE]; + int i, j; + + dev->flags |= ATA_DFLAG_DEVSLP; err_mask = ata_read_log_page(dev, ATA_LOG_SATA_ID_DEV_DATA, ATA_LOG_SATA_SETTINGS, - dev->sata_settings, + sata_setting, 1); if (err_mask) ata_dev_dbg(dev, "failed to get Identify Device Data, Emask 0x%x\n", err_mask); + else + for (i = 0; i < ATA_LOG_DEVSLP_SIZE; i++) { + j = ATA_LOG_DEVSLP_OFFSET + i; + dev->devslp_timing[i] = sata_setting[j]; + } } dev->cdb_len = 16; diff --git a/drivers/ata/libata-eh.c b/drivers/ata/libata-eh.c index bf039b0e97b7..bcf4437214f5 100644 --- a/drivers/ata/libata-eh.c +++ b/drivers/ata/libata-eh.c @@ -2094,7 +2094,7 @@ static unsigned int ata_eh_speed_down(struct ata_device *dev, */ static inline int ata_eh_worth_retry(struct ata_queued_cmd *qc) { - if (qc->flags & AC_ERR_MEDIA) + if (qc->err_mask & AC_ERR_MEDIA) return 0; /* don't retry media errors */ if (qc->flags & ATA_QCFLAG_IO) return 1; /* otherwise retry anything from fs stack */ diff --git a/drivers/base/cpu.c b/drivers/base/cpu.c index 63452943abd1..fb10728f6372 100644 --- a/drivers/base/cpu.c +++ b/drivers/base/cpu.c @@ -224,7 +224,7 @@ static void cpu_device_release(struct device *dev) * by the cpu device. * * Never copy this way of doing things, or you too will be made fun of - * on the linux-kerenl list, you have been warned. + * on the linux-kernel list, you have been warned. */ } diff --git a/drivers/base/firmware_class.c b/drivers/base/firmware_class.c index d81460309182..b392b353be39 100644 --- a/drivers/base/firmware_class.c +++ b/drivers/base/firmware_class.c @@ -305,7 +305,7 @@ static bool fw_read_file_contents(struct file *file, struct firmware_buf *fw_buf char *buf; size = fw_file_size(file); - if (size < 0) + if (size <= 0) return false; buf = vmalloc(size); if (!buf) diff --git a/drivers/base/regmap/regmap-debugfs.c b/drivers/base/regmap/regmap-debugfs.c index 07aad786f817..d9a6c94ce423 100644 --- a/drivers/base/regmap/regmap-debugfs.c +++ b/drivers/base/regmap/regmap-debugfs.c @@ -56,6 +56,19 @@ static const struct file_operations regmap_name_fops = { .llseek = default_llseek, }; +static void regmap_debugfs_free_dump_cache(struct regmap *map) +{ + struct regmap_debugfs_off_cache *c; + + while (!list_empty(&map->debugfs_off_cache)) { + c = list_first_entry(&map->debugfs_off_cache, + struct regmap_debugfs_off_cache, + list); + list_del(&c->list); + kfree(c); + } +} + /* * Work out where the start offset maps into register numbers, bearing * in mind that we suppress hidden registers. @@ -91,8 +104,10 @@ static unsigned int regmap_debugfs_get_dump_start(struct regmap *map, /* No cache entry? Start a new one */ if (!c) { c = kzalloc(sizeof(*c), GFP_KERNEL); - if (!c) - break; + if (!c) { + regmap_debugfs_free_dump_cache(map); + return base; + } c->min = p; c->base_reg = i; } @@ -101,14 +116,32 @@ static unsigned int regmap_debugfs_get_dump_start(struct regmap *map, } } + /* Close the last entry off if we didn't scan beyond it */ + if (c) { + c->max = p - 1; + list_add_tail(&c->list, + &map->debugfs_off_cache); + } + + /* + * This should never happen; we return above if we fail to + * allocate and we should never be in this code if there are + * no registers at all. + */ + if (list_empty(&map->debugfs_off_cache)) { + WARN_ON(list_empty(&map->debugfs_off_cache)); + return base; + } + /* Find the relevant block */ list_for_each_entry(c, &map->debugfs_off_cache, list) { - if (*pos >= c->min && *pos <= c->max) { + if (from >= c->min && from <= c->max) { *pos = c->min; return c->base_reg; } - ret = c->max; + *pos = c->min; + ret = c->base_reg; } return ret; @@ -387,16 +420,8 @@ void regmap_debugfs_init(struct regmap *map, const char *name) void regmap_debugfs_exit(struct regmap *map) { - struct regmap_debugfs_off_cache *c; - debugfs_remove_recursive(map->debugfs); - while (!list_empty(&map->debugfs_off_cache)) { - c = list_first_entry(&map->debugfs_off_cache, - struct regmap_debugfs_off_cache, - list); - list_del(&c->list); - kfree(c); - } + regmap_debugfs_free_dump_cache(map); kfree(map->debugfs_name); } diff --git a/drivers/base/regmap/regmap.c b/drivers/base/regmap/regmap.c index 42d5cb0f503f..f00b059c057a 100644 --- a/drivers/base/regmap/regmap.c +++ b/drivers/base/regmap/regmap.c @@ -1106,7 +1106,7 @@ EXPORT_SYMBOL_GPL(regmap_raw_write); * @val_count: Number of registers to write * * This function is intended to be used for writing a large block of - * data to be device either in single transfer or multiple transfer. + * data to the device either in single transfer or multiple transfer. * * A value of zero will be returned on success, a negative errno will * be returned in error cases. diff --git a/drivers/block/virtio_blk.c b/drivers/block/virtio_blk.c index 9d8409c02082..8ad21a25bc0d 100644 --- a/drivers/block/virtio_blk.c +++ b/drivers/block/virtio_blk.c @@ -889,6 +889,7 @@ static void virtblk_remove(struct virtio_device *vdev) { struct virtio_blk *vblk = vdev->priv; int index = vblk->index; + int refc; /* Prevent config work handler from accessing the device. */ mutex_lock(&vblk->config_lock); @@ -903,11 +904,15 @@ static void virtblk_remove(struct virtio_device *vdev) flush_work(&vblk->config_work); + refc = atomic_read(&disk_to_dev(vblk->disk)->kobj.kref.refcount); put_disk(vblk->disk); mempool_destroy(vblk->pool); vdev->config->del_vqs(vdev); kfree(vblk); - ida_simple_remove(&vd_index_ida, index); + + /* Only free device id if we don't have any users */ + if (refc == 1) + ida_simple_remove(&vd_index_ida, index); } #ifdef CONFIG_PM diff --git a/drivers/bluetooth/ath3k.c b/drivers/bluetooth/ath3k.c index b00000e8aef6..33c9a44a9678 100644 --- a/drivers/bluetooth/ath3k.c +++ b/drivers/bluetooth/ath3k.c @@ -77,10 +77,15 @@ static struct usb_device_id ath3k_table[] = { { USB_DEVICE(0x0CF3, 0x311D) }, { USB_DEVICE(0x13d3, 0x3375) }, { USB_DEVICE(0x04CA, 0x3005) }, + { USB_DEVICE(0x04CA, 0x3006) }, + { USB_DEVICE(0x04CA, 0x3008) }, { USB_DEVICE(0x13d3, 0x3362) }, { USB_DEVICE(0x0CF3, 0xE004) }, { USB_DEVICE(0x0930, 0x0219) }, { USB_DEVICE(0x0489, 0xe057) }, + { USB_DEVICE(0x13d3, 0x3393) }, + { USB_DEVICE(0x0489, 0xe04e) }, + { USB_DEVICE(0x0489, 0xe056) }, /* Atheros AR5BBU12 with sflash firmware */ { USB_DEVICE(0x0489, 0xE02C) }, @@ -104,10 +109,15 @@ static struct usb_device_id ath3k_blist_tbl[] = { { USB_DEVICE(0x0cf3, 0x311D), .driver_info = BTUSB_ATH3012 }, { USB_DEVICE(0x13d3, 0x3375), .driver_info = BTUSB_ATH3012 }, { USB_DEVICE(0x04ca, 0x3005), .driver_info = BTUSB_ATH3012 }, + { USB_DEVICE(0x04ca, 0x3006), .driver_info = BTUSB_ATH3012 }, + { USB_DEVICE(0x04ca, 0x3008), .driver_info = BTUSB_ATH3012 }, { USB_DEVICE(0x13d3, 0x3362), .driver_info = BTUSB_ATH3012 }, { USB_DEVICE(0x0cf3, 0xe004), .driver_info = BTUSB_ATH3012 }, { USB_DEVICE(0x0930, 0x0219), .driver_info = BTUSB_ATH3012 }, { USB_DEVICE(0x0489, 0xe057), .driver_info = BTUSB_ATH3012 }, + { USB_DEVICE(0x13d3, 0x3393), .driver_info = BTUSB_ATH3012 }, + { USB_DEVICE(0x0489, 0xe04e), .driver_info = BTUSB_ATH3012 }, + { USB_DEVICE(0x0489, 0xe056), .driver_info = BTUSB_ATH3012 }, /* Atheros AR5BBU22 with sflash firmware */ { USB_DEVICE(0x0489, 0xE03C), .driver_info = BTUSB_ATH3012 }, diff --git a/drivers/bluetooth/btusb.c b/drivers/bluetooth/btusb.c index a1d4ede5b892..7e351e345476 100644 --- a/drivers/bluetooth/btusb.c +++ b/drivers/bluetooth/btusb.c @@ -135,10 +135,15 @@ static struct usb_device_id blacklist_table[] = { { USB_DEVICE(0x0cf3, 0x311d), .driver_info = BTUSB_ATH3012 }, { USB_DEVICE(0x13d3, 0x3375), .driver_info = BTUSB_ATH3012 }, { USB_DEVICE(0x04ca, 0x3005), .driver_info = BTUSB_ATH3012 }, + { USB_DEVICE(0x04ca, 0x3006), .driver_info = BTUSB_ATH3012 }, + { USB_DEVICE(0x04ca, 0x3008), .driver_info = BTUSB_ATH3012 }, { USB_DEVICE(0x13d3, 0x3362), .driver_info = BTUSB_ATH3012 }, { USB_DEVICE(0x0cf3, 0xe004), .driver_info = BTUSB_ATH3012 }, { USB_DEVICE(0x0930, 0x0219), .driver_info = BTUSB_ATH3012 }, { USB_DEVICE(0x0489, 0xe057), .driver_info = BTUSB_ATH3012 }, + { USB_DEVICE(0x13d3, 0x3393), .driver_info = BTUSB_ATH3012 }, + { USB_DEVICE(0x0489, 0xe04e), .driver_info = BTUSB_ATH3012 }, + { USB_DEVICE(0x0489, 0xe056), .driver_info = BTUSB_ATH3012 }, /* Atheros AR5BBU12 with sflash firmware */ { USB_DEVICE(0x0489, 0xe02c), .driver_info = BTUSB_IGNORE }, diff --git a/drivers/char/agp/intel-gtt.c b/drivers/char/agp/intel-gtt.c index dbd901e94ea6..b8e2014cb9cb 100644 --- a/drivers/char/agp/intel-gtt.c +++ b/drivers/char/agp/intel-gtt.c @@ -60,7 +60,6 @@ struct intel_gtt_driver { }; static struct _intel_private { - struct intel_gtt base; const struct intel_gtt_driver *driver; struct pci_dev *pcidev; /* device one */ struct pci_dev *bridge_dev; @@ -75,7 +74,18 @@ static struct _intel_private { struct resource ifp_resource; int resource_valid; struct page *scratch_page; + phys_addr_t scratch_page_dma; int refcount; + /* Whether i915 needs to use the dmar apis or not. */ + unsigned int needs_dmar : 1; + phys_addr_t gma_bus_addr; + /* Size of memory reserved for graphics by the BIOS */ + unsigned int stolen_size; + /* Total number of gtt entries. */ + unsigned int gtt_total_entries; + /* Part of the gtt that is mappable by the cpu, for those chips where + * this is not the full gtt. */ + unsigned int gtt_mappable_entries; } intel_private; #define INTEL_GTT_GEN intel_private.driver->gen @@ -291,15 +301,15 @@ static int intel_gtt_setup_scratch_page(void) get_page(page); set_pages_uc(page, 1); - if (intel_private.base.needs_dmar) { + if (intel_private.needs_dmar) { dma_addr = pci_map_page(intel_private.pcidev, page, 0, PAGE_SIZE, PCI_DMA_BIDIRECTIONAL); if (pci_dma_mapping_error(intel_private.pcidev, dma_addr)) return -EINVAL; - intel_private.base.scratch_page_dma = dma_addr; + intel_private.scratch_page_dma = dma_addr; } else - intel_private.base.scratch_page_dma = page_to_phys(page); + intel_private.scratch_page_dma = page_to_phys(page); intel_private.scratch_page = page; @@ -506,7 +516,7 @@ static unsigned int intel_gtt_total_entries(void) /* On previous hardware, the GTT size was just what was * required to map the aperture. */ - return intel_private.base.gtt_mappable_entries; + return intel_private.gtt_mappable_entries; } } @@ -546,7 +556,7 @@ static unsigned int intel_gtt_mappable_entries(void) static void intel_gtt_teardown_scratch_page(void) { set_pages_wb(intel_private.scratch_page, 1); - pci_unmap_page(intel_private.pcidev, intel_private.base.scratch_page_dma, + pci_unmap_page(intel_private.pcidev, intel_private.scratch_page_dma, PAGE_SIZE, PCI_DMA_BIDIRECTIONAL); put_page(intel_private.scratch_page); __free_page(intel_private.scratch_page); @@ -562,6 +572,40 @@ static void intel_gtt_cleanup(void) intel_gtt_teardown_scratch_page(); } +/* Certain Gen5 chipsets require require idling the GPU before + * unmapping anything from the GTT when VT-d is enabled. + */ +static inline int needs_ilk_vtd_wa(void) +{ +#ifdef CONFIG_INTEL_IOMMU + const unsigned short gpu_devid = intel_private.pcidev->device; + + /* Query intel_iommu to see if we need the workaround. Presumably that + * was loaded first. + */ + if ((gpu_devid == PCI_DEVICE_ID_INTEL_IRONLAKE_M_HB || + gpu_devid == PCI_DEVICE_ID_INTEL_IRONLAKE_M_IG) && + intel_iommu_gfx_mapped) + return 1; +#endif + return 0; +} + +static bool intel_gtt_can_wc(void) +{ + if (INTEL_GTT_GEN <= 2) + return false; + + if (INTEL_GTT_GEN >= 6) + return false; + + /* Reports of major corruption with ILK vt'd enabled */ + if (needs_ilk_vtd_wa()) + return false; + + return true; +} + static int intel_gtt_init(void) { u32 gma_addr; @@ -572,8 +616,8 @@ static int intel_gtt_init(void) if (ret != 0) return ret; - intel_private.base.gtt_mappable_entries = intel_gtt_mappable_entries(); - intel_private.base.gtt_total_entries = intel_gtt_total_entries(); + intel_private.gtt_mappable_entries = intel_gtt_mappable_entries(); + intel_private.gtt_total_entries = intel_gtt_total_entries(); /* save the PGETBL reg for resume */ intel_private.PGETBL_save = @@ -585,13 +629,13 @@ static int intel_gtt_init(void) dev_info(&intel_private.bridge_dev->dev, "detected gtt size: %dK total, %dK mappable\n", - intel_private.base.gtt_total_entries * 4, - intel_private.base.gtt_mappable_entries * 4); + intel_private.gtt_total_entries * 4, + intel_private.gtt_mappable_entries * 4); - gtt_map_size = intel_private.base.gtt_total_entries * 4; + gtt_map_size = intel_private.gtt_total_entries * 4; intel_private.gtt = NULL; - if (INTEL_GTT_GEN < 6 && INTEL_GTT_GEN > 2) + if (intel_gtt_can_wc()) intel_private.gtt = ioremap_wc(intel_private.gtt_bus_addr, gtt_map_size); if (intel_private.gtt == NULL) @@ -602,13 +646,12 @@ static int intel_gtt_init(void) iounmap(intel_private.registers); return -ENOMEM; } - intel_private.base.gtt = intel_private.gtt; global_cache_flush(); /* FIXME: ? */ - intel_private.base.stolen_size = intel_gtt_stolen_size(); + intel_private.stolen_size = intel_gtt_stolen_size(); - intel_private.base.needs_dmar = USE_PCI_DMA_API && INTEL_GTT_GEN > 2; + intel_private.needs_dmar = USE_PCI_DMA_API && INTEL_GTT_GEN > 2; ret = intel_gtt_setup_scratch_page(); if (ret != 0) { @@ -623,7 +666,7 @@ static int intel_gtt_init(void) pci_read_config_dword(intel_private.pcidev, I915_GMADDR, &gma_addr); - intel_private.base.gma_bus_addr = (gma_addr & PCI_BASE_ADDRESS_MEM_MASK); + intel_private.gma_bus_addr = (gma_addr & PCI_BASE_ADDRESS_MEM_MASK); return 0; } @@ -634,8 +677,7 @@ static int intel_fake_agp_fetch_size(void) unsigned int aper_size; int i; - aper_size = (intel_private.base.gtt_mappable_entries << PAGE_SHIFT) - / MB(1); + aper_size = (intel_private.gtt_mappable_entries << PAGE_SHIFT) / MB(1); for (i = 0; i < num_sizes; i++) { if (aper_size == intel_fake_agp_sizes[i].size) { @@ -779,7 +821,7 @@ static int intel_fake_agp_configure(void) return -EIO; intel_private.clear_fake_agp = true; - agp_bridge->gart_bus_addr = intel_private.base.gma_bus_addr; + agp_bridge->gart_bus_addr = intel_private.gma_bus_addr; return 0; } @@ -841,12 +883,9 @@ static int intel_fake_agp_insert_entries(struct agp_memory *mem, { int ret = -EINVAL; - if (intel_private.base.do_idle_maps) - return -ENODEV; - if (intel_private.clear_fake_agp) { - int start = intel_private.base.stolen_size / PAGE_SIZE; - int end = intel_private.base.gtt_mappable_entries; + int start = intel_private.stolen_size / PAGE_SIZE; + int end = intel_private.gtt_mappable_entries; intel_gtt_clear_range(start, end - start); intel_private.clear_fake_agp = false; } @@ -857,7 +896,7 @@ static int intel_fake_agp_insert_entries(struct agp_memory *mem, if (mem->page_count == 0) goto out; - if (pg_start + mem->page_count > intel_private.base.gtt_total_entries) + if (pg_start + mem->page_count > intel_private.gtt_total_entries) goto out_err; if (type != mem->type) @@ -869,7 +908,7 @@ static int intel_fake_agp_insert_entries(struct agp_memory *mem, if (!mem->is_flushed) global_cache_flush(); - if (intel_private.base.needs_dmar) { + if (intel_private.needs_dmar) { struct sg_table st; ret = intel_gtt_map_memory(mem->pages, mem->page_count, &st); @@ -895,7 +934,7 @@ void intel_gtt_clear_range(unsigned int first_entry, unsigned int num_entries) unsigned int i; for (i = first_entry; i < (first_entry + num_entries); i++) { - intel_private.driver->write_entry(intel_private.base.scratch_page_dma, + intel_private.driver->write_entry(intel_private.scratch_page_dma, i, 0); } readl(intel_private.gtt+i-1); @@ -908,12 +947,9 @@ static int intel_fake_agp_remove_entries(struct agp_memory *mem, if (mem->page_count == 0) return 0; - if (intel_private.base.do_idle_maps) - return -ENODEV; - intel_gtt_clear_range(pg_start, mem->page_count); - if (intel_private.base.needs_dmar) { + if (intel_private.needs_dmar) { intel_gtt_unmap_memory(mem->sg_list, mem->num_sg); mem->sg_list = NULL; mem->num_sg = 0; @@ -1070,25 +1106,6 @@ static void i965_write_entry(dma_addr_t addr, writel(addr | pte_flags, intel_private.gtt + entry); } -/* Certain Gen5 chipsets require require idling the GPU before - * unmapping anything from the GTT when VT-d is enabled. - */ -static inline int needs_idle_maps(void) -{ -#ifdef CONFIG_INTEL_IOMMU - const unsigned short gpu_devid = intel_private.pcidev->device; - - /* Query intel_iommu to see if we need the workaround. Presumably that - * was loaded first. - */ - if ((gpu_devid == PCI_DEVICE_ID_INTEL_IRONLAKE_M_HB || - gpu_devid == PCI_DEVICE_ID_INTEL_IRONLAKE_M_IG) && - intel_iommu_gfx_mapped) - return 1; -#endif - return 0; -} - static int i9xx_setup(void) { u32 reg_addr, gtt_addr; @@ -1116,9 +1133,6 @@ static int i9xx_setup(void) break; } - if (needs_idle_maps()) - intel_private.base.do_idle_maps = 1; - intel_i9xx_setup_flush(); return 0; @@ -1390,9 +1404,13 @@ int intel_gmch_probe(struct pci_dev *bridge_pdev, struct pci_dev *gpu_pdev, } EXPORT_SYMBOL(intel_gmch_probe); -struct intel_gtt *intel_gtt_get(void) +void intel_gtt_get(size_t *gtt_total, size_t *stolen_size, + phys_addr_t *mappable_base, unsigned long *mappable_end) { - return &intel_private.base; + *gtt_total = intel_private.gtt_total_entries << PAGE_SHIFT; + *stolen_size = intel_private.stolen_size; + *mappable_base = intel_private.gma_bus_addr; + *mappable_end = intel_private.gtt_mappable_entries << PAGE_SHIFT; } EXPORT_SYMBOL(intel_gtt_get); diff --git a/drivers/clk/mvebu/clk-cpu.c b/drivers/clk/mvebu/clk-cpu.c index ff004578a119..9dd2551a0a41 100644 --- a/drivers/clk/mvebu/clk-cpu.c +++ b/drivers/clk/mvebu/clk-cpu.c @@ -124,7 +124,7 @@ void __init of_cpu_clk_setup(struct device_node *node) clks = kzalloc(ncpus * sizeof(*clks), GFP_KERNEL); if (WARN_ON(!clks)) - return; + goto clks_out; for_each_node_by_type(dn, "cpu") { struct clk_init_data init; @@ -134,11 +134,11 @@ void __init of_cpu_clk_setup(struct device_node *node) int cpu, err; if (WARN_ON(!clk_name)) - return; + goto bail_out; err = of_property_read_u32(dn, "reg", &cpu); if (WARN_ON(err)) - return; + goto bail_out; sprintf(clk_name, "cpu%d", cpu); parent_clk = of_clk_get(node, 0); @@ -167,6 +167,9 @@ void __init of_cpu_clk_setup(struct device_node *node) return; bail_out: kfree(clks); + while(ncpus--) + kfree(cpuclk[ncpus].clk_name); +clks_out: kfree(cpuclk); } diff --git a/drivers/cpufreq/Kconfig.x86 b/drivers/cpufreq/Kconfig.x86 index 934854ae5eb4..7227cd734042 100644 --- a/drivers/cpufreq/Kconfig.x86 +++ b/drivers/cpufreq/Kconfig.x86 @@ -106,7 +106,7 @@ config X86_POWERNOW_K7_ACPI config X86_POWERNOW_K8 tristate "AMD Opteron/Athlon64 PowerNow!" select CPU_FREQ_TABLE - depends on ACPI && ACPI_PROCESSOR + depends on ACPI && ACPI_PROCESSOR && X86_ACPI_CPUFREQ help This adds the CPUFreq driver for K8/early Opteron/Athlon64 processors. Support for K10 and newer processors is now in acpi-cpufreq. diff --git a/drivers/cpufreq/acpi-cpufreq.c b/drivers/cpufreq/acpi-cpufreq.c index 0d048f6a2b23..7b0d49d78c61 100644 --- a/drivers/cpufreq/acpi-cpufreq.c +++ b/drivers/cpufreq/acpi-cpufreq.c @@ -1030,4 +1030,11 @@ MODULE_PARM_DESC(acpi_pstate_strict, late_initcall(acpi_cpufreq_init); module_exit(acpi_cpufreq_exit); +static const struct x86_cpu_id acpi_cpufreq_ids[] = { + X86_FEATURE_MATCH(X86_FEATURE_ACPI), + X86_FEATURE_MATCH(X86_FEATURE_HW_PSTATE), + {} +}; +MODULE_DEVICE_TABLE(x86cpu, acpi_cpufreq_ids); + MODULE_ALIAS("acpi"); diff --git a/drivers/cpufreq/cpufreq-cpu0.c b/drivers/cpufreq/cpufreq-cpu0.c index 52bf36d599f5..debc5a7c8db6 100644 --- a/drivers/cpufreq/cpufreq-cpu0.c +++ b/drivers/cpufreq/cpufreq-cpu0.c @@ -71,12 +71,15 @@ static int cpu0_set_target(struct cpufreq_policy *policy, } if (cpu_reg) { + rcu_read_lock(); opp = opp_find_freq_ceil(cpu_dev, &freq_Hz); if (IS_ERR(opp)) { + rcu_read_unlock(); pr_err("failed to find OPP for %ld\n", freq_Hz); return PTR_ERR(opp); } volt = opp_get_voltage(opp); + rcu_read_unlock(); tol = volt * voltage_tolerance / 100; volt_old = regulator_get_voltage(cpu_reg); } @@ -236,12 +239,14 @@ static int cpu0_cpufreq_driver_init(void) */ for (i = 0; freq_table[i].frequency != CPUFREQ_TABLE_END; i++) ; + rcu_read_lock(); opp = opp_find_freq_exact(cpu_dev, freq_table[0].frequency * 1000, true); min_uV = opp_get_voltage(opp); opp = opp_find_freq_exact(cpu_dev, freq_table[i-1].frequency * 1000, true); max_uV = opp_get_voltage(opp); + rcu_read_unlock(); ret = regulator_set_voltage_time(cpu_reg, min_uV, max_uV); if (ret > 0) transition_latency += ret * 1000; diff --git a/drivers/cpufreq/omap-cpufreq.c b/drivers/cpufreq/omap-cpufreq.c index 1f3417a8322d..97102b05843f 100644 --- a/drivers/cpufreq/omap-cpufreq.c +++ b/drivers/cpufreq/omap-cpufreq.c @@ -110,13 +110,16 @@ static int omap_target(struct cpufreq_policy *policy, freq = ret; if (mpu_reg) { + rcu_read_lock(); opp = opp_find_freq_ceil(mpu_dev, &freq); if (IS_ERR(opp)) { + rcu_read_unlock(); dev_err(mpu_dev, "%s: unable to find MPU OPP for %d\n", __func__, freqs.new); return -EINVAL; } volt = opp_get_voltage(opp); + rcu_read_unlock(); tol = volt * OPP_TOLERANCE / 100; volt_old = regulator_get_voltage(mpu_reg); } diff --git a/drivers/cpuidle/cpuidle.c b/drivers/cpuidle/cpuidle.c index fb4a7dd57f94..e1f6860e069c 100644 --- a/drivers/cpuidle/cpuidle.c +++ b/drivers/cpuidle/cpuidle.c @@ -69,24 +69,15 @@ int cpuidle_play_dead(void) { struct cpuidle_device *dev = __this_cpu_read(cpuidle_devices); struct cpuidle_driver *drv = cpuidle_get_cpu_driver(dev); - int i, dead_state = -1; - int power_usage = INT_MAX; + int i; if (!drv) return -ENODEV; /* Find lowest-power state that supports long-term idle */ - for (i = CPUIDLE_DRIVER_STATE_START; i < drv->state_count; i++) { - struct cpuidle_state *s = &drv->states[i]; - - if (s->power_usage < power_usage && s->enter_dead) { - power_usage = s->power_usage; - dead_state = i; - } - } - - if (dead_state != -1) - return drv->states[dead_state].enter_dead(dev, dead_state); + for (i = drv->state_count - 1; i >= CPUIDLE_DRIVER_STATE_START; i--) + if (drv->states[i].enter_dead) + return drv->states[i].enter_dead(dev, i); return -ENODEV; } diff --git a/drivers/cpuidle/driver.c b/drivers/cpuidle/driver.c index c2b281afe0ed..422c7b69ba7c 100644 --- a/drivers/cpuidle/driver.c +++ b/drivers/cpuidle/driver.c @@ -19,34 +19,9 @@ DEFINE_SPINLOCK(cpuidle_driver_lock); static void __cpuidle_set_cpu_driver(struct cpuidle_driver *drv, int cpu); static struct cpuidle_driver * __cpuidle_get_cpu_driver(int cpu); -static void set_power_states(struct cpuidle_driver *drv) -{ - int i; - - /* - * cpuidle driver should set the drv->power_specified bit - * before registering if the driver provides - * power_usage numbers. - * - * If power_specified is not set, - * we fill in power_usage with decreasing values as the - * cpuidle code has an implicit assumption that state Cn - * uses less power than C(n-1). - * - * With CONFIG_ARCH_HAS_CPU_RELAX, C0 is already assigned - * an power value of -1. So we use -2, -3, etc, for other - * c-states. - */ - for (i = CPUIDLE_DRIVER_STATE_START; i < drv->state_count; i++) - drv->states[i].power_usage = -1 - i; -} - static void __cpuidle_driver_init(struct cpuidle_driver *drv) { drv->refcnt = 0; - - if (!drv->power_specified) - set_power_states(drv); } static int __cpuidle_register_driver(struct cpuidle_driver *drv, int cpu) diff --git a/drivers/cpuidle/governors/menu.c b/drivers/cpuidle/governors/menu.c index 20ea33afdda1..fe343a06b7da 100644 --- a/drivers/cpuidle/governors/menu.c +++ b/drivers/cpuidle/governors/menu.c @@ -312,7 +312,6 @@ static int menu_select(struct cpuidle_driver *drv, struct cpuidle_device *dev) { struct menu_device *data = &__get_cpu_var(menu_devices); int latency_req = pm_qos_request(PM_QOS_CPU_DMA_LATENCY); - int power_usage = INT_MAX; int i; int multiplier; struct timespec t; @@ -383,11 +382,8 @@ static int menu_select(struct cpuidle_driver *drv, struct cpuidle_device *dev) if (s->exit_latency * multiplier > data->predicted_us) continue; - if (s->power_usage < power_usage) { - power_usage = s->power_usage; - data->last_state_idx = i; - data->exit_us = s->exit_latency; - } + data->last_state_idx = i; + data->exit_us = s->exit_latency; } /* not deepest C-state chosen for low predicted residency */ diff --git a/drivers/cpuidle/sysfs.c b/drivers/cpuidle/sysfs.c index 340942946106..428754af6236 100644 --- a/drivers/cpuidle/sysfs.c +++ b/drivers/cpuidle/sysfs.c @@ -374,7 +374,7 @@ static int cpuidle_add_state_sysfs(struct cpuidle_device *device) struct cpuidle_driver *drv = cpuidle_get_cpu_driver(device); /* state statistics */ - for (i = 0; i < drv->state_count; i++) { + for (i = 0; i < device->state_count; i++) { kobj = kzalloc(sizeof(struct cpuidle_state_kobj), GFP_KERNEL); if (!kobj) goto error_state; diff --git a/drivers/devfreq/devfreq.c b/drivers/devfreq/devfreq.c index 53766f39aadd..3b367973a802 100644 --- a/drivers/devfreq/devfreq.c +++ b/drivers/devfreq/devfreq.c @@ -994,6 +994,11 @@ module_exit(devfreq_exit); * @freq: The frequency given to target function * @flags: Flags handed from devfreq framework. * + * Locking: This function must be called under rcu_read_lock(). opp is a rcu + * protected pointer. The reason for the same is that the opp pointer which is + * returned will remain valid for use with opp_get_{voltage, freq} only while + * under the locked area. The pointer returned must be used prior to unlocking + * with rcu_read_unlock() to maintain the integrity of the pointer. */ struct opp *devfreq_recommended_opp(struct device *dev, unsigned long *freq, u32 flags) diff --git a/drivers/devfreq/exynos4_bus.c b/drivers/devfreq/exynos4_bus.c index 80c745e83082..46d94e9e95b5 100644 --- a/drivers/devfreq/exynos4_bus.c +++ b/drivers/devfreq/exynos4_bus.c @@ -73,6 +73,16 @@ enum busclk_level_idx { #define EX4210_LV_NUM (LV_2 + 1) #define EX4x12_LV_NUM (LV_4 + 1) +/** + * struct busfreq_opp_info - opp information for bus + * @rate: Frequency in hertz + * @volt: Voltage in microvolts corresponding to this OPP + */ +struct busfreq_opp_info { + unsigned long rate; + unsigned long volt; +}; + struct busfreq_data { enum exynos4_busf_type type; struct device *dev; @@ -80,7 +90,7 @@ struct busfreq_data { bool disabled; struct regulator *vdd_int; struct regulator *vdd_mif; /* Exynos4412/4212 only */ - struct opp *curr_opp; + struct busfreq_opp_info curr_oppinfo; struct exynos4_ppmu dmc[2]; struct notifier_block pm_notifier; @@ -296,13 +306,14 @@ static unsigned int exynos4x12_clkdiv_sclkip[][3] = { }; -static int exynos4210_set_busclk(struct busfreq_data *data, struct opp *opp) +static int exynos4210_set_busclk(struct busfreq_data *data, + struct busfreq_opp_info *oppi) { unsigned int index; unsigned int tmp; for (index = LV_0; index < EX4210_LV_NUM; index++) - if (opp_get_freq(opp) == exynos4210_busclk_table[index].clk) + if (oppi->rate == exynos4210_busclk_table[index].clk) break; if (index == EX4210_LV_NUM) @@ -361,13 +372,14 @@ static int exynos4210_set_busclk(struct busfreq_data *data, struct opp *opp) return 0; } -static int exynos4x12_set_busclk(struct busfreq_data *data, struct opp *opp) +static int exynos4x12_set_busclk(struct busfreq_data *data, + struct busfreq_opp_info *oppi) { unsigned int index; unsigned int tmp; for (index = LV_0; index < EX4x12_LV_NUM; index++) - if (opp_get_freq(opp) == exynos4x12_mifclk_table[index].clk) + if (oppi->rate == exynos4x12_mifclk_table[index].clk) break; if (index == EX4x12_LV_NUM) @@ -576,11 +588,12 @@ static int exynos4x12_get_intspec(unsigned long mifclk) return -EINVAL; } -static int exynos4_bus_setvolt(struct busfreq_data *data, struct opp *opp, - struct opp *oldopp) +static int exynos4_bus_setvolt(struct busfreq_data *data, + struct busfreq_opp_info *oppi, + struct busfreq_opp_info *oldoppi) { int err = 0, tmp; - unsigned long volt = opp_get_voltage(opp); + unsigned long volt = oppi->volt; switch (data->type) { case TYPE_BUSF_EXYNOS4210: @@ -595,11 +608,11 @@ static int exynos4_bus_setvolt(struct busfreq_data *data, struct opp *opp, if (err) break; - tmp = exynos4x12_get_intspec(opp_get_freq(opp)); + tmp = exynos4x12_get_intspec(oppi->rate); if (tmp < 0) { err = tmp; regulator_set_voltage(data->vdd_mif, - opp_get_voltage(oldopp), + oldoppi->volt, MAX_SAFEVOLT); break; } @@ -609,7 +622,7 @@ static int exynos4_bus_setvolt(struct busfreq_data *data, struct opp *opp, /* Try to recover */ if (err) regulator_set_voltage(data->vdd_mif, - opp_get_voltage(oldopp), + oldoppi->volt, MAX_SAFEVOLT); break; default: @@ -626,17 +639,26 @@ static int exynos4_bus_target(struct device *dev, unsigned long *_freq, struct platform_device *pdev = container_of(dev, struct platform_device, dev); struct busfreq_data *data = platform_get_drvdata(pdev); - struct opp *opp = devfreq_recommended_opp(dev, _freq, flags); - unsigned long freq = opp_get_freq(opp); - unsigned long old_freq = opp_get_freq(data->curr_opp); + struct opp *opp; + unsigned long freq; + unsigned long old_freq = data->curr_oppinfo.rate; + struct busfreq_opp_info new_oppinfo; - if (IS_ERR(opp)) + rcu_read_lock(); + opp = devfreq_recommended_opp(dev, _freq, flags); + if (IS_ERR(opp)) { + rcu_read_unlock(); return PTR_ERR(opp); + } + new_oppinfo.rate = opp_get_freq(opp); + new_oppinfo.volt = opp_get_voltage(opp); + rcu_read_unlock(); + freq = new_oppinfo.rate; if (old_freq == freq) return 0; - dev_dbg(dev, "targetting %lukHz %luuV\n", freq, opp_get_voltage(opp)); + dev_dbg(dev, "targetting %lukHz %luuV\n", freq, new_oppinfo.volt); mutex_lock(&data->lock); @@ -644,17 +666,18 @@ static int exynos4_bus_target(struct device *dev, unsigned long *_freq, goto out; if (old_freq < freq) - err = exynos4_bus_setvolt(data, opp, data->curr_opp); + err = exynos4_bus_setvolt(data, &new_oppinfo, + &data->curr_oppinfo); if (err) goto out; if (old_freq != freq) { switch (data->type) { case TYPE_BUSF_EXYNOS4210: - err = exynos4210_set_busclk(data, opp); + err = exynos4210_set_busclk(data, &new_oppinfo); break; case TYPE_BUSF_EXYNOS4x12: - err = exynos4x12_set_busclk(data, opp); + err = exynos4x12_set_busclk(data, &new_oppinfo); break; default: err = -EINVAL; @@ -664,11 +687,12 @@ static int exynos4_bus_target(struct device *dev, unsigned long *_freq, goto out; if (old_freq > freq) - err = exynos4_bus_setvolt(data, opp, data->curr_opp); + err = exynos4_bus_setvolt(data, &new_oppinfo, + &data->curr_oppinfo); if (err) goto out; - data->curr_opp = opp; + data->curr_oppinfo = new_oppinfo; out: mutex_unlock(&data->lock); return err; @@ -702,7 +726,7 @@ static int exynos4_bus_get_dev_status(struct device *dev, exynos4_read_ppmu(data); busier_dmc = exynos4_get_busier_dmc(data); - stat->current_frequency = opp_get_freq(data->curr_opp); + stat->current_frequency = data->curr_oppinfo.rate; if (busier_dmc) addr = S5P_VA_DMC1; @@ -933,6 +957,7 @@ static int exynos4_busfreq_pm_notifier_event(struct notifier_block *this, struct busfreq_data *data = container_of(this, struct busfreq_data, pm_notifier); struct opp *opp; + struct busfreq_opp_info new_oppinfo; unsigned long maxfreq = ULONG_MAX; int err = 0; @@ -943,18 +968,29 @@ static int exynos4_busfreq_pm_notifier_event(struct notifier_block *this, data->disabled = true; + rcu_read_lock(); opp = opp_find_freq_floor(data->dev, &maxfreq); + if (IS_ERR(opp)) { + rcu_read_unlock(); + dev_err(data->dev, "%s: unable to find a min freq\n", + __func__); + return PTR_ERR(opp); + } + new_oppinfo.rate = opp_get_freq(opp); + new_oppinfo.volt = opp_get_voltage(opp); + rcu_read_unlock(); - err = exynos4_bus_setvolt(data, opp, data->curr_opp); + err = exynos4_bus_setvolt(data, &new_oppinfo, + &data->curr_oppinfo); if (err) goto unlock; switch (data->type) { case TYPE_BUSF_EXYNOS4210: - err = exynos4210_set_busclk(data, opp); + err = exynos4210_set_busclk(data, &new_oppinfo); break; case TYPE_BUSF_EXYNOS4x12: - err = exynos4x12_set_busclk(data, opp); + err = exynos4x12_set_busclk(data, &new_oppinfo); break; default: err = -EINVAL; @@ -962,7 +998,7 @@ static int exynos4_busfreq_pm_notifier_event(struct notifier_block *this, if (err) goto unlock; - data->curr_opp = opp; + data->curr_oppinfo = new_oppinfo; unlock: mutex_unlock(&data->lock); if (err) @@ -1027,13 +1063,17 @@ static int exynos4_busfreq_probe(struct platform_device *pdev) } } + rcu_read_lock(); opp = opp_find_freq_floor(dev, &exynos4_devfreq_profile.initial_freq); if (IS_ERR(opp)) { + rcu_read_unlock(); dev_err(dev, "Invalid initial frequency %lu kHz.\n", exynos4_devfreq_profile.initial_freq); return PTR_ERR(opp); } - data->curr_opp = opp; + data->curr_oppinfo.rate = opp_get_freq(opp); + data->curr_oppinfo.volt = opp_get_voltage(opp); + rcu_read_unlock(); platform_set_drvdata(pdev, data); diff --git a/drivers/dma/imx-dma.c b/drivers/dma/imx-dma.c index dbf0e6f8de8a..a7dcf78b1ff8 100644 --- a/drivers/dma/imx-dma.c +++ b/drivers/dma/imx-dma.c @@ -684,9 +684,8 @@ static int imxdma_control(struct dma_chan *chan, enum dma_ctrl_cmd cmd, break; } - imxdmac->hw_chaining = 1; - if (!imxdma_hw_chain(imxdmac)) - return -EINVAL; + imxdmac->hw_chaining = 0; + imxdmac->ccr_from_device = (mode | IMX_DMA_TYPE_FIFO) | ((IMX_DMA_MEMSIZE_32 | IMX_DMA_TYPE_LINEAR) << 2) | CCR_REN; diff --git a/drivers/dma/ioat/dma_v3.c b/drivers/dma/ioat/dma_v3.c index e5fc944de1f0..3e9d66920eb3 100644 --- a/drivers/dma/ioat/dma_v3.c +++ b/drivers/dma/ioat/dma_v3.c @@ -951,7 +951,7 @@ static int ioat_xor_val_self_test(struct ioatdma_device *device) goto free_resources; } } - dma_sync_single_for_device(dev, dest_dma, PAGE_SIZE, DMA_TO_DEVICE); + dma_sync_single_for_device(dev, dest_dma, PAGE_SIZE, DMA_FROM_DEVICE); /* skip validate if the capability is not present */ if (!dma_has_cap(DMA_XOR_VAL, dma_chan->device->cap_mask)) diff --git a/drivers/dma/tegra20-apb-dma.c b/drivers/dma/tegra20-apb-dma.c index c39e61bc8172..3cad856fe67f 100644 --- a/drivers/dma/tegra20-apb-dma.c +++ b/drivers/dma/tegra20-apb-dma.c @@ -266,6 +266,7 @@ static struct tegra_dma_desc *tegra_dma_desc_get( if (async_tx_test_ack(&dma_desc->txd)) { list_del(&dma_desc->node); spin_unlock_irqrestore(&tdc->lock, flags); + dma_desc->txd.flags = 0; return dma_desc; } } @@ -1050,7 +1051,9 @@ struct dma_async_tx_descriptor *tegra_dma_prep_dma_cyclic( TEGRA_APBDMA_AHBSEQ_WRAP_SHIFT; ahb_seq |= TEGRA_APBDMA_AHBSEQ_BUS_WIDTH_32; - csr |= TEGRA_APBDMA_CSR_FLOW | TEGRA_APBDMA_CSR_IE_EOC; + csr |= TEGRA_APBDMA_CSR_FLOW; + if (flags & DMA_PREP_INTERRUPT) + csr |= TEGRA_APBDMA_CSR_IE_EOC; csr |= tdc->dma_sconfig.slave_id << TEGRA_APBDMA_CSR_REQ_SEL_SHIFT; apb_seq |= TEGRA_APBDMA_APBSEQ_WRAP_WORD_1; @@ -1095,7 +1098,8 @@ struct dma_async_tx_descriptor *tegra_dma_prep_dma_cyclic( mem += len; } sg_req->last_sg = true; - dma_desc->txd.flags = 0; + if (flags & DMA_CTRL_ACK) + dma_desc->txd.flags = DMA_CTRL_ACK; /* * Make sure that mode should not be conflicting with currently diff --git a/drivers/edac/edac_mc.c b/drivers/edac/edac_mc.c index 281f566a5513..d1e9eb191f2b 100644 --- a/drivers/edac/edac_mc.c +++ b/drivers/edac/edac_mc.c @@ -340,7 +340,7 @@ struct mem_ctl_info *edac_mc_alloc(unsigned mc_num, /* * Alocate and fill the csrow/channels structs */ - mci->csrows = kcalloc(sizeof(*mci->csrows), tot_csrows, GFP_KERNEL); + mci->csrows = kcalloc(tot_csrows, sizeof(*mci->csrows), GFP_KERNEL); if (!mci->csrows) goto error; for (row = 0; row < tot_csrows; row++) { @@ -351,7 +351,7 @@ struct mem_ctl_info *edac_mc_alloc(unsigned mc_num, csr->csrow_idx = row; csr->mci = mci; csr->nr_channels = tot_channels; - csr->channels = kcalloc(sizeof(*csr->channels), tot_channels, + csr->channels = kcalloc(tot_channels, sizeof(*csr->channels), GFP_KERNEL); if (!csr->channels) goto error; @@ -369,7 +369,7 @@ struct mem_ctl_info *edac_mc_alloc(unsigned mc_num, /* * Allocate and fill the dimm structs */ - mci->dimms = kcalloc(sizeof(*mci->dimms), tot_dimms, GFP_KERNEL); + mci->dimms = kcalloc(tot_dimms, sizeof(*mci->dimms), GFP_KERNEL); if (!mci->dimms) goto error; diff --git a/drivers/edac/edac_pci_sysfs.c b/drivers/edac/edac_pci_sysfs.c index dc6e905ee1a5..0056c4dae9d5 100644 --- a/drivers/edac/edac_pci_sysfs.c +++ b/drivers/edac/edac_pci_sysfs.c @@ -256,7 +256,7 @@ static ssize_t edac_pci_dev_store(struct kobject *kobj, struct edac_pci_dev_attribute *edac_pci_dev; edac_pci_dev = (struct edac_pci_dev_attribute *)attr; - if (edac_pci_dev->show) + if (edac_pci_dev->store) return edac_pci_dev->store(edac_pci_dev->value, buffer, count); return -EIO; } diff --git a/drivers/firmware/dmi_scan.c b/drivers/firmware/dmi_scan.c index fd3ae6290d71..982f1f5f5742 100644 --- a/drivers/firmware/dmi_scan.c +++ b/drivers/firmware/dmi_scan.c @@ -471,7 +471,7 @@ void __init dmi_scan_machine(void) char __iomem *p, *q; int rc; - if (efi_enabled) { + if (efi_enabled(EFI_CONFIG_TABLES)) { if (efi.smbios == EFI_INVALID_TABLE_ADDR) goto error; diff --git a/drivers/firmware/efivars.c b/drivers/firmware/efivars.c index 7b1c37497c9a..f5596db0cf58 100644 --- a/drivers/firmware/efivars.c +++ b/drivers/firmware/efivars.c @@ -674,7 +674,7 @@ static int efi_status_to_err(efi_status_t status) err = -EACCES; break; case EFI_NOT_FOUND: - err = -ENOENT; + err = -EIO; break; default: err = -EINVAL; @@ -793,6 +793,7 @@ static ssize_t efivarfs_file_write(struct file *file, spin_unlock(&efivars->lock); efivar_unregister(var); drop_nlink(inode); + d_delete(file->f_dentry); dput(file->f_dentry); } else { @@ -994,7 +995,7 @@ static int efivarfs_unlink(struct inode *dir, struct dentry *dentry) list_del(&var->list); spin_unlock(&efivars->lock); efivar_unregister(var); - drop_nlink(dir); + drop_nlink(dentry->d_inode); dput(dentry); return 0; } @@ -1782,7 +1783,7 @@ efivars_init(void) printk(KERN_INFO "EFI Variables Facility v%s %s\n", EFIVARS_VERSION, EFIVARS_DATE); - if (!efi_enabled) + if (!efi_enabled(EFI_RUNTIME_SERVICES)) return 0; /* For now we'll register the efi directory at /sys/firmware/efi */ @@ -1822,7 +1823,7 @@ err_put: static void __exit efivars_exit(void) { - if (efi_enabled) { + if (efi_enabled(EFI_RUNTIME_SERVICES)) { unregister_efivars(&__efivars); kobject_put(efi_kobj); } diff --git a/drivers/firmware/iscsi_ibft_find.c b/drivers/firmware/iscsi_ibft_find.c index 4da4eb9ae926..2224f1dc074b 100644 --- a/drivers/firmware/iscsi_ibft_find.c +++ b/drivers/firmware/iscsi_ibft_find.c @@ -99,7 +99,7 @@ unsigned long __init find_ibft_region(unsigned long *sizep) /* iBFT 1.03 section 1.4.3.1 mandates that UEFI machines will * only use ACPI for this */ - if (!efi_enabled) + if (!efi_enabled(EFI_BOOT)) find_ibft_in_mem(); if (ibft_addr) { diff --git a/drivers/gpio/gpio-mvebu.c b/drivers/gpio/gpio-mvebu.c index 7d9bd94be8d2..6819d63cb167 100644 --- a/drivers/gpio/gpio-mvebu.c +++ b/drivers/gpio/gpio-mvebu.c @@ -547,7 +547,6 @@ static int mvebu_gpio_probe(struct platform_device *pdev) mvchip->membase = devm_request_and_ioremap(&pdev->dev, res); if (! mvchip->membase) { dev_err(&pdev->dev, "Cannot ioremap\n"); - kfree(mvchip->chip.label); return -ENOMEM; } @@ -557,14 +556,12 @@ static int mvebu_gpio_probe(struct platform_device *pdev) res = platform_get_resource(pdev, IORESOURCE_MEM, 1); if (! res) { dev_err(&pdev->dev, "Cannot get memory resource\n"); - kfree(mvchip->chip.label); return -ENODEV; } mvchip->percpu_membase = devm_request_and_ioremap(&pdev->dev, res); if (! mvchip->percpu_membase) { dev_err(&pdev->dev, "Cannot ioremap\n"); - kfree(mvchip->chip.label); return -ENOMEM; } } @@ -625,7 +622,6 @@ static int mvebu_gpio_probe(struct platform_device *pdev) mvchip->irqbase = irq_alloc_descs(-1, 0, ngpios, -1); if (mvchip->irqbase < 0) { dev_err(&pdev->dev, "no irqs\n"); - kfree(mvchip->chip.label); return -ENOMEM; } @@ -633,7 +629,6 @@ static int mvebu_gpio_probe(struct platform_device *pdev) mvchip->membase, handle_level_irq); if (! gc) { dev_err(&pdev->dev, "Cannot allocate generic irq_chip\n"); - kfree(mvchip->chip.label); return -ENOMEM; } @@ -668,7 +663,6 @@ static int mvebu_gpio_probe(struct platform_device *pdev) irq_remove_generic_chip(gc, IRQ_MSK(ngpios), IRQ_NOREQUEST, IRQ_LEVEL | IRQ_NOPROBE); kfree(gc); - kfree(mvchip->chip.label); return -ENODEV; } diff --git a/drivers/gpio/gpio-samsung.c b/drivers/gpio/gpio-samsung.c index 01f7fe955590..76be7eed79de 100644 --- a/drivers/gpio/gpio-samsung.c +++ b/drivers/gpio/gpio-samsung.c @@ -32,7 +32,6 @@ #include <mach/hardware.h> #include <mach/map.h> -#include <mach/regs-clock.h> #include <mach/regs-gpio.h> #include <plat/cpu.h> @@ -446,7 +445,7 @@ static struct samsung_gpio_cfg s3c24xx_gpiocfg_banka = { }; #endif -#if defined(CONFIG_ARCH_EXYNOS4) || defined(CONFIG_ARCH_EXYNOS5) +#if defined(CONFIG_ARCH_EXYNOS4) || defined(CONFIG_SOC_EXYNOS5250) static struct samsung_gpio_cfg exynos_gpio_cfg = { .set_pull = exynos_gpio_setpull, .get_pull = exynos_gpio_getpull, @@ -2446,7 +2445,7 @@ static struct samsung_gpio_chip exynos4_gpios_3[] = { }; #endif -#ifdef CONFIG_ARCH_EXYNOS5 +#ifdef CONFIG_SOC_EXYNOS5250 static struct samsung_gpio_chip exynos5_gpios_1[] = { { .chip = { @@ -2614,7 +2613,7 @@ static struct samsung_gpio_chip exynos5_gpios_1[] = { }; #endif -#ifdef CONFIG_ARCH_EXYNOS5 +#ifdef CONFIG_SOC_EXYNOS5250 static struct samsung_gpio_chip exynos5_gpios_2[] = { { .chip = { @@ -2675,7 +2674,7 @@ static struct samsung_gpio_chip exynos5_gpios_2[] = { }; #endif -#ifdef CONFIG_ARCH_EXYNOS5 +#ifdef CONFIG_SOC_EXYNOS5250 static struct samsung_gpio_chip exynos5_gpios_3[] = { { .chip = { @@ -2711,7 +2710,7 @@ static struct samsung_gpio_chip exynos5_gpios_3[] = { }; #endif -#ifdef CONFIG_ARCH_EXYNOS5 +#ifdef CONFIG_SOC_EXYNOS5250 static struct samsung_gpio_chip exynos5_gpios_4[] = { { .chip = { @@ -3010,7 +3009,7 @@ static __init int samsung_gpiolib_init(void) int i, nr_chips; int group = 0; -#ifdef CONFIG_PINCTRL_SAMSUNG +#if defined(CONFIG_PINCTRL_EXYNOS) || defined(CONFIG_PINCTRL_EXYNOS5440) /* * This gpio driver includes support for device tree support and there * are platforms using it. In order to maintain compatibility with those @@ -3026,6 +3025,7 @@ static __init int samsung_gpiolib_init(void) static const struct of_device_id exynos_pinctrl_ids[] = { { .compatible = "samsung,pinctrl-exynos4210", }, { .compatible = "samsung,pinctrl-exynos4x12", }, + { .compatible = "samsung,pinctrl-exynos5440", }, }; for_each_matching_node(pctrl_np, exynos_pinctrl_ids) if (pctrl_np && of_device_is_available(pctrl_np)) diff --git a/drivers/gpu/Makefile b/drivers/gpu/Makefile index cc9277885dd0..30879df3daea 100644 --- a/drivers/gpu/Makefile +++ b/drivers/gpu/Makefile @@ -1 +1 @@ -obj-y += drm/ vga/ stub/ +obj-y += drm/ vga/ diff --git a/drivers/gpu/drm/Kconfig b/drivers/gpu/drm/Kconfig index 3399b209260a..0ce5f52ac56e 100644 --- a/drivers/gpu/drm/Kconfig +++ b/drivers/gpu/drm/Kconfig @@ -69,6 +69,8 @@ config DRM_KMS_CMA_HELPER help Choose this if you need the KMS CMA helper functions +source "drivers/gpu/drm/i2c/Kconfig" + config DRM_TDFX tristate "3dfx Banshee/Voodoo3+" depends on DRM && PCI @@ -213,3 +215,5 @@ source "drivers/gpu/drm/cirrus/Kconfig" source "drivers/gpu/drm/shmobile/Kconfig" source "drivers/gpu/drm/tegra/Kconfig" + +source "drivers/gpu/drm/omapdrm/Kconfig" diff --git a/drivers/gpu/drm/Makefile b/drivers/gpu/drm/Makefile index 6f58c81cfcbc..b6b43cbc18e4 100644 --- a/drivers/gpu/drm/Makefile +++ b/drivers/gpu/drm/Makefile @@ -50,4 +50,5 @@ obj-$(CONFIG_DRM_UDL) += udl/ obj-$(CONFIG_DRM_AST) += ast/ obj-$(CONFIG_DRM_SHMOBILE) +=shmobile/ obj-$(CONFIG_DRM_TEGRA) += tegra/ +obj-$(CONFIG_DRM_OMAP) += omapdrm/ obj-y += i2c/ diff --git a/drivers/gpu/drm/ast/ast_drv.c b/drivers/gpu/drm/ast/ast_drv.c index 2d2c2f8d6dc6..df0d0a08097a 100644 --- a/drivers/gpu/drm/ast/ast_drv.c +++ b/drivers/gpu/drm/ast/ast_drv.c @@ -94,9 +94,9 @@ static int ast_drm_thaw(struct drm_device *dev) ast_post_gpu(dev); drm_mode_config_reset(dev); - mutex_lock(&dev->mode_config.mutex); + drm_modeset_lock_all(dev); drm_helper_resume_force_mode(dev); - mutex_unlock(&dev->mode_config.mutex); + drm_modeset_unlock_all(dev); console_lock(); ast_fbdev_set_suspend(dev, 0); diff --git a/drivers/gpu/drm/ast/ast_drv.h b/drivers/gpu/drm/ast/ast_drv.h index 5ccf984f063a..528429252f0f 100644 --- a/drivers/gpu/drm/ast/ast_drv.h +++ b/drivers/gpu/drm/ast/ast_drv.h @@ -98,6 +98,8 @@ struct ast_private { struct drm_gem_object *cursor_cache; uint64_t cursor_cache_gpu_addr; + /* Acces to this cache is protected by the crtc->mutex of the only crtc + * we have. */ struct ttm_bo_kmap_obj cache_kmap; int next_cursor; }; diff --git a/drivers/gpu/drm/ast/ast_fb.c b/drivers/gpu/drm/ast/ast_fb.c index d9ec77959dff..34931fe7d2c5 100644 --- a/drivers/gpu/drm/ast/ast_fb.c +++ b/drivers/gpu/drm/ast/ast_fb.c @@ -40,6 +40,7 @@ #include <drm/drmP.h> #include <drm/drm_crtc.h> #include <drm/drm_fb_helper.h> +#include <drm/drm_crtc_helper.h> #include "ast_drv.h" static void ast_dirty_update(struct ast_fbdev *afbdev, @@ -145,9 +146,10 @@ static int astfb_create_object(struct ast_fbdev *afbdev, return ret; } -static int astfb_create(struct ast_fbdev *afbdev, +static int astfb_create(struct drm_fb_helper *helper, struct drm_fb_helper_surface_size *sizes) { + struct ast_fbdev *afbdev = (struct ast_fbdev *)helper; struct drm_device *dev = afbdev->helper.dev; struct drm_mode_fb_cmd2 mode_cmd; struct drm_framebuffer *fb; @@ -248,26 +250,10 @@ static void ast_fb_gamma_get(struct drm_crtc *crtc, u16 *red, u16 *green, *blue = ast_crtc->lut_b[regno] << 8; } -static int ast_find_or_create_single(struct drm_fb_helper *helper, - struct drm_fb_helper_surface_size *sizes) -{ - struct ast_fbdev *afbdev = (struct ast_fbdev *)helper; - int new_fb = 0; - int ret; - - if (!helper->fb) { - ret = astfb_create(afbdev, sizes); - if (ret) - return ret; - new_fb = 1; - } - return new_fb; -} - static struct drm_fb_helper_funcs ast_fb_helper_funcs = { .gamma_set = ast_fb_gamma_set, .gamma_get = ast_fb_gamma_get, - .fb_probe = ast_find_or_create_single, + .fb_probe = astfb_create, }; static void ast_fbdev_destroy(struct drm_device *dev, @@ -290,6 +276,7 @@ static void ast_fbdev_destroy(struct drm_device *dev, drm_fb_helper_fini(&afbdev->helper); vfree(afbdev->sysram); + drm_framebuffer_unregister_private(&afb->base); drm_framebuffer_cleanup(&afb->base); } @@ -313,6 +300,10 @@ int ast_fbdev_init(struct drm_device *dev) } drm_fb_helper_single_add_all_connectors(&afbdev->helper); + + /* disable all the possible outputs/crtcs before entering KMS mode */ + drm_helper_disable_unused_functions(dev); + drm_fb_helper_initial_config(&afbdev->helper, 32); return 0; } diff --git a/drivers/gpu/drm/ast/ast_main.c b/drivers/gpu/drm/ast/ast_main.c index f668e6cc0f7a..f60fd7bd1183 100644 --- a/drivers/gpu/drm/ast/ast_main.c +++ b/drivers/gpu/drm/ast/ast_main.c @@ -246,16 +246,8 @@ static void ast_user_framebuffer_destroy(struct drm_framebuffer *fb) kfree(fb); } -static int ast_user_framebuffer_create_handle(struct drm_framebuffer *fb, - struct drm_file *file, - unsigned int *handle) -{ - return -EINVAL; -} - static const struct drm_framebuffer_funcs ast_fb_funcs = { .destroy = ast_user_framebuffer_destroy, - .create_handle = ast_user_framebuffer_create_handle, }; @@ -266,13 +258,13 @@ int ast_framebuffer_init(struct drm_device *dev, { int ret; + drm_helper_mode_fill_fb_struct(&ast_fb->base, mode_cmd); + ast_fb->obj = obj; ret = drm_framebuffer_init(dev, &ast_fb->base, &ast_fb_funcs); if (ret) { DRM_ERROR("framebuffer init failed %d\n", ret); return ret; } - drm_helper_mode_fill_fb_struct(&ast_fb->base, mode_cmd); - ast_fb->obj = obj; return 0; } diff --git a/drivers/gpu/drm/cirrus/cirrus_fbdev.c b/drivers/gpu/drm/cirrus/cirrus_fbdev.c index 6c6b4c87d309..e25afccaf85b 100644 --- a/drivers/gpu/drm/cirrus/cirrus_fbdev.c +++ b/drivers/gpu/drm/cirrus/cirrus_fbdev.c @@ -11,6 +11,7 @@ #include <linux/module.h> #include <drm/drmP.h> #include <drm/drm_fb_helper.h> +#include <drm/drm_crtc_helper.h> #include <linux/fb.h> @@ -120,9 +121,10 @@ static int cirrusfb_create_object(struct cirrus_fbdev *afbdev, return ret; } -static int cirrusfb_create(struct cirrus_fbdev *gfbdev, +static int cirrusfb_create(struct drm_fb_helper *helper, struct drm_fb_helper_surface_size *sizes) { + struct cirrus_fbdev *gfbdev = (struct cirrus_fbdev *)helper; struct drm_device *dev = gfbdev->helper.dev; struct cirrus_device *cdev = gfbdev->helper.dev->dev_private; struct fb_info *info; @@ -219,23 +221,6 @@ out_iounmap: return ret; } -static int cirrus_fb_find_or_create_single(struct drm_fb_helper *helper, - struct drm_fb_helper_surface_size - *sizes) -{ - struct cirrus_fbdev *gfbdev = (struct cirrus_fbdev *)helper; - int new_fb = 0; - int ret; - - if (!helper->fb) { - ret = cirrusfb_create(gfbdev, sizes); - if (ret) - return ret; - new_fb = 1; - } - return new_fb; -} - static int cirrus_fbdev_destroy(struct drm_device *dev, struct cirrus_fbdev *gfbdev) { @@ -258,6 +243,7 @@ static int cirrus_fbdev_destroy(struct drm_device *dev, vfree(gfbdev->sysram); drm_fb_helper_fini(&gfbdev->helper); + drm_framebuffer_unregister_private(&gfb->base); drm_framebuffer_cleanup(&gfb->base); return 0; @@ -266,7 +252,7 @@ static int cirrus_fbdev_destroy(struct drm_device *dev, static struct drm_fb_helper_funcs cirrus_fb_helper_funcs = { .gamma_set = cirrus_crtc_fb_gamma_set, .gamma_get = cirrus_crtc_fb_gamma_get, - .fb_probe = cirrus_fb_find_or_create_single, + .fb_probe = cirrusfb_create, }; int cirrus_fbdev_init(struct cirrus_device *cdev) @@ -290,6 +276,9 @@ int cirrus_fbdev_init(struct cirrus_device *cdev) return ret; } drm_fb_helper_single_add_all_connectors(&gfbdev->helper); + + /* disable all the possible outputs/crtcs before entering KMS mode */ + drm_helper_disable_unused_functions(cdev->dev); drm_fb_helper_initial_config(&gfbdev->helper, bpp_sel); return 0; diff --git a/drivers/gpu/drm/cirrus/cirrus_main.c b/drivers/gpu/drm/cirrus/cirrus_main.c index 6a9b12e88d46..35cbae827771 100644 --- a/drivers/gpu/drm/cirrus/cirrus_main.c +++ b/drivers/gpu/drm/cirrus/cirrus_main.c @@ -23,16 +23,8 @@ static void cirrus_user_framebuffer_destroy(struct drm_framebuffer *fb) kfree(fb); } -static int cirrus_user_framebuffer_create_handle(struct drm_framebuffer *fb, - struct drm_file *file_priv, - unsigned int *handle) -{ - return 0; -} - static const struct drm_framebuffer_funcs cirrus_fb_funcs = { .destroy = cirrus_user_framebuffer_destroy, - .create_handle = cirrus_user_framebuffer_create_handle, }; int cirrus_framebuffer_init(struct drm_device *dev, @@ -42,13 +34,13 @@ int cirrus_framebuffer_init(struct drm_device *dev, { int ret; + drm_helper_mode_fill_fb_struct(&gfb->base, mode_cmd); + gfb->obj = obj; ret = drm_framebuffer_init(dev, &gfb->base, &cirrus_fb_funcs); if (ret) { DRM_ERROR("drm_framebuffer_init failed: %d\n", ret); return ret; } - drm_helper_mode_fill_fb_struct(&gfb->base, mode_cmd); - gfb->obj = obj; return 0; } diff --git a/drivers/gpu/drm/drm_crtc.c b/drivers/gpu/drm/drm_crtc.c index f2d667b8bee2..781aef524b3b 100644 --- a/drivers/gpu/drm/drm_crtc.c +++ b/drivers/gpu/drm/drm_crtc.c @@ -37,6 +37,54 @@ #include <drm/drm_edid.h> #include <drm/drm_fourcc.h> +/** + * drm_modeset_lock_all - take all modeset locks + * @dev: drm device + * + * This function takes all modeset locks, suitable where a more fine-grained + * scheme isn't (yet) implemented. + */ +void drm_modeset_lock_all(struct drm_device *dev) +{ + struct drm_crtc *crtc; + + mutex_lock(&dev->mode_config.mutex); + + list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) + mutex_lock_nest_lock(&crtc->mutex, &dev->mode_config.mutex); +} +EXPORT_SYMBOL(drm_modeset_lock_all); + +/** + * drm_modeset_unlock_all - drop all modeset locks + * @dev: device + */ +void drm_modeset_unlock_all(struct drm_device *dev) +{ + struct drm_crtc *crtc; + + list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) + mutex_unlock(&crtc->mutex); + + mutex_unlock(&dev->mode_config.mutex); +} +EXPORT_SYMBOL(drm_modeset_unlock_all); + +/** + * drm_warn_on_modeset_not_all_locked - check that all modeset locks are locked + * @dev: device + */ +void drm_warn_on_modeset_not_all_locked(struct drm_device *dev) +{ + struct drm_crtc *crtc; + + list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) + WARN_ON(!mutex_is_locked(&crtc->mutex)); + + WARN_ON(!mutex_is_locked(&dev->mode_config.mutex)); +} +EXPORT_SYMBOL(drm_warn_on_modeset_not_all_locked); + /* Avoid boilerplate. I'm tired of typing. */ #define DRM_ENUM_NAME_FN(fnname, list) \ char *fnname(int val) \ @@ -203,12 +251,10 @@ char *drm_get_connector_status_name(enum drm_connector_status status) } /** - * drm_mode_object_get - allocate a new identifier + * drm_mode_object_get - allocate a new modeset identifier * @dev: DRM device - * @ptr: object pointer, used to generate unique ID - * @type: object type - * - * LOCKING: + * @obj: object pointer, used to generate unique ID + * @obj_type: object type * * Create a unique identifier based on @ptr in @dev's identifier space. Used * for tracking modes, CRTCs and connectors. @@ -231,24 +277,27 @@ again: mutex_lock(&dev->mode_config.idr_mutex); ret = idr_get_new_above(&dev->mode_config.crtc_idr, obj, 1, &new_id); + + if (!ret) { + /* + * Set up the object linking under the protection of the idr + * lock so that other users can't see inconsistent state. + */ + obj->id = new_id; + obj->type = obj_type; + } mutex_unlock(&dev->mode_config.idr_mutex); + if (ret == -EAGAIN) goto again; - else if (ret) - return ret; - obj->id = new_id; - obj->type = obj_type; - return 0; + return ret; } /** - * drm_mode_object_put - free an identifer + * drm_mode_object_put - free a modeset identifer * @dev: DRM device - * @id: ID to free - * - * LOCKING: - * Caller must hold DRM mode_config lock. + * @object: object to free * * Free @id from @dev's unique identifier pool. */ @@ -260,11 +309,24 @@ static void drm_mode_object_put(struct drm_device *dev, mutex_unlock(&dev->mode_config.idr_mutex); } +/** + * drm_mode_object_find - look up a drm object with static lifetime + * @dev: drm device + * @id: id of the mode object + * @type: type of the mode object + * + * Note that framebuffers cannot be looked up with this functions - since those + * are reference counted, they need special treatment. + */ struct drm_mode_object *drm_mode_object_find(struct drm_device *dev, uint32_t id, uint32_t type) { struct drm_mode_object *obj = NULL; + /* Framebuffers are reference counted and need their own lookup + * function.*/ + WARN_ON(type == DRM_MODE_OBJECT_FB); + mutex_lock(&dev->mode_config.idr_mutex); obj = idr_find(&dev->mode_config.crtc_idr, id); if (!obj || (obj->type != type) || (obj->id != id)) @@ -278,13 +340,18 @@ EXPORT_SYMBOL(drm_mode_object_find); /** * drm_framebuffer_init - initialize a framebuffer * @dev: DRM device - * - * LOCKING: - * Caller must hold mode config lock. + * @fb: framebuffer to be initialized + * @funcs: ... with these functions * * Allocates an ID for the framebuffer's parent mode object, sets its mode * functions & device file and adds it to the master fd list. * + * IMPORTANT: + * This functions publishes the fb and makes it available for concurrent access + * by other users. Which means by this point the fb _must_ be fully set up - + * since all the fb attributes are invariant over its lifetime, no further + * locking but only correct reference counting is required. + * * RETURNS: * Zero on success, error code on failure. */ @@ -293,16 +360,23 @@ int drm_framebuffer_init(struct drm_device *dev, struct drm_framebuffer *fb, { int ret; + mutex_lock(&dev->mode_config.fb_lock); kref_init(&fb->refcount); + INIT_LIST_HEAD(&fb->filp_head); + fb->dev = dev; + fb->funcs = funcs; ret = drm_mode_object_get(dev, &fb->base, DRM_MODE_OBJECT_FB); if (ret) - return ret; + goto out; + + /* Grab the idr reference. */ + drm_framebuffer_reference(fb); - fb->dev = dev; - fb->funcs = funcs; dev->mode_config.num_fb++; list_add(&fb->head, &dev->mode_config.fb_list); +out: + mutex_unlock(&dev->mode_config.fb_lock); return 0; } @@ -315,23 +389,63 @@ static void drm_framebuffer_free(struct kref *kref) fb->funcs->destroy(fb); } +static struct drm_framebuffer *__drm_framebuffer_lookup(struct drm_device *dev, + uint32_t id) +{ + struct drm_mode_object *obj = NULL; + struct drm_framebuffer *fb; + + mutex_lock(&dev->mode_config.idr_mutex); + obj = idr_find(&dev->mode_config.crtc_idr, id); + if (!obj || (obj->type != DRM_MODE_OBJECT_FB) || (obj->id != id)) + fb = NULL; + else + fb = obj_to_fb(obj); + mutex_unlock(&dev->mode_config.idr_mutex); + + return fb; +} + +/** + * drm_framebuffer_lookup - look up a drm framebuffer and grab a reference + * @dev: drm device + * @id: id of the fb object + * + * If successful, this grabs an additional reference to the framebuffer - + * callers need to make sure to eventually unreference the returned framebuffer + * again. + */ +struct drm_framebuffer *drm_framebuffer_lookup(struct drm_device *dev, + uint32_t id) +{ + struct drm_framebuffer *fb; + + mutex_lock(&dev->mode_config.fb_lock); + fb = __drm_framebuffer_lookup(dev, id); + if (fb) + kref_get(&fb->refcount); + mutex_unlock(&dev->mode_config.fb_lock); + + return fb; +} +EXPORT_SYMBOL(drm_framebuffer_lookup); + /** * drm_framebuffer_unreference - unref a framebuffer + * @fb: framebuffer to unref * - * LOCKING: - * Caller must hold mode config lock. + * This functions decrements the fb's refcount and frees it if it drops to zero. */ void drm_framebuffer_unreference(struct drm_framebuffer *fb) { - struct drm_device *dev = fb->dev; DRM_DEBUG("FB ID: %d\n", fb->base.id); - WARN_ON(!mutex_is_locked(&dev->mode_config.mutex)); kref_put(&fb->refcount, drm_framebuffer_free); } EXPORT_SYMBOL(drm_framebuffer_unreference); /** * drm_framebuffer_reference - incr the fb refcnt + * @fb: framebuffer */ void drm_framebuffer_reference(struct drm_framebuffer *fb) { @@ -340,29 +454,74 @@ void drm_framebuffer_reference(struct drm_framebuffer *fb) } EXPORT_SYMBOL(drm_framebuffer_reference); +static void drm_framebuffer_free_bug(struct kref *kref) +{ + BUG(); +} + +static void __drm_framebuffer_unreference(struct drm_framebuffer *fb) +{ + DRM_DEBUG("FB ID: %d\n", fb->base.id); + kref_put(&fb->refcount, drm_framebuffer_free_bug); +} + +/* dev->mode_config.fb_lock must be held! */ +static void __drm_framebuffer_unregister(struct drm_device *dev, + struct drm_framebuffer *fb) +{ + mutex_lock(&dev->mode_config.idr_mutex); + idr_remove(&dev->mode_config.crtc_idr, fb->base.id); + mutex_unlock(&dev->mode_config.idr_mutex); + + fb->base.id = 0; + + __drm_framebuffer_unreference(fb); +} + +/** + * drm_framebuffer_unregister_private - unregister a private fb from the lookup idr + * @fb: fb to unregister + * + * Drivers need to call this when cleaning up driver-private framebuffers, e.g. + * those used for fbdev. Note that the caller must hold a reference of it's own, + * i.e. the object may not be destroyed through this call (since it'll lead to a + * locking inversion). + */ +void drm_framebuffer_unregister_private(struct drm_framebuffer *fb) +{ + struct drm_device *dev = fb->dev; + + mutex_lock(&dev->mode_config.fb_lock); + /* Mark fb as reaped and drop idr ref. */ + __drm_framebuffer_unregister(dev, fb); + mutex_unlock(&dev->mode_config.fb_lock); +} +EXPORT_SYMBOL(drm_framebuffer_unregister_private); + /** * drm_framebuffer_cleanup - remove a framebuffer object * @fb: framebuffer to remove * - * LOCKING: - * Caller must hold mode config lock. + * Cleanup references to a user-created framebuffer. This function is intended + * to be used from the drivers ->destroy callback. + * + * Note that this function does not remove the fb from active usuage - if it is + * still used anywhere, hilarity can ensue since userspace could call getfb on + * the id and get back -EINVAL. Obviously no concern at driver unload time. * - * Scans all the CRTCs in @dev's mode_config. If they're using @fb, removes - * it, setting it to NULL. + * Also, the framebuffer will not be removed from the lookup idr - for + * user-created framebuffers this will happen in in the rmfb ioctl. For + * driver-private objects (e.g. for fbdev) drivers need to explicitly call + * drm_framebuffer_unregister_private. */ void drm_framebuffer_cleanup(struct drm_framebuffer *fb) { struct drm_device *dev = fb->dev; - /* - * This could be moved to drm_framebuffer_remove(), but for - * debugging is nice to keep around the list of fb's that are - * no longer associated w/ a drm_file but are not unreferenced - * yet. (i915 and omapdrm have debugfs files which will show - * this.) - */ - drm_mode_object_put(dev, &fb->base); + + mutex_lock(&dev->mode_config.fb_lock); list_del(&fb->head); dev->mode_config.num_fb--; + mutex_unlock(&dev->mode_config.fb_lock); } EXPORT_SYMBOL(drm_framebuffer_cleanup); @@ -370,11 +529,13 @@ EXPORT_SYMBOL(drm_framebuffer_cleanup); * drm_framebuffer_remove - remove and unreference a framebuffer object * @fb: framebuffer to remove * - * LOCKING: - * Caller must hold mode config lock. - * * Scans all the CRTCs and planes in @dev's mode_config. If they're - * using @fb, removes it, setting it to NULL. + * using @fb, removes it, setting it to NULL. Then drops the reference to the + * passed-in framebuffer. Might take the modeset locks. + * + * Note that this function optimizes the cleanup away if the caller holds the + * last reference to the framebuffer. It is also guaranteed to not take the + * modeset locks in this case. */ void drm_framebuffer_remove(struct drm_framebuffer *fb) { @@ -384,33 +545,53 @@ void drm_framebuffer_remove(struct drm_framebuffer *fb) struct drm_mode_set set; int ret; - /* remove from any CRTC */ - list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { - if (crtc->fb == fb) { - /* should turn off the crtc */ - memset(&set, 0, sizeof(struct drm_mode_set)); - set.crtc = crtc; - set.fb = NULL; - ret = crtc->funcs->set_config(&set); - if (ret) - DRM_ERROR("failed to reset crtc %p when fb was deleted\n", crtc); + WARN_ON(!list_empty(&fb->filp_head)); + + /* + * drm ABI mandates that we remove any deleted framebuffers from active + * useage. But since most sane clients only remove framebuffers they no + * longer need, try to optimize this away. + * + * Since we're holding a reference ourselves, observing a refcount of 1 + * means that we're the last holder and can skip it. Also, the refcount + * can never increase from 1 again, so we don't need any barriers or + * locks. + * + * Note that userspace could try to race with use and instate a new + * usage _after_ we've cleared all current ones. End result will be an + * in-use fb with fb-id == 0. Userspace is allowed to shoot its own foot + * in this manner. + */ + if (atomic_read(&fb->refcount.refcount) > 1) { + drm_modeset_lock_all(dev); + /* remove from any CRTC */ + list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { + if (crtc->fb == fb) { + /* should turn off the crtc */ + memset(&set, 0, sizeof(struct drm_mode_set)); + set.crtc = crtc; + set.fb = NULL; + ret = drm_mode_set_config_internal(&set); + if (ret) + DRM_ERROR("failed to reset crtc %p when fb was deleted\n", crtc); + } } - } - list_for_each_entry(plane, &dev->mode_config.plane_list, head) { - if (plane->fb == fb) { - /* should turn off the crtc */ - ret = plane->funcs->disable_plane(plane); - if (ret) - DRM_ERROR("failed to disable plane with busy fb\n"); - /* disconnect the plane from the fb and crtc: */ - plane->fb = NULL; - plane->crtc = NULL; + list_for_each_entry(plane, &dev->mode_config.plane_list, head) { + if (plane->fb == fb) { + /* should turn off the crtc */ + ret = plane->funcs->disable_plane(plane); + if (ret) + DRM_ERROR("failed to disable plane with busy fb\n"); + /* disconnect the plane from the fb and crtc: */ + __drm_framebuffer_unreference(plane->fb); + plane->fb = NULL; + plane->crtc = NULL; + } } + drm_modeset_unlock_all(dev); } - list_del(&fb->filp_head); - drm_framebuffer_unreference(fb); } EXPORT_SYMBOL(drm_framebuffer_remove); @@ -421,9 +602,6 @@ EXPORT_SYMBOL(drm_framebuffer_remove); * @crtc: CRTC object to init * @funcs: callbacks for the new CRTC * - * LOCKING: - * Takes mode_config lock. - * * Inits a new object created as base part of an driver crtc object. * * RETURNS: @@ -438,7 +616,9 @@ int drm_crtc_init(struct drm_device *dev, struct drm_crtc *crtc, crtc->funcs = funcs; crtc->invert_dimensions = false; - mutex_lock(&dev->mode_config.mutex); + drm_modeset_lock_all(dev); + mutex_init(&crtc->mutex); + mutex_lock_nest_lock(&crtc->mutex, &dev->mode_config.mutex); ret = drm_mode_object_get(dev, &crtc->base, DRM_MODE_OBJECT_CRTC); if (ret) @@ -450,7 +630,7 @@ int drm_crtc_init(struct drm_device *dev, struct drm_crtc *crtc, dev->mode_config.num_crtc++; out: - mutex_unlock(&dev->mode_config.mutex); + drm_modeset_unlock_all(dev); return ret; } @@ -460,9 +640,6 @@ EXPORT_SYMBOL(drm_crtc_init); * drm_crtc_cleanup - Cleans up the core crtc usage. * @crtc: CRTC to cleanup * - * LOCKING: - * Caller must hold mode config lock. - * * Cleanup @crtc. Removes from drm modesetting space * does NOT free object, caller does that. */ @@ -484,9 +661,6 @@ EXPORT_SYMBOL(drm_crtc_cleanup); * @connector: connector the new mode * @mode: mode data * - * LOCKING: - * Caller must hold mode config lock. - * * Add @mode to @connector's mode list for later use. */ void drm_mode_probed_add(struct drm_connector *connector, @@ -501,9 +675,6 @@ EXPORT_SYMBOL(drm_mode_probed_add); * @connector: connector list to modify * @mode: mode to remove * - * LOCKING: - * Caller must hold mode config lock. - * * Remove @mode from @connector's mode list, then free it. */ void drm_mode_remove(struct drm_connector *connector, @@ -519,10 +690,7 @@ EXPORT_SYMBOL(drm_mode_remove); * @dev: DRM device * @connector: the connector to init * @funcs: callbacks for this connector - * @name: user visible name of the connector - * - * LOCKING: - * Takes mode config lock. + * @connector_type: user visible type of the connector * * Initialises a preallocated connector. Connectors should be * subclassed as part of driver connector objects. @@ -537,7 +705,7 @@ int drm_connector_init(struct drm_device *dev, { int ret; - mutex_lock(&dev->mode_config.mutex); + drm_modeset_lock_all(dev); ret = drm_mode_object_get(dev, &connector->base, DRM_MODE_OBJECT_CONNECTOR); if (ret) @@ -567,7 +735,7 @@ int drm_connector_init(struct drm_device *dev, dev->mode_config.dpms_property, 0); out: - mutex_unlock(&dev->mode_config.mutex); + drm_modeset_unlock_all(dev); return ret; } @@ -577,9 +745,6 @@ EXPORT_SYMBOL(drm_connector_init); * drm_connector_cleanup - cleans up an initialised connector * @connector: connector to cleanup * - * LOCKING: - * Takes mode config lock. - * * Cleans up the connector but doesn't free the object. */ void drm_connector_cleanup(struct drm_connector *connector) @@ -596,11 +761,9 @@ void drm_connector_cleanup(struct drm_connector *connector) list_for_each_entry_safe(mode, t, &connector->user_modes, head) drm_mode_remove(connector, mode); - mutex_lock(&dev->mode_config.mutex); drm_mode_object_put(dev, &connector->base); list_del(&connector->head); dev->mode_config.num_connector--; - mutex_unlock(&dev->mode_config.mutex); } EXPORT_SYMBOL(drm_connector_cleanup); @@ -622,7 +785,7 @@ int drm_encoder_init(struct drm_device *dev, { int ret; - mutex_lock(&dev->mode_config.mutex); + drm_modeset_lock_all(dev); ret = drm_mode_object_get(dev, &encoder->base, DRM_MODE_OBJECT_ENCODER); if (ret) @@ -636,7 +799,7 @@ int drm_encoder_init(struct drm_device *dev, dev->mode_config.num_encoder++; out: - mutex_unlock(&dev->mode_config.mutex); + drm_modeset_unlock_all(dev); return ret; } @@ -645,11 +808,11 @@ EXPORT_SYMBOL(drm_encoder_init); void drm_encoder_cleanup(struct drm_encoder *encoder) { struct drm_device *dev = encoder->dev; - mutex_lock(&dev->mode_config.mutex); + drm_modeset_lock_all(dev); drm_mode_object_put(dev, &encoder->base); list_del(&encoder->head); dev->mode_config.num_encoder--; - mutex_unlock(&dev->mode_config.mutex); + drm_modeset_unlock_all(dev); } EXPORT_SYMBOL(drm_encoder_cleanup); @@ -661,7 +824,7 @@ int drm_plane_init(struct drm_device *dev, struct drm_plane *plane, { int ret; - mutex_lock(&dev->mode_config.mutex); + drm_modeset_lock_all(dev); ret = drm_mode_object_get(dev, &plane->base, DRM_MODE_OBJECT_PLANE); if (ret) @@ -695,7 +858,7 @@ int drm_plane_init(struct drm_device *dev, struct drm_plane *plane, } out: - mutex_unlock(&dev->mode_config.mutex); + drm_modeset_unlock_all(dev); return ret; } @@ -705,7 +868,7 @@ void drm_plane_cleanup(struct drm_plane *plane) { struct drm_device *dev = plane->dev; - mutex_lock(&dev->mode_config.mutex); + drm_modeset_lock_all(dev); kfree(plane->format_types); drm_mode_object_put(dev, &plane->base); /* if not added to a list, it must be a private plane */ @@ -713,7 +876,7 @@ void drm_plane_cleanup(struct drm_plane *plane) list_del(&plane->head); dev->mode_config.num_plane--; } - mutex_unlock(&dev->mode_config.mutex); + drm_modeset_unlock_all(dev); } EXPORT_SYMBOL(drm_plane_cleanup); @@ -721,9 +884,6 @@ EXPORT_SYMBOL(drm_plane_cleanup); * drm_mode_create - create a new display mode * @dev: DRM device * - * LOCKING: - * Caller must hold DRM mode_config lock. - * * Create a new drm_display_mode, give it an ID, and return it. * * RETURNS: @@ -751,9 +911,6 @@ EXPORT_SYMBOL(drm_mode_create); * @dev: DRM device * @mode: mode to remove * - * LOCKING: - * Caller must hold mode config lock. - * * Free @mode's unique identifier, then free it. */ void drm_mode_destroy(struct drm_device *dev, struct drm_display_mode *mode) @@ -978,16 +1135,19 @@ EXPORT_SYMBOL(drm_mode_create_dirty_info_property); * drm_mode_config_init - initialize DRM mode_configuration structure * @dev: DRM device * - * LOCKING: - * None, should happen single threaded at init time. - * * Initialize @dev's mode_config structure, used for tracking the graphics * configuration of @dev. + * + * Since this initializes the modeset locks, no locking is possible. Which is no + * problem, since this should happen single threaded at init time. It is the + * driver's problem to ensure this guarantee. + * */ void drm_mode_config_init(struct drm_device *dev) { mutex_init(&dev->mode_config.mutex); mutex_init(&dev->mode_config.idr_mutex); + mutex_init(&dev->mode_config.fb_lock); INIT_LIST_HEAD(&dev->mode_config.fb_list); INIT_LIST_HEAD(&dev->mode_config.crtc_list); INIT_LIST_HEAD(&dev->mode_config.connector_list); @@ -997,9 +1157,9 @@ void drm_mode_config_init(struct drm_device *dev) INIT_LIST_HEAD(&dev->mode_config.plane_list); idr_init(&dev->mode_config.crtc_idr); - mutex_lock(&dev->mode_config.mutex); + drm_modeset_lock_all(dev); drm_mode_create_standard_connector_properties(dev); - mutex_unlock(&dev->mode_config.mutex); + drm_modeset_unlock_all(dev); /* Just to be sure */ dev->mode_config.num_fb = 0; @@ -1057,12 +1217,13 @@ EXPORT_SYMBOL(drm_mode_group_init_legacy_group); * drm_mode_config_cleanup - free up DRM mode_config info * @dev: DRM device * - * LOCKING: - * Caller must hold mode config lock. - * * Free up all the connectors and CRTCs associated with this DRM device, then * free up the framebuffers and associated buffer objects. * + * Note that since this /should/ happen single-threaded at driver/device + * teardown time, no locking is required. It's the driver's job to ensure that + * this guarantee actually holds true. + * * FIXME: cleanup any dangling user buffer objects too */ void drm_mode_config_cleanup(struct drm_device *dev) @@ -1089,6 +1250,15 @@ void drm_mode_config_cleanup(struct drm_device *dev) drm_property_destroy(dev, property); } + /* + * Single-threaded teardown context, so it's not required to grab the + * fb_lock to protect against concurrent fb_list access. Contrary, it + * would actually deadlock with the drm_framebuffer_cleanup function. + * + * Also, if there are any framebuffers left, that's a driver leak now, + * so politely WARN about this. + */ + WARN_ON(!list_empty(&dev->mode_config.fb_list)); list_for_each_entry_safe(fb, fbt, &dev->mode_config.fb_list, head) { drm_framebuffer_remove(fb); } @@ -1112,9 +1282,6 @@ EXPORT_SYMBOL(drm_mode_config_cleanup); * @out: drm_mode_modeinfo struct to return to the user * @in: drm_display_mode to use * - * LOCKING: - * None. - * * Convert a drm_display_mode into a drm_mode_modeinfo structure to return to * the user. */ @@ -1151,9 +1318,6 @@ static void drm_crtc_convert_to_umode(struct drm_mode_modeinfo *out, * @out: drm_display_mode to return to the user * @in: drm_mode_modeinfo to use * - * LOCKING: - * None. - * * Convert a drm_mode_modeinfo into a drm_display_mode structure to return to * the caller. * @@ -1188,13 +1352,9 @@ static int drm_crtc_convert_umode(struct drm_display_mode *out, /** * drm_mode_getresources - get graphics configuration - * @inode: inode from the ioctl - * @filp: file * from the ioctl - * @cmd: cmd from ioctl - * @arg: arg from ioctl - * - * LOCKING: - * Takes mode config lock. + * @dev: drm device for the ioctl + * @data: data pointer for the ioctl + * @file_priv: drm file for the ioctl call * * Construct a set of configuration description structures and return * them to the user, including CRTC, connector and framebuffer configuration. @@ -1228,8 +1388,8 @@ int drm_mode_getresources(struct drm_device *dev, void *data, if (!drm_core_check_feature(dev, DRIVER_MODESET)) return -EINVAL; - mutex_lock(&dev->mode_config.mutex); + mutex_lock(&file_priv->fbs_lock); /* * For the non-control nodes we need to limit the list of resources * by IDs in the group list for this node @@ -1237,6 +1397,23 @@ int drm_mode_getresources(struct drm_device *dev, void *data, list_for_each(lh, &file_priv->fbs) fb_count++; + /* handle this in 4 parts */ + /* FBs */ + if (card_res->count_fbs >= fb_count) { + copied = 0; + fb_id = (uint32_t __user *)(unsigned long)card_res->fb_id_ptr; + list_for_each_entry(fb, &file_priv->fbs, filp_head) { + if (put_user(fb->base.id, fb_id + copied)) { + mutex_unlock(&file_priv->fbs_lock); + return -EFAULT; + } + copied++; + } + } + card_res->count_fbs = fb_count; + mutex_unlock(&file_priv->fbs_lock); + + drm_modeset_lock_all(dev); mode_group = &file_priv->master->minor->mode_group; if (file_priv->master->minor->type == DRM_MINOR_CONTROL) { @@ -1260,21 +1437,6 @@ int drm_mode_getresources(struct drm_device *dev, void *data, card_res->max_width = dev->mode_config.max_width; card_res->min_width = dev->mode_config.min_width; - /* handle this in 4 parts */ - /* FBs */ - if (card_res->count_fbs >= fb_count) { - copied = 0; - fb_id = (uint32_t __user *)(unsigned long)card_res->fb_id_ptr; - list_for_each_entry(fb, &file_priv->fbs, filp_head) { - if (put_user(fb->base.id, fb_id + copied)) { - ret = -EFAULT; - goto out; - } - copied++; - } - } - card_res->count_fbs = fb_count; - /* CRTCs */ if (card_res->count_crtcs >= crtc_count) { copied = 0; @@ -1370,19 +1532,15 @@ int drm_mode_getresources(struct drm_device *dev, void *data, card_res->count_connectors, card_res->count_encoders); out: - mutex_unlock(&dev->mode_config.mutex); + drm_modeset_unlock_all(dev); return ret; } /** * drm_mode_getcrtc - get CRTC configuration - * @inode: inode from the ioctl - * @filp: file * from the ioctl - * @cmd: cmd from ioctl - * @arg: arg from ioctl - * - * LOCKING: - * Takes mode config lock. + * @dev: drm device for the ioctl + * @data: data pointer for the ioctl + * @file_priv: drm file for the ioctl call * * Construct a CRTC configuration structure to return to the user. * @@ -1402,7 +1560,7 @@ int drm_mode_getcrtc(struct drm_device *dev, if (!drm_core_check_feature(dev, DRIVER_MODESET)) return -EINVAL; - mutex_lock(&dev->mode_config.mutex); + drm_modeset_lock_all(dev); obj = drm_mode_object_find(dev, crtc_resp->crtc_id, DRM_MODE_OBJECT_CRTC); @@ -1430,19 +1588,15 @@ int drm_mode_getcrtc(struct drm_device *dev, } out: - mutex_unlock(&dev->mode_config.mutex); + drm_modeset_unlock_all(dev); return ret; } /** * drm_mode_getconnector - get connector configuration - * @inode: inode from the ioctl - * @filp: file * from the ioctl - * @cmd: cmd from ioctl - * @arg: arg from ioctl - * - * LOCKING: - * Takes mode config lock. + * @dev: drm device for the ioctl + * @data: data pointer for the ioctl + * @file_priv: drm file for the ioctl call * * Construct a connector configuration structure to return to the user. * @@ -1575,6 +1729,7 @@ int drm_mode_getconnector(struct drm_device *dev, void *data, out: mutex_unlock(&dev->mode_config.mutex); + return ret; } @@ -1589,7 +1744,7 @@ int drm_mode_getencoder(struct drm_device *dev, void *data, if (!drm_core_check_feature(dev, DRIVER_MODESET)) return -EINVAL; - mutex_lock(&dev->mode_config.mutex); + drm_modeset_lock_all(dev); obj = drm_mode_object_find(dev, enc_resp->encoder_id, DRM_MODE_OBJECT_ENCODER); if (!obj) { @@ -1608,7 +1763,7 @@ int drm_mode_getencoder(struct drm_device *dev, void *data, enc_resp->possible_clones = encoder->possible_clones; out: - mutex_unlock(&dev->mode_config.mutex); + drm_modeset_unlock_all(dev); return ret; } @@ -1618,9 +1773,6 @@ out: * @data: ioctl data * @file_priv: DRM file info * - * LOCKING: - * Takes mode config lock. - * * Return an plane count and set of IDs. */ int drm_mode_getplane_res(struct drm_device *dev, void *data, @@ -1635,7 +1787,7 @@ int drm_mode_getplane_res(struct drm_device *dev, void *data, if (!drm_core_check_feature(dev, DRIVER_MODESET)) return -EINVAL; - mutex_lock(&dev->mode_config.mutex); + drm_modeset_lock_all(dev); config = &dev->mode_config; /* @@ -1657,7 +1809,7 @@ int drm_mode_getplane_res(struct drm_device *dev, void *data, plane_resp->count_planes = config->num_plane; out: - mutex_unlock(&dev->mode_config.mutex); + drm_modeset_unlock_all(dev); return ret; } @@ -1667,9 +1819,6 @@ out: * @data: ioctl data * @file_priv: DRM file info * - * LOCKING: - * Takes mode config lock. - * * Return plane info, including formats supported, gamma size, any * current fb, etc. */ @@ -1685,7 +1834,7 @@ int drm_mode_getplane(struct drm_device *dev, void *data, if (!drm_core_check_feature(dev, DRIVER_MODESET)) return -EINVAL; - mutex_lock(&dev->mode_config.mutex); + drm_modeset_lock_all(dev); obj = drm_mode_object_find(dev, plane_resp->plane_id, DRM_MODE_OBJECT_PLANE); if (!obj) { @@ -1725,7 +1874,7 @@ int drm_mode_getplane(struct drm_device *dev, void *data, plane_resp->count_format_types = plane->format_count; out: - mutex_unlock(&dev->mode_config.mutex); + drm_modeset_unlock_all(dev); return ret; } @@ -1733,10 +1882,7 @@ out: * drm_mode_setplane - set up or tear down an plane * @dev: DRM device * @data: ioctl data* - * @file_prive: DRM file info - * - * LOCKING: - * Takes mode config lock. + * @file_priv: DRM file info * * Set plane info, including placement, fb, scaling, and other factors. * Or pass a NULL fb to disable. @@ -1748,7 +1894,7 @@ int drm_mode_setplane(struct drm_device *dev, void *data, struct drm_mode_object *obj; struct drm_plane *plane; struct drm_crtc *crtc; - struct drm_framebuffer *fb; + struct drm_framebuffer *fb = NULL, *old_fb = NULL; int ret = 0; unsigned int fb_width, fb_height; int i; @@ -1756,8 +1902,6 @@ int drm_mode_setplane(struct drm_device *dev, void *data, if (!drm_core_check_feature(dev, DRIVER_MODESET)) return -EINVAL; - mutex_lock(&dev->mode_config.mutex); - /* * First, find the plane, crtc, and fb objects. If not available, * we don't bother to call the driver. @@ -1767,16 +1911,18 @@ int drm_mode_setplane(struct drm_device *dev, void *data, if (!obj) { DRM_DEBUG_KMS("Unknown plane ID %d\n", plane_req->plane_id); - ret = -ENOENT; - goto out; + return -ENOENT; } plane = obj_to_plane(obj); /* No fb means shut it down */ if (!plane_req->fb_id) { + drm_modeset_lock_all(dev); + old_fb = plane->fb; plane->funcs->disable_plane(plane); plane->crtc = NULL; plane->fb = NULL; + drm_modeset_unlock_all(dev); goto out; } @@ -1790,15 +1936,13 @@ int drm_mode_setplane(struct drm_device *dev, void *data, } crtc = obj_to_crtc(obj); - obj = drm_mode_object_find(dev, plane_req->fb_id, - DRM_MODE_OBJECT_FB); - if (!obj) { + fb = drm_framebuffer_lookup(dev, plane_req->fb_id); + if (!fb) { DRM_DEBUG_KMS("Unknown framebuffer ID %d\n", plane_req->fb_id); ret = -ENOENT; goto out; } - fb = obj_to_fb(obj); /* Check whether this plane supports the fb pixel format. */ for (i = 0; i < plane->format_count; i++) @@ -1844,31 +1988,62 @@ int drm_mode_setplane(struct drm_device *dev, void *data, goto out; } + drm_modeset_lock_all(dev); ret = plane->funcs->update_plane(plane, crtc, fb, plane_req->crtc_x, plane_req->crtc_y, plane_req->crtc_w, plane_req->crtc_h, plane_req->src_x, plane_req->src_y, plane_req->src_w, plane_req->src_h); if (!ret) { + old_fb = plane->fb; plane->crtc = crtc; plane->fb = fb; + fb = NULL; } + drm_modeset_unlock_all(dev); out: - mutex_unlock(&dev->mode_config.mutex); + if (fb) + drm_framebuffer_unreference(fb); + if (old_fb) + drm_framebuffer_unreference(old_fb); return ret; } /** - * drm_mode_setcrtc - set CRTC configuration - * @inode: inode from the ioctl - * @filp: file * from the ioctl - * @cmd: cmd from ioctl - * @arg: arg from ioctl + * drm_mode_set_config_internal - helper to call ->set_config + * @set: modeset config to set * - * LOCKING: - * Takes mode config lock. + * This is a little helper to wrap internal calls to the ->set_config driver + * interface. The only thing it adds is correct refcounting dance. + */ +int drm_mode_set_config_internal(struct drm_mode_set *set) +{ + struct drm_crtc *crtc = set->crtc; + struct drm_framebuffer *fb, *old_fb; + int ret; + + old_fb = crtc->fb; + fb = set->fb; + + ret = crtc->funcs->set_config(set); + if (ret == 0) { + if (old_fb) + drm_framebuffer_unreference(old_fb); + if (fb) + drm_framebuffer_reference(fb); + } + + return ret; +} +EXPORT_SYMBOL(drm_mode_set_config_internal); + +/** + * drm_mode_setcrtc - set CRTC configuration + * @dev: drm device for the ioctl + * @data: data pointer for the ioctl + * @file_priv: drm file for the ioctl call * * Build a new CRTC configuration based on user request. * @@ -1899,7 +2074,7 @@ int drm_mode_setcrtc(struct drm_device *dev, void *data, if (crtc_req->x > INT_MAX || crtc_req->y > INT_MAX) return -ERANGE; - mutex_lock(&dev->mode_config.mutex); + drm_modeset_lock_all(dev); obj = drm_mode_object_find(dev, crtc_req->crtc_id, DRM_MODE_OBJECT_CRTC); if (!obj) { @@ -1921,16 +2096,16 @@ int drm_mode_setcrtc(struct drm_device *dev, void *data, goto out; } fb = crtc->fb; + /* Make refcounting symmetric with the lookup path. */ + drm_framebuffer_reference(fb); } else { - obj = drm_mode_object_find(dev, crtc_req->fb_id, - DRM_MODE_OBJECT_FB); - if (!obj) { + fb = drm_framebuffer_lookup(dev, crtc_req->fb_id); + if (!fb) { DRM_DEBUG_KMS("Unknown FB ID%d\n", crtc_req->fb_id); ret = -EINVAL; goto out; } - fb = obj_to_fb(obj); } mode = drm_mode_create(dev); @@ -2027,12 +2202,15 @@ int drm_mode_setcrtc(struct drm_device *dev, void *data, set.connectors = connector_set; set.num_connectors = crtc_req->count_connectors; set.fb = fb; - ret = crtc->funcs->set_config(&set); + ret = drm_mode_set_config_internal(&set); out: + if (fb) + drm_framebuffer_unreference(fb); + kfree(connector_set); drm_mode_destroy(dev, mode); - mutex_unlock(&dev->mode_config.mutex); + drm_modeset_unlock_all(dev); return ret; } @@ -2050,15 +2228,14 @@ int drm_mode_cursor_ioctl(struct drm_device *dev, if (!req->flags || (~DRM_MODE_CURSOR_FLAGS & req->flags)) return -EINVAL; - mutex_lock(&dev->mode_config.mutex); obj = drm_mode_object_find(dev, req->crtc_id, DRM_MODE_OBJECT_CRTC); if (!obj) { DRM_DEBUG_KMS("Unknown CRTC ID %d\n", req->crtc_id); - ret = -EINVAL; - goto out; + return -EINVAL; } crtc = obj_to_crtc(obj); + mutex_lock(&crtc->mutex); if (req->flags & DRM_MODE_CURSOR_BO) { if (!crtc->funcs->cursor_set) { ret = -ENXIO; @@ -2078,7 +2255,8 @@ int drm_mode_cursor_ioctl(struct drm_device *dev, } } out: - mutex_unlock(&dev->mode_config.mutex); + mutex_unlock(&crtc->mutex); + return ret; } @@ -2089,7 +2267,7 @@ uint32_t drm_mode_legacy_fb_format(uint32_t bpp, uint32_t depth) switch (bpp) { case 8: - fmt = DRM_FORMAT_RGB332; + fmt = DRM_FORMAT_C8; break; case 16: if (depth == 15) @@ -2120,13 +2298,9 @@ EXPORT_SYMBOL(drm_mode_legacy_fb_format); /** * drm_mode_addfb - add an FB to the graphics configuration - * @inode: inode from the ioctl - * @filp: file * from the ioctl - * @cmd: cmd from ioctl - * @arg: arg from ioctl - * - * LOCKING: - * Takes mode config lock. + * @dev: drm device for the ioctl + * @data: data pointer for the ioctl + * @file_priv: drm file for the ioctl call * * Add a new FB to the specified CRTC, given a user request. * @@ -2161,24 +2335,19 @@ int drm_mode_addfb(struct drm_device *dev, if ((config->min_height > r.height) || (r.height > config->max_height)) return -EINVAL; - mutex_lock(&dev->mode_config.mutex); - - /* TODO check buffer is sufficiently large */ - /* TODO setup destructor callback */ - fb = dev->mode_config.funcs->fb_create(dev, file_priv, &r); if (IS_ERR(fb)) { DRM_DEBUG_KMS("could not create framebuffer\n"); - ret = PTR_ERR(fb); - goto out; + drm_modeset_unlock_all(dev); + return PTR_ERR(fb); } + mutex_lock(&file_priv->fbs_lock); or->fb_id = fb->base.id; list_add(&fb->filp_head, &file_priv->fbs); DRM_DEBUG_KMS("[FB:%d]\n", fb->base.id); + mutex_unlock(&file_priv->fbs_lock); -out: - mutex_unlock(&dev->mode_config.mutex); return ret; } @@ -2304,13 +2473,9 @@ static int framebuffer_check(const struct drm_mode_fb_cmd2 *r) /** * drm_mode_addfb2 - add an FB to the graphics configuration - * @inode: inode from the ioctl - * @filp: file * from the ioctl - * @cmd: cmd from ioctl - * @arg: arg from ioctl - * - * LOCKING: - * Takes mode config lock. + * @dev: drm device for the ioctl + * @data: data pointer for the ioctl + * @file_priv: drm file for the ioctl call * * Add a new FB to the specified CRTC, given a user request with format. * @@ -2350,33 +2515,28 @@ int drm_mode_addfb2(struct drm_device *dev, if (ret) return ret; - mutex_lock(&dev->mode_config.mutex); - fb = dev->mode_config.funcs->fb_create(dev, file_priv, r); if (IS_ERR(fb)) { DRM_DEBUG_KMS("could not create framebuffer\n"); - ret = PTR_ERR(fb); - goto out; + drm_modeset_unlock_all(dev); + return PTR_ERR(fb); } + mutex_lock(&file_priv->fbs_lock); r->fb_id = fb->base.id; list_add(&fb->filp_head, &file_priv->fbs); DRM_DEBUG_KMS("[FB:%d]\n", fb->base.id); + mutex_unlock(&file_priv->fbs_lock); + -out: - mutex_unlock(&dev->mode_config.mutex); return ret; } /** * drm_mode_rmfb - remove an FB from the configuration - * @inode: inode from the ioctl - * @filp: file * from the ioctl - * @cmd: cmd from ioctl - * @arg: arg from ioctl - * - * LOCKING: - * Takes mode config lock. + * @dev: drm device for the ioctl + * @data: data pointer for the ioctl + * @file_priv: drm file for the ioctl call * * Remove the FB specified by the user. * @@ -2388,50 +2548,49 @@ out: int drm_mode_rmfb(struct drm_device *dev, void *data, struct drm_file *file_priv) { - struct drm_mode_object *obj; struct drm_framebuffer *fb = NULL; struct drm_framebuffer *fbl = NULL; uint32_t *id = data; - int ret = 0; int found = 0; if (!drm_core_check_feature(dev, DRIVER_MODESET)) return -EINVAL; - mutex_lock(&dev->mode_config.mutex); - obj = drm_mode_object_find(dev, *id, DRM_MODE_OBJECT_FB); - /* TODO check that we really get a framebuffer back. */ - if (!obj) { - ret = -EINVAL; - goto out; - } - fb = obj_to_fb(obj); + mutex_lock(&file_priv->fbs_lock); + mutex_lock(&dev->mode_config.fb_lock); + fb = __drm_framebuffer_lookup(dev, *id); + if (!fb) + goto fail_lookup; list_for_each_entry(fbl, &file_priv->fbs, filp_head) if (fb == fbl) found = 1; + if (!found) + goto fail_lookup; - if (!found) { - ret = -EINVAL; - goto out; - } + /* Mark fb as reaped, we still have a ref from fpriv->fbs. */ + __drm_framebuffer_unregister(dev, fb); + + list_del_init(&fb->filp_head); + mutex_unlock(&dev->mode_config.fb_lock); + mutex_unlock(&file_priv->fbs_lock); drm_framebuffer_remove(fb); -out: - mutex_unlock(&dev->mode_config.mutex); - return ret; + return 0; + +fail_lookup: + mutex_unlock(&dev->mode_config.fb_lock); + mutex_unlock(&file_priv->fbs_lock); + + return -EINVAL; } /** * drm_mode_getfb - get FB info - * @inode: inode from the ioctl - * @filp: file * from the ioctl - * @cmd: cmd from ioctl - * @arg: arg from ioctl - * - * LOCKING: - * Takes mode config lock. + * @dev: drm device for the ioctl + * @data: data pointer for the ioctl + * @file_priv: drm file for the ioctl call * * Lookup the FB given its ID and return info about it. * @@ -2444,30 +2603,28 @@ int drm_mode_getfb(struct drm_device *dev, void *data, struct drm_file *file_priv) { struct drm_mode_fb_cmd *r = data; - struct drm_mode_object *obj; struct drm_framebuffer *fb; - int ret = 0; + int ret; if (!drm_core_check_feature(dev, DRIVER_MODESET)) return -EINVAL; - mutex_lock(&dev->mode_config.mutex); - obj = drm_mode_object_find(dev, r->fb_id, DRM_MODE_OBJECT_FB); - if (!obj) { - ret = -EINVAL; - goto out; - } - fb = obj_to_fb(obj); + fb = drm_framebuffer_lookup(dev, r->fb_id); + if (!fb) + return -EINVAL; r->height = fb->height; r->width = fb->width; r->depth = fb->depth; r->bpp = fb->bits_per_pixel; r->pitch = fb->pitches[0]; - fb->funcs->create_handle(fb, file_priv, &r->handle); + if (fb->funcs->create_handle) + ret = fb->funcs->create_handle(fb, file_priv, &r->handle); + else + ret = -ENODEV; + + drm_framebuffer_unreference(fb); -out: - mutex_unlock(&dev->mode_config.mutex); return ret; } @@ -2477,7 +2634,6 @@ int drm_mode_dirtyfb_ioctl(struct drm_device *dev, struct drm_clip_rect __user *clips_ptr; struct drm_clip_rect *clips = NULL; struct drm_mode_fb_dirty_cmd *r = data; - struct drm_mode_object *obj; struct drm_framebuffer *fb; unsigned flags; int num_clips; @@ -2486,13 +2642,9 @@ int drm_mode_dirtyfb_ioctl(struct drm_device *dev, if (!drm_core_check_feature(dev, DRIVER_MODESET)) return -EINVAL; - mutex_lock(&dev->mode_config.mutex); - obj = drm_mode_object_find(dev, r->fb_id, DRM_MODE_OBJECT_FB); - if (!obj) { - ret = -EINVAL; - goto out_err1; - } - fb = obj_to_fb(obj); + fb = drm_framebuffer_lookup(dev, r->fb_id); + if (!fb) + return -EINVAL; num_clips = r->num_clips; clips_ptr = (struct drm_clip_rect __user *)(unsigned long)r->clips_ptr; @@ -2530,27 +2682,26 @@ int drm_mode_dirtyfb_ioctl(struct drm_device *dev, } if (fb->funcs->dirty) { + drm_modeset_lock_all(dev); ret = fb->funcs->dirty(fb, file_priv, flags, r->color, clips, num_clips); + drm_modeset_unlock_all(dev); } else { ret = -ENOSYS; - goto out_err2; } out_err2: kfree(clips); out_err1: - mutex_unlock(&dev->mode_config.mutex); + drm_framebuffer_unreference(fb); + return ret; } /** * drm_fb_release - remove and free the FBs on this file - * @filp: file * from the ioctl - * - * LOCKING: - * Takes mode config lock. + * @priv: drm file for the ioctl * * Destroy all the FBs associated with @filp. * @@ -2564,11 +2715,20 @@ void drm_fb_release(struct drm_file *priv) struct drm_device *dev = priv->minor->dev; struct drm_framebuffer *fb, *tfb; - mutex_lock(&dev->mode_config.mutex); + mutex_lock(&priv->fbs_lock); list_for_each_entry_safe(fb, tfb, &priv->fbs, filp_head) { + + mutex_lock(&dev->mode_config.fb_lock); + /* Mark fb as reaped, we still have a ref from fpriv->fbs. */ + __drm_framebuffer_unregister(dev, fb); + mutex_unlock(&dev->mode_config.fb_lock); + + list_del_init(&fb->filp_head); + + /* This will also drop the fpriv->fbs reference. */ drm_framebuffer_remove(fb); } - mutex_unlock(&dev->mode_config.mutex); + mutex_unlock(&priv->fbs_lock); } /** @@ -2660,10 +2820,9 @@ EXPORT_SYMBOL(drm_mode_detachmode_crtc); /** * drm_fb_attachmode - Attach a user mode to an connector - * @inode: inode from the ioctl - * @filp: file * from the ioctl - * @cmd: cmd from ioctl - * @arg: arg from ioctl + * @dev: drm device for the ioctl + * @data: data pointer for the ioctl + * @file_priv: drm file for the ioctl call * * This attaches a user specified mode to an connector. * Called by the user via ioctl. @@ -2684,7 +2843,7 @@ int drm_mode_attachmode_ioctl(struct drm_device *dev, if (!drm_core_check_feature(dev, DRIVER_MODESET)) return -EINVAL; - mutex_lock(&dev->mode_config.mutex); + drm_modeset_lock_all(dev); obj = drm_mode_object_find(dev, mode_cmd->connector_id, DRM_MODE_OBJECT_CONNECTOR); if (!obj) { @@ -2708,17 +2867,16 @@ int drm_mode_attachmode_ioctl(struct drm_device *dev, drm_mode_attachmode(dev, connector, mode); out: - mutex_unlock(&dev->mode_config.mutex); + drm_modeset_unlock_all(dev); return ret; } /** * drm_fb_detachmode - Detach a user specified mode from an connector - * @inode: inode from the ioctl - * @filp: file * from the ioctl - * @cmd: cmd from ioctl - * @arg: arg from ioctl + * @dev: drm device for the ioctl + * @data: data pointer for the ioctl + * @file_priv: drm file for the ioctl call * * Called by the user via ioctl. * @@ -2738,7 +2896,7 @@ int drm_mode_detachmode_ioctl(struct drm_device *dev, if (!drm_core_check_feature(dev, DRIVER_MODESET)) return -EINVAL; - mutex_lock(&dev->mode_config.mutex); + drm_modeset_lock_all(dev); obj = drm_mode_object_find(dev, mode_cmd->connector_id, DRM_MODE_OBJECT_CONNECTOR); if (!obj) { @@ -2755,7 +2913,7 @@ int drm_mode_detachmode_ioctl(struct drm_device *dev, ret = drm_mode_detachmode(dev, connector, &mode); out: - mutex_unlock(&dev->mode_config.mutex); + drm_modeset_unlock_all(dev); return ret; } @@ -3001,7 +3159,7 @@ int drm_mode_getproperty_ioctl(struct drm_device *dev, if (!drm_core_check_feature(dev, DRIVER_MODESET)) return -EINVAL; - mutex_lock(&dev->mode_config.mutex); + drm_modeset_lock_all(dev); obj = drm_mode_object_find(dev, out_resp->prop_id, DRM_MODE_OBJECT_PROPERTY); if (!obj) { ret = -EINVAL; @@ -3079,7 +3237,7 @@ int drm_mode_getproperty_ioctl(struct drm_device *dev, out_resp->count_enum_blobs = blob_count; } done: - mutex_unlock(&dev->mode_config.mutex); + drm_modeset_unlock_all(dev); return ret; } @@ -3130,7 +3288,7 @@ int drm_mode_getblob_ioctl(struct drm_device *dev, if (!drm_core_check_feature(dev, DRIVER_MODESET)) return -EINVAL; - mutex_lock(&dev->mode_config.mutex); + drm_modeset_lock_all(dev); obj = drm_mode_object_find(dev, out_resp->blob_id, DRM_MODE_OBJECT_BLOB); if (!obj) { ret = -EINVAL; @@ -3148,7 +3306,7 @@ int drm_mode_getblob_ioctl(struct drm_device *dev, out_resp->length = blob->length; done: - mutex_unlock(&dev->mode_config.mutex); + drm_modeset_unlock_all(dev); return ret; } @@ -3290,7 +3448,7 @@ int drm_mode_obj_get_properties_ioctl(struct drm_device *dev, void *data, if (!drm_core_check_feature(dev, DRIVER_MODESET)) return -EINVAL; - mutex_lock(&dev->mode_config.mutex); + drm_modeset_lock_all(dev); obj = drm_mode_object_find(dev, arg->obj_id, arg->obj_type); if (!obj) { @@ -3327,7 +3485,7 @@ int drm_mode_obj_get_properties_ioctl(struct drm_device *dev, void *data, } arg->count_props = props_count; out: - mutex_unlock(&dev->mode_config.mutex); + drm_modeset_unlock_all(dev); return ret; } @@ -3344,7 +3502,7 @@ int drm_mode_obj_set_property_ioctl(struct drm_device *dev, void *data, if (!drm_core_check_feature(dev, DRIVER_MODESET)) return -EINVAL; - mutex_lock(&dev->mode_config.mutex); + drm_modeset_lock_all(dev); arg_obj = drm_mode_object_find(dev, arg->obj_id, arg->obj_type); if (!arg_obj) @@ -3382,7 +3540,7 @@ int drm_mode_obj_set_property_ioctl(struct drm_device *dev, void *data, } out: - mutex_unlock(&dev->mode_config.mutex); + drm_modeset_unlock_all(dev); return ret; } @@ -3444,7 +3602,7 @@ int drm_mode_gamma_set_ioctl(struct drm_device *dev, if (!drm_core_check_feature(dev, DRIVER_MODESET)) return -EINVAL; - mutex_lock(&dev->mode_config.mutex); + drm_modeset_lock_all(dev); obj = drm_mode_object_find(dev, crtc_lut->crtc_id, DRM_MODE_OBJECT_CRTC); if (!obj) { ret = -EINVAL; @@ -3485,7 +3643,7 @@ int drm_mode_gamma_set_ioctl(struct drm_device *dev, crtc->funcs->gamma_set(crtc, r_base, g_base, b_base, 0, crtc->gamma_size); out: - mutex_unlock(&dev->mode_config.mutex); + drm_modeset_unlock_all(dev); return ret; } @@ -3503,7 +3661,7 @@ int drm_mode_gamma_get_ioctl(struct drm_device *dev, if (!drm_core_check_feature(dev, DRIVER_MODESET)) return -EINVAL; - mutex_lock(&dev->mode_config.mutex); + drm_modeset_lock_all(dev); obj = drm_mode_object_find(dev, crtc_lut->crtc_id, DRM_MODE_OBJECT_CRTC); if (!obj) { ret = -EINVAL; @@ -3536,7 +3694,7 @@ int drm_mode_gamma_get_ioctl(struct drm_device *dev, goto out; } out: - mutex_unlock(&dev->mode_config.mutex); + drm_modeset_unlock_all(dev); return ret; } @@ -3546,7 +3704,7 @@ int drm_mode_page_flip_ioctl(struct drm_device *dev, struct drm_mode_crtc_page_flip *page_flip = data; struct drm_mode_object *obj; struct drm_crtc *crtc; - struct drm_framebuffer *fb; + struct drm_framebuffer *fb = NULL, *old_fb = NULL; struct drm_pending_vblank_event *e = NULL; unsigned long flags; int hdisplay, vdisplay; @@ -3556,12 +3714,12 @@ int drm_mode_page_flip_ioctl(struct drm_device *dev, page_flip->reserved != 0) return -EINVAL; - mutex_lock(&dev->mode_config.mutex); obj = drm_mode_object_find(dev, page_flip->crtc_id, DRM_MODE_OBJECT_CRTC); if (!obj) - goto out; + return -EINVAL; crtc = obj_to_crtc(obj); + mutex_lock(&crtc->mutex); if (crtc->fb == NULL) { /* The framebuffer is currently unbound, presumably * due to a hotplug event, that userspace has not @@ -3574,10 +3732,9 @@ int drm_mode_page_flip_ioctl(struct drm_device *dev, if (crtc->funcs->page_flip == NULL) goto out; - obj = drm_mode_object_find(dev, page_flip->fb_id, DRM_MODE_OBJECT_FB); - if (!obj) + fb = drm_framebuffer_lookup(dev, page_flip->fb_id); + if (!fb) goto out; - fb = obj_to_fb(obj); hdisplay = crtc->mode.hdisplay; vdisplay = crtc->mode.vdisplay; @@ -3623,6 +3780,7 @@ int drm_mode_page_flip_ioctl(struct drm_device *dev, (void (*) (struct drm_pending_event *)) kfree; } + old_fb = crtc->fb; ret = crtc->funcs->page_flip(crtc, fb, e); if (ret) { if (page_flip->flags & DRM_MODE_PAGE_FLIP_EVENT) { @@ -3631,10 +3789,20 @@ int drm_mode_page_flip_ioctl(struct drm_device *dev, spin_unlock_irqrestore(&dev->event_lock, flags); kfree(e); } + /* Keep the old fb, don't unref it. */ + old_fb = NULL; + } else { + /* Unref only the old framebuffer. */ + fb = NULL; } out: - mutex_unlock(&dev->mode_config.mutex); + if (fb) + drm_framebuffer_unreference(fb); + if (old_fb) + drm_framebuffer_unreference(old_fb); + mutex_unlock(&crtc->mutex); + return ret; } @@ -3702,6 +3870,7 @@ void drm_fb_get_bpp_depth(uint32_t format, unsigned int *depth, int *bpp) { switch (format) { + case DRM_FORMAT_C8: case DRM_FORMAT_RGB332: case DRM_FORMAT_BGR233: *depth = 8; diff --git a/drivers/gpu/drm/drm_edid.c b/drivers/gpu/drm/drm_edid.c index 5a3770fbd770..67aa0dd68250 100644 --- a/drivers/gpu/drm/drm_edid.c +++ b/drivers/gpu/drm/drm_edid.c @@ -87,9 +87,6 @@ static struct edid_quirk { int product_id; u32 quirks; } edid_quirk_list[] = { - /* ASUS VW222S */ - { "ACI", 0x22a2, EDID_QUIRK_FORCE_REDUCED_BLANKING }, - /* Acer AL1706 */ { "ACR", 44358, EDID_QUIRK_PREFER_LARGE_60 }, /* Acer F51 */ @@ -357,10 +354,14 @@ drm_do_get_edid(struct drm_connector *connector, struct i2c_adapter *adapter) break; } } - if (i == 4) + + if (i == 4 && print_bad_edid) { dev_warn(connector->dev->dev, "%s: Ignoring invalid EDID block %d.\n", drm_get_connector_name(connector), j); + + connector->bad_edid_counter++; + } } if (valid_extensions != block[0x7e]) { @@ -1483,9 +1484,11 @@ add_detailed_modes(struct drm_connector *connector, struct edid *edid, #define VIDEO_BLOCK 0x02 #define VENDOR_BLOCK 0x03 #define SPEAKER_BLOCK 0x04 +#define VIDEO_CAPABILITY_BLOCK 0x07 #define EDID_BASIC_AUDIO (1 << 6) #define EDID_CEA_YCRCB444 (1 << 5) #define EDID_CEA_YCRCB422 (1 << 4) +#define EDID_CEA_VCDB_QS (1 << 6) /** * Search EDID for CEA extension block. @@ -1902,6 +1905,37 @@ end: EXPORT_SYMBOL(drm_detect_monitor_audio); /** + * drm_rgb_quant_range_selectable - is RGB quantization range selectable? + * + * Check whether the monitor reports the RGB quantization range selection + * as supported. The AVI infoframe can then be used to inform the monitor + * which quantization range (full or limited) is used. + */ +bool drm_rgb_quant_range_selectable(struct edid *edid) +{ + u8 *edid_ext; + int i, start, end; + + edid_ext = drm_find_cea_extension(edid); + if (!edid_ext) + return false; + + if (cea_db_offsets(edid_ext, &start, &end)) + return false; + + for_each_cea_db(edid_ext, i, start, end) { + if (cea_db_tag(&edid_ext[i]) == VIDEO_CAPABILITY_BLOCK && + cea_db_payload_len(&edid_ext[i]) == 2) { + DRM_DEBUG_KMS("CEA VCDB 0x%02x\n", edid_ext[i + 2]); + return edid_ext[i + 2] & EDID_CEA_VCDB_QS; + } + } + + return false; +} +EXPORT_SYMBOL(drm_rgb_quant_range_selectable); + +/** * drm_add_display_info - pull display info out if present * @edid: EDID data * @info: display info (attached to connector) @@ -2020,7 +2054,8 @@ int drm_add_edid_modes(struct drm_connector *connector, struct edid *edid) num_modes += add_cvt_modes(connector, edid); num_modes += add_standard_modes(connector, edid); num_modes += add_established_modes(connector, edid); - num_modes += add_inferred_modes(connector, edid); + if (edid->features & DRM_EDID_FEATURE_DEFAULT_GTF) + num_modes += add_inferred_modes(connector, edid); num_modes += add_cea_modes(connector, edid); if (quirks & (EDID_QUIRK_PREFER_LARGE_60 | EDID_QUIRK_PREFER_LARGE_75)) diff --git a/drivers/gpu/drm/drm_fb_cma_helper.c b/drivers/gpu/drm/drm_fb_cma_helper.c index fd9d0af4d536..1c8549dae99a 100644 --- a/drivers/gpu/drm/drm_fb_cma_helper.c +++ b/drivers/gpu/drm/drm_fb_cma_helper.c @@ -85,6 +85,11 @@ static struct drm_fb_cma *drm_fb_cma_alloc(struct drm_device *dev, if (!fb_cma) return ERR_PTR(-ENOMEM); + drm_helper_mode_fill_fb_struct(&fb_cma->fb, mode_cmd); + + for (i = 0; i < num_planes; i++) + fb_cma->obj[i] = obj[i]; + ret = drm_framebuffer_init(dev, &fb_cma->fb, &drm_fb_cma_funcs); if (ret) { dev_err(dev->dev, "Failed to initalize framebuffer: %d\n", ret); @@ -92,11 +97,6 @@ static struct drm_fb_cma *drm_fb_cma_alloc(struct drm_device *dev, return ERR_PTR(ret); } - drm_helper_mode_fill_fb_struct(&fb_cma->fb, mode_cmd); - - for (i = 0; i < num_planes; i++) - fb_cma->obj[i] = obj[i]; - return fb_cma; } @@ -266,6 +266,7 @@ static int drm_fbdev_cma_create(struct drm_fb_helper *helper, return 0; err_drm_fb_cma_destroy: + drm_framebuffer_unregister_private(fb); drm_fb_cma_destroy(fb); err_framebuffer_release: framebuffer_release(fbi); @@ -274,23 +275,8 @@ err_drm_gem_cma_free_object: return ret; } -static int drm_fbdev_cma_probe(struct drm_fb_helper *helper, - struct drm_fb_helper_surface_size *sizes) -{ - int ret = 0; - - if (!helper->fb) { - ret = drm_fbdev_cma_create(helper, sizes); - if (ret < 0) - return ret; - ret = 1; - } - - return ret; -} - static struct drm_fb_helper_funcs drm_fb_cma_helper_funcs = { - .fb_probe = drm_fbdev_cma_probe, + .fb_probe = drm_fbdev_cma_create, }; /** @@ -332,6 +318,9 @@ struct drm_fbdev_cma *drm_fbdev_cma_init(struct drm_device *dev, } + /* disable all the possible outputs/crtcs before entering KMS mode */ + drm_helper_disable_unused_functions(dev); + ret = drm_fb_helper_initial_config(helper, preferred_bpp); if (ret < 0) { dev_err(dev->dev, "Failed to set inital hw configuration.\n"); @@ -370,8 +359,10 @@ void drm_fbdev_cma_fini(struct drm_fbdev_cma *fbdev_cma) framebuffer_release(info); } - if (fbdev_cma->fb) + if (fbdev_cma->fb) { + drm_framebuffer_unregister_private(&fbdev_cma->fb->fb); drm_fb_cma_destroy(&fbdev_cma->fb->fb); + } drm_fb_helper_fini(&fbdev_cma->fb_helper); kfree(fbdev_cma); @@ -386,8 +377,13 @@ EXPORT_SYMBOL_GPL(drm_fbdev_cma_fini); */ void drm_fbdev_cma_restore_mode(struct drm_fbdev_cma *fbdev_cma) { - if (fbdev_cma) + if (fbdev_cma) { + struct drm_device *dev = fbdev_cma->fb_helper.dev; + + drm_modeset_lock_all(dev); drm_fb_helper_restore_fbdev_mode(&fbdev_cma->fb_helper); + drm_modeset_unlock_all(dev); + } } EXPORT_SYMBOL_GPL(drm_fbdev_cma_restore_mode); diff --git a/drivers/gpu/drm/drm_fb_helper.c b/drivers/gpu/drm/drm_fb_helper.c index 954d175bd7fa..59d6b9bf204b 100644 --- a/drivers/gpu/drm/drm_fb_helper.c +++ b/drivers/gpu/drm/drm_fb_helper.c @@ -52,9 +52,36 @@ static LIST_HEAD(kernel_fb_helper_list); * mode setting driver. They can be used mostly independantely from the crtc * helper functions used by many drivers to implement the kernel mode setting * interfaces. + * + * Initialization is done as a three-step process with drm_fb_helper_init(), + * drm_fb_helper_single_add_all_connectors() and drm_fb_helper_initial_config(). + * Drivers with fancier requirements than the default beheviour can override the + * second step with their own code. Teardown is done with drm_fb_helper_fini(). + * + * At runtime drivers should restore the fbdev console by calling + * drm_fb_helper_restore_fbdev_mode() from their ->lastclose callback. They + * should also notify the fb helper code from updates to the output + * configuration by calling drm_fb_helper_hotplug_event(). For easier + * integration with the output polling code in drm_crtc_helper.c the modeset + * code proves a ->output_poll_changed callback. + * + * All other functions exported by the fb helper library can be used to + * implement the fbdev driver interface by the driver. */ -/* simple single crtc case helper function */ +/** + * drm_fb_helper_single_add_all_connectors() - add all connectors to fbdev + * emulation helper + * @fb_helper: fbdev initialized with drm_fb_helper_init + * + * This functions adds all the available connectors for use with the given + * fb_helper. This is a separate step to allow drivers to freely assign + * connectors to the fbdev, e.g. if some are reserved for special purposes or + * not adequate to be used for the fbcon. + * + * Since this is part of the initial setup before the fbdev is published, no + * locking is required. + */ int drm_fb_helper_single_add_all_connectors(struct drm_fb_helper *fb_helper) { struct drm_device *dev = fb_helper->dev; @@ -163,6 +190,10 @@ static void drm_fb_helper_restore_lut_atomic(struct drm_crtc *crtc) crtc->funcs->gamma_set(crtc, r_base, g_base, b_base, 0, crtc->gamma_size); } +/** + * drm_fb_helper_debug_enter - implementation for ->fb_debug_enter + * @info: fbdev registered by the helper + */ int drm_fb_helper_debug_enter(struct fb_info *info) { struct drm_fb_helper *helper = info->par; @@ -208,6 +239,10 @@ static struct drm_framebuffer *drm_mode_config_fb(struct drm_crtc *crtc) return NULL; } +/** + * drm_fb_helper_debug_leave - implementation for ->fb_debug_leave + * @info: fbdev registered by the helper + */ int drm_fb_helper_debug_leave(struct fb_info *info) { struct drm_fb_helper *helper = info->par; @@ -239,13 +274,24 @@ int drm_fb_helper_debug_leave(struct fb_info *info) } EXPORT_SYMBOL(drm_fb_helper_debug_leave); +/** + * drm_fb_helper_restore_fbdev_mode - restore fbdev configuration + * @fb_helper: fbcon to restore + * + * This should be called from driver's drm ->lastclose callback + * when implementing an fbcon on top of kms using this helper. This ensures that + * the user isn't greeted with a black screen when e.g. X dies. + */ bool drm_fb_helper_restore_fbdev_mode(struct drm_fb_helper *fb_helper) { bool error = false; int i, ret; + + drm_warn_on_modeset_not_all_locked(fb_helper->dev); + for (i = 0; i < fb_helper->crtc_count; i++) { struct drm_mode_set *mode_set = &fb_helper->crtc_info[i].mode_set; - ret = mode_set->crtc->funcs->set_config(mode_set); + ret = drm_mode_set_config_internal(mode_set); if (ret) error = true; } @@ -253,6 +299,10 @@ bool drm_fb_helper_restore_fbdev_mode(struct drm_fb_helper *fb_helper) } EXPORT_SYMBOL(drm_fb_helper_restore_fbdev_mode); +/* + * restore fbcon display for all kms driver's using this helper, used for sysrq + * and panic handling. + */ static bool drm_fb_helper_force_kernel_mode(void) { bool ret, error = false; @@ -272,7 +322,7 @@ static bool drm_fb_helper_force_kernel_mode(void) return error; } -int drm_fb_helper_panic(struct notifier_block *n, unsigned long ununsed, +static int drm_fb_helper_panic(struct notifier_block *n, unsigned long ununsed, void *panic_str) { /* @@ -285,30 +335,36 @@ int drm_fb_helper_panic(struct notifier_block *n, unsigned long ununsed, pr_err("panic occurred, switching back to text console\n"); return drm_fb_helper_force_kernel_mode(); } -EXPORT_SYMBOL(drm_fb_helper_panic); static struct notifier_block paniced = { .notifier_call = drm_fb_helper_panic, }; -/** - * drm_fb_helper_restore - restore the framebuffer console (kernel) config - * - * Restore's the kernel's fbcon mode, used for lastclose & panic paths. - */ -void drm_fb_helper_restore(void) +static bool drm_fb_helper_is_bound(struct drm_fb_helper *fb_helper) { - bool ret; - ret = drm_fb_helper_force_kernel_mode(); - if (ret == true) - DRM_ERROR("Failed to restore crtc configuration\n"); + struct drm_device *dev = fb_helper->dev; + struct drm_crtc *crtc; + int bound = 0, crtcs_bound = 0; + + list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { + if (crtc->fb) + crtcs_bound++; + if (crtc->fb == fb_helper->fb) + bound++; + } + + if (bound < crtcs_bound) + return false; + return true; } -EXPORT_SYMBOL(drm_fb_helper_restore); #ifdef CONFIG_MAGIC_SYSRQ static void drm_fb_helper_restore_work_fn(struct work_struct *ignored) { - drm_fb_helper_restore(); + bool ret; + ret = drm_fb_helper_force_kernel_mode(); + if (ret == true) + DRM_ERROR("Failed to restore crtc configuration\n"); } static DECLARE_WORK(drm_fb_helper_restore_work, drm_fb_helper_restore_work_fn); @@ -335,9 +391,22 @@ static void drm_fb_helper_dpms(struct fb_info *info, int dpms_mode) int i, j; /* + * fbdev->blank can be called from irq context in case of a panic. + * Since we already have our own special panic handler which will + * restore the fbdev console mode completely, just bail out early. + */ + if (oops_in_progress) + return; + + /* * For each CRTC in this fb, turn the connectors on/off. */ - mutex_lock(&dev->mode_config.mutex); + drm_modeset_lock_all(dev); + if (!drm_fb_helper_is_bound(fb_helper)) { + drm_modeset_unlock_all(dev); + return; + } + for (i = 0; i < fb_helper->crtc_count; i++) { crtc = fb_helper->crtc_info[i].mode_set.crtc; @@ -352,9 +421,14 @@ static void drm_fb_helper_dpms(struct fb_info *info, int dpms_mode) dev->mode_config.dpms_property, dpms_mode); } } - mutex_unlock(&dev->mode_config.mutex); + drm_modeset_unlock_all(dev); } +/** + * drm_fb_helper_blank - implementation for ->fb_blank + * @blank: desired blanking state + * @info: fbdev registered by the helper + */ int drm_fb_helper_blank(int blank, struct fb_info *info) { switch (blank) { @@ -398,6 +472,24 @@ static void drm_fb_helper_crtc_free(struct drm_fb_helper *helper) kfree(helper->crtc_info); } +/** + * drm_fb_helper_init - initialize a drm_fb_helper structure + * @dev: drm device + * @fb_helper: driver-allocated fbdev helper structure to initialize + * @crtc_count: maximum number of crtcs to support in this fbdev emulation + * @max_conn_count: max connector count + * + * This allocates the structures for the fbdev helper with the given limits. + * Note that this won't yet touch the hardware (through the driver interfaces) + * nor register the fbdev. This is only done in drm_fb_helper_initial_config() + * to allow driver writes more control over the exact init sequence. + * + * Drivers must set fb_helper->funcs before calling + * drm_fb_helper_initial_config(). + * + * RETURNS: + * Zero if everything went ok, nonzero otherwise. + */ int drm_fb_helper_init(struct drm_device *dev, struct drm_fb_helper *fb_helper, int crtc_count, int max_conn_count) @@ -526,6 +618,11 @@ static int setcolreg(struct drm_crtc *crtc, u16 red, u16 green, return 0; } +/** + * drm_fb_helper_setcmap - implementation for ->fb_setcmap + * @cmap: cmap to set + * @info: fbdev registered by the helper + */ int drm_fb_helper_setcmap(struct fb_cmap *cmap, struct fb_info *info) { struct drm_fb_helper *fb_helper = info->par; @@ -565,6 +662,11 @@ int drm_fb_helper_setcmap(struct fb_cmap *cmap, struct fb_info *info) } EXPORT_SYMBOL(drm_fb_helper_setcmap); +/** + * drm_fb_helper_check_var - implementation for ->fb_check_var + * @var: screeninfo to check + * @info: fbdev registered by the helper + */ int drm_fb_helper_check_var(struct fb_var_screeninfo *var, struct fb_info *info) { @@ -657,13 +759,19 @@ int drm_fb_helper_check_var(struct fb_var_screeninfo *var, } EXPORT_SYMBOL(drm_fb_helper_check_var); -/* this will let fbcon do the mode init */ +/** + * drm_fb_helper_set_par - implementation for ->fb_set_par + * @info: fbdev registered by the helper + * + * This will let fbcon do the mode init and is called at initialization time by + * the fbdev core when registering the driver, and later on through the hotplug + * callback. + */ int drm_fb_helper_set_par(struct fb_info *info) { struct drm_fb_helper *fb_helper = info->par; struct drm_device *dev = fb_helper->dev; struct fb_var_screeninfo *var = &info->var; - struct drm_crtc *crtc; int ret; int i; @@ -672,16 +780,15 @@ int drm_fb_helper_set_par(struct fb_info *info) return -EINVAL; } - mutex_lock(&dev->mode_config.mutex); + drm_modeset_lock_all(dev); for (i = 0; i < fb_helper->crtc_count; i++) { - crtc = fb_helper->crtc_info[i].mode_set.crtc; - ret = crtc->funcs->set_config(&fb_helper->crtc_info[i].mode_set); + ret = drm_mode_set_config_internal(&fb_helper->crtc_info[i].mode_set); if (ret) { - mutex_unlock(&dev->mode_config.mutex); + drm_modeset_unlock_all(dev); return ret; } } - mutex_unlock(&dev->mode_config.mutex); + drm_modeset_unlock_all(dev); if (fb_helper->delayed_hotplug) { fb_helper->delayed_hotplug = false; @@ -691,6 +798,11 @@ int drm_fb_helper_set_par(struct fb_info *info) } EXPORT_SYMBOL(drm_fb_helper_set_par); +/** + * drm_fb_helper_pan_display - implementation for ->fb_pan_display + * @var: updated screen information + * @info: fbdev registered by the helper + */ int drm_fb_helper_pan_display(struct fb_var_screeninfo *var, struct fb_info *info) { @@ -701,7 +813,12 @@ int drm_fb_helper_pan_display(struct fb_var_screeninfo *var, int ret = 0; int i; - mutex_lock(&dev->mode_config.mutex); + drm_modeset_lock_all(dev); + if (!drm_fb_helper_is_bound(fb_helper)) { + drm_modeset_unlock_all(dev); + return -EBUSY; + } + for (i = 0; i < fb_helper->crtc_count; i++) { crtc = fb_helper->crtc_info[i].mode_set.crtc; @@ -711,22 +828,27 @@ int drm_fb_helper_pan_display(struct fb_var_screeninfo *var, modeset->y = var->yoffset; if (modeset->num_connectors) { - ret = crtc->funcs->set_config(modeset); + ret = drm_mode_set_config_internal(modeset); if (!ret) { info->var.xoffset = var->xoffset; info->var.yoffset = var->yoffset; } } } - mutex_unlock(&dev->mode_config.mutex); + drm_modeset_unlock_all(dev); return ret; } EXPORT_SYMBOL(drm_fb_helper_pan_display); -int drm_fb_helper_single_fb_probe(struct drm_fb_helper *fb_helper, - int preferred_bpp) +/* + * Allocates the backing storage and sets up the fbdev info structure through + * the ->fb_probe callback and then registers the fbdev and sets up the panic + * notifier. + */ +static int drm_fb_helper_single_fb_probe(struct drm_fb_helper *fb_helper, + int preferred_bpp) { - int new_fb = 0; + int ret = 0; int crtc_count = 0; int i; struct fb_info *info; @@ -804,27 +926,30 @@ int drm_fb_helper_single_fb_probe(struct drm_fb_helper *fb_helper, } /* push down into drivers */ - new_fb = (*fb_helper->funcs->fb_probe)(fb_helper, &sizes); - if (new_fb < 0) - return new_fb; + ret = (*fb_helper->funcs->fb_probe)(fb_helper, &sizes); + if (ret < 0) + return ret; info = fb_helper->fbdev; - /* set the fb pointer */ + /* + * Set the fb pointer - usually drm_setup_crtcs does this for hotplug + * events, but at init time drm_setup_crtcs needs to be called before + * the fb is allocated (since we need to figure out the desired size of + * the fb before we can allocate it ...). Hence we need to fix things up + * here again. + */ for (i = 0; i < fb_helper->crtc_count; i++) - fb_helper->crtc_info[i].mode_set.fb = fb_helper->fb; + if (fb_helper->crtc_info[i].mode_set.num_connectors) + fb_helper->crtc_info[i].mode_set.fb = fb_helper->fb; - if (new_fb) { - info->var.pixclock = 0; - if (register_framebuffer(info) < 0) - return -EINVAL; - dev_info(fb_helper->dev->dev, "fb%d: %s frame buffer device\n", - info->node, info->fix.id); + info->var.pixclock = 0; + if (register_framebuffer(info) < 0) + return -EINVAL; - } else { - drm_fb_helper_set_par(info); - } + dev_info(fb_helper->dev->dev, "fb%d: %s frame buffer device\n", + info->node, info->fix.id); /* Switch back to kernel console on panic */ /* multi card linked list maybe */ @@ -834,13 +959,25 @@ int drm_fb_helper_single_fb_probe(struct drm_fb_helper *fb_helper, &paniced); register_sysrq_key('v', &sysrq_drm_fb_helper_restore_op); } - if (new_fb) - list_add(&fb_helper->kernel_fb_list, &kernel_fb_helper_list); + + list_add(&fb_helper->kernel_fb_list, &kernel_fb_helper_list); return 0; } -EXPORT_SYMBOL(drm_fb_helper_single_fb_probe); +/** + * drm_fb_helper_fill_fix - initializes fixed fbdev information + * @info: fbdev registered by the helper + * @pitch: desired pitch + * @depth: desired depth + * + * Helper to fill in the fixed fbdev information useful for a non-accelerated + * fbdev emulations. Drivers which support acceleration methods which impose + * additional constraints need to set up their own limits. + * + * Drivers should call this (or their equivalent setup code) from their + * ->fb_probe callback. + */ void drm_fb_helper_fill_fix(struct fb_info *info, uint32_t pitch, uint32_t depth) { @@ -861,6 +998,20 @@ void drm_fb_helper_fill_fix(struct fb_info *info, uint32_t pitch, } EXPORT_SYMBOL(drm_fb_helper_fill_fix); +/** + * drm_fb_helper_fill_var - initalizes variable fbdev information + * @info: fbdev instance to set up + * @fb_helper: fb helper instance to use as template + * @fb_width: desired fb width + * @fb_height: desired fb height + * + * Sets up the variable fbdev metainformation from the given fb helper instance + * and the drm framebuffer allocated in fb_helper->fb. + * + * Drivers should call this (or their equivalent setup code) from their + * ->fb_probe callback after having allocated the fbdev backing + * storage framebuffer. + */ void drm_fb_helper_fill_var(struct fb_info *info, struct drm_fb_helper *fb_helper, uint32_t fb_width, uint32_t fb_height) { @@ -1284,6 +1435,7 @@ static void drm_setup_crtcs(struct drm_fb_helper *fb_helper) for (i = 0; i < fb_helper->crtc_count; i++) { modeset = &fb_helper->crtc_info[i].mode_set; modeset->num_connectors = 0; + modeset->fb = NULL; } for (i = 0; i < fb_helper->connector_count; i++) { @@ -1300,9 +1452,21 @@ static void drm_setup_crtcs(struct drm_fb_helper *fb_helper) modeset->mode = drm_mode_duplicate(dev, fb_crtc->desired_mode); modeset->connectors[modeset->num_connectors++] = fb_helper->connector_info[i]->connector; + modeset->fb = fb_helper->fb; } } + /* Clear out any old modes if there are no more connected outputs. */ + for (i = 0; i < fb_helper->crtc_count; i++) { + modeset = &fb_helper->crtc_info[i].mode_set; + if (modeset->num_connectors == 0) { + BUG_ON(modeset->fb); + BUG_ON(modeset->num_connectors); + if (modeset->mode) + drm_mode_destroy(dev, modeset->mode); + modeset->mode = NULL; + } + } out: kfree(crtcs); kfree(modes); @@ -1310,18 +1474,23 @@ out: } /** - * drm_helper_initial_config - setup a sane initial connector configuration + * drm_fb_helper_initial_config - setup a sane initial connector configuration * @fb_helper: fb_helper device struct * @bpp_sel: bpp value to use for the framebuffer configuration * - * LOCKING: - * Called at init time by the driver to set up the @fb_helper initial - * configuration, must take the mode config lock. - * * Scans the CRTCs and connectors and tries to put together an initial setup. * At the moment, this is a cloned configuration across all heads with * a new framebuffer object as the backing store. * + * Note that this also registers the fbdev and so allows userspace to call into + * the driver through the fbdev interfaces. + * + * This function will call down into the ->fb_probe callback to let + * the driver allocate and initialize the fbdev info structure and the drm + * framebuffer used to back the fbdev. drm_fb_helper_fill_var() and + * drm_fb_helper_fill_fix() are provided as helpers to setup simple default + * values for the fbdev info structure. + * * RETURNS: * Zero if everything went ok, nonzero otherwise. */ @@ -1330,9 +1499,6 @@ bool drm_fb_helper_initial_config(struct drm_fb_helper *fb_helper, int bpp_sel) struct drm_device *dev = fb_helper->dev; int count = 0; - /* disable all the possible outputs/crtcs before entering KMS mode */ - drm_helper_disable_unused_functions(fb_helper->dev); - drm_fb_helper_parse_command_line(fb_helper); count = drm_fb_helper_probe_connector_modes(fb_helper, @@ -1355,12 +1521,17 @@ EXPORT_SYMBOL(drm_fb_helper_initial_config); * probing all the outputs attached to the fb * @fb_helper: the drm_fb_helper * - * LOCKING: - * Called at runtime, must take mode config lock. - * * Scan the connectors attached to the fb_helper and try to put together a * setup after *notification of a change in output configuration. * + * Called at runtime, takes the mode config locks to be able to check/change the + * modeset configuration. Must be run from process context (which usually means + * either the output polling work or a work item launched from the driver's + * hotplug interrupt). + * + * Note that the driver must ensure that this is only called _after_ the fb has + * been fully set up, i.e. after the call to drm_fb_helper_initial_config. + * * RETURNS: * 0 on success and a non-zero error code otherwise. */ @@ -1369,23 +1540,14 @@ int drm_fb_helper_hotplug_event(struct drm_fb_helper *fb_helper) struct drm_device *dev = fb_helper->dev; int count = 0; u32 max_width, max_height, bpp_sel; - int bound = 0, crtcs_bound = 0; - struct drm_crtc *crtc; if (!fb_helper->fb) return 0; - mutex_lock(&dev->mode_config.mutex); - list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { - if (crtc->fb) - crtcs_bound++; - if (crtc->fb == fb_helper->fb) - bound++; - } - - if (bound < crtcs_bound) { + drm_modeset_lock_all(dev); + if (!drm_fb_helper_is_bound(fb_helper)) { fb_helper->delayed_hotplug = true; - mutex_unlock(&dev->mode_config.mutex); + drm_modeset_unlock_all(dev); return 0; } DRM_DEBUG_KMS("\n"); @@ -1397,9 +1559,11 @@ int drm_fb_helper_hotplug_event(struct drm_fb_helper *fb_helper) count = drm_fb_helper_probe_connector_modes(fb_helper, max_width, max_height); drm_setup_crtcs(fb_helper); - mutex_unlock(&dev->mode_config.mutex); + drm_modeset_unlock_all(dev); - return drm_fb_helper_single_fb_probe(fb_helper, bpp_sel); + drm_fb_helper_set_par(fb_helper->fbdev); + + return 0; } EXPORT_SYMBOL(drm_fb_helper_hotplug_event); diff --git a/drivers/gpu/drm/drm_fops.c b/drivers/gpu/drm/drm_fops.c index 133b4132983e..13fdcd10a605 100644 --- a/drivers/gpu/drm/drm_fops.c +++ b/drivers/gpu/drm/drm_fops.c @@ -276,6 +276,7 @@ static int drm_open_helper(struct inode *inode, struct file *filp, INIT_LIST_HEAD(&priv->lhead); INIT_LIST_HEAD(&priv->fbs); + mutex_init(&priv->fbs_lock); INIT_LIST_HEAD(&priv->event_list); init_waitqueue_head(&priv->event_wait); priv->event_space = 4096; /* set aside 4k for event buffer */ diff --git a/drivers/gpu/drm/drm_irq.c b/drivers/gpu/drm/drm_irq.c index 19c01ca3cc76..38e79927b2d7 100644 --- a/drivers/gpu/drm/drm_irq.c +++ b/drivers/gpu/drm/drm_irq.c @@ -505,6 +505,7 @@ void drm_calc_timestamping_constants(struct drm_crtc *crtc) /* Valid dotclock? */ if (dotclock > 0) { + int frame_size; /* Convert scanline length in pixels and video dot clock to * line duration, frame duration and pixel duration in * nanoseconds: @@ -512,7 +513,10 @@ void drm_calc_timestamping_constants(struct drm_crtc *crtc) pixeldur_ns = (s64) div64_u64(1000000000, dotclock); linedur_ns = (s64) div64_u64(((u64) crtc->hwmode.crtc_htotal * 1000000000), dotclock); - framedur_ns = (s64) crtc->hwmode.crtc_vtotal * linedur_ns; + frame_size = crtc->hwmode.crtc_htotal * + crtc->hwmode.crtc_vtotal; + framedur_ns = (s64) div64_u64((u64) frame_size * 1000000000, + dotclock); } else DRM_ERROR("crtc %d: Can't calculate constants, dotclock = 0!\n", crtc->base.id); @@ -1218,8 +1222,9 @@ int drm_wait_vblank(struct drm_device *dev, void *data, int ret; unsigned int flags, seq, crtc, high_crtc; - if ((!drm_dev_to_irq(dev)) || (!dev->irq_enabled)) - return -EINVAL; + if (drm_core_check_feature(dev, DRIVER_HAVE_IRQ)) + if ((!drm_dev_to_irq(dev)) || (!dev->irq_enabled)) + return -EINVAL; if (vblwait->request.type & _DRM_VBLANK_SIGNAL) return -EINVAL; diff --git a/drivers/gpu/drm/drm_mm.c b/drivers/gpu/drm/drm_mm.c index 2aa331499f81..db1e2d6f90d7 100644 --- a/drivers/gpu/drm/drm_mm.c +++ b/drivers/gpu/drm/drm_mm.c @@ -102,20 +102,6 @@ int drm_mm_pre_get(struct drm_mm *mm) } EXPORT_SYMBOL(drm_mm_pre_get); -static inline unsigned long drm_mm_hole_node_start(struct drm_mm_node *hole_node) -{ - return hole_node->start + hole_node->size; -} - -static inline unsigned long drm_mm_hole_node_end(struct drm_mm_node *hole_node) -{ - struct drm_mm_node *next_node = - list_entry(hole_node->node_list.next, struct drm_mm_node, - node_list); - - return next_node->start; -} - static void drm_mm_insert_helper(struct drm_mm_node *hole_node, struct drm_mm_node *node, unsigned long size, unsigned alignment, @@ -127,7 +113,7 @@ static void drm_mm_insert_helper(struct drm_mm_node *hole_node, unsigned long adj_start = hole_start; unsigned long adj_end = hole_end; - BUG_ON(!hole_node->hole_follows || node->allocated); + BUG_ON(node->allocated); if (mm->color_adjust) mm->color_adjust(hole_node, color, &adj_start, &adj_end); @@ -155,12 +141,57 @@ static void drm_mm_insert_helper(struct drm_mm_node *hole_node, BUG_ON(node->start + node->size > adj_end); node->hole_follows = 0; - if (node->start + node->size < hole_end) { + if (__drm_mm_hole_node_start(node) < hole_end) { list_add(&node->hole_stack, &mm->hole_stack); node->hole_follows = 1; } } +struct drm_mm_node *drm_mm_create_block(struct drm_mm *mm, + unsigned long start, + unsigned long size, + bool atomic) +{ + struct drm_mm_node *hole, *node; + unsigned long end = start + size; + unsigned long hole_start; + unsigned long hole_end; + + drm_mm_for_each_hole(hole, mm, hole_start, hole_end) { + if (hole_start > start || hole_end < end) + continue; + + node = drm_mm_kmalloc(mm, atomic); + if (unlikely(node == NULL)) + return NULL; + + node->start = start; + node->size = size; + node->mm = mm; + node->allocated = 1; + + INIT_LIST_HEAD(&node->hole_stack); + list_add(&node->node_list, &hole->node_list); + + if (start == hole_start) { + hole->hole_follows = 0; + list_del_init(&hole->hole_stack); + } + + node->hole_follows = 0; + if (end != hole_end) { + list_add(&node->hole_stack, &mm->hole_stack); + node->hole_follows = 1; + } + + return node; + } + + WARN(1, "no hole found for block 0x%lx + 0x%lx\n", start, size); + return NULL; +} +EXPORT_SYMBOL(drm_mm_create_block); + struct drm_mm_node *drm_mm_get_block_generic(struct drm_mm_node *hole_node, unsigned long size, unsigned alignment, @@ -253,7 +284,7 @@ static void drm_mm_insert_helper_range(struct drm_mm_node *hole_node, BUG_ON(node->start + node->size > end); node->hole_follows = 0; - if (node->start + node->size < hole_end) { + if (__drm_mm_hole_node_start(node) < hole_end) { list_add(&node->hole_stack, &mm->hole_stack); node->hole_follows = 1; } @@ -327,12 +358,13 @@ void drm_mm_remove_node(struct drm_mm_node *node) list_entry(node->node_list.prev, struct drm_mm_node, node_list); if (node->hole_follows) { - BUG_ON(drm_mm_hole_node_start(node) - == drm_mm_hole_node_end(node)); + BUG_ON(__drm_mm_hole_node_start(node) == + __drm_mm_hole_node_end(node)); list_del(&node->hole_stack); } else - BUG_ON(drm_mm_hole_node_start(node) - != drm_mm_hole_node_end(node)); + BUG_ON(__drm_mm_hole_node_start(node) != + __drm_mm_hole_node_end(node)); + if (!prev_node->hole_follows) { prev_node->hole_follows = 1; @@ -390,6 +422,8 @@ struct drm_mm_node *drm_mm_search_free_generic(const struct drm_mm *mm, { struct drm_mm_node *entry; struct drm_mm_node *best; + unsigned long adj_start; + unsigned long adj_end; unsigned long best_size; BUG_ON(mm->scanned_blocks); @@ -397,17 +431,13 @@ struct drm_mm_node *drm_mm_search_free_generic(const struct drm_mm *mm, best = NULL; best_size = ~0UL; - list_for_each_entry(entry, &mm->hole_stack, hole_stack) { - unsigned long adj_start = drm_mm_hole_node_start(entry); - unsigned long adj_end = drm_mm_hole_node_end(entry); - + drm_mm_for_each_hole(entry, mm, adj_start, adj_end) { if (mm->color_adjust) { mm->color_adjust(entry, color, &adj_start, &adj_end); if (adj_end <= adj_start) continue; } - BUG_ON(!entry->hole_follows); if (!check_free_hole(adj_start, adj_end, size, alignment)) continue; @@ -434,6 +464,8 @@ struct drm_mm_node *drm_mm_search_free_in_range_generic(const struct drm_mm *mm, { struct drm_mm_node *entry; struct drm_mm_node *best; + unsigned long adj_start; + unsigned long adj_end; unsigned long best_size; BUG_ON(mm->scanned_blocks); @@ -441,13 +473,11 @@ struct drm_mm_node *drm_mm_search_free_in_range_generic(const struct drm_mm *mm, best = NULL; best_size = ~0UL; - list_for_each_entry(entry, &mm->hole_stack, hole_stack) { - unsigned long adj_start = drm_mm_hole_node_start(entry) < start ? - start : drm_mm_hole_node_start(entry); - unsigned long adj_end = drm_mm_hole_node_end(entry) > end ? - end : drm_mm_hole_node_end(entry); - - BUG_ON(!entry->hole_follows); + drm_mm_for_each_hole(entry, mm, adj_start, adj_end) { + if (adj_start < start) + adj_start = start; + if (adj_end > end) + adj_end = end; if (mm->color_adjust) { mm->color_adjust(entry, color, &adj_start, &adj_end); diff --git a/drivers/gpu/drm/drm_modes.c b/drivers/gpu/drm/drm_modes.c index d8da30e90db5..04fa6f1808d1 100644 --- a/drivers/gpu/drm/drm_modes.c +++ b/drivers/gpu/drm/drm_modes.c @@ -35,6 +35,8 @@ #include <linux/export.h> #include <drm/drmP.h> #include <drm/drm_crtc.h> +#include <video/of_videomode.h> +#include <video/videomode.h> /** * drm_mode_debug_printmodeline - debug print a mode @@ -504,6 +506,74 @@ drm_gtf_mode(struct drm_device *dev, int hdisplay, int vdisplay, int vrefresh, } EXPORT_SYMBOL(drm_gtf_mode); +#if IS_ENABLED(CONFIG_VIDEOMODE) +int drm_display_mode_from_videomode(const struct videomode *vm, + struct drm_display_mode *dmode) +{ + dmode->hdisplay = vm->hactive; + dmode->hsync_start = dmode->hdisplay + vm->hfront_porch; + dmode->hsync_end = dmode->hsync_start + vm->hsync_len; + dmode->htotal = dmode->hsync_end + vm->hback_porch; + + dmode->vdisplay = vm->vactive; + dmode->vsync_start = dmode->vdisplay + vm->vfront_porch; + dmode->vsync_end = dmode->vsync_start + vm->vsync_len; + dmode->vtotal = dmode->vsync_end + vm->vback_porch; + + dmode->clock = vm->pixelclock / 1000; + + dmode->flags = 0; + if (vm->dmt_flags & VESA_DMT_HSYNC_HIGH) + dmode->flags |= DRM_MODE_FLAG_PHSYNC; + else if (vm->dmt_flags & VESA_DMT_HSYNC_LOW) + dmode->flags |= DRM_MODE_FLAG_NHSYNC; + if (vm->dmt_flags & VESA_DMT_VSYNC_HIGH) + dmode->flags |= DRM_MODE_FLAG_PVSYNC; + else if (vm->dmt_flags & VESA_DMT_VSYNC_LOW) + dmode->flags |= DRM_MODE_FLAG_NVSYNC; + if (vm->data_flags & DISPLAY_FLAGS_INTERLACED) + dmode->flags |= DRM_MODE_FLAG_INTERLACE; + if (vm->data_flags & DISPLAY_FLAGS_DOUBLESCAN) + dmode->flags |= DRM_MODE_FLAG_DBLSCAN; + drm_mode_set_name(dmode); + + return 0; +} +EXPORT_SYMBOL_GPL(drm_display_mode_from_videomode); +#endif + +#if IS_ENABLED(CONFIG_OF_VIDEOMODE) +/** + * of_get_drm_display_mode - get a drm_display_mode from devicetree + * @np: device_node with the timing specification + * @dmode: will be set to the return value + * @index: index into the list of display timings in devicetree + * + * This function is expensive and should only be used, if only one mode is to be + * read from DT. To get multiple modes start with of_get_display_timings and + * work with that instead. + */ +int of_get_drm_display_mode(struct device_node *np, + struct drm_display_mode *dmode, int index) +{ + struct videomode vm; + int ret; + + ret = of_get_videomode(np, &vm, index); + if (ret) + return ret; + + drm_display_mode_from_videomode(&vm, dmode); + + pr_debug("%s: got %dx%d display mode from %s\n", + of_node_full_name(np), vm.hactive, vm.vactive, np->name); + drm_mode_debug_printmodeline(dmode); + + return 0; +} +EXPORT_SYMBOL_GPL(of_get_drm_display_mode); +#endif + /** * drm_mode_set_name - set the name on a mode * @mode: name will be set in this mode diff --git a/drivers/gpu/drm/drm_pci.c b/drivers/gpu/drm/drm_pci.c index 754bc96e10c7..bd719e936e13 100644 --- a/drivers/gpu/drm/drm_pci.c +++ b/drivers/gpu/drm/drm_pci.c @@ -439,78 +439,67 @@ int drm_pci_init(struct drm_driver *driver, struct pci_driver *pdriver) return 0; } -#else - -int drm_pci_init(struct drm_driver *driver, struct pci_driver *pdriver) -{ - return -1; -} - -#endif - -EXPORT_SYMBOL(drm_pci_init); - -/*@}*/ -void drm_pci_exit(struct drm_driver *driver, struct pci_driver *pdriver) -{ - struct drm_device *dev, *tmp; - DRM_DEBUG("\n"); - - if (driver->driver_features & DRIVER_MODESET) { - pci_unregister_driver(pdriver); - } else { - list_for_each_entry_safe(dev, tmp, &driver->device_list, driver_item) - drm_put_dev(dev); - } - DRM_INFO("Module unloaded\n"); -} -EXPORT_SYMBOL(drm_pci_exit); - int drm_pcie_get_speed_cap_mask(struct drm_device *dev, u32 *mask) { struct pci_dev *root; - int pos; - u32 lnkcap = 0, lnkcap2 = 0; + u32 lnkcap, lnkcap2; *mask = 0; if (!dev->pdev) return -EINVAL; - if (!pci_is_pcie(dev->pdev)) - return -EINVAL; - root = dev->pdev->bus->self; - pos = pci_pcie_cap(root); - if (!pos) - return -EINVAL; - /* we've been informed via and serverworks don't make the cut */ if (root->vendor == PCI_VENDOR_ID_VIA || root->vendor == PCI_VENDOR_ID_SERVERWORKS) return -EINVAL; - pci_read_config_dword(root, pos + PCI_EXP_LNKCAP, &lnkcap); - pci_read_config_dword(root, pos + PCI_EXP_LNKCAP2, &lnkcap2); + pcie_capability_read_dword(root, PCI_EXP_LNKCAP, &lnkcap); + pcie_capability_read_dword(root, PCI_EXP_LNKCAP2, &lnkcap2); - lnkcap &= PCI_EXP_LNKCAP_SLS; - lnkcap2 &= 0xfe; - - if (lnkcap2) { /* PCIE GEN 3.0 */ + if (lnkcap2) { /* PCIe r3.0-compliant */ if (lnkcap2 & PCI_EXP_LNKCAP2_SLS_2_5GB) *mask |= DRM_PCIE_SPEED_25; if (lnkcap2 & PCI_EXP_LNKCAP2_SLS_5_0GB) *mask |= DRM_PCIE_SPEED_50; if (lnkcap2 & PCI_EXP_LNKCAP2_SLS_8_0GB) *mask |= DRM_PCIE_SPEED_80; - } else { - if (lnkcap & 1) + } else { /* pre-r3.0 */ + if (lnkcap & PCI_EXP_LNKCAP_SLS_2_5GB) *mask |= DRM_PCIE_SPEED_25; - if (lnkcap & 2) - *mask |= DRM_PCIE_SPEED_50; + if (lnkcap & PCI_EXP_LNKCAP_SLS_5_0GB) + *mask |= (DRM_PCIE_SPEED_25 | DRM_PCIE_SPEED_50); } DRM_INFO("probing gen 2 caps for device %x:%x = %x/%x\n", root->vendor, root->device, lnkcap, lnkcap2); return 0; } EXPORT_SYMBOL(drm_pcie_get_speed_cap_mask); + +#else + +int drm_pci_init(struct drm_driver *driver, struct pci_driver *pdriver) +{ + return -1; +} + +#endif + +EXPORT_SYMBOL(drm_pci_init); + +/*@}*/ +void drm_pci_exit(struct drm_driver *driver, struct pci_driver *pdriver) +{ + struct drm_device *dev, *tmp; + DRM_DEBUG("\n"); + + if (driver->driver_features & DRIVER_MODESET) { + pci_unregister_driver(pdriver); + } else { + list_for_each_entry_safe(dev, tmp, &driver->device_list, driver_item) + drm_put_dev(dev); + } + DRM_INFO("Module unloaded\n"); +} +EXPORT_SYMBOL(drm_pci_exit); diff --git a/drivers/gpu/drm/drm_prime.c b/drivers/gpu/drm/drm_prime.c index 7f125738f44e..366910ddcfcb 100644 --- a/drivers/gpu/drm/drm_prime.c +++ b/drivers/gpu/drm/drm_prime.c @@ -53,7 +53,8 @@ * Self-importing: if userspace is using PRIME as a replacement for flink * then it will get a fd->handle request for a GEM object that it created. * Drivers should detect this situation and return back the gem object - * from the dma-buf private. + * from the dma-buf private. Prime will do this automatically for drivers that + * use the drm_gem_prime_{import,export} helpers. */ struct drm_prime_member { @@ -62,6 +63,137 @@ struct drm_prime_member { uint32_t handle; }; +static struct sg_table *drm_gem_map_dma_buf(struct dma_buf_attachment *attach, + enum dma_data_direction dir) +{ + struct drm_gem_object *obj = attach->dmabuf->priv; + struct sg_table *sgt; + + mutex_lock(&obj->dev->struct_mutex); + + sgt = obj->dev->driver->gem_prime_get_sg_table(obj); + + if (!IS_ERR_OR_NULL(sgt)) + dma_map_sg(attach->dev, sgt->sgl, sgt->nents, dir); + + mutex_unlock(&obj->dev->struct_mutex); + return sgt; +} + +static void drm_gem_unmap_dma_buf(struct dma_buf_attachment *attach, + struct sg_table *sgt, enum dma_data_direction dir) +{ + dma_unmap_sg(attach->dev, sgt->sgl, sgt->nents, dir); + sg_free_table(sgt); + kfree(sgt); +} + +static void drm_gem_dmabuf_release(struct dma_buf *dma_buf) +{ + struct drm_gem_object *obj = dma_buf->priv; + + if (obj->export_dma_buf == dma_buf) { + /* drop the reference on the export fd holds */ + obj->export_dma_buf = NULL; + drm_gem_object_unreference_unlocked(obj); + } +} + +static void *drm_gem_dmabuf_vmap(struct dma_buf *dma_buf) +{ + struct drm_gem_object *obj = dma_buf->priv; + struct drm_device *dev = obj->dev; + + return dev->driver->gem_prime_vmap(obj); +} + +static void drm_gem_dmabuf_vunmap(struct dma_buf *dma_buf, void *vaddr) +{ + struct drm_gem_object *obj = dma_buf->priv; + struct drm_device *dev = obj->dev; + + dev->driver->gem_prime_vunmap(obj, vaddr); +} + +static void *drm_gem_dmabuf_kmap_atomic(struct dma_buf *dma_buf, + unsigned long page_num) +{ + return NULL; +} + +static void drm_gem_dmabuf_kunmap_atomic(struct dma_buf *dma_buf, + unsigned long page_num, void *addr) +{ + +} +static void *drm_gem_dmabuf_kmap(struct dma_buf *dma_buf, + unsigned long page_num) +{ + return NULL; +} + +static void drm_gem_dmabuf_kunmap(struct dma_buf *dma_buf, + unsigned long page_num, void *addr) +{ + +} + +static int drm_gem_dmabuf_mmap(struct dma_buf *dma_buf, + struct vm_area_struct *vma) +{ + return -EINVAL; +} + +static const struct dma_buf_ops drm_gem_prime_dmabuf_ops = { + .map_dma_buf = drm_gem_map_dma_buf, + .unmap_dma_buf = drm_gem_unmap_dma_buf, + .release = drm_gem_dmabuf_release, + .kmap = drm_gem_dmabuf_kmap, + .kmap_atomic = drm_gem_dmabuf_kmap_atomic, + .kunmap = drm_gem_dmabuf_kunmap, + .kunmap_atomic = drm_gem_dmabuf_kunmap_atomic, + .mmap = drm_gem_dmabuf_mmap, + .vmap = drm_gem_dmabuf_vmap, + .vunmap = drm_gem_dmabuf_vunmap, +}; + +/** + * DOC: PRIME Helpers + * + * Drivers can implement @gem_prime_export and @gem_prime_import in terms of + * simpler APIs by using the helper functions @drm_gem_prime_export and + * @drm_gem_prime_import. These functions implement dma-buf support in terms of + * five lower-level driver callbacks: + * + * Export callbacks: + * + * - @gem_prime_pin (optional): prepare a GEM object for exporting + * + * - @gem_prime_get_sg_table: provide a scatter/gather table of pinned pages + * + * - @gem_prime_vmap: vmap a buffer exported by your driver + * + * - @gem_prime_vunmap: vunmap a buffer exported by your driver + * + * Import callback: + * + * - @gem_prime_import_sg_table (import): produce a GEM object from another + * driver's scatter/gather table + */ + +struct dma_buf *drm_gem_prime_export(struct drm_device *dev, + struct drm_gem_object *obj, int flags) +{ + if (dev->driver->gem_prime_pin) { + int ret = dev->driver->gem_prime_pin(obj); + if (ret) + return ERR_PTR(ret); + } + return dma_buf_export(obj, &drm_gem_prime_dmabuf_ops, obj->size, + 0600); +} +EXPORT_SYMBOL(drm_gem_prime_export); + int drm_gem_prime_handle_to_fd(struct drm_device *dev, struct drm_file *file_priv, uint32_t handle, uint32_t flags, int *prime_fd) @@ -117,6 +249,58 @@ int drm_gem_prime_handle_to_fd(struct drm_device *dev, } EXPORT_SYMBOL(drm_gem_prime_handle_to_fd); +struct drm_gem_object *drm_gem_prime_import(struct drm_device *dev, + struct dma_buf *dma_buf) +{ + struct dma_buf_attachment *attach; + struct sg_table *sgt; + struct drm_gem_object *obj; + int ret; + + if (!dev->driver->gem_prime_import_sg_table) + return ERR_PTR(-EINVAL); + + if (dma_buf->ops == &drm_gem_prime_dmabuf_ops) { + obj = dma_buf->priv; + if (obj->dev == dev) { + /* + * Importing dmabuf exported from out own gem increases + * refcount on gem itself instead of f_count of dmabuf. + */ + drm_gem_object_reference(obj); + dma_buf_put(dma_buf); + return obj; + } + } + + attach = dma_buf_attach(dma_buf, dev->dev); + if (IS_ERR(attach)) + return ERR_PTR(PTR_ERR(attach)); + + sgt = dma_buf_map_attachment(attach, DMA_BIDIRECTIONAL); + if (IS_ERR_OR_NULL(sgt)) { + ret = PTR_ERR(sgt); + goto fail_detach; + } + + obj = dev->driver->gem_prime_import_sg_table(dev, dma_buf->size, sgt); + if (IS_ERR(obj)) { + ret = PTR_ERR(obj); + goto fail_unmap; + } + + obj->import_attach = attach; + + return obj; + +fail_unmap: + dma_buf_unmap_attachment(attach, sgt, DMA_BIDIRECTIONAL); +fail_detach: + dma_buf_detach(dma_buf, attach); + return ERR_PTR(ret); +} +EXPORT_SYMBOL(drm_gem_prime_import); + int drm_gem_prime_fd_to_handle(struct drm_device *dev, struct drm_file *file_priv, int prime_fd, uint32_t *handle) { diff --git a/drivers/gpu/drm/drm_usb.c b/drivers/gpu/drm/drm_usb.c index 3cec30611417..34a156f0c336 100644 --- a/drivers/gpu/drm/drm_usb.c +++ b/drivers/gpu/drm/drm_usb.c @@ -18,7 +18,7 @@ int drm_get_usb_dev(struct usb_interface *interface, usbdev = interface_to_usbdev(interface); dev->usbdev = usbdev; - dev->dev = &usbdev->dev; + dev->dev = &interface->dev; mutex_lock(&drm_global_mutex); diff --git a/drivers/gpu/drm/exynos/exynos_drm_fbdev.c b/drivers/gpu/drm/exynos/exynos_drm_fbdev.c index 71f867340a88..68f0045f86b8 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_fbdev.c +++ b/drivers/gpu/drm/exynos/exynos_drm_fbdev.c @@ -226,36 +226,8 @@ out: return ret; } -static int exynos_drm_fbdev_probe(struct drm_fb_helper *helper, - struct drm_fb_helper_surface_size *sizes) -{ - int ret = 0; - - DRM_DEBUG_KMS("%s\n", __FILE__); - - /* - * with !helper->fb, it means that this funcion is called first time - * and after that, the helper->fb would be used as clone mode. - */ - if (!helper->fb) { - ret = exynos_drm_fbdev_create(helper, sizes); - if (ret < 0) { - DRM_ERROR("failed to create fbdev.\n"); - return ret; - } - - /* - * fb_helper expects a value more than 1 if succeed - * because register_framebuffer() should be called. - */ - ret = 1; - } - - return ret; -} - static struct drm_fb_helper_funcs exynos_drm_fb_helper_funcs = { - .fb_probe = exynos_drm_fbdev_probe, + .fb_probe = exynos_drm_fbdev_create, }; int exynos_drm_fbdev_init(struct drm_device *dev) @@ -295,6 +267,9 @@ int exynos_drm_fbdev_init(struct drm_device *dev) } + /* disable all the possible outputs/crtcs before entering KMS mode */ + drm_helper_disable_unused_functions(dev); + ret = drm_fb_helper_initial_config(helper, PREFERRED_BPP); if (ret < 0) { DRM_ERROR("failed to set up hw configuration.\n"); @@ -326,8 +301,10 @@ static void exynos_drm_fbdev_destroy(struct drm_device *dev, /* release drm framebuffer and real buffer */ if (fb_helper->fb && fb_helper->fb->funcs) { fb = fb_helper->fb; - if (fb) + if (fb) { + drm_framebuffer_unregister_private(fb); drm_framebuffer_remove(fb); + } } /* release linux framebuffer */ @@ -374,5 +351,7 @@ void exynos_drm_fbdev_restore_mode(struct drm_device *dev) if (!private || !private->fb_helper) return; + drm_modeset_lock_all(dev); drm_fb_helper_restore_fbdev_mode(private->fb_helper); + drm_modeset_unlock_all(dev); } diff --git a/drivers/gpu/drm/gma500/framebuffer.c b/drivers/gpu/drm/gma500/framebuffer.c index afded54dbb10..2590cac84257 100644 --- a/drivers/gpu/drm/gma500/framebuffer.c +++ b/drivers/gpu/drm/gma500/framebuffer.c @@ -260,13 +260,13 @@ static int psb_framebuffer_init(struct drm_device *dev, default: return -EINVAL; } + drm_helper_mode_fill_fb_struct(&fb->base, mode_cmd); + fb->gtt = gt; ret = drm_framebuffer_init(dev, &fb->base, &psb_fb_funcs); if (ret) { dev_err(dev->dev, "framebuffer init failed: %d\n", ret); return ret; } - drm_helper_mode_fill_fb_struct(&fb->base, mode_cmd); - fb->gtt = gt; return 0; } @@ -545,9 +545,7 @@ static int psbfb_probe(struct drm_fb_helper *helper, struct psb_fbdev *psb_fbdev = (struct psb_fbdev *)helper; struct drm_device *dev = psb_fbdev->psb_fb_helper.dev; struct drm_psb_private *dev_priv = dev->dev_private; - int new_fb = 0; int bytespp; - int ret; bytespp = sizes->surface_bpp / 8; if (bytespp == 3) /* no 24bit packed */ @@ -562,13 +560,7 @@ static int psbfb_probe(struct drm_fb_helper *helper, sizes->surface_depth = 16; } - if (!helper->fb) { - ret = psbfb_create(psb_fbdev, sizes); - if (ret) - return ret; - new_fb = 1; - } - return new_fb; + return psbfb_create(psb_fbdev, sizes); } static struct drm_fb_helper_funcs psb_fb_helper_funcs = { @@ -590,6 +582,7 @@ static int psb_fbdev_destroy(struct drm_device *dev, struct psb_fbdev *fbdev) framebuffer_release(info); } drm_fb_helper_fini(&fbdev->psb_fb_helper); + drm_framebuffer_unregister_private(&psbfb->base); drm_framebuffer_cleanup(&psbfb->base); if (psbfb->gtt) @@ -615,6 +608,10 @@ int psb_fbdev_init(struct drm_device *dev) INTELFB_CONN_LIMIT); drm_fb_helper_single_add_all_connectors(&fbdev->psb_fb_helper); + + /* disable all the possible outputs/crtcs before entering KMS mode */ + drm_helper_disable_unused_functions(dev); + drm_fb_helper_initial_config(&fbdev->psb_fb_helper, 32); return 0; } @@ -668,30 +665,6 @@ static void psb_user_framebuffer_destroy(struct drm_framebuffer *fb) { struct psb_framebuffer *psbfb = to_psb_fb(fb); struct gtt_range *r = psbfb->gtt; - struct drm_device *dev = fb->dev; - struct drm_psb_private *dev_priv = dev->dev_private; - struct psb_fbdev *fbdev = dev_priv->fbdev; - struct drm_crtc *crtc; - int reset = 0; - - /* Should never get stolen memory for a user fb */ - WARN_ON(r->stolen); - - /* Check if we are erroneously live */ - list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) - if (crtc->fb == fb) - reset = 1; - - if (reset) - /* - * Now force a sane response before we permit the DRM CRTC - * layer to do stupid things like blank the display. Instead - * we reset this framebuffer as if the user had forced a reset. - * We must do this before the cleanup so that the DRM layer - * doesn't get a chance to stick its oar in where it isn't - * wanted. - */ - drm_fb_helper_restore_fbdev_mode(&fbdev->psb_fb_helper); /* Let DRM do its clean up */ drm_framebuffer_cleanup(fb); diff --git a/drivers/gpu/drm/gma500/psb_device.c b/drivers/gpu/drm/gma500/psb_device.c index b58c4701c4e8..f6f534b4197e 100644 --- a/drivers/gpu/drm/gma500/psb_device.c +++ b/drivers/gpu/drm/gma500/psb_device.c @@ -194,7 +194,7 @@ static int psb_save_display_registers(struct drm_device *dev) regs->saveCHICKENBIT = PSB_RVDC32(DSPCHICKENBIT); /* Save crtc and output state */ - mutex_lock(&dev->mode_config.mutex); + drm_modeset_lock_all(dev); list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { if (drm_helper_crtc_in_use(crtc)) crtc->funcs->save(crtc); @@ -204,7 +204,7 @@ static int psb_save_display_registers(struct drm_device *dev) if (connector->funcs->save) connector->funcs->save(connector); - mutex_unlock(&dev->mode_config.mutex); + drm_modeset_unlock_all(dev); return 0; } @@ -234,7 +234,7 @@ static int psb_restore_display_registers(struct drm_device *dev) /*make sure VGA plane is off. it initializes to on after reset!*/ PSB_WVDC32(0x80000000, VGACNTRL); - mutex_lock(&dev->mode_config.mutex); + drm_modeset_lock_all(dev); list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) if (drm_helper_crtc_in_use(crtc)) crtc->funcs->restore(crtc); @@ -243,7 +243,7 @@ static int psb_restore_display_registers(struct drm_device *dev) if (connector->funcs->restore) connector->funcs->restore(connector); - mutex_unlock(&dev->mode_config.mutex); + drm_modeset_unlock_all(dev); return 0; } diff --git a/drivers/gpu/drm/gma500/psb_drv.c b/drivers/gpu/drm/gma500/psb_drv.c index dd1fbfa7e467..111e3df9c5de 100644 --- a/drivers/gpu/drm/gma500/psb_drv.c +++ b/drivers/gpu/drm/gma500/psb_drv.c @@ -149,6 +149,16 @@ static struct drm_ioctl_desc psb_ioctls[] = { static void psb_lastclose(struct drm_device *dev) { + int ret; + struct drm_psb_private *dev_priv = dev->dev_private; + struct psb_fbdev *fbdev = dev_priv->fbdev; + + drm_modeset_lock_all(dev); + ret = drm_fb_helper_restore_fbdev_mode(&fbdev->psb_fb_helper); + if (ret) + DRM_DEBUG("failed to restore crtc mode\n"); + drm_modeset_unlock_all(dev); + return; } @@ -476,7 +486,7 @@ static int psb_mode_operation_ioctl(struct drm_device *dev, void *data, case PSB_MODE_OPERATION_MODE_VALID: umode = &arg->mode; - mutex_lock(&dev->mode_config.mutex); + drm_modeset_lock_all(dev); obj = drm_mode_object_find(dev, obj_id, DRM_MODE_OBJECT_CONNECTOR); @@ -525,7 +535,7 @@ static int psb_mode_operation_ioctl(struct drm_device *dev, void *data, if (mode) drm_mode_destroy(dev, mode); mode_op_out: - mutex_unlock(&dev->mode_config.mutex); + drm_modeset_unlock_all(dev); return ret; default: diff --git a/drivers/gpu/drm/i2c/Kconfig b/drivers/gpu/drm/i2c/Kconfig new file mode 100644 index 000000000000..16118363509a --- /dev/null +++ b/drivers/gpu/drm/i2c/Kconfig @@ -0,0 +1,22 @@ +menu "I2C encoder or helper chips" + depends on DRM && DRM_KMS_HELPER && I2C + +config DRM_I2C_CH7006 + tristate "Chrontel ch7006 TV encoder" + default m if DRM_NOUVEAU + help + Support for Chrontel ch7006 and similar TV encoders, found + on some nVidia video cards. + + This driver is currently only useful if you're also using + the nouveau driver. + +config DRM_I2C_SIL164 + tristate "Silicon Image sil164 TMDS transmitter" + default m if DRM_NOUVEAU + help + Support for sil164 and similar single-link (or dual-link + when used in pairs) TMDS transmitters, used in some nVidia + video cards. + +endmenu diff --git a/drivers/gpu/drm/i2c/ch7006_drv.c b/drivers/gpu/drm/i2c/ch7006_drv.c index b865d0728e28..51fa32392029 100644 --- a/drivers/gpu/drm/i2c/ch7006_drv.c +++ b/drivers/gpu/drm/i2c/ch7006_drv.c @@ -364,7 +364,7 @@ static int ch7006_encoder_set_property(struct drm_encoder *encoder, .crtc = crtc, }; - crtc->funcs->set_config(&modeset); + drm_mode_set_config_internal(&modeset); } } diff --git a/drivers/gpu/drm/i915/Makefile b/drivers/gpu/drm/i915/Makefile index 0f2c5493242b..91f3ac6cef35 100644 --- a/drivers/gpu/drm/i915/Makefile +++ b/drivers/gpu/drm/i915/Makefile @@ -16,6 +16,7 @@ i915-y := i915_drv.o i915_dma.o i915_irq.o \ i915_gem_tiling.o \ i915_sysfs.o \ i915_trace_points.o \ + i915_ums.o \ intel_display.o \ intel_crt.o \ intel_lvds.o \ diff --git a/drivers/gpu/drm/i915/i915_debugfs.c b/drivers/gpu/drm/i915/i915_debugfs.c index 9d4a2c2adf0e..7c65ab83914a 100644 --- a/drivers/gpu/drm/i915/i915_debugfs.c +++ b/drivers/gpu/drm/i915/i915_debugfs.c @@ -103,7 +103,7 @@ static const char *cache_level_str(int type) static void describe_obj(struct seq_file *m, struct drm_i915_gem_object *obj) { - seq_printf(m, "%p: %s%s %8zdKiB %04x %04x %d %d %d%s%s%s", + seq_printf(m, "%p: %s%s %8zdKiB %02x %02x %d %d %d%s%s%s", &obj->base, get_pin_flag(obj), get_tiling_flag(obj), @@ -125,6 +125,8 @@ describe_obj(struct seq_file *m, struct drm_i915_gem_object *obj) if (obj->gtt_space != NULL) seq_printf(m, " (gtt offset: %08x, size: %08x)", obj->gtt_offset, (unsigned int)obj->gtt_space->size); + if (obj->stolen) + seq_printf(m, " (stolen: %08lx)", obj->stolen->start); if (obj->pin_mappable || obj->fault_mappable) { char s[3], *t = s; if (obj->pin_mappable) @@ -257,8 +259,9 @@ static int i915_gem_object_info(struct seq_file *m, void* data) seq_printf(m, "%u fault mappable objects, %zu bytes\n", count, size); - seq_printf(m, "%zu [%zu] gtt total\n", - dev_priv->mm.gtt_total, dev_priv->mm.mappable_gtt_total); + seq_printf(m, "%zu [%lu] gtt total\n", + dev_priv->gtt.total, + dev_priv->gtt.mappable_end - dev_priv->gtt.start); mutex_unlock(&dev->struct_mutex); @@ -388,7 +391,7 @@ static void i915_ring_seqno_info(struct seq_file *m, struct intel_ring_buffer *ring) { if (ring->get_seqno) { - seq_printf(m, "Current sequence (%s): %d\n", + seq_printf(m, "Current sequence (%s): %u\n", ring->name, ring->get_seqno(ring, false)); } } @@ -545,11 +548,11 @@ static int i915_hws_info(struct seq_file *m, void *data) struct drm_device *dev = node->minor->dev; drm_i915_private_t *dev_priv = dev->dev_private; struct intel_ring_buffer *ring; - const volatile u32 __iomem *hws; + const u32 *hws; int i; ring = &dev_priv->ring[(uintptr_t)node->info_ent->data]; - hws = (volatile u32 __iomem *)ring->status_page.page_addr; + hws = ring->status_page.page_addr; if (hws == NULL) return 0; @@ -609,7 +612,7 @@ static void print_error_buffers(struct seq_file *m, seq_printf(m, "%s [%d]:\n", name, count); while (count--) { - seq_printf(m, " %08x %8u %04x %04x %x %x%s%s%s%s%s%s%s", + seq_printf(m, " %08x %8u %02x %02x %x %x%s%s%s%s%s%s%s", err->gtt_offset, err->size, err->read_domains, @@ -691,7 +694,7 @@ static int i915_error_state(struct seq_file *m, void *unused) seq_printf(m, "Time: %ld s %ld us\n", error->time.tv_sec, error->time.tv_usec); - seq_printf(m, "Kernel: " UTS_RELEASE); + seq_printf(m, "Kernel: " UTS_RELEASE "\n"); seq_printf(m, "PCI ID: 0x%04x\n", dev->pci_device); seq_printf(m, "EIR: 0x%08x\n", error->eir); seq_printf(m, "IER: 0x%08x\n", error->ier); @@ -816,11 +819,11 @@ static int i915_error_state_open(struct inode *inode, struct file *file) error_priv->dev = dev; - spin_lock_irqsave(&dev_priv->error_lock, flags); - error_priv->error = dev_priv->first_error; + spin_lock_irqsave(&dev_priv->gpu_error.lock, flags); + error_priv->error = dev_priv->gpu_error.first_error; if (error_priv->error) kref_get(&error_priv->error->ref); - spin_unlock_irqrestore(&dev_priv->error_lock, flags); + spin_unlock_irqrestore(&dev_priv->gpu_error.lock, flags); return single_open(file, i915_error_state, error_priv); } @@ -846,6 +849,77 @@ static const struct file_operations i915_error_state_fops = { .release = i915_error_state_release, }; +static ssize_t +i915_next_seqno_read(struct file *filp, + char __user *ubuf, + size_t max, + loff_t *ppos) +{ + struct drm_device *dev = filp->private_data; + drm_i915_private_t *dev_priv = dev->dev_private; + char buf[80]; + int len; + int ret; + + ret = mutex_lock_interruptible(&dev->struct_mutex); + if (ret) + return ret; + + len = snprintf(buf, sizeof(buf), + "next_seqno : 0x%x\n", + dev_priv->next_seqno); + + mutex_unlock(&dev->struct_mutex); + + if (len > sizeof(buf)) + len = sizeof(buf); + + return simple_read_from_buffer(ubuf, max, ppos, buf, len); +} + +static ssize_t +i915_next_seqno_write(struct file *filp, + const char __user *ubuf, + size_t cnt, + loff_t *ppos) +{ + struct drm_device *dev = filp->private_data; + char buf[20]; + u32 val = 1; + int ret; + + if (cnt > 0) { + if (cnt > sizeof(buf) - 1) + return -EINVAL; + + if (copy_from_user(buf, ubuf, cnt)) + return -EFAULT; + buf[cnt] = 0; + + ret = kstrtouint(buf, 0, &val); + if (ret < 0) + return ret; + } + + ret = mutex_lock_interruptible(&dev->struct_mutex); + if (ret) + return ret; + + ret = i915_gem_set_seqno(dev, val); + + mutex_unlock(&dev->struct_mutex); + + return ret ?: cnt; +} + +static const struct file_operations i915_next_seqno_fops = { + .owner = THIS_MODULE, + .open = simple_open, + .read = i915_next_seqno_read, + .write = i915_next_seqno_write, + .llseek = default_llseek, +}; + static int i915_rstdby_delays(struct seq_file *m, void *unused) { struct drm_info_node *node = (struct drm_info_node *) m->private; @@ -888,7 +962,7 @@ static int i915_cur_delayinfo(struct seq_file *m, void *unused) u32 gt_perf_status = I915_READ(GEN6_GT_PERF_STATUS); u32 rp_state_limits = I915_READ(GEN6_RP_STATE_LIMITS); u32 rp_state_cap = I915_READ(GEN6_RP_STATE_CAP); - u32 rpstat; + u32 rpstat, cagf; u32 rpupei, rpcurup, rpprevup; u32 rpdownei, rpcurdown, rpprevdown; int max_freq; @@ -907,6 +981,11 @@ static int i915_cur_delayinfo(struct seq_file *m, void *unused) rpdownei = I915_READ(GEN6_RP_CUR_DOWN_EI); rpcurdown = I915_READ(GEN6_RP_CUR_DOWN); rpprevdown = I915_READ(GEN6_RP_PREV_DOWN); + if (IS_HASWELL(dev)) + cagf = (rpstat & HSW_CAGF_MASK) >> HSW_CAGF_SHIFT; + else + cagf = (rpstat & GEN6_CAGF_MASK) >> GEN6_CAGF_SHIFT; + cagf *= GT_FREQUENCY_MULTIPLIER; gen6_gt_force_wake_put(dev_priv); mutex_unlock(&dev->struct_mutex); @@ -919,8 +998,7 @@ static int i915_cur_delayinfo(struct seq_file *m, void *unused) gt_perf_status & 0xff); seq_printf(m, "Render p-state limit: %d\n", rp_state_limits & 0xff); - seq_printf(m, "CAGF: %dMHz\n", ((rpstat & GEN6_CAGF_MASK) >> - GEN6_CAGF_SHIFT) * GT_FREQUENCY_MULTIPLIER); + seq_printf(m, "CAGF: %dMHz\n", cagf); seq_printf(m, "RP CUR UP EI: %dus\n", rpupei & GEN6_CURICONT_MASK); seq_printf(m, "RP CUR UP: %dus\n", rpcurup & @@ -1372,28 +1450,31 @@ static int i915_gem_framebuffer_info(struct seq_file *m, void *data) ifbdev = dev_priv->fbdev; fb = to_intel_framebuffer(ifbdev->helper.fb); - seq_printf(m, "fbcon size: %d x %d, depth %d, %d bpp, obj ", + seq_printf(m, "fbcon size: %d x %d, depth %d, %d bpp, refcount %d, obj ", fb->base.width, fb->base.height, fb->base.depth, - fb->base.bits_per_pixel); + fb->base.bits_per_pixel, + atomic_read(&fb->base.refcount.refcount)); describe_obj(m, fb->obj); seq_printf(m, "\n"); + mutex_unlock(&dev->mode_config.mutex); + mutex_lock(&dev->mode_config.fb_lock); list_for_each_entry(fb, &dev->mode_config.fb_list, base.head) { if (&fb->base == ifbdev->helper.fb) continue; - seq_printf(m, "user size: %d x %d, depth %d, %d bpp, obj ", + seq_printf(m, "user size: %d x %d, depth %d, %d bpp, refcount %d, obj ", fb->base.width, fb->base.height, fb->base.depth, - fb->base.bits_per_pixel); + fb->base.bits_per_pixel, + atomic_read(&fb->base.refcount.refcount)); describe_obj(m, fb->obj); seq_printf(m, "\n"); } - - mutex_unlock(&dev->mode_config.mutex); + mutex_unlock(&dev->mode_config.fb_lock); return 0; } @@ -1403,7 +1484,8 @@ static int i915_context_status(struct seq_file *m, void *unused) struct drm_info_node *node = (struct drm_info_node *) m->private; struct drm_device *dev = node->minor->dev; drm_i915_private_t *dev_priv = dev->dev_private; - int ret; + struct intel_ring_buffer *ring; + int ret, i; ret = mutex_lock_interruptible(&dev->mode_config.mutex); if (ret) @@ -1421,6 +1503,14 @@ static int i915_context_status(struct seq_file *m, void *unused) seq_printf(m, "\n"); } + for_each_ring(ring, dev_priv, i) { + if (ring->default_context) { + seq_printf(m, "HW default context %s ring ", ring->name); + describe_obj(m, ring->default_context->obj); + seq_printf(m, "\n"); + } + } + mutex_unlock(&dev->mode_config.mutex); return 0; @@ -1556,7 +1646,7 @@ static int i915_dpio_info(struct seq_file *m, void *data) return 0; } - ret = mutex_lock_interruptible(&dev->mode_config.mutex); + ret = mutex_lock_interruptible(&dev_priv->dpio_lock); if (ret) return ret; @@ -1585,7 +1675,7 @@ static int i915_dpio_info(struct seq_file *m, void *data) seq_printf(m, "DPIO_FASTCLK_DISABLE: 0x%08x\n", intel_dpio_read(dev_priv, DPIO_FASTCLK_DISABLE)); - mutex_unlock(&dev->mode_config.mutex); + mutex_unlock(&dev_priv->dpio_lock); return 0; } @@ -1603,7 +1693,7 @@ i915_wedged_read(struct file *filp, len = snprintf(buf, sizeof(buf), "wedged : %d\n", - atomic_read(&dev_priv->mm.wedged)); + atomic_read(&dev_priv->gpu_error.reset_counter)); if (len > sizeof(buf)) len = sizeof(buf); @@ -1658,7 +1748,7 @@ i915_ring_stop_read(struct file *filp, int len; len = snprintf(buf, sizeof(buf), - "0x%08x\n", dev_priv->stop_rings); + "0x%08x\n", dev_priv->gpu_error.stop_rings); if (len > sizeof(buf)) len = sizeof(buf); @@ -1694,7 +1784,7 @@ i915_ring_stop_write(struct file *filp, if (ret) return ret; - dev_priv->stop_rings = val; + dev_priv->gpu_error.stop_rings = val; mutex_unlock(&dev->struct_mutex); return cnt; @@ -1708,6 +1798,102 @@ static const struct file_operations i915_ring_stop_fops = { .llseek = default_llseek, }; +#define DROP_UNBOUND 0x1 +#define DROP_BOUND 0x2 +#define DROP_RETIRE 0x4 +#define DROP_ACTIVE 0x8 +#define DROP_ALL (DROP_UNBOUND | \ + DROP_BOUND | \ + DROP_RETIRE | \ + DROP_ACTIVE) +static ssize_t +i915_drop_caches_read(struct file *filp, + char __user *ubuf, + size_t max, + loff_t *ppos) +{ + char buf[20]; + int len; + + len = snprintf(buf, sizeof(buf), "0x%08x\n", DROP_ALL); + if (len > sizeof(buf)) + len = sizeof(buf); + + return simple_read_from_buffer(ubuf, max, ppos, buf, len); +} + +static ssize_t +i915_drop_caches_write(struct file *filp, + const char __user *ubuf, + size_t cnt, + loff_t *ppos) +{ + struct drm_device *dev = filp->private_data; + struct drm_i915_private *dev_priv = dev->dev_private; + struct drm_i915_gem_object *obj, *next; + char buf[20]; + int val = 0, ret; + + if (cnt > 0) { + if (cnt > sizeof(buf) - 1) + return -EINVAL; + + if (copy_from_user(buf, ubuf, cnt)) + return -EFAULT; + buf[cnt] = 0; + + val = simple_strtoul(buf, NULL, 0); + } + + DRM_DEBUG_DRIVER("Dropping caches: 0x%08x\n", val); + + /* No need to check and wait for gpu resets, only libdrm auto-restarts + * on ioctls on -EAGAIN. */ + ret = mutex_lock_interruptible(&dev->struct_mutex); + if (ret) + return ret; + + if (val & DROP_ACTIVE) { + ret = i915_gpu_idle(dev); + if (ret) + goto unlock; + } + + if (val & (DROP_RETIRE | DROP_ACTIVE)) + i915_gem_retire_requests(dev); + + if (val & DROP_BOUND) { + list_for_each_entry_safe(obj, next, &dev_priv->mm.inactive_list, mm_list) + if (obj->pin_count == 0) { + ret = i915_gem_object_unbind(obj); + if (ret) + goto unlock; + } + } + + if (val & DROP_UNBOUND) { + list_for_each_entry_safe(obj, next, &dev_priv->mm.unbound_list, gtt_list) + if (obj->pages_pin_count == 0) { + ret = i915_gem_object_put_pages(obj); + if (ret) + goto unlock; + } + } + +unlock: + mutex_unlock(&dev->struct_mutex); + + return ret ?: cnt; +} + +static const struct file_operations i915_drop_caches_fops = { + .owner = THIS_MODULE, + .open = simple_open, + .read = i915_drop_caches_read, + .write = i915_drop_caches_write, + .llseek = default_llseek, +}; + static ssize_t i915_max_freq_read(struct file *filp, char __user *ubuf, @@ -2105,11 +2291,23 @@ int i915_debugfs_init(struct drm_minor *minor) return ret; ret = i915_debugfs_create(minor->debugfs_root, minor, + "i915_gem_drop_caches", + &i915_drop_caches_fops); + if (ret) + return ret; + + ret = i915_debugfs_create(minor->debugfs_root, minor, "i915_error_state", &i915_error_state_fops); if (ret) return ret; + ret = i915_debugfs_create(minor->debugfs_root, minor, + "i915_next_seqno", + &i915_next_seqno_fops); + if (ret) + return ret; + return drm_debugfs_create_files(i915_debugfs_list, I915_DEBUGFS_ENTRIES, minor->debugfs_root, minor); @@ -2129,10 +2327,14 @@ void i915_debugfs_cleanup(struct drm_minor *minor) 1, minor); drm_debugfs_remove_files((struct drm_info_list *) &i915_cache_sharing_fops, 1, minor); + drm_debugfs_remove_files((struct drm_info_list *) &i915_drop_caches_fops, + 1, minor); drm_debugfs_remove_files((struct drm_info_list *) &i915_ring_stop_fops, 1, minor); drm_debugfs_remove_files((struct drm_info_list *) &i915_error_state_fops, 1, minor); + drm_debugfs_remove_files((struct drm_info_list *) &i915_next_seqno_fops, + 1, minor); } #endif /* CONFIG_DEBUG_FS */ diff --git a/drivers/gpu/drm/i915/i915_dma.c b/drivers/gpu/drm/i915/i915_dma.c index 99daa896105d..4fa6beb14c77 100644 --- a/drivers/gpu/drm/i915/i915_dma.c +++ b/drivers/gpu/drm/i915/i915_dma.c @@ -992,6 +992,12 @@ static int i915_getparam(struct drm_device *dev, void *data, case I915_PARAM_HAS_PINNED_BATCHES: value = 1; break; + case I915_PARAM_HAS_EXEC_NO_RELOC: + value = 1; + break; + case I915_PARAM_HAS_EXEC_HANDLE_LUT: + value = 1; + break; default: DRM_DEBUG_DRIVER("Unknown parameter %d\n", param->param); @@ -1070,7 +1076,7 @@ static int i915_set_status_page(struct drm_device *dev, void *data, ring->status_page.gfx_addr = hws->addr & (0x1ffff<<12); dev_priv->dri1.gfx_hws_cpu_addr = - ioremap_wc(dev_priv->mm.gtt_base_addr + hws->addr, 4096); + ioremap_wc(dev_priv->gtt.mappable_base + hws->addr, 4096); if (dev_priv->dri1.gfx_hws_cpu_addr == NULL) { i915_dma_cleanup(dev); ring->status_page.gfx_addr = 0; @@ -1297,19 +1303,21 @@ static int i915_load_modeset_init(struct drm_device *dev) if (ret) goto cleanup_vga_switcheroo; + ret = drm_irq_install(dev); + if (ret) + goto cleanup_gem_stolen; + + /* Important: The output setup functions called by modeset_init need + * working irqs for e.g. gmbus and dp aux transfers. */ intel_modeset_init(dev); ret = i915_gem_init(dev); if (ret) - goto cleanup_gem_stolen; - - intel_modeset_gem_init(dev); + goto cleanup_irq; INIT_WORK(&dev_priv->console_resume_work, intel_console_resume); - ret = drm_irq_install(dev); - if (ret) - goto cleanup_gem; + intel_modeset_gem_init(dev); /* Always safe in the mode setting case. */ /* FIXME: do pre/post-mode set stuff in core KMS code */ @@ -1317,7 +1325,25 @@ static int i915_load_modeset_init(struct drm_device *dev) ret = intel_fbdev_init(dev); if (ret) - goto cleanup_irq; + goto cleanup_gem; + + /* Only enable hotplug handling once the fbdev is fully set up. */ + intel_hpd_init(dev); + + /* + * Some ports require correctly set-up hpd registers for detection to + * work properly (leading to ghost connected connector status), e.g. VGA + * on gm45. Hence we can only set up the initial fbdev config after hpd + * irqs are fully enabled. Now we should scan for the initial config + * only once hotplug handling is enabled, but due to screwed-up locking + * around kms/fbdev init we can't protect the fdbev initial config + * scanning against hotplug events. Hence do this first and ignore the + * tiny window where we will loose hotplug notifactions. + */ + intel_fbdev_initial_config(dev); + + /* Only enable hotplug handling once the fbdev is fully set up. */ + dev_priv->enable_hotplug_processing = true; drm_kms_helper_poll_init(dev); @@ -1326,13 +1352,13 @@ static int i915_load_modeset_init(struct drm_device *dev) return 0; -cleanup_irq: - drm_irq_uninstall(dev); cleanup_gem: mutex_lock(&dev->struct_mutex); i915_gem_cleanup_ringbuffer(dev); mutex_unlock(&dev->struct_mutex); i915_gem_cleanup_aliasing_ppgtt(dev); +cleanup_irq: + drm_irq_uninstall(dev); cleanup_gem_stolen: i915_gem_cleanup_stolen(dev); cleanup_vga_switcheroo: @@ -1400,9 +1426,9 @@ static void i915_kick_out_firmware_fb(struct drm_i915_private *dev_priv) if (!ap) return; - ap->ranges[0].base = dev_priv->mm.gtt->gma_bus_addr; - ap->ranges[0].size = - dev_priv->mm.gtt->gtt_mappable_entries << PAGE_SHIFT; + ap->ranges[0].base = dev_priv->gtt.mappable_base; + ap->ranges[0].size = dev_priv->gtt.mappable_end - dev_priv->gtt.start; + primary = pdev->resource[PCI_ROM_RESOURCE].flags & IORESOURCE_ROM_SHADOW; @@ -1516,18 +1542,17 @@ int i915_driver_load(struct drm_device *dev, unsigned long flags) goto put_gmch; } - aperture_size = dev_priv->mm.gtt->gtt_mappable_entries << PAGE_SHIFT; - dev_priv->mm.gtt_base_addr = dev_priv->mm.gtt->gma_bus_addr; + aperture_size = dev_priv->gtt.mappable_end; - dev_priv->mm.gtt_mapping = - io_mapping_create_wc(dev_priv->mm.gtt_base_addr, + dev_priv->gtt.mappable = + io_mapping_create_wc(dev_priv->gtt.mappable_base, aperture_size); - if (dev_priv->mm.gtt_mapping == NULL) { + if (dev_priv->gtt.mappable == NULL) { ret = -EIO; goto out_rmmap; } - i915_mtrr_setup(dev_priv, dev_priv->mm.gtt_base_addr, + i915_mtrr_setup(dev_priv, dev_priv->gtt.mappable_base, aperture_size); /* The i915 workqueue is primarily used for batched retirement of @@ -1580,11 +1605,12 @@ int i915_driver_load(struct drm_device *dev, unsigned long flags) pci_enable_msi(dev->pdev); spin_lock_init(&dev_priv->irq_lock); - spin_lock_init(&dev_priv->error_lock); + spin_lock_init(&dev_priv->gpu_error.lock); spin_lock_init(&dev_priv->rps.lock); - spin_lock_init(&dev_priv->dpio_lock); + mutex_init(&dev_priv->dpio_lock); mutex_init(&dev_priv->rps.hw_lock); + mutex_init(&dev_priv->modeset_restore_lock); if (IS_IVYBRIDGE(dev) || IS_HASWELL(dev)) dev_priv->num_pipe = 3; @@ -1614,9 +1640,6 @@ int i915_driver_load(struct drm_device *dev, unsigned long flags) intel_opregion_init(dev); acpi_video_register(); - setup_timer(&dev_priv->hangcheck_timer, i915_hangcheck_elapsed, - (unsigned long) dev); - if (IS_GEN5(dev)) intel_gpu_ips_init(dev_priv); @@ -1635,15 +1658,15 @@ out_gem_unload: out_mtrrfree: if (dev_priv->mm.gtt_mtrr >= 0) { mtrr_del(dev_priv->mm.gtt_mtrr, - dev_priv->mm.gtt_base_addr, + dev_priv->gtt.mappable_base, aperture_size); dev_priv->mm.gtt_mtrr = -1; } - io_mapping_free(dev_priv->mm.gtt_mapping); + io_mapping_free(dev_priv->gtt.mappable); out_rmmap: pci_iounmap(dev->pdev, dev_priv->regs); put_gmch: - i915_gem_gtt_fini(dev); + dev_priv->gtt.gtt_remove(dev); put_bridge: pci_dev_put(dev_priv->bridge_dev); free_priv: @@ -1673,11 +1696,11 @@ int i915_driver_unload(struct drm_device *dev) /* Cancel the retire work handler, which should be idle now. */ cancel_delayed_work_sync(&dev_priv->mm.retire_work); - io_mapping_free(dev_priv->mm.gtt_mapping); + io_mapping_free(dev_priv->gtt.mappable); if (dev_priv->mm.gtt_mtrr >= 0) { mtrr_del(dev_priv->mm.gtt_mtrr, - dev_priv->mm.gtt_base_addr, - dev_priv->mm.gtt->gtt_mappable_entries * PAGE_SIZE); + dev_priv->gtt.mappable_base, + dev_priv->gtt.mappable_end); dev_priv->mm.gtt_mtrr = -1; } @@ -1703,8 +1726,8 @@ int i915_driver_unload(struct drm_device *dev) } /* Free error state after interrupts are fully disabled. */ - del_timer_sync(&dev_priv->hangcheck_timer); - cancel_work_sync(&dev_priv->error_work); + del_timer_sync(&dev_priv->gpu_error.hangcheck_timer); + cancel_work_sync(&dev_priv->gpu_error.work); i915_destroy_error_state(dev); if (dev->pdev->msi_enabled) @@ -1723,9 +1746,6 @@ int i915_driver_unload(struct drm_device *dev) mutex_unlock(&dev->struct_mutex); i915_gem_cleanup_aliasing_ppgtt(dev); i915_gem_cleanup_stolen(dev); - drm_mm_takedown(&dev_priv->mm.stolen); - - intel_cleanup_overlay(dev); if (!I915_NEED_GFX_HWS(dev)) i915_free_hws(dev); @@ -1738,6 +1758,10 @@ int i915_driver_unload(struct drm_device *dev) intel_teardown_mchbar(dev); destroy_workqueue(dev_priv->wq); + pm_qos_remove_request(&dev_priv->pm_qos); + + if (dev_priv->slab) + kmem_cache_destroy(dev_priv->slab); pci_dev_put(dev_priv->bridge_dev); kfree(dev->dev_private); diff --git a/drivers/gpu/drm/i915/i915_drv.c b/drivers/gpu/drm/i915/i915_drv.c index 117265840b1f..c5b8c81b9440 100644 --- a/drivers/gpu/drm/i915/i915_drv.c +++ b/drivers/gpu/drm/i915/i915_drv.c @@ -276,6 +276,7 @@ static const struct intel_device_info intel_valleyview_m_info = { .has_bsd_ring = 1, .has_blt_ring = 1, .is_valleyview = 1, + .display_mmio_offset = VLV_DISPLAY_BASE, }; static const struct intel_device_info intel_valleyview_d_info = { @@ -285,6 +286,7 @@ static const struct intel_device_info intel_valleyview_d_info = { .has_bsd_ring = 1, .has_blt_ring = 1, .is_valleyview = 1, + .display_mmio_offset = VLV_DISPLAY_BASE, }; static const struct intel_device_info intel_haswell_d_info = { @@ -468,6 +470,13 @@ static int i915_drm_freeze(struct drm_device *dev) { struct drm_i915_private *dev_priv = dev->dev_private; + /* ignore lid events during suspend */ + mutex_lock(&dev_priv->modeset_restore_lock); + dev_priv->modeset_restore = MODESET_SUSPENDED; + mutex_unlock(&dev_priv->modeset_restore_lock); + + intel_set_power_well(dev, true); + drm_kms_helper_poll_disable(dev); pci_save_state(dev->pdev); @@ -492,9 +501,6 @@ static int i915_drm_freeze(struct drm_device *dev) intel_opregion_fini(dev); - /* Modeset on resume, not lid events */ - dev_priv->modeset_on_lid = 0; - console_lock(); intel_fbdev_set_suspend(dev, 1); console_unlock(); @@ -565,12 +571,11 @@ static int __i915_drm_thaw(struct drm_device *dev) intel_modeset_init_hw(dev); intel_modeset_setup_hw_state(dev, false); drm_irq_install(dev); + intel_hpd_init(dev); } intel_opregion_init(dev); - dev_priv->modeset_on_lid = 0; - /* * The console lock can be pretty contented on resume due * to all the printk activity. Try to keep it out of the hot @@ -583,6 +588,9 @@ static int __i915_drm_thaw(struct drm_device *dev) schedule_work(&dev_priv->console_resume_work); } + mutex_lock(&dev_priv->modeset_restore_lock); + dev_priv->modeset_restore = MODESET_DONE; + mutex_unlock(&dev_priv->modeset_restore_lock); return error; } @@ -778,9 +786,9 @@ int intel_gpu_reset(struct drm_device *dev) } /* Also reset the gpu hangman. */ - if (dev_priv->stop_rings) { + if (dev_priv->gpu_error.stop_rings) { DRM_DEBUG("Simulated gpu hang, resetting stop_rings\n"); - dev_priv->stop_rings = 0; + dev_priv->gpu_error.stop_rings = 0; if (ret == -ENODEV) { DRM_ERROR("Reset not implemented, but ignoring " "error for simulated gpu hangs\n"); @@ -819,12 +827,12 @@ int i915_reset(struct drm_device *dev) i915_gem_reset(dev); ret = -ENODEV; - if (get_seconds() - dev_priv->last_gpu_reset < 5) + if (get_seconds() - dev_priv->gpu_error.last_reset < 5) DRM_ERROR("GPU hanging too fast, declaring wedged!\n"); else ret = intel_gpu_reset(dev); - dev_priv->last_gpu_reset = get_seconds(); + dev_priv->gpu_error.last_reset = get_seconds(); if (ret) { DRM_ERROR("Failed to reset chip.\n"); mutex_unlock(&dev->struct_mutex); @@ -870,6 +878,7 @@ int i915_reset(struct drm_device *dev) drm_irq_uninstall(dev); drm_irq_install(dev); + intel_hpd_init(dev); } else { mutex_unlock(&dev->struct_mutex); } @@ -1113,102 +1122,6 @@ MODULE_LICENSE("GPL and additional rights"); ((HAS_FORCE_WAKE((dev_priv)->dev)) && \ ((reg) < 0x40000) && \ ((reg) != FORCEWAKE)) - -static bool IS_DISPLAYREG(u32 reg) -{ - /* - * This should make it easier to transition modules over to the - * new register block scheme, since we can do it incrementally. - */ - if (reg >= VLV_DISPLAY_BASE) - return false; - - if (reg >= RENDER_RING_BASE && - reg < RENDER_RING_BASE + 0xff) - return false; - if (reg >= GEN6_BSD_RING_BASE && - reg < GEN6_BSD_RING_BASE + 0xff) - return false; - if (reg >= BLT_RING_BASE && - reg < BLT_RING_BASE + 0xff) - return false; - - if (reg == PGTBL_ER) - return false; - - if (reg >= IPEIR_I965 && - reg < HWSTAM) - return false; - - if (reg == MI_MODE) - return false; - - if (reg == GFX_MODE_GEN7) - return false; - - if (reg == RENDER_HWS_PGA_GEN7 || - reg == BSD_HWS_PGA_GEN7 || - reg == BLT_HWS_PGA_GEN7) - return false; - - if (reg == GEN6_BSD_SLEEP_PSMI_CONTROL || - reg == GEN6_BSD_RNCID) - return false; - - if (reg == GEN6_BLITTER_ECOSKPD) - return false; - - if (reg >= 0x4000c && - reg <= 0x4002c) - return false; - - if (reg >= 0x4f000 && - reg <= 0x4f08f) - return false; - - if (reg >= 0x4f100 && - reg <= 0x4f11f) - return false; - - if (reg >= VLV_MASTER_IER && - reg <= GEN6_PMIER) - return false; - - if (reg >= FENCE_REG_SANDYBRIDGE_0 && - reg < (FENCE_REG_SANDYBRIDGE_0 + (16*8))) - return false; - - if (reg >= VLV_IIR_RW && - reg <= VLV_ISR) - return false; - - if (reg == FORCEWAKE_VLV || - reg == FORCEWAKE_ACK_VLV) - return false; - - if (reg == GEN6_GDRST) - return false; - - switch (reg) { - case _3D_CHICKEN3: - case IVB_CHICKEN3: - case GEN7_COMMON_SLICE_CHICKEN1: - case GEN7_L3CNTLREG1: - case GEN7_L3_CHICKEN_MODE_REGISTER: - case GEN7_ROW_CHICKEN2: - case GEN7_L3SQCREG4: - case GEN7_SQ_CHICKEN_MBCUNIT_CONFIG: - case GEN7_HALF_SLICE_CHICKEN1: - case GEN6_MBCTL: - case GEN6_UCGCTL2: - return false; - default: - break; - } - - return true; -} - static void ilk_dummy_write(struct drm_i915_private *dev_priv) { @@ -1232,8 +1145,6 @@ u##x i915_read##x(struct drm_i915_private *dev_priv, u32 reg) { \ if (dev_priv->forcewake_count == 0) \ dev_priv->gt.force_wake_put(dev_priv); \ spin_unlock_irqrestore(&dev_priv->gt_lock, irqflags); \ - } else if (IS_VALLEYVIEW(dev_priv->dev) && IS_DISPLAYREG(reg)) { \ - val = read##y(dev_priv->regs + reg + 0x180000); \ } else { \ val = read##y(dev_priv->regs + reg); \ } \ @@ -1260,11 +1171,7 @@ void i915_write##x(struct drm_i915_private *dev_priv, u32 reg, u##x val) { \ DRM_ERROR("Unknown unclaimed register before writing to %x\n", reg); \ I915_WRITE_NOTRACE(GEN7_ERR_INT, ERR_INT_MMIO_UNCLAIMED); \ } \ - if (IS_VALLEYVIEW(dev_priv->dev) && IS_DISPLAYREG(reg)) { \ - write##y(val, dev_priv->regs + reg + 0x180000); \ - } else { \ - write##y(val, dev_priv->regs + reg); \ - } \ + write##y(val, dev_priv->regs + reg); \ if (unlikely(__fifo_ret)) { \ gen6_gt_check_fifodbg(dev_priv); \ } \ diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h index 12ab3bdea54d..e95337c97459 100644 --- a/drivers/gpu/drm/i915/i915_drv.h +++ b/drivers/gpu/drm/i915/i915_drv.h @@ -30,6 +30,8 @@ #ifndef _I915_DRV_H_ #define _I915_DRV_H_ +#include <uapi/drm/i915_drm.h> + #include "i915_reg.h" #include "intel_bios.h" #include "intel_ringbuffer.h" @@ -40,6 +42,7 @@ #include <linux/backlight.h> #include <linux/intel-iommu.h> #include <linux/kref.h> +#include <linux/pm_qos.h> /* General customization: */ @@ -83,7 +86,12 @@ enum port { }; #define port_name(p) ((p) + 'A') -#define I915_GEM_GPU_DOMAINS (~(I915_GEM_DOMAIN_CPU | I915_GEM_DOMAIN_GTT)) +#define I915_GEM_GPU_DOMAINS \ + (I915_GEM_DOMAIN_RENDER | \ + I915_GEM_DOMAIN_SAMPLER | \ + I915_GEM_DOMAIN_COMMAND | \ + I915_GEM_DOMAIN_INSTRUCTION | \ + I915_GEM_DOMAIN_VERTEX) #define for_each_pipe(p) for ((p) = 0; (p) < dev_priv->num_pipe; (p)++) @@ -101,6 +109,19 @@ struct intel_pch_pll { }; #define I915_NUM_PLLS 2 +/* Used by dp and fdi links */ +struct intel_link_m_n { + uint32_t tu; + uint32_t gmch_m; + uint32_t gmch_n; + uint32_t link_m; + uint32_t link_n; +}; + +void intel_link_compute_m_n(int bpp, int nlanes, + int pixel_clock, int link_clock, + struct intel_link_m_n *m_n); + struct intel_ddi_plls { int spll_refcount; int wrpll1_refcount; @@ -279,6 +300,7 @@ struct drm_i915_display_funcs { struct drm_i915_gem_object *obj); int (*update_plane)(struct drm_crtc *crtc, struct drm_framebuffer *fb, int x, int y); + void (*hpd_irq_setup)(struct drm_device *dev); /* clock updates for mode set */ /* cursor updates */ /* render clock increase/decrease */ @@ -318,6 +340,7 @@ struct drm_i915_gt_funcs { DEV_INFO_FLAG(has_llc) struct intel_device_info { + u32 display_mmio_offset; u8 gen; u8 is_mobile:1; u8 is_i85x:1; @@ -345,6 +368,50 @@ struct intel_device_info { u8 has_llc:1; }; +enum i915_cache_level { + I915_CACHE_NONE = 0, + I915_CACHE_LLC, + I915_CACHE_LLC_MLC, /* gen6+, in docs at least! */ +}; + +/* The Graphics Translation Table is the way in which GEN hardware translates a + * Graphics Virtual Address into a Physical Address. In addition to the normal + * collateral associated with any va->pa translations GEN hardware also has a + * portion of the GTT which can be mapped by the CPU and remain both coherent + * and correct (in cases like swizzling). That region is referred to as GMADR in + * the spec. + */ +struct i915_gtt { + unsigned long start; /* Start offset of used GTT */ + size_t total; /* Total size GTT can map */ + size_t stolen_size; /* Total size of stolen memory */ + + unsigned long mappable_end; /* End offset that we can CPU map */ + struct io_mapping *mappable; /* Mapping to our CPU mappable region */ + phys_addr_t mappable_base; /* PA of our GMADR */ + + /** "Graphics Stolen Memory" holds the global PTEs */ + void __iomem *gsm; + + bool do_idle_maps; + dma_addr_t scratch_page_dma; + struct page *scratch_page; + + /* global gtt ops */ + int (*gtt_probe)(struct drm_device *dev, size_t *gtt_total, + size_t *stolen, phys_addr_t *mappable_base, + unsigned long *mappable_end); + void (*gtt_remove)(struct drm_device *dev); + void (*gtt_clear_range)(struct drm_device *dev, + unsigned int first_entry, + unsigned int num_entries); + void (*gtt_insert_entries)(struct drm_device *dev, + struct sg_table *st, + unsigned int pg_start, + enum i915_cache_level cache_level); +}; +#define gtt_total_entries(gtt) ((gtt).total >> PAGE_SHIFT) + #define I915_PPGTT_PD_ENTRIES 512 #define I915_PPGTT_PT_ENTRIES 1024 struct i915_hw_ppgtt { @@ -354,6 +421,16 @@ struct i915_hw_ppgtt { uint32_t pd_offset; dma_addr_t *pt_dma_addr; dma_addr_t scratch_page_dma_addr; + + /* pte functions, mirroring the interface of the global gtt. */ + void (*clear_range)(struct i915_hw_ppgtt *ppgtt, + unsigned int first_entry, + unsigned int num_entries); + void (*insert_entries)(struct i915_hw_ppgtt *ppgtt, + struct sg_table *st, + unsigned int pg_start, + enum i915_cache_level cache_level); + void (*cleanup)(struct i915_hw_ppgtt *ppgtt); }; @@ -580,6 +657,9 @@ struct intel_gen6_power_mgmt { struct mutex hw_lock; }; +/* defined intel_pm.c */ +extern spinlock_t mchdev_lock; + struct intel_ilk_power_mgmt { u8 cur_delay; u8 min_delay; @@ -620,8 +700,162 @@ struct intel_l3_parity { struct work_struct error_work; }; +struct i915_gem_mm { + /** Memory allocator for GTT stolen memory */ + struct drm_mm stolen; + /** Memory allocator for GTT */ + struct drm_mm gtt_space; + /** List of all objects in gtt_space. Used to restore gtt + * mappings on resume */ + struct list_head bound_list; + /** + * List of objects which are not bound to the GTT (thus + * are idle and not used by the GPU) but still have + * (presumably uncached) pages still attached. + */ + struct list_head unbound_list; + + /** Usable portion of the GTT for GEM */ + unsigned long stolen_base; /* limited to low memory (32-bit) */ + + int gtt_mtrr; + + /** PPGTT used for aliasing the PPGTT with the GTT */ + struct i915_hw_ppgtt *aliasing_ppgtt; + + struct shrinker inactive_shrinker; + bool shrinker_no_lock_stealing; + + /** + * List of objects currently involved in rendering. + * + * Includes buffers having the contents of their GPU caches + * flushed, not necessarily primitives. last_rendering_seqno + * represents when the rendering involved will be completed. + * + * A reference is held on the buffer while on this list. + */ + struct list_head active_list; + + /** + * LRU list of objects which are not in the ringbuffer and + * are ready to unbind, but are still in the GTT. + * + * last_rendering_seqno is 0 while an object is in this list. + * + * A reference is not held on the buffer while on this list, + * as merely being GTT-bound shouldn't prevent its being + * freed, and we'll pull it off the list in the free path. + */ + struct list_head inactive_list; + + /** LRU list of objects with fence regs on them. */ + struct list_head fence_list; + + /** + * We leave the user IRQ off as much as possible, + * but this means that requests will finish and never + * be retired once the system goes idle. Set a timer to + * fire periodically while the ring is running. When it + * fires, go retire requests. + */ + struct delayed_work retire_work; + + /** + * Are we in a non-interruptible section of code like + * modesetting? + */ + bool interruptible; + + /** + * Flag if the X Server, and thus DRM, is not currently in + * control of the device. + * + * This is set between LeaveVT and EnterVT. It needs to be + * replaced with a semaphore. It also needs to be + * transitioned away from for kernel modesetting. + */ + int suspended; + + /** Bit 6 swizzling required for X tiling */ + uint32_t bit_6_swizzle_x; + /** Bit 6 swizzling required for Y tiling */ + uint32_t bit_6_swizzle_y; + + /* storage for physical objects */ + struct drm_i915_gem_phys_object *phys_objs[I915_MAX_PHYS_OBJECT]; + + /* accounting, useful for userland debugging */ + size_t object_memory; + u32 object_count; +}; + +struct i915_gpu_error { + /* For hangcheck timer */ +#define DRM_I915_HANGCHECK_PERIOD 1500 /* in ms */ +#define DRM_I915_HANGCHECK_JIFFIES msecs_to_jiffies(DRM_I915_HANGCHECK_PERIOD) + struct timer_list hangcheck_timer; + int hangcheck_count; + uint32_t last_acthd[I915_NUM_RINGS]; + uint32_t prev_instdone[I915_NUM_INSTDONE_REG]; + + /* For reset and error_state handling. */ + spinlock_t lock; + /* Protected by the above dev->gpu_error.lock. */ + struct drm_i915_error_state *first_error; + struct work_struct work; + + unsigned long last_reset; + + /** + * State variable and reset counter controlling the reset flow + * + * Upper bits are for the reset counter. This counter is used by the + * wait_seqno code to race-free noticed that a reset event happened and + * that it needs to restart the entire ioctl (since most likely the + * seqno it waited for won't ever signal anytime soon). + * + * This is important for lock-free wait paths, where no contended lock + * naturally enforces the correct ordering between the bail-out of the + * waiter and the gpu reset work code. + * + * Lowest bit controls the reset state machine: Set means a reset is in + * progress. This state will (presuming we don't have any bugs) decay + * into either unset (successful reset) or the special WEDGED value (hw + * terminally sour). All waiters on the reset_queue will be woken when + * that happens. + */ + atomic_t reset_counter; + + /** + * Special values/flags for reset_counter + * + * Note that the code relies on + * I915_WEDGED & I915_RESET_IN_PROGRESS_FLAG + * being true. + */ +#define I915_RESET_IN_PROGRESS_FLAG 1 +#define I915_WEDGED 0xffffffff + + /** + * Waitqueue to signal when the reset has completed. Used by clients + * that wait for dev_priv->mm.wedged to settle. + */ + wait_queue_head_t reset_queue; + + /* For gpu hang simulation. */ + unsigned int stop_rings; +}; + +enum modeset_restore { + MODESET_ON_LID_OPEN, + MODESET_DONE, + MODESET_SUSPENDED, +}; + typedef struct drm_i915_private { struct drm_device *dev; + struct kmem_cache *slab; const struct intel_device_info *info; @@ -636,10 +870,11 @@ typedef struct drm_i915_private { /** forcewake_count is protected by gt_lock */ unsigned forcewake_count; /** gt_lock is also taken in irq contexts. */ - struct spinlock gt_lock; + spinlock_t gt_lock; struct intel_gmbus gmbus[GMBUS_NUM_PORTS]; + /** gmbus_mutex protects against concurrent usage of the single hw gmbus * controller on different i2c buses. */ struct mutex gmbus_mutex; @@ -649,9 +884,11 @@ typedef struct drm_i915_private { */ uint32_t gpio_mmio_base; + wait_queue_head_t gmbus_wait_queue; + struct pci_dev *bridge_dev; struct intel_ring_buffer ring[I915_NUM_RINGS]; - uint32_t next_seqno; + uint32_t last_seqno, next_seqno; drm_dma_handle_t *status_page_dmah; struct resource mch_res; @@ -661,31 +898,24 @@ typedef struct drm_i915_private { /* protects the irq masks */ spinlock_t irq_lock; + /* To control wakeup latency, e.g. for irq-driven dp aux transfers. */ + struct pm_qos_request pm_qos; + /* DPIO indirect register protection */ - spinlock_t dpio_lock; + struct mutex dpio_lock; /** Cached value of IMR to avoid reads in updating the bitfield */ u32 pipestat[2]; u32 irq_mask; u32 gt_irq_mask; - u32 pch_irq_mask; u32 hotplug_supported_mask; struct work_struct hotplug_work; + bool enable_hotplug_processing; int num_pipe; int num_pch_pll; - /* For hangcheck timer */ -#define DRM_I915_HANGCHECK_PERIOD 1500 /* in ms */ -#define DRM_I915_HANGCHECK_JIFFIES msecs_to_jiffies(DRM_I915_HANGCHECK_PERIOD) - struct timer_list hangcheck_timer; - int hangcheck_count; - uint32_t last_acthd[I915_NUM_RINGS]; - uint32_t prev_instdone[I915_NUM_INSTDONE_REG]; - - unsigned int stop_rings; - unsigned long cfb_size; unsigned int cfb_fb; enum plane cfb_plane; @@ -696,7 +926,7 @@ typedef struct drm_i915_private { /* overlay */ struct intel_overlay *overlay; - bool sprite_scaling_enabled; + unsigned int sprite_scaling_enabled; /* LVDS info */ int backlight_level; /* restore backlight to this value */ @@ -713,7 +943,6 @@ typedef struct drm_i915_private { unsigned int display_clock_mode:1; int lvds_ssc_freq; unsigned int bios_lvds_val; /* initial [PCH_]LVDS reg val in VBIOS */ - unsigned int lvds_val; /* used for checking LVDS channel mode */ struct { int rate; int lanes; @@ -734,11 +963,6 @@ typedef struct drm_i915_private { unsigned int fsb_freq, mem_freq, is_ddr3; - spinlock_t error_lock; - /* Protected by dev->error_lock. */ - struct drm_i915_error_state *first_error; - struct work_struct error_work; - struct completion error_completion; struct workqueue_struct *wq; /* Display functions */ @@ -750,115 +974,12 @@ typedef struct drm_i915_private { unsigned long quirks; - /* Register state */ - bool modeset_on_lid; + enum modeset_restore modeset_restore; + struct mutex modeset_restore_lock; - struct { - /** Bridge to intel-gtt-ko */ - struct intel_gtt *gtt; - /** Memory allocator for GTT stolen memory */ - struct drm_mm stolen; - /** Memory allocator for GTT */ - struct drm_mm gtt_space; - /** List of all objects in gtt_space. Used to restore gtt - * mappings on resume */ - struct list_head bound_list; - /** - * List of objects which are not bound to the GTT (thus - * are idle and not used by the GPU) but still have - * (presumably uncached) pages still attached. - */ - struct list_head unbound_list; - - /** Usable portion of the GTT for GEM */ - unsigned long gtt_start; - unsigned long gtt_mappable_end; - unsigned long gtt_end; - - struct io_mapping *gtt_mapping; - phys_addr_t gtt_base_addr; - int gtt_mtrr; - - /** PPGTT used for aliasing the PPGTT with the GTT */ - struct i915_hw_ppgtt *aliasing_ppgtt; - - struct shrinker inactive_shrinker; - bool shrinker_no_lock_stealing; - - /** - * List of objects currently involved in rendering. - * - * Includes buffers having the contents of their GPU caches - * flushed, not necessarily primitives. last_rendering_seqno - * represents when the rendering involved will be completed. - * - * A reference is held on the buffer while on this list. - */ - struct list_head active_list; - - /** - * LRU list of objects which are not in the ringbuffer and - * are ready to unbind, but are still in the GTT. - * - * last_rendering_seqno is 0 while an object is in this list. - * - * A reference is not held on the buffer while on this list, - * as merely being GTT-bound shouldn't prevent its being - * freed, and we'll pull it off the list in the free path. - */ - struct list_head inactive_list; - - /** LRU list of objects with fence regs on them. */ - struct list_head fence_list; - - /** - * We leave the user IRQ off as much as possible, - * but this means that requests will finish and never - * be retired once the system goes idle. Set a timer to - * fire periodically while the ring is running. When it - * fires, go retire requests. - */ - struct delayed_work retire_work; - - /** - * Are we in a non-interruptible section of code like - * modesetting? - */ - bool interruptible; - - /** - * Flag if the X Server, and thus DRM, is not currently in - * control of the device. - * - * This is set between LeaveVT and EnterVT. It needs to be - * replaced with a semaphore. It also needs to be - * transitioned away from for kernel modesetting. - */ - int suspended; - - /** - * Flag if the hardware appears to be wedged. - * - * This is set when attempts to idle the device timeout. - * It prevents command submission from occurring and makes - * every pending request fail - */ - atomic_t wedged; - - /** Bit 6 swizzling required for X tiling */ - uint32_t bit_6_swizzle_x; - /** Bit 6 swizzling required for Y tiling */ - uint32_t bit_6_swizzle_y; - - /* storage for physical objects */ - struct drm_i915_gem_phys_object *phys_objs[I915_MAX_PHYS_OBJECT]; - - /* accounting, useful for userland debugging */ - size_t gtt_total; - size_t mappable_gtt_total; - size_t object_memory; - u32 object_count; - } mm; + struct i915_gtt gtt; + + struct i915_gem_mm mm; /* Kernel Modesetting */ @@ -900,7 +1021,7 @@ typedef struct drm_i915_private { struct drm_mm_node *compressed_fb; struct drm_mm_node *compressed_llb; - unsigned long last_gpu_reset; + struct i915_gpu_error gpu_error; /* list of fbdev register on this device */ struct intel_fbdev *fbdev; @@ -919,7 +1040,7 @@ typedef struct drm_i915_private { bool hw_contexts_disabled; uint32_t hw_context_size; - bool fdi_rx_polarity_reversed; + u32 fdi_rx_config; struct i915_suspend_saved_registers regfile; @@ -940,11 +1061,7 @@ enum hdmi_force_audio { HDMI_AUDIO_ON, /* force turn on HDMI audio */ }; -enum i915_cache_level { - I915_CACHE_NONE = 0, - I915_CACHE_LLC, - I915_CACHE_LLC_MLC, /* gen6+, in docs at least! */ -}; +#define I915_GTT_RESERVED ((struct drm_mm_node *)0x1) struct drm_i915_gem_object_ops { /* Interface between the GEM object and its backing storage. @@ -971,6 +1088,8 @@ struct drm_i915_gem_object { /** Current space allocated to this object in the GTT, if any. */ struct drm_mm_node *gtt_space; + /** Stolen memory for this object, instead of being backed by shmem. */ + struct drm_mm_node *stolen; struct list_head gtt_list; /** This object's place on the active/inactive lists */ @@ -1096,13 +1215,6 @@ struct drm_i915_gem_object { /** for phy allocated objects */ struct drm_i915_gem_phys_object *phys_obj; - - /** - * Number of crtcs where this object is currently the fb, but - * will be page flipped away on the next vblank. When it - * reaches 0, dev_priv->pending_flip_queue will be woken up. - */ - atomic_t pending_flip; }; #define to_gem_object(obj) (&((struct drm_i915_gem_object *)(obj))->base) @@ -1141,7 +1253,7 @@ struct drm_i915_gem_request { struct drm_i915_file_private { struct { - struct spinlock lock; + spinlock_t lock; struct list_head request_list; } mm; struct idr context_idr; @@ -1227,6 +1339,8 @@ struct drm_i915_file_private { #define HAS_PIPE_CONTROL(dev) (INTEL_INFO(dev)->gen >= 5) +#define HAS_DDI(dev) (IS_HASWELL(dev)) + #define INTEL_PCH_DEVICE_ID_MASK 0xff00 #define INTEL_PCH_IBX_DEVICE_ID_TYPE 0x3b00 #define INTEL_PCH_CPT_DEVICE_ID_TYPE 0x1c00 @@ -1323,6 +1437,7 @@ void i915_hangcheck_elapsed(unsigned long data); void i915_handle_error(struct drm_device *dev, bool wedged); extern void intel_irq_init(struct drm_device *dev); +extern void intel_hpd_init(struct drm_device *dev); extern void intel_gt_init(struct drm_device *dev); extern void intel_gt_reset(struct drm_device *dev); @@ -1391,18 +1506,22 @@ int i915_gem_get_aperture_ioctl(struct drm_device *dev, void *data, int i915_gem_wait_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv); void i915_gem_load(struct drm_device *dev); +void *i915_gem_object_alloc(struct drm_device *dev); +void i915_gem_object_free(struct drm_i915_gem_object *obj); int i915_gem_init_object(struct drm_gem_object *obj); void i915_gem_object_init(struct drm_i915_gem_object *obj, const struct drm_i915_gem_object_ops *ops); struct drm_i915_gem_object *i915_gem_alloc_object(struct drm_device *dev, size_t size); void i915_gem_free_object(struct drm_gem_object *obj); + int __must_check i915_gem_object_pin(struct drm_i915_gem_object *obj, uint32_t alignment, bool map_and_fenceable, bool nonblocking); void i915_gem_object_unpin(struct drm_i915_gem_object *obj); int __must_check i915_gem_object_unbind(struct drm_i915_gem_object *obj); +int i915_gem_object_put_pages(struct drm_i915_gem_object *obj); void i915_gem_release_mmap(struct drm_i915_gem_object *obj); void i915_gem_lastclose(struct drm_device *dev); @@ -1454,8 +1573,8 @@ i915_seqno_passed(uint32_t seq1, uint32_t seq2) return (int32_t)(seq1 - seq2) >= 0; } -extern int i915_gem_get_seqno(struct drm_device *dev, u32 *seqno); - +int __must_check i915_gem_get_seqno(struct drm_device *dev, u32 *seqno); +int __must_check i915_gem_set_seqno(struct drm_device *dev, u32 seqno); int __must_check i915_gem_object_get_fence(struct drm_i915_gem_object *obj); int __must_check i915_gem_object_put_fence(struct drm_i915_gem_object *obj); @@ -1481,8 +1600,18 @@ i915_gem_object_unpin_fence(struct drm_i915_gem_object *obj) void i915_gem_retire_requests(struct drm_device *dev); void i915_gem_retire_requests_ring(struct intel_ring_buffer *ring); -int __must_check i915_gem_check_wedge(struct drm_i915_private *dev_priv, +int __must_check i915_gem_check_wedge(struct i915_gpu_error *error, bool interruptible); +static inline bool i915_reset_in_progress(struct i915_gpu_error *error) +{ + return unlikely(atomic_read(&error->reset_counter) + & I915_RESET_IN_PROGRESS_FLAG); +} + +static inline bool i915_terminally_wedged(struct i915_gpu_error *error) +{ + return atomic_read(&error->reset_counter) == I915_WEDGED; +} void i915_gem_reset(struct drm_device *dev); void i915_gem_clflush_object(struct drm_i915_gem_object *obj); @@ -1523,9 +1652,10 @@ void i915_gem_free_all_phys_object(struct drm_device *dev); void i915_gem_release(struct drm_device *dev, struct drm_file *file); uint32_t -i915_gem_get_unfenced_gtt_alignment(struct drm_device *dev, - uint32_t size, - int tiling_mode); +i915_gem_get_gtt_size(struct drm_device *dev, uint32_t size, int tiling_mode); +uint32_t +i915_gem_get_gtt_alignment(struct drm_device *dev, uint32_t size, + int tiling_mode, bool fenced); int i915_gem_object_set_cache_level(struct drm_i915_gem_object *obj, enum i915_cache_level cache_level); @@ -1548,7 +1678,6 @@ int i915_gem_context_destroy_ioctl(struct drm_device *dev, void *data, struct drm_file *file); /* i915_gem_gtt.c */ -int __must_check i915_gem_init_aliasing_ppgtt(struct drm_device *dev); void i915_gem_cleanup_aliasing_ppgtt(struct drm_device *dev); void i915_ppgtt_bind_object(struct i915_hw_ppgtt *ppgtt, struct drm_i915_gem_object *obj, @@ -1562,12 +1691,10 @@ void i915_gem_gtt_bind_object(struct drm_i915_gem_object *obj, enum i915_cache_level cache_level); void i915_gem_gtt_unbind_object(struct drm_i915_gem_object *obj); void i915_gem_gtt_finish_object(struct drm_i915_gem_object *obj); -void i915_gem_init_global_gtt(struct drm_device *dev, - unsigned long start, - unsigned long mappable_end, - unsigned long end); +void i915_gem_init_global_gtt(struct drm_device *dev); +void i915_gem_setup_global_gtt(struct drm_device *dev, unsigned long start, + unsigned long mappable_end, unsigned long end); int i915_gem_gtt_init(struct drm_device *dev); -void i915_gem_gtt_fini(struct drm_device *dev); static inline void i915_gem_chipset_flush(struct drm_device *dev) { if (INTEL_INFO(dev)->gen < 6) @@ -1585,9 +1712,22 @@ int i915_gem_evict_everything(struct drm_device *dev); /* i915_gem_stolen.c */ int i915_gem_init_stolen(struct drm_device *dev); +int i915_gem_stolen_setup_compression(struct drm_device *dev, int size); +void i915_gem_stolen_cleanup_compression(struct drm_device *dev); void i915_gem_cleanup_stolen(struct drm_device *dev); +struct drm_i915_gem_object * +i915_gem_object_create_stolen(struct drm_device *dev, u32 size); +void i915_gem_object_release_stolen(struct drm_i915_gem_object *obj); /* i915_gem_tiling.c */ +inline static bool i915_gem_object_needs_bit17_swizzle(struct drm_i915_gem_object *obj) +{ + drm_i915_private_t *dev_priv = obj->base.dev->dev_private; + + return dev_priv->mm.bit_6_swizzle_x == I915_BIT_6_SWIZZLE_9_10_17 && + obj->tiling_mode != I915_TILING_NONE; +} + void i915_gem_detect_bit_6_swizzle(struct drm_device *dev); void i915_gem_object_do_bit_17_swizzle(struct drm_i915_gem_object *obj); void i915_gem_object_save_bit_17_swizzle(struct drm_i915_gem_object *obj); @@ -1613,9 +1753,9 @@ void i915_debugfs_cleanup(struct drm_minor *minor); extern int i915_save_state(struct drm_device *dev); extern int i915_restore_state(struct drm_device *dev); -/* i915_suspend.c */ -extern int i915_save_state(struct drm_device *dev); -extern int i915_restore_state(struct drm_device *dev); +/* i915_ums.c */ +void i915_save_display_reg(struct drm_device *dev); +void i915_restore_display_reg(struct drm_device *dev); /* i915_sysfs.c */ void i915_setup_sysfs(struct drm_device *dev_priv); @@ -1672,6 +1812,7 @@ extern void intel_modeset_cleanup(struct drm_device *dev); extern int intel_modeset_vga_set_state(struct drm_device *dev, bool state); extern void intel_modeset_setup_hw_state(struct drm_device *dev, bool force_restore); +extern void i915_redisable_vga(struct drm_device *dev); extern bool intel_fbc_enabled(struct drm_device *dev); extern void intel_disable_fbc(struct drm_device *dev); extern bool ironlake_set_drps(struct drm_device *dev, u8 val); @@ -1744,5 +1885,19 @@ __i915_write(64, q) #define POSTING_READ(reg) (void)I915_READ_NOTRACE(reg) #define POSTING_READ16(reg) (void)I915_READ16_NOTRACE(reg) +/* "Broadcast RGB" property */ +#define INTEL_BROADCAST_RGB_AUTO 0 +#define INTEL_BROADCAST_RGB_FULL 1 +#define INTEL_BROADCAST_RGB_LIMITED 2 + +static inline uint32_t i915_vgacntrl_reg(struct drm_device *dev) +{ + if (HAS_PCH_SPLIT(dev)) + return CPU_VGACNTRL; + else if (IS_VALLEYVIEW(dev)) + return VLV_VGACNTRL; + else + return VGACNTRL; +} #endif diff --git a/drivers/gpu/drm/i915/i915_gem.c b/drivers/gpu/drm/i915/i915_gem.c index 8febea6daa08..8413ffced815 100644 --- a/drivers/gpu/drm/i915/i915_gem.c +++ b/drivers/gpu/drm/i915/i915_gem.c @@ -87,47 +87,43 @@ static void i915_gem_info_remove_obj(struct drm_i915_private *dev_priv, } static int -i915_gem_wait_for_error(struct drm_device *dev) +i915_gem_wait_for_error(struct i915_gpu_error *error) { - struct drm_i915_private *dev_priv = dev->dev_private; - struct completion *x = &dev_priv->error_completion; - unsigned long flags; int ret; - if (!atomic_read(&dev_priv->mm.wedged)) +#define EXIT_COND (!i915_reset_in_progress(error)) + if (EXIT_COND) return 0; + /* GPU is already declared terminally dead, give up. */ + if (i915_terminally_wedged(error)) + return -EIO; + /* * Only wait 10 seconds for the gpu reset to complete to avoid hanging * userspace. If it takes that long something really bad is going on and * we should simply try to bail out and fail as gracefully as possible. */ - ret = wait_for_completion_interruptible_timeout(x, 10*HZ); + ret = wait_event_interruptible_timeout(error->reset_queue, + EXIT_COND, + 10*HZ); if (ret == 0) { DRM_ERROR("Timed out waiting for the gpu reset to complete\n"); return -EIO; } else if (ret < 0) { return ret; } +#undef EXIT_COND - if (atomic_read(&dev_priv->mm.wedged)) { - /* GPU is hung, bump the completion count to account for - * the token we just consumed so that we never hit zero and - * end up waiting upon a subsequent completion event that - * will never happen. - */ - spin_lock_irqsave(&x->wait.lock, flags); - x->done++; - spin_unlock_irqrestore(&x->wait.lock, flags); - } return 0; } int i915_mutex_lock_interruptible(struct drm_device *dev) { + struct drm_i915_private *dev_priv = dev->dev_private; int ret; - ret = i915_gem_wait_for_error(dev); + ret = i915_gem_wait_for_error(&dev_priv->gpu_error); if (ret) return ret; @@ -149,6 +145,7 @@ int i915_gem_init_ioctl(struct drm_device *dev, void *data, struct drm_file *file) { + struct drm_i915_private *dev_priv = dev->dev_private; struct drm_i915_gem_init *args = data; if (drm_core_check_feature(dev, DRIVER_MODESET)) @@ -163,8 +160,9 @@ i915_gem_init_ioctl(struct drm_device *dev, void *data, return -ENODEV; mutex_lock(&dev->struct_mutex); - i915_gem_init_global_gtt(dev, args->gtt_start, - args->gtt_end, args->gtt_end); + i915_gem_setup_global_gtt(dev, args->gtt_start, args->gtt_end, + args->gtt_end); + dev_priv->gtt.mappable_end = args->gtt_end; mutex_unlock(&dev->struct_mutex); return 0; @@ -186,12 +184,24 @@ i915_gem_get_aperture_ioctl(struct drm_device *dev, void *data, pinned += obj->gtt_space->size; mutex_unlock(&dev->struct_mutex); - args->aper_size = dev_priv->mm.gtt_total; + args->aper_size = dev_priv->gtt.total; args->aper_available_size = args->aper_size - pinned; return 0; } +void *i915_gem_object_alloc(struct drm_device *dev) +{ + struct drm_i915_private *dev_priv = dev->dev_private; + return kmem_cache_alloc(dev_priv->slab, GFP_KERNEL | __GFP_ZERO); +} + +void i915_gem_object_free(struct drm_i915_gem_object *obj) +{ + struct drm_i915_private *dev_priv = obj->base.dev->dev_private; + kmem_cache_free(dev_priv->slab, obj); +} + static int i915_gem_create(struct drm_file *file, struct drm_device *dev, @@ -215,7 +225,7 @@ i915_gem_create(struct drm_file *file, if (ret) { drm_gem_object_release(&obj->base); i915_gem_info_remove_obj(dev->dev_private, obj->base.size); - kfree(obj); + i915_gem_object_free(obj); return ret; } @@ -259,14 +269,6 @@ i915_gem_create_ioctl(struct drm_device *dev, void *data, args->size, &args->handle); } -static int i915_gem_object_needs_bit17_swizzle(struct drm_i915_gem_object *obj) -{ - drm_i915_private_t *dev_priv = obj->base.dev->dev_private; - - return dev_priv->mm.bit_6_swizzle_x == I915_BIT_6_SWIZZLE_9_10_17 && - obj->tiling_mode != I915_TILING_NONE; -} - static inline int __copy_to_user_swizzled(char __user *cpu_vaddr, const char *gpu_vaddr, int gpu_offset, @@ -407,7 +409,6 @@ i915_gem_shmem_pread(struct drm_device *dev, loff_t offset; int shmem_page_offset, page_length, ret = 0; int obj_do_bit17_swizzling, page_do_bit17_swizzling; - int hit_slowpath = 0; int prefaulted = 0; int needs_clflush = 0; struct scatterlist *sg; @@ -469,7 +470,6 @@ i915_gem_shmem_pread(struct drm_device *dev, if (ret == 0) goto next_page; - hit_slowpath = 1; mutex_unlock(&dev->struct_mutex); if (!prefaulted) { @@ -502,12 +502,6 @@ next_page: out: i915_gem_object_unpin_pages(obj); - if (hit_slowpath) { - /* Fixup: Kill any reinstated backing storage pages */ - if (obj->madv == __I915_MADV_PURGED) - i915_gem_object_truncate(obj); - } - return ret; } @@ -641,7 +635,7 @@ i915_gem_gtt_pwrite_fast(struct drm_device *dev, * source page isn't available. Return the error and we'll * retry in the slow path. */ - if (fast_user_write(dev_priv->mm.gtt_mapping, page_base, + if (fast_user_write(dev_priv->gtt.mappable, page_base, page_offset, user_data, page_length)) { ret = -EFAULT; goto out_unpin; @@ -838,12 +832,13 @@ out: i915_gem_object_unpin_pages(obj); if (hit_slowpath) { - /* Fixup: Kill any reinstated backing storage pages */ - if (obj->madv == __I915_MADV_PURGED) - i915_gem_object_truncate(obj); - /* and flush dirty cachelines in case the object isn't in the cpu write - * domain anymore. */ - if (obj->base.write_domain != I915_GEM_DOMAIN_CPU) { + /* + * Fixup: Flush cpu caches in case we didn't flush the dirty + * cachelines in-line while writing and the object moved + * out of the cpu write domain while we've dropped the lock. + */ + if (!needs_clflush_after && + obj->base.write_domain != I915_GEM_DOMAIN_CPU) { i915_gem_clflush_object(obj); i915_gem_chipset_flush(dev); } @@ -940,26 +935,17 @@ unlock: } int -i915_gem_check_wedge(struct drm_i915_private *dev_priv, +i915_gem_check_wedge(struct i915_gpu_error *error, bool interruptible) { - if (atomic_read(&dev_priv->mm.wedged)) { - struct completion *x = &dev_priv->error_completion; - bool recovery_complete; - unsigned long flags; - - /* Give the error handler a chance to run. */ - spin_lock_irqsave(&x->wait.lock, flags); - recovery_complete = x->done > 0; - spin_unlock_irqrestore(&x->wait.lock, flags); - + if (i915_reset_in_progress(error)) { /* Non-interruptible callers can't handle -EAGAIN, hence return * -EIO unconditionally for these. */ if (!interruptible) return -EIO; - /* Recovery complete, but still wedged means reset failure. */ - if (recovery_complete) + /* Recovery complete, but the reset failed ... */ + if (i915_terminally_wedged(error)) return -EIO; return -EAGAIN; @@ -990,13 +976,22 @@ i915_gem_check_olr(struct intel_ring_buffer *ring, u32 seqno) * __wait_seqno - wait until execution of seqno has finished * @ring: the ring expected to report seqno * @seqno: duh! + * @reset_counter: reset sequence associated with the given seqno * @interruptible: do an interruptible wait (normally yes) * @timeout: in - how long to wait (NULL forever); out - how much time remaining * + * Note: It is of utmost importance that the passed in seqno and reset_counter + * values have been read by the caller in an smp safe manner. Where read-side + * locks are involved, it is sufficient to read the reset_counter before + * unlocking the lock that protects the seqno. For lockless tricks, the + * reset_counter _must_ be read before, and an appropriate smp_rmb must be + * inserted. + * * Returns 0 if the seqno was found within the alloted time. Else returns the * errno with remaining time filled in timeout argument. */ static int __wait_seqno(struct intel_ring_buffer *ring, u32 seqno, + unsigned reset_counter, bool interruptible, struct timespec *timeout) { drm_i915_private_t *dev_priv = ring->dev->dev_private; @@ -1026,7 +1021,8 @@ static int __wait_seqno(struct intel_ring_buffer *ring, u32 seqno, #define EXIT_COND \ (i915_seqno_passed(ring->get_seqno(ring, false), seqno) || \ - atomic_read(&dev_priv->mm.wedged)) + i915_reset_in_progress(&dev_priv->gpu_error) || \ + reset_counter != atomic_read(&dev_priv->gpu_error.reset_counter)) do { if (interruptible) end = wait_event_interruptible_timeout(ring->irq_queue, @@ -1036,7 +1032,14 @@ static int __wait_seqno(struct intel_ring_buffer *ring, u32 seqno, end = wait_event_timeout(ring->irq_queue, EXIT_COND, timeout_jiffies); - ret = i915_gem_check_wedge(dev_priv, interruptible); + /* We need to check whether any gpu reset happened in between + * the caller grabbing the seqno and now ... */ + if (reset_counter != atomic_read(&dev_priv->gpu_error.reset_counter)) + end = -EAGAIN; + + /* ... but upgrade the -EGAIN to an -EIO if the gpu is truely + * gone. */ + ret = i915_gem_check_wedge(&dev_priv->gpu_error, interruptible); if (ret) end = ret; } while (end == 0 && wait_forever); @@ -1082,7 +1085,7 @@ i915_wait_seqno(struct intel_ring_buffer *ring, uint32_t seqno) BUG_ON(!mutex_is_locked(&dev->struct_mutex)); BUG_ON(seqno == 0); - ret = i915_gem_check_wedge(dev_priv, interruptible); + ret = i915_gem_check_wedge(&dev_priv->gpu_error, interruptible); if (ret) return ret; @@ -1090,7 +1093,9 @@ i915_wait_seqno(struct intel_ring_buffer *ring, uint32_t seqno) if (ret) return ret; - return __wait_seqno(ring, seqno, interruptible, NULL); + return __wait_seqno(ring, seqno, + atomic_read(&dev_priv->gpu_error.reset_counter), + interruptible, NULL); } /** @@ -1137,6 +1142,7 @@ i915_gem_object_wait_rendering__nonblocking(struct drm_i915_gem_object *obj, struct drm_device *dev = obj->base.dev; struct drm_i915_private *dev_priv = dev->dev_private; struct intel_ring_buffer *ring = obj->ring; + unsigned reset_counter; u32 seqno; int ret; @@ -1147,7 +1153,7 @@ i915_gem_object_wait_rendering__nonblocking(struct drm_i915_gem_object *obj, if (seqno == 0) return 0; - ret = i915_gem_check_wedge(dev_priv, true); + ret = i915_gem_check_wedge(&dev_priv->gpu_error, true); if (ret) return ret; @@ -1155,8 +1161,9 @@ i915_gem_object_wait_rendering__nonblocking(struct drm_i915_gem_object *obj, if (ret) return ret; + reset_counter = atomic_read(&dev_priv->gpu_error.reset_counter); mutex_unlock(&dev->struct_mutex); - ret = __wait_seqno(ring, seqno, true, NULL); + ret = __wait_seqno(ring, seqno, reset_counter, true, NULL); mutex_lock(&dev->struct_mutex); i915_gem_retire_requests_ring(ring); @@ -1344,6 +1351,12 @@ int i915_gem_fault(struct vm_area_struct *vma, struct vm_fault *vmf) trace_i915_gem_object_fault(obj, page_offset, true, write); + /* Access to snoopable pages through the GTT is incoherent. */ + if (obj->cache_level != I915_CACHE_NONE && !HAS_LLC(dev)) { + ret = -EINVAL; + goto unlock; + } + /* Now bind it into the GTT if needed */ ret = i915_gem_object_pin(obj, 0, true, false); if (ret) @@ -1359,7 +1372,7 @@ int i915_gem_fault(struct vm_area_struct *vma, struct vm_fault *vmf) obj->fault_mappable = true; - pfn = ((dev_priv->mm.gtt_base_addr + obj->gtt_offset) >> PAGE_SHIFT) + + pfn = ((dev_priv->gtt.mappable_base + obj->gtt_offset) >> PAGE_SHIFT) + page_offset; /* Finally, remap it using the new GTT offset */ @@ -1374,7 +1387,7 @@ out: /* If this -EIO is due to a gpu hang, give the reset code a * chance to clean up the mess. Otherwise return the proper * SIGBUS. */ - if (!atomic_read(&dev_priv->mm.wedged)) + if (i915_terminally_wedged(&dev_priv->gpu_error)) return VM_FAULT_SIGBUS; case -EAGAIN: /* Give the error handler a chance to run and move the @@ -1432,7 +1445,7 @@ i915_gem_release_mmap(struct drm_i915_gem_object *obj) obj->fault_mappable = false; } -static uint32_t +uint32_t i915_gem_get_gtt_size(struct drm_device *dev, uint32_t size, int tiling_mode) { uint32_t gtt_size; @@ -1460,16 +1473,15 @@ i915_gem_get_gtt_size(struct drm_device *dev, uint32_t size, int tiling_mode) * Return the required GTT alignment for an object, taking into account * potential fence register mapping. */ -static uint32_t -i915_gem_get_gtt_alignment(struct drm_device *dev, - uint32_t size, - int tiling_mode) +uint32_t +i915_gem_get_gtt_alignment(struct drm_device *dev, uint32_t size, + int tiling_mode, bool fenced) { /* * Minimum alignment is 4k (GTT page size), but might be greater * if a fence register is needed for the object. */ - if (INTEL_INFO(dev)->gen >= 4 || + if (INTEL_INFO(dev)->gen >= 4 || (!fenced && IS_G33(dev)) || tiling_mode == I915_TILING_NONE) return 4096; @@ -1480,35 +1492,6 @@ i915_gem_get_gtt_alignment(struct drm_device *dev, return i915_gem_get_gtt_size(dev, size, tiling_mode); } -/** - * i915_gem_get_unfenced_gtt_alignment - return required GTT alignment for an - * unfenced object - * @dev: the device - * @size: size of the object - * @tiling_mode: tiling mode of the object - * - * Return the required GTT alignment for an object, only taking into account - * unfenced tiled surface requirements. - */ -uint32_t -i915_gem_get_unfenced_gtt_alignment(struct drm_device *dev, - uint32_t size, - int tiling_mode) -{ - /* - * Minimum alignment is 4k (GTT page size) for sane hw. - */ - if (INTEL_INFO(dev)->gen >= 4 || IS_G33(dev) || - tiling_mode == I915_TILING_NONE) - return 4096; - - /* Previous hardware however needs to be aligned to a power-of-two - * tile height. The simplest method for determining this is to reuse - * the power-of-tile object size. - */ - return i915_gem_get_gtt_size(dev, size, tiling_mode); -} - static int i915_gem_object_create_mmap_offset(struct drm_i915_gem_object *obj) { struct drm_i915_private *dev_priv = obj->base.dev->dev_private; @@ -1571,7 +1554,7 @@ i915_gem_mmap_gtt(struct drm_file *file, goto unlock; } - if (obj->base.size > dev_priv->mm.gtt_mappable_end) { + if (obj->base.size > dev_priv->gtt.mappable_end) { ret = -E2BIG; goto out; } @@ -1689,7 +1672,7 @@ i915_gem_object_put_pages_gtt(struct drm_i915_gem_object *obj) kfree(obj->pages); } -static int +int i915_gem_object_put_pages(struct drm_i915_gem_object *obj) { const struct drm_i915_gem_object_ops *ops = obj->ops; @@ -1862,6 +1845,11 @@ i915_gem_object_get_pages(struct drm_i915_gem_object *obj) if (obj->pages) return 0; + if (obj->madv != I915_MADV_WILLNEED) { + DRM_ERROR("Attempting to obtain a purgeable object\n"); + return -EINVAL; + } + BUG_ON(obj->pages_pin_count); ret = ops->get_pages(obj); @@ -1918,9 +1906,6 @@ i915_gem_object_move_to_inactive(struct drm_i915_gem_object *obj) BUG_ON(obj->base.write_domain & ~I915_GEM_GPU_DOMAINS); BUG_ON(!obj->active); - if (obj->pin_count) /* are we a framebuffer? */ - intel_mark_fb_idle(obj); - list_move_tail(&obj->mm_list, &dev_priv->mm.inactive_list); list_del_init(&obj->ring_list); @@ -1940,30 +1925,24 @@ i915_gem_object_move_to_inactive(struct drm_i915_gem_object *obj) } static int -i915_gem_handle_seqno_wrap(struct drm_device *dev) +i915_gem_init_seqno(struct drm_device *dev, u32 seqno) { struct drm_i915_private *dev_priv = dev->dev_private; struct intel_ring_buffer *ring; int ret, i, j; - /* The hardware uses various monotonic 32-bit counters, if we - * detect that they will wraparound we need to idle the GPU - * and reset those counters. - */ - ret = 0; + /* Carefully retire all requests without writing to the rings */ for_each_ring(ring, dev_priv, i) { - for (j = 0; j < ARRAY_SIZE(ring->sync_seqno); j++) - ret |= ring->sync_seqno[j] != 0; + ret = intel_ring_idle(ring); + if (ret) + return ret; } - if (ret == 0) - return ret; - - ret = i915_gpu_idle(dev); - if (ret) - return ret; - i915_gem_retire_requests(dev); + + /* Finally reset hw state */ for_each_ring(ring, dev_priv, i) { + intel_ring_init_seqno(ring, seqno); + for (j = 0; j < ARRAY_SIZE(ring->sync_seqno); j++) ring->sync_seqno[j] = 0; } @@ -1971,6 +1950,32 @@ i915_gem_handle_seqno_wrap(struct drm_device *dev) return 0; } +int i915_gem_set_seqno(struct drm_device *dev, u32 seqno) +{ + struct drm_i915_private *dev_priv = dev->dev_private; + int ret; + + if (seqno == 0) + return -EINVAL; + + /* HWS page needs to be set less than what we + * will inject to ring + */ + ret = i915_gem_init_seqno(dev, seqno - 1); + if (ret) + return ret; + + /* Carefully set the last_seqno value so that wrap + * detection still works + */ + dev_priv->next_seqno = seqno; + dev_priv->last_seqno = seqno - 1; + if (dev_priv->last_seqno == 0) + dev_priv->last_seqno--; + + return 0; +} + int i915_gem_get_seqno(struct drm_device *dev, u32 *seqno) { @@ -1978,14 +1983,14 @@ i915_gem_get_seqno(struct drm_device *dev, u32 *seqno) /* reserve 0 for non-seqno */ if (dev_priv->next_seqno == 0) { - int ret = i915_gem_handle_seqno_wrap(dev); + int ret = i915_gem_init_seqno(dev, 0); if (ret) return ret; dev_priv->next_seqno = 1; } - *seqno = dev_priv->next_seqno++; + *seqno = dev_priv->last_seqno = dev_priv->next_seqno++; return 0; } @@ -2052,7 +2057,7 @@ i915_add_request(struct intel_ring_buffer *ring, if (!dev_priv->mm.suspended) { if (i915_enable_hangcheck) { - mod_timer(&dev_priv->hangcheck_timer, + mod_timer(&dev_priv->gpu_error.hangcheck_timer, round_jiffies_up(jiffies + DRM_I915_HANGCHECK_JIFFIES)); } if (was_empty) { @@ -2317,10 +2322,12 @@ i915_gem_object_flush_active(struct drm_i915_gem_object *obj) int i915_gem_wait_ioctl(struct drm_device *dev, void *data, struct drm_file *file) { + drm_i915_private_t *dev_priv = dev->dev_private; struct drm_i915_gem_wait *args = data; struct drm_i915_gem_object *obj; struct intel_ring_buffer *ring = NULL; struct timespec timeout_stack, *timeout = NULL; + unsigned reset_counter; u32 seqno = 0; int ret = 0; @@ -2361,9 +2368,10 @@ i915_gem_wait_ioctl(struct drm_device *dev, void *data, struct drm_file *file) } drm_gem_object_unreference(&obj->base); + reset_counter = atomic_read(&dev_priv->gpu_error.reset_counter); mutex_unlock(&dev->struct_mutex); - ret = __wait_seqno(ring, seqno, true, timeout); + ret = __wait_seqno(ring, seqno, reset_counter, true, timeout); if (timeout) { WARN_ON(!timespec_valid(timeout)); args->timeout_ns = timespec_to_ns(timeout); @@ -2427,15 +2435,15 @@ static void i915_gem_object_finish_gtt(struct drm_i915_gem_object *obj) { u32 old_write_domain, old_read_domains; - /* Act a barrier for all accesses through the GTT */ - mb(); - /* Force a pagefault for domain tracking on next user access */ i915_gem_release_mmap(obj); if ((obj->base.read_domains & I915_GEM_DOMAIN_GTT) == 0) return; + /* Wait for any direct GTT access to complete */ + mb(); + old_read_domains = obj->base.read_domains; old_write_domain = obj->base.write_domain; @@ -2454,7 +2462,7 @@ int i915_gem_object_unbind(struct drm_i915_gem_object *obj) { drm_i915_private_t *dev_priv = obj->base.dev->dev_private; - int ret = 0; + int ret; if (obj->gtt_space == NULL) return 0; @@ -2521,52 +2529,38 @@ int i915_gpu_idle(struct drm_device *dev) return 0; } -static void sandybridge_write_fence_reg(struct drm_device *dev, int reg, - struct drm_i915_gem_object *obj) -{ - drm_i915_private_t *dev_priv = dev->dev_private; - uint64_t val; - - if (obj) { - u32 size = obj->gtt_space->size; - - val = (uint64_t)((obj->gtt_offset + size - 4096) & - 0xfffff000) << 32; - val |= obj->gtt_offset & 0xfffff000; - val |= (uint64_t)((obj->stride / 128) - 1) << - SANDYBRIDGE_FENCE_PITCH_SHIFT; - - if (obj->tiling_mode == I915_TILING_Y) - val |= 1 << I965_FENCE_TILING_Y_SHIFT; - val |= I965_FENCE_REG_VALID; - } else - val = 0; - - I915_WRITE64(FENCE_REG_SANDYBRIDGE_0 + reg * 8, val); - POSTING_READ(FENCE_REG_SANDYBRIDGE_0 + reg * 8); -} - static void i965_write_fence_reg(struct drm_device *dev, int reg, struct drm_i915_gem_object *obj) { drm_i915_private_t *dev_priv = dev->dev_private; + int fence_reg; + int fence_pitch_shift; uint64_t val; + if (INTEL_INFO(dev)->gen >= 6) { + fence_reg = FENCE_REG_SANDYBRIDGE_0; + fence_pitch_shift = SANDYBRIDGE_FENCE_PITCH_SHIFT; + } else { + fence_reg = FENCE_REG_965_0; + fence_pitch_shift = I965_FENCE_PITCH_SHIFT; + } + if (obj) { u32 size = obj->gtt_space->size; val = (uint64_t)((obj->gtt_offset + size - 4096) & 0xfffff000) << 32; val |= obj->gtt_offset & 0xfffff000; - val |= ((obj->stride / 128) - 1) << I965_FENCE_PITCH_SHIFT; + val |= (uint64_t)((obj->stride / 128) - 1) << fence_pitch_shift; if (obj->tiling_mode == I915_TILING_Y) val |= 1 << I965_FENCE_TILING_Y_SHIFT; val |= I965_FENCE_REG_VALID; } else val = 0; - I915_WRITE64(FENCE_REG_965_0 + reg * 8, val); - POSTING_READ(FENCE_REG_965_0 + reg * 8); + fence_reg += reg * 8; + I915_WRITE64(fence_reg, val); + POSTING_READ(fence_reg); } static void i915_write_fence_reg(struct drm_device *dev, int reg, @@ -2645,18 +2639,37 @@ static void i830_write_fence_reg(struct drm_device *dev, int reg, POSTING_READ(FENCE_REG_830_0 + reg * 4); } +inline static bool i915_gem_object_needs_mb(struct drm_i915_gem_object *obj) +{ + return obj && obj->base.read_domains & I915_GEM_DOMAIN_GTT; +} + static void i915_gem_write_fence(struct drm_device *dev, int reg, struct drm_i915_gem_object *obj) { + struct drm_i915_private *dev_priv = dev->dev_private; + + /* Ensure that all CPU reads are completed before installing a fence + * and all writes before removing the fence. + */ + if (i915_gem_object_needs_mb(dev_priv->fence_regs[reg].obj)) + mb(); + switch (INTEL_INFO(dev)->gen) { case 7: - case 6: sandybridge_write_fence_reg(dev, reg, obj); break; + case 6: case 5: case 4: i965_write_fence_reg(dev, reg, obj); break; case 3: i915_write_fence_reg(dev, reg, obj); break; case 2: i830_write_fence_reg(dev, reg, obj); break; - default: break; + default: BUG(); } + + /* And similarly be paranoid that no direct access to this region + * is reordered to before the fence is installed. + */ + if (i915_gem_object_needs_mb(obj)) + mb(); } static inline int fence_number(struct drm_i915_private *dev_priv, @@ -2686,7 +2699,7 @@ static void i915_gem_object_update_fence(struct drm_i915_gem_object *obj, } static int -i915_gem_object_flush_fence(struct drm_i915_gem_object *obj) +i915_gem_object_wait_fence(struct drm_i915_gem_object *obj) { if (obj->last_fenced_seqno) { int ret = i915_wait_seqno(obj->ring, obj->last_fenced_seqno); @@ -2696,12 +2709,6 @@ i915_gem_object_flush_fence(struct drm_i915_gem_object *obj) obj->last_fenced_seqno = 0; } - /* Ensure that all CPU reads are completed before installing a fence - * and all writes before removing the fence. - */ - if (obj->base.read_domains & I915_GEM_DOMAIN_GTT) - mb(); - obj->fenced_gpu_access = false; return 0; } @@ -2712,7 +2719,7 @@ i915_gem_object_put_fence(struct drm_i915_gem_object *obj) struct drm_i915_private *dev_priv = obj->base.dev->dev_private; int ret; - ret = i915_gem_object_flush_fence(obj); + ret = i915_gem_object_wait_fence(obj); if (ret) return ret; @@ -2786,7 +2793,7 @@ i915_gem_object_get_fence(struct drm_i915_gem_object *obj) * will need to serialise the write to the associated fence register? */ if (obj->fence_dirty) { - ret = i915_gem_object_flush_fence(obj); + ret = i915_gem_object_wait_fence(obj); if (ret) return ret; } @@ -2807,7 +2814,7 @@ i915_gem_object_get_fence(struct drm_i915_gem_object *obj) if (reg->obj) { struct drm_i915_gem_object *old = reg->obj; - ret = i915_gem_object_flush_fence(old); + ret = i915_gem_object_wait_fence(old); if (ret) return ret; @@ -2830,7 +2837,7 @@ static bool i915_gem_valid_gtt_space(struct drm_device *dev, /* On non-LLC machines we have to be careful when putting differing * types of snoopable memory together to avoid the prefetcher - * crossing memory domains and dieing. + * crossing memory domains and dying. */ if (HAS_LLC(dev)) return true; @@ -2908,21 +2915,16 @@ i915_gem_object_bind_to_gtt(struct drm_i915_gem_object *obj, bool mappable, fenceable; int ret; - if (obj->madv != I915_MADV_WILLNEED) { - DRM_ERROR("Attempting to bind a purgeable object\n"); - return -EINVAL; - } - fence_size = i915_gem_get_gtt_size(dev, obj->base.size, obj->tiling_mode); fence_alignment = i915_gem_get_gtt_alignment(dev, obj->base.size, - obj->tiling_mode); + obj->tiling_mode, true); unfenced_alignment = - i915_gem_get_unfenced_gtt_alignment(dev, + i915_gem_get_gtt_alignment(dev, obj->base.size, - obj->tiling_mode); + obj->tiling_mode, false); if (alignment == 0) alignment = map_and_fenceable ? fence_alignment : @@ -2938,7 +2940,7 @@ i915_gem_object_bind_to_gtt(struct drm_i915_gem_object *obj, * before evicting everything in a vain attempt to find space. */ if (obj->base.size > - (map_and_fenceable ? dev_priv->mm.gtt_mappable_end : dev_priv->mm.gtt_total)) { + (map_and_fenceable ? dev_priv->gtt.mappable_end : dev_priv->gtt.total)) { DRM_ERROR("Attempting to bind an object larger than the aperture\n"); return -E2BIG; } @@ -2959,7 +2961,7 @@ i915_gem_object_bind_to_gtt(struct drm_i915_gem_object *obj, if (map_and_fenceable) ret = drm_mm_insert_node_in_range_generic(&dev_priv->mm.gtt_space, node, size, alignment, obj->cache_level, - 0, dev_priv->mm.gtt_mappable_end); + 0, dev_priv->gtt.mappable_end); else ret = drm_mm_insert_node_generic(&dev_priv->mm.gtt_space, node, size, alignment, obj->cache_level); @@ -2999,7 +3001,7 @@ i915_gem_object_bind_to_gtt(struct drm_i915_gem_object *obj, (node->start & (fence_alignment - 1)) == 0; mappable = - obj->gtt_offset + obj->base.size <= dev_priv->mm.gtt_mappable_end; + obj->gtt_offset + obj->base.size <= dev_priv->gtt.mappable_end; obj->map_and_fenceable = mappable && fenceable; @@ -3019,6 +3021,13 @@ i915_gem_clflush_object(struct drm_i915_gem_object *obj) if (obj->pages == NULL) return; + /* + * Stolen memory is always coherent with the GPU as it is explicitly + * marked as wc by the system, or the system is cache-coherent. + */ + if (obj->stolen) + return; + /* If the GPU is snooping the contents of the CPU cache, * we do not need to manually clear the CPU cache lines. However, * the caches are only snooped when the render cache is @@ -3107,6 +3116,13 @@ i915_gem_object_set_to_gtt_domain(struct drm_i915_gem_object *obj, bool write) i915_gem_object_flush_cpu_write_domain(obj); + /* Serialise direct access to this object with the barriers for + * coherent writes from the GPU, by effectively invalidating the + * GTT domain upon first access. + */ + if ((obj->base.read_domains & I915_GEM_DOMAIN_GTT) == 0) + mb(); + old_write_domain = obj->base.write_domain; old_read_domains = obj->base.read_domains; @@ -3413,11 +3429,17 @@ i915_gem_ring_throttle(struct drm_device *dev, struct drm_file *file) unsigned long recent_enough = jiffies - msecs_to_jiffies(20); struct drm_i915_gem_request *request; struct intel_ring_buffer *ring = NULL; + unsigned reset_counter; u32 seqno = 0; int ret; - if (atomic_read(&dev_priv->mm.wedged)) - return -EIO; + ret = i915_gem_wait_for_error(&dev_priv->gpu_error); + if (ret) + return ret; + + ret = i915_gem_check_wedge(&dev_priv->gpu_error, false); + if (ret) + return ret; spin_lock(&file_priv->mm.lock); list_for_each_entry(request, &file_priv->mm.request_list, client_list) { @@ -3427,12 +3449,13 @@ i915_gem_ring_throttle(struct drm_device *dev, struct drm_file *file) ring = request->ring; seqno = request->seqno; } + reset_counter = atomic_read(&dev_priv->gpu_error.reset_counter); spin_unlock(&file_priv->mm.lock); if (seqno == 0) return 0; - ret = __wait_seqno(ring, seqno, true, NULL); + ret = __wait_seqno(ring, seqno, reset_counter, true, NULL); if (ret == 0) queue_delayed_work(dev_priv->wq, &dev_priv->mm.retire_work, 0); @@ -3706,14 +3729,14 @@ struct drm_i915_gem_object *i915_gem_alloc_object(struct drm_device *dev, { struct drm_i915_gem_object *obj; struct address_space *mapping; - u32 mask; + gfp_t mask; - obj = kzalloc(sizeof(*obj), GFP_KERNEL); + obj = i915_gem_object_alloc(dev); if (obj == NULL) return NULL; if (drm_gem_object_init(dev, &obj->base, size) != 0) { - kfree(obj); + i915_gem_object_free(obj); return NULL; } @@ -3785,6 +3808,7 @@ void i915_gem_free_object(struct drm_gem_object *gem_obj) obj->pages_pin_count = 0; i915_gem_object_put_pages(obj); i915_gem_object_free_mmap_offset(obj); + i915_gem_object_release_stolen(obj); BUG_ON(obj->pages); @@ -3795,7 +3819,7 @@ void i915_gem_free_object(struct drm_gem_object *gem_obj) i915_gem_info_remove_obj(dev_priv, obj->base.size); kfree(obj->bit_17); - kfree(obj); + i915_gem_object_free(obj); } int @@ -3829,7 +3853,7 @@ i915_gem_idle(struct drm_device *dev) * And not confound mm.suspended! */ dev_priv->mm.suspended = 1; - del_timer_sync(&dev_priv->hangcheck_timer); + del_timer_sync(&dev_priv->gpu_error.hangcheck_timer); i915_kernel_lost_context(dev); i915_gem_cleanup_ringbuffer(dev); @@ -3848,7 +3872,7 @@ void i915_gem_l3_remap(struct drm_device *dev) u32 misccpctl; int i; - if (!IS_IVYBRIDGE(dev)) + if (!HAS_L3_GPU_CACHE(dev)) return; if (!dev_priv->l3_parity.remap_info) @@ -3891,8 +3915,10 @@ void i915_gem_init_swizzling(struct drm_device *dev) I915_WRITE(TILECTL, I915_READ(TILECTL) | TILECTL_SWZCTL); if (IS_GEN6(dev)) I915_WRITE(ARB_MODE, _MASKED_BIT_ENABLE(ARB_MODE_SWIZZLE_SNB)); - else + else if (IS_GEN7(dev)) I915_WRITE(ARB_MODE, _MASKED_BIT_ENABLE(ARB_MODE_SWIZZLE_IVB)); + else + BUG(); } static bool @@ -3911,22 +3937,11 @@ intel_enable_blt(struct drm_device *dev) return true; } -int -i915_gem_init_hw(struct drm_device *dev) +static int i915_gem_init_rings(struct drm_device *dev) { - drm_i915_private_t *dev_priv = dev->dev_private; + struct drm_i915_private *dev_priv = dev->dev_private; int ret; - if (INTEL_INFO(dev)->gen < 6 && !intel_enable_gtt()) - return -EIO; - - if (IS_HASWELL(dev) && (I915_READ(0x120010) == 1)) - I915_WRITE(0x9008, I915_READ(0x9008) | 0xf0000); - - i915_gem_l3_remap(dev); - - i915_gem_init_swizzling(dev); - ret = intel_init_render_ring_buffer(dev); if (ret) return ret; @@ -3943,76 +3958,59 @@ i915_gem_init_hw(struct drm_device *dev) goto cleanup_bsd_ring; } - dev_priv->next_seqno = 1; - - /* - * XXX: There was some w/a described somewhere suggesting loading - * contexts before PPGTT. - */ - i915_gem_context_init(dev); - i915_gem_init_ppgtt(dev); + ret = i915_gem_set_seqno(dev, ((u32)~0 - 0x1000)); + if (ret) + goto cleanup_blt_ring; return 0; +cleanup_blt_ring: + intel_cleanup_ring_buffer(&dev_priv->ring[BCS]); cleanup_bsd_ring: intel_cleanup_ring_buffer(&dev_priv->ring[VCS]); cleanup_render_ring: intel_cleanup_ring_buffer(&dev_priv->ring[RCS]); + return ret; } -static bool -intel_enable_ppgtt(struct drm_device *dev) +int +i915_gem_init_hw(struct drm_device *dev) { - if (i915_enable_ppgtt >= 0) - return i915_enable_ppgtt; + drm_i915_private_t *dev_priv = dev->dev_private; + int ret; -#ifdef CONFIG_INTEL_IOMMU - /* Disable ppgtt on SNB if VT-d is on. */ - if (INTEL_INFO(dev)->gen == 6 && intel_iommu_gfx_mapped) - return false; -#endif + if (INTEL_INFO(dev)->gen < 6 && !intel_enable_gtt()) + return -EIO; - return true; + if (IS_HASWELL(dev) && (I915_READ(0x120010) == 1)) + I915_WRITE(0x9008, I915_READ(0x9008) | 0xf0000); + + i915_gem_l3_remap(dev); + + i915_gem_init_swizzling(dev); + + ret = i915_gem_init_rings(dev); + if (ret) + return ret; + + /* + * XXX: There was some w/a described somewhere suggesting loading + * contexts before PPGTT. + */ + i915_gem_context_init(dev); + i915_gem_init_ppgtt(dev); + + return 0; } int i915_gem_init(struct drm_device *dev) { struct drm_i915_private *dev_priv = dev->dev_private; - unsigned long gtt_size, mappable_size; int ret; - gtt_size = dev_priv->mm.gtt->gtt_total_entries << PAGE_SHIFT; - mappable_size = dev_priv->mm.gtt->gtt_mappable_entries << PAGE_SHIFT; - mutex_lock(&dev->struct_mutex); - if (intel_enable_ppgtt(dev) && HAS_ALIASING_PPGTT(dev)) { - /* PPGTT pdes are stolen from global gtt ptes, so shrink the - * aperture accordingly when using aliasing ppgtt. */ - gtt_size -= I915_PPGTT_PD_ENTRIES*PAGE_SIZE; - - i915_gem_init_global_gtt(dev, 0, mappable_size, gtt_size); - - ret = i915_gem_init_aliasing_ppgtt(dev); - if (ret) { - mutex_unlock(&dev->struct_mutex); - return ret; - } - } else { - /* Let GEM Manage all of the aperture. - * - * However, leave one page at the end still bound to the scratch - * page. There are a number of places where the hardware - * apparently prefetches past the end of the object, and we've - * seen multiple hangs with the GPU head pointer stuck in a - * batchbuffer bound at the last page of the aperture. One page - * should be enough to keep any prefetching inside of the - * aperture. - */ - i915_gem_init_global_gtt(dev, 0, mappable_size, - gtt_size); - } - + i915_gem_init_global_gtt(dev); ret = i915_gem_init_hw(dev); mutex_unlock(&dev->struct_mutex); if (ret) { @@ -4047,9 +4045,9 @@ i915_gem_entervt_ioctl(struct drm_device *dev, void *data, if (drm_core_check_feature(dev, DRIVER_MODESET)) return 0; - if (atomic_read(&dev_priv->mm.wedged)) { + if (i915_reset_in_progress(&dev_priv->gpu_error)) { DRM_ERROR("Reenabling wedged hardware, good luck\n"); - atomic_set(&dev_priv->mm.wedged, 0); + atomic_set(&dev_priv->gpu_error.reset_counter, 0); } mutex_lock(&dev->struct_mutex); @@ -4113,8 +4111,14 @@ init_ring_lists(struct intel_ring_buffer *ring) void i915_gem_load(struct drm_device *dev) { - int i; drm_i915_private_t *dev_priv = dev->dev_private; + int i; + + dev_priv->slab = + kmem_cache_create("i915_gem_object", + sizeof(struct drm_i915_gem_object), 0, + SLAB_HWCACHE_ALIGN, + NULL); INIT_LIST_HEAD(&dev_priv->mm.active_list); INIT_LIST_HEAD(&dev_priv->mm.inactive_list); @@ -4127,7 +4131,7 @@ i915_gem_load(struct drm_device *dev) INIT_LIST_HEAD(&dev_priv->fence_regs[i].lru_list); INIT_DELAYED_WORK(&dev_priv->mm.retire_work, i915_gem_retire_work_handler); - init_completion(&dev_priv->error_completion); + init_waitqueue_head(&dev_priv->gpu_error.reset_queue); /* On GEN3 we really need to make sure the ARB C3 LP bit is set */ if (IS_GEN3(dev)) { diff --git a/drivers/gpu/drm/i915/i915_gem_context.c b/drivers/gpu/drm/i915/i915_gem_context.c index a3f06bcad551..21177d9df423 100644 --- a/drivers/gpu/drm/i915/i915_gem_context.c +++ b/drivers/gpu/drm/i915/i915_gem_context.c @@ -126,13 +126,8 @@ static int get_context_size(struct drm_device *dev) static void do_destroy(struct i915_hw_context *ctx) { - struct drm_device *dev = ctx->obj->base.dev; - struct drm_i915_private *dev_priv = dev->dev_private; - if (ctx->file_priv) idr_remove(&ctx->file_priv->context_idr, ctx->id); - else - BUG_ON(ctx != dev_priv->ring[RCS].default_context); drm_gem_object_unreference(&ctx->obj->base); kfree(ctx); @@ -242,7 +237,6 @@ err_destroy: void i915_gem_context_init(struct drm_device *dev) { struct drm_i915_private *dev_priv = dev->dev_private; - uint32_t ctx_size; if (!HAS_HW_CONTEXTS(dev)) { dev_priv->hw_contexts_disabled = true; @@ -254,11 +248,9 @@ void i915_gem_context_init(struct drm_device *dev) dev_priv->ring[RCS].default_context) return; - ctx_size = get_context_size(dev); - dev_priv->hw_context_size = get_context_size(dev); - dev_priv->hw_context_size = round_up(dev_priv->hw_context_size, 4096); + dev_priv->hw_context_size = round_up(get_context_size(dev), 4096); - if (ctx_size <= 0 || ctx_size > (1<<20)) { + if (dev_priv->hw_context_size > (1<<20)) { dev_priv->hw_contexts_disabled = true; return; } diff --git a/drivers/gpu/drm/i915/i915_gem_dmabuf.c b/drivers/gpu/drm/i915/i915_gem_dmabuf.c index abeaafef6d7e..6a5af6828624 100644 --- a/drivers/gpu/drm/i915/i915_gem_dmabuf.c +++ b/drivers/gpu/drm/i915/i915_gem_dmabuf.c @@ -281,8 +281,7 @@ struct drm_gem_object *i915_gem_prime_import(struct drm_device *dev, if (IS_ERR(attach)) return ERR_CAST(attach); - - obj = kzalloc(sizeof(*obj), GFP_KERNEL); + obj = i915_gem_object_alloc(dev); if (obj == NULL) { ret = -ENOMEM; goto fail_detach; @@ -290,7 +289,7 @@ struct drm_gem_object *i915_gem_prime_import(struct drm_device *dev, ret = drm_gem_private_object_init(dev, &obj->base, dma_buf->size); if (ret) { - kfree(obj); + i915_gem_object_free(obj); goto fail_detach; } diff --git a/drivers/gpu/drm/i915/i915_gem_evict.c b/drivers/gpu/drm/i915/i915_gem_evict.c index 776a3225184c..c86d5d9356fd 100644 --- a/drivers/gpu/drm/i915/i915_gem_evict.c +++ b/drivers/gpu/drm/i915/i915_gem_evict.c @@ -80,7 +80,7 @@ i915_gem_evict_something(struct drm_device *dev, int min_size, if (mappable) drm_mm_init_scan_with_range(&dev_priv->mm.gtt_space, min_size, alignment, cache_level, - 0, dev_priv->mm.gtt_mappable_end); + 0, dev_priv->gtt.mappable_end); else drm_mm_init_scan(&dev_priv->mm.gtt_space, min_size, alignment, cache_level); diff --git a/drivers/gpu/drm/i915/i915_gem_execbuffer.c b/drivers/gpu/drm/i915/i915_gem_execbuffer.c index 26d08bb58218..2f2daebd0eef 100644 --- a/drivers/gpu/drm/i915/i915_gem_execbuffer.c +++ b/drivers/gpu/drm/i915/i915_gem_execbuffer.c @@ -34,61 +34,133 @@ #include <linux/dma_remapping.h> struct eb_objects { + struct list_head objects; int and; - struct hlist_head buckets[0]; + union { + struct drm_i915_gem_object *lut[0]; + struct hlist_head buckets[0]; + }; }; static struct eb_objects * -eb_create(int size) +eb_create(struct drm_i915_gem_execbuffer2 *args) { - struct eb_objects *eb; - int count = PAGE_SIZE / sizeof(struct hlist_head) / 2; - BUILD_BUG_ON(!is_power_of_2(PAGE_SIZE / sizeof(struct hlist_head))); - while (count > size) - count >>= 1; - eb = kzalloc(count*sizeof(struct hlist_head) + - sizeof(struct eb_objects), - GFP_KERNEL); - if (eb == NULL) - return eb; - - eb->and = count - 1; + struct eb_objects *eb = NULL; + + if (args->flags & I915_EXEC_HANDLE_LUT) { + int size = args->buffer_count; + size *= sizeof(struct drm_i915_gem_object *); + size += sizeof(struct eb_objects); + eb = kmalloc(size, GFP_TEMPORARY | __GFP_NOWARN | __GFP_NORETRY); + } + + if (eb == NULL) { + int size = args->buffer_count; + int count = PAGE_SIZE / sizeof(struct hlist_head) / 2; + BUILD_BUG_ON(!is_power_of_2(PAGE_SIZE / sizeof(struct hlist_head))); + while (count > 2*size) + count >>= 1; + eb = kzalloc(count*sizeof(struct hlist_head) + + sizeof(struct eb_objects), + GFP_TEMPORARY); + if (eb == NULL) + return eb; + + eb->and = count - 1; + } else + eb->and = -args->buffer_count; + + INIT_LIST_HEAD(&eb->objects); return eb; } static void eb_reset(struct eb_objects *eb) { - memset(eb->buckets, 0, (eb->and+1)*sizeof(struct hlist_head)); + if (eb->and >= 0) + memset(eb->buckets, 0, (eb->and+1)*sizeof(struct hlist_head)); } -static void -eb_add_object(struct eb_objects *eb, struct drm_i915_gem_object *obj) +static int +eb_lookup_objects(struct eb_objects *eb, + struct drm_i915_gem_exec_object2 *exec, + const struct drm_i915_gem_execbuffer2 *args, + struct drm_file *file) { - hlist_add_head(&obj->exec_node, - &eb->buckets[obj->exec_handle & eb->and]); + int i; + + spin_lock(&file->table_lock); + for (i = 0; i < args->buffer_count; i++) { + struct drm_i915_gem_object *obj; + + obj = to_intel_bo(idr_find(&file->object_idr, exec[i].handle)); + if (obj == NULL) { + spin_unlock(&file->table_lock); + DRM_DEBUG("Invalid object handle %d at index %d\n", + exec[i].handle, i); + return -ENOENT; + } + + if (!list_empty(&obj->exec_list)) { + spin_unlock(&file->table_lock); + DRM_DEBUG("Object %p [handle %d, index %d] appears more than once in object list\n", + obj, exec[i].handle, i); + return -EINVAL; + } + + drm_gem_object_reference(&obj->base); + list_add_tail(&obj->exec_list, &eb->objects); + + obj->exec_entry = &exec[i]; + if (eb->and < 0) { + eb->lut[i] = obj; + } else { + uint32_t handle = args->flags & I915_EXEC_HANDLE_LUT ? i : exec[i].handle; + obj->exec_handle = handle; + hlist_add_head(&obj->exec_node, + &eb->buckets[handle & eb->and]); + } + } + spin_unlock(&file->table_lock); + + return 0; } static struct drm_i915_gem_object * eb_get_object(struct eb_objects *eb, unsigned long handle) { - struct hlist_head *head; - struct hlist_node *node; - struct drm_i915_gem_object *obj; + if (eb->and < 0) { + if (handle >= -eb->and) + return NULL; + return eb->lut[handle]; + } else { + struct hlist_head *head; + struct hlist_node *node; - head = &eb->buckets[handle & eb->and]; - hlist_for_each(node, head) { - obj = hlist_entry(node, struct drm_i915_gem_object, exec_node); - if (obj->exec_handle == handle) - return obj; - } + head = &eb->buckets[handle & eb->and]; + hlist_for_each(node, head) { + struct drm_i915_gem_object *obj; - return NULL; + obj = hlist_entry(node, struct drm_i915_gem_object, exec_node); + if (obj->exec_handle == handle) + return obj; + } + return NULL; + } } static void eb_destroy(struct eb_objects *eb) { + while (!list_empty(&eb->objects)) { + struct drm_i915_gem_object *obj; + + obj = list_first_entry(&eb->objects, + struct drm_i915_gem_object, + exec_list); + list_del_init(&obj->exec_list); + drm_gem_object_unreference(&obj->base); + } kfree(eb); } @@ -150,17 +222,6 @@ i915_gem_execbuffer_relocate_entry(struct drm_i915_gem_object *obj, reloc->write_domain); return ret; } - if (unlikely(reloc->write_domain && target_obj->pending_write_domain && - reloc->write_domain != target_obj->pending_write_domain)) { - DRM_DEBUG("Write domain conflict: " - "obj %p target %d offset %d " - "new %08x old %08x\n", - obj, reloc->target_handle, - (int) reloc->offset, - reloc->write_domain, - target_obj->pending_write_domain); - return ret; - } target_obj->pending_read_domains |= reloc->read_domains; target_obj->pending_write_domain |= reloc->write_domain; @@ -220,7 +281,7 @@ i915_gem_execbuffer_relocate_entry(struct drm_i915_gem_object *obj, /* Map the page containing the relocation we're going to perform. */ reloc->offset += obj->gtt_offset; - reloc_page = io_mapping_map_atomic_wc(dev_priv->mm.gtt_mapping, + reloc_page = io_mapping_map_atomic_wc(dev_priv->gtt.mappable, reloc->offset & PAGE_MASK); reloc_entry = (uint32_t __iomem *) (reloc_page + (reloc->offset & ~PAGE_MASK)); @@ -299,8 +360,7 @@ i915_gem_execbuffer_relocate_object_slow(struct drm_i915_gem_object *obj, static int i915_gem_execbuffer_relocate(struct drm_device *dev, - struct eb_objects *eb, - struct list_head *objects) + struct eb_objects *eb) { struct drm_i915_gem_object *obj; int ret = 0; @@ -313,7 +373,7 @@ i915_gem_execbuffer_relocate(struct drm_device *dev, * lockdep complains vehemently. */ pagefault_disable(); - list_for_each_entry(obj, objects, exec_list) { + list_for_each_entry(obj, &eb->objects, exec_list) { ret = i915_gem_execbuffer_relocate_object(obj, eb); if (ret) break; @@ -335,7 +395,8 @@ need_reloc_mappable(struct drm_i915_gem_object *obj) static int i915_gem_execbuffer_reserve_object(struct drm_i915_gem_object *obj, - struct intel_ring_buffer *ring) + struct intel_ring_buffer *ring, + bool *need_reloc) { struct drm_i915_private *dev_priv = obj->base.dev->dev_private; struct drm_i915_gem_exec_object2 *entry = obj->exec_entry; @@ -376,7 +437,20 @@ i915_gem_execbuffer_reserve_object(struct drm_i915_gem_object *obj, obj->has_aliasing_ppgtt_mapping = 1; } - entry->offset = obj->gtt_offset; + if (entry->offset != obj->gtt_offset) { + entry->offset = obj->gtt_offset; + *need_reloc = true; + } + + if (entry->flags & EXEC_OBJECT_WRITE) { + obj->base.pending_read_domains = I915_GEM_DOMAIN_RENDER; + obj->base.pending_write_domain = I915_GEM_DOMAIN_RENDER; + } + + if (entry->flags & EXEC_OBJECT_NEEDS_GTT && + !obj->has_global_gtt_mapping) + i915_gem_gtt_bind_object(obj, obj->cache_level); + return 0; } @@ -402,7 +476,8 @@ i915_gem_execbuffer_unreserve_object(struct drm_i915_gem_object *obj) static int i915_gem_execbuffer_reserve(struct intel_ring_buffer *ring, struct drm_file *file, - struct list_head *objects) + struct list_head *objects, + bool *need_relocs) { struct drm_i915_gem_object *obj; struct list_head ordered_objects; @@ -430,7 +505,7 @@ i915_gem_execbuffer_reserve(struct intel_ring_buffer *ring, else list_move_tail(&obj->exec_list, &ordered_objects); - obj->base.pending_read_domains = 0; + obj->base.pending_read_domains = I915_GEM_GPU_DOMAINS & ~I915_GEM_DOMAIN_COMMAND; obj->base.pending_write_domain = 0; obj->pending_fenced_gpu_access = false; } @@ -470,7 +545,7 @@ i915_gem_execbuffer_reserve(struct intel_ring_buffer *ring, (need_mappable && !obj->map_and_fenceable)) ret = i915_gem_object_unbind(obj); else - ret = i915_gem_execbuffer_reserve_object(obj, ring); + ret = i915_gem_execbuffer_reserve_object(obj, ring, need_relocs); if (ret) goto err; } @@ -480,7 +555,7 @@ i915_gem_execbuffer_reserve(struct intel_ring_buffer *ring, if (obj->gtt_space) continue; - ret = i915_gem_execbuffer_reserve_object(obj, ring); + ret = i915_gem_execbuffer_reserve_object(obj, ring, need_relocs); if (ret) goto err; } @@ -500,21 +575,22 @@ err: /* Decrement pin count for bound objects */ static int i915_gem_execbuffer_relocate_slow(struct drm_device *dev, + struct drm_i915_gem_execbuffer2 *args, struct drm_file *file, struct intel_ring_buffer *ring, - struct list_head *objects, struct eb_objects *eb, - struct drm_i915_gem_exec_object2 *exec, - int count) + struct drm_i915_gem_exec_object2 *exec) { struct drm_i915_gem_relocation_entry *reloc; struct drm_i915_gem_object *obj; + bool need_relocs; int *reloc_offset; int i, total, ret; + int count = args->buffer_count; /* We may process another execbuffer during the unlock... */ - while (!list_empty(objects)) { - obj = list_first_entry(objects, + while (!list_empty(&eb->objects)) { + obj = list_first_entry(&eb->objects, struct drm_i915_gem_object, exec_list); list_del_init(&obj->exec_list); @@ -582,27 +658,16 @@ i915_gem_execbuffer_relocate_slow(struct drm_device *dev, /* reacquire the objects */ eb_reset(eb); - for (i = 0; i < count; i++) { - obj = to_intel_bo(drm_gem_object_lookup(dev, file, - exec[i].handle)); - if (&obj->base == NULL) { - DRM_DEBUG("Invalid object handle %d at index %d\n", - exec[i].handle, i); - ret = -ENOENT; - goto err; - } - - list_add_tail(&obj->exec_list, objects); - obj->exec_handle = exec[i].handle; - obj->exec_entry = &exec[i]; - eb_add_object(eb, obj); - } + ret = eb_lookup_objects(eb, exec, args, file); + if (ret) + goto err; - ret = i915_gem_execbuffer_reserve(ring, file, objects); + need_relocs = (args->flags & I915_EXEC_NO_RELOC) == 0; + ret = i915_gem_execbuffer_reserve(ring, file, &eb->objects, &need_relocs); if (ret) goto err; - list_for_each_entry(obj, objects, exec_list) { + list_for_each_entry(obj, &eb->objects, exec_list) { int offset = obj->exec_entry - exec; ret = i915_gem_execbuffer_relocate_object_slow(obj, eb, reloc + reloc_offset[offset]); @@ -623,44 +688,11 @@ err: } static int -i915_gem_execbuffer_wait_for_flips(struct intel_ring_buffer *ring, u32 flips) -{ - u32 plane, flip_mask; - int ret; - - /* Check for any pending flips. As we only maintain a flip queue depth - * of 1, we can simply insert a WAIT for the next display flip prior - * to executing the batch and avoid stalling the CPU. - */ - - for (plane = 0; flips >> plane; plane++) { - if (((flips >> plane) & 1) == 0) - continue; - - if (plane) - flip_mask = MI_WAIT_FOR_PLANE_B_FLIP; - else - flip_mask = MI_WAIT_FOR_PLANE_A_FLIP; - - ret = intel_ring_begin(ring, 2); - if (ret) - return ret; - - intel_ring_emit(ring, MI_WAIT_FOR_EVENT | flip_mask); - intel_ring_emit(ring, MI_NOOP); - intel_ring_advance(ring); - } - - return 0; -} - -static int i915_gem_execbuffer_move_to_gpu(struct intel_ring_buffer *ring, struct list_head *objects) { struct drm_i915_gem_object *obj; uint32_t flush_domains = 0; - uint32_t flips = 0; int ret; list_for_each_entry(obj, objects, exec_list) { @@ -671,18 +703,9 @@ i915_gem_execbuffer_move_to_gpu(struct intel_ring_buffer *ring, if (obj->base.write_domain & I915_GEM_DOMAIN_CPU) i915_gem_clflush_object(obj); - if (obj->base.pending_write_domain) - flips |= atomic_read(&obj->pending_flip); - flush_domains |= obj->base.write_domain; } - if (flips) { - ret = i915_gem_execbuffer_wait_for_flips(ring, flips); - if (ret) - return ret; - } - if (flush_domains & I915_GEM_DOMAIN_CPU) i915_gem_chipset_flush(ring->dev); @@ -698,6 +721,9 @@ i915_gem_execbuffer_move_to_gpu(struct intel_ring_buffer *ring, static bool i915_gem_check_execbuffer(struct drm_i915_gem_execbuffer2 *exec) { + if (exec->flags & __I915_EXEC_UNKNOWN_FLAGS) + return false; + return ((exec->batch_start_offset | exec->batch_len) & 0x7) == 0; } @@ -711,6 +737,9 @@ validate_exec_list(struct drm_i915_gem_exec_object2 *exec, char __user *ptr = (char __user *)(uintptr_t)exec[i].relocs_ptr; int length; /* limited by fault_in_pages_readable() */ + if (exec[i].flags & __EXEC_OBJECT_UNKNOWN_FLAGS) + return -EINVAL; + /* First check for malicious input causing overflow */ if (exec[i].relocation_count > INT_MAX / sizeof(struct drm_i915_gem_relocation_entry)) @@ -718,9 +747,6 @@ validate_exec_list(struct drm_i915_gem_exec_object2 *exec, length = exec[i].relocation_count * sizeof(struct drm_i915_gem_relocation_entry); - if (!access_ok(VERIFY_READ, ptr, length)) - return -EFAULT; - /* we may also need to update the presumed offsets */ if (!access_ok(VERIFY_WRITE, ptr, length)) return -EFAULT; @@ -742,8 +768,10 @@ i915_gem_execbuffer_move_to_active(struct list_head *objects, u32 old_read = obj->base.read_domains; u32 old_write = obj->base.write_domain; - obj->base.read_domains = obj->base.pending_read_domains; obj->base.write_domain = obj->base.pending_write_domain; + if (obj->base.write_domain == 0) + obj->base.pending_read_domains |= obj->base.read_domains; + obj->base.read_domains = obj->base.pending_read_domains; obj->fenced_gpu_access = obj->pending_fenced_gpu_access; i915_gem_object_move_to_active(obj, ring); @@ -802,21 +830,18 @@ i915_gem_do_execbuffer(struct drm_device *dev, void *data, struct drm_i915_gem_exec_object2 *exec) { drm_i915_private_t *dev_priv = dev->dev_private; - struct list_head objects; struct eb_objects *eb; struct drm_i915_gem_object *batch_obj; struct drm_clip_rect *cliprects = NULL; struct intel_ring_buffer *ring; u32 ctx_id = i915_execbuffer2_get_context_id(*args); u32 exec_start, exec_len; - u32 mask; - u32 flags; + u32 mask, flags; int ret, mode, i; + bool need_relocs; - if (!i915_gem_check_execbuffer(args)) { - DRM_DEBUG("execbuf with invalid offset/length\n"); + if (!i915_gem_check_execbuffer(args)) return -EINVAL; - } ret = validate_exec_list(exec, args->buffer_count); if (ret) @@ -937,7 +962,7 @@ i915_gem_do_execbuffer(struct drm_device *dev, void *data, goto pre_mutex_err; } - eb = eb_create(args->buffer_count); + eb = eb_create(args); if (eb == NULL) { mutex_unlock(&dev->struct_mutex); ret = -ENOMEM; @@ -945,51 +970,28 @@ i915_gem_do_execbuffer(struct drm_device *dev, void *data, } /* Look up object handles */ - INIT_LIST_HEAD(&objects); - for (i = 0; i < args->buffer_count; i++) { - struct drm_i915_gem_object *obj; - - obj = to_intel_bo(drm_gem_object_lookup(dev, file, - exec[i].handle)); - if (&obj->base == NULL) { - DRM_DEBUG("Invalid object handle %d at index %d\n", - exec[i].handle, i); - /* prevent error path from reading uninitialized data */ - ret = -ENOENT; - goto err; - } - - if (!list_empty(&obj->exec_list)) { - DRM_DEBUG("Object %p [handle %d, index %d] appears more than once in object list\n", - obj, exec[i].handle, i); - ret = -EINVAL; - goto err; - } - - list_add_tail(&obj->exec_list, &objects); - obj->exec_handle = exec[i].handle; - obj->exec_entry = &exec[i]; - eb_add_object(eb, obj); - } + ret = eb_lookup_objects(eb, exec, args, file); + if (ret) + goto err; /* take note of the batch buffer before we might reorder the lists */ - batch_obj = list_entry(objects.prev, + batch_obj = list_entry(eb->objects.prev, struct drm_i915_gem_object, exec_list); /* Move the objects en-masse into the GTT, evicting if necessary. */ - ret = i915_gem_execbuffer_reserve(ring, file, &objects); + need_relocs = (args->flags & I915_EXEC_NO_RELOC) == 0; + ret = i915_gem_execbuffer_reserve(ring, file, &eb->objects, &need_relocs); if (ret) goto err; /* The objects are in their final locations, apply the relocations. */ - ret = i915_gem_execbuffer_relocate(dev, eb, &objects); + if (need_relocs) + ret = i915_gem_execbuffer_relocate(dev, eb); if (ret) { if (ret == -EFAULT) { - ret = i915_gem_execbuffer_relocate_slow(dev, file, ring, - &objects, eb, - exec, - args->buffer_count); + ret = i915_gem_execbuffer_relocate_slow(dev, args, file, ring, + eb, exec); BUG_ON(!mutex_is_locked(&dev->struct_mutex)); } if (ret) @@ -1011,7 +1013,7 @@ i915_gem_do_execbuffer(struct drm_device *dev, void *data, if (flags & I915_DISPATCH_SECURE && !batch_obj->has_global_gtt_mapping) i915_gem_gtt_bind_object(batch_obj, batch_obj->cache_level); - ret = i915_gem_execbuffer_move_to_gpu(ring, &objects); + ret = i915_gem_execbuffer_move_to_gpu(ring, &eb->objects); if (ret) goto err; @@ -1065,20 +1067,11 @@ i915_gem_do_execbuffer(struct drm_device *dev, void *data, trace_i915_gem_ring_dispatch(ring, intel_ring_get_seqno(ring), flags); - i915_gem_execbuffer_move_to_active(&objects, ring); + i915_gem_execbuffer_move_to_active(&eb->objects, ring); i915_gem_execbuffer_retire_commands(dev, file, ring); err: eb_destroy(eb); - while (!list_empty(&objects)) { - struct drm_i915_gem_object *obj; - - obj = list_first_entry(&objects, - struct drm_i915_gem_object, - exec_list); - list_del_init(&obj->exec_list); - drm_gem_object_unreference(&obj->base); - } mutex_unlock(&dev->struct_mutex); @@ -1187,7 +1180,7 @@ i915_gem_execbuffer2(struct drm_device *dev, void *data, } exec2_list = kmalloc(sizeof(*exec2_list)*args->buffer_count, - GFP_KERNEL | __GFP_NOWARN | __GFP_NORETRY); + GFP_TEMPORARY | __GFP_NOWARN | __GFP_NORETRY); if (exec2_list == NULL) exec2_list = drm_malloc_ab(sizeof(*exec2_list), args->buffer_count); diff --git a/drivers/gpu/drm/i915/i915_gem_gtt.c b/drivers/gpu/drm/i915/i915_gem_gtt.c index 2c150dee78a7..926a1e2dd234 100644 --- a/drivers/gpu/drm/i915/i915_gem_gtt.c +++ b/drivers/gpu/drm/i915/i915_gem_gtt.c @@ -44,9 +44,9 @@ typedef uint32_t gtt_pte_t; #define GEN6_PTE_CACHE_LLC_MLC (3 << 1) #define GEN6_PTE_ADDR_ENCODE(addr) GEN6_GTT_ADDR_ENCODE(addr) -static inline gtt_pte_t pte_encode(struct drm_device *dev, - dma_addr_t addr, - enum i915_cache_level level) +static inline gtt_pte_t gen6_pte_encode(struct drm_device *dev, + dma_addr_t addr, + enum i915_cache_level level) { gtt_pte_t pte = GEN6_PTE_VALID; pte |= GEN6_PTE_ADDR_ENCODE(addr); @@ -77,7 +77,7 @@ static inline gtt_pte_t pte_encode(struct drm_device *dev, } /* PPGTT support for Sandybdrige/Gen6 and later */ -static void i915_ppgtt_clear_range(struct i915_hw_ppgtt *ppgtt, +static void gen6_ppgtt_clear_range(struct i915_hw_ppgtt *ppgtt, unsigned first_entry, unsigned num_entries) { @@ -87,8 +87,9 @@ static void i915_ppgtt_clear_range(struct i915_hw_ppgtt *ppgtt, unsigned first_pte = first_entry % I915_PPGTT_PT_ENTRIES; unsigned last_pte, i; - scratch_pte = pte_encode(ppgtt->dev, ppgtt->scratch_page_dma_addr, - I915_CACHE_LLC); + scratch_pte = gen6_pte_encode(ppgtt->dev, + ppgtt->scratch_page_dma_addr, + I915_CACHE_LLC); while (num_entries) { last_pte = first_pte + num_entries; @@ -108,10 +109,72 @@ static void i915_ppgtt_clear_range(struct i915_hw_ppgtt *ppgtt, } } -int i915_gem_init_aliasing_ppgtt(struct drm_device *dev) +static void gen6_ppgtt_insert_entries(struct i915_hw_ppgtt *ppgtt, + struct sg_table *pages, + unsigned first_entry, + enum i915_cache_level cache_level) { + gtt_pte_t *pt_vaddr; + unsigned act_pd = first_entry / I915_PPGTT_PT_ENTRIES; + unsigned first_pte = first_entry % I915_PPGTT_PT_ENTRIES; + unsigned i, j, m, segment_len; + dma_addr_t page_addr; + struct scatterlist *sg; + + /* init sg walking */ + sg = pages->sgl; + i = 0; + segment_len = sg_dma_len(sg) >> PAGE_SHIFT; + m = 0; + + while (i < pages->nents) { + pt_vaddr = kmap_atomic(ppgtt->pt_pages[act_pd]); + + for (j = first_pte; j < I915_PPGTT_PT_ENTRIES; j++) { + page_addr = sg_dma_address(sg) + (m << PAGE_SHIFT); + pt_vaddr[j] = gen6_pte_encode(ppgtt->dev, page_addr, + cache_level); + + /* grab the next page */ + if (++m == segment_len) { + if (++i == pages->nents) + break; + + sg = sg_next(sg); + segment_len = sg_dma_len(sg) >> PAGE_SHIFT; + m = 0; + } + } + + kunmap_atomic(pt_vaddr); + + first_pte = 0; + act_pd++; + } +} + +static void gen6_ppgtt_cleanup(struct i915_hw_ppgtt *ppgtt) +{ + int i; + + if (ppgtt->pt_dma_addr) { + for (i = 0; i < ppgtt->num_pd_entries; i++) + pci_unmap_page(ppgtt->dev->pdev, + ppgtt->pt_dma_addr[i], + 4096, PCI_DMA_BIDIRECTIONAL); + } + + kfree(ppgtt->pt_dma_addr); + for (i = 0; i < ppgtt->num_pd_entries; i++) + __free_page(ppgtt->pt_pages[i]); + kfree(ppgtt->pt_pages); + kfree(ppgtt); +} + +static int gen6_ppgtt_init(struct i915_hw_ppgtt *ppgtt) +{ + struct drm_device *dev = ppgtt->dev; struct drm_i915_private *dev_priv = dev->dev_private; - struct i915_hw_ppgtt *ppgtt; unsigned first_pd_entry_in_global_pt; int i; int ret = -ENOMEM; @@ -119,18 +182,17 @@ int i915_gem_init_aliasing_ppgtt(struct drm_device *dev) /* ppgtt PDEs reside in the global gtt pagetable, which has 512*1024 * entries. For aliasing ppgtt support we just steal them at the end for * now. */ - first_pd_entry_in_global_pt = dev_priv->mm.gtt->gtt_total_entries - I915_PPGTT_PD_ENTRIES; - - ppgtt = kzalloc(sizeof(*ppgtt), GFP_KERNEL); - if (!ppgtt) - return ret; + first_pd_entry_in_global_pt = + gtt_total_entries(dev_priv->gtt) - I915_PPGTT_PD_ENTRIES; - ppgtt->dev = dev; ppgtt->num_pd_entries = I915_PPGTT_PD_ENTRIES; + ppgtt->clear_range = gen6_ppgtt_clear_range; + ppgtt->insert_entries = gen6_ppgtt_insert_entries; + ppgtt->cleanup = gen6_ppgtt_cleanup; ppgtt->pt_pages = kzalloc(sizeof(struct page *)*ppgtt->num_pd_entries, GFP_KERNEL); if (!ppgtt->pt_pages) - goto err_ppgtt; + return -ENOMEM; for (i = 0; i < ppgtt->num_pd_entries; i++) { ppgtt->pt_pages[i] = alloc_page(GFP_KERNEL); @@ -138,39 +200,32 @@ int i915_gem_init_aliasing_ppgtt(struct drm_device *dev) goto err_pt_alloc; } - if (dev_priv->mm.gtt->needs_dmar) { - ppgtt->pt_dma_addr = kzalloc(sizeof(dma_addr_t) - *ppgtt->num_pd_entries, - GFP_KERNEL); - if (!ppgtt->pt_dma_addr) - goto err_pt_alloc; + ppgtt->pt_dma_addr = kzalloc(sizeof(dma_addr_t) *ppgtt->num_pd_entries, + GFP_KERNEL); + if (!ppgtt->pt_dma_addr) + goto err_pt_alloc; - for (i = 0; i < ppgtt->num_pd_entries; i++) { - dma_addr_t pt_addr; + for (i = 0; i < ppgtt->num_pd_entries; i++) { + dma_addr_t pt_addr; - pt_addr = pci_map_page(dev->pdev, ppgtt->pt_pages[i], - 0, 4096, - PCI_DMA_BIDIRECTIONAL); + pt_addr = pci_map_page(dev->pdev, ppgtt->pt_pages[i], 0, 4096, + PCI_DMA_BIDIRECTIONAL); - if (pci_dma_mapping_error(dev->pdev, - pt_addr)) { - ret = -EIO; - goto err_pd_pin; + if (pci_dma_mapping_error(dev->pdev, pt_addr)) { + ret = -EIO; + goto err_pd_pin; - } - ppgtt->pt_dma_addr[i] = pt_addr; } + ppgtt->pt_dma_addr[i] = pt_addr; } - ppgtt->scratch_page_dma_addr = dev_priv->mm.gtt->scratch_page_dma; + ppgtt->scratch_page_dma_addr = dev_priv->gtt.scratch_page_dma; - i915_ppgtt_clear_range(ppgtt, 0, - ppgtt->num_pd_entries*I915_PPGTT_PT_ENTRIES); + ppgtt->clear_range(ppgtt, 0, + ppgtt->num_pd_entries*I915_PPGTT_PT_ENTRIES); ppgtt->pd_offset = (first_pd_entry_in_global_pt)*sizeof(gtt_pte_t); - dev_priv->mm.aliasing_ppgtt = ppgtt; - return 0; err_pd_pin: @@ -186,94 +241,57 @@ err_pt_alloc: __free_page(ppgtt->pt_pages[i]); } kfree(ppgtt->pt_pages); -err_ppgtt: - kfree(ppgtt); return ret; } -void i915_gem_cleanup_aliasing_ppgtt(struct drm_device *dev) +static int i915_gem_init_aliasing_ppgtt(struct drm_device *dev) { struct drm_i915_private *dev_priv = dev->dev_private; - struct i915_hw_ppgtt *ppgtt = dev_priv->mm.aliasing_ppgtt; - int i; + struct i915_hw_ppgtt *ppgtt; + int ret; + ppgtt = kzalloc(sizeof(*ppgtt), GFP_KERNEL); if (!ppgtt) - return; + return -ENOMEM; - if (ppgtt->pt_dma_addr) { - for (i = 0; i < ppgtt->num_pd_entries; i++) - pci_unmap_page(dev->pdev, ppgtt->pt_dma_addr[i], - 4096, PCI_DMA_BIDIRECTIONAL); - } + ppgtt->dev = dev; - kfree(ppgtt->pt_dma_addr); - for (i = 0; i < ppgtt->num_pd_entries; i++) - __free_page(ppgtt->pt_pages[i]); - kfree(ppgtt->pt_pages); - kfree(ppgtt); + ret = gen6_ppgtt_init(ppgtt); + if (ret) + kfree(ppgtt); + else + dev_priv->mm.aliasing_ppgtt = ppgtt; + + return ret; } -static void i915_ppgtt_insert_sg_entries(struct i915_hw_ppgtt *ppgtt, - const struct sg_table *pages, - unsigned first_entry, - enum i915_cache_level cache_level) +void i915_gem_cleanup_aliasing_ppgtt(struct drm_device *dev) { - gtt_pte_t *pt_vaddr; - unsigned act_pd = first_entry / I915_PPGTT_PT_ENTRIES; - unsigned first_pte = first_entry % I915_PPGTT_PT_ENTRIES; - unsigned i, j, m, segment_len; - dma_addr_t page_addr; - struct scatterlist *sg; - - /* init sg walking */ - sg = pages->sgl; - i = 0; - segment_len = sg_dma_len(sg) >> PAGE_SHIFT; - m = 0; - - while (i < pages->nents) { - pt_vaddr = kmap_atomic(ppgtt->pt_pages[act_pd]); - - for (j = first_pte; j < I915_PPGTT_PT_ENTRIES; j++) { - page_addr = sg_dma_address(sg) + (m << PAGE_SHIFT); - pt_vaddr[j] = pte_encode(ppgtt->dev, page_addr, - cache_level); - - /* grab the next page */ - if (++m == segment_len) { - if (++i == pages->nents) - break; - - sg = sg_next(sg); - segment_len = sg_dma_len(sg) >> PAGE_SHIFT; - m = 0; - } - } + struct drm_i915_private *dev_priv = dev->dev_private; + struct i915_hw_ppgtt *ppgtt = dev_priv->mm.aliasing_ppgtt; - kunmap_atomic(pt_vaddr); + if (!ppgtt) + return; - first_pte = 0; - act_pd++; - } + ppgtt->cleanup(ppgtt); } void i915_ppgtt_bind_object(struct i915_hw_ppgtt *ppgtt, struct drm_i915_gem_object *obj, enum i915_cache_level cache_level) { - i915_ppgtt_insert_sg_entries(ppgtt, - obj->pages, - obj->gtt_space->start >> PAGE_SHIFT, - cache_level); + ppgtt->insert_entries(ppgtt, obj->pages, + obj->gtt_space->start >> PAGE_SHIFT, + cache_level); } void i915_ppgtt_unbind_object(struct i915_hw_ppgtt *ppgtt, struct drm_i915_gem_object *obj) { - i915_ppgtt_clear_range(ppgtt, - obj->gtt_space->start >> PAGE_SHIFT, - obj->base.size >> PAGE_SHIFT); + ppgtt->clear_range(ppgtt, + obj->gtt_space->start >> PAGE_SHIFT, + obj->base.size >> PAGE_SHIFT); } void i915_gem_init_ppgtt(struct drm_device *dev) @@ -282,7 +300,7 @@ void i915_gem_init_ppgtt(struct drm_device *dev) uint32_t pd_offset; struct intel_ring_buffer *ring; struct i915_hw_ppgtt *ppgtt = dev_priv->mm.aliasing_ppgtt; - uint32_t __iomem *pd_addr; + gtt_pte_t __iomem *pd_addr; uint32_t pd_entry; int i; @@ -290,15 +308,11 @@ void i915_gem_init_ppgtt(struct drm_device *dev) return; - pd_addr = dev_priv->mm.gtt->gtt + ppgtt->pd_offset/sizeof(uint32_t); + pd_addr = (gtt_pte_t __iomem*)dev_priv->gtt.gsm + ppgtt->pd_offset/sizeof(gtt_pte_t); for (i = 0; i < ppgtt->num_pd_entries; i++) { dma_addr_t pt_addr; - if (dev_priv->mm.gtt->needs_dmar) - pt_addr = ppgtt->pt_dma_addr[i]; - else - pt_addr = page_to_phys(ppgtt->pt_pages[i]); - + pt_addr = ppgtt->pt_dma_addr[i]; pd_entry = GEN6_PDE_ADDR_ENCODE(pt_addr); pd_entry |= GEN6_PDE_VALID; @@ -338,11 +352,27 @@ void i915_gem_init_ppgtt(struct drm_device *dev) } } +extern int intel_iommu_gfx_mapped; +/* Certain Gen5 chipsets require require idling the GPU before + * unmapping anything from the GTT when VT-d is enabled. + */ +static inline bool needs_idle_maps(struct drm_device *dev) +{ +#ifdef CONFIG_INTEL_IOMMU + /* Query intel_iommu to see if we need the workaround. Presumably that + * was loaded first. + */ + if (IS_GEN5(dev) && IS_MOBILE(dev) && intel_iommu_gfx_mapped) + return true; +#endif + return false; +} + static bool do_idling(struct drm_i915_private *dev_priv) { bool ret = dev_priv->mm.interruptible; - if (unlikely(dev_priv->mm.gtt->do_idle_maps)) { + if (unlikely(dev_priv->gtt.do_idle_maps)) { dev_priv->mm.interruptible = false; if (i915_gpu_idle(dev_priv->dev)) { DRM_ERROR("Couldn't idle GPU\n"); @@ -356,45 +386,18 @@ static bool do_idling(struct drm_i915_private *dev_priv) static void undo_idling(struct drm_i915_private *dev_priv, bool interruptible) { - if (unlikely(dev_priv->mm.gtt->do_idle_maps)) + if (unlikely(dev_priv->gtt.do_idle_maps)) dev_priv->mm.interruptible = interruptible; } - -static void i915_ggtt_clear_range(struct drm_device *dev, - unsigned first_entry, - unsigned num_entries) -{ - struct drm_i915_private *dev_priv = dev->dev_private; - gtt_pte_t scratch_pte; - gtt_pte_t __iomem *gtt_base = dev_priv->mm.gtt->gtt + first_entry; - const int max_entries = dev_priv->mm.gtt->gtt_total_entries - first_entry; - int i; - - if (INTEL_INFO(dev)->gen < 6) { - intel_gtt_clear_range(first_entry, num_entries); - return; - } - - if (WARN(num_entries > max_entries, - "First entry = %d; Num entries = %d (max=%d)\n", - first_entry, num_entries, max_entries)) - num_entries = max_entries; - - scratch_pte = pte_encode(dev, dev_priv->mm.gtt->scratch_page_dma, I915_CACHE_LLC); - for (i = 0; i < num_entries; i++) - iowrite32(scratch_pte, >t_base[i]); - readl(gtt_base); -} - void i915_gem_restore_gtt_mappings(struct drm_device *dev) { struct drm_i915_private *dev_priv = dev->dev_private; struct drm_i915_gem_object *obj; /* First fill our portion of the GTT with scratch pages */ - i915_ggtt_clear_range(dev, dev_priv->mm.gtt_start / PAGE_SIZE, - (dev_priv->mm.gtt_end - dev_priv->mm.gtt_start) / PAGE_SIZE); + dev_priv->gtt.gtt_clear_range(dev, dev_priv->gtt.start / PAGE_SIZE, + dev_priv->gtt.total / PAGE_SIZE); list_for_each_entry(obj, &dev_priv->mm.bound_list, gtt_list) { i915_gem_clflush_object(obj); @@ -423,16 +426,15 @@ int i915_gem_gtt_prepare_object(struct drm_i915_gem_object *obj) * within the global GTT as well as accessible by the GPU through the GMADR * mapped BAR (dev_priv->mm.gtt->gtt). */ -static void gen6_ggtt_bind_object(struct drm_i915_gem_object *obj, - enum i915_cache_level level) +static void gen6_ggtt_insert_entries(struct drm_device *dev, + struct sg_table *st, + unsigned int first_entry, + enum i915_cache_level level) { - struct drm_device *dev = obj->base.dev; struct drm_i915_private *dev_priv = dev->dev_private; - struct sg_table *st = obj->pages; struct scatterlist *sg = st->sgl; - const int first_entry = obj->gtt_space->start >> PAGE_SHIFT; - const int max_entries = dev_priv->mm.gtt->gtt_total_entries - first_entry; - gtt_pte_t __iomem *gtt_entries = dev_priv->mm.gtt->gtt + first_entry; + gtt_pte_t __iomem *gtt_entries = + (gtt_pte_t __iomem *)dev_priv->gtt.gsm + first_entry; int unused, i = 0; unsigned int len, m = 0; dma_addr_t addr; @@ -441,14 +443,12 @@ static void gen6_ggtt_bind_object(struct drm_i915_gem_object *obj, len = sg_dma_len(sg) >> PAGE_SHIFT; for (m = 0; m < len; m++) { addr = sg_dma_address(sg) + (m << PAGE_SHIFT); - iowrite32(pte_encode(dev, addr, level), >t_entries[i]); + iowrite32(gen6_pte_encode(dev, addr, level), + >t_entries[i]); i++; } } - BUG_ON(i > max_entries); - BUG_ON(i != obj->base.size / PAGE_SIZE); - /* XXX: This serves as a posting read to make sure that the PTE has * actually been updated. There is some concern that even though * registers and PTEs are within the same BAR that they are potentially @@ -456,7 +456,8 @@ static void gen6_ggtt_bind_object(struct drm_i915_gem_object *obj, * hardware should work, we must keep this posting read for paranoia. */ if (i != 0) - WARN_ON(readl(>t_entries[i-1]) != pte_encode(dev, addr, level)); + WARN_ON(readl(>t_entries[i-1]) + != gen6_pte_encode(dev, addr, level)); /* This next bit makes the above posting read even more important. We * want to flush the TLBs only after we're certain all the PTE updates @@ -466,28 +467,70 @@ static void gen6_ggtt_bind_object(struct drm_i915_gem_object *obj, POSTING_READ(GFX_FLSH_CNTL_GEN6); } +static void gen6_ggtt_clear_range(struct drm_device *dev, + unsigned int first_entry, + unsigned int num_entries) +{ + struct drm_i915_private *dev_priv = dev->dev_private; + gtt_pte_t scratch_pte; + gtt_pte_t __iomem *gtt_base = (gtt_pte_t __iomem *) dev_priv->gtt.gsm + first_entry; + const int max_entries = gtt_total_entries(dev_priv->gtt) - first_entry; + int i; + + if (WARN(num_entries > max_entries, + "First entry = %d; Num entries = %d (max=%d)\n", + first_entry, num_entries, max_entries)) + num_entries = max_entries; + + scratch_pte = gen6_pte_encode(dev, dev_priv->gtt.scratch_page_dma, + I915_CACHE_LLC); + for (i = 0; i < num_entries; i++) + iowrite32(scratch_pte, >t_base[i]); + readl(gtt_base); +} + + +static void i915_ggtt_insert_entries(struct drm_device *dev, + struct sg_table *st, + unsigned int pg_start, + enum i915_cache_level cache_level) +{ + unsigned int flags = (cache_level == I915_CACHE_NONE) ? + AGP_USER_MEMORY : AGP_USER_CACHED_MEMORY; + + intel_gtt_insert_sg_entries(st, pg_start, flags); + +} + +static void i915_ggtt_clear_range(struct drm_device *dev, + unsigned int first_entry, + unsigned int num_entries) +{ + intel_gtt_clear_range(first_entry, num_entries); +} + + void i915_gem_gtt_bind_object(struct drm_i915_gem_object *obj, enum i915_cache_level cache_level) { struct drm_device *dev = obj->base.dev; - if (INTEL_INFO(dev)->gen < 6) { - unsigned int flags = (cache_level == I915_CACHE_NONE) ? - AGP_USER_MEMORY : AGP_USER_CACHED_MEMORY; - intel_gtt_insert_sg_entries(obj->pages, - obj->gtt_space->start >> PAGE_SHIFT, - flags); - } else { - gen6_ggtt_bind_object(obj, cache_level); - } + struct drm_i915_private *dev_priv = dev->dev_private; + + dev_priv->gtt.gtt_insert_entries(dev, obj->pages, + obj->gtt_space->start >> PAGE_SHIFT, + cache_level); obj->has_global_gtt_mapping = 1; } void i915_gem_gtt_unbind_object(struct drm_i915_gem_object *obj) { - i915_ggtt_clear_range(obj->base.dev, - obj->gtt_space->start >> PAGE_SHIFT, - obj->base.size >> PAGE_SHIFT); + struct drm_device *dev = obj->base.dev; + struct drm_i915_private *dev_priv = dev->dev_private; + + dev_priv->gtt.gtt_clear_range(obj->base.dev, + obj->gtt_space->start >> PAGE_SHIFT, + obj->base.size >> PAGE_SHIFT); obj->has_global_gtt_mapping = 0; } @@ -524,27 +567,101 @@ static void i915_gtt_color_adjust(struct drm_mm_node *node, *end -= 4096; } } - -void i915_gem_init_global_gtt(struct drm_device *dev, - unsigned long start, - unsigned long mappable_end, - unsigned long end) +void i915_gem_setup_global_gtt(struct drm_device *dev, + unsigned long start, + unsigned long mappable_end, + unsigned long end) { + /* Let GEM Manage all of the aperture. + * + * However, leave one page at the end still bound to the scratch page. + * There are a number of places where the hardware apparently prefetches + * past the end of the object, and we've seen multiple hangs with the + * GPU head pointer stuck in a batchbuffer bound at the last page of the + * aperture. One page should be enough to keep any prefetching inside + * of the aperture. + */ drm_i915_private_t *dev_priv = dev->dev_private; + struct drm_mm_node *entry; + struct drm_i915_gem_object *obj; + unsigned long hole_start, hole_end; - /* Substract the guard page ... */ + BUG_ON(mappable_end > end); + + /* Subtract the guard page ... */ drm_mm_init(&dev_priv->mm.gtt_space, start, end - start - PAGE_SIZE); if (!HAS_LLC(dev)) dev_priv->mm.gtt_space.color_adjust = i915_gtt_color_adjust; - dev_priv->mm.gtt_start = start; - dev_priv->mm.gtt_mappable_end = mappable_end; - dev_priv->mm.gtt_end = end; - dev_priv->mm.gtt_total = end - start; - dev_priv->mm.mappable_gtt_total = min(end, mappable_end) - start; + /* Mark any preallocated objects as occupied */ + list_for_each_entry(obj, &dev_priv->mm.bound_list, gtt_list) { + DRM_DEBUG_KMS("reserving preallocated space: %x + %zx\n", + obj->gtt_offset, obj->base.size); + + BUG_ON(obj->gtt_space != I915_GTT_RESERVED); + obj->gtt_space = drm_mm_create_block(&dev_priv->mm.gtt_space, + obj->gtt_offset, + obj->base.size, + false); + obj->has_global_gtt_mapping = 1; + } + + dev_priv->gtt.start = start; + dev_priv->gtt.total = end - start; + + /* Clear any non-preallocated blocks */ + drm_mm_for_each_hole(entry, &dev_priv->mm.gtt_space, + hole_start, hole_end) { + DRM_DEBUG_KMS("clearing unused GTT space: [%lx, %lx]\n", + hole_start, hole_end); + dev_priv->gtt.gtt_clear_range(dev, hole_start / PAGE_SIZE, + (hole_end-hole_start) / PAGE_SIZE); + } - /* ... but ensure that we clear the entire range. */ - i915_ggtt_clear_range(dev, start / PAGE_SIZE, (end-start) / PAGE_SIZE); + /* And finally clear the reserved guard page */ + dev_priv->gtt.gtt_clear_range(dev, end / PAGE_SIZE - 1, 1); +} + +static bool +intel_enable_ppgtt(struct drm_device *dev) +{ + if (i915_enable_ppgtt >= 0) + return i915_enable_ppgtt; + +#ifdef CONFIG_INTEL_IOMMU + /* Disable ppgtt on SNB if VT-d is on. */ + if (INTEL_INFO(dev)->gen == 6 && intel_iommu_gfx_mapped) + return false; +#endif + + return true; +} + +void i915_gem_init_global_gtt(struct drm_device *dev) +{ + struct drm_i915_private *dev_priv = dev->dev_private; + unsigned long gtt_size, mappable_size; + + gtt_size = dev_priv->gtt.total; + mappable_size = dev_priv->gtt.mappable_end; + + if (intel_enable_ppgtt(dev) && HAS_ALIASING_PPGTT(dev)) { + int ret; + /* PPGTT pdes are stolen from global gtt ptes, so shrink the + * aperture accordingly when using aliasing ppgtt. */ + gtt_size -= I915_PPGTT_PD_ENTRIES*PAGE_SIZE; + + i915_gem_setup_global_gtt(dev, 0, mappable_size, gtt_size); + + ret = i915_gem_init_aliasing_ppgtt(dev); + if (!ret) + return; + + DRM_ERROR("Aliased PPGTT setup failed %d\n", ret); + drm_mm_takedown(&dev_priv->mm.gtt_space); + gtt_size += I915_PPGTT_PD_ENTRIES*PAGE_SIZE; + } + i915_gem_setup_global_gtt(dev, 0, mappable_size, gtt_size); } static int setup_scratch_page(struct drm_device *dev) @@ -567,8 +684,8 @@ static int setup_scratch_page(struct drm_device *dev) #else dma_addr = page_to_phys(page); #endif - dev_priv->mm.gtt->scratch_page = page; - dev_priv->mm.gtt->scratch_page_dma = dma_addr; + dev_priv->gtt.scratch_page = page; + dev_priv->gtt.scratch_page_dma = dma_addr; return 0; } @@ -576,11 +693,11 @@ static int setup_scratch_page(struct drm_device *dev) static void teardown_scratch_page(struct drm_device *dev) { struct drm_i915_private *dev_priv = dev->dev_private; - set_pages_wb(dev_priv->mm.gtt->scratch_page, 1); - pci_unmap_page(dev->pdev, dev_priv->mm.gtt->scratch_page_dma, + set_pages_wb(dev_priv->gtt.scratch_page, 1); + pci_unmap_page(dev->pdev, dev_priv->gtt.scratch_page_dma, PAGE_SIZE, PCI_DMA_BIDIRECTIONAL); - put_page(dev_priv->mm.gtt->scratch_page); - __free_page(dev_priv->mm.gtt->scratch_page); + put_page(dev_priv->gtt.scratch_page); + __free_page(dev_priv->gtt.scratch_page); } static inline unsigned int gen6_get_total_gtt_size(u16 snb_gmch_ctl) @@ -590,14 +707,14 @@ static inline unsigned int gen6_get_total_gtt_size(u16 snb_gmch_ctl) return snb_gmch_ctl << 20; } -static inline unsigned int gen6_get_stolen_size(u16 snb_gmch_ctl) +static inline size_t gen6_get_stolen_size(u16 snb_gmch_ctl) { snb_gmch_ctl >>= SNB_GMCH_GMS_SHIFT; snb_gmch_ctl &= SNB_GMCH_GMS_MASK; return snb_gmch_ctl << 25; /* 32 MB units */ } -static inline unsigned int gen7_get_stolen_size(u16 snb_gmch_ctl) +static inline size_t gen7_get_stolen_size(u16 snb_gmch_ctl) { static const int stolen_decoder[] = { 0, 0, 0, 0, 0, 32, 48, 64, 128, 256, 96, 160, 224, 352}; @@ -606,103 +723,127 @@ static inline unsigned int gen7_get_stolen_size(u16 snb_gmch_ctl) return stolen_decoder[snb_gmch_ctl] << 20; } -int i915_gem_gtt_init(struct drm_device *dev) +static int gen6_gmch_probe(struct drm_device *dev, + size_t *gtt_total, + size_t *stolen, + phys_addr_t *mappable_base, + unsigned long *mappable_end) { struct drm_i915_private *dev_priv = dev->dev_private; phys_addr_t gtt_bus_addr; + unsigned int gtt_size; u16 snb_gmch_ctl; int ret; - /* On modern platforms we need not worry ourself with the legacy - * hostbridge query stuff. Skip it entirely - */ - if (INTEL_INFO(dev)->gen < 6) { - ret = intel_gmch_probe(dev_priv->bridge_dev, dev->pdev, NULL); - if (!ret) { - DRM_ERROR("failed to set up gmch\n"); - return -EIO; - } + *mappable_base = pci_resource_start(dev->pdev, 2); + *mappable_end = pci_resource_len(dev->pdev, 2); - dev_priv->mm.gtt = intel_gtt_get(); - if (!dev_priv->mm.gtt) { - DRM_ERROR("Failed to initialize GTT\n"); - intel_gmch_remove(); - return -ENODEV; - } - return 0; + /* 64/512MB is the current min/max we actually know of, but this is just + * a coarse sanity check. + */ + if ((*mappable_end < (64<<20) || (*mappable_end > (512<<20)))) { + DRM_ERROR("Unknown GMADR size (%lx)\n", + dev_priv->gtt.mappable_end); + return -ENXIO; } - dev_priv->mm.gtt = kzalloc(sizeof(*dev_priv->mm.gtt), GFP_KERNEL); - if (!dev_priv->mm.gtt) - return -ENOMEM; - if (!pci_set_dma_mask(dev->pdev, DMA_BIT_MASK(40))) pci_set_consistent_dma_mask(dev->pdev, DMA_BIT_MASK(40)); + pci_read_config_word(dev->pdev, SNB_GMCH_CTRL, &snb_gmch_ctl); + gtt_size = gen6_get_total_gtt_size(snb_gmch_ctl); -#ifdef CONFIG_INTEL_IOMMU - dev_priv->mm.gtt->needs_dmar = 1; -#endif + if (IS_GEN7(dev)) + *stolen = gen7_get_stolen_size(snb_gmch_ctl); + else + *stolen = gen6_get_stolen_size(snb_gmch_ctl); + + *gtt_total = (gtt_size / sizeof(gtt_pte_t)) << PAGE_SHIFT; /* For GEN6+ the PTEs for the ggtt live at 2MB + BAR0 */ gtt_bus_addr = pci_resource_start(dev->pdev, 0) + (2<<20); - dev_priv->mm.gtt->gma_bus_addr = pci_resource_start(dev->pdev, 2); - - /* i9xx_setup */ - pci_read_config_word(dev->pdev, SNB_GMCH_CTRL, &snb_gmch_ctl); - dev_priv->mm.gtt->gtt_total_entries = - gen6_get_total_gtt_size(snb_gmch_ctl) / sizeof(gtt_pte_t); - if (INTEL_INFO(dev)->gen < 7) - dev_priv->mm.gtt->stolen_size = gen6_get_stolen_size(snb_gmch_ctl); - else - dev_priv->mm.gtt->stolen_size = gen7_get_stolen_size(snb_gmch_ctl); - - dev_priv->mm.gtt->gtt_mappable_entries = pci_resource_len(dev->pdev, 2) >> PAGE_SHIFT; - /* 64/512MB is the current min/max we actually know of, but this is just a - * coarse sanity check. - */ - if ((dev_priv->mm.gtt->gtt_mappable_entries >> 8) < 64 || - dev_priv->mm.gtt->gtt_mappable_entries > dev_priv->mm.gtt->gtt_total_entries) { - DRM_ERROR("Unknown GMADR entries (%d)\n", - dev_priv->mm.gtt->gtt_mappable_entries); - ret = -ENXIO; - goto err_out; + dev_priv->gtt.gsm = ioremap_wc(gtt_bus_addr, gtt_size); + if (!dev_priv->gtt.gsm) { + DRM_ERROR("Failed to map the gtt page table\n"); + return -ENOMEM; } ret = setup_scratch_page(dev); - if (ret) { + if (ret) DRM_ERROR("Scratch setup failed\n"); - goto err_out; - } - dev_priv->mm.gtt->gtt = ioremap_wc(gtt_bus_addr, - dev_priv->mm.gtt->gtt_total_entries * sizeof(gtt_pte_t)); - if (!dev_priv->mm.gtt->gtt) { - DRM_ERROR("Failed to map the gtt page table\n"); - teardown_scratch_page(dev); - ret = -ENOMEM; - goto err_out; + dev_priv->gtt.gtt_clear_range = gen6_ggtt_clear_range; + dev_priv->gtt.gtt_insert_entries = gen6_ggtt_insert_entries; + + return ret; +} + +static void gen6_gmch_remove(struct drm_device *dev) +{ + struct drm_i915_private *dev_priv = dev->dev_private; + iounmap(dev_priv->gtt.gsm); + teardown_scratch_page(dev_priv->dev); +} + +static int i915_gmch_probe(struct drm_device *dev, + size_t *gtt_total, + size_t *stolen, + phys_addr_t *mappable_base, + unsigned long *mappable_end) +{ + struct drm_i915_private *dev_priv = dev->dev_private; + int ret; + + ret = intel_gmch_probe(dev_priv->bridge_dev, dev_priv->dev->pdev, NULL); + if (!ret) { + DRM_ERROR("failed to set up gmch\n"); + return -EIO; } - /* GMADR is the PCI aperture used by SW to access tiled GFX surfaces in a linear fashion. */ - DRM_INFO("Memory usable by graphics device = %dM\n", dev_priv->mm.gtt->gtt_total_entries >> 8); - DRM_DEBUG_DRIVER("GMADR size = %dM\n", dev_priv->mm.gtt->gtt_mappable_entries >> 8); - DRM_DEBUG_DRIVER("GTT stolen size = %dM\n", dev_priv->mm.gtt->stolen_size >> 20); + intel_gtt_get(gtt_total, stolen, mappable_base, mappable_end); + + dev_priv->gtt.do_idle_maps = needs_idle_maps(dev_priv->dev); + dev_priv->gtt.gtt_clear_range = i915_ggtt_clear_range; + dev_priv->gtt.gtt_insert_entries = i915_ggtt_insert_entries; return 0; +} -err_out: - kfree(dev_priv->mm.gtt); - if (INTEL_INFO(dev)->gen < 6) - intel_gmch_remove(); - return ret; +static void i915_gmch_remove(struct drm_device *dev) +{ + intel_gmch_remove(); } -void i915_gem_gtt_fini(struct drm_device *dev) +int i915_gem_gtt_init(struct drm_device *dev) { struct drm_i915_private *dev_priv = dev->dev_private; - iounmap(dev_priv->mm.gtt->gtt); - teardown_scratch_page(dev); - if (INTEL_INFO(dev)->gen < 6) - intel_gmch_remove(); - kfree(dev_priv->mm.gtt); + struct i915_gtt *gtt = &dev_priv->gtt; + unsigned long gtt_size; + int ret; + + if (INTEL_INFO(dev)->gen <= 5) { + dev_priv->gtt.gtt_probe = i915_gmch_probe; + dev_priv->gtt.gtt_remove = i915_gmch_remove; + } else { + dev_priv->gtt.gtt_probe = gen6_gmch_probe; + dev_priv->gtt.gtt_remove = gen6_gmch_remove; + } + + ret = dev_priv->gtt.gtt_probe(dev, &dev_priv->gtt.total, + &dev_priv->gtt.stolen_size, + >t->mappable_base, + >t->mappable_end); + if (ret) + return ret; + + gtt_size = (dev_priv->gtt.total >> PAGE_SHIFT) * sizeof(gtt_pte_t); + + /* GMADR is the PCI mmio aperture into the global GTT. */ + DRM_INFO("Memory usable by graphics device = %zdM\n", + dev_priv->gtt.total >> 20); + DRM_DEBUG_DRIVER("GMADR size = %ldM\n", + dev_priv->gtt.mappable_end >> 20); + DRM_DEBUG_DRIVER("GTT stolen size = %zdM\n", + dev_priv->gtt.stolen_size >> 20); + + return 0; } diff --git a/drivers/gpu/drm/i915/i915_gem_stolen.c b/drivers/gpu/drm/i915/i915_gem_stolen.c index 8e91083b126f..69d97cbac13c 100644 --- a/drivers/gpu/drm/i915/i915_gem_stolen.c +++ b/drivers/gpu/drm/i915/i915_gem_stolen.c @@ -42,85 +42,73 @@ * for is a boon. */ -#define PTE_ADDRESS_MASK 0xfffff000 -#define PTE_ADDRESS_MASK_HIGH 0x000000f0 /* i915+ */ -#define PTE_MAPPING_TYPE_UNCACHED (0 << 1) -#define PTE_MAPPING_TYPE_DCACHE (1 << 1) /* i830 only */ -#define PTE_MAPPING_TYPE_CACHED (3 << 1) -#define PTE_MAPPING_TYPE_MASK (3 << 1) -#define PTE_VALID (1 << 0) - -/** - * i915_stolen_to_phys - take an offset into stolen memory and turn it into - * a physical one - * @dev: drm device - * @offset: address to translate - * - * Some chip functions require allocations from stolen space and need the - * physical address of the memory in question. - */ -static unsigned long i915_stolen_to_phys(struct drm_device *dev, u32 offset) +static unsigned long i915_stolen_to_physical(struct drm_device *dev) { struct drm_i915_private *dev_priv = dev->dev_private; struct pci_dev *pdev = dev_priv->bridge_dev; u32 base; -#if 0 /* On the machines I have tested the Graphics Base of Stolen Memory - * is unreliable, so compute the base by subtracting the stolen memory - * from the Top of Low Usable DRAM which is where the BIOS places - * the graphics stolen memory. + * is unreliable, so on those compute the base by subtracting the + * stolen memory from the Top of Low Usable DRAM which is where the + * BIOS places the graphics stolen memory. + * + * On gen2, the layout is slightly different with the Graphics Segment + * immediately following Top of Memory (or Top of Usable DRAM). Note + * it appears that TOUD is only reported by 865g, so we just use the + * top of memory as determined by the e820 probe. + * + * XXX gen2 requires an unavailable symbol and 945gm fails with + * its value of TOLUD. */ - if (INTEL_INFO(dev)->gen > 3 || IS_G33(dev)) { - /* top 32bits are reserved = 0 */ + base = 0; + if (INTEL_INFO(dev)->gen >= 6) { + /* Read Base Data of Stolen Memory Register (BDSM) directly. + * Note that there is also a MCHBAR miror at 0x1080c0 or + * we could use device 2:0x5c instead. + */ + pci_read_config_dword(pdev, 0xB0, &base); + base &= ~4095; /* lower bits used for locking register */ + } else if (INTEL_INFO(dev)->gen > 3 || IS_G33(dev)) { + /* Read Graphics Base of Stolen Memory directly */ pci_read_config_dword(pdev, 0xA4, &base); - } else { - /* XXX presume 8xx is the same as i915 */ - pci_bus_read_config_dword(pdev->bus, 2, 0x5C, &base); - } -#else - if (INTEL_INFO(dev)->gen > 3 || IS_G33(dev)) { - u16 val; - pci_read_config_word(pdev, 0xb0, &val); - base = val >> 4 << 20; - } else { +#if 0 + } else if (IS_GEN3(dev)) { u8 val; + /* Stolen is immediately below Top of Low Usable DRAM */ pci_read_config_byte(pdev, 0x9c, &val); base = val >> 3 << 27; - } - base -= dev_priv->mm.gtt->stolen_size; + base -= dev_priv->mm.gtt->stolen_size; + } else { + /* Stolen is immediately above Top of Memory */ + base = max_low_pfn_mapped << PAGE_SHIFT; #endif + } - return base + offset; + return base; } -static void i915_warn_stolen(struct drm_device *dev) -{ - DRM_INFO("not enough stolen space for compressed buffer, disabling\n"); - DRM_INFO("hint: you may be able to increase stolen memory size in the BIOS to avoid this\n"); -} - -static void i915_setup_compression(struct drm_device *dev, int size) +static int i915_setup_compression(struct drm_device *dev, int size) { struct drm_i915_private *dev_priv = dev->dev_private; struct drm_mm_node *compressed_fb, *uninitialized_var(compressed_llb); - unsigned long cfb_base; - unsigned long ll_base = 0; - - /* Just in case the BIOS is doing something questionable. */ - intel_disable_fbc(dev); - compressed_fb = drm_mm_search_free(&dev_priv->mm.stolen, size, 4096, 0); + /* Try to over-allocate to reduce reallocations and fragmentation */ + compressed_fb = drm_mm_search_free(&dev_priv->mm.stolen, + size <<= 1, 4096, 0); + if (!compressed_fb) + compressed_fb = drm_mm_search_free(&dev_priv->mm.stolen, + size >>= 1, 4096, 0); if (compressed_fb) compressed_fb = drm_mm_get_block(compressed_fb, size, 4096); if (!compressed_fb) goto err; - cfb_base = i915_stolen_to_phys(dev, compressed_fb->start); - if (!cfb_base) - goto err_fb; - - if (!(IS_GM45(dev) || HAS_PCH_SPLIT(dev))) { + if (HAS_PCH_SPLIT(dev)) + I915_WRITE(ILK_DPFC_CB_BASE, compressed_fb->start); + else if (IS_GM45(dev)) { + I915_WRITE(DPFC_CB_BASE, compressed_fb->start); + } else { compressed_llb = drm_mm_search_free(&dev_priv->mm.stolen, 4096, 4096, 0); if (compressed_llb) @@ -129,73 +117,206 @@ static void i915_setup_compression(struct drm_device *dev, int size) if (!compressed_llb) goto err_fb; - ll_base = i915_stolen_to_phys(dev, compressed_llb->start); - if (!ll_base) - goto err_llb; + dev_priv->compressed_llb = compressed_llb; + + I915_WRITE(FBC_CFB_BASE, + dev_priv->mm.stolen_base + compressed_fb->start); + I915_WRITE(FBC_LL_BASE, + dev_priv->mm.stolen_base + compressed_llb->start); } + dev_priv->compressed_fb = compressed_fb; dev_priv->cfb_size = size; - dev_priv->compressed_fb = compressed_fb; - if (HAS_PCH_SPLIT(dev)) - I915_WRITE(ILK_DPFC_CB_BASE, compressed_fb->start); - else if (IS_GM45(dev)) { - I915_WRITE(DPFC_CB_BASE, compressed_fb->start); - } else { - I915_WRITE(FBC_CFB_BASE, cfb_base); - I915_WRITE(FBC_LL_BASE, ll_base); - dev_priv->compressed_llb = compressed_llb; - } + DRM_DEBUG_KMS("reserved %d bytes of contiguous stolen space for FBC\n", + size); - DRM_DEBUG_KMS("FBC base 0x%08lx, ll base 0x%08lx, size %dM\n", - cfb_base, ll_base, size >> 20); - return; + return 0; -err_llb: - drm_mm_put_block(compressed_llb); err_fb: drm_mm_put_block(compressed_fb); err: - dev_priv->no_fbc_reason = FBC_STOLEN_TOO_SMALL; - i915_warn_stolen(dev); + return -ENOSPC; +} + +int i915_gem_stolen_setup_compression(struct drm_device *dev, int size) +{ + struct drm_i915_private *dev_priv = dev->dev_private; + + if (dev_priv->mm.stolen_base == 0) + return -ENODEV; + + if (size < dev_priv->cfb_size) + return 0; + + /* Release any current block */ + i915_gem_stolen_cleanup_compression(dev); + + return i915_setup_compression(dev, size); } -static void i915_cleanup_compression(struct drm_device *dev) +void i915_gem_stolen_cleanup_compression(struct drm_device *dev) { struct drm_i915_private *dev_priv = dev->dev_private; - drm_mm_put_block(dev_priv->compressed_fb); + if (dev_priv->cfb_size == 0) + return; + + if (dev_priv->compressed_fb) + drm_mm_put_block(dev_priv->compressed_fb); + if (dev_priv->compressed_llb) drm_mm_put_block(dev_priv->compressed_llb); + + dev_priv->cfb_size = 0; } void i915_gem_cleanup_stolen(struct drm_device *dev) { - if (I915_HAS_FBC(dev) && i915_powersave) - i915_cleanup_compression(dev); + struct drm_i915_private *dev_priv = dev->dev_private; + + i915_gem_stolen_cleanup_compression(dev); + drm_mm_takedown(&dev_priv->mm.stolen); } int i915_gem_init_stolen(struct drm_device *dev) { struct drm_i915_private *dev_priv = dev->dev_private; - unsigned long prealloc_size = dev_priv->mm.gtt->stolen_size; + + dev_priv->mm.stolen_base = i915_stolen_to_physical(dev); + if (dev_priv->mm.stolen_base == 0) + return 0; + + DRM_DEBUG_KMS("found %zd bytes of stolen memory at %08lx\n", + dev_priv->gtt.stolen_size, dev_priv->mm.stolen_base); /* Basic memrange allocator for stolen space */ - drm_mm_init(&dev_priv->mm.stolen, 0, prealloc_size); + drm_mm_init(&dev_priv->mm.stolen, 0, dev_priv->gtt.stolen_size); + + return 0; +} - /* Try to set up FBC with a reasonable compressed buffer size */ - if (I915_HAS_FBC(dev) && i915_powersave) { - int cfb_size; +static struct sg_table * +i915_pages_create_for_stolen(struct drm_device *dev, + u32 offset, u32 size) +{ + struct drm_i915_private *dev_priv = dev->dev_private; + struct sg_table *st; + struct scatterlist *sg; + + DRM_DEBUG_DRIVER("offset=0x%x, size=%d\n", offset, size); + BUG_ON(offset > dev_priv->gtt.stolen_size - size); - /* Leave 1M for line length buffer & misc. */ + /* We hide that we have no struct page backing our stolen object + * by wrapping the contiguous physical allocation with a fake + * dma mapping in a single scatterlist. + */ + + st = kmalloc(sizeof(*st), GFP_KERNEL); + if (st == NULL) + return NULL; - /* Try to get a 32M buffer... */ - if (prealloc_size > (36*1024*1024)) - cfb_size = 32*1024*1024; - else /* fall back to 7/8 of the stolen space */ - cfb_size = prealloc_size * 7 / 8; - i915_setup_compression(dev, cfb_size); + if (sg_alloc_table(st, 1, GFP_KERNEL)) { + kfree(st); + return NULL; } - return 0; + sg = st->sgl; + sg->offset = offset; + sg->length = size; + + sg_dma_address(sg) = (dma_addr_t)dev_priv->mm.stolen_base + offset; + sg_dma_len(sg) = size; + + return st; +} + +static int i915_gem_object_get_pages_stolen(struct drm_i915_gem_object *obj) +{ + BUG(); + return -EINVAL; +} + +static void i915_gem_object_put_pages_stolen(struct drm_i915_gem_object *obj) +{ + /* Should only be called during free */ + sg_free_table(obj->pages); + kfree(obj->pages); +} + +static const struct drm_i915_gem_object_ops i915_gem_object_stolen_ops = { + .get_pages = i915_gem_object_get_pages_stolen, + .put_pages = i915_gem_object_put_pages_stolen, +}; + +static struct drm_i915_gem_object * +_i915_gem_object_create_stolen(struct drm_device *dev, + struct drm_mm_node *stolen) +{ + struct drm_i915_gem_object *obj; + + obj = i915_gem_object_alloc(dev); + if (obj == NULL) + return NULL; + + if (drm_gem_private_object_init(dev, &obj->base, stolen->size)) + goto cleanup; + + i915_gem_object_init(obj, &i915_gem_object_stolen_ops); + + obj->pages = i915_pages_create_for_stolen(dev, + stolen->start, stolen->size); + if (obj->pages == NULL) + goto cleanup; + + obj->has_dma_mapping = true; + obj->pages_pin_count = 1; + obj->stolen = stolen; + + obj->base.write_domain = I915_GEM_DOMAIN_GTT; + obj->base.read_domains = I915_GEM_DOMAIN_GTT; + obj->cache_level = I915_CACHE_NONE; + + return obj; + +cleanup: + i915_gem_object_free(obj); + return NULL; +} + +struct drm_i915_gem_object * +i915_gem_object_create_stolen(struct drm_device *dev, u32 size) +{ + struct drm_i915_private *dev_priv = dev->dev_private; + struct drm_i915_gem_object *obj; + struct drm_mm_node *stolen; + + if (dev_priv->mm.stolen_base == 0) + return NULL; + + DRM_DEBUG_KMS("creating stolen object: size=%x\n", size); + if (size == 0) + return NULL; + + stolen = drm_mm_search_free(&dev_priv->mm.stolen, size, 4096, 0); + if (stolen) + stolen = drm_mm_get_block(stolen, size, 4096); + if (stolen == NULL) + return NULL; + + obj = _i915_gem_object_create_stolen(dev, stolen); + if (obj) + return obj; + + drm_mm_put_block(stolen); + return NULL; +} + +void +i915_gem_object_release_stolen(struct drm_i915_gem_object *obj) +{ + if (obj->stolen) { + drm_mm_put_block(obj->stolen); + obj->stolen = NULL; + } } diff --git a/drivers/gpu/drm/i915/i915_gem_tiling.c b/drivers/gpu/drm/i915/i915_gem_tiling.c index cedbfd7b3dfa..abcba2f5a788 100644 --- a/drivers/gpu/drm/i915/i915_gem_tiling.c +++ b/drivers/gpu/drm/i915/i915_gem_tiling.c @@ -272,18 +272,7 @@ i915_gem_object_fence_ok(struct drm_i915_gem_object *obj, int tiling_mode) return false; } - /* - * Previous chips need to be aligned to the size of the smallest - * fence register that can contain the object. - */ - if (INTEL_INFO(obj->base.dev)->gen == 3) - size = 1024*1024; - else - size = 512*1024; - - while (size < obj->base.size) - size <<= 1; - + size = i915_gem_get_gtt_size(obj->base.dev, obj->base.size, tiling_mode); if (obj->gtt_space->size != size) return false; @@ -368,15 +357,15 @@ i915_gem_set_tiling(struct drm_device *dev, void *data, obj->map_and_fenceable = obj->gtt_space == NULL || - (obj->gtt_offset + obj->base.size <= dev_priv->mm.gtt_mappable_end && + (obj->gtt_offset + obj->base.size <= dev_priv->gtt.mappable_end && i915_gem_object_fence_ok(obj, args->tiling_mode)); /* Rebind if we need a change of alignment */ if (!obj->map_and_fenceable) { u32 unfenced_alignment = - i915_gem_get_unfenced_gtt_alignment(dev, - obj->base.size, - args->tiling_mode); + i915_gem_get_gtt_alignment(dev, obj->base.size, + args->tiling_mode, + false); if (obj->gtt_offset & (unfenced_alignment - 1)) ret = i915_gem_object_unbind(obj); } @@ -396,6 +385,18 @@ i915_gem_set_tiling(struct drm_device *dev, void *data, /* we have to maintain this existing ABI... */ args->stride = obj->stride; args->tiling_mode = obj->tiling_mode; + + /* Try to preallocate memory required to save swizzling on put-pages */ + if (i915_gem_object_needs_bit17_swizzle(obj)) { + if (obj->bit_17 == NULL) { + obj->bit_17 = kmalloc(BITS_TO_LONGS(obj->base.size >> PAGE_SHIFT) * + sizeof(long), GFP_KERNEL); + } + } else { + kfree(obj->bit_17); + obj->bit_17 = NULL; + } + drm_gem_object_unreference(&obj->base); mutex_unlock(&dev->struct_mutex); diff --git a/drivers/gpu/drm/i915/i915_irq.c b/drivers/gpu/drm/i915/i915_irq.c index fe843389c7b4..2cd97d1cc920 100644 --- a/drivers/gpu/drm/i915/i915_irq.c +++ b/drivers/gpu/drm/i915/i915_irq.c @@ -287,6 +287,10 @@ static void i915_hotplug_work_func(struct work_struct *work) struct drm_mode_config *mode_config = &dev->mode_config; struct intel_encoder *encoder; + /* HPD irq before everything is fully set up. */ + if (!dev_priv->enable_hotplug_processing) + return; + mutex_lock(&mode_config->mutex); DRM_DEBUG_KMS("running encoder hotplug functions\n"); @@ -300,9 +304,6 @@ static void i915_hotplug_work_func(struct work_struct *work) drm_helper_hpd_irq_event(dev); } -/* defined intel_pm.c */ -extern spinlock_t mchdev_lock; - static void ironlake_handle_rps_change(struct drm_device *dev) { drm_i915_private_t *dev_priv = dev->dev_private; @@ -355,8 +356,8 @@ static void notify_ring(struct drm_device *dev, wake_up_all(&ring->irq_queue); if (i915_enable_hangcheck) { - dev_priv->hangcheck_count = 0; - mod_timer(&dev_priv->hangcheck_timer, + dev_priv->gpu_error.hangcheck_count = 0; + mod_timer(&dev_priv->gpu_error.hangcheck_timer, round_jiffies_up(jiffies + DRM_I915_HANGCHECK_JIFFIES)); } } @@ -524,6 +525,20 @@ static void gen6_queue_rps_work(struct drm_i915_private *dev_priv, queue_work(dev_priv->wq, &dev_priv->rps.work); } +static void gmbus_irq_handler(struct drm_device *dev) +{ + struct drm_i915_private *dev_priv = (drm_i915_private_t *) dev->dev_private; + + wake_up_all(&dev_priv->gmbus_wait_queue); +} + +static void dp_aux_irq_handler(struct drm_device *dev) +{ + struct drm_i915_private *dev_priv = (drm_i915_private_t *) dev->dev_private; + + wake_up_all(&dev_priv->gmbus_wait_queue); +} + static irqreturn_t valleyview_irq_handler(int irq, void *arg) { struct drm_device *dev = (struct drm_device *) arg; @@ -533,7 +548,6 @@ static irqreturn_t valleyview_irq_handler(int irq, void *arg) unsigned long irqflags; int pipe; u32 pipe_stats[I915_MAX_PIPES]; - bool blc_event; atomic_inc(&dev_priv->irq_received); @@ -590,8 +604,8 @@ static irqreturn_t valleyview_irq_handler(int irq, void *arg) I915_READ(PORT_HOTPLUG_STAT); } - if (pipe_stats[pipe] & PIPE_LEGACY_BLC_EVENT_STATUS) - blc_event = true; + if (pipe_stats[0] & PIPE_GMBUS_INTERRUPT_STATUS) + gmbus_irq_handler(dev); if (pm_iir & GEN6_PM_DEFERRED_EVENTS) gen6_queue_rps_work(dev_priv, pm_iir); @@ -618,8 +632,11 @@ static void ibx_irq_handler(struct drm_device *dev, u32 pch_iir) (pch_iir & SDE_AUDIO_POWER_MASK) >> SDE_AUDIO_POWER_SHIFT); + if (pch_iir & SDE_AUX_MASK) + dp_aux_irq_handler(dev); + if (pch_iir & SDE_GMBUS) - DRM_DEBUG_DRIVER("PCH GMBUS interrupt\n"); + gmbus_irq_handler(dev); if (pch_iir & SDE_AUDIO_HDCP_MASK) DRM_DEBUG_DRIVER("PCH HDCP audio interrupt\n"); @@ -662,10 +679,10 @@ static void cpt_irq_handler(struct drm_device *dev, u32 pch_iir) SDE_AUDIO_POWER_SHIFT_CPT); if (pch_iir & SDE_AUX_MASK_CPT) - DRM_DEBUG_DRIVER("AUX channel interrupt\n"); + dp_aux_irq_handler(dev); if (pch_iir & SDE_GMBUS_CPT) - DRM_DEBUG_DRIVER("PCH GMBUS interrupt\n"); + gmbus_irq_handler(dev); if (pch_iir & SDE_AUDIO_CP_REQ_CPT) DRM_DEBUG_DRIVER("Audio CP request interrupt\n"); @@ -703,6 +720,9 @@ static irqreturn_t ivybridge_irq_handler(int irq, void *arg) de_iir = I915_READ(DEIIR); if (de_iir) { + if (de_iir & DE_AUX_CHANNEL_A_IVB) + dp_aux_irq_handler(dev); + if (de_iir & DE_GSE_IVB) intel_opregion_gse_intr(dev); @@ -758,7 +778,7 @@ static irqreturn_t ironlake_irq_handler(int irq, void *arg) struct drm_device *dev = (struct drm_device *) arg; drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private; int ret = IRQ_NONE; - u32 de_iir, gt_iir, de_ier, pch_iir, pm_iir; + u32 de_iir, gt_iir, de_ier, pm_iir; atomic_inc(&dev_priv->irq_received); @@ -769,11 +789,9 @@ static irqreturn_t ironlake_irq_handler(int irq, void *arg) de_iir = I915_READ(DEIIR); gt_iir = I915_READ(GTIIR); - pch_iir = I915_READ(SDEIIR); pm_iir = I915_READ(GEN6_PMIIR); - if (de_iir == 0 && gt_iir == 0 && pch_iir == 0 && - (!IS_GEN6(dev) || pm_iir == 0)) + if (de_iir == 0 && gt_iir == 0 && (!IS_GEN6(dev) || pm_iir == 0)) goto done; ret = IRQ_HANDLED; @@ -783,6 +801,9 @@ static irqreturn_t ironlake_irq_handler(int irq, void *arg) else snb_gt_irq_handler(dev, dev_priv, gt_iir); + if (de_iir & DE_AUX_CHANNEL_A) + dp_aux_irq_handler(dev); + if (de_iir & DE_GSE) intel_opregion_gse_intr(dev); @@ -804,10 +825,15 @@ static irqreturn_t ironlake_irq_handler(int irq, void *arg) /* check event from PCH */ if (de_iir & DE_PCH_EVENT) { + u32 pch_iir = I915_READ(SDEIIR); + if (HAS_PCH_CPT(dev)) cpt_irq_handler(dev, pch_iir); else ibx_irq_handler(dev, pch_iir); + + /* should clear PCH hotplug event before clear CPU irq */ + I915_WRITE(SDEIIR, pch_iir); } if (IS_GEN5(dev) && de_iir & DE_PCU_EVENT) @@ -816,8 +842,6 @@ static irqreturn_t ironlake_irq_handler(int irq, void *arg) if (IS_GEN6(dev) && pm_iir & GEN6_PM_DEFERRED_EVENTS) gen6_queue_rps_work(dev_priv, pm_iir); - /* should clear PCH hotplug event before clear CPU irq */ - I915_WRITE(SDEIIR, pch_iir); I915_WRITE(GTIIR, gt_iir); I915_WRITE(DEIIR, de_iir); I915_WRITE(GEN6_PMIIR, pm_iir); @@ -838,23 +862,60 @@ done: */ static void i915_error_work_func(struct work_struct *work) { - drm_i915_private_t *dev_priv = container_of(work, drm_i915_private_t, - error_work); + struct i915_gpu_error *error = container_of(work, struct i915_gpu_error, + work); + drm_i915_private_t *dev_priv = container_of(error, drm_i915_private_t, + gpu_error); struct drm_device *dev = dev_priv->dev; + struct intel_ring_buffer *ring; char *error_event[] = { "ERROR=1", NULL }; char *reset_event[] = { "RESET=1", NULL }; char *reset_done_event[] = { "ERROR=0", NULL }; + int i, ret; kobject_uevent_env(&dev->primary->kdev.kobj, KOBJ_CHANGE, error_event); - if (atomic_read(&dev_priv->mm.wedged)) { + /* + * Note that there's only one work item which does gpu resets, so we + * need not worry about concurrent gpu resets potentially incrementing + * error->reset_counter twice. We only need to take care of another + * racing irq/hangcheck declaring the gpu dead for a second time. A + * quick check for that is good enough: schedule_work ensures the + * correct ordering between hang detection and this work item, and since + * the reset in-progress bit is only ever set by code outside of this + * work we don't need to worry about any other races. + */ + if (i915_reset_in_progress(error) && !i915_terminally_wedged(error)) { DRM_DEBUG_DRIVER("resetting chip\n"); - kobject_uevent_env(&dev->primary->kdev.kobj, KOBJ_CHANGE, reset_event); - if (!i915_reset(dev)) { - atomic_set(&dev_priv->mm.wedged, 0); - kobject_uevent_env(&dev->primary->kdev.kobj, KOBJ_CHANGE, reset_done_event); + kobject_uevent_env(&dev->primary->kdev.kobj, KOBJ_CHANGE, + reset_event); + + ret = i915_reset(dev); + + if (ret == 0) { + /* + * After all the gem state is reset, increment the reset + * counter and wake up everyone waiting for the reset to + * complete. + * + * Since unlock operations are a one-sided barrier only, + * we need to insert a barrier here to order any seqno + * updates before + * the counter increment. + */ + smp_mb__before_atomic_inc(); + atomic_inc(&dev_priv->gpu_error.reset_counter); + + kobject_uevent_env(&dev->primary->kdev.kobj, + KOBJ_CHANGE, reset_done_event); + } else { + atomic_set(&error->reset_counter, I915_WEDGED); } - complete_all(&dev_priv->error_completion); + + for_each_ring(ring, dev_priv, i) + wake_up_all(&ring->irq_queue); + + wake_up_all(&dev_priv->gpu_error.reset_queue); } } @@ -915,7 +976,7 @@ i915_error_object_create(struct drm_i915_private *dev_priv, goto unwind; local_irq_save(flags); - if (reloc_offset < dev_priv->mm.gtt_mappable_end && + if (reloc_offset < dev_priv->gtt.mappable_end && src->has_global_gtt_mapping) { void __iomem *s; @@ -924,10 +985,18 @@ i915_error_object_create(struct drm_i915_private *dev_priv, * captures what the GPU read. */ - s = io_mapping_map_atomic_wc(dev_priv->mm.gtt_mapping, + s = io_mapping_map_atomic_wc(dev_priv->gtt.mappable, reloc_offset); memcpy_fromio(d, s, PAGE_SIZE); io_mapping_unmap_atomic(s); + } else if (src->stolen) { + unsigned long offset; + + offset = dev_priv->mm.stolen_base; + offset += src->stolen->start; + offset += i << PAGE_SHIFT; + + memcpy_fromio(d, (void __iomem *) offset, PAGE_SIZE); } else { struct page *page; void *s; @@ -1074,6 +1143,8 @@ static void i915_gem_record_fences(struct drm_device *dev, error->fence[i] = I915_READ(FENCE_REG_830_0 + (i * 4)); break; + default: + BUG(); } } @@ -1222,9 +1293,9 @@ static void i915_capture_error_state(struct drm_device *dev) unsigned long flags; int i, pipe; - spin_lock_irqsave(&dev_priv->error_lock, flags); - error = dev_priv->first_error; - spin_unlock_irqrestore(&dev_priv->error_lock, flags); + spin_lock_irqsave(&dev_priv->gpu_error.lock, flags); + error = dev_priv->gpu_error.first_error; + spin_unlock_irqrestore(&dev_priv->gpu_error.lock, flags); if (error) return; @@ -1235,7 +1306,8 @@ static void i915_capture_error_state(struct drm_device *dev) return; } - DRM_INFO("capturing error event; look for more information in /debug/dri/%d/i915_error_state\n", + DRM_INFO("capturing error event; look for more information in" + "/sys/kernel/debug/dri/%d/i915_error_state\n", dev->primary->index); kref_init(&error->ref); @@ -1318,12 +1390,12 @@ static void i915_capture_error_state(struct drm_device *dev) error->overlay = intel_overlay_capture_error_state(dev); error->display = intel_display_capture_error_state(dev); - spin_lock_irqsave(&dev_priv->error_lock, flags); - if (dev_priv->first_error == NULL) { - dev_priv->first_error = error; + spin_lock_irqsave(&dev_priv->gpu_error.lock, flags); + if (dev_priv->gpu_error.first_error == NULL) { + dev_priv->gpu_error.first_error = error; error = NULL; } - spin_unlock_irqrestore(&dev_priv->error_lock, flags); + spin_unlock_irqrestore(&dev_priv->gpu_error.lock, flags); if (error) i915_error_state_free(&error->ref); @@ -1335,10 +1407,10 @@ void i915_destroy_error_state(struct drm_device *dev) struct drm_i915_error_state *error; unsigned long flags; - spin_lock_irqsave(&dev_priv->error_lock, flags); - error = dev_priv->first_error; - dev_priv->first_error = NULL; - spin_unlock_irqrestore(&dev_priv->error_lock, flags); + spin_lock_irqsave(&dev_priv->gpu_error.lock, flags); + error = dev_priv->gpu_error.first_error; + dev_priv->gpu_error.first_error = NULL; + spin_unlock_irqrestore(&dev_priv->gpu_error.lock, flags); if (error) kref_put(&error->ref, i915_error_state_free); @@ -1459,17 +1531,18 @@ void i915_handle_error(struct drm_device *dev, bool wedged) i915_report_and_clear_eir(dev); if (wedged) { - INIT_COMPLETION(dev_priv->error_completion); - atomic_set(&dev_priv->mm.wedged, 1); + atomic_set_mask(I915_RESET_IN_PROGRESS_FLAG, + &dev_priv->gpu_error.reset_counter); /* - * Wakeup waiting processes so they don't hang + * Wakeup waiting processes so that the reset work item + * doesn't deadlock trying to grab various locks. */ for_each_ring(ring, dev_priv, i) wake_up_all(&ring->irq_queue); } - queue_work(dev_priv->wq, &dev_priv->error_work); + queue_work(dev_priv->wq, &dev_priv->gpu_error.work); } static void i915_pageflip_stall_check(struct drm_device *dev, int pipe) @@ -1700,7 +1773,7 @@ static bool i915_hangcheck_hung(struct drm_device *dev) { drm_i915_private_t *dev_priv = dev->dev_private; - if (dev_priv->hangcheck_count++ > 1) { + if (dev_priv->gpu_error.hangcheck_count++ > 1) { bool hung = true; DRM_ERROR("Hangcheck timer elapsed... GPU hung\n"); @@ -1759,25 +1832,29 @@ void i915_hangcheck_elapsed(unsigned long data) goto repeat; } - dev_priv->hangcheck_count = 0; + dev_priv->gpu_error.hangcheck_count = 0; return; } i915_get_extra_instdone(dev, instdone); - if (memcmp(dev_priv->last_acthd, acthd, sizeof(acthd)) == 0 && - memcmp(dev_priv->prev_instdone, instdone, sizeof(instdone)) == 0) { + if (memcmp(dev_priv->gpu_error.last_acthd, acthd, + sizeof(acthd)) == 0 && + memcmp(dev_priv->gpu_error.prev_instdone, instdone, + sizeof(instdone)) == 0) { if (i915_hangcheck_hung(dev)) return; } else { - dev_priv->hangcheck_count = 0; + dev_priv->gpu_error.hangcheck_count = 0; - memcpy(dev_priv->last_acthd, acthd, sizeof(acthd)); - memcpy(dev_priv->prev_instdone, instdone, sizeof(instdone)); + memcpy(dev_priv->gpu_error.last_acthd, acthd, + sizeof(acthd)); + memcpy(dev_priv->gpu_error.prev_instdone, instdone, + sizeof(instdone)); } repeat: /* Reset timer case chip hangs without another request being added */ - mod_timer(&dev_priv->hangcheck_timer, + mod_timer(&dev_priv->gpu_error.hangcheck_timer, round_jiffies_up(jiffies + DRM_I915_HANGCHECK_JIFFIES)); } @@ -1847,7 +1924,7 @@ static void valleyview_irq_preinstall(struct drm_device *dev) * This register is the same on all known PCH chips. */ -static void ironlake_enable_pch_hotplug(struct drm_device *dev) +static void ibx_enable_hotplug(struct drm_device *dev) { drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private; u32 hotplug; @@ -1860,14 +1937,36 @@ static void ironlake_enable_pch_hotplug(struct drm_device *dev) I915_WRITE(PCH_PORT_HOTPLUG, hotplug); } +static void ibx_irq_postinstall(struct drm_device *dev) +{ + drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private; + u32 mask; + + if (HAS_PCH_IBX(dev)) + mask = SDE_HOTPLUG_MASK | + SDE_GMBUS | + SDE_AUX_MASK; + else + mask = SDE_HOTPLUG_MASK_CPT | + SDE_GMBUS_CPT | + SDE_AUX_MASK_CPT; + + I915_WRITE(SDEIIR, I915_READ(SDEIIR)); + I915_WRITE(SDEIMR, ~mask); + I915_WRITE(SDEIER, mask); + POSTING_READ(SDEIER); + + ibx_enable_hotplug(dev); +} + static int ironlake_irq_postinstall(struct drm_device *dev) { drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private; /* enable kind of interrupts always enabled */ u32 display_mask = DE_MASTER_IRQ_CONTROL | DE_GSE | DE_PCH_EVENT | - DE_PLANEA_FLIP_DONE | DE_PLANEB_FLIP_DONE; + DE_PLANEA_FLIP_DONE | DE_PLANEB_FLIP_DONE | + DE_AUX_CHANNEL_A; u32 render_irqs; - u32 hotplug_mask; dev_priv->irq_mask = ~display_mask; @@ -1895,27 +1994,7 @@ static int ironlake_irq_postinstall(struct drm_device *dev) I915_WRITE(GTIER, render_irqs); POSTING_READ(GTIER); - if (HAS_PCH_CPT(dev)) { - hotplug_mask = (SDE_CRT_HOTPLUG_CPT | - SDE_PORTB_HOTPLUG_CPT | - SDE_PORTC_HOTPLUG_CPT | - SDE_PORTD_HOTPLUG_CPT); - } else { - hotplug_mask = (SDE_CRT_HOTPLUG | - SDE_PORTB_HOTPLUG | - SDE_PORTC_HOTPLUG | - SDE_PORTD_HOTPLUG | - SDE_AUX_MASK); - } - - dev_priv->pch_irq_mask = ~hotplug_mask; - - I915_WRITE(SDEIIR, I915_READ(SDEIIR)); - I915_WRITE(SDEIMR, dev_priv->pch_irq_mask); - I915_WRITE(SDEIER, hotplug_mask); - POSTING_READ(SDEIER); - - ironlake_enable_pch_hotplug(dev); + ibx_irq_postinstall(dev); if (IS_IRONLAKE_M(dev)) { /* Clear & enable PCU event interrupts */ @@ -1935,9 +2014,9 @@ static int ivybridge_irq_postinstall(struct drm_device *dev) DE_MASTER_IRQ_CONTROL | DE_GSE_IVB | DE_PCH_EVENT_IVB | DE_PLANEC_FLIP_DONE_IVB | DE_PLANEB_FLIP_DONE_IVB | - DE_PLANEA_FLIP_DONE_IVB; + DE_PLANEA_FLIP_DONE_IVB | + DE_AUX_CHANNEL_A_IVB; u32 render_irqs; - u32 hotplug_mask; dev_priv->irq_mask = ~display_mask; @@ -1961,18 +2040,7 @@ static int ivybridge_irq_postinstall(struct drm_device *dev) I915_WRITE(GTIER, render_irqs); POSTING_READ(GTIER); - hotplug_mask = (SDE_CRT_HOTPLUG_CPT | - SDE_PORTB_HOTPLUG_CPT | - SDE_PORTC_HOTPLUG_CPT | - SDE_PORTD_HOTPLUG_CPT); - dev_priv->pch_irq_mask = ~hotplug_mask; - - I915_WRITE(SDEIIR, I915_READ(SDEIIR)); - I915_WRITE(SDEIMR, dev_priv->pch_irq_mask); - I915_WRITE(SDEIER, hotplug_mask); - POSTING_READ(SDEIER); - - ironlake_enable_pch_hotplug(dev); + ibx_irq_postinstall(dev); return 0; } @@ -1981,7 +2049,6 @@ static int valleyview_irq_postinstall(struct drm_device *dev) { drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private; u32 enable_mask; - u32 hotplug_en = I915_READ(PORT_HOTPLUG_EN); u32 pipestat_enable = PLANE_FLIP_DONE_INT_EN_VLV; u32 render_irqs; u16 msid; @@ -2010,6 +2077,9 @@ static int valleyview_irq_postinstall(struct drm_device *dev) msid |= (1<<14); pci_write_config_word(dev_priv->dev->pdev, 0x98, msid); + I915_WRITE(PORT_HOTPLUG_EN, 0); + POSTING_READ(PORT_HOTPLUG_EN); + I915_WRITE(VLV_IMR, dev_priv->irq_mask); I915_WRITE(VLV_IER, enable_mask); I915_WRITE(VLV_IIR, 0xffffffff); @@ -2018,6 +2088,7 @@ static int valleyview_irq_postinstall(struct drm_device *dev) POSTING_READ(VLV_IER); i915_enable_pipestat(dev_priv, 0, pipestat_enable); + i915_enable_pipestat(dev_priv, 0, PIPE_GMBUS_EVENT_ENABLE); i915_enable_pipestat(dev_priv, 1, pipestat_enable); I915_WRITE(VLV_IIR, 0xffffffff); @@ -2038,13 +2109,22 @@ static int valleyview_irq_postinstall(struct drm_device *dev) #endif I915_WRITE(VLV_MASTER_IER, MASTER_INTERRUPT_ENABLE); + + return 0; +} + +static void valleyview_hpd_irq_setup(struct drm_device *dev) +{ + drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private; + u32 hotplug_en = I915_READ(PORT_HOTPLUG_EN); + /* Note HDMI and DP share bits */ - if (dev_priv->hotplug_supported_mask & HDMIB_HOTPLUG_INT_STATUS) - hotplug_en |= HDMIB_HOTPLUG_INT_EN; - if (dev_priv->hotplug_supported_mask & HDMIC_HOTPLUG_INT_STATUS) - hotplug_en |= HDMIC_HOTPLUG_INT_EN; - if (dev_priv->hotplug_supported_mask & HDMID_HOTPLUG_INT_STATUS) - hotplug_en |= HDMID_HOTPLUG_INT_EN; + if (dev_priv->hotplug_supported_mask & PORTB_HOTPLUG_INT_STATUS) + hotplug_en |= PORTB_HOTPLUG_INT_EN; + if (dev_priv->hotplug_supported_mask & PORTC_HOTPLUG_INT_STATUS) + hotplug_en |= PORTC_HOTPLUG_INT_EN; + if (dev_priv->hotplug_supported_mask & PORTD_HOTPLUG_INT_STATUS) + hotplug_en |= PORTD_HOTPLUG_INT_EN; if (dev_priv->hotplug_supported_mask & SDVOC_HOTPLUG_INT_STATUS_I915) hotplug_en |= SDVOC_HOTPLUG_INT_EN; if (dev_priv->hotplug_supported_mask & SDVOB_HOTPLUG_INT_STATUS_I915) @@ -2055,8 +2135,6 @@ static int valleyview_irq_postinstall(struct drm_device *dev) } I915_WRITE(PORT_HOTPLUG_EN, hotplug_en); - - return 0; } static void valleyview_irq_uninstall(struct drm_device *dev) @@ -2286,6 +2364,9 @@ static int i915_irq_postinstall(struct drm_device *dev) I915_USER_INTERRUPT; if (I915_HAS_HOTPLUG(dev)) { + I915_WRITE(PORT_HOTPLUG_EN, 0); + POSTING_READ(PORT_HOTPLUG_EN); + /* Enable in IER... */ enable_mask |= I915_DISPLAY_PORT_INTERRUPT; /* and unmask in IMR */ @@ -2296,15 +2377,25 @@ static int i915_irq_postinstall(struct drm_device *dev) I915_WRITE(IER, enable_mask); POSTING_READ(IER); + intel_opregion_enable_asle(dev); + + return 0; +} + +static void i915_hpd_irq_setup(struct drm_device *dev) +{ + drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private; + u32 hotplug_en; + if (I915_HAS_HOTPLUG(dev)) { - u32 hotplug_en = I915_READ(PORT_HOTPLUG_EN); - - if (dev_priv->hotplug_supported_mask & HDMIB_HOTPLUG_INT_STATUS) - hotplug_en |= HDMIB_HOTPLUG_INT_EN; - if (dev_priv->hotplug_supported_mask & HDMIC_HOTPLUG_INT_STATUS) - hotplug_en |= HDMIC_HOTPLUG_INT_EN; - if (dev_priv->hotplug_supported_mask & HDMID_HOTPLUG_INT_STATUS) - hotplug_en |= HDMID_HOTPLUG_INT_EN; + hotplug_en = I915_READ(PORT_HOTPLUG_EN); + + if (dev_priv->hotplug_supported_mask & PORTB_HOTPLUG_INT_STATUS) + hotplug_en |= PORTB_HOTPLUG_INT_EN; + if (dev_priv->hotplug_supported_mask & PORTC_HOTPLUG_INT_STATUS) + hotplug_en |= PORTC_HOTPLUG_INT_EN; + if (dev_priv->hotplug_supported_mask & PORTD_HOTPLUG_INT_STATUS) + hotplug_en |= PORTD_HOTPLUG_INT_EN; if (dev_priv->hotplug_supported_mask & SDVOC_HOTPLUG_INT_STATUS_I915) hotplug_en |= SDVOC_HOTPLUG_INT_EN; if (dev_priv->hotplug_supported_mask & SDVOB_HOTPLUG_INT_STATUS_I915) @@ -2318,10 +2409,6 @@ static int i915_irq_postinstall(struct drm_device *dev) I915_WRITE(PORT_HOTPLUG_EN, hotplug_en); } - - intel_opregion_enable_asle(dev); - - return 0; } static irqreturn_t i915_irq_handler(int irq, void *arg) @@ -2481,7 +2568,6 @@ static void i965_irq_preinstall(struct drm_device * dev) static int i965_irq_postinstall(struct drm_device *dev) { drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private; - u32 hotplug_en; u32 enable_mask; u32 error_mask; @@ -2502,6 +2588,7 @@ static int i965_irq_postinstall(struct drm_device *dev) dev_priv->pipestat[0] = 0; dev_priv->pipestat[1] = 0; + i915_enable_pipestat(dev_priv, 0, PIPE_GMBUS_EVENT_ENABLE); /* * Enable some error detection, note the instruction error mask @@ -2522,14 +2609,27 @@ static int i965_irq_postinstall(struct drm_device *dev) I915_WRITE(IER, enable_mask); POSTING_READ(IER); + I915_WRITE(PORT_HOTPLUG_EN, 0); + POSTING_READ(PORT_HOTPLUG_EN); + + intel_opregion_enable_asle(dev); + + return 0; +} + +static void i965_hpd_irq_setup(struct drm_device *dev) +{ + drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private; + u32 hotplug_en; + /* Note HDMI and DP share hotplug bits */ hotplug_en = 0; - if (dev_priv->hotplug_supported_mask & HDMIB_HOTPLUG_INT_STATUS) - hotplug_en |= HDMIB_HOTPLUG_INT_EN; - if (dev_priv->hotplug_supported_mask & HDMIC_HOTPLUG_INT_STATUS) - hotplug_en |= HDMIC_HOTPLUG_INT_EN; - if (dev_priv->hotplug_supported_mask & HDMID_HOTPLUG_INT_STATUS) - hotplug_en |= HDMID_HOTPLUG_INT_EN; + if (dev_priv->hotplug_supported_mask & PORTB_HOTPLUG_INT_STATUS) + hotplug_en |= PORTB_HOTPLUG_INT_EN; + if (dev_priv->hotplug_supported_mask & PORTC_HOTPLUG_INT_STATUS) + hotplug_en |= PORTC_HOTPLUG_INT_EN; + if (dev_priv->hotplug_supported_mask & PORTD_HOTPLUG_INT_STATUS) + hotplug_en |= PORTD_HOTPLUG_INT_EN; if (IS_G4X(dev)) { if (dev_priv->hotplug_supported_mask & SDVOC_HOTPLUG_INT_STATUS_G4X) hotplug_en |= SDVOC_HOTPLUG_INT_EN; @@ -2556,10 +2656,6 @@ static int i965_irq_postinstall(struct drm_device *dev) /* Ignore TV since it's buggy */ I915_WRITE(PORT_HOTPLUG_EN, hotplug_en); - - intel_opregion_enable_asle(dev); - - return 0; } static irqreturn_t i965_irq_handler(int irq, void *arg) @@ -2655,6 +2751,9 @@ static irqreturn_t i965_irq_handler(int irq, void *arg) if (blc_event || (iir & I915_ASLE_INTERRUPT)) intel_opregion_asle_intr(dev); + if (pipe_stats[0] & PIPE_GMBUS_INTERRUPT_STATUS) + gmbus_irq_handler(dev); + /* With MSI, interrupts are only generated when iir * transitions from zero to nonzero. If another bit got * set while we were handling the existing iir bits, then @@ -2706,10 +2805,16 @@ void intel_irq_init(struct drm_device *dev) struct drm_i915_private *dev_priv = dev->dev_private; INIT_WORK(&dev_priv->hotplug_work, i915_hotplug_work_func); - INIT_WORK(&dev_priv->error_work, i915_error_work_func); + INIT_WORK(&dev_priv->gpu_error.work, i915_error_work_func); INIT_WORK(&dev_priv->rps.work, gen6_pm_rps_work); INIT_WORK(&dev_priv->l3_parity.error_work, ivybridge_parity_work); + setup_timer(&dev_priv->gpu_error.hangcheck_timer, + i915_hangcheck_elapsed, + (unsigned long) dev); + + pm_qos_add_request(&dev_priv->pm_qos, PM_QOS_CPU_DMA_LATENCY, PM_QOS_DEFAULT_VALUE); + dev->driver->get_vblank_counter = i915_get_vblank_counter; dev->max_vblank_count = 0xffffff; /* only 24 bits of frame count */ if (IS_G4X(dev) || INTEL_INFO(dev)->gen >= 5) { @@ -2730,7 +2835,8 @@ void intel_irq_init(struct drm_device *dev) dev->driver->irq_uninstall = valleyview_irq_uninstall; dev->driver->enable_vblank = valleyview_enable_vblank; dev->driver->disable_vblank = valleyview_disable_vblank; - } else if (IS_IVYBRIDGE(dev)) { + dev_priv->display.hpd_irq_setup = valleyview_hpd_irq_setup; + } else if (IS_IVYBRIDGE(dev) || IS_HASWELL(dev)) { /* Share pre & uninstall handlers with ILK/SNB */ dev->driver->irq_handler = ivybridge_irq_handler; dev->driver->irq_preinstall = ironlake_irq_preinstall; @@ -2738,14 +2844,6 @@ void intel_irq_init(struct drm_device *dev) dev->driver->irq_uninstall = ironlake_irq_uninstall; dev->driver->enable_vblank = ivybridge_enable_vblank; dev->driver->disable_vblank = ivybridge_disable_vblank; - } else if (IS_HASWELL(dev)) { - /* Share interrupts handling with IVB */ - dev->driver->irq_handler = ivybridge_irq_handler; - dev->driver->irq_preinstall = ironlake_irq_preinstall; - dev->driver->irq_postinstall = ivybridge_irq_postinstall; - dev->driver->irq_uninstall = ironlake_irq_uninstall; - dev->driver->enable_vblank = ivybridge_enable_vblank; - dev->driver->disable_vblank = ivybridge_disable_vblank; } else if (HAS_PCH_SPLIT(dev)) { dev->driver->irq_handler = ironlake_irq_handler; dev->driver->irq_preinstall = ironlake_irq_preinstall; @@ -2764,13 +2862,23 @@ void intel_irq_init(struct drm_device *dev) dev->driver->irq_postinstall = i915_irq_postinstall; dev->driver->irq_uninstall = i915_irq_uninstall; dev->driver->irq_handler = i915_irq_handler; + dev_priv->display.hpd_irq_setup = i915_hpd_irq_setup; } else { dev->driver->irq_preinstall = i965_irq_preinstall; dev->driver->irq_postinstall = i965_irq_postinstall; dev->driver->irq_uninstall = i965_irq_uninstall; dev->driver->irq_handler = i965_irq_handler; + dev_priv->display.hpd_irq_setup = i965_hpd_irq_setup; } dev->driver->enable_vblank = i915_enable_vblank; dev->driver->disable_vblank = i915_disable_vblank; } } + +void intel_hpd_init(struct drm_device *dev) +{ + struct drm_i915_private *dev_priv = dev->dev_private; + + if (dev_priv->display.hpd_irq_setup) + dev_priv->display.hpd_irq_setup(dev); +} diff --git a/drivers/gpu/drm/i915/i915_reg.h b/drivers/gpu/drm/i915/i915_reg.h index 59afb7eb6db6..527b664d3434 100644 --- a/drivers/gpu/drm/i915/i915_reg.h +++ b/drivers/gpu/drm/i915/i915_reg.h @@ -141,8 +141,15 @@ #define VGA_MSR_MEM_EN (1<<1) #define VGA_MSR_CGA_MODE (1<<0) -#define VGA_SR_INDEX 0x3c4 -#define VGA_SR_DATA 0x3c5 +/* + * SR01 is the only VGA register touched on non-UMS setups. + * VLV doesn't do UMS, so the sequencer index/data registers + * are the only VGA registers which need to include + * display_mmio_offset. + */ +#define VGA_SR_INDEX (dev_priv->info->display_mmio_offset + 0x3c4) +#define SR01 1 +#define VGA_SR_DATA (dev_priv->info->display_mmio_offset + 0x3c5) #define VGA_AR_INDEX 0x3c0 #define VGA_AR_VID_EN (1<<5) @@ -301,6 +308,7 @@ #define DISPLAY_PLANE_A (0<<20) #define DISPLAY_PLANE_B (1<<20) #define GFX_OP_PIPE_CONTROL(len) ((0x3<<29)|(0x3<<27)|(0x2<<24)|(len-2)) +#define PIPE_CONTROL_GLOBAL_GTT_IVB (1<<24) /* gen7+ */ #define PIPE_CONTROL_CS_STALL (1<<20) #define PIPE_CONTROL_TLB_INVALIDATE (1<<18) #define PIPE_CONTROL_QW_WRITE (1<<14) @@ -335,17 +343,19 @@ * 0x801c/3c: core clock bits * 0x8048/68: low pass filter coefficients * 0x8100: fast clock controls + * + * DPIO is VLV only. */ -#define DPIO_PKT 0x2100 +#define DPIO_PKT (VLV_DISPLAY_BASE + 0x2100) #define DPIO_RID (0<<24) #define DPIO_OP_WRITE (1<<16) #define DPIO_OP_READ (0<<16) #define DPIO_PORTID (0x12<<8) #define DPIO_BYTE (0xf<<4) #define DPIO_BUSY (1<<0) /* status only */ -#define DPIO_DATA 0x2104 -#define DPIO_REG 0x2108 -#define DPIO_CTL 0x2110 +#define DPIO_DATA (VLV_DISPLAY_BASE + 0x2104) +#define DPIO_REG (VLV_DISPLAY_BASE + 0x2108) +#define DPIO_CTL (VLV_DISPLAY_BASE + 0x2110) #define DPIO_MODSEL1 (1<<3) /* if ref clk b == 27 */ #define DPIO_MODSEL0 (1<<2) /* if ref clk a == 27 */ #define DPIO_SFR_BYPASS (1<<1) @@ -556,13 +566,13 @@ #define IIR 0x020a4 #define IMR 0x020a8 #define ISR 0x020ac -#define VLV_GUNIT_CLOCK_GATE 0x182060 +#define VLV_GUNIT_CLOCK_GATE (VLV_DISPLAY_BASE + 0x2060) #define GCFG_DIS (1<<8) -#define VLV_IIR_RW 0x182084 -#define VLV_IER 0x1820a0 -#define VLV_IIR 0x1820a4 -#define VLV_IMR 0x1820a8 -#define VLV_ISR 0x1820ac +#define VLV_IIR_RW (VLV_DISPLAY_BASE + 0x2084) +#define VLV_IER (VLV_DISPLAY_BASE + 0x20a0) +#define VLV_IIR (VLV_DISPLAY_BASE + 0x20a4) +#define VLV_IMR (VLV_DISPLAY_BASE + 0x20a8) +#define VLV_ISR (VLV_DISPLAY_BASE + 0x20ac) #define I915_PIPE_CONTROL_NOTIFY_INTERRUPT (1<<18) #define I915_DISPLAY_PORT_INTERRUPT (1<<17) #define I915_RENDER_COMMAND_PARSER_ERROR_INTERRUPT (1<<15) @@ -735,6 +745,7 @@ #define GEN7_FF_TS_SCHED_HS0 (0x3<<16) #define GEN7_FF_TS_SCHED_LOAD_BALANCE (0x1<<16) #define GEN7_FF_TS_SCHED_HW (0x0<<16) /* Default */ +#define GEN7_FF_VS_REF_CNT_FFME (1 << 15) #define GEN7_FF_VS_SCHED_HS1 (0x5<<12) #define GEN7_FF_VS_SCHED_HS0 (0x3<<12) #define GEN7_FF_VS_SCHED_LOAD_BALANCE (0x1<<12) /* Default */ @@ -921,8 +932,8 @@ #define VGA1_PD_P1_DIV_2 (1 << 13) #define VGA1_PD_P1_SHIFT 8 #define VGA1_PD_P1_MASK (0x1f << 8) -#define _DPLL_A 0x06014 -#define _DPLL_B 0x06018 +#define _DPLL_A (dev_priv->info->display_mmio_offset + 0x6014) +#define _DPLL_B (dev_priv->info->display_mmio_offset + 0x6018) #define DPLL(pipe) _PIPE(pipe, _DPLL_A, _DPLL_B) #define DPLL_VCO_ENABLE (1 << 31) #define DPLL_DVO_HIGH_SPEED (1 << 30) @@ -943,23 +954,6 @@ #define DPLL_LOCK_VLV (1<<15) #define DPLL_INTEGRATED_CLOCK_VLV (1<<13) -#define SRX_INDEX 0x3c4 -#define SRX_DATA 0x3c5 -#define SR01 1 -#define SR01_SCREEN_OFF (1<<5) - -#define PPCR 0x61204 -#define PPCR_ON (1<<0) - -#define DVOB 0x61140 -#define DVOB_ON (1<<31) -#define DVOC 0x61160 -#define DVOC_ON (1<<31) -#define LVDS 0x61180 -#define LVDS_ON (1<<31) - -/* Scratch pad debug 0 reg: - */ #define DPLL_FPA01_P1_POST_DIV_MASK_I830 0x001f0000 /* * The i830 generation, in LVDS mode, defines P1 as the bit number set within @@ -998,7 +992,7 @@ #define SDVO_MULTIPLIER_MASK 0x000000ff #define SDVO_MULTIPLIER_SHIFT_HIRES 4 #define SDVO_MULTIPLIER_SHIFT_VGA 0 -#define _DPLL_A_MD 0x0601c /* 965+ only */ +#define _DPLL_A_MD (dev_priv->info->display_mmio_offset + 0x601c) /* 965+ only */ /* * UDI pixel divider, controlling how many pixels are stuffed into a packet. * @@ -1035,7 +1029,7 @@ */ #define DPLL_MD_VGA_UDI_MULTIPLIER_MASK 0x0000003f #define DPLL_MD_VGA_UDI_MULTIPLIER_SHIFT 0 -#define _DPLL_B_MD 0x06020 /* 965+ only */ +#define _DPLL_B_MD (dev_priv->info->display_mmio_offset + 0x6020) /* 965+ only */ #define DPLL_MD(pipe) _PIPE(pipe, _DPLL_A_MD, _DPLL_B_MD) #define _FPA0 0x06040 @@ -1178,15 +1172,15 @@ #define RAMCLK_GATE_D 0x6210 /* CRL only */ #define DEUC 0x6214 /* CRL only */ -#define FW_BLC_SELF_VLV 0x6500 +#define FW_BLC_SELF_VLV (VLV_DISPLAY_BASE + 0x6500) #define FW_CSPWRDWNEN (1<<15) /* * Palette regs */ -#define _PALETTE_A 0x0a000 -#define _PALETTE_B 0x0a800 +#define _PALETTE_A (dev_priv->info->display_mmio_offset + 0xa000) +#define _PALETTE_B (dev_priv->info->display_mmio_offset + 0xa800) #define PALETTE(pipe) _PIPE(pipe, _PALETTE_A, _PALETTE_B) /* MCH MMIO space */ @@ -1242,6 +1236,10 @@ #define MAD_DIMM_A_SIZE_SHIFT 0 #define MAD_DIMM_A_SIZE_MASK (0xff << MAD_DIMM_A_SIZE_SHIFT) +/** snb MCH registers for priority tuning */ +#define MCH_SSKPD (MCHBAR_MIRROR_BASE_SNB + 0x5d10) +#define MCH_SSKPD_WM0_MASK 0x3f +#define MCH_SSKPD_WM0_VAL 0xc /* Clocking configuration register */ #define CLKCFG 0x10c00 @@ -1551,26 +1549,26 @@ */ /* Pipe A timing regs */ -#define _HTOTAL_A 0x60000 -#define _HBLANK_A 0x60004 -#define _HSYNC_A 0x60008 -#define _VTOTAL_A 0x6000c -#define _VBLANK_A 0x60010 -#define _VSYNC_A 0x60014 -#define _PIPEASRC 0x6001c -#define _BCLRPAT_A 0x60020 -#define _VSYNCSHIFT_A 0x60028 +#define _HTOTAL_A (dev_priv->info->display_mmio_offset + 0x60000) +#define _HBLANK_A (dev_priv->info->display_mmio_offset + 0x60004) +#define _HSYNC_A (dev_priv->info->display_mmio_offset + 0x60008) +#define _VTOTAL_A (dev_priv->info->display_mmio_offset + 0x6000c) +#define _VBLANK_A (dev_priv->info->display_mmio_offset + 0x60010) +#define _VSYNC_A (dev_priv->info->display_mmio_offset + 0x60014) +#define _PIPEASRC (dev_priv->info->display_mmio_offset + 0x6001c) +#define _BCLRPAT_A (dev_priv->info->display_mmio_offset + 0x60020) +#define _VSYNCSHIFT_A (dev_priv->info->display_mmio_offset + 0x60028) /* Pipe B timing regs */ -#define _HTOTAL_B 0x61000 -#define _HBLANK_B 0x61004 -#define _HSYNC_B 0x61008 -#define _VTOTAL_B 0x6100c -#define _VBLANK_B 0x61010 -#define _VSYNC_B 0x61014 -#define _PIPEBSRC 0x6101c -#define _BCLRPAT_B 0x61020 -#define _VSYNCSHIFT_B 0x61028 +#define _HTOTAL_B (dev_priv->info->display_mmio_offset + 0x61000) +#define _HBLANK_B (dev_priv->info->display_mmio_offset + 0x61004) +#define _HSYNC_B (dev_priv->info->display_mmio_offset + 0x61008) +#define _VTOTAL_B (dev_priv->info->display_mmio_offset + 0x6100c) +#define _VBLANK_B (dev_priv->info->display_mmio_offset + 0x61010) +#define _VSYNC_B (dev_priv->info->display_mmio_offset + 0x61014) +#define _PIPEBSRC (dev_priv->info->display_mmio_offset + 0x6101c) +#define _BCLRPAT_B (dev_priv->info->display_mmio_offset + 0x61020) +#define _VSYNCSHIFT_B (dev_priv->info->display_mmio_offset + 0x61028) #define HTOTAL(trans) _TRANSCODER(trans, _HTOTAL_A, _HTOTAL_B) @@ -1631,13 +1629,10 @@ /* Hotplug control (945+ only) */ -#define PORT_HOTPLUG_EN 0x61110 -#define HDMIB_HOTPLUG_INT_EN (1 << 29) -#define DPB_HOTPLUG_INT_EN (1 << 29) -#define HDMIC_HOTPLUG_INT_EN (1 << 28) -#define DPC_HOTPLUG_INT_EN (1 << 28) -#define HDMID_HOTPLUG_INT_EN (1 << 27) -#define DPD_HOTPLUG_INT_EN (1 << 27) +#define PORT_HOTPLUG_EN (dev_priv->info->display_mmio_offset + 0x61110) +#define PORTB_HOTPLUG_INT_EN (1 << 29) +#define PORTC_HOTPLUG_INT_EN (1 << 28) +#define PORTD_HOTPLUG_INT_EN (1 << 27) #define SDVOB_HOTPLUG_INT_EN (1 << 26) #define SDVOC_HOTPLUG_INT_EN (1 << 25) #define TV_HOTPLUG_INT_EN (1 << 18) @@ -1658,21 +1653,14 @@ #define CRT_HOTPLUG_DETECT_VOLTAGE_325MV (0 << 2) #define CRT_HOTPLUG_DETECT_VOLTAGE_475MV (1 << 2) -#define PORT_HOTPLUG_STAT 0x61114 +#define PORT_HOTPLUG_STAT (dev_priv->info->display_mmio_offset + 0x61114) /* HDMI/DP bits are gen4+ */ -#define DPB_HOTPLUG_LIVE_STATUS (1 << 29) -#define DPC_HOTPLUG_LIVE_STATUS (1 << 28) -#define DPD_HOTPLUG_LIVE_STATUS (1 << 27) -#define DPD_HOTPLUG_INT_STATUS (3 << 21) -#define DPC_HOTPLUG_INT_STATUS (3 << 19) -#define DPB_HOTPLUG_INT_STATUS (3 << 17) -/* HDMI bits are shared with the DP bits */ -#define HDMIB_HOTPLUG_LIVE_STATUS (1 << 29) -#define HDMIC_HOTPLUG_LIVE_STATUS (1 << 28) -#define HDMID_HOTPLUG_LIVE_STATUS (1 << 27) -#define HDMID_HOTPLUG_INT_STATUS (3 << 21) -#define HDMIC_HOTPLUG_INT_STATUS (3 << 19) -#define HDMIB_HOTPLUG_INT_STATUS (3 << 17) +#define PORTB_HOTPLUG_LIVE_STATUS (1 << 29) +#define PORTC_HOTPLUG_LIVE_STATUS (1 << 28) +#define PORTD_HOTPLUG_LIVE_STATUS (1 << 27) +#define PORTD_HOTPLUG_INT_STATUS (3 << 21) +#define PORTC_HOTPLUG_INT_STATUS (3 << 19) +#define PORTB_HOTPLUG_INT_STATUS (3 << 17) /* CRT/TV common between gen3+ */ #define CRT_HOTPLUG_INT_STATUS (1 << 11) #define TV_HOTPLUG_INT_STATUS (1 << 10) @@ -1877,7 +1865,7 @@ #define PP_DIVISOR 0x61210 /* Panel fitting */ -#define PFIT_CONTROL 0x61230 +#define PFIT_CONTROL (dev_priv->info->display_mmio_offset + 0x61230) #define PFIT_ENABLE (1 << 31) #define PFIT_PIPE_MASK (3 << 29) #define PFIT_PIPE_SHIFT 29 @@ -1895,9 +1883,7 @@ #define PFIT_SCALING_PROGRAMMED (1 << 26) #define PFIT_SCALING_PILLAR (2 << 26) #define PFIT_SCALING_LETTER (3 << 26) -#define PFIT_PGM_RATIOS 0x61234 -#define PFIT_VERT_SCALE_MASK 0xfff00000 -#define PFIT_HORIZ_SCALE_MASK 0x0000fff0 +#define PFIT_PGM_RATIOS (dev_priv->info->display_mmio_offset + 0x61234) /* Pre-965 */ #define PFIT_VERT_SCALE_SHIFT 20 #define PFIT_VERT_SCALE_MASK 0xfff00000 @@ -1909,7 +1895,7 @@ #define PFIT_HORIZ_SCALE_SHIFT_965 0 #define PFIT_HORIZ_SCALE_MASK_965 0x00001fff -#define PFIT_AUTO_RATIOS 0x61238 +#define PFIT_AUTO_RATIOS (dev_priv->info->display_mmio_offset + 0x61238) /* Backlight control */ #define BLC_PWM_CTL2 0x61250 /* 965+ only */ @@ -2639,10 +2625,10 @@ /* Display & cursor control */ /* Pipe A */ -#define _PIPEADSL 0x70000 +#define _PIPEADSL (dev_priv->info->display_mmio_offset + 0x70000) #define DSL_LINEMASK_GEN2 0x00000fff #define DSL_LINEMASK_GEN3 0x00001fff -#define _PIPEACONF 0x70008 +#define _PIPEACONF (dev_priv->info->display_mmio_offset + 0x70008) #define PIPECONF_ENABLE (1<<31) #define PIPECONF_DISABLE 0 #define PIPECONF_DOUBLE_WIDE (1<<30) @@ -2671,18 +2657,19 @@ #define PIPECONF_INTERLACED_DBL_ILK (4 << 21) /* ilk/snb only */ #define PIPECONF_PFIT_PF_INTERLACED_DBL_ILK (5 << 21) /* ilk/snb only */ #define PIPECONF_CXSR_DOWNCLOCK (1<<16) -#define PIPECONF_BPP_MASK (0x000000e0) -#define PIPECONF_BPP_8 (0<<5) -#define PIPECONF_BPP_10 (1<<5) -#define PIPECONF_BPP_6 (2<<5) -#define PIPECONF_BPP_12 (3<<5) +#define PIPECONF_COLOR_RANGE_SELECT (1 << 13) +#define PIPECONF_BPC_MASK (0x7 << 5) +#define PIPECONF_8BPC (0<<5) +#define PIPECONF_10BPC (1<<5) +#define PIPECONF_6BPC (2<<5) +#define PIPECONF_12BPC (3<<5) #define PIPECONF_DITHER_EN (1<<4) #define PIPECONF_DITHER_TYPE_MASK (0x0000000c) #define PIPECONF_DITHER_TYPE_SP (0<<2) #define PIPECONF_DITHER_TYPE_ST1 (1<<2) #define PIPECONF_DITHER_TYPE_ST2 (2<<2) #define PIPECONF_DITHER_TYPE_TEMP (3<<2) -#define _PIPEASTAT 0x70024 +#define _PIPEASTAT (dev_priv->info->display_mmio_offset + 0x70024) #define PIPE_FIFO_UNDERRUN_STATUS (1UL<<31) #define SPRITE1_FLIPDONE_INT_EN_VLV (1UL<<30) #define PIPE_CRC_ERROR_ENABLE (1UL<<29) @@ -2693,7 +2680,7 @@ #define PIPE_VSYNC_INTERRUPT_ENABLE (1UL<<25) #define PIPE_DISPLAY_LINE_COMPARE_ENABLE (1UL<<24) #define PIPE_DPST_EVENT_ENABLE (1UL<<23) -#define SPRITE0_FLIP_DONE_INT_EN_VLV (1UL<<26) +#define SPRITE0_FLIP_DONE_INT_EN_VLV (1UL<<22) #define PIPE_LEGACY_BLC_EVENT_ENABLE (1UL<<22) #define PIPE_ODD_FIELD_INTERRUPT_ENABLE (1UL<<21) #define PIPE_EVEN_FIELD_INTERRUPT_ENABLE (1UL<<20) @@ -2703,7 +2690,7 @@ #define PIPEA_HBLANK_INT_EN_VLV (1UL<<16) #define PIPE_OVERLAY_UPDATED_ENABLE (1UL<<16) #define SPRITE1_FLIPDONE_INT_STATUS_VLV (1UL<<15) -#define SPRITE0_FLIPDONE_INT_STATUS_VLV (1UL<<15) +#define SPRITE0_FLIPDONE_INT_STATUS_VLV (1UL<<14) #define PIPE_CRC_ERROR_INTERRUPT_STATUS (1UL<<13) #define PIPE_CRC_DONE_INTERRUPT_STATUS (1UL<<12) #define PIPE_GMBUS_INTERRUPT_STATUS (1UL<<11) @@ -2719,11 +2706,6 @@ #define PIPE_START_VBLANK_INTERRUPT_STATUS (1UL<<2) /* 965 or later */ #define PIPE_VBLANK_INTERRUPT_STATUS (1UL<<1) #define PIPE_OVERLAY_UPDATED_STATUS (1UL<<0) -#define PIPE_BPC_MASK (7 << 5) /* Ironlake */ -#define PIPE_8BPC (0 << 5) -#define PIPE_10BPC (1 << 5) -#define PIPE_6BPC (2 << 5) -#define PIPE_12BPC (3 << 5) #define PIPESRC(pipe) _PIPE(pipe, _PIPEASRC, _PIPEBSRC) #define PIPECONF(tran) _TRANSCODER(tran, _PIPEACONF, _PIPEBCONF) @@ -2732,7 +2714,7 @@ #define PIPEFRAMEPIXEL(pipe) _PIPE(pipe, _PIPEAFRAMEPIXEL, _PIPEBFRAMEPIXEL) #define PIPESTAT(pipe) _PIPE(pipe, _PIPEASTAT, _PIPEBSTAT) -#define VLV_DPFLIPSTAT 0x70028 +#define VLV_DPFLIPSTAT (VLV_DISPLAY_BASE + 0x70028) #define PIPEB_LINE_COMPARE_INT_EN (1<<29) #define PIPEB_HLINE_INT_EN (1<<28) #define PIPEB_VBLANK_INT_EN (1<<27) @@ -2746,7 +2728,7 @@ #define SPRITEA_FLIPDONE_INT_EN (1<<17) #define PLANEA_FLIPDONE_INT_EN (1<<16) -#define DPINVGTT 0x7002c /* VLV only */ +#define DPINVGTT (VLV_DISPLAY_BASE + 0x7002c) /* VLV only */ #define CURSORB_INVALID_GTT_INT_EN (1<<23) #define CURSORA_INVALID_GTT_INT_EN (1<<22) #define SPRITED_INVALID_GTT_INT_EN (1<<21) @@ -2774,7 +2756,7 @@ #define DSPARB_BEND_SHIFT 9 /* on 855 */ #define DSPARB_AEND_SHIFT 0 -#define DSPFW1 0x70034 +#define DSPFW1 (dev_priv->info->display_mmio_offset + 0x70034) #define DSPFW_SR_SHIFT 23 #define DSPFW_SR_MASK (0x1ff<<23) #define DSPFW_CURSORB_SHIFT 16 @@ -2782,11 +2764,11 @@ #define DSPFW_PLANEB_SHIFT 8 #define DSPFW_PLANEB_MASK (0x7f<<8) #define DSPFW_PLANEA_MASK (0x7f) -#define DSPFW2 0x70038 +#define DSPFW2 (dev_priv->info->display_mmio_offset + 0x70038) #define DSPFW_CURSORA_MASK 0x00003f00 #define DSPFW_CURSORA_SHIFT 8 #define DSPFW_PLANEC_MASK (0x7f) -#define DSPFW3 0x7003c +#define DSPFW3 (dev_priv->info->display_mmio_offset + 0x7003c) #define DSPFW_HPLL_SR_EN (1<<31) #define DSPFW_CURSOR_SR_SHIFT 24 #define PINEVIEW_SELF_REFRESH_EN (1<<30) @@ -2798,13 +2780,13 @@ /* drain latency register values*/ #define DRAIN_LATENCY_PRECISION_32 32 #define DRAIN_LATENCY_PRECISION_16 16 -#define VLV_DDL1 0x70050 +#define VLV_DDL1 (VLV_DISPLAY_BASE + 0x70050) #define DDL_CURSORA_PRECISION_32 (1<<31) #define DDL_CURSORA_PRECISION_16 (0<<31) #define DDL_CURSORA_SHIFT 24 #define DDL_PLANEA_PRECISION_32 (1<<7) #define DDL_PLANEA_PRECISION_16 (0<<7) -#define VLV_DDL2 0x70054 +#define VLV_DDL2 (VLV_DISPLAY_BASE + 0x70054) #define DDL_CURSORB_PRECISION_32 (1<<31) #define DDL_CURSORB_PRECISION_16 (0<<31) #define DDL_CURSORB_SHIFT 24 @@ -2948,10 +2930,10 @@ * } while (high1 != high2); * frame = (high1 << 8) | low1; */ -#define _PIPEAFRAMEHIGH 0x70040 +#define _PIPEAFRAMEHIGH (dev_priv->info->display_mmio_offset + 0x70040) #define PIPE_FRAME_HIGH_MASK 0x0000ffff #define PIPE_FRAME_HIGH_SHIFT 0 -#define _PIPEAFRAMEPIXEL 0x70044 +#define _PIPEAFRAMEPIXEL (dev_priv->info->display_mmio_offset + 0x70044) #define PIPE_FRAME_LOW_MASK 0xff000000 #define PIPE_FRAME_LOW_SHIFT 24 #define PIPE_PIXEL_MASK 0x00ffffff @@ -2962,11 +2944,12 @@ #define PIPE_FRMCOUNT_GM45(pipe) _PIPE(pipe, _PIPEA_FRMCOUNT_GM45, _PIPEB_FRMCOUNT_GM45) /* Cursor A & B regs */ -#define _CURACNTR 0x70080 +#define _CURACNTR (dev_priv->info->display_mmio_offset + 0x70080) /* Old style CUR*CNTR flags (desktop 8xx) */ #define CURSOR_ENABLE 0x80000000 #define CURSOR_GAMMA_ENABLE 0x40000000 #define CURSOR_STRIDE_MASK 0x30000000 +#define CURSOR_PIPE_CSC_ENABLE (1<<24) #define CURSOR_FORMAT_SHIFT 24 #define CURSOR_FORMAT_MASK (0x07 << CURSOR_FORMAT_SHIFT) #define CURSOR_FORMAT_2C (0x00 << CURSOR_FORMAT_SHIFT) @@ -2983,16 +2966,16 @@ #define MCURSOR_PIPE_A 0x00 #define MCURSOR_PIPE_B (1 << 28) #define MCURSOR_GAMMA_ENABLE (1 << 26) -#define _CURABASE 0x70084 -#define _CURAPOS 0x70088 +#define _CURABASE (dev_priv->info->display_mmio_offset + 0x70084) +#define _CURAPOS (dev_priv->info->display_mmio_offset + 0x70088) #define CURSOR_POS_MASK 0x007FF #define CURSOR_POS_SIGN 0x8000 #define CURSOR_X_SHIFT 0 #define CURSOR_Y_SHIFT 16 #define CURSIZE 0x700a0 -#define _CURBCNTR 0x700c0 -#define _CURBBASE 0x700c4 -#define _CURBPOS 0x700c8 +#define _CURBCNTR (dev_priv->info->display_mmio_offset + 0x700c0) +#define _CURBBASE (dev_priv->info->display_mmio_offset + 0x700c4) +#define _CURBPOS (dev_priv->info->display_mmio_offset + 0x700c8) #define _CURBCNTR_IVB 0x71080 #define _CURBBASE_IVB 0x71084 @@ -3007,7 +2990,7 @@ #define CURPOS_IVB(pipe) _PIPE(pipe, _CURAPOS, _CURBPOS_IVB) /* Display A control */ -#define _DSPACNTR 0x70180 +#define _DSPACNTR (dev_priv->info->display_mmio_offset + 0x70180) #define DISPLAY_PLANE_ENABLE (1<<31) #define DISPLAY_PLANE_DISABLE 0 #define DISPPLANE_GAMMA_ENABLE (1<<30) @@ -3028,6 +3011,7 @@ #define DISPPLANE_RGBA888 (0xf<<26) #define DISPPLANE_STEREO_ENABLE (1<<25) #define DISPPLANE_STEREO_DISABLE 0 +#define DISPPLANE_PIPE_CSC_ENABLE (1<<24) #define DISPPLANE_SEL_PIPE_SHIFT 24 #define DISPPLANE_SEL_PIPE_MASK (3<<DISPPLANE_SEL_PIPE_SHIFT) #define DISPPLANE_SEL_PIPE_A 0 @@ -3040,14 +3024,14 @@ #define DISPPLANE_STEREO_POLARITY_SECOND (1<<18) #define DISPPLANE_TRICKLE_FEED_DISABLE (1<<14) /* Ironlake */ #define DISPPLANE_TILED (1<<10) -#define _DSPAADDR 0x70184 -#define _DSPASTRIDE 0x70188 -#define _DSPAPOS 0x7018C /* reserved */ -#define _DSPASIZE 0x70190 -#define _DSPASURF 0x7019C /* 965+ only */ -#define _DSPATILEOFF 0x701A4 /* 965+ only */ -#define _DSPAOFFSET 0x701A4 /* HSW */ -#define _DSPASURFLIVE 0x701AC +#define _DSPAADDR (dev_priv->info->display_mmio_offset + 0x70184) +#define _DSPASTRIDE (dev_priv->info->display_mmio_offset + 0x70188) +#define _DSPAPOS (dev_priv->info->display_mmio_offset + 0x7018C) /* reserved */ +#define _DSPASIZE (dev_priv->info->display_mmio_offset + 0x70190) +#define _DSPASURF (dev_priv->info->display_mmio_offset + 0x7019C) /* 965+ only */ +#define _DSPATILEOFF (dev_priv->info->display_mmio_offset + 0x701A4) /* 965+ only */ +#define _DSPAOFFSET (dev_priv->info->display_mmio_offset + 0x701A4) /* HSW */ +#define _DSPASURFLIVE (dev_priv->info->display_mmio_offset + 0x701AC) #define DSPCNTR(plane) _PIPE(plane, _DSPACNTR, _DSPBCNTR) #define DSPADDR(plane) _PIPE(plane, _DSPAADDR, _DSPBADDR) @@ -3068,44 +3052,44 @@ (I915_WRITE((reg), (gfx_addr) | I915_LO_DISPBASE(I915_READ(reg)))) /* VBIOS flags */ -#define SWF00 0x71410 -#define SWF01 0x71414 -#define SWF02 0x71418 -#define SWF03 0x7141c -#define SWF04 0x71420 -#define SWF05 0x71424 -#define SWF06 0x71428 -#define SWF10 0x70410 -#define SWF11 0x70414 -#define SWF14 0x71420 -#define SWF30 0x72414 -#define SWF31 0x72418 -#define SWF32 0x7241c +#define SWF00 (dev_priv->info->display_mmio_offset + 0x71410) +#define SWF01 (dev_priv->info->display_mmio_offset + 0x71414) +#define SWF02 (dev_priv->info->display_mmio_offset + 0x71418) +#define SWF03 (dev_priv->info->display_mmio_offset + 0x7141c) +#define SWF04 (dev_priv->info->display_mmio_offset + 0x71420) +#define SWF05 (dev_priv->info->display_mmio_offset + 0x71424) +#define SWF06 (dev_priv->info->display_mmio_offset + 0x71428) +#define SWF10 (dev_priv->info->display_mmio_offset + 0x70410) +#define SWF11 (dev_priv->info->display_mmio_offset + 0x70414) +#define SWF14 (dev_priv->info->display_mmio_offset + 0x71420) +#define SWF30 (dev_priv->info->display_mmio_offset + 0x72414) +#define SWF31 (dev_priv->info->display_mmio_offset + 0x72418) +#define SWF32 (dev_priv->info->display_mmio_offset + 0x7241c) /* Pipe B */ -#define _PIPEBDSL 0x71000 -#define _PIPEBCONF 0x71008 -#define _PIPEBSTAT 0x71024 -#define _PIPEBFRAMEHIGH 0x71040 -#define _PIPEBFRAMEPIXEL 0x71044 +#define _PIPEBDSL (dev_priv->info->display_mmio_offset + 0x71000) +#define _PIPEBCONF (dev_priv->info->display_mmio_offset + 0x71008) +#define _PIPEBSTAT (dev_priv->info->display_mmio_offset + 0x71024) +#define _PIPEBFRAMEHIGH (dev_priv->info->display_mmio_offset + 0x71040) +#define _PIPEBFRAMEPIXEL (dev_priv->info->display_mmio_offset + 0x71044) #define _PIPEB_FRMCOUNT_GM45 0x71040 #define _PIPEB_FLIPCOUNT_GM45 0x71044 /* Display B control */ -#define _DSPBCNTR 0x71180 +#define _DSPBCNTR (dev_priv->info->display_mmio_offset + 0x71180) #define DISPPLANE_ALPHA_TRANS_ENABLE (1<<15) #define DISPPLANE_ALPHA_TRANS_DISABLE 0 #define DISPPLANE_SPRITE_ABOVE_DISPLAY 0 #define DISPPLANE_SPRITE_ABOVE_OVERLAY (1) -#define _DSPBADDR 0x71184 -#define _DSPBSTRIDE 0x71188 -#define _DSPBPOS 0x7118C -#define _DSPBSIZE 0x71190 -#define _DSPBSURF 0x7119C -#define _DSPBTILEOFF 0x711A4 -#define _DSPBOFFSET 0x711A4 -#define _DSPBSURFLIVE 0x711AC +#define _DSPBADDR (dev_priv->info->display_mmio_offset + 0x71184) +#define _DSPBSTRIDE (dev_priv->info->display_mmio_offset + 0x71188) +#define _DSPBPOS (dev_priv->info->display_mmio_offset + 0x7118C) +#define _DSPBSIZE (dev_priv->info->display_mmio_offset + 0x71190) +#define _DSPBSURF (dev_priv->info->display_mmio_offset + 0x7119C) +#define _DSPBTILEOFF (dev_priv->info->display_mmio_offset + 0x711A4) +#define _DSPBOFFSET (dev_priv->info->display_mmio_offset + 0x711A4) +#define _DSPBSURFLIVE (dev_priv->info->display_mmio_offset + 0x711AC) /* Sprite A control */ #define _DVSACNTR 0x72180 @@ -3116,6 +3100,7 @@ #define DVS_FORMAT_RGBX101010 (1<<25) #define DVS_FORMAT_RGBX888 (2<<25) #define DVS_FORMAT_RGBX161616 (3<<25) +#define DVS_PIPE_CSC_ENABLE (1<<24) #define DVS_SOURCE_KEY (1<<22) #define DVS_RGB_ORDER_XBGR (1<<20) #define DVS_YUV_BYTE_ORDER_MASK (3<<16) @@ -3183,7 +3168,7 @@ #define SPRITE_FORMAT_RGBX161616 (3<<25) #define SPRITE_FORMAT_YUV444 (4<<25) #define SPRITE_FORMAT_XR_BGR101010 (5<<25) /* Extended range */ -#define SPRITE_CSC_ENABLE (1<<24) +#define SPRITE_PIPE_CSC_ENABLE (1<<24) #define SPRITE_SOURCE_KEY (1<<22) #define SPRITE_RGB_ORDER_RGBX (1<<20) /* only for 888 and 161616 */ #define SPRITE_YUV_TO_RGB_CSC_DISABLE (1<<19) @@ -3254,6 +3239,8 @@ # define VGA_2X_MODE (1 << 30) # define VGA_PIPE_B_SELECT (1 << 29) +#define VLV_VGACNTRL (VLV_DISPLAY_BASE + 0x71400) + /* Ironlake */ #define CPU_VGACNTRL 0x41000 @@ -3294,41 +3281,41 @@ #define FDI_PLL_FREQ_DISABLE_COUNT_LIMIT_MASK 0xff -#define _PIPEA_DATA_M1 0x60030 +#define _PIPEA_DATA_M1 (dev_priv->info->display_mmio_offset + 0x60030) #define TU_SIZE(x) (((x)-1) << 25) /* default size 64 */ #define TU_SIZE_MASK 0x7e000000 #define PIPE_DATA_M1_OFFSET 0 -#define _PIPEA_DATA_N1 0x60034 +#define _PIPEA_DATA_N1 (dev_priv->info->display_mmio_offset + 0x60034) #define PIPE_DATA_N1_OFFSET 0 -#define _PIPEA_DATA_M2 0x60038 +#define _PIPEA_DATA_M2 (dev_priv->info->display_mmio_offset + 0x60038) #define PIPE_DATA_M2_OFFSET 0 -#define _PIPEA_DATA_N2 0x6003c +#define _PIPEA_DATA_N2 (dev_priv->info->display_mmio_offset + 0x6003c) #define PIPE_DATA_N2_OFFSET 0 -#define _PIPEA_LINK_M1 0x60040 +#define _PIPEA_LINK_M1 (dev_priv->info->display_mmio_offset + 0x60040) #define PIPE_LINK_M1_OFFSET 0 -#define _PIPEA_LINK_N1 0x60044 +#define _PIPEA_LINK_N1 (dev_priv->info->display_mmio_offset + 0x60044) #define PIPE_LINK_N1_OFFSET 0 -#define _PIPEA_LINK_M2 0x60048 +#define _PIPEA_LINK_M2 (dev_priv->info->display_mmio_offset + 0x60048) #define PIPE_LINK_M2_OFFSET 0 -#define _PIPEA_LINK_N2 0x6004c +#define _PIPEA_LINK_N2 (dev_priv->info->display_mmio_offset + 0x6004c) #define PIPE_LINK_N2_OFFSET 0 /* PIPEB timing regs are same start from 0x61000 */ -#define _PIPEB_DATA_M1 0x61030 -#define _PIPEB_DATA_N1 0x61034 +#define _PIPEB_DATA_M1 (dev_priv->info->display_mmio_offset + 0x61030) +#define _PIPEB_DATA_N1 (dev_priv->info->display_mmio_offset + 0x61034) -#define _PIPEB_DATA_M2 0x61038 -#define _PIPEB_DATA_N2 0x6103c +#define _PIPEB_DATA_M2 (dev_priv->info->display_mmio_offset + 0x61038) +#define _PIPEB_DATA_N2 (dev_priv->info->display_mmio_offset + 0x6103c) -#define _PIPEB_LINK_M1 0x61040 -#define _PIPEB_LINK_N1 0x61044 +#define _PIPEB_LINK_M1 (dev_priv->info->display_mmio_offset + 0x61040) +#define _PIPEB_LINK_N1 (dev_priv->info->display_mmio_offset + 0x61044) -#define _PIPEB_LINK_M2 0x61048 -#define _PIPEB_LINK_N2 0x6104c +#define _PIPEB_LINK_M2 (dev_priv->info->display_mmio_offset + 0x61048) +#define _PIPEB_LINK_N2 (dev_priv->info->display_mmio_offset + 0x6104c) #define PIPE_DATA_M1(tran) _TRANSCODER(tran, _PIPEA_DATA_M1, _PIPEB_DATA_M1) #define PIPE_DATA_N1(tran) _TRANSCODER(tran, _PIPEA_DATA_N1, _PIPEB_DATA_N1) @@ -3581,27 +3568,30 @@ #define PORTD_PULSE_DURATION_6ms (2 << 18) #define PORTD_PULSE_DURATION_100ms (3 << 18) #define PORTD_PULSE_DURATION_MASK (3 << 18) -#define PORTD_HOTPLUG_NO_DETECT (0) -#define PORTD_HOTPLUG_SHORT_DETECT (1 << 16) -#define PORTD_HOTPLUG_LONG_DETECT (1 << 17) +#define PORTD_HOTPLUG_STATUS_MASK (0x3 << 16) +#define PORTD_HOTPLUG_NO_DETECT (0 << 16) +#define PORTD_HOTPLUG_SHORT_DETECT (1 << 16) +#define PORTD_HOTPLUG_LONG_DETECT (2 << 16) #define PORTC_HOTPLUG_ENABLE (1 << 12) #define PORTC_PULSE_DURATION_2ms (0) #define PORTC_PULSE_DURATION_4_5ms (1 << 10) #define PORTC_PULSE_DURATION_6ms (2 << 10) #define PORTC_PULSE_DURATION_100ms (3 << 10) #define PORTC_PULSE_DURATION_MASK (3 << 10) -#define PORTC_HOTPLUG_NO_DETECT (0) -#define PORTC_HOTPLUG_SHORT_DETECT (1 << 8) -#define PORTC_HOTPLUG_LONG_DETECT (1 << 9) +#define PORTC_HOTPLUG_STATUS_MASK (0x3 << 8) +#define PORTC_HOTPLUG_NO_DETECT (0 << 8) +#define PORTC_HOTPLUG_SHORT_DETECT (1 << 8) +#define PORTC_HOTPLUG_LONG_DETECT (2 << 8) #define PORTB_HOTPLUG_ENABLE (1 << 4) #define PORTB_PULSE_DURATION_2ms (0) #define PORTB_PULSE_DURATION_4_5ms (1 << 2) #define PORTB_PULSE_DURATION_6ms (2 << 2) #define PORTB_PULSE_DURATION_100ms (3 << 2) #define PORTB_PULSE_DURATION_MASK (3 << 2) -#define PORTB_HOTPLUG_NO_DETECT (0) -#define PORTB_HOTPLUG_SHORT_DETECT (1 << 0) -#define PORTB_HOTPLUG_LONG_DETECT (1 << 1) +#define PORTB_HOTPLUG_STATUS_MASK (0x3 << 0) +#define PORTB_HOTPLUG_NO_DETECT (0 << 0) +#define PORTB_HOTPLUG_SHORT_DETECT (1 << 0) +#define PORTB_HOTPLUG_LONG_DETECT (2 << 0) #define PCH_GPIOA 0xc5010 #define PCH_GPIOB 0xc5014 @@ -3722,13 +3712,13 @@ #define TVIDEO_DIP_DATA(pipe) _PIPE(pipe, _VIDEO_DIP_DATA_A, _VIDEO_DIP_DATA_B) #define TVIDEO_DIP_GCP(pipe) _PIPE(pipe, _VIDEO_DIP_GCP_A, _VIDEO_DIP_GCP_B) -#define VLV_VIDEO_DIP_CTL_A 0x60200 -#define VLV_VIDEO_DIP_DATA_A 0x60208 -#define VLV_VIDEO_DIP_GDCP_PAYLOAD_A 0x60210 +#define VLV_VIDEO_DIP_CTL_A (VLV_DISPLAY_BASE + 0x60200) +#define VLV_VIDEO_DIP_DATA_A (VLV_DISPLAY_BASE + 0x60208) +#define VLV_VIDEO_DIP_GDCP_PAYLOAD_A (VLV_DISPLAY_BASE + 0x60210) -#define VLV_VIDEO_DIP_CTL_B 0x61170 -#define VLV_VIDEO_DIP_DATA_B 0x61174 -#define VLV_VIDEO_DIP_GDCP_PAYLOAD_B 0x61178 +#define VLV_VIDEO_DIP_CTL_B (VLV_DISPLAY_BASE + 0x61170) +#define VLV_VIDEO_DIP_DATA_B (VLV_DISPLAY_BASE + 0x61174) +#define VLV_VIDEO_DIP_GDCP_PAYLOAD_B (VLV_DISPLAY_BASE + 0x61178) #define VLV_TVIDEO_DIP_CTL(pipe) \ _PIPE(pipe, VLV_VIDEO_DIP_CTL_A, VLV_VIDEO_DIP_CTL_B) @@ -3820,8 +3810,6 @@ #define TRANS_FSYNC_DELAY_HB2 (1<<27) #define TRANS_FSYNC_DELAY_HB3 (2<<27) #define TRANS_FSYNC_DELAY_HB4 (3<<27) -#define TRANS_DP_AUDIO_ONLY (1<<26) -#define TRANS_DP_VIDEO_AUDIO (0<<26) #define TRANS_INTERLACE_MASK (7<<21) #define TRANS_PROGRESSIVE (0<<21) #define TRANS_INTERLACED (3<<21) @@ -3927,7 +3915,7 @@ #define FDI_10BPC (1<<16) #define FDI_6BPC (2<<16) #define FDI_12BPC (3<<16) -#define FDI_LINK_REVERSE_OVERWRITE (1<<15) +#define FDI_RX_LINK_REVERSAL_OVERRIDE (1<<15) #define FDI_DMI_LINK_REVERSE_MASK (1<<14) #define FDI_RX_PLL_ENABLE (1<<13) #define FDI_FS_ERR_CORRECT_ENABLE (1<<11) @@ -4020,17 +4008,17 @@ #define LVDS_DETECTED (1 << 1) /* vlv has 2 sets of panel control regs. */ -#define PIPEA_PP_STATUS 0x61200 -#define PIPEA_PP_CONTROL 0x61204 -#define PIPEA_PP_ON_DELAYS 0x61208 -#define PIPEA_PP_OFF_DELAYS 0x6120c -#define PIPEA_PP_DIVISOR 0x61210 - -#define PIPEB_PP_STATUS 0x61300 -#define PIPEB_PP_CONTROL 0x61304 -#define PIPEB_PP_ON_DELAYS 0x61308 -#define PIPEB_PP_OFF_DELAYS 0x6130c -#define PIPEB_PP_DIVISOR 0x61310 +#define PIPEA_PP_STATUS (VLV_DISPLAY_BASE + 0x61200) +#define PIPEA_PP_CONTROL (VLV_DISPLAY_BASE + 0x61204) +#define PIPEA_PP_ON_DELAYS (VLV_DISPLAY_BASE + 0x61208) +#define PIPEA_PP_OFF_DELAYS (VLV_DISPLAY_BASE + 0x6120c) +#define PIPEA_PP_DIVISOR (VLV_DISPLAY_BASE + 0x61210) + +#define PIPEB_PP_STATUS (VLV_DISPLAY_BASE + 0x61300) +#define PIPEB_PP_CONTROL (VLV_DISPLAY_BASE + 0x61304) +#define PIPEB_PP_ON_DELAYS (VLV_DISPLAY_BASE + 0x61308) +#define PIPEB_PP_OFF_DELAYS (VLV_DISPLAY_BASE + 0x6130c) +#define PIPEB_PP_DIVISOR (VLV_DISPLAY_BASE + 0x61310) #define PCH_PP_STATUS 0xc7200 #define PCH_PP_CONTROL 0xc7204 @@ -4211,7 +4199,9 @@ #define GEN6_RP_INTERRUPT_LIMITS 0xA014 #define GEN6_RPSTAT1 0xA01C #define GEN6_CAGF_SHIFT 8 +#define HSW_CAGF_SHIFT 7 #define GEN6_CAGF_MASK (0x7f << GEN6_CAGF_SHIFT) +#define HSW_CAGF_MASK (0x7f << HSW_CAGF_SHIFT) #define GEN6_RP_CONTROL 0xA024 #define GEN6_RP_MEDIA_TURBO (1<<11) #define GEN6_RP_MEDIA_MODE_MASK (3<<9) @@ -4280,8 +4270,8 @@ #define GEN6_PCODE_READ_MIN_FREQ_TABLE 0x9 #define GEN6_PCODE_WRITE_RC6VIDS 0x4 #define GEN6_PCODE_READ_RC6VIDS 0x5 -#define GEN6_ENCODE_RC6_VID(mv) (((mv) / 5) - 245) < 0 ?: 0 -#define GEN6_DECODE_RC6_VID(vids) (((vids) * 5) > 0 ? ((vids) * 5) + 245 : 0) +#define GEN6_ENCODE_RC6_VID(mv) (((mv) - 245) / 5) +#define GEN6_DECODE_RC6_VID(vids) (((vids) * 5) + 245) #define GEN6_PCODE_DATA 0x138128 #define GEN6_PCODE_FREQ_IA_RATIO_SHIFT 8 @@ -4322,7 +4312,7 @@ #define GEN7_ROW_CHICKEN2_GT2 0xf4f4 #define DOP_CLOCK_GATING_DISABLE (1<<0) -#define G4X_AUD_VID_DID 0x62020 +#define G4X_AUD_VID_DID (dev_priv->info->display_mmio_offset + 0x62020) #define INTEL_AUDIO_DEVCL 0x808629FB #define INTEL_AUDIO_DEVBLC 0x80862801 #define INTEL_AUDIO_DEVCTG 0x80862802 @@ -4438,10 +4428,10 @@ #define AUDIO_CP_READY_C (1<<9) /* HSW Power Wells */ -#define HSW_PWR_WELL_CTL1 0x45400 /* BIOS */ -#define HSW_PWR_WELL_CTL2 0x45404 /* Driver */ -#define HSW_PWR_WELL_CTL3 0x45408 /* KVMR */ -#define HSW_PWR_WELL_CTL4 0x4540C /* Debug */ +#define HSW_PWR_WELL_BIOS 0x45400 /* CTL1 */ +#define HSW_PWR_WELL_DRIVER 0x45404 /* CTL2 */ +#define HSW_PWR_WELL_KVMR 0x45408 /* CTL3 */ +#define HSW_PWR_WELL_DEBUG 0x4540C /* CTL4 */ #define HSW_PWR_WELL_ENABLE (1<<31) #define HSW_PWR_WELL_STATE (1<<30) #define HSW_PWR_WELL_CTL5 0x45410 @@ -4524,6 +4514,7 @@ #define DDI_BUF_EMP_800MV_0DB_HSW (7<<24) /* Sel7 */ #define DDI_BUF_EMP_800MV_3_5DB_HSW (8<<24) /* Sel8 */ #define DDI_BUF_EMP_MASK (0xf<<24) +#define DDI_BUF_PORT_REVERSAL (1<<16) #define DDI_BUF_IS_IDLE (1<<7) #define DDI_A_4_LANES (1<<4) #define DDI_PORT_WIDTH_X1 (0<<1) @@ -4657,4 +4648,51 @@ #define WM_DBG_DISALLOW_MAXFIFO (1<<1) #define WM_DBG_DISALLOW_SPRITE (1<<2) +/* pipe CSC */ +#define _PIPE_A_CSC_COEFF_RY_GY 0x49010 +#define _PIPE_A_CSC_COEFF_BY 0x49014 +#define _PIPE_A_CSC_COEFF_RU_GU 0x49018 +#define _PIPE_A_CSC_COEFF_BU 0x4901c +#define _PIPE_A_CSC_COEFF_RV_GV 0x49020 +#define _PIPE_A_CSC_COEFF_BV 0x49024 +#define _PIPE_A_CSC_MODE 0x49028 +#define _PIPE_A_CSC_PREOFF_HI 0x49030 +#define _PIPE_A_CSC_PREOFF_ME 0x49034 +#define _PIPE_A_CSC_PREOFF_LO 0x49038 +#define _PIPE_A_CSC_POSTOFF_HI 0x49040 +#define _PIPE_A_CSC_POSTOFF_ME 0x49044 +#define _PIPE_A_CSC_POSTOFF_LO 0x49048 + +#define _PIPE_B_CSC_COEFF_RY_GY 0x49110 +#define _PIPE_B_CSC_COEFF_BY 0x49114 +#define _PIPE_B_CSC_COEFF_RU_GU 0x49118 +#define _PIPE_B_CSC_COEFF_BU 0x4911c +#define _PIPE_B_CSC_COEFF_RV_GV 0x49120 +#define _PIPE_B_CSC_COEFF_BV 0x49124 +#define _PIPE_B_CSC_MODE 0x49128 +#define _PIPE_B_CSC_PREOFF_HI 0x49130 +#define _PIPE_B_CSC_PREOFF_ME 0x49134 +#define _PIPE_B_CSC_PREOFF_LO 0x49138 +#define _PIPE_B_CSC_POSTOFF_HI 0x49140 +#define _PIPE_B_CSC_POSTOFF_ME 0x49144 +#define _PIPE_B_CSC_POSTOFF_LO 0x49148 + +#define CSC_BLACK_SCREEN_OFFSET (1 << 2) +#define CSC_POSITION_BEFORE_GAMMA (1 << 1) +#define CSC_MODE_YUV_TO_RGB (1 << 0) + +#define PIPE_CSC_COEFF_RY_GY(pipe) _PIPE(pipe, _PIPE_A_CSC_COEFF_RY_GY, _PIPE_B_CSC_COEFF_RY_GY) +#define PIPE_CSC_COEFF_BY(pipe) _PIPE(pipe, _PIPE_A_CSC_COEFF_BY, _PIPE_B_CSC_COEFF_BY) +#define PIPE_CSC_COEFF_RU_GU(pipe) _PIPE(pipe, _PIPE_A_CSC_COEFF_RU_GU, _PIPE_B_CSC_COEFF_RU_GU) +#define PIPE_CSC_COEFF_BU(pipe) _PIPE(pipe, _PIPE_A_CSC_COEFF_BU, _PIPE_B_CSC_COEFF_BU) +#define PIPE_CSC_COEFF_RV_GV(pipe) _PIPE(pipe, _PIPE_A_CSC_COEFF_RV_GV, _PIPE_B_CSC_COEFF_RV_GV) +#define PIPE_CSC_COEFF_BV(pipe) _PIPE(pipe, _PIPE_A_CSC_COEFF_BV, _PIPE_B_CSC_COEFF_BV) +#define PIPE_CSC_MODE(pipe) _PIPE(pipe, _PIPE_A_CSC_MODE, _PIPE_B_CSC_MODE) +#define PIPE_CSC_PREOFF_HI(pipe) _PIPE(pipe, _PIPE_A_CSC_PREOFF_HI, _PIPE_B_CSC_PREOFF_HI) +#define PIPE_CSC_PREOFF_ME(pipe) _PIPE(pipe, _PIPE_A_CSC_PREOFF_ME, _PIPE_B_CSC_PREOFF_ME) +#define PIPE_CSC_PREOFF_LO(pipe) _PIPE(pipe, _PIPE_A_CSC_PREOFF_LO, _PIPE_B_CSC_PREOFF_LO) +#define PIPE_CSC_POSTOFF_HI(pipe) _PIPE(pipe, _PIPE_A_CSC_POSTOFF_HI, _PIPE_B_CSC_POSTOFF_HI) +#define PIPE_CSC_POSTOFF_ME(pipe) _PIPE(pipe, _PIPE_A_CSC_POSTOFF_ME, _PIPE_B_CSC_POSTOFF_ME) +#define PIPE_CSC_POSTOFF_LO(pipe) _PIPE(pipe, _PIPE_A_CSC_POSTOFF_LO, _PIPE_B_CSC_POSTOFF_LO) + #endif /* _I915_REG_H_ */ diff --git a/drivers/gpu/drm/i915/i915_suspend.c b/drivers/gpu/drm/i915/i915_suspend.c index 63d4d30c39de..2135f21ea458 100644 --- a/drivers/gpu/drm/i915/i915_suspend.c +++ b/drivers/gpu/drm/i915/i915_suspend.c @@ -29,67 +29,6 @@ #include "intel_drv.h" #include "i915_reg.h" -static bool i915_pipe_enabled(struct drm_device *dev, enum pipe pipe) -{ - struct drm_i915_private *dev_priv = dev->dev_private; - u32 dpll_reg; - - /* On IVB, 3rd pipe shares PLL with another one */ - if (pipe > 1) - return false; - - if (HAS_PCH_SPLIT(dev)) - dpll_reg = _PCH_DPLL(pipe); - else - dpll_reg = (pipe == PIPE_A) ? _DPLL_A : _DPLL_B; - - return (I915_READ(dpll_reg) & DPLL_VCO_ENABLE); -} - -static void i915_save_palette(struct drm_device *dev, enum pipe pipe) -{ - struct drm_i915_private *dev_priv = dev->dev_private; - unsigned long reg = (pipe == PIPE_A ? _PALETTE_A : _PALETTE_B); - u32 *array; - int i; - - if (!i915_pipe_enabled(dev, pipe)) - return; - - if (HAS_PCH_SPLIT(dev)) - reg = (pipe == PIPE_A) ? _LGC_PALETTE_A : _LGC_PALETTE_B; - - if (pipe == PIPE_A) - array = dev_priv->regfile.save_palette_a; - else - array = dev_priv->regfile.save_palette_b; - - for (i = 0; i < 256; i++) - array[i] = I915_READ(reg + (i << 2)); -} - -static void i915_restore_palette(struct drm_device *dev, enum pipe pipe) -{ - struct drm_i915_private *dev_priv = dev->dev_private; - unsigned long reg = (pipe == PIPE_A ? _PALETTE_A : _PALETTE_B); - u32 *array; - int i; - - if (!i915_pipe_enabled(dev, pipe)) - return; - - if (HAS_PCH_SPLIT(dev)) - reg = (pipe == PIPE_A) ? _LGC_PALETTE_A : _LGC_PALETTE_B; - - if (pipe == PIPE_A) - array = dev_priv->regfile.save_palette_a; - else - array = dev_priv->regfile.save_palette_b; - - for (i = 0; i < 256; i++) - I915_WRITE(reg + (i << 2), array[i]); -} - static u8 i915_read_indexed(struct drm_device *dev, u16 index_port, u16 data_port, u8 reg) { struct drm_i915_private *dev_priv = dev->dev_private; @@ -130,6 +69,12 @@ static void i915_save_vga(struct drm_device *dev) int i; u16 cr_index, cr_data, st01; + /* VGA state */ + dev_priv->regfile.saveVGA0 = I915_READ(VGA0); + dev_priv->regfile.saveVGA1 = I915_READ(VGA1); + dev_priv->regfile.saveVGA_PD = I915_READ(VGA_PD); + dev_priv->regfile.saveVGACNTRL = I915_READ(i915_vgacntrl_reg(dev)); + /* VGA color palette registers */ dev_priv->regfile.saveDACMASK = I915_READ8(VGA_DACMASK); @@ -188,6 +133,15 @@ static void i915_restore_vga(struct drm_device *dev) int i; u16 cr_index, cr_data, st01; + /* VGA state */ + I915_WRITE(i915_vgacntrl_reg(dev), dev_priv->regfile.saveVGACNTRL); + + I915_WRITE(VGA0, dev_priv->regfile.saveVGA0); + I915_WRITE(VGA1, dev_priv->regfile.saveVGA1); + I915_WRITE(VGA_PD, dev_priv->regfile.saveVGA_PD); + POSTING_READ(VGA_PD); + udelay(150); + /* MSR bits */ I915_WRITE8(VGA_MSR_WRITE, dev_priv->regfile.saveMSR); if (dev_priv->regfile.saveMSR & VGA_MSR_CGA_MODE) { @@ -235,396 +189,18 @@ static void i915_restore_vga(struct drm_device *dev) I915_WRITE8(VGA_DACMASK, dev_priv->regfile.saveDACMASK); } -static void i915_save_modeset_reg(struct drm_device *dev) -{ - struct drm_i915_private *dev_priv = dev->dev_private; - int i; - - if (drm_core_check_feature(dev, DRIVER_MODESET)) - return; - - /* Cursor state */ - dev_priv->regfile.saveCURACNTR = I915_READ(_CURACNTR); - dev_priv->regfile.saveCURAPOS = I915_READ(_CURAPOS); - dev_priv->regfile.saveCURABASE = I915_READ(_CURABASE); - dev_priv->regfile.saveCURBCNTR = I915_READ(_CURBCNTR); - dev_priv->regfile.saveCURBPOS = I915_READ(_CURBPOS); - dev_priv->regfile.saveCURBBASE = I915_READ(_CURBBASE); - if (IS_GEN2(dev)) - dev_priv->regfile.saveCURSIZE = I915_READ(CURSIZE); - - if (HAS_PCH_SPLIT(dev)) { - dev_priv->regfile.savePCH_DREF_CONTROL = I915_READ(PCH_DREF_CONTROL); - dev_priv->regfile.saveDISP_ARB_CTL = I915_READ(DISP_ARB_CTL); - } - - /* Pipe & plane A info */ - dev_priv->regfile.savePIPEACONF = I915_READ(_PIPEACONF); - dev_priv->regfile.savePIPEASRC = I915_READ(_PIPEASRC); - if (HAS_PCH_SPLIT(dev)) { - dev_priv->regfile.saveFPA0 = I915_READ(_PCH_FPA0); - dev_priv->regfile.saveFPA1 = I915_READ(_PCH_FPA1); - dev_priv->regfile.saveDPLL_A = I915_READ(_PCH_DPLL_A); - } else { - dev_priv->regfile.saveFPA0 = I915_READ(_FPA0); - dev_priv->regfile.saveFPA1 = I915_READ(_FPA1); - dev_priv->regfile.saveDPLL_A = I915_READ(_DPLL_A); - } - if (INTEL_INFO(dev)->gen >= 4 && !HAS_PCH_SPLIT(dev)) - dev_priv->regfile.saveDPLL_A_MD = I915_READ(_DPLL_A_MD); - dev_priv->regfile.saveHTOTAL_A = I915_READ(_HTOTAL_A); - dev_priv->regfile.saveHBLANK_A = I915_READ(_HBLANK_A); - dev_priv->regfile.saveHSYNC_A = I915_READ(_HSYNC_A); - dev_priv->regfile.saveVTOTAL_A = I915_READ(_VTOTAL_A); - dev_priv->regfile.saveVBLANK_A = I915_READ(_VBLANK_A); - dev_priv->regfile.saveVSYNC_A = I915_READ(_VSYNC_A); - if (!HAS_PCH_SPLIT(dev)) - dev_priv->regfile.saveBCLRPAT_A = I915_READ(_BCLRPAT_A); - - if (HAS_PCH_SPLIT(dev)) { - dev_priv->regfile.savePIPEA_DATA_M1 = I915_READ(_PIPEA_DATA_M1); - dev_priv->regfile.savePIPEA_DATA_N1 = I915_READ(_PIPEA_DATA_N1); - dev_priv->regfile.savePIPEA_LINK_M1 = I915_READ(_PIPEA_LINK_M1); - dev_priv->regfile.savePIPEA_LINK_N1 = I915_READ(_PIPEA_LINK_N1); - - dev_priv->regfile.saveFDI_TXA_CTL = I915_READ(_FDI_TXA_CTL); - dev_priv->regfile.saveFDI_RXA_CTL = I915_READ(_FDI_RXA_CTL); - - dev_priv->regfile.savePFA_CTL_1 = I915_READ(_PFA_CTL_1); - dev_priv->regfile.savePFA_WIN_SZ = I915_READ(_PFA_WIN_SZ); - dev_priv->regfile.savePFA_WIN_POS = I915_READ(_PFA_WIN_POS); - - dev_priv->regfile.saveTRANSACONF = I915_READ(_TRANSACONF); - dev_priv->regfile.saveTRANS_HTOTAL_A = I915_READ(_TRANS_HTOTAL_A); - dev_priv->regfile.saveTRANS_HBLANK_A = I915_READ(_TRANS_HBLANK_A); - dev_priv->regfile.saveTRANS_HSYNC_A = I915_READ(_TRANS_HSYNC_A); - dev_priv->regfile.saveTRANS_VTOTAL_A = I915_READ(_TRANS_VTOTAL_A); - dev_priv->regfile.saveTRANS_VBLANK_A = I915_READ(_TRANS_VBLANK_A); - dev_priv->regfile.saveTRANS_VSYNC_A = I915_READ(_TRANS_VSYNC_A); - } - - dev_priv->regfile.saveDSPACNTR = I915_READ(_DSPACNTR); - dev_priv->regfile.saveDSPASTRIDE = I915_READ(_DSPASTRIDE); - dev_priv->regfile.saveDSPASIZE = I915_READ(_DSPASIZE); - dev_priv->regfile.saveDSPAPOS = I915_READ(_DSPAPOS); - dev_priv->regfile.saveDSPAADDR = I915_READ(_DSPAADDR); - if (INTEL_INFO(dev)->gen >= 4) { - dev_priv->regfile.saveDSPASURF = I915_READ(_DSPASURF); - dev_priv->regfile.saveDSPATILEOFF = I915_READ(_DSPATILEOFF); - } - i915_save_palette(dev, PIPE_A); - dev_priv->regfile.savePIPEASTAT = I915_READ(_PIPEASTAT); - - /* Pipe & plane B info */ - dev_priv->regfile.savePIPEBCONF = I915_READ(_PIPEBCONF); - dev_priv->regfile.savePIPEBSRC = I915_READ(_PIPEBSRC); - if (HAS_PCH_SPLIT(dev)) { - dev_priv->regfile.saveFPB0 = I915_READ(_PCH_FPB0); - dev_priv->regfile.saveFPB1 = I915_READ(_PCH_FPB1); - dev_priv->regfile.saveDPLL_B = I915_READ(_PCH_DPLL_B); - } else { - dev_priv->regfile.saveFPB0 = I915_READ(_FPB0); - dev_priv->regfile.saveFPB1 = I915_READ(_FPB1); - dev_priv->regfile.saveDPLL_B = I915_READ(_DPLL_B); - } - if (INTEL_INFO(dev)->gen >= 4 && !HAS_PCH_SPLIT(dev)) - dev_priv->regfile.saveDPLL_B_MD = I915_READ(_DPLL_B_MD); - dev_priv->regfile.saveHTOTAL_B = I915_READ(_HTOTAL_B); - dev_priv->regfile.saveHBLANK_B = I915_READ(_HBLANK_B); - dev_priv->regfile.saveHSYNC_B = I915_READ(_HSYNC_B); - dev_priv->regfile.saveVTOTAL_B = I915_READ(_VTOTAL_B); - dev_priv->regfile.saveVBLANK_B = I915_READ(_VBLANK_B); - dev_priv->regfile.saveVSYNC_B = I915_READ(_VSYNC_B); - if (!HAS_PCH_SPLIT(dev)) - dev_priv->regfile.saveBCLRPAT_B = I915_READ(_BCLRPAT_B); - - if (HAS_PCH_SPLIT(dev)) { - dev_priv->regfile.savePIPEB_DATA_M1 = I915_READ(_PIPEB_DATA_M1); - dev_priv->regfile.savePIPEB_DATA_N1 = I915_READ(_PIPEB_DATA_N1); - dev_priv->regfile.savePIPEB_LINK_M1 = I915_READ(_PIPEB_LINK_M1); - dev_priv->regfile.savePIPEB_LINK_N1 = I915_READ(_PIPEB_LINK_N1); - - dev_priv->regfile.saveFDI_TXB_CTL = I915_READ(_FDI_TXB_CTL); - dev_priv->regfile.saveFDI_RXB_CTL = I915_READ(_FDI_RXB_CTL); - - dev_priv->regfile.savePFB_CTL_1 = I915_READ(_PFB_CTL_1); - dev_priv->regfile.savePFB_WIN_SZ = I915_READ(_PFB_WIN_SZ); - dev_priv->regfile.savePFB_WIN_POS = I915_READ(_PFB_WIN_POS); - - dev_priv->regfile.saveTRANSBCONF = I915_READ(_TRANSBCONF); - dev_priv->regfile.saveTRANS_HTOTAL_B = I915_READ(_TRANS_HTOTAL_B); - dev_priv->regfile.saveTRANS_HBLANK_B = I915_READ(_TRANS_HBLANK_B); - dev_priv->regfile.saveTRANS_HSYNC_B = I915_READ(_TRANS_HSYNC_B); - dev_priv->regfile.saveTRANS_VTOTAL_B = I915_READ(_TRANS_VTOTAL_B); - dev_priv->regfile.saveTRANS_VBLANK_B = I915_READ(_TRANS_VBLANK_B); - dev_priv->regfile.saveTRANS_VSYNC_B = I915_READ(_TRANS_VSYNC_B); - } - - dev_priv->regfile.saveDSPBCNTR = I915_READ(_DSPBCNTR); - dev_priv->regfile.saveDSPBSTRIDE = I915_READ(_DSPBSTRIDE); - dev_priv->regfile.saveDSPBSIZE = I915_READ(_DSPBSIZE); - dev_priv->regfile.saveDSPBPOS = I915_READ(_DSPBPOS); - dev_priv->regfile.saveDSPBADDR = I915_READ(_DSPBADDR); - if (INTEL_INFO(dev)->gen >= 4) { - dev_priv->regfile.saveDSPBSURF = I915_READ(_DSPBSURF); - dev_priv->regfile.saveDSPBTILEOFF = I915_READ(_DSPBTILEOFF); - } - i915_save_palette(dev, PIPE_B); - dev_priv->regfile.savePIPEBSTAT = I915_READ(_PIPEBSTAT); - - /* Fences */ - switch (INTEL_INFO(dev)->gen) { - case 7: - case 6: - for (i = 0; i < 16; i++) - dev_priv->regfile.saveFENCE[i] = I915_READ64(FENCE_REG_SANDYBRIDGE_0 + (i * 8)); - break; - case 5: - case 4: - for (i = 0; i < 16; i++) - dev_priv->regfile.saveFENCE[i] = I915_READ64(FENCE_REG_965_0 + (i * 8)); - break; - case 3: - if (IS_I945G(dev) || IS_I945GM(dev) || IS_G33(dev)) - for (i = 0; i < 8; i++) - dev_priv->regfile.saveFENCE[i+8] = I915_READ(FENCE_REG_945_8 + (i * 4)); - case 2: - for (i = 0; i < 8; i++) - dev_priv->regfile.saveFENCE[i] = I915_READ(FENCE_REG_830_0 + (i * 4)); - break; - } - - /* CRT state */ - if (HAS_PCH_SPLIT(dev)) - dev_priv->regfile.saveADPA = I915_READ(PCH_ADPA); - else - dev_priv->regfile.saveADPA = I915_READ(ADPA); - - return; -} - -static void i915_restore_modeset_reg(struct drm_device *dev) -{ - struct drm_i915_private *dev_priv = dev->dev_private; - int dpll_a_reg, fpa0_reg, fpa1_reg; - int dpll_b_reg, fpb0_reg, fpb1_reg; - int i; - - if (drm_core_check_feature(dev, DRIVER_MODESET)) - return; - - /* Fences */ - switch (INTEL_INFO(dev)->gen) { - case 7: - case 6: - for (i = 0; i < 16; i++) - I915_WRITE64(FENCE_REG_SANDYBRIDGE_0 + (i * 8), dev_priv->regfile.saveFENCE[i]); - break; - case 5: - case 4: - for (i = 0; i < 16; i++) - I915_WRITE64(FENCE_REG_965_0 + (i * 8), dev_priv->regfile.saveFENCE[i]); - break; - case 3: - case 2: - if (IS_I945G(dev) || IS_I945GM(dev) || IS_G33(dev)) - for (i = 0; i < 8; i++) - I915_WRITE(FENCE_REG_945_8 + (i * 4), dev_priv->regfile.saveFENCE[i+8]); - for (i = 0; i < 8; i++) - I915_WRITE(FENCE_REG_830_0 + (i * 4), dev_priv->regfile.saveFENCE[i]); - break; - } - - - if (HAS_PCH_SPLIT(dev)) { - dpll_a_reg = _PCH_DPLL_A; - dpll_b_reg = _PCH_DPLL_B; - fpa0_reg = _PCH_FPA0; - fpb0_reg = _PCH_FPB0; - fpa1_reg = _PCH_FPA1; - fpb1_reg = _PCH_FPB1; - } else { - dpll_a_reg = _DPLL_A; - dpll_b_reg = _DPLL_B; - fpa0_reg = _FPA0; - fpb0_reg = _FPB0; - fpa1_reg = _FPA1; - fpb1_reg = _FPB1; - } - - if (HAS_PCH_SPLIT(dev)) { - I915_WRITE(PCH_DREF_CONTROL, dev_priv->regfile.savePCH_DREF_CONTROL); - I915_WRITE(DISP_ARB_CTL, dev_priv->regfile.saveDISP_ARB_CTL); - } - - /* Pipe & plane A info */ - /* Prime the clock */ - if (dev_priv->regfile.saveDPLL_A & DPLL_VCO_ENABLE) { - I915_WRITE(dpll_a_reg, dev_priv->regfile.saveDPLL_A & - ~DPLL_VCO_ENABLE); - POSTING_READ(dpll_a_reg); - udelay(150); - } - I915_WRITE(fpa0_reg, dev_priv->regfile.saveFPA0); - I915_WRITE(fpa1_reg, dev_priv->regfile.saveFPA1); - /* Actually enable it */ - I915_WRITE(dpll_a_reg, dev_priv->regfile.saveDPLL_A); - POSTING_READ(dpll_a_reg); - udelay(150); - if (INTEL_INFO(dev)->gen >= 4 && !HAS_PCH_SPLIT(dev)) { - I915_WRITE(_DPLL_A_MD, dev_priv->regfile.saveDPLL_A_MD); - POSTING_READ(_DPLL_A_MD); - } - udelay(150); - - /* Restore mode */ - I915_WRITE(_HTOTAL_A, dev_priv->regfile.saveHTOTAL_A); - I915_WRITE(_HBLANK_A, dev_priv->regfile.saveHBLANK_A); - I915_WRITE(_HSYNC_A, dev_priv->regfile.saveHSYNC_A); - I915_WRITE(_VTOTAL_A, dev_priv->regfile.saveVTOTAL_A); - I915_WRITE(_VBLANK_A, dev_priv->regfile.saveVBLANK_A); - I915_WRITE(_VSYNC_A, dev_priv->regfile.saveVSYNC_A); - if (!HAS_PCH_SPLIT(dev)) - I915_WRITE(_BCLRPAT_A, dev_priv->regfile.saveBCLRPAT_A); - - if (HAS_PCH_SPLIT(dev)) { - I915_WRITE(_PIPEA_DATA_M1, dev_priv->regfile.savePIPEA_DATA_M1); - I915_WRITE(_PIPEA_DATA_N1, dev_priv->regfile.savePIPEA_DATA_N1); - I915_WRITE(_PIPEA_LINK_M1, dev_priv->regfile.savePIPEA_LINK_M1); - I915_WRITE(_PIPEA_LINK_N1, dev_priv->regfile.savePIPEA_LINK_N1); - - I915_WRITE(_FDI_RXA_CTL, dev_priv->regfile.saveFDI_RXA_CTL); - I915_WRITE(_FDI_TXA_CTL, dev_priv->regfile.saveFDI_TXA_CTL); - - I915_WRITE(_PFA_CTL_1, dev_priv->regfile.savePFA_CTL_1); - I915_WRITE(_PFA_WIN_SZ, dev_priv->regfile.savePFA_WIN_SZ); - I915_WRITE(_PFA_WIN_POS, dev_priv->regfile.savePFA_WIN_POS); - - I915_WRITE(_TRANSACONF, dev_priv->regfile.saveTRANSACONF); - I915_WRITE(_TRANS_HTOTAL_A, dev_priv->regfile.saveTRANS_HTOTAL_A); - I915_WRITE(_TRANS_HBLANK_A, dev_priv->regfile.saveTRANS_HBLANK_A); - I915_WRITE(_TRANS_HSYNC_A, dev_priv->regfile.saveTRANS_HSYNC_A); - I915_WRITE(_TRANS_VTOTAL_A, dev_priv->regfile.saveTRANS_VTOTAL_A); - I915_WRITE(_TRANS_VBLANK_A, dev_priv->regfile.saveTRANS_VBLANK_A); - I915_WRITE(_TRANS_VSYNC_A, dev_priv->regfile.saveTRANS_VSYNC_A); - } - - /* Restore plane info */ - I915_WRITE(_DSPASIZE, dev_priv->regfile.saveDSPASIZE); - I915_WRITE(_DSPAPOS, dev_priv->regfile.saveDSPAPOS); - I915_WRITE(_PIPEASRC, dev_priv->regfile.savePIPEASRC); - I915_WRITE(_DSPAADDR, dev_priv->regfile.saveDSPAADDR); - I915_WRITE(_DSPASTRIDE, dev_priv->regfile.saveDSPASTRIDE); - if (INTEL_INFO(dev)->gen >= 4) { - I915_WRITE(_DSPASURF, dev_priv->regfile.saveDSPASURF); - I915_WRITE(_DSPATILEOFF, dev_priv->regfile.saveDSPATILEOFF); - } - - I915_WRITE(_PIPEACONF, dev_priv->regfile.savePIPEACONF); - - i915_restore_palette(dev, PIPE_A); - /* Enable the plane */ - I915_WRITE(_DSPACNTR, dev_priv->regfile.saveDSPACNTR); - I915_WRITE(_DSPAADDR, I915_READ(_DSPAADDR)); - - /* Pipe & plane B info */ - if (dev_priv->regfile.saveDPLL_B & DPLL_VCO_ENABLE) { - I915_WRITE(dpll_b_reg, dev_priv->regfile.saveDPLL_B & - ~DPLL_VCO_ENABLE); - POSTING_READ(dpll_b_reg); - udelay(150); - } - I915_WRITE(fpb0_reg, dev_priv->regfile.saveFPB0); - I915_WRITE(fpb1_reg, dev_priv->regfile.saveFPB1); - /* Actually enable it */ - I915_WRITE(dpll_b_reg, dev_priv->regfile.saveDPLL_B); - POSTING_READ(dpll_b_reg); - udelay(150); - if (INTEL_INFO(dev)->gen >= 4 && !HAS_PCH_SPLIT(dev)) { - I915_WRITE(_DPLL_B_MD, dev_priv->regfile.saveDPLL_B_MD); - POSTING_READ(_DPLL_B_MD); - } - udelay(150); - - /* Restore mode */ - I915_WRITE(_HTOTAL_B, dev_priv->regfile.saveHTOTAL_B); - I915_WRITE(_HBLANK_B, dev_priv->regfile.saveHBLANK_B); - I915_WRITE(_HSYNC_B, dev_priv->regfile.saveHSYNC_B); - I915_WRITE(_VTOTAL_B, dev_priv->regfile.saveVTOTAL_B); - I915_WRITE(_VBLANK_B, dev_priv->regfile.saveVBLANK_B); - I915_WRITE(_VSYNC_B, dev_priv->regfile.saveVSYNC_B); - if (!HAS_PCH_SPLIT(dev)) - I915_WRITE(_BCLRPAT_B, dev_priv->regfile.saveBCLRPAT_B); - - if (HAS_PCH_SPLIT(dev)) { - I915_WRITE(_PIPEB_DATA_M1, dev_priv->regfile.savePIPEB_DATA_M1); - I915_WRITE(_PIPEB_DATA_N1, dev_priv->regfile.savePIPEB_DATA_N1); - I915_WRITE(_PIPEB_LINK_M1, dev_priv->regfile.savePIPEB_LINK_M1); - I915_WRITE(_PIPEB_LINK_N1, dev_priv->regfile.savePIPEB_LINK_N1); - - I915_WRITE(_FDI_RXB_CTL, dev_priv->regfile.saveFDI_RXB_CTL); - I915_WRITE(_FDI_TXB_CTL, dev_priv->regfile.saveFDI_TXB_CTL); - - I915_WRITE(_PFB_CTL_1, dev_priv->regfile.savePFB_CTL_1); - I915_WRITE(_PFB_WIN_SZ, dev_priv->regfile.savePFB_WIN_SZ); - I915_WRITE(_PFB_WIN_POS, dev_priv->regfile.savePFB_WIN_POS); - - I915_WRITE(_TRANSBCONF, dev_priv->regfile.saveTRANSBCONF); - I915_WRITE(_TRANS_HTOTAL_B, dev_priv->regfile.saveTRANS_HTOTAL_B); - I915_WRITE(_TRANS_HBLANK_B, dev_priv->regfile.saveTRANS_HBLANK_B); - I915_WRITE(_TRANS_HSYNC_B, dev_priv->regfile.saveTRANS_HSYNC_B); - I915_WRITE(_TRANS_VTOTAL_B, dev_priv->regfile.saveTRANS_VTOTAL_B); - I915_WRITE(_TRANS_VBLANK_B, dev_priv->regfile.saveTRANS_VBLANK_B); - I915_WRITE(_TRANS_VSYNC_B, dev_priv->regfile.saveTRANS_VSYNC_B); - } - - /* Restore plane info */ - I915_WRITE(_DSPBSIZE, dev_priv->regfile.saveDSPBSIZE); - I915_WRITE(_DSPBPOS, dev_priv->regfile.saveDSPBPOS); - I915_WRITE(_PIPEBSRC, dev_priv->regfile.savePIPEBSRC); - I915_WRITE(_DSPBADDR, dev_priv->regfile.saveDSPBADDR); - I915_WRITE(_DSPBSTRIDE, dev_priv->regfile.saveDSPBSTRIDE); - if (INTEL_INFO(dev)->gen >= 4) { - I915_WRITE(_DSPBSURF, dev_priv->regfile.saveDSPBSURF); - I915_WRITE(_DSPBTILEOFF, dev_priv->regfile.saveDSPBTILEOFF); - } - - I915_WRITE(_PIPEBCONF, dev_priv->regfile.savePIPEBCONF); - - i915_restore_palette(dev, PIPE_B); - /* Enable the plane */ - I915_WRITE(_DSPBCNTR, dev_priv->regfile.saveDSPBCNTR); - I915_WRITE(_DSPBADDR, I915_READ(_DSPBADDR)); - - /* Cursor state */ - I915_WRITE(_CURAPOS, dev_priv->regfile.saveCURAPOS); - I915_WRITE(_CURACNTR, dev_priv->regfile.saveCURACNTR); - I915_WRITE(_CURABASE, dev_priv->regfile.saveCURABASE); - I915_WRITE(_CURBPOS, dev_priv->regfile.saveCURBPOS); - I915_WRITE(_CURBCNTR, dev_priv->regfile.saveCURBCNTR); - I915_WRITE(_CURBBASE, dev_priv->regfile.saveCURBBASE); - if (IS_GEN2(dev)) - I915_WRITE(CURSIZE, dev_priv->regfile.saveCURSIZE); - - /* CRT state */ - if (HAS_PCH_SPLIT(dev)) - I915_WRITE(PCH_ADPA, dev_priv->regfile.saveADPA); - else - I915_WRITE(ADPA, dev_priv->regfile.saveADPA); - - return; -} - static void i915_save_display(struct drm_device *dev) { struct drm_i915_private *dev_priv = dev->dev_private; /* Display arbitration control */ - dev_priv->regfile.saveDSPARB = I915_READ(DSPARB); + if (INTEL_INFO(dev)->gen <= 4) + dev_priv->regfile.saveDSPARB = I915_READ(DSPARB); /* This is only meaningful in non-KMS mode */ /* Don't regfile.save them in KMS mode */ - i915_save_modeset_reg(dev); + if (!drm_core_check_feature(dev, DRIVER_MODESET)) + i915_save_display_reg(dev); /* LVDS state */ if (HAS_PCH_SPLIT(dev)) { @@ -658,24 +234,6 @@ static void i915_save_display(struct drm_device *dev) dev_priv->regfile.savePP_DIVISOR = I915_READ(PP_DIVISOR); } - if (!drm_core_check_feature(dev, DRIVER_MODESET)) { - /* Display Port state */ - if (SUPPORTS_INTEGRATED_DP(dev)) { - dev_priv->regfile.saveDP_B = I915_READ(DP_B); - dev_priv->regfile.saveDP_C = I915_READ(DP_C); - dev_priv->regfile.saveDP_D = I915_READ(DP_D); - dev_priv->regfile.savePIPEA_GMCH_DATA_M = I915_READ(_PIPEA_GMCH_DATA_M); - dev_priv->regfile.savePIPEB_GMCH_DATA_M = I915_READ(_PIPEB_GMCH_DATA_M); - dev_priv->regfile.savePIPEA_GMCH_DATA_N = I915_READ(_PIPEA_GMCH_DATA_N); - dev_priv->regfile.savePIPEB_GMCH_DATA_N = I915_READ(_PIPEB_GMCH_DATA_N); - dev_priv->regfile.savePIPEA_DP_LINK_M = I915_READ(_PIPEA_DP_LINK_M); - dev_priv->regfile.savePIPEB_DP_LINK_M = I915_READ(_PIPEB_DP_LINK_M); - dev_priv->regfile.savePIPEA_DP_LINK_N = I915_READ(_PIPEA_DP_LINK_N); - dev_priv->regfile.savePIPEB_DP_LINK_N = I915_READ(_PIPEB_DP_LINK_N); - } - /* FIXME: regfile.save TV & SDVO state */ - } - /* Only regfile.save FBC state on the platform that supports FBC */ if (I915_HAS_FBC(dev)) { if (HAS_PCH_SPLIT(dev)) { @@ -690,16 +248,8 @@ static void i915_save_display(struct drm_device *dev) } } - /* VGA state */ - dev_priv->regfile.saveVGA0 = I915_READ(VGA0); - dev_priv->regfile.saveVGA1 = I915_READ(VGA1); - dev_priv->regfile.saveVGA_PD = I915_READ(VGA_PD); - if (HAS_PCH_SPLIT(dev)) - dev_priv->regfile.saveVGACNTRL = I915_READ(CPU_VGACNTRL); - else - dev_priv->regfile.saveVGACNTRL = I915_READ(VGACNTRL); - - i915_save_vga(dev); + if (!drm_core_check_feature(dev, DRIVER_MODESET)) + i915_save_vga(dev); } static void i915_restore_display(struct drm_device *dev) @@ -707,25 +257,11 @@ static void i915_restore_display(struct drm_device *dev) struct drm_i915_private *dev_priv = dev->dev_private; /* Display arbitration */ - I915_WRITE(DSPARB, dev_priv->regfile.saveDSPARB); + if (INTEL_INFO(dev)->gen <= 4) + I915_WRITE(DSPARB, dev_priv->regfile.saveDSPARB); - if (!drm_core_check_feature(dev, DRIVER_MODESET)) { - /* Display port ratios (must be done before clock is set) */ - if (SUPPORTS_INTEGRATED_DP(dev)) { - I915_WRITE(_PIPEA_GMCH_DATA_M, dev_priv->regfile.savePIPEA_GMCH_DATA_M); - I915_WRITE(_PIPEB_GMCH_DATA_M, dev_priv->regfile.savePIPEB_GMCH_DATA_M); - I915_WRITE(_PIPEA_GMCH_DATA_N, dev_priv->regfile.savePIPEA_GMCH_DATA_N); - I915_WRITE(_PIPEB_GMCH_DATA_N, dev_priv->regfile.savePIPEB_GMCH_DATA_N); - I915_WRITE(_PIPEA_DP_LINK_M, dev_priv->regfile.savePIPEA_DP_LINK_M); - I915_WRITE(_PIPEB_DP_LINK_M, dev_priv->regfile.savePIPEB_DP_LINK_M); - I915_WRITE(_PIPEA_DP_LINK_N, dev_priv->regfile.savePIPEA_DP_LINK_N); - I915_WRITE(_PIPEB_DP_LINK_N, dev_priv->regfile.savePIPEB_DP_LINK_N); - } - } - - /* This is only meaningful in non-KMS mode */ - /* Don't restore them in KMS mode */ - i915_restore_modeset_reg(dev); + if (!drm_core_check_feature(dev, DRIVER_MODESET)) + i915_restore_display_reg(dev); /* LVDS state */ if (INTEL_INFO(dev)->gen >= 4 && !HAS_PCH_SPLIT(dev)) @@ -763,16 +299,6 @@ static void i915_restore_display(struct drm_device *dev) I915_WRITE(PP_CONTROL, dev_priv->regfile.savePP_CONTROL); } - if (!drm_core_check_feature(dev, DRIVER_MODESET)) { - /* Display Port state */ - if (SUPPORTS_INTEGRATED_DP(dev)) { - I915_WRITE(DP_B, dev_priv->regfile.saveDP_B); - I915_WRITE(DP_C, dev_priv->regfile.saveDP_C); - I915_WRITE(DP_D, dev_priv->regfile.saveDP_D); - } - /* FIXME: restore TV & SDVO state */ - } - /* only restore FBC info on the platform that supports FBC*/ intel_disable_fbc(dev); if (I915_HAS_FBC(dev)) { @@ -787,19 +313,11 @@ static void i915_restore_display(struct drm_device *dev) I915_WRITE(FBC_CONTROL, dev_priv->regfile.saveFBC_CONTROL); } } - /* VGA state */ - if (HAS_PCH_SPLIT(dev)) - I915_WRITE(CPU_VGACNTRL, dev_priv->regfile.saveVGACNTRL); - else - I915_WRITE(VGACNTRL, dev_priv->regfile.saveVGACNTRL); - I915_WRITE(VGA0, dev_priv->regfile.saveVGA0); - I915_WRITE(VGA1, dev_priv->regfile.saveVGA1); - I915_WRITE(VGA_PD, dev_priv->regfile.saveVGA_PD); - POSTING_READ(VGA_PD); - udelay(150); - - i915_restore_vga(dev); + if (!drm_core_check_feature(dev, DRIVER_MODESET)) + i915_restore_vga(dev); + else + i915_redisable_vga(dev); } int i915_save_state(struct drm_device *dev) diff --git a/drivers/gpu/drm/i915/i915_ums.c b/drivers/gpu/drm/i915/i915_ums.c new file mode 100644 index 000000000000..985a09716237 --- /dev/null +++ b/drivers/gpu/drm/i915/i915_ums.c @@ -0,0 +1,503 @@ +/* + * + * Copyright 2008 (c) Intel Corporation + * Jesse Barnes <jbarnes@virtuousgeek.org> + * Copyright 2013 (c) Intel Corporation + * Daniel Vetter <daniel.vetter@ffwll.ch> + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sub license, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice (including the + * next paragraph) shall be included in all copies or substantial portions + * of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. + * IN NO EVENT SHALL TUNGSTEN GRAPHICS 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. + */ + +#include <drm/drmP.h> +#include <drm/i915_drm.h> +#include "intel_drv.h" +#include "i915_reg.h" + +static bool i915_pipe_enabled(struct drm_device *dev, enum pipe pipe) +{ + struct drm_i915_private *dev_priv = dev->dev_private; + u32 dpll_reg; + + /* On IVB, 3rd pipe shares PLL with another one */ + if (pipe > 1) + return false; + + if (HAS_PCH_SPLIT(dev)) + dpll_reg = _PCH_DPLL(pipe); + else + dpll_reg = (pipe == PIPE_A) ? _DPLL_A : _DPLL_B; + + return (I915_READ(dpll_reg) & DPLL_VCO_ENABLE); +} + +static void i915_save_palette(struct drm_device *dev, enum pipe pipe) +{ + struct drm_i915_private *dev_priv = dev->dev_private; + unsigned long reg = (pipe == PIPE_A ? _PALETTE_A : _PALETTE_B); + u32 *array; + int i; + + if (!i915_pipe_enabled(dev, pipe)) + return; + + if (HAS_PCH_SPLIT(dev)) + reg = (pipe == PIPE_A) ? _LGC_PALETTE_A : _LGC_PALETTE_B; + + if (pipe == PIPE_A) + array = dev_priv->regfile.save_palette_a; + else + array = dev_priv->regfile.save_palette_b; + + for (i = 0; i < 256; i++) + array[i] = I915_READ(reg + (i << 2)); +} + +static void i915_restore_palette(struct drm_device *dev, enum pipe pipe) +{ + struct drm_i915_private *dev_priv = dev->dev_private; + unsigned long reg = (pipe == PIPE_A ? _PALETTE_A : _PALETTE_B); + u32 *array; + int i; + + if (!i915_pipe_enabled(dev, pipe)) + return; + + if (HAS_PCH_SPLIT(dev)) + reg = (pipe == PIPE_A) ? _LGC_PALETTE_A : _LGC_PALETTE_B; + + if (pipe == PIPE_A) + array = dev_priv->regfile.save_palette_a; + else + array = dev_priv->regfile.save_palette_b; + + for (i = 0; i < 256; i++) + I915_WRITE(reg + (i << 2), array[i]); +} + +void i915_save_display_reg(struct drm_device *dev) +{ + struct drm_i915_private *dev_priv = dev->dev_private; + int i; + + /* Cursor state */ + dev_priv->regfile.saveCURACNTR = I915_READ(_CURACNTR); + dev_priv->regfile.saveCURAPOS = I915_READ(_CURAPOS); + dev_priv->regfile.saveCURABASE = I915_READ(_CURABASE); + dev_priv->regfile.saveCURBCNTR = I915_READ(_CURBCNTR); + dev_priv->regfile.saveCURBPOS = I915_READ(_CURBPOS); + dev_priv->regfile.saveCURBBASE = I915_READ(_CURBBASE); + if (IS_GEN2(dev)) + dev_priv->regfile.saveCURSIZE = I915_READ(CURSIZE); + + if (HAS_PCH_SPLIT(dev)) { + dev_priv->regfile.savePCH_DREF_CONTROL = I915_READ(PCH_DREF_CONTROL); + dev_priv->regfile.saveDISP_ARB_CTL = I915_READ(DISP_ARB_CTL); + } + + /* Pipe & plane A info */ + dev_priv->regfile.savePIPEACONF = I915_READ(_PIPEACONF); + dev_priv->regfile.savePIPEASRC = I915_READ(_PIPEASRC); + if (HAS_PCH_SPLIT(dev)) { + dev_priv->regfile.saveFPA0 = I915_READ(_PCH_FPA0); + dev_priv->regfile.saveFPA1 = I915_READ(_PCH_FPA1); + dev_priv->regfile.saveDPLL_A = I915_READ(_PCH_DPLL_A); + } else { + dev_priv->regfile.saveFPA0 = I915_READ(_FPA0); + dev_priv->regfile.saveFPA1 = I915_READ(_FPA1); + dev_priv->regfile.saveDPLL_A = I915_READ(_DPLL_A); + } + if (INTEL_INFO(dev)->gen >= 4 && !HAS_PCH_SPLIT(dev)) + dev_priv->regfile.saveDPLL_A_MD = I915_READ(_DPLL_A_MD); + dev_priv->regfile.saveHTOTAL_A = I915_READ(_HTOTAL_A); + dev_priv->regfile.saveHBLANK_A = I915_READ(_HBLANK_A); + dev_priv->regfile.saveHSYNC_A = I915_READ(_HSYNC_A); + dev_priv->regfile.saveVTOTAL_A = I915_READ(_VTOTAL_A); + dev_priv->regfile.saveVBLANK_A = I915_READ(_VBLANK_A); + dev_priv->regfile.saveVSYNC_A = I915_READ(_VSYNC_A); + if (!HAS_PCH_SPLIT(dev)) + dev_priv->regfile.saveBCLRPAT_A = I915_READ(_BCLRPAT_A); + + if (HAS_PCH_SPLIT(dev)) { + dev_priv->regfile.savePIPEA_DATA_M1 = I915_READ(_PIPEA_DATA_M1); + dev_priv->regfile.savePIPEA_DATA_N1 = I915_READ(_PIPEA_DATA_N1); + dev_priv->regfile.savePIPEA_LINK_M1 = I915_READ(_PIPEA_LINK_M1); + dev_priv->regfile.savePIPEA_LINK_N1 = I915_READ(_PIPEA_LINK_N1); + + dev_priv->regfile.saveFDI_TXA_CTL = I915_READ(_FDI_TXA_CTL); + dev_priv->regfile.saveFDI_RXA_CTL = I915_READ(_FDI_RXA_CTL); + + dev_priv->regfile.savePFA_CTL_1 = I915_READ(_PFA_CTL_1); + dev_priv->regfile.savePFA_WIN_SZ = I915_READ(_PFA_WIN_SZ); + dev_priv->regfile.savePFA_WIN_POS = I915_READ(_PFA_WIN_POS); + + dev_priv->regfile.saveTRANSACONF = I915_READ(_TRANSACONF); + dev_priv->regfile.saveTRANS_HTOTAL_A = I915_READ(_TRANS_HTOTAL_A); + dev_priv->regfile.saveTRANS_HBLANK_A = I915_READ(_TRANS_HBLANK_A); + dev_priv->regfile.saveTRANS_HSYNC_A = I915_READ(_TRANS_HSYNC_A); + dev_priv->regfile.saveTRANS_VTOTAL_A = I915_READ(_TRANS_VTOTAL_A); + dev_priv->regfile.saveTRANS_VBLANK_A = I915_READ(_TRANS_VBLANK_A); + dev_priv->regfile.saveTRANS_VSYNC_A = I915_READ(_TRANS_VSYNC_A); + } + + dev_priv->regfile.saveDSPACNTR = I915_READ(_DSPACNTR); + dev_priv->regfile.saveDSPASTRIDE = I915_READ(_DSPASTRIDE); + dev_priv->regfile.saveDSPASIZE = I915_READ(_DSPASIZE); + dev_priv->regfile.saveDSPAPOS = I915_READ(_DSPAPOS); + dev_priv->regfile.saveDSPAADDR = I915_READ(_DSPAADDR); + if (INTEL_INFO(dev)->gen >= 4) { + dev_priv->regfile.saveDSPASURF = I915_READ(_DSPASURF); + dev_priv->regfile.saveDSPATILEOFF = I915_READ(_DSPATILEOFF); + } + i915_save_palette(dev, PIPE_A); + dev_priv->regfile.savePIPEASTAT = I915_READ(_PIPEASTAT); + + /* Pipe & plane B info */ + dev_priv->regfile.savePIPEBCONF = I915_READ(_PIPEBCONF); + dev_priv->regfile.savePIPEBSRC = I915_READ(_PIPEBSRC); + if (HAS_PCH_SPLIT(dev)) { + dev_priv->regfile.saveFPB0 = I915_READ(_PCH_FPB0); + dev_priv->regfile.saveFPB1 = I915_READ(_PCH_FPB1); + dev_priv->regfile.saveDPLL_B = I915_READ(_PCH_DPLL_B); + } else { + dev_priv->regfile.saveFPB0 = I915_READ(_FPB0); + dev_priv->regfile.saveFPB1 = I915_READ(_FPB1); + dev_priv->regfile.saveDPLL_B = I915_READ(_DPLL_B); + } + if (INTEL_INFO(dev)->gen >= 4 && !HAS_PCH_SPLIT(dev)) + dev_priv->regfile.saveDPLL_B_MD = I915_READ(_DPLL_B_MD); + dev_priv->regfile.saveHTOTAL_B = I915_READ(_HTOTAL_B); + dev_priv->regfile.saveHBLANK_B = I915_READ(_HBLANK_B); + dev_priv->regfile.saveHSYNC_B = I915_READ(_HSYNC_B); + dev_priv->regfile.saveVTOTAL_B = I915_READ(_VTOTAL_B); + dev_priv->regfile.saveVBLANK_B = I915_READ(_VBLANK_B); + dev_priv->regfile.saveVSYNC_B = I915_READ(_VSYNC_B); + if (!HAS_PCH_SPLIT(dev)) + dev_priv->regfile.saveBCLRPAT_B = I915_READ(_BCLRPAT_B); + + if (HAS_PCH_SPLIT(dev)) { + dev_priv->regfile.savePIPEB_DATA_M1 = I915_READ(_PIPEB_DATA_M1); + dev_priv->regfile.savePIPEB_DATA_N1 = I915_READ(_PIPEB_DATA_N1); + dev_priv->regfile.savePIPEB_LINK_M1 = I915_READ(_PIPEB_LINK_M1); + dev_priv->regfile.savePIPEB_LINK_N1 = I915_READ(_PIPEB_LINK_N1); + + dev_priv->regfile.saveFDI_TXB_CTL = I915_READ(_FDI_TXB_CTL); + dev_priv->regfile.saveFDI_RXB_CTL = I915_READ(_FDI_RXB_CTL); + + dev_priv->regfile.savePFB_CTL_1 = I915_READ(_PFB_CTL_1); + dev_priv->regfile.savePFB_WIN_SZ = I915_READ(_PFB_WIN_SZ); + dev_priv->regfile.savePFB_WIN_POS = I915_READ(_PFB_WIN_POS); + + dev_priv->regfile.saveTRANSBCONF = I915_READ(_TRANSBCONF); + dev_priv->regfile.saveTRANS_HTOTAL_B = I915_READ(_TRANS_HTOTAL_B); + dev_priv->regfile.saveTRANS_HBLANK_B = I915_READ(_TRANS_HBLANK_B); + dev_priv->regfile.saveTRANS_HSYNC_B = I915_READ(_TRANS_HSYNC_B); + dev_priv->regfile.saveTRANS_VTOTAL_B = I915_READ(_TRANS_VTOTAL_B); + dev_priv->regfile.saveTRANS_VBLANK_B = I915_READ(_TRANS_VBLANK_B); + dev_priv->regfile.saveTRANS_VSYNC_B = I915_READ(_TRANS_VSYNC_B); + } + + dev_priv->regfile.saveDSPBCNTR = I915_READ(_DSPBCNTR); + dev_priv->regfile.saveDSPBSTRIDE = I915_READ(_DSPBSTRIDE); + dev_priv->regfile.saveDSPBSIZE = I915_READ(_DSPBSIZE); + dev_priv->regfile.saveDSPBPOS = I915_READ(_DSPBPOS); + dev_priv->regfile.saveDSPBADDR = I915_READ(_DSPBADDR); + if (INTEL_INFO(dev)->gen >= 4) { + dev_priv->regfile.saveDSPBSURF = I915_READ(_DSPBSURF); + dev_priv->regfile.saveDSPBTILEOFF = I915_READ(_DSPBTILEOFF); + } + i915_save_palette(dev, PIPE_B); + dev_priv->regfile.savePIPEBSTAT = I915_READ(_PIPEBSTAT); + + /* Fences */ + switch (INTEL_INFO(dev)->gen) { + case 7: + case 6: + for (i = 0; i < 16; i++) + dev_priv->regfile.saveFENCE[i] = I915_READ64(FENCE_REG_SANDYBRIDGE_0 + (i * 8)); + break; + case 5: + case 4: + for (i = 0; i < 16; i++) + dev_priv->regfile.saveFENCE[i] = I915_READ64(FENCE_REG_965_0 + (i * 8)); + break; + case 3: + if (IS_I945G(dev) || IS_I945GM(dev) || IS_G33(dev)) + for (i = 0; i < 8; i++) + dev_priv->regfile.saveFENCE[i+8] = I915_READ(FENCE_REG_945_8 + (i * 4)); + case 2: + for (i = 0; i < 8; i++) + dev_priv->regfile.saveFENCE[i] = I915_READ(FENCE_REG_830_0 + (i * 4)); + break; + } + + /* CRT state */ + if (HAS_PCH_SPLIT(dev)) + dev_priv->regfile.saveADPA = I915_READ(PCH_ADPA); + else + dev_priv->regfile.saveADPA = I915_READ(ADPA); + + /* Display Port state */ + if (SUPPORTS_INTEGRATED_DP(dev)) { + dev_priv->regfile.saveDP_B = I915_READ(DP_B); + dev_priv->regfile.saveDP_C = I915_READ(DP_C); + dev_priv->regfile.saveDP_D = I915_READ(DP_D); + dev_priv->regfile.savePIPEA_GMCH_DATA_M = I915_READ(_PIPEA_GMCH_DATA_M); + dev_priv->regfile.savePIPEB_GMCH_DATA_M = I915_READ(_PIPEB_GMCH_DATA_M); + dev_priv->regfile.savePIPEA_GMCH_DATA_N = I915_READ(_PIPEA_GMCH_DATA_N); + dev_priv->regfile.savePIPEB_GMCH_DATA_N = I915_READ(_PIPEB_GMCH_DATA_N); + dev_priv->regfile.savePIPEA_DP_LINK_M = I915_READ(_PIPEA_DP_LINK_M); + dev_priv->regfile.savePIPEB_DP_LINK_M = I915_READ(_PIPEB_DP_LINK_M); + dev_priv->regfile.savePIPEA_DP_LINK_N = I915_READ(_PIPEA_DP_LINK_N); + dev_priv->regfile.savePIPEB_DP_LINK_N = I915_READ(_PIPEB_DP_LINK_N); + } + /* FIXME: regfile.save TV & SDVO state */ + + return; +} + +void i915_restore_display_reg(struct drm_device *dev) +{ + struct drm_i915_private *dev_priv = dev->dev_private; + int dpll_a_reg, fpa0_reg, fpa1_reg; + int dpll_b_reg, fpb0_reg, fpb1_reg; + int i; + + /* Display port ratios (must be done before clock is set) */ + if (SUPPORTS_INTEGRATED_DP(dev)) { + I915_WRITE(_PIPEA_GMCH_DATA_M, dev_priv->regfile.savePIPEA_GMCH_DATA_M); + I915_WRITE(_PIPEB_GMCH_DATA_M, dev_priv->regfile.savePIPEB_GMCH_DATA_M); + I915_WRITE(_PIPEA_GMCH_DATA_N, dev_priv->regfile.savePIPEA_GMCH_DATA_N); + I915_WRITE(_PIPEB_GMCH_DATA_N, dev_priv->regfile.savePIPEB_GMCH_DATA_N); + I915_WRITE(_PIPEA_DP_LINK_M, dev_priv->regfile.savePIPEA_DP_LINK_M); + I915_WRITE(_PIPEB_DP_LINK_M, dev_priv->regfile.savePIPEB_DP_LINK_M); + I915_WRITE(_PIPEA_DP_LINK_N, dev_priv->regfile.savePIPEA_DP_LINK_N); + I915_WRITE(_PIPEB_DP_LINK_N, dev_priv->regfile.savePIPEB_DP_LINK_N); + } + + /* Fences */ + switch (INTEL_INFO(dev)->gen) { + case 7: + case 6: + for (i = 0; i < 16; i++) + I915_WRITE64(FENCE_REG_SANDYBRIDGE_0 + (i * 8), dev_priv->regfile.saveFENCE[i]); + break; + case 5: + case 4: + for (i = 0; i < 16; i++) + I915_WRITE64(FENCE_REG_965_0 + (i * 8), dev_priv->regfile.saveFENCE[i]); + break; + case 3: + case 2: + if (IS_I945G(dev) || IS_I945GM(dev) || IS_G33(dev)) + for (i = 0; i < 8; i++) + I915_WRITE(FENCE_REG_945_8 + (i * 4), dev_priv->regfile.saveFENCE[i+8]); + for (i = 0; i < 8; i++) + I915_WRITE(FENCE_REG_830_0 + (i * 4), dev_priv->regfile.saveFENCE[i]); + break; + } + + + if (HAS_PCH_SPLIT(dev)) { + dpll_a_reg = _PCH_DPLL_A; + dpll_b_reg = _PCH_DPLL_B; + fpa0_reg = _PCH_FPA0; + fpb0_reg = _PCH_FPB0; + fpa1_reg = _PCH_FPA1; + fpb1_reg = _PCH_FPB1; + } else { + dpll_a_reg = _DPLL_A; + dpll_b_reg = _DPLL_B; + fpa0_reg = _FPA0; + fpb0_reg = _FPB0; + fpa1_reg = _FPA1; + fpb1_reg = _FPB1; + } + + if (HAS_PCH_SPLIT(dev)) { + I915_WRITE(PCH_DREF_CONTROL, dev_priv->regfile.savePCH_DREF_CONTROL); + I915_WRITE(DISP_ARB_CTL, dev_priv->regfile.saveDISP_ARB_CTL); + } + + /* Pipe & plane A info */ + /* Prime the clock */ + if (dev_priv->regfile.saveDPLL_A & DPLL_VCO_ENABLE) { + I915_WRITE(dpll_a_reg, dev_priv->regfile.saveDPLL_A & + ~DPLL_VCO_ENABLE); + POSTING_READ(dpll_a_reg); + udelay(150); + } + I915_WRITE(fpa0_reg, dev_priv->regfile.saveFPA0); + I915_WRITE(fpa1_reg, dev_priv->regfile.saveFPA1); + /* Actually enable it */ + I915_WRITE(dpll_a_reg, dev_priv->regfile.saveDPLL_A); + POSTING_READ(dpll_a_reg); + udelay(150); + if (INTEL_INFO(dev)->gen >= 4 && !HAS_PCH_SPLIT(dev)) { + I915_WRITE(_DPLL_A_MD, dev_priv->regfile.saveDPLL_A_MD); + POSTING_READ(_DPLL_A_MD); + } + udelay(150); + + /* Restore mode */ + I915_WRITE(_HTOTAL_A, dev_priv->regfile.saveHTOTAL_A); + I915_WRITE(_HBLANK_A, dev_priv->regfile.saveHBLANK_A); + I915_WRITE(_HSYNC_A, dev_priv->regfile.saveHSYNC_A); + I915_WRITE(_VTOTAL_A, dev_priv->regfile.saveVTOTAL_A); + I915_WRITE(_VBLANK_A, dev_priv->regfile.saveVBLANK_A); + I915_WRITE(_VSYNC_A, dev_priv->regfile.saveVSYNC_A); + if (!HAS_PCH_SPLIT(dev)) + I915_WRITE(_BCLRPAT_A, dev_priv->regfile.saveBCLRPAT_A); + + if (HAS_PCH_SPLIT(dev)) { + I915_WRITE(_PIPEA_DATA_M1, dev_priv->regfile.savePIPEA_DATA_M1); + I915_WRITE(_PIPEA_DATA_N1, dev_priv->regfile.savePIPEA_DATA_N1); + I915_WRITE(_PIPEA_LINK_M1, dev_priv->regfile.savePIPEA_LINK_M1); + I915_WRITE(_PIPEA_LINK_N1, dev_priv->regfile.savePIPEA_LINK_N1); + + I915_WRITE(_FDI_RXA_CTL, dev_priv->regfile.saveFDI_RXA_CTL); + I915_WRITE(_FDI_TXA_CTL, dev_priv->regfile.saveFDI_TXA_CTL); + + I915_WRITE(_PFA_CTL_1, dev_priv->regfile.savePFA_CTL_1); + I915_WRITE(_PFA_WIN_SZ, dev_priv->regfile.savePFA_WIN_SZ); + I915_WRITE(_PFA_WIN_POS, dev_priv->regfile.savePFA_WIN_POS); + + I915_WRITE(_TRANSACONF, dev_priv->regfile.saveTRANSACONF); + I915_WRITE(_TRANS_HTOTAL_A, dev_priv->regfile.saveTRANS_HTOTAL_A); + I915_WRITE(_TRANS_HBLANK_A, dev_priv->regfile.saveTRANS_HBLANK_A); + I915_WRITE(_TRANS_HSYNC_A, dev_priv->regfile.saveTRANS_HSYNC_A); + I915_WRITE(_TRANS_VTOTAL_A, dev_priv->regfile.saveTRANS_VTOTAL_A); + I915_WRITE(_TRANS_VBLANK_A, dev_priv->regfile.saveTRANS_VBLANK_A); + I915_WRITE(_TRANS_VSYNC_A, dev_priv->regfile.saveTRANS_VSYNC_A); + } + + /* Restore plane info */ + I915_WRITE(_DSPASIZE, dev_priv->regfile.saveDSPASIZE); + I915_WRITE(_DSPAPOS, dev_priv->regfile.saveDSPAPOS); + I915_WRITE(_PIPEASRC, dev_priv->regfile.savePIPEASRC); + I915_WRITE(_DSPAADDR, dev_priv->regfile.saveDSPAADDR); + I915_WRITE(_DSPASTRIDE, dev_priv->regfile.saveDSPASTRIDE); + if (INTEL_INFO(dev)->gen >= 4) { + I915_WRITE(_DSPASURF, dev_priv->regfile.saveDSPASURF); + I915_WRITE(_DSPATILEOFF, dev_priv->regfile.saveDSPATILEOFF); + } + + I915_WRITE(_PIPEACONF, dev_priv->regfile.savePIPEACONF); + + i915_restore_palette(dev, PIPE_A); + /* Enable the plane */ + I915_WRITE(_DSPACNTR, dev_priv->regfile.saveDSPACNTR); + I915_WRITE(_DSPAADDR, I915_READ(_DSPAADDR)); + + /* Pipe & plane B info */ + if (dev_priv->regfile.saveDPLL_B & DPLL_VCO_ENABLE) { + I915_WRITE(dpll_b_reg, dev_priv->regfile.saveDPLL_B & + ~DPLL_VCO_ENABLE); + POSTING_READ(dpll_b_reg); + udelay(150); + } + I915_WRITE(fpb0_reg, dev_priv->regfile.saveFPB0); + I915_WRITE(fpb1_reg, dev_priv->regfile.saveFPB1); + /* Actually enable it */ + I915_WRITE(dpll_b_reg, dev_priv->regfile.saveDPLL_B); + POSTING_READ(dpll_b_reg); + udelay(150); + if (INTEL_INFO(dev)->gen >= 4 && !HAS_PCH_SPLIT(dev)) { + I915_WRITE(_DPLL_B_MD, dev_priv->regfile.saveDPLL_B_MD); + POSTING_READ(_DPLL_B_MD); + } + udelay(150); + + /* Restore mode */ + I915_WRITE(_HTOTAL_B, dev_priv->regfile.saveHTOTAL_B); + I915_WRITE(_HBLANK_B, dev_priv->regfile.saveHBLANK_B); + I915_WRITE(_HSYNC_B, dev_priv->regfile.saveHSYNC_B); + I915_WRITE(_VTOTAL_B, dev_priv->regfile.saveVTOTAL_B); + I915_WRITE(_VBLANK_B, dev_priv->regfile.saveVBLANK_B); + I915_WRITE(_VSYNC_B, dev_priv->regfile.saveVSYNC_B); + if (!HAS_PCH_SPLIT(dev)) + I915_WRITE(_BCLRPAT_B, dev_priv->regfile.saveBCLRPAT_B); + + if (HAS_PCH_SPLIT(dev)) { + I915_WRITE(_PIPEB_DATA_M1, dev_priv->regfile.savePIPEB_DATA_M1); + I915_WRITE(_PIPEB_DATA_N1, dev_priv->regfile.savePIPEB_DATA_N1); + I915_WRITE(_PIPEB_LINK_M1, dev_priv->regfile.savePIPEB_LINK_M1); + I915_WRITE(_PIPEB_LINK_N1, dev_priv->regfile.savePIPEB_LINK_N1); + + I915_WRITE(_FDI_RXB_CTL, dev_priv->regfile.saveFDI_RXB_CTL); + I915_WRITE(_FDI_TXB_CTL, dev_priv->regfile.saveFDI_TXB_CTL); + + I915_WRITE(_PFB_CTL_1, dev_priv->regfile.savePFB_CTL_1); + I915_WRITE(_PFB_WIN_SZ, dev_priv->regfile.savePFB_WIN_SZ); + I915_WRITE(_PFB_WIN_POS, dev_priv->regfile.savePFB_WIN_POS); + + I915_WRITE(_TRANSBCONF, dev_priv->regfile.saveTRANSBCONF); + I915_WRITE(_TRANS_HTOTAL_B, dev_priv->regfile.saveTRANS_HTOTAL_B); + I915_WRITE(_TRANS_HBLANK_B, dev_priv->regfile.saveTRANS_HBLANK_B); + I915_WRITE(_TRANS_HSYNC_B, dev_priv->regfile.saveTRANS_HSYNC_B); + I915_WRITE(_TRANS_VTOTAL_B, dev_priv->regfile.saveTRANS_VTOTAL_B); + I915_WRITE(_TRANS_VBLANK_B, dev_priv->regfile.saveTRANS_VBLANK_B); + I915_WRITE(_TRANS_VSYNC_B, dev_priv->regfile.saveTRANS_VSYNC_B); + } + + /* Restore plane info */ + I915_WRITE(_DSPBSIZE, dev_priv->regfile.saveDSPBSIZE); + I915_WRITE(_DSPBPOS, dev_priv->regfile.saveDSPBPOS); + I915_WRITE(_PIPEBSRC, dev_priv->regfile.savePIPEBSRC); + I915_WRITE(_DSPBADDR, dev_priv->regfile.saveDSPBADDR); + I915_WRITE(_DSPBSTRIDE, dev_priv->regfile.saveDSPBSTRIDE); + if (INTEL_INFO(dev)->gen >= 4) { + I915_WRITE(_DSPBSURF, dev_priv->regfile.saveDSPBSURF); + I915_WRITE(_DSPBTILEOFF, dev_priv->regfile.saveDSPBTILEOFF); + } + + I915_WRITE(_PIPEBCONF, dev_priv->regfile.savePIPEBCONF); + + i915_restore_palette(dev, PIPE_B); + /* Enable the plane */ + I915_WRITE(_DSPBCNTR, dev_priv->regfile.saveDSPBCNTR); + I915_WRITE(_DSPBADDR, I915_READ(_DSPBADDR)); + + /* Cursor state */ + I915_WRITE(_CURAPOS, dev_priv->regfile.saveCURAPOS); + I915_WRITE(_CURACNTR, dev_priv->regfile.saveCURACNTR); + I915_WRITE(_CURABASE, dev_priv->regfile.saveCURABASE); + I915_WRITE(_CURBPOS, dev_priv->regfile.saveCURBPOS); + I915_WRITE(_CURBCNTR, dev_priv->regfile.saveCURBCNTR); + I915_WRITE(_CURBBASE, dev_priv->regfile.saveCURBBASE); + if (IS_GEN2(dev)) + I915_WRITE(CURSIZE, dev_priv->regfile.saveCURSIZE); + + /* CRT state */ + if (HAS_PCH_SPLIT(dev)) + I915_WRITE(PCH_ADPA, dev_priv->regfile.saveADPA); + else + I915_WRITE(ADPA, dev_priv->regfile.saveADPA); + + /* Display Port state */ + if (SUPPORTS_INTEGRATED_DP(dev)) { + I915_WRITE(DP_B, dev_priv->regfile.saveDP_B); + I915_WRITE(DP_C, dev_priv->regfile.saveDP_C); + I915_WRITE(DP_D, dev_priv->regfile.saveDP_D); + } + /* FIXME: restore TV & SDVO state */ + + return; +} diff --git a/drivers/gpu/drm/i915/intel_crt.c b/drivers/gpu/drm/i915/intel_crt.c index 9293878ec7eb..969d08c72d10 100644 --- a/drivers/gpu/drm/i915/intel_crt.c +++ b/drivers/gpu/drm/i915/intel_crt.c @@ -267,27 +267,27 @@ static bool intel_ironlake_crt_detect_hotplug(struct drm_connector *connector) crt->force_hotplug_required = 0; - save_adpa = adpa = I915_READ(PCH_ADPA); + save_adpa = adpa = I915_READ(crt->adpa_reg); DRM_DEBUG_KMS("trigger hotplug detect cycle: adpa=0x%x\n", adpa); adpa |= ADPA_CRT_HOTPLUG_FORCE_TRIGGER; if (turn_off_dac) adpa &= ~ADPA_DAC_ENABLE; - I915_WRITE(PCH_ADPA, adpa); + I915_WRITE(crt->adpa_reg, adpa); - if (wait_for((I915_READ(PCH_ADPA) & ADPA_CRT_HOTPLUG_FORCE_TRIGGER) == 0, + if (wait_for((I915_READ(crt->adpa_reg) & ADPA_CRT_HOTPLUG_FORCE_TRIGGER) == 0, 1000)) DRM_DEBUG_KMS("timed out waiting for FORCE_TRIGGER"); if (turn_off_dac) { - I915_WRITE(PCH_ADPA, save_adpa); - POSTING_READ(PCH_ADPA); + I915_WRITE(crt->adpa_reg, save_adpa); + POSTING_READ(crt->adpa_reg); } } /* Check the status to see if both blue and green are on now */ - adpa = I915_READ(PCH_ADPA); + adpa = I915_READ(crt->adpa_reg); if ((adpa & ADPA_CRT_HOTPLUG_MONITOR_MASK) != 0) ret = true; else @@ -300,26 +300,27 @@ static bool intel_ironlake_crt_detect_hotplug(struct drm_connector *connector) static bool valleyview_crt_detect_hotplug(struct drm_connector *connector) { struct drm_device *dev = connector->dev; + struct intel_crt *crt = intel_attached_crt(connector); struct drm_i915_private *dev_priv = dev->dev_private; u32 adpa; bool ret; u32 save_adpa; - save_adpa = adpa = I915_READ(ADPA); + save_adpa = adpa = I915_READ(crt->adpa_reg); DRM_DEBUG_KMS("trigger hotplug detect cycle: adpa=0x%x\n", adpa); adpa |= ADPA_CRT_HOTPLUG_FORCE_TRIGGER; - I915_WRITE(ADPA, adpa); + I915_WRITE(crt->adpa_reg, adpa); - if (wait_for((I915_READ(ADPA) & ADPA_CRT_HOTPLUG_FORCE_TRIGGER) == 0, + if (wait_for((I915_READ(crt->adpa_reg) & ADPA_CRT_HOTPLUG_FORCE_TRIGGER) == 0, 1000)) { DRM_DEBUG_KMS("timed out waiting for FORCE_TRIGGER"); - I915_WRITE(ADPA, save_adpa); + I915_WRITE(crt->adpa_reg, save_adpa); } /* Check the status to see if both blue and green are on now */ - adpa = I915_READ(ADPA); + adpa = I915_READ(crt->adpa_reg); if ((adpa & ADPA_CRT_HOTPLUG_MONITOR_MASK) != 0) ret = true; else @@ -665,11 +666,11 @@ static void intel_crt_reset(struct drm_connector *connector) if (HAS_PCH_SPLIT(dev)) { u32 adpa; - adpa = I915_READ(PCH_ADPA); + adpa = I915_READ(crt->adpa_reg); adpa &= ~ADPA_CRT_HOTPLUG_MASK; adpa |= ADPA_HOTPLUG_BITS; - I915_WRITE(PCH_ADPA, adpa); - POSTING_READ(PCH_ADPA); + I915_WRITE(crt->adpa_reg, adpa); + POSTING_READ(crt->adpa_reg); DRM_DEBUG_KMS("pch crt adpa set to 0x%x\n", adpa); crt->force_hotplug_required = 1; @@ -684,7 +685,6 @@ static void intel_crt_reset(struct drm_connector *connector) static const struct drm_encoder_helper_funcs crt_encoder_funcs = { .mode_fixup = intel_crt_mode_fixup, .mode_set = intel_crt_mode_set, - .disable = intel_encoder_noop, }; static const struct drm_connector_funcs intel_crt_connector_funcs = { @@ -776,7 +776,7 @@ void intel_crt_init(struct drm_device *dev) crt->base.disable = intel_disable_crt; crt->base.enable = intel_enable_crt; - if (IS_HASWELL(dev)) + if (HAS_DDI(dev)) crt->base.get_hw_state = intel_ddi_get_hw_state; else crt->base.get_hw_state = intel_crt_get_hw_state; @@ -800,10 +800,14 @@ void intel_crt_init(struct drm_device *dev) dev_priv->hotplug_supported_mask |= CRT_HOTPLUG_INT_STATUS; /* - * TODO: find a proper way to discover whether we need to set the - * polarity reversal bit or not, instead of relying on the BIOS. + * TODO: find a proper way to discover whether we need to set the the + * polarity and link reversal bits or not, instead of relying on the + * BIOS. */ - if (HAS_PCH_LPT(dev)) - dev_priv->fdi_rx_polarity_reversed = - !!(I915_READ(_FDI_RXA_CTL) & FDI_RX_POLARITY_REVERSED_LPT); + if (HAS_PCH_LPT(dev)) { + u32 fdi_config = FDI_RX_POLARITY_REVERSED_LPT | + FDI_RX_LINK_REVERSAL_OVERRIDE; + + dev_priv->fdi_rx_config = I915_READ(_FDI_RXA_CTL) & fdi_config; + } } diff --git a/drivers/gpu/drm/i915/intel_ddi.c b/drivers/gpu/drm/i915/intel_ddi.c index 4bad0f724019..d64af5aa4a1c 100644 --- a/drivers/gpu/drm/i915/intel_ddi.c +++ b/drivers/gpu/drm/i915/intel_ddi.c @@ -84,7 +84,8 @@ static enum port intel_ddi_get_encoder_port(struct intel_encoder *intel_encoder) * in either FDI or DP modes only, as HDMI connections will work with both * of those */ -void intel_prepare_ddi_buffers(struct drm_device *dev, enum port port, bool use_fdi_mode) +static void intel_prepare_ddi_buffers(struct drm_device *dev, enum port port, + bool use_fdi_mode) { struct drm_i915_private *dev_priv = dev->dev_private; u32 reg; @@ -114,16 +115,17 @@ void intel_prepare_ddi(struct drm_device *dev) { int port; - if (IS_HASWELL(dev)) { - for (port = PORT_A; port < PORT_E; port++) - intel_prepare_ddi_buffers(dev, port, false); + if (!HAS_DDI(dev)) + return; - /* DDI E is the suggested one to work in FDI mode, so program is as such by - * default. It will have to be re-programmed in case a digital DP output - * will be detected on it - */ - intel_prepare_ddi_buffers(dev, PORT_E, true); - } + for (port = PORT_A; port < PORT_E; port++) + intel_prepare_ddi_buffers(dev, port, false); + + /* DDI E is the suggested one to work in FDI mode, so program is as such + * by default. It will have to be re-programmed in case a digital DP + * output will be detected on it + */ + intel_prepare_ddi_buffers(dev, PORT_E, true); } static const long hsw_ddi_buf_ctl_values[] = { @@ -178,10 +180,8 @@ void hsw_fdi_link_train(struct drm_crtc *crtc) FDI_RX_TP1_TO_TP2_48 | FDI_RX_FDI_DELAY_90); /* Enable the PCH Receiver FDI PLL */ - rx_ctl_val = FDI_RX_PLL_ENABLE | FDI_RX_ENHANCE_FRAME_ENABLE | - ((intel_crtc->fdi_lanes - 1) << 19); - if (dev_priv->fdi_rx_polarity_reversed) - rx_ctl_val |= FDI_RX_POLARITY_REVERSED_LPT; + rx_ctl_val = dev_priv->fdi_rx_config | FDI_RX_ENHANCE_FRAME_ENABLE | + FDI_RX_PLL_ENABLE | ((intel_crtc->fdi_lanes - 1) << 19); I915_WRITE(_FDI_RXA_CTL, rx_ctl_val); POSTING_READ(_FDI_RXA_CTL); udelay(220); @@ -203,7 +203,10 @@ void hsw_fdi_link_train(struct drm_crtc *crtc) DP_TP_CTL_LINK_TRAIN_PAT1 | DP_TP_CTL_ENABLE); - /* Configure and enable DDI_BUF_CTL for DDI E with next voltage */ + /* Configure and enable DDI_BUF_CTL for DDI E with next voltage. + * DDI E does not support port reversal, the functionality is + * achieved on the PCH side in FDI_RX_CTL, so no need to set the + * port reversal bit */ I915_WRITE(DDI_BUF_CTL(PORT_E), DDI_BUF_CTL_ENABLE | ((intel_crtc->fdi_lanes - 1) << 1) | @@ -675,10 +678,14 @@ static void intel_ddi_mode_set(struct drm_encoder *encoder, DRM_DEBUG_KMS("Preparing DDI mode for Haswell on port %c, pipe %c\n", port_name(port), pipe_name(pipe)); + intel_crtc->eld_vld = false; if (type == INTEL_OUTPUT_DISPLAYPORT || type == INTEL_OUTPUT_EDP) { struct intel_dp *intel_dp = enc_to_intel_dp(encoder); + struct intel_digital_port *intel_dig_port = + enc_to_dig_port(encoder); - intel_dp->DP = DDI_BUF_CTL_ENABLE | DDI_BUF_EMP_400MV_0DB_HSW; + intel_dp->DP = intel_dig_port->port_reversal | + DDI_BUF_CTL_ENABLE | DDI_BUF_EMP_400MV_0DB_HSW; switch (intel_dp->lane_count) { case 1: intel_dp->DP |= DDI_PORT_WIDTH_X1; @@ -985,7 +992,13 @@ void intel_ddi_enable_pipe_func(struct drm_crtc *crtc) if (cpu_transcoder == TRANSCODER_EDP) { switch (pipe) { case PIPE_A: - temp |= TRANS_DDI_EDP_INPUT_A_ONOFF; + /* Can only use the always-on power well for eDP when + * not using the panel fitter, and when not using motion + * blur mitigation (which we don't support). */ + if (dev_priv->pch_pf_size) + temp |= TRANS_DDI_EDP_INPUT_A_ONOFF; + else + temp |= TRANS_DDI_EDP_INPUT_A_ON; break; case PIPE_B: temp |= TRANS_DDI_EDP_INPUT_B_ONOFF; @@ -1069,7 +1082,7 @@ bool intel_ddi_connector_get_hw_state(struct intel_connector *intel_connector) if (port == PORT_A) cpu_transcoder = TRANSCODER_EDP; else - cpu_transcoder = pipe; + cpu_transcoder = (enum transcoder) pipe; tmp = I915_READ(TRANS_DDI_FUNC_CTL(cpu_transcoder)); @@ -1285,34 +1298,58 @@ static void intel_ddi_post_disable(struct intel_encoder *intel_encoder) static void intel_enable_ddi(struct intel_encoder *intel_encoder) { struct drm_encoder *encoder = &intel_encoder->base; + struct drm_crtc *crtc = encoder->crtc; + struct intel_crtc *intel_crtc = to_intel_crtc(crtc); + int pipe = intel_crtc->pipe; struct drm_device *dev = encoder->dev; struct drm_i915_private *dev_priv = dev->dev_private; enum port port = intel_ddi_get_encoder_port(intel_encoder); int type = intel_encoder->type; + uint32_t tmp; if (type == INTEL_OUTPUT_HDMI) { + struct intel_digital_port *intel_dig_port = + enc_to_dig_port(encoder); + /* In HDMI/DVI mode, the port width, and swing/emphasis values * are ignored so nothing special needs to be done besides * enabling the port. */ - I915_WRITE(DDI_BUF_CTL(port), DDI_BUF_CTL_ENABLE); + I915_WRITE(DDI_BUF_CTL(port), + intel_dig_port->port_reversal | DDI_BUF_CTL_ENABLE); } else if (type == INTEL_OUTPUT_EDP) { struct intel_dp *intel_dp = enc_to_intel_dp(encoder); ironlake_edp_backlight_on(intel_dp); } + + if (intel_crtc->eld_vld) { + tmp = I915_READ(HSW_AUD_PIN_ELD_CP_VLD); + tmp |= ((AUDIO_OUTPUT_ENABLE_A | AUDIO_ELD_VALID_A) << (pipe * 4)); + I915_WRITE(HSW_AUD_PIN_ELD_CP_VLD, tmp); + } } static void intel_disable_ddi(struct intel_encoder *intel_encoder) { struct drm_encoder *encoder = &intel_encoder->base; + struct drm_crtc *crtc = encoder->crtc; + struct intel_crtc *intel_crtc = to_intel_crtc(crtc); + int pipe = intel_crtc->pipe; int type = intel_encoder->type; + struct drm_device *dev = encoder->dev; + struct drm_i915_private *dev_priv = dev->dev_private; + uint32_t tmp; if (type == INTEL_OUTPUT_EDP) { struct intel_dp *intel_dp = enc_to_intel_dp(encoder); ironlake_edp_backlight_off(intel_dp); } + + tmp = I915_READ(HSW_AUD_PIN_ELD_CP_VLD); + tmp &= ~((AUDIO_OUTPUT_ENABLE_A | AUDIO_ELD_VALID_A) << (pipe * 4)); + I915_WRITE(HSW_AUD_PIN_ELD_CP_VLD, tmp); } int intel_ddi_get_cdclk_freq(struct drm_i915_private *dev_priv) @@ -1452,11 +1489,11 @@ static const struct drm_encoder_funcs intel_ddi_funcs = { static const struct drm_encoder_helper_funcs intel_ddi_helper_funcs = { .mode_fixup = intel_ddi_mode_fixup, .mode_set = intel_ddi_mode_set, - .disable = intel_encoder_noop, }; void intel_ddi_init(struct drm_device *dev, enum port port) { + struct drm_i915_private *dev_priv = dev->dev_private; struct intel_digital_port *intel_dig_port; struct intel_encoder *intel_encoder; struct drm_encoder *encoder; @@ -1497,6 +1534,8 @@ void intel_ddi_init(struct drm_device *dev, enum port port) intel_encoder->get_hw_state = intel_ddi_get_hw_state; intel_dig_port->port = port; + intel_dig_port->port_reversal = I915_READ(DDI_BUF_CTL(port)) & + DDI_BUF_PORT_REVERSAL; if (hdmi_connector) intel_dig_port->hdmi.sdvox_reg = DDI_BUF_CTL(port); else diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index da1ad9c80bb5..6337196b7931 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -154,8 +154,8 @@ static const intel_limit_t intel_limits_i9xx_sdvo = { .vco = { .min = 1400000, .max = 2800000 }, .n = { .min = 1, .max = 6 }, .m = { .min = 70, .max = 120 }, - .m1 = { .min = 10, .max = 22 }, - .m2 = { .min = 5, .max = 9 }, + .m1 = { .min = 8, .max = 18 }, + .m2 = { .min = 3, .max = 7 }, .p = { .min = 5, .max = 80 }, .p1 = { .min = 1, .max = 8 }, .p2 = { .dot_limit = 200000, @@ -168,8 +168,8 @@ static const intel_limit_t intel_limits_i9xx_lvds = { .vco = { .min = 1400000, .max = 2800000 }, .n = { .min = 1, .max = 6 }, .m = { .min = 70, .max = 120 }, - .m1 = { .min = 10, .max = 22 }, - .m2 = { .min = 5, .max = 9 }, + .m1 = { .min = 8, .max = 18 }, + .m2 = { .min = 3, .max = 7 }, .p = { .min = 7, .max = 98 }, .p1 = { .min = 1, .max = 8 }, .p2 = { .dot_limit = 112000, @@ -416,13 +416,11 @@ static const intel_limit_t intel_limits_vlv_dp = { u32 intel_dpio_read(struct drm_i915_private *dev_priv, int reg) { - unsigned long flags; - u32 val = 0; + WARN_ON(!mutex_is_locked(&dev_priv->dpio_lock)); - spin_lock_irqsave(&dev_priv->dpio_lock, flags); if (wait_for_atomic_us((I915_READ(DPIO_PKT) & DPIO_BUSY) == 0, 100)) { DRM_ERROR("DPIO idle wait timed out\n"); - goto out_unlock; + return 0; } I915_WRITE(DPIO_REG, reg); @@ -430,24 +428,20 @@ u32 intel_dpio_read(struct drm_i915_private *dev_priv, int reg) DPIO_BYTE); if (wait_for_atomic_us((I915_READ(DPIO_PKT) & DPIO_BUSY) == 0, 100)) { DRM_ERROR("DPIO read wait timed out\n"); - goto out_unlock; + return 0; } - val = I915_READ(DPIO_DATA); -out_unlock: - spin_unlock_irqrestore(&dev_priv->dpio_lock, flags); - return val; + return I915_READ(DPIO_DATA); } static void intel_dpio_write(struct drm_i915_private *dev_priv, int reg, u32 val) { - unsigned long flags; + WARN_ON(!mutex_is_locked(&dev_priv->dpio_lock)); - spin_lock_irqsave(&dev_priv->dpio_lock, flags); if (wait_for_atomic_us((I915_READ(DPIO_PKT) & DPIO_BUSY) == 0, 100)) { DRM_ERROR("DPIO idle wait timed out\n"); - goto out_unlock; + return; } I915_WRITE(DPIO_DATA, val); @@ -456,9 +450,6 @@ static void intel_dpio_write(struct drm_i915_private *dev_priv, int reg, DPIO_BYTE); if (wait_for_atomic_us((I915_READ(DPIO_PKT) & DPIO_BUSY) == 0, 100)) DRM_ERROR("DPIO write wait timed out\n"); - -out_unlock: - spin_unlock_irqrestore(&dev_priv->dpio_lock, flags); } static void vlv_init_dpio(struct drm_device *dev) @@ -472,61 +463,14 @@ static void vlv_init_dpio(struct drm_device *dev) POSTING_READ(DPIO_CTL); } -static int intel_dual_link_lvds_callback(const struct dmi_system_id *id) -{ - DRM_INFO("Forcing lvds to dual link mode on %s\n", id->ident); - return 1; -} - -static const struct dmi_system_id intel_dual_link_lvds[] = { - { - .callback = intel_dual_link_lvds_callback, - .ident = "Apple MacBook Pro (Core i5/i7 Series)", - .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "Apple Inc."), - DMI_MATCH(DMI_PRODUCT_NAME, "MacBookPro8,2"), - }, - }, - { } /* terminating entry */ -}; - -static bool is_dual_link_lvds(struct drm_i915_private *dev_priv, - unsigned int reg) -{ - unsigned int val; - - /* use the module option value if specified */ - if (i915_lvds_channel_mode > 0) - return i915_lvds_channel_mode == 2; - - if (dmi_check_system(intel_dual_link_lvds)) - return true; - - if (dev_priv->lvds_val) - val = dev_priv->lvds_val; - else { - /* BIOS should set the proper LVDS register value at boot, but - * in reality, it doesn't set the value when the lid is closed; - * we need to check "the value to be set" in VBT when LVDS - * register is uninitialized. - */ - val = I915_READ(reg); - if (!(val & ~(LVDS_PIPE_MASK | LVDS_DETECTED))) - val = dev_priv->bios_lvds_val; - dev_priv->lvds_val = val; - } - return (val & LVDS_CLKB_POWER_MASK) == LVDS_CLKB_POWER_UP; -} - static const intel_limit_t *intel_ironlake_limit(struct drm_crtc *crtc, int refclk) { struct drm_device *dev = crtc->dev; - struct drm_i915_private *dev_priv = dev->dev_private; const intel_limit_t *limit; if (intel_pipe_has_type(crtc, INTEL_OUTPUT_LVDS)) { - if (is_dual_link_lvds(dev_priv, PCH_LVDS)) { + if (intel_is_dual_link_lvds(dev)) { /* LVDS dual channel */ if (refclk == 100000) limit = &intel_limits_ironlake_dual_lvds_100m; @@ -550,11 +494,10 @@ static const intel_limit_t *intel_ironlake_limit(struct drm_crtc *crtc, static const intel_limit_t *intel_g4x_limit(struct drm_crtc *crtc) { struct drm_device *dev = crtc->dev; - struct drm_i915_private *dev_priv = dev->dev_private; const intel_limit_t *limit; if (intel_pipe_has_type(crtc, INTEL_OUTPUT_LVDS)) { - if (is_dual_link_lvds(dev_priv, LVDS)) + if (intel_is_dual_link_lvds(dev)) /* LVDS with dual channel */ limit = &intel_limits_g4x_dual_channel_lvds; else @@ -686,19 +629,16 @@ intel_find_best_PLL(const intel_limit_t *limit, struct drm_crtc *crtc, { struct drm_device *dev = crtc->dev; - struct drm_i915_private *dev_priv = dev->dev_private; intel_clock_t clock; int err = target; - if (intel_pipe_has_type(crtc, INTEL_OUTPUT_LVDS) && - (I915_READ(LVDS)) != 0) { + if (intel_pipe_has_type(crtc, INTEL_OUTPUT_LVDS)) { /* - * For LVDS, if the panel is on, just rely on its current - * settings for dual-channel. We haven't figured out how to - * reliably set up different single/dual channel state, if we - * even can. + * For LVDS just rely on its current settings for dual-channel. + * We haven't figured out how to reliably set up different + * single/dual channel state, if we even can. */ - if (is_dual_link_lvds(dev_priv, LVDS)) + if (intel_is_dual_link_lvds(dev)) clock.p2 = limit->p2.p2_fast; else clock.p2 = limit->p2.p2_slow; @@ -751,7 +691,6 @@ intel_g4x_find_best_PLL(const intel_limit_t *limit, struct drm_crtc *crtc, intel_clock_t *best_clock) { struct drm_device *dev = crtc->dev; - struct drm_i915_private *dev_priv = dev->dev_private; intel_clock_t clock; int max_n; bool found; @@ -766,8 +705,7 @@ intel_g4x_find_best_PLL(const intel_limit_t *limit, struct drm_crtc *crtc, lvds_reg = PCH_LVDS; else lvds_reg = LVDS; - if ((I915_READ(lvds_reg) & LVDS_CLKB_POWER_MASK) == - LVDS_CLKB_POWER_UP) + if (intel_is_dual_link_lvds(dev)) clock.p2 = limit->p2.p2_fast; else clock.p2 = limit->p2.p2_slow; @@ -1047,6 +985,51 @@ void intel_wait_for_pipe_off(struct drm_device *dev, int pipe) } } +/* + * ibx_digital_port_connected - is the specified port connected? + * @dev_priv: i915 private structure + * @port: the port to test + * + * Returns true if @port is connected, false otherwise. + */ +bool ibx_digital_port_connected(struct drm_i915_private *dev_priv, + struct intel_digital_port *port) +{ + u32 bit; + + if (HAS_PCH_IBX(dev_priv->dev)) { + switch(port->port) { + case PORT_B: + bit = SDE_PORTB_HOTPLUG; + break; + case PORT_C: + bit = SDE_PORTC_HOTPLUG; + break; + case PORT_D: + bit = SDE_PORTD_HOTPLUG; + break; + default: + return true; + } + } else { + switch(port->port) { + case PORT_B: + bit = SDE_PORTB_HOTPLUG_CPT; + break; + case PORT_C: + bit = SDE_PORTC_HOTPLUG_CPT; + break; + case PORT_D: + bit = SDE_PORTD_HOTPLUG_CPT; + break; + default: + return true; + } + } + + return I915_READ(SDEISR) & bit; +} + static const char *state_string(bool enabled) { return enabled ? "on" : "off"; @@ -1125,8 +1108,8 @@ static void assert_fdi_tx(struct drm_i915_private *dev_priv, enum transcoder cpu_transcoder = intel_pipe_to_cpu_transcoder(dev_priv, pipe); - if (IS_HASWELL(dev_priv->dev)) { - /* On Haswell, DDI is used instead of FDI_TX_CTL */ + if (HAS_DDI(dev_priv->dev)) { + /* DDI does not have a specific FDI_TX register */ reg = TRANS_DDI_FUNC_CTL(cpu_transcoder); val = I915_READ(reg); cur_state = !!(val & TRANS_DDI_FUNC_ENABLE); @@ -1170,7 +1153,7 @@ static void assert_fdi_tx_pll_enabled(struct drm_i915_private *dev_priv, return; /* On Haswell, DDI ports are responsible for the FDI PLL setup */ - if (IS_HASWELL(dev_priv->dev)) + if (HAS_DDI(dev_priv->dev)) return; reg = FDI_TX_CTL(pipe); @@ -1231,9 +1214,15 @@ void assert_pipe(struct drm_i915_private *dev_priv, if (pipe == PIPE_A && dev_priv->quirks & QUIRK_PIPEA_FORCE) state = true; - reg = PIPECONF(cpu_transcoder); - val = I915_READ(reg); - cur_state = !!(val & PIPECONF_ENABLE); + if (IS_HASWELL(dev_priv->dev) && cpu_transcoder != TRANSCODER_EDP && + !(I915_READ(HSW_PWR_WELL_DRIVER) & HSW_PWR_WELL_ENABLE)) { + cur_state = false; + } else { + reg = PIPECONF(cpu_transcoder); + val = I915_READ(reg); + cur_state = !!(val & PIPECONF_ENABLE); + } + WARN(cur_state != state, "pipe %c assertion failure (expected %s, current %s)\n", pipe_name(pipe), state_string(state), state_string(cur_state)); @@ -1509,13 +1498,14 @@ static void intel_sbi_write(struct drm_i915_private *dev_priv, u16 reg, u32 value, enum intel_sbi_destination destination) { - unsigned long flags; u32 tmp; - spin_lock_irqsave(&dev_priv->dpio_lock, flags); - if (wait_for((I915_READ(SBI_CTL_STAT) & SBI_BUSY) == 0, 100)) { + WARN_ON(!mutex_is_locked(&dev_priv->dpio_lock)); + + if (wait_for((I915_READ(SBI_CTL_STAT) & SBI_BUSY) == 0, + 100)) { DRM_ERROR("timeout waiting for SBI to become ready\n"); - goto out_unlock; + return; } I915_WRITE(SBI_ADDR, (reg << 16)); @@ -1530,24 +1520,21 @@ intel_sbi_write(struct drm_i915_private *dev_priv, u16 reg, u32 value, if (wait_for((I915_READ(SBI_CTL_STAT) & (SBI_BUSY | SBI_RESPONSE_FAIL)) == 0, 100)) { DRM_ERROR("timeout waiting for SBI to complete write transaction\n"); - goto out_unlock; + return; } - -out_unlock: - spin_unlock_irqrestore(&dev_priv->dpio_lock, flags); } static u32 intel_sbi_read(struct drm_i915_private *dev_priv, u16 reg, enum intel_sbi_destination destination) { - unsigned long flags; u32 value = 0; + WARN_ON(!mutex_is_locked(&dev_priv->dpio_lock)); - spin_lock_irqsave(&dev_priv->dpio_lock, flags); - if (wait_for((I915_READ(SBI_CTL_STAT) & SBI_BUSY) == 0, 100)) { + if (wait_for((I915_READ(SBI_CTL_STAT) & SBI_BUSY) == 0, + 100)) { DRM_ERROR("timeout waiting for SBI to become ready\n"); - goto out_unlock; + return 0; } I915_WRITE(SBI_ADDR, (reg << 16)); @@ -1561,14 +1548,10 @@ intel_sbi_read(struct drm_i915_private *dev_priv, u16 reg, if (wait_for((I915_READ(SBI_CTL_STAT) & (SBI_BUSY | SBI_RESPONSE_FAIL)) == 0, 100)) { DRM_ERROR("timeout waiting for SBI to complete read transaction\n"); - goto out_unlock; + return 0; } - value = I915_READ(SBI_DATA); - -out_unlock: - spin_unlock_irqrestore(&dev_priv->dpio_lock, flags); - return value; + return I915_READ(SBI_DATA); } /** @@ -1700,8 +1683,8 @@ static void ironlake_enable_pch_transcoder(struct drm_i915_private *dev_priv, * make the BPC in transcoder be consistent with * that in pipeconf reg. */ - val &= ~PIPE_BPC_MASK; - val |= pipeconf_val & PIPE_BPC_MASK; + val &= ~PIPECONF_BPC_MASK; + val |= pipeconf_val & PIPECONF_BPC_MASK; } val &= ~TRANS_INTERLACE_MASK; @@ -1728,7 +1711,7 @@ static void lpt_enable_pch_transcoder(struct drm_i915_private *dev_priv, BUG_ON(dev_priv->info->gen < 5); /* FDI must be feeding us bits for PCH ports */ - assert_fdi_tx_enabled(dev_priv, cpu_transcoder); + assert_fdi_tx_enabled(dev_priv, (enum pipe) cpu_transcoder); assert_fdi_rx_enabled(dev_priv, TRANSCODER_A); /* Workaround: set timing override bit. */ @@ -1816,11 +1799,11 @@ static void intel_enable_pipe(struct drm_i915_private *dev_priv, enum pipe pipe, { enum transcoder cpu_transcoder = intel_pipe_to_cpu_transcoder(dev_priv, pipe); - enum transcoder pch_transcoder; + enum pipe pch_transcoder; int reg; u32 val; - if (IS_HASWELL(dev_priv->dev)) + if (HAS_PCH_LPT(dev_priv->dev)) pch_transcoder = TRANSCODER_A; else pch_transcoder = pipe; @@ -1836,7 +1819,8 @@ static void intel_enable_pipe(struct drm_i915_private *dev_priv, enum pipe pipe, if (pch_port) { /* if driving the PCH, we need FDI enabled */ assert_fdi_rx_pll_enabled(dev_priv, pch_transcoder); - assert_fdi_tx_pll_enabled(dev_priv, cpu_transcoder); + assert_fdi_tx_pll_enabled(dev_priv, + (enum pipe) cpu_transcoder); } /* FIXME: assert CPU port conditions for SNB+ */ } @@ -2242,10 +2226,6 @@ intel_finish_fb(struct drm_framebuffer *old_fb) bool was_interruptible = dev_priv->mm.interruptible; int ret; - wait_event(dev_priv->pending_flip_queue, - atomic_read(&dev_priv->mm.wedged) || - atomic_read(&obj->pending_flip) == 0); - /* Big Hammer, we also need to ensure that any pending * MI_WAIT_FOR_EVENT inside a user batch buffer on the * current scanout is retired before unpinning the old @@ -2350,43 +2330,6 @@ intel_pipe_set_base(struct drm_crtc *crtc, int x, int y, return 0; } -static void ironlake_set_pll_edp(struct drm_crtc *crtc, int clock) -{ - struct drm_device *dev = crtc->dev; - struct drm_i915_private *dev_priv = dev->dev_private; - u32 dpa_ctl; - - DRM_DEBUG_KMS("eDP PLL enable for clock %d\n", clock); - dpa_ctl = I915_READ(DP_A); - dpa_ctl &= ~DP_PLL_FREQ_MASK; - - if (clock < 200000) { - u32 temp; - dpa_ctl |= DP_PLL_FREQ_160MHZ; - /* workaround for 160Mhz: - 1) program 0x4600c bits 15:0 = 0x8124 - 2) program 0x46010 bit 0 = 1 - 3) program 0x46034 bit 24 = 1 - 4) program 0x64000 bit 14 = 1 - */ - temp = I915_READ(0x4600c); - temp &= 0xffff0000; - I915_WRITE(0x4600c, temp | 0x8124); - - temp = I915_READ(0x46010); - I915_WRITE(0x46010, temp | 1); - - temp = I915_READ(0x46034); - I915_WRITE(0x46034, temp | (1 << 24)); - } else { - dpa_ctl |= DP_PLL_FREQ_270MHZ; - } - I915_WRITE(DP_A, dpa_ctl); - - POSTING_READ(DP_A); - udelay(500); -} - static void intel_fdi_normal_train(struct drm_crtc *crtc) { struct drm_device *dev = crtc->dev; @@ -2815,7 +2758,7 @@ static void ironlake_fdi_pll_enable(struct intel_crtc *intel_crtc) temp = I915_READ(reg); temp &= ~((0x7 << 19) | (0x7 << 16)); temp |= (intel_crtc->fdi_lanes - 1) << 19; - temp |= (I915_READ(PIPECONF(pipe)) & PIPE_BPC_MASK) << 11; + temp |= (I915_READ(PIPECONF(pipe)) & PIPECONF_BPC_MASK) << 11; I915_WRITE(reg, temp | FDI_RX_PLL_ENABLE); POSTING_READ(reg); @@ -2828,18 +2771,14 @@ static void ironlake_fdi_pll_enable(struct intel_crtc *intel_crtc) POSTING_READ(reg); udelay(200); - /* On Haswell, the PLL configuration for ports and pipes is handled - * separately, as part of DDI setup */ - if (!IS_HASWELL(dev)) { - /* Enable CPU FDI TX PLL, always on for Ironlake */ - reg = FDI_TX_CTL(pipe); - temp = I915_READ(reg); - if ((temp & FDI_TX_PLL_ENABLE) == 0) { - I915_WRITE(reg, temp | FDI_TX_PLL_ENABLE); + /* Enable CPU FDI TX PLL, always on for Ironlake */ + reg = FDI_TX_CTL(pipe); + temp = I915_READ(reg); + if ((temp & FDI_TX_PLL_ENABLE) == 0) { + I915_WRITE(reg, temp | FDI_TX_PLL_ENABLE); - POSTING_READ(reg); - udelay(100); - } + POSTING_READ(reg); + udelay(100); } } @@ -2889,7 +2828,7 @@ static void ironlake_fdi_disable(struct drm_crtc *crtc) reg = FDI_RX_CTL(pipe); temp = I915_READ(reg); temp &= ~(0x7 << 16); - temp |= (I915_READ(PIPECONF(pipe)) & PIPE_BPC_MASK) << 11; + temp |= (I915_READ(PIPECONF(pipe)) & PIPECONF_BPC_MASK) << 11; I915_WRITE(reg, temp & ~FDI_RX_ENABLE); POSTING_READ(reg); @@ -2918,7 +2857,7 @@ static void ironlake_fdi_disable(struct drm_crtc *crtc) } /* BPC in FDI rx is consistent with that in PIPECONF */ temp &= ~(0x07 << 16); - temp |= (I915_READ(PIPECONF(pipe)) & PIPE_BPC_MASK) << 11; + temp |= (I915_READ(PIPECONF(pipe)) & PIPECONF_BPC_MASK) << 11; I915_WRITE(reg, temp); POSTING_READ(reg); @@ -2929,10 +2868,12 @@ static bool intel_crtc_has_pending_flip(struct drm_crtc *crtc) { struct drm_device *dev = crtc->dev; struct drm_i915_private *dev_priv = dev->dev_private; + struct intel_crtc *intel_crtc = to_intel_crtc(crtc); unsigned long flags; bool pending; - if (atomic_read(&dev_priv->mm.wedged)) + if (i915_reset_in_progress(&dev_priv->gpu_error) || + intel_crtc->reset_counter != atomic_read(&dev_priv->gpu_error.reset_counter)) return false; spin_lock_irqsave(&dev->event_lock, flags); @@ -2950,6 +2891,8 @@ static void intel_crtc_wait_for_pending_flips(struct drm_crtc *crtc) if (crtc->fb == NULL) return; + WARN_ON(waitqueue_active(&dev_priv->pending_flip_queue)); + wait_event(dev_priv->pending_flip_queue, !intel_crtc_has_pending_flip(crtc)); @@ -2992,6 +2935,8 @@ static void lpt_program_iclkip(struct drm_crtc *crtc) u32 divsel, phaseinc, auxdiv, phasedir = 0; u32 temp; + mutex_lock(&dev_priv->dpio_lock); + /* It is necessary to ungate the pixclk gate prior to programming * the divisors, and gate it back when it is done. */ @@ -3066,6 +3011,8 @@ static void lpt_program_iclkip(struct drm_crtc *crtc) udelay(24); I915_WRITE(PIXCLK_GATE, PIXCLK_GATE_UNGATE); + + mutex_unlock(&dev_priv->dpio_lock); } /* @@ -3146,7 +3093,7 @@ static void ironlake_pch_enable(struct drm_crtc *crtc) if (HAS_PCH_CPT(dev) && (intel_pipe_has_type(crtc, INTEL_OUTPUT_DISPLAYPORT) || intel_pipe_has_type(crtc, INTEL_OUTPUT_EDP))) { - u32 bpc = (I915_READ(PIPECONF(pipe)) & PIPE_BPC_MASK) >> 5; + u32 bpc = (I915_READ(PIPECONF(pipe)) & PIPECONF_BPC_MASK) >> 5; reg = TRANS_DP_CTL(pipe); temp = I915_READ(reg); temp &= ~(TRANS_DP_PORT_SEL_MASK | @@ -3623,7 +3570,7 @@ static void haswell_crtc_off(struct drm_crtc *crtc) /* Stop saying we're using TRANSCODER_EDP because some other CRTC might * start using it. */ - intel_crtc->cpu_transcoder = intel_crtc->pipe; + intel_crtc->cpu_transcoder = (enum transcoder) intel_crtc->pipe; intel_ddi_put_crtc_pll(crtc); } @@ -3664,6 +3611,11 @@ static void i9xx_crtc_enable(struct drm_crtc *crtc) intel_update_watermarks(dev); intel_enable_pll(dev_priv, pipe); + + for_each_encoder_on_crtc(dev, crtc, encoder) + if (encoder->pre_enable) + encoder->pre_enable(encoder); + intel_enable_pipe(dev_priv, pipe, false); intel_enable_plane(dev_priv, plane, pipe); @@ -3686,6 +3638,7 @@ static void i9xx_crtc_disable(struct drm_crtc *crtc) struct intel_encoder *encoder; int pipe = intel_crtc->pipe; int plane = intel_crtc->plane; + u32 pctl; if (!intel_crtc->active) @@ -3705,6 +3658,13 @@ static void i9xx_crtc_disable(struct drm_crtc *crtc) intel_disable_plane(dev_priv, plane, pipe); intel_disable_pipe(dev_priv, pipe); + + /* Disable pannel fitter if it is on this pipe. */ + pctl = I915_READ(PFIT_CONTROL); + if ((pctl & PFIT_ENABLE) && + ((pctl & PFIT_PIPE_MASK) >> PFIT_PIPE_SHIFT) == pipe) + I915_WRITE(PFIT_CONTROL, 0); + intel_disable_pll(dev_priv, pipe); intel_crtc->active = false; @@ -3767,19 +3727,17 @@ void intel_crtc_update_dpms(struct drm_crtc *crtc) intel_crtc_update_sarea(crtc, enable); } -static void intel_crtc_noop(struct drm_crtc *crtc) -{ -} - static void intel_crtc_disable(struct drm_crtc *crtc) { struct drm_device *dev = crtc->dev; struct drm_connector *connector; struct drm_i915_private *dev_priv = dev->dev_private; + struct intel_crtc *intel_crtc = to_intel_crtc(crtc); /* crtc should still be enabled when we disable it. */ WARN_ON(!crtc->enabled); + intel_crtc->eld_vld = false; dev_priv->display.crtc_disable(crtc); intel_crtc_update_sarea(crtc, false); dev_priv->display.off(crtc); @@ -3817,10 +3775,6 @@ void intel_modeset_disable(struct drm_device *dev) } } -void intel_encoder_noop(struct drm_encoder *encoder) -{ -} - void intel_encoder_destroy(struct drm_encoder *encoder) { struct intel_encoder *intel_encoder = to_intel_encoder(encoder); @@ -4012,16 +3966,8 @@ static int i830_get_display_clock_speed(struct drm_device *dev) return 133000; } -struct fdi_m_n { - u32 tu; - u32 gmch_m; - u32 gmch_n; - u32 link_m; - u32 link_n; -}; - static void -fdi_reduce_ratio(u32 *num, u32 *den) +intel_reduce_ratio(uint32_t *num, uint32_t *den) { while (*num > 0xffffff || *den > 0xffffff) { *num >>= 1; @@ -4029,20 +3975,18 @@ fdi_reduce_ratio(u32 *num, u32 *den) } } -static void -ironlake_compute_m_n(int bits_per_pixel, int nlanes, int pixel_clock, - int link_clock, struct fdi_m_n *m_n) +void +intel_link_compute_m_n(int bits_per_pixel, int nlanes, + int pixel_clock, int link_clock, + struct intel_link_m_n *m_n) { - m_n->tu = 64; /* default size */ - - /* BUG_ON(pixel_clock > INT_MAX / 36); */ + m_n->tu = 64; m_n->gmch_m = bits_per_pixel * pixel_clock; m_n->gmch_n = link_clock * nlanes * 8; - fdi_reduce_ratio(&m_n->gmch_m, &m_n->gmch_n); - + intel_reduce_ratio(&m_n->gmch_m, &m_n->gmch_n); m_n->link_m = pixel_clock; m_n->link_n = link_clock; - fdi_reduce_ratio(&m_n->link_m, &m_n->link_n); + intel_reduce_ratio(&m_n->link_m, &m_n->link_n); } static inline bool intel_panel_use_ssc(struct drm_i915_private *dev_priv) @@ -4289,51 +4233,6 @@ static void i9xx_update_pll_dividers(struct drm_crtc *crtc, } } -static void intel_update_lvds(struct drm_crtc *crtc, intel_clock_t *clock, - struct drm_display_mode *adjusted_mode) -{ - struct drm_device *dev = crtc->dev; - struct drm_i915_private *dev_priv = dev->dev_private; - struct intel_crtc *intel_crtc = to_intel_crtc(crtc); - int pipe = intel_crtc->pipe; - u32 temp; - - temp = I915_READ(LVDS); - temp |= LVDS_PORT_EN | LVDS_A0A2_CLKA_POWER_UP; - if (pipe == 1) { - temp |= LVDS_PIPEB_SELECT; - } else { - temp &= ~LVDS_PIPEB_SELECT; - } - /* set the corresponsding LVDS_BORDER bit */ - temp |= dev_priv->lvds_border_bits; - /* Set the B0-B3 data pairs corresponding to whether we're going to - * set the DPLLs for dual-channel mode or not. - */ - if (clock->p2 == 7) - temp |= LVDS_B0B3_POWER_UP | LVDS_CLKB_POWER_UP; - else - temp &= ~(LVDS_B0B3_POWER_UP | LVDS_CLKB_POWER_UP); - - /* It would be nice to set 24 vs 18-bit mode (LVDS_A3_POWER_UP) - * appropriately here, but we need to look more thoroughly into how - * panels behave in the two modes. - */ - /* set the dithering flag on LVDS as needed */ - if (INTEL_INFO(dev)->gen >= 4) { - if (dev_priv->lvds_dither) - temp |= LVDS_ENABLE_DITHER; - else - temp &= ~LVDS_ENABLE_DITHER; - } - temp &= ~(LVDS_HSYNC_POLARITY | LVDS_VSYNC_POLARITY); - if (adjusted_mode->flags & DRM_MODE_FLAG_NHSYNC) - temp |= LVDS_HSYNC_POLARITY; - if (adjusted_mode->flags & DRM_MODE_FLAG_NVSYNC) - temp |= LVDS_VSYNC_POLARITY; - I915_WRITE(LVDS, temp); -} - static void vlv_update_pll(struct drm_crtc *crtc, struct drm_display_mode *mode, struct drm_display_mode *adjusted_mode, @@ -4349,6 +4248,8 @@ static void vlv_update_pll(struct drm_crtc *crtc, bool is_sdvo; u32 temp; + mutex_lock(&dev_priv->dpio_lock); + is_sdvo = intel_pipe_has_type(crtc, INTEL_OUTPUT_SDVO) || intel_pipe_has_type(crtc, INTEL_OUTPUT_HDMI); @@ -4432,6 +4333,8 @@ static void vlv_update_pll(struct drm_crtc *crtc, temp |= (1 << 21); intel_dpio_write(dev_priv, DPIO_DATA_CHANNEL2, temp); } + + mutex_unlock(&dev_priv->dpio_lock); } static void i9xx_update_pll(struct drm_crtc *crtc, @@ -4443,6 +4346,7 @@ static void i9xx_update_pll(struct drm_crtc *crtc, struct drm_device *dev = crtc->dev; struct drm_i915_private *dev_priv = dev->dev_private; struct intel_crtc *intel_crtc = to_intel_crtc(crtc); + struct intel_encoder *encoder; int pipe = intel_crtc->pipe; u32 dpll; bool is_sdvo; @@ -4511,12 +4415,9 @@ static void i9xx_update_pll(struct drm_crtc *crtc, POSTING_READ(DPLL(pipe)); udelay(150); - /* The LVDS pin pair needs to be on before the DPLLs are enabled. - * This is an exception to the general rule that mode_set doesn't turn - * things on. - */ - if (intel_pipe_has_type(crtc, INTEL_OUTPUT_LVDS)) - intel_update_lvds(crtc, clock, adjusted_mode); + for_each_encoder_on_crtc(dev, crtc, encoder) + if (encoder->pre_pll_enable) + encoder->pre_pll_enable(encoder); if (intel_pipe_has_type(crtc, INTEL_OUTPUT_DISPLAYPORT)) intel_dp_set_m_n(crtc, mode, adjusted_mode); @@ -4555,6 +4456,7 @@ static void i8xx_update_pll(struct drm_crtc *crtc, struct drm_device *dev = crtc->dev; struct drm_i915_private *dev_priv = dev->dev_private; struct intel_crtc *intel_crtc = to_intel_crtc(crtc); + struct intel_encoder *encoder; int pipe = intel_crtc->pipe; u32 dpll; @@ -4588,12 +4490,9 @@ static void i8xx_update_pll(struct drm_crtc *crtc, POSTING_READ(DPLL(pipe)); udelay(150); - /* The LVDS pin pair needs to be on before the DPLLs are enabled. - * This is an exception to the general rule that mode_set doesn't turn - * things on. - */ - if (intel_pipe_has_type(crtc, INTEL_OUTPUT_LVDS)) - intel_update_lvds(crtc, clock, adjusted_mode); + for_each_encoder_on_crtc(dev, crtc, encoder) + if (encoder->pre_pll_enable) + encoder->pre_pll_enable(encoder); I915_WRITE(DPLL(pipe), dpll); @@ -4783,10 +4682,10 @@ static int i9xx_crtc_mode_set(struct drm_crtc *crtc, } /* default to 8bpc */ - pipeconf &= ~(PIPECONF_BPP_MASK | PIPECONF_DITHER_EN); + pipeconf &= ~(PIPECONF_BPC_MASK | PIPECONF_DITHER_EN); if (is_dp) { if (adjusted_mode->private_flags & INTEL_MODE_DP_FORCE_6BPC) { - pipeconf |= PIPECONF_BPP_6 | + pipeconf |= PIPECONF_6BPC | PIPECONF_DITHER_EN | PIPECONF_DITHER_TYPE_SP; } @@ -4794,7 +4693,7 @@ static int i9xx_crtc_mode_set(struct drm_crtc *crtc, if (IS_VALLEYVIEW(dev) && intel_pipe_has_type(crtc, INTEL_OUTPUT_EDP)) { if (adjusted_mode->private_flags & INTEL_MODE_DP_FORCE_6BPC) { - pipeconf |= PIPECONF_BPP_6 | + pipeconf |= PIPECONF_6BPC | PIPECONF_ENABLE | I965_PIPECONF_ACTIVE; } @@ -4981,6 +4880,8 @@ static void lpt_init_pch_refclk(struct drm_device *dev) if (!has_vga) return; + mutex_lock(&dev_priv->dpio_lock); + /* XXX: Rip out SDV support once Haswell ships for real. */ if (IS_HASWELL(dev) && (dev->pci_device & 0xFF00) == 0x0C00) is_sdv = true; @@ -5123,6 +5024,8 @@ static void lpt_init_pch_refclk(struct drm_device *dev) tmp = intel_sbi_read(dev_priv, SBI_DBUFF0, SBI_ICLK); tmp |= SBI_DBUFF0_ENABLE; intel_sbi_write(dev_priv, SBI_DBUFF0, tmp, SBI_ICLK); + + mutex_unlock(&dev_priv->dpio_lock); } /* @@ -5177,19 +5080,19 @@ static void ironlake_set_pipeconf(struct drm_crtc *crtc, val = I915_READ(PIPECONF(pipe)); - val &= ~PIPE_BPC_MASK; + val &= ~PIPECONF_BPC_MASK; switch (intel_crtc->bpp) { case 18: - val |= PIPE_6BPC; + val |= PIPECONF_6BPC; break; case 24: - val |= PIPE_8BPC; + val |= PIPECONF_8BPC; break; case 30: - val |= PIPE_10BPC; + val |= PIPECONF_10BPC; break; case 36: - val |= PIPE_12BPC; + val |= PIPECONF_12BPC; break; default: /* Case prevented by intel_choose_pipe_bpp_dither. */ @@ -5206,10 +5109,80 @@ static void ironlake_set_pipeconf(struct drm_crtc *crtc, else val |= PIPECONF_PROGRESSIVE; + if (adjusted_mode->private_flags & INTEL_MODE_LIMITED_COLOR_RANGE) + val |= PIPECONF_COLOR_RANGE_SELECT; + else + val &= ~PIPECONF_COLOR_RANGE_SELECT; + I915_WRITE(PIPECONF(pipe), val); POSTING_READ(PIPECONF(pipe)); } +/* + * Set up the pipe CSC unit. + * + * Currently only full range RGB to limited range RGB conversion + * is supported, but eventually this should handle various + * RGB<->YCbCr scenarios as well. + */ +static void intel_set_pipe_csc(struct drm_crtc *crtc, + const struct drm_display_mode *adjusted_mode) +{ + struct drm_device *dev = crtc->dev; + struct drm_i915_private *dev_priv = dev->dev_private; + struct intel_crtc *intel_crtc = to_intel_crtc(crtc); + int pipe = intel_crtc->pipe; + uint16_t coeff = 0x7800; /* 1.0 */ + + /* + * TODO: Check what kind of values actually come out of the pipe + * with these coeff/postoff values and adjust to get the best + * accuracy. Perhaps we even need to take the bpc value into + * consideration. + */ + + if (adjusted_mode->private_flags & INTEL_MODE_LIMITED_COLOR_RANGE) + coeff = ((235 - 16) * (1 << 12) / 255) & 0xff8; /* 0.xxx... */ + + /* + * GY/GU and RY/RU should be the other way around according + * to BSpec, but reality doesn't agree. Just set them up in + * a way that results in the correct picture. + */ + I915_WRITE(PIPE_CSC_COEFF_RY_GY(pipe), coeff << 16); + I915_WRITE(PIPE_CSC_COEFF_BY(pipe), 0); + + I915_WRITE(PIPE_CSC_COEFF_RU_GU(pipe), coeff); + I915_WRITE(PIPE_CSC_COEFF_BU(pipe), 0); + + I915_WRITE(PIPE_CSC_COEFF_RV_GV(pipe), 0); + I915_WRITE(PIPE_CSC_COEFF_BV(pipe), coeff << 16); + + I915_WRITE(PIPE_CSC_PREOFF_HI(pipe), 0); + I915_WRITE(PIPE_CSC_PREOFF_ME(pipe), 0); + I915_WRITE(PIPE_CSC_PREOFF_LO(pipe), 0); + + if (INTEL_INFO(dev)->gen > 6) { + uint16_t postoff = 0; + + if (adjusted_mode->private_flags & INTEL_MODE_LIMITED_COLOR_RANGE) + postoff = (16 * (1 << 13) / 255) & 0x1fff; + + I915_WRITE(PIPE_CSC_POSTOFF_HI(pipe), postoff); + I915_WRITE(PIPE_CSC_POSTOFF_ME(pipe), postoff); + I915_WRITE(PIPE_CSC_POSTOFF_LO(pipe), postoff); + + I915_WRITE(PIPE_CSC_MODE(pipe), 0); + } else { + uint32_t mode = CSC_MODE_YUV_TO_RGB; + + if (adjusted_mode->private_flags & INTEL_MODE_LIMITED_COLOR_RANGE) + mode |= CSC_BLACK_SCREEN_OFFSET; + + I915_WRITE(PIPE_CSC_MODE(pipe), mode); + } +} + static void haswell_set_pipeconf(struct drm_crtc *crtc, struct drm_display_mode *adjusted_mode, bool dither) @@ -5400,7 +5373,7 @@ static void ironlake_set_m_n(struct drm_crtc *crtc, struct intel_crtc *intel_crtc = to_intel_crtc(crtc); enum transcoder cpu_transcoder = intel_crtc->cpu_transcoder; struct intel_encoder *intel_encoder, *edp_encoder = NULL; - struct fdi_m_n m_n = {0}; + struct intel_link_m_n m_n = {0}; int target_clock, pixel_multiplier, lane, link_bw; bool is_dp = false, is_cpu_edp = false; @@ -5452,8 +5425,7 @@ static void ironlake_set_m_n(struct drm_crtc *crtc, if (pixel_multiplier > 1) link_bw *= pixel_multiplier; - ironlake_compute_m_n(intel_crtc->bpp, lane, target_clock, link_bw, - &m_n); + intel_link_compute_m_n(intel_crtc->bpp, lane, target_clock, link_bw, &m_n); I915_WRITE(PIPE_DATA_M1(cpu_transcoder), TU_SIZE(m_n.tu) | m_n.gmch_m); I915_WRITE(PIPE_DATA_N1(cpu_transcoder), m_n.gmch_n); @@ -5506,7 +5478,7 @@ static uint32_t ironlake_compute_dpll(struct intel_crtc *intel_crtc, if (is_lvds) { if ((intel_panel_use_ssc(dev_priv) && dev_priv->lvds_ssc_freq == 100) || - (I915_READ(PCH_LVDS) & LVDS_CLKB_POWER_MASK) == LVDS_CLKB_POWER_UP) + intel_is_dual_link_lvds(dev)) factor = 25; } else if (is_sdvo && is_tv) factor = 20; @@ -5581,7 +5553,6 @@ static int ironlake_crtc_mode_set(struct drm_crtc *crtc, bool ok, has_reduced_clock = false; bool is_lvds = false, is_dp = false, is_cpu_edp = false; struct intel_encoder *encoder; - u32 temp; int ret; bool dither, fdi_config_ok; @@ -5645,54 +5616,12 @@ static int ironlake_crtc_mode_set(struct drm_crtc *crtc, } else intel_put_pch_pll(intel_crtc); - /* The LVDS pin pair needs to be on before the DPLLs are enabled. - * This is an exception to the general rule that mode_set doesn't turn - * things on. - */ - if (is_lvds) { - temp = I915_READ(PCH_LVDS); - temp |= LVDS_PORT_EN | LVDS_A0A2_CLKA_POWER_UP; - if (HAS_PCH_CPT(dev)) { - temp &= ~PORT_TRANS_SEL_MASK; - temp |= PORT_TRANS_SEL_CPT(pipe); - } else { - if (pipe == 1) - temp |= LVDS_PIPEB_SELECT; - else - temp &= ~LVDS_PIPEB_SELECT; - } - - /* set the corresponsding LVDS_BORDER bit */ - temp |= dev_priv->lvds_border_bits; - /* Set the B0-B3 data pairs corresponding to whether we're going to - * set the DPLLs for dual-channel mode or not. - */ - if (clock.p2 == 7) - temp |= LVDS_B0B3_POWER_UP | LVDS_CLKB_POWER_UP; - else - temp &= ~(LVDS_B0B3_POWER_UP | LVDS_CLKB_POWER_UP); - - /* It would be nice to set 24 vs 18-bit mode (LVDS_A3_POWER_UP) - * appropriately here, but we need to look more thoroughly into how - * panels behave in the two modes. - */ - temp &= ~(LVDS_HSYNC_POLARITY | LVDS_VSYNC_POLARITY); - if (adjusted_mode->flags & DRM_MODE_FLAG_NHSYNC) - temp |= LVDS_HSYNC_POLARITY; - if (adjusted_mode->flags & DRM_MODE_FLAG_NVSYNC) - temp |= LVDS_VSYNC_POLARITY; - I915_WRITE(PCH_LVDS, temp); - } - - if (is_dp && !is_cpu_edp) { + if (is_dp && !is_cpu_edp) intel_dp_set_m_n(crtc, mode, adjusted_mode); - } else { - /* For non-DP output, clear any trans DP clock recovery setting.*/ - I915_WRITE(TRANSDATA_M1(pipe), 0); - I915_WRITE(TRANSDATA_N1(pipe), 0); - I915_WRITE(TRANSDPLINK_M1(pipe), 0); - I915_WRITE(TRANSDPLINK_N1(pipe), 0); - } + + for_each_encoder_on_crtc(dev, crtc, encoder) + if (encoder->pre_pll_enable) + encoder->pre_pll_enable(encoder); if (intel_crtc->pch_pll) { I915_WRITE(intel_crtc->pch_pll->pll_reg, dpll); @@ -5727,9 +5656,6 @@ static int ironlake_crtc_mode_set(struct drm_crtc *crtc, fdi_config_ok = ironlake_check_fdi_lanes(intel_crtc); - if (is_cpu_edp) - ironlake_set_pll_edp(crtc, adjusted_mode->clock); - ironlake_set_pipeconf(crtc, adjusted_mode, dither); intel_wait_for_vblank(dev, pipe); @@ -5747,6 +5673,35 @@ static int ironlake_crtc_mode_set(struct drm_crtc *crtc, return fdi_config_ok ? ret : -EINVAL; } +static void haswell_modeset_global_resources(struct drm_device *dev) +{ + struct drm_i915_private *dev_priv = dev->dev_private; + bool enable = false; + struct intel_crtc *crtc; + struct intel_encoder *encoder; + + list_for_each_entry(crtc, &dev->mode_config.crtc_list, base.head) { + if (crtc->pipe != PIPE_A && crtc->base.enabled) + enable = true; + /* XXX: Should check for edp transcoder here, but thanks to init + * sequence that's not yet available. Just in case desktop eDP + * on PORT D is possible on haswell, too. */ + } + + list_for_each_entry(encoder, &dev->mode_config.encoder_list, + base.head) { + if (encoder->type != INTEL_OUTPUT_EDP && + encoder->connectors_active) + enable = true; + } + + /* Even the eDP panel fitter is outside the always-on well. */ + if (dev_priv->pch_pf_size) + enable = true; + + intel_set_power_well(dev, enable); +} + static int haswell_crtc_mode_set(struct drm_crtc *crtc, struct drm_display_mode *mode, struct drm_display_mode *adjusted_mode, @@ -5759,20 +5714,13 @@ static int haswell_crtc_mode_set(struct drm_crtc *crtc, int pipe = intel_crtc->pipe; int plane = intel_crtc->plane; int num_connectors = 0; - intel_clock_t clock, reduced_clock; - u32 dpll = 0, fp = 0, fp2 = 0; - bool ok, has_reduced_clock = false; - bool is_lvds = false, is_dp = false, is_cpu_edp = false; + bool is_dp = false, is_cpu_edp = false; struct intel_encoder *encoder; - u32 temp; int ret; bool dither; for_each_encoder_on_crtc(dev, crtc, encoder) { switch (encoder->type) { - case INTEL_OUTPUT_LVDS: - is_lvds = true; - break; case INTEL_OUTPUT_DISPLAYPORT: is_dp = true; break; @@ -5786,11 +5734,6 @@ static int haswell_crtc_mode_set(struct drm_crtc *crtc, num_connectors++; } - if (is_cpu_edp) - intel_crtc->cpu_transcoder = TRANSCODER_EDP; - else - intel_crtc->cpu_transcoder = pipe; - /* We are not sure yet this won't happen. */ WARN(!HAS_PCH_LPT(dev), "Unexpected PCH type %d\n", INTEL_PCH_TYPE(dev)); @@ -5806,147 +5749,32 @@ static int haswell_crtc_mode_set(struct drm_crtc *crtc, if (!intel_ddi_pll_mode_set(crtc, adjusted_mode->clock)) return -EINVAL; - if (HAS_PCH_IBX(dev) || HAS_PCH_CPT(dev)) { - ok = ironlake_compute_clocks(crtc, adjusted_mode, &clock, - &has_reduced_clock, - &reduced_clock); - if (!ok) { - DRM_ERROR("Couldn't find PLL settings for mode!\n"); - return -EINVAL; - } - } - /* Ensure that the cursor is valid for the new mode before changing... */ intel_crtc_update_cursor(crtc, true); /* determine panel color depth */ dither = intel_choose_pipe_bpp_dither(crtc, fb, &intel_crtc->bpp, adjusted_mode); - if (is_lvds && dev_priv->lvds_dither) - dither = true; DRM_DEBUG_KMS("Mode for pipe %d:\n", pipe); drm_mode_debug_printmodeline(mode); - if (HAS_PCH_IBX(dev) || HAS_PCH_CPT(dev)) { - fp = clock.n << 16 | clock.m1 << 8 | clock.m2; - if (has_reduced_clock) - fp2 = reduced_clock.n << 16 | reduced_clock.m1 << 8 | - reduced_clock.m2; - - dpll = ironlake_compute_dpll(intel_crtc, adjusted_mode, &clock, - fp); - - /* CPU eDP is the only output that doesn't need a PCH PLL of its - * own on pre-Haswell/LPT generation */ - if (!is_cpu_edp) { - struct intel_pch_pll *pll; - - pll = intel_get_pch_pll(intel_crtc, dpll, fp); - if (pll == NULL) { - DRM_DEBUG_DRIVER("failed to find PLL for pipe %d\n", - pipe); - return -EINVAL; - } - } else - intel_put_pch_pll(intel_crtc); - - /* The LVDS pin pair needs to be on before the DPLLs are - * enabled. This is an exception to the general rule that - * mode_set doesn't turn things on. - */ - if (is_lvds) { - temp = I915_READ(PCH_LVDS); - temp |= LVDS_PORT_EN | LVDS_A0A2_CLKA_POWER_UP; - if (HAS_PCH_CPT(dev)) { - temp &= ~PORT_TRANS_SEL_MASK; - temp |= PORT_TRANS_SEL_CPT(pipe); - } else { - if (pipe == 1) - temp |= LVDS_PIPEB_SELECT; - else - temp &= ~LVDS_PIPEB_SELECT; - } - - /* set the corresponsding LVDS_BORDER bit */ - temp |= dev_priv->lvds_border_bits; - /* Set the B0-B3 data pairs corresponding to whether - * we're going to set the DPLLs for dual-channel mode or - * not. - */ - if (clock.p2 == 7) - temp |= LVDS_B0B3_POWER_UP | LVDS_CLKB_POWER_UP; - else - temp &= ~(LVDS_B0B3_POWER_UP | - LVDS_CLKB_POWER_UP); - - /* It would be nice to set 24 vs 18-bit mode - * (LVDS_A3_POWER_UP) appropriately here, but we need to - * look more thoroughly into how panels behave in the - * two modes. - */ - temp &= ~(LVDS_HSYNC_POLARITY | LVDS_VSYNC_POLARITY); - if (adjusted_mode->flags & DRM_MODE_FLAG_NHSYNC) - temp |= LVDS_HSYNC_POLARITY; - if (adjusted_mode->flags & DRM_MODE_FLAG_NVSYNC) - temp |= LVDS_VSYNC_POLARITY; - I915_WRITE(PCH_LVDS, temp); - } - } - - if (is_dp && !is_cpu_edp) { + if (is_dp && !is_cpu_edp) intel_dp_set_m_n(crtc, mode, adjusted_mode); - } else { - if (HAS_PCH_IBX(dev) || HAS_PCH_CPT(dev)) { - /* For non-DP output, clear any trans DP clock recovery - * setting.*/ - I915_WRITE(TRANSDATA_M1(pipe), 0); - I915_WRITE(TRANSDATA_N1(pipe), 0); - I915_WRITE(TRANSDPLINK_M1(pipe), 0); - I915_WRITE(TRANSDPLINK_N1(pipe), 0); - } - } intel_crtc->lowfreq_avail = false; - if (HAS_PCH_IBX(dev) || HAS_PCH_CPT(dev)) { - if (intel_crtc->pch_pll) { - I915_WRITE(intel_crtc->pch_pll->pll_reg, dpll); - - /* Wait for the clocks to stabilize. */ - POSTING_READ(intel_crtc->pch_pll->pll_reg); - udelay(150); - - /* The pixel multiplier can only be updated once the - * DPLL is enabled and the clocks are stable. - * - * So write it again. - */ - I915_WRITE(intel_crtc->pch_pll->pll_reg, dpll); - } - - if (intel_crtc->pch_pll) { - if (is_lvds && has_reduced_clock && i915_powersave) { - I915_WRITE(intel_crtc->pch_pll->fp1_reg, fp2); - intel_crtc->lowfreq_avail = true; - } else { - I915_WRITE(intel_crtc->pch_pll->fp1_reg, fp); - } - } - } intel_set_pipe_timings(intel_crtc, mode, adjusted_mode); if (!is_dp || is_cpu_edp) ironlake_set_m_n(crtc, mode, adjusted_mode); - if (HAS_PCH_IBX(dev) || HAS_PCH_CPT(dev)) - if (is_cpu_edp) - ironlake_set_pll_edp(crtc, adjusted_mode->clock); - haswell_set_pipeconf(crtc, adjusted_mode, dither); + intel_set_pipe_csc(crtc, adjusted_mode); + /* Set up the display plane register */ - I915_WRITE(DSPCNTR(plane), DISPPLANE_GAMMA_ENABLE); + I915_WRITE(DSPCNTR(plane), DISPPLANE_GAMMA_ENABLE | DISPPLANE_PIPE_CSC_ENABLE); POSTING_READ(DSPCNTR(plane)); ret = intel_pipe_set_base(crtc, x, y, fb); @@ -5972,6 +5800,11 @@ static int intel_crtc_mode_set(struct drm_crtc *crtc, int pipe = intel_crtc->pipe; int ret; + if (IS_HASWELL(dev) && intel_pipe_has_type(crtc, INTEL_OUTPUT_EDP)) + intel_crtc->cpu_transcoder = TRANSCODER_EDP; + else + intel_crtc->cpu_transcoder = pipe; + drm_vblank_pre_modeset(dev, pipe); ret = dev_priv->display.crtc_mode_set(crtc, mode, adjusted_mode, @@ -6068,6 +5901,7 @@ static void haswell_write_eld(struct drm_connector *connector, struct drm_i915_private *dev_priv = connector->dev->dev_private; uint8_t *eld = connector->eld; struct drm_device *dev = crtc->dev; + struct intel_crtc *intel_crtc = to_intel_crtc(crtc); uint32_t eldv; uint32_t i; int len; @@ -6109,6 +5943,7 @@ static void haswell_write_eld(struct drm_connector *connector, DRM_DEBUG_DRIVER("ELD on pipe %c\n", pipe_name(pipe)); eldv = AUDIO_ELD_VALID_A << (pipe * 4); + intel_crtc->eld_vld = true; if (intel_pipe_has_type(crtc, INTEL_OUTPUT_DISPLAYPORT)) { DRM_DEBUG_DRIVER("ELD: DisplayPort detected\n"); @@ -6344,6 +6179,8 @@ static void ivb_update_cursor(struct drm_crtc *crtc, u32 base) cntl &= ~(CURSOR_MODE | MCURSOR_GAMMA_ENABLE); cntl |= CURSOR_MODE_DISABLE; } + if (IS_HASWELL(dev)) + cntl |= CURSOR_PIPE_CSC_ENABLE; I915_WRITE(CURCNTR_IVB(pipe), cntl); intel_crtc->cursor_visible = visible; @@ -6700,6 +6537,8 @@ bool intel_get_load_detect_pipe(struct drm_connector *connector, if (encoder->crtc) { crtc = encoder->crtc; + mutex_lock(&crtc->mutex); + old->dpms_mode = connector->dpms; old->load_detect_temp = false; @@ -6729,6 +6568,7 @@ bool intel_get_load_detect_pipe(struct drm_connector *connector, return false; } + mutex_lock(&crtc->mutex); intel_encoder->new_crtc = to_intel_crtc(crtc); to_intel_connector(connector)->new_encoder = intel_encoder; @@ -6756,13 +6596,15 @@ bool intel_get_load_detect_pipe(struct drm_connector *connector, DRM_DEBUG_KMS("reusing fbdev for load-detection framebuffer\n"); if (IS_ERR(fb)) { DRM_DEBUG_KMS("failed to allocate framebuffer for load-detection\n"); + mutex_unlock(&crtc->mutex); return false; } - if (!intel_set_mode(crtc, mode, 0, 0, fb)) { + if (intel_set_mode(crtc, mode, 0, 0, fb)) { DRM_DEBUG_KMS("failed to set mode on load-detect pipe\n"); if (old->release_fb) old->release_fb->funcs->destroy(old->release_fb); + mutex_unlock(&crtc->mutex); return false; } @@ -6777,27 +6619,31 @@ void intel_release_load_detect_pipe(struct drm_connector *connector, struct intel_encoder *intel_encoder = intel_attached_encoder(connector); struct drm_encoder *encoder = &intel_encoder->base; + struct drm_crtc *crtc = encoder->crtc; DRM_DEBUG_KMS("[CONNECTOR:%d:%s], [ENCODER:%d:%s]\n", connector->base.id, drm_get_connector_name(connector), encoder->base.id, drm_get_encoder_name(encoder)); if (old->load_detect_temp) { - struct drm_crtc *crtc = encoder->crtc; - to_intel_connector(connector)->new_encoder = NULL; intel_encoder->new_crtc = NULL; intel_set_mode(crtc, NULL, 0, 0, NULL); - if (old->release_fb) - old->release_fb->funcs->destroy(old->release_fb); + if (old->release_fb) { + drm_framebuffer_unregister_private(old->release_fb); + drm_framebuffer_unreference(old->release_fb); + } + mutex_unlock(&crtc->mutex); return; } /* Switch crtc and encoder back off if necessary */ if (old->dpms_mode != DRM_MODE_DPMS_ON) connector->funcs->dpms(connector, old->dpms_mode); + + mutex_unlock(&crtc->mutex); } /* Returns the clock of the currently programmed mode of the given pipe. */ @@ -6993,11 +6839,6 @@ void intel_mark_busy(struct drm_device *dev) void intel_mark_idle(struct drm_device *dev) { -} - -void intel_mark_fb_busy(struct drm_i915_gem_object *obj) -{ - struct drm_device *dev = obj->base.dev; struct drm_crtc *crtc; if (!i915_powersave) @@ -7007,12 +6848,11 @@ void intel_mark_fb_busy(struct drm_i915_gem_object *obj) if (!crtc->fb) continue; - if (to_intel_framebuffer(crtc->fb)->obj == obj) - intel_increase_pllclock(crtc); + intel_decrease_pllclock(crtc); } } -void intel_mark_fb_idle(struct drm_i915_gem_object *obj) +void intel_mark_fb_busy(struct drm_i915_gem_object *obj) { struct drm_device *dev = obj->base.dev; struct drm_crtc *crtc; @@ -7025,7 +6865,7 @@ void intel_mark_fb_idle(struct drm_i915_gem_object *obj) continue; if (to_intel_framebuffer(crtc->fb)->obj == obj) - intel_decrease_pllclock(crtc); + intel_increase_pllclock(crtc); } } @@ -7109,9 +6949,7 @@ static void do_intel_finish_page_flip(struct drm_device *dev, obj = work->old_fb_obj; - atomic_clear_mask(1 << intel_crtc->plane, - &obj->pending_flip.counter); - wake_up(&dev_priv->pending_flip_queue); + wake_up_all(&dev_priv->pending_flip_queue); queue_work(dev_priv->wq, &work->work); @@ -7474,11 +7312,8 @@ static int intel_crtc_page_flip(struct drm_crtc *crtc, work->enable_stall_check = true; - /* Block clients from rendering to the new back buffer until - * the flip occurs and the object is no longer visible. - */ - atomic_add(1 << intel_crtc->plane, &work->old_fb_obj->pending_flip); atomic_inc(&intel_crtc->unpin_work_count); + intel_crtc->reset_counter = atomic_read(&dev_priv->gpu_error.reset_counter); ret = dev_priv->display.queue_flip(dev, crtc, fb, obj); if (ret) @@ -7494,7 +7329,6 @@ static int intel_crtc_page_flip(struct drm_crtc *crtc, cleanup_pending: atomic_dec(&intel_crtc->unpin_work_count); - atomic_sub(1 << intel_crtc->plane, &work->old_fb_obj->pending_flip); drm_gem_object_unreference(&work->old_fb_obj->base); drm_gem_object_unreference(&obj->base); mutex_unlock(&dev->struct_mutex); @@ -7514,7 +7348,6 @@ free_work: static struct drm_crtc_helper_funcs intel_helper_funcs = { .mode_set_base_atomic = intel_pipe_set_base_atomic, .load_lut = intel_crtc_load_lut, - .disable = intel_crtc_noop, }; bool intel_encoder_check_is_cloned(struct intel_encoder *encoder) @@ -7904,16 +7737,21 @@ intel_modeset_check_state(struct drm_device *dev) } } -bool intel_set_mode(struct drm_crtc *crtc, - struct drm_display_mode *mode, - int x, int y, struct drm_framebuffer *fb) +int intel_set_mode(struct drm_crtc *crtc, + struct drm_display_mode *mode, + int x, int y, struct drm_framebuffer *fb) { struct drm_device *dev = crtc->dev; drm_i915_private_t *dev_priv = dev->dev_private; - struct drm_display_mode *adjusted_mode, saved_mode, saved_hwmode; + struct drm_display_mode *adjusted_mode, *saved_mode, *saved_hwmode; struct intel_crtc *intel_crtc; unsigned disable_pipes, prepare_pipes, modeset_pipes; - bool ret = true; + int ret = 0; + + saved_mode = kmalloc(2 * sizeof(*saved_mode), GFP_KERNEL); + if (!saved_mode) + return -ENOMEM; + saved_hwmode = saved_mode + 1; intel_modeset_affected_pipes(crtc, &modeset_pipes, &prepare_pipes, &disable_pipes); @@ -7924,8 +7762,8 @@ bool intel_set_mode(struct drm_crtc *crtc, for_each_intel_crtc_masked(dev, disable_pipes, intel_crtc) intel_crtc_disable(&intel_crtc->base); - saved_hwmode = crtc->hwmode; - saved_mode = crtc->mode; + *saved_hwmode = crtc->hwmode; + *saved_mode = crtc->mode; /* Hack: Because we don't (yet) support global modeset on multiple * crtcs, we don't keep track of the new mode for more than one crtc. @@ -7936,7 +7774,8 @@ bool intel_set_mode(struct drm_crtc *crtc, if (modeset_pipes) { adjusted_mode = intel_modeset_adjusted_mode(crtc, mode); if (IS_ERR(adjusted_mode)) { - return false; + ret = PTR_ERR(adjusted_mode); + goto out; } } @@ -7962,11 +7801,11 @@ bool intel_set_mode(struct drm_crtc *crtc, * on the DPLL. */ for_each_intel_crtc_masked(dev, modeset_pipes, intel_crtc) { - ret = !intel_crtc_mode_set(&intel_crtc->base, - mode, adjusted_mode, - x, y, fb); - if (!ret) - goto done; + ret = intel_crtc_mode_set(&intel_crtc->base, + mode, adjusted_mode, + x, y, fb); + if (ret) + goto done; } /* Now enable the clocks, plane, pipe, and connectors that we set up. */ @@ -7987,16 +7826,23 @@ bool intel_set_mode(struct drm_crtc *crtc, /* FIXME: add subpixel order */ done: drm_mode_destroy(dev, adjusted_mode); - if (!ret && crtc->enabled) { - crtc->hwmode = saved_hwmode; - crtc->mode = saved_mode; + if (ret && crtc->enabled) { + crtc->hwmode = *saved_hwmode; + crtc->mode = *saved_mode; } else { intel_modeset_check_state(dev); } +out: + kfree(saved_mode); return ret; } +void intel_crtc_restore_mode(struct drm_crtc *crtc) +{ + intel_set_mode(crtc, &crtc->mode, crtc->x, crtc->y, crtc->fb); +} + #undef for_each_intel_crtc_masked static void intel_set_config_free(struct intel_set_config *config) @@ -8109,7 +7955,7 @@ intel_modeset_stage_output_state(struct drm_device *dev, struct intel_encoder *encoder; int count, ro; - /* The upper layers ensure that we either disabl a crtc or have a list + /* The upper layers ensure that we either disable a crtc or have a list * of connectors. For paranoia, double-check this. */ WARN_ON(!set->fb && (set->num_connectors != 0)); WARN_ON(set->fb && (set->num_connectors == 0)); @@ -8211,14 +8057,9 @@ static int intel_crtc_set_config(struct drm_mode_set *set) BUG_ON(!set->crtc); BUG_ON(!set->crtc->helper_private); - if (!set->mode) - set->fb = NULL; - - /* The fb helper likes to play gross jokes with ->mode_set_config. - * Unfortunately the crtc helper doesn't do much at all for this case, - * so we have to cope with this madness until the fb helper is fixed up. */ - if (set->fb && set->num_connectors == 0) - return 0; + /* Enforce sane interface api - has been abused by the fb helper. */ + BUG_ON(!set->mode && set->fb); + BUG_ON(set->fb && set->num_connectors == 0); if (set->fb) { DRM_DEBUG_KMS("[CRTC:%d] [FB:%d] #connectors=%d (x y) (%i %i)\n", @@ -8262,11 +8103,11 @@ static int intel_crtc_set_config(struct drm_mode_set *set) drm_mode_debug_printmodeline(set->mode); } - if (!intel_set_mode(set->crtc, set->mode, - set->x, set->y, set->fb)) { - DRM_ERROR("failed to set mode on [CRTC:%d]\n", - set->crtc->base.id); - ret = -EINVAL; + ret = intel_set_mode(set->crtc, set->mode, + set->x, set->y, set->fb); + if (ret) { + DRM_ERROR("failed to set mode on [CRTC:%d], err = %d\n", + set->crtc->base.id, ret); goto fail; } } else if (config->fb_changed) { @@ -8283,8 +8124,8 @@ fail: /* Try to restore the config */ if (config->mode_changed && - !intel_set_mode(save_set.crtc, save_set.mode, - save_set.x, save_set.y, save_set.fb)) + intel_set_mode(save_set.crtc, save_set.mode, + save_set.x, save_set.y, save_set.fb)) DRM_ERROR("failed to restore config after modeset failure\n"); out_config: @@ -8303,7 +8144,7 @@ static const struct drm_crtc_funcs intel_crtc_funcs = { static void intel_cpu_pll_init(struct drm_device *dev) { - if (IS_HASWELL(dev)) + if (HAS_DDI(dev)) intel_ddi_pll_init(dev); } @@ -8439,11 +8280,10 @@ static void intel_setup_outputs(struct drm_device *dev) I915_WRITE(PFIT_CONTROL, 0); } - if (!(IS_HASWELL(dev) && - (I915_READ(DDI_BUF_CTL(PORT_A)) & DDI_A_4_LANES))) + if (!(HAS_DDI(dev) && (I915_READ(DDI_BUF_CTL(PORT_A)) & DDI_A_4_LANES))) intel_crt_init(dev); - if (IS_HASWELL(dev)) { + if (HAS_DDI(dev)) { int found; /* Haswell uses DDI functions to detect digital outputs */ @@ -8490,23 +8330,18 @@ static void intel_setup_outputs(struct drm_device *dev) if (I915_READ(PCH_DP_D) & DP_DETECTED) intel_dp_init(dev, PCH_DP_D, PORT_D); } else if (IS_VALLEYVIEW(dev)) { - int found; - /* Check for built-in panel first. Shares lanes with HDMI on SDVOC */ - if (I915_READ(DP_C) & DP_DETECTED) - intel_dp_init(dev, DP_C, PORT_C); + if (I915_READ(VLV_DISPLAY_BASE + DP_C) & DP_DETECTED) + intel_dp_init(dev, VLV_DISPLAY_BASE + DP_C, PORT_C); - if (I915_READ(SDVOB) & PORT_DETECTED) { - /* SDVOB multiplex with HDMIB */ - found = intel_sdvo_init(dev, SDVOB, true); - if (!found) - intel_hdmi_init(dev, SDVOB, PORT_B); - if (!found && (I915_READ(DP_B) & DP_DETECTED)) - intel_dp_init(dev, DP_B, PORT_B); + if (I915_READ(VLV_DISPLAY_BASE + SDVOB) & PORT_DETECTED) { + intel_hdmi_init(dev, VLV_DISPLAY_BASE + SDVOB, PORT_B); + if (I915_READ(VLV_DISPLAY_BASE + DP_B) & DP_DETECTED) + intel_dp_init(dev, VLV_DISPLAY_BASE + DP_B, PORT_B); } - if (I915_READ(SDVOC) & PORT_DETECTED) - intel_hdmi_init(dev, SDVOC, PORT_C); + if (I915_READ(VLV_DISPLAY_BASE + SDVOC) & PORT_DETECTED) + intel_hdmi_init(dev, VLV_DISPLAY_BASE + SDVOC, PORT_C); } else if (SUPPORTS_DIGITAL_OUTPUTS(dev)) { bool found = false; @@ -8666,14 +8501,15 @@ int intel_framebuffer_init(struct drm_device *dev, if (mode_cmd->offsets[0] != 0) return -EINVAL; + drm_helper_mode_fill_fb_struct(&intel_fb->base, mode_cmd); + intel_fb->obj = obj; + ret = drm_framebuffer_init(dev, &intel_fb->base, &intel_fb_funcs); if (ret) { DRM_ERROR("framebuffer init failed %d\n", ret); return ret; } - drm_helper_mode_fill_fb_struct(&intel_fb->base, mode_cmd); - intel_fb->obj = obj; return 0; } @@ -8703,7 +8539,7 @@ static void intel_init_display(struct drm_device *dev) struct drm_i915_private *dev_priv = dev->dev_private; /* We always want a DPMS function */ - if (IS_HASWELL(dev)) { + if (HAS_DDI(dev)) { dev_priv->display.crtc_mode_set = haswell_crtc_mode_set; dev_priv->display.crtc_enable = haswell_crtc_enable; dev_priv->display.crtc_disable = haswell_crtc_disable; @@ -8765,8 +8601,9 @@ static void intel_init_display(struct drm_device *dev) } else if (IS_HASWELL(dev)) { dev_priv->display.fdi_link_train = hsw_fdi_link_train; dev_priv->display.write_eld = haswell_write_eld; - } else - dev_priv->display.update_wm = NULL; + dev_priv->display.modeset_global_resources = + haswell_modeset_global_resources; + } } else if (IS_G4X(dev)) { dev_priv->display.write_eld = g4x_write_eld; } @@ -8888,6 +8725,18 @@ static struct intel_quirk intel_quirks[] = { /* Acer Aspire 5734Z must invert backlight brightness */ { 0x2a42, 0x1025, 0x0459, quirk_invert_brightness }, + + /* Acer/eMachines G725 */ + { 0x2a42, 0x1025, 0x0210, quirk_invert_brightness }, + + /* Acer/eMachines e725 */ + { 0x2a42, 0x1025, 0x0212, quirk_invert_brightness }, + + /* Acer/Packard Bell NCL20 */ + { 0x2a42, 0x1025, 0x034b, quirk_invert_brightness }, + + /* Acer Aspire 4736Z */ + { 0x2a42, 0x1025, 0x0260, quirk_invert_brightness }, }; static void intel_init_quirks(struct drm_device *dev) @@ -8916,12 +8765,7 @@ static void i915_disable_vga(struct drm_device *dev) { struct drm_i915_private *dev_priv = dev->dev_private; u8 sr1; - u32 vga_reg; - - if (HAS_PCH_SPLIT(dev)) - vga_reg = CPU_VGACNTRL; - else - vga_reg = VGACNTRL; + u32 vga_reg = i915_vgacntrl_reg(dev); vga_get_uninterruptible(dev->pdev, VGA_RSRC_LEGACY_IO); outb(SR01, VGA_SR_INDEX); @@ -8936,10 +8780,7 @@ static void i915_disable_vga(struct drm_device *dev) void intel_modeset_init_hw(struct drm_device *dev) { - /* We attempt to init the necessary power wells early in the initialization - * time, so the subsystems that expect power to be enabled can work. - */ - intel_init_power_wells(dev); + intel_init_power_well(dev); intel_prepare_ddi(dev); @@ -8981,7 +8822,7 @@ void intel_modeset_init(struct drm_device *dev) dev->mode_config.max_width = 8192; dev->mode_config.max_height = 8192; } - dev->mode_config.fb_base = dev_priv->mm.gtt_base_addr; + dev->mode_config.fb_base = dev_priv->gtt.mappable_base; DRM_DEBUG_KMS("%d display pipe%s available.\n", dev_priv->num_pipe, dev_priv->num_pipe > 1 ? "s" : ""); @@ -8999,6 +8840,9 @@ void intel_modeset_init(struct drm_device *dev) /* Just disable it once at startup */ i915_disable_vga(dev); intel_setup_outputs(dev); + + /* Just in case the BIOS is doing something questionable. */ + intel_disable_fbc(dev); } static void @@ -9180,20 +9024,14 @@ static void intel_sanitize_encoder(struct intel_encoder *encoder) * the crtc fixup. */ } -static void i915_redisable_vga(struct drm_device *dev) +void i915_redisable_vga(struct drm_device *dev) { struct drm_i915_private *dev_priv = dev->dev_private; - u32 vga_reg; - - if (HAS_PCH_SPLIT(dev)) - vga_reg = CPU_VGACNTRL; - else - vga_reg = VGACNTRL; + u32 vga_reg = i915_vgacntrl_reg(dev); if (I915_READ(vga_reg) != VGA_DISP_DISABLE) { DRM_DEBUG_KMS("Something enabled VGA plane, disabling it\n"); - I915_WRITE(vga_reg, VGA_DISP_DISABLE); - POSTING_READ(vga_reg); + i915_disable_vga(dev); } } @@ -9209,7 +9047,7 @@ void intel_modeset_setup_hw_state(struct drm_device *dev, struct intel_encoder *encoder; struct intel_connector *connector; - if (IS_HASWELL(dev)) { + if (HAS_DDI(dev)) { tmp = I915_READ(TRANS_DDI_FUNC_CTL(TRANSCODER_EDP)); if (tmp & TRANS_DDI_FUNC_ENABLE) { @@ -9250,7 +9088,7 @@ void intel_modeset_setup_hw_state(struct drm_device *dev, crtc->active ? "enabled" : "disabled"); } - if (IS_HASWELL(dev)) + if (HAS_DDI(dev)) intel_ddi_setup_hw_pll_state(dev); list_for_each_entry(encoder, &dev->mode_config.encoder_list, @@ -9301,9 +9139,7 @@ void intel_modeset_setup_hw_state(struct drm_device *dev, if (force_restore) { for_each_pipe(pipe) { - crtc = to_intel_crtc(dev_priv->pipe_to_crtc_mapping[pipe]); - intel_set_mode(&crtc->base, &crtc->base.mode, - crtc->base.x, crtc->base.y, crtc->base.fb); + intel_crtc_restore_mode(dev_priv->pipe_to_crtc_mapping[pipe]); } i915_redisable_vga(dev); @@ -9367,6 +9203,8 @@ void intel_modeset_cleanup(struct drm_device *dev) flush_scheduled_work(); drm_mode_config_cleanup(dev); + + intel_cleanup_overlay(dev); } /* diff --git a/drivers/gpu/drm/i915/intel_dp.c b/drivers/gpu/drm/i915/intel_dp.c index fb3715b4b09d..31c0205685ab 100644 --- a/drivers/gpu/drm/i915/intel_dp.c +++ b/drivers/gpu/drm/i915/intel_dp.c @@ -148,15 +148,6 @@ intel_dp_max_link_bw(struct intel_dp *intel_dp) return max_link_bw; } -static int -intel_dp_link_clock(uint8_t link_bw) -{ - if (link_bw == DP_LINK_BW_2_7) - return 270000; - else - return 162000; -} - /* * The units on the numbers in the next two are... bizarre. Examples will * make it clearer; this one parallels an example in the eDP spec. @@ -191,7 +182,8 @@ intel_dp_adjust_dithering(struct intel_dp *intel_dp, struct drm_display_mode *mode, bool adjust_mode) { - int max_link_clock = intel_dp_link_clock(intel_dp_max_link_bw(intel_dp)); + int max_link_clock = + drm_dp_bw_code_to_link_rate(intel_dp_max_link_bw(intel_dp)); int max_lanes = drm_dp_max_lane_count(intel_dp->dpcd); int max_rate, mode_rate; @@ -330,6 +322,48 @@ intel_dp_check_edp(struct intel_dp *intel_dp) } } +static uint32_t +intel_dp_aux_wait_done(struct intel_dp *intel_dp, bool has_aux_irq) +{ + struct intel_digital_port *intel_dig_port = dp_to_dig_port(intel_dp); + struct drm_device *dev = intel_dig_port->base.base.dev; + struct drm_i915_private *dev_priv = dev->dev_private; + uint32_t ch_ctl = intel_dp->output_reg + 0x10; + uint32_t status; + bool done; + + if (IS_HASWELL(dev)) { + switch (intel_dig_port->port) { + case PORT_A: + ch_ctl = DPA_AUX_CH_CTL; + break; + case PORT_B: + ch_ctl = PCH_DPB_AUX_CH_CTL; + break; + case PORT_C: + ch_ctl = PCH_DPC_AUX_CH_CTL; + break; + case PORT_D: + ch_ctl = PCH_DPD_AUX_CH_CTL; + break; + default: + BUG(); + } + } + +#define C (((status = I915_READ_NOTRACE(ch_ctl)) & DP_AUX_CH_CTL_SEND_BUSY) == 0) + if (has_aux_irq) + done = wait_event_timeout(dev_priv->gmbus_wait_queue, C, 10); + else + done = wait_for_atomic(C, 10) == 0; + if (!done) + DRM_ERROR("dp aux hw did not signal timeout (has irq: %i)!\n", + has_aux_irq); +#undef C + + return status; +} + static int intel_dp_aux_ch(struct intel_dp *intel_dp, uint8_t *send, int send_bytes, @@ -341,11 +375,17 @@ intel_dp_aux_ch(struct intel_dp *intel_dp, struct drm_i915_private *dev_priv = dev->dev_private; uint32_t ch_ctl = output_reg + 0x10; uint32_t ch_data = ch_ctl + 4; - int i; - int recv_bytes; + int i, ret, recv_bytes; uint32_t status; uint32_t aux_clock_divider; int try, precharge; + bool has_aux_irq = INTEL_INFO(dev)->gen >= 5 && !IS_VALLEYVIEW(dev); + + /* dp aux is extremely sensitive to irq latency, hence request the + * lowest possible wakeup latency and so prevent the cpu from going into + * deep sleep states. + */ + pm_qos_update_request(&dev_priv->pm_qos, 0); if (IS_HASWELL(dev)) { switch (intel_dig_port->port) { @@ -379,7 +419,7 @@ intel_dp_aux_ch(struct intel_dp *intel_dp, * clock divider. */ if (is_cpu_edp(intel_dp)) { - if (IS_HASWELL(dev)) + if (HAS_DDI(dev)) aux_clock_divider = intel_ddi_get_cdclk_freq(dev_priv) >> 1; else if (IS_VALLEYVIEW(dev)) aux_clock_divider = 100; @@ -399,7 +439,7 @@ intel_dp_aux_ch(struct intel_dp *intel_dp, /* Try to wait for any previous AUX channel activity */ for (try = 0; try < 3; try++) { - status = I915_READ(ch_ctl); + status = I915_READ_NOTRACE(ch_ctl); if ((status & DP_AUX_CH_CTL_SEND_BUSY) == 0) break; msleep(1); @@ -408,7 +448,8 @@ intel_dp_aux_ch(struct intel_dp *intel_dp, if (try == 3) { WARN(1, "dp_aux_ch not started status 0x%08x\n", I915_READ(ch_ctl)); - return -EBUSY; + ret = -EBUSY; + goto out; } /* Must try at least 3 times according to DP spec */ @@ -421,6 +462,7 @@ intel_dp_aux_ch(struct intel_dp *intel_dp, /* Send the command and wait for it to complete */ I915_WRITE(ch_ctl, DP_AUX_CH_CTL_SEND_BUSY | + (has_aux_irq ? DP_AUX_CH_CTL_INTERRUPT : 0) | DP_AUX_CH_CTL_TIME_OUT_400us | (send_bytes << DP_AUX_CH_CTL_MESSAGE_SIZE_SHIFT) | (precharge << DP_AUX_CH_CTL_PRECHARGE_2US_SHIFT) | @@ -428,12 +470,8 @@ intel_dp_aux_ch(struct intel_dp *intel_dp, DP_AUX_CH_CTL_DONE | DP_AUX_CH_CTL_TIME_OUT_ERROR | DP_AUX_CH_CTL_RECEIVE_ERROR); - for (;;) { - status = I915_READ(ch_ctl); - if ((status & DP_AUX_CH_CTL_SEND_BUSY) == 0) - break; - udelay(100); - } + + status = intel_dp_aux_wait_done(intel_dp, has_aux_irq); /* Clear done status and any errors */ I915_WRITE(ch_ctl, @@ -451,7 +489,8 @@ intel_dp_aux_ch(struct intel_dp *intel_dp, if ((status & DP_AUX_CH_CTL_DONE) == 0) { DRM_ERROR("dp_aux_ch not done status 0x%08x\n", status); - return -EBUSY; + ret = -EBUSY; + goto out; } /* Check for timeout or receive error. @@ -459,14 +498,16 @@ intel_dp_aux_ch(struct intel_dp *intel_dp, */ if (status & DP_AUX_CH_CTL_RECEIVE_ERROR) { DRM_ERROR("dp_aux_ch receive error status 0x%08x\n", status); - return -EIO; + ret = -EIO; + goto out; } /* Timeouts occur when the device isn't connected, so they're * "normal" -- don't fill the kernel log with these */ if (status & DP_AUX_CH_CTL_TIME_OUT_ERROR) { DRM_DEBUG_KMS("dp_aux_ch timeout status 0x%08x\n", status); - return -ETIMEDOUT; + ret = -ETIMEDOUT; + goto out; } /* Unload any bytes sent back from the other side */ @@ -479,7 +520,11 @@ intel_dp_aux_ch(struct intel_dp *intel_dp, unpack_aux(I915_READ(ch_data + i), recv + i, recv_bytes - i); - return recv_bytes; + ret = recv_bytes; +out: + pm_qos_update_request(&dev_priv->pm_qos, PM_QOS_DEFAULT_VALUE); + + return ret; } /* Write data to the aux channel in native mode */ @@ -718,16 +763,35 @@ intel_dp_mode_fixup(struct drm_encoder *encoder, return false; bpp = adjusted_mode->private_flags & INTEL_MODE_DP_FORCE_6BPC ? 18 : 24; + + if (intel_dp->color_range_auto) { + /* + * See: + * CEA-861-E - 5.1 Default Encoding Parameters + * VESA DisplayPort Ver.1.2a - 5.1.1.1 Video Colorimetry + */ + if (bpp != 18 && drm_mode_cea_vic(adjusted_mode) > 1) + intel_dp->color_range = DP_COLOR_RANGE_16_235; + else + intel_dp->color_range = 0; + } + + if (intel_dp->color_range) + adjusted_mode->private_flags |= INTEL_MODE_LIMITED_COLOR_RANGE; + mode_rate = intel_dp_link_required(adjusted_mode->clock, bpp); for (clock = 0; clock <= max_clock; clock++) { for (lane_count = 1; lane_count <= max_lane_count; lane_count <<= 1) { - int link_avail = intel_dp_max_data_rate(intel_dp_link_clock(bws[clock]), lane_count); + int link_bw_clock = + drm_dp_bw_code_to_link_rate(bws[clock]); + int link_avail = intel_dp_max_data_rate(link_bw_clock, + lane_count); if (mode_rate <= link_avail) { intel_dp->link_bw = bws[clock]; intel_dp->lane_count = lane_count; - adjusted_mode->clock = intel_dp_link_clock(intel_dp->link_bw); + adjusted_mode->clock = link_bw_clock; DRM_DEBUG_KMS("DP link bw %02x lane " "count %d clock %d bpp %d\n", intel_dp->link_bw, intel_dp->lane_count, @@ -742,39 +806,6 @@ intel_dp_mode_fixup(struct drm_encoder *encoder, return false; } -struct intel_dp_m_n { - uint32_t tu; - uint32_t gmch_m; - uint32_t gmch_n; - uint32_t link_m; - uint32_t link_n; -}; - -static void -intel_reduce_ratio(uint32_t *num, uint32_t *den) -{ - while (*num > 0xffffff || *den > 0xffffff) { - *num >>= 1; - *den >>= 1; - } -} - -static void -intel_dp_compute_m_n(int bpp, - int nlanes, - int pixel_clock, - int link_clock, - struct intel_dp_m_n *m_n) -{ - m_n->tu = 64; - m_n->gmch_m = (pixel_clock * bpp) >> 3; - m_n->gmch_n = link_clock * nlanes; - intel_reduce_ratio(&m_n->gmch_m, &m_n->gmch_n); - m_n->link_m = pixel_clock; - m_n->link_n = link_clock; - intel_reduce_ratio(&m_n->link_m, &m_n->link_n); -} - void intel_dp_set_m_n(struct drm_crtc *crtc, struct drm_display_mode *mode, struct drm_display_mode *adjusted_mode) @@ -785,7 +816,7 @@ intel_dp_set_m_n(struct drm_crtc *crtc, struct drm_display_mode *mode, struct drm_i915_private *dev_priv = dev->dev_private; struct intel_crtc *intel_crtc = to_intel_crtc(crtc); int lane_count = 4; - struct intel_dp_m_n m_n; + struct intel_link_m_n m_n; int pipe = intel_crtc->pipe; enum transcoder cpu_transcoder = intel_crtc->cpu_transcoder; @@ -808,8 +839,8 @@ intel_dp_set_m_n(struct drm_crtc *crtc, struct drm_display_mode *mode, * the number of bytes_per_pixel post-LUT, which we always * set up for 8-bits of R/G/B, or 3 bytes total. */ - intel_dp_compute_m_n(intel_crtc->bpp, lane_count, - mode->clock, adjusted_mode->clock, &m_n); + intel_link_compute_m_n(intel_crtc->bpp, lane_count, + mode->clock, adjusted_mode->clock, &m_n); if (IS_HASWELL(dev)) { I915_WRITE(PIPE_DATA_M1(cpu_transcoder), @@ -851,6 +882,32 @@ void intel_dp_init_link_config(struct intel_dp *intel_dp) } } +static void ironlake_set_pll_edp(struct drm_crtc *crtc, int clock) +{ + struct drm_device *dev = crtc->dev; + struct drm_i915_private *dev_priv = dev->dev_private; + u32 dpa_ctl; + + DRM_DEBUG_KMS("eDP PLL enable for clock %d\n", clock); + dpa_ctl = I915_READ(DP_A); + dpa_ctl &= ~DP_PLL_FREQ_MASK; + + if (clock < 200000) { + /* For a long time we've carried around a ILK-DevA w/a for the + * 160MHz clock. If we're really unlucky, it's still required. + */ + DRM_DEBUG_KMS("160MHz cpu eDP clock, might need ilk devA w/a\n"); + dpa_ctl |= DP_PLL_FREQ_160MHZ; + } else { + dpa_ctl |= DP_PLL_FREQ_270MHZ; + } + + I915_WRITE(DP_A, dpa_ctl); + + POSTING_READ(DP_A); + udelay(500); +} + static void intel_dp_mode_set(struct drm_encoder *encoder, struct drm_display_mode *mode, struct drm_display_mode *adjusted_mode) @@ -926,7 +983,8 @@ intel_dp_mode_set(struct drm_encoder *encoder, struct drm_display_mode *mode, else intel_dp->DP |= DP_PLL_FREQ_270MHZ; } else if (!HAS_PCH_CPT(dev) || is_cpu_edp(intel_dp)) { - intel_dp->DP |= intel_dp->color_range; + if (!HAS_PCH_SPLIT(dev)) + intel_dp->DP |= intel_dp->color_range; if (adjusted_mode->flags & DRM_MODE_FLAG_PHSYNC) intel_dp->DP |= DP_SYNC_HS_HIGH; @@ -950,6 +1008,9 @@ intel_dp_mode_set(struct drm_encoder *encoder, struct drm_display_mode *mode, } else { intel_dp->DP |= DP_LINK_TRAIN_OFF_CPT; } + + if (is_cpu_edp(intel_dp)) + ironlake_set_pll_edp(crtc, adjusted_mode->clock); } #define IDLE_ON_MASK (PP_ON | 0 | PP_SEQUENCE_MASK | 0 | PP_SEQUENCE_STATE_MASK) @@ -1057,6 +1118,8 @@ static void ironlake_panel_vdd_off_sync(struct intel_dp *intel_dp) struct drm_i915_private *dev_priv = dev->dev_private; u32 pp; + WARN_ON(!mutex_is_locked(&dev->mode_config.mutex)); + if (!intel_dp->want_panel_vdd && ironlake_edp_have_panel_vdd(intel_dp)) { pp = ironlake_get_pp_control(dev_priv); pp &= ~EDP_FORCE_VDD; @@ -1543,7 +1606,7 @@ intel_get_adjust_train(struct intel_dp *intel_dp, uint8_t link_status[DP_LINK_ST } static uint32_t -intel_dp_signal_levels(uint8_t train_set) +intel_gen4_signal_levels(uint8_t train_set) { uint32_t signal_levels = 0; @@ -1641,7 +1704,7 @@ intel_gen7_edp_signal_levels(uint8_t train_set) /* Gen7.5's (HSW) DP voltage swing and pre-emphasis control */ static uint32_t -intel_dp_signal_levels_hsw(uint8_t train_set) +intel_hsw_signal_levels(uint8_t train_set) { int signal_levels = train_set & (DP_TRAIN_VOLTAGE_SWING_MASK | DP_TRAIN_PRE_EMPHASIS_MASK); @@ -1673,6 +1736,34 @@ intel_dp_signal_levels_hsw(uint8_t train_set) } } +/* Properly updates "DP" with the correct signal levels. */ +static void +intel_dp_set_signal_levels(struct intel_dp *intel_dp, uint32_t *DP) +{ + struct intel_digital_port *intel_dig_port = dp_to_dig_port(intel_dp); + struct drm_device *dev = intel_dig_port->base.base.dev; + uint32_t signal_levels, mask; + uint8_t train_set = intel_dp->train_set[0]; + + if (IS_HASWELL(dev)) { + signal_levels = intel_hsw_signal_levels(train_set); + mask = DDI_BUF_EMP_MASK; + } else if (IS_GEN7(dev) && is_cpu_edp(intel_dp) && !IS_VALLEYVIEW(dev)) { + signal_levels = intel_gen7_edp_signal_levels(train_set); + mask = EDP_LINK_TRAIN_VOL_EMP_MASK_IVB; + } else if (IS_GEN6(dev) && is_cpu_edp(intel_dp)) { + signal_levels = intel_gen6_edp_signal_levels(train_set); + mask = EDP_LINK_TRAIN_VOL_EMP_MASK_SNB; + } else { + signal_levels = intel_gen4_signal_levels(train_set); + mask = DP_VOLTAGE_MASK | DP_PRE_EMPHASIS_MASK; + } + + DRM_DEBUG_KMS("Using signal levels %08x\n", signal_levels); + + *DP = (*DP & ~mask) | signal_levels; +} + static bool intel_dp_set_link_train(struct intel_dp *intel_dp, uint32_t dp_reg_value, @@ -1696,14 +1787,18 @@ intel_dp_set_link_train(struct intel_dp *intel_dp, temp &= ~DP_TP_CTL_LINK_TRAIN_MASK; switch (dp_train_pat & DP_TRAINING_PATTERN_MASK) { case DP_TRAINING_PATTERN_DISABLE: - temp |= DP_TP_CTL_LINK_TRAIN_IDLE; - I915_WRITE(DP_TP_CTL(port), temp); - if (wait_for((I915_READ(DP_TP_STATUS(port)) & - DP_TP_STATUS_IDLE_DONE), 1)) - DRM_ERROR("Timed out waiting for DP idle patterns\n"); + if (port != PORT_A) { + temp |= DP_TP_CTL_LINK_TRAIN_IDLE; + I915_WRITE(DP_TP_CTL(port), temp); + + if (wait_for((I915_READ(DP_TP_STATUS(port)) & + DP_TP_STATUS_IDLE_DONE), 1)) + DRM_ERROR("Timed out waiting for DP idle patterns\n"); + + temp &= ~DP_TP_CTL_LINK_TRAIN_MASK; + } - temp &= ~DP_TP_CTL_LINK_TRAIN_MASK; temp |= DP_TP_CTL_LINK_TRAIN_NORMAL; break; @@ -1791,7 +1886,7 @@ intel_dp_start_link_train(struct intel_dp *intel_dp) int voltage_tries, loop_tries; uint32_t DP = intel_dp->DP; - if (IS_HASWELL(dev)) + if (HAS_DDI(dev)) intel_ddi_prepare_link_retrain(encoder); /* Write the link configuration data */ @@ -1809,24 +1904,8 @@ intel_dp_start_link_train(struct intel_dp *intel_dp) for (;;) { /* Use intel_dp->train_set[0] to set the voltage and pre emphasis values */ uint8_t link_status[DP_LINK_STATUS_SIZE]; - uint32_t signal_levels; - - if (IS_HASWELL(dev)) { - signal_levels = intel_dp_signal_levels_hsw( - intel_dp->train_set[0]); - DP = (DP & ~DDI_BUF_EMP_MASK) | signal_levels; - } else if (IS_GEN7(dev) && is_cpu_edp(intel_dp) && !IS_VALLEYVIEW(dev)) { - signal_levels = intel_gen7_edp_signal_levels(intel_dp->train_set[0]); - DP = (DP & ~EDP_LINK_TRAIN_VOL_EMP_MASK_IVB) | signal_levels; - } else if (IS_GEN6(dev) && is_cpu_edp(intel_dp)) { - signal_levels = intel_gen6_edp_signal_levels(intel_dp->train_set[0]); - DP = (DP & ~EDP_LINK_TRAIN_VOL_EMP_MASK_SNB) | signal_levels; - } else { - signal_levels = intel_dp_signal_levels(intel_dp->train_set[0]); - DP = (DP & ~(DP_VOLTAGE_MASK|DP_PRE_EMPHASIS_MASK)) | signal_levels; - } - DRM_DEBUG_KMS("training pattern 1 signal levels %08x\n", - signal_levels); + + intel_dp_set_signal_levels(intel_dp, &DP); /* Set training pattern 1 */ if (!intel_dp_set_link_train(intel_dp, DP, @@ -1882,7 +1961,6 @@ intel_dp_start_link_train(struct intel_dp *intel_dp) void intel_dp_complete_link_train(struct intel_dp *intel_dp) { - struct drm_device *dev = intel_dp_to_dev(intel_dp); bool channel_eq = false; int tries, cr_tries; uint32_t DP = intel_dp->DP; @@ -1892,8 +1970,6 @@ intel_dp_complete_link_train(struct intel_dp *intel_dp) cr_tries = 0; channel_eq = false; for (;;) { - /* Use intel_dp->train_set[0] to set the voltage and pre emphasis values */ - uint32_t signal_levels; uint8_t link_status[DP_LINK_STATUS_SIZE]; if (cr_tries > 5) { @@ -1902,19 +1978,7 @@ intel_dp_complete_link_train(struct intel_dp *intel_dp) break; } - if (IS_HASWELL(dev)) { - signal_levels = intel_dp_signal_levels_hsw(intel_dp->train_set[0]); - DP = (DP & ~DDI_BUF_EMP_MASK) | signal_levels; - } else if (IS_GEN7(dev) && is_cpu_edp(intel_dp) && !IS_VALLEYVIEW(dev)) { - signal_levels = intel_gen7_edp_signal_levels(intel_dp->train_set[0]); - DP = (DP & ~EDP_LINK_TRAIN_VOL_EMP_MASK_IVB) | signal_levels; - } else if (IS_GEN6(dev) && is_cpu_edp(intel_dp)) { - signal_levels = intel_gen6_edp_signal_levels(intel_dp->train_set[0]); - DP = (DP & ~EDP_LINK_TRAIN_VOL_EMP_MASK_SNB) | signal_levels; - } else { - signal_levels = intel_dp_signal_levels(intel_dp->train_set[0]); - DP = (DP & ~(DP_VOLTAGE_MASK|DP_PRE_EMPHASIS_MASK)) | signal_levels; - } + intel_dp_set_signal_levels(intel_dp, &DP); /* channel eq pattern */ if (!intel_dp_set_link_train(intel_dp, DP, @@ -1964,6 +2028,8 @@ intel_dp_link_down(struct intel_dp *intel_dp) struct intel_digital_port *intel_dig_port = dp_to_dig_port(intel_dp); struct drm_device *dev = intel_dig_port->base.base.dev; struct drm_i915_private *dev_priv = dev->dev_private; + struct intel_crtc *intel_crtc = + to_intel_crtc(intel_dig_port->base.base.crtc); uint32_t DP = intel_dp->DP; /* @@ -1981,7 +2047,7 @@ intel_dp_link_down(struct intel_dp *intel_dp) * intel_ddi_prepare_link_retrain will take care of redoing the link * train. */ - if (IS_HASWELL(dev)) + if (HAS_DDI(dev)) return; if (WARN_ON((I915_READ(intel_dp->output_reg) & DP_PORT_EN) == 0)) @@ -1998,7 +2064,8 @@ intel_dp_link_down(struct intel_dp *intel_dp) } POSTING_READ(intel_dp->output_reg); - msleep(17); + /* We don't really know why we're doing this */ + intel_wait_for_vblank(dev, intel_crtc->pipe); if (HAS_PCH_IBX(dev) && I915_READ(intel_dp->output_reg) & DP_PIPEB_SELECT) { @@ -2018,19 +2085,14 @@ intel_dp_link_down(struct intel_dp *intel_dp) /* Changes to enable or select take place the vblank * after being written. */ - if (crtc == NULL) { - /* We can arrive here never having been attached - * to a CRTC, for instance, due to inheriting - * random state from the BIOS. - * - * If the pipe is not running, play safe and - * wait for the clocks to stabilise before - * continuing. - */ + if (WARN_ON(crtc == NULL)) { + /* We should never try to disable a port without a crtc + * attached. For paranoia keep the code around for a + * bit. */ POSTING_READ(intel_dp->output_reg); msleep(50); } else - intel_wait_for_vblank(dev, to_intel_crtc(crtc)->pipe); + intel_wait_for_vblank(dev, intel_crtc->pipe); } DP &= ~DP_AUDIO_OUTPUT_ENABLE; @@ -2042,10 +2104,16 @@ intel_dp_link_down(struct intel_dp *intel_dp) static bool intel_dp_get_dpcd(struct intel_dp *intel_dp) { + char dpcd_hex_dump[sizeof(intel_dp->dpcd) * 3]; + if (intel_dp_aux_native_read_retry(intel_dp, 0x000, intel_dp->dpcd, sizeof(intel_dp->dpcd)) == 0) return false; /* aux transfer failed */ + hex_dump_to_buffer(intel_dp->dpcd, sizeof(intel_dp->dpcd), + 32, 1, dpcd_hex_dump, sizeof(dpcd_hex_dump), false); + DRM_DEBUG_KMS("DPCD: %s\n", dpcd_hex_dump); + if (intel_dp->dpcd[DP_DPCD_REV] == 0) return false; /* DPCD not present */ @@ -2206,6 +2274,8 @@ static enum drm_connector_status ironlake_dp_detect(struct intel_dp *intel_dp) { struct drm_device *dev = intel_dp_to_dev(intel_dp); + struct drm_i915_private *dev_priv = dev->dev_private; + struct intel_digital_port *intel_dig_port = dp_to_dig_port(intel_dp); enum drm_connector_status status; /* Can't disconnect eDP, but you can close the lid... */ @@ -2216,6 +2286,9 @@ ironlake_dp_detect(struct intel_dp *intel_dp) return status; } + if (!ibx_digital_port_connected(dev_priv, intel_dig_port)) + return connector_status_disconnected; + return intel_dp_detect_dpcd(intel_dp); } @@ -2224,17 +2297,18 @@ g4x_dp_detect(struct intel_dp *intel_dp) { struct drm_device *dev = intel_dp_to_dev(intel_dp); struct drm_i915_private *dev_priv = dev->dev_private; + struct intel_digital_port *intel_dig_port = dp_to_dig_port(intel_dp); uint32_t bit; - switch (intel_dp->output_reg) { - case DP_B: - bit = DPB_HOTPLUG_LIVE_STATUS; + switch (intel_dig_port->port) { + case PORT_B: + bit = PORTB_HOTPLUG_LIVE_STATUS; break; - case DP_C: - bit = DPC_HOTPLUG_LIVE_STATUS; + case PORT_C: + bit = PORTC_HOTPLUG_LIVE_STATUS; break; - case DP_D: - bit = DPD_HOTPLUG_LIVE_STATUS; + case PORT_D: + bit = PORTD_HOTPLUG_LIVE_STATUS; break; default: return connector_status_unknown; @@ -2290,13 +2364,6 @@ intel_dp_get_edid_modes(struct drm_connector *connector, struct i2c_adapter *ada return intel_ddc_get_modes(connector, adapter); } - -/** - * Uses CRT_HOTPLUG_EN and CRT_HOTPLUG_STAT to detect DP connection. - * - * \return true if DP port is connected. - * \return false if DP port is disconnected. - */ static enum drm_connector_status intel_dp_detect(struct drm_connector *connector, bool force) { @@ -2306,7 +2373,6 @@ intel_dp_detect(struct drm_connector *connector, bool force) struct drm_device *dev = connector->dev; enum drm_connector_status status; struct edid *edid = NULL; - char dpcd_hex_dump[sizeof(intel_dp->dpcd) * 3]; intel_dp->has_audio = false; @@ -2315,10 +2381,6 @@ intel_dp_detect(struct drm_connector *connector, bool force) else status = g4x_dp_detect(intel_dp); - hex_dump_to_buffer(intel_dp->dpcd, sizeof(intel_dp->dpcd), - 32, 1, dpcd_hex_dump, sizeof(dpcd_hex_dump), false); - DRM_DEBUG_KMS("DPCD: %s\n", dpcd_hex_dump); - if (status != connector_status_connected) return status; @@ -2419,10 +2481,21 @@ intel_dp_set_property(struct drm_connector *connector, } if (property == dev_priv->broadcast_rgb_property) { - if (val == !!intel_dp->color_range) - return 0; - - intel_dp->color_range = val ? DP_COLOR_RANGE_16_235 : 0; + switch (val) { + case INTEL_BROADCAST_RGB_AUTO: + intel_dp->color_range_auto = true; + break; + case INTEL_BROADCAST_RGB_FULL: + intel_dp->color_range_auto = false; + intel_dp->color_range = 0; + break; + case INTEL_BROADCAST_RGB_LIMITED: + intel_dp->color_range_auto = false; + intel_dp->color_range = DP_COLOR_RANGE_16_235; + break; + default: + return -EINVAL; + } goto done; } @@ -2445,11 +2518,8 @@ intel_dp_set_property(struct drm_connector *connector, return -EINVAL; done: - if (intel_encoder->base.crtc) { - struct drm_crtc *crtc = intel_encoder->base.crtc; - intel_set_mode(crtc, &crtc->mode, - crtc->x, crtc->y, crtc->fb); - } + if (intel_encoder->base.crtc) + intel_crtc_restore_mode(intel_encoder->base.crtc); return 0; } @@ -2491,7 +2561,6 @@ void intel_dp_encoder_destroy(struct drm_encoder *encoder) static const struct drm_encoder_helper_funcs intel_dp_helper_funcs = { .mode_fixup = intel_dp_mode_fixup, .mode_set = intel_dp_mode_set, - .disable = intel_encoder_noop, }; static const struct drm_connector_funcs intel_dp_connector_funcs = { @@ -2566,6 +2635,7 @@ intel_dp_add_properties(struct intel_dp *intel_dp, struct drm_connector *connect intel_attach_force_audio_property(connector); intel_attach_broadcast_rgb_property(connector); + intel_dp->color_range_auto = true; if (is_edp(intel_dp)) { drm_mode_create_scaling_mode_property(connector->dev); @@ -2755,7 +2825,7 @@ intel_dp_init_connector(struct intel_digital_port *intel_dig_port, intel_connector_attach_encoder(intel_connector, intel_encoder); drm_sysfs_connector_add(connector); - if (IS_HASWELL(dev)) + if (HAS_DDI(dev)) intel_connector->get_hw_state = intel_ddi_connector_get_hw_state; else intel_connector->get_hw_state = intel_connector_get_hw_state; @@ -2767,15 +2837,15 @@ intel_dp_init_connector(struct intel_digital_port *intel_dig_port, name = "DPDDC-A"; break; case PORT_B: - dev_priv->hotplug_supported_mask |= DPB_HOTPLUG_INT_STATUS; + dev_priv->hotplug_supported_mask |= PORTB_HOTPLUG_INT_STATUS; name = "DPDDC-B"; break; case PORT_C: - dev_priv->hotplug_supported_mask |= DPC_HOTPLUG_INT_STATUS; + dev_priv->hotplug_supported_mask |= PORTC_HOTPLUG_INT_STATUS; name = "DPDDC-C"; break; case PORT_D: - dev_priv->hotplug_supported_mask |= DPD_HOTPLUG_INT_STATUS; + dev_priv->hotplug_supported_mask |= PORTD_HOTPLUG_INT_STATUS; name = "DPDDC-D"; break; default: diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h index 8a1bd4a3ad0d..d282052aadd4 100644 --- a/drivers/gpu/drm/i915/intel_drv.h +++ b/drivers/gpu/drm/i915/intel_drv.h @@ -109,6 +109,11 @@ * timings in the mode to prevent the crtc fixup from overwriting them. * Currently only lvds needs that. */ #define INTEL_MODE_CRTC_TIMINGS_SET (0x20) +/* + * Set when limited 16-235 (as opposed to full 0-255) RGB color range is + * to be used. + */ +#define INTEL_MODE_LIMITED_COLOR_RANGE (0x40) static inline void intel_mode_set_pixel_multiplier(struct drm_display_mode *mode, @@ -153,6 +158,7 @@ struct intel_encoder { bool cloneable; bool connectors_active; void (*hot_plug)(struct intel_encoder *); + void (*pre_pll_enable)(struct intel_encoder *); void (*pre_enable)(struct intel_encoder *); void (*enable)(struct intel_encoder *); void (*disable)(struct intel_encoder *); @@ -205,6 +211,7 @@ struct intel_crtc { * some outputs connected to this crtc. */ bool active; + bool eld_vld; bool primary_disabled; /* is the crtc obscured by a plane? */ bool lowfreq_avail; struct intel_overlay *overlay; @@ -228,6 +235,9 @@ struct intel_crtc { /* We can share PLLs across outputs if the timings match */ struct intel_pch_pll *pch_pll; uint32_t ddi_pll_sel; + + /* reset counter value when the last flip was submitted */ + unsigned int reset_counter; }; struct intel_plane { @@ -283,6 +293,9 @@ struct cxsr_latency { #define DIP_LEN_AVI 13 #define DIP_AVI_PR_1 0 #define DIP_AVI_PR_2 1 +#define DIP_AVI_RGB_QUANT_RANGE_DEFAULT (0 << 2) +#define DIP_AVI_RGB_QUANT_RANGE_LIMITED (1 << 2) +#define DIP_AVI_RGB_QUANT_RANGE_FULL (2 << 2) #define DIP_TYPE_SPD 0x83 #define DIP_VERSION_SPD 0x1 @@ -337,9 +350,11 @@ struct intel_hdmi { u32 sdvox_reg; int ddc_bus; uint32_t color_range; + bool color_range_auto; bool has_hdmi_sink; bool has_audio; enum hdmi_force_audio force_audio; + bool rgb_quant_range_selectable; void (*write_infoframe)(struct drm_encoder *encoder, struct dip_infoframe *frame); void (*set_infoframes)(struct drm_encoder *encoder, @@ -356,6 +371,7 @@ struct intel_dp { bool has_audio; enum hdmi_force_audio force_audio; uint32_t color_range; + bool color_range_auto; uint8_t link_bw; uint8_t lane_count; uint8_t dpcd[DP_RECEIVER_CAP_SIZE]; @@ -377,6 +393,7 @@ struct intel_dp { struct intel_digital_port { struct intel_encoder base; enum port port; + u32 port_reversal; struct intel_dp dp; struct intel_hdmi hdmi; }; @@ -439,10 +456,10 @@ extern bool intel_sdvo_init(struct drm_device *dev, uint32_t sdvo_reg, extern void intel_dvo_init(struct drm_device *dev); extern void intel_tv_init(struct drm_device *dev); extern void intel_mark_busy(struct drm_device *dev); -extern void intel_mark_idle(struct drm_device *dev); extern void intel_mark_fb_busy(struct drm_i915_gem_object *obj); -extern void intel_mark_fb_idle(struct drm_i915_gem_object *obj); +extern void intel_mark_idle(struct drm_device *dev); extern bool intel_lvds_init(struct drm_device *dev); +extern bool intel_is_dual_link_lvds(struct drm_device *dev); extern void intel_dp_init(struct drm_device *dev, int output_reg, enum port port); extern void intel_dp_init_connector(struct intel_digital_port *intel_dig_port, @@ -502,12 +519,12 @@ struct intel_set_config { bool mode_changed; }; -extern bool intel_set_mode(struct drm_crtc *crtc, struct drm_display_mode *mode, - int x, int y, struct drm_framebuffer *old_fb); +extern int intel_set_mode(struct drm_crtc *crtc, struct drm_display_mode *mode, + int x, int y, struct drm_framebuffer *old_fb); extern void intel_modeset_disable(struct drm_device *dev); +extern void intel_crtc_restore_mode(struct drm_crtc *crtc); extern void intel_crtc_load_lut(struct drm_crtc *crtc); extern void intel_crtc_update_dpms(struct drm_crtc *crtc); -extern void intel_encoder_noop(struct drm_encoder *encoder); extern void intel_encoder_destroy(struct drm_encoder *encoder); extern void intel_encoder_dpms(struct intel_encoder *encoder, int mode); extern bool intel_encoder_check_is_cloned(struct intel_encoder *encoder); @@ -546,6 +563,9 @@ hdmi_to_dig_port(struct intel_hdmi *intel_hdmi) return container_of(intel_hdmi, struct intel_digital_port, hdmi); } +bool ibx_digital_port_connected(struct drm_i915_private *dev_priv, + struct intel_digital_port *port); + extern void intel_connector_attach_encoder(struct intel_connector *connector, struct intel_encoder *encoder); extern struct drm_encoder *intel_best_encoder(struct drm_connector *connector); @@ -589,6 +609,7 @@ extern int intel_framebuffer_init(struct drm_device *dev, struct drm_mode_fb_cmd2 *mode_cmd, struct drm_i915_gem_object *obj); extern int intel_fbdev_init(struct drm_device *dev); +extern void intel_fbdev_initial_config(struct drm_device *dev); extern void intel_fbdev_fini(struct drm_device *dev); extern void intel_fbdev_set_suspend(struct drm_device *dev, int state); extern void intel_prepare_page_flip(struct drm_device *dev, int plane); @@ -648,7 +669,8 @@ extern void intel_update_fbc(struct drm_device *dev); extern void intel_gpu_ips_init(struct drm_i915_private *dev_priv); extern void intel_gpu_ips_teardown(void); -extern void intel_init_power_wells(struct drm_device *dev); +extern void intel_init_power_well(struct drm_device *dev); +extern void intel_set_power_well(struct drm_device *dev, bool enable); extern void intel_enable_gt_powersave(struct drm_device *dev); extern void intel_disable_gt_powersave(struct drm_device *dev); extern void gen6_gt_check_fifodbg(struct drm_i915_private *dev_priv); diff --git a/drivers/gpu/drm/i915/intel_dvo.c b/drivers/gpu/drm/i915/intel_dvo.c index 15da99533e5b..00e70dbe82da 100644 --- a/drivers/gpu/drm/i915/intel_dvo.c +++ b/drivers/gpu/drm/i915/intel_dvo.c @@ -345,7 +345,6 @@ static void intel_dvo_destroy(struct drm_connector *connector) static const struct drm_encoder_helper_funcs intel_dvo_helper_funcs = { .mode_fixup = intel_dvo_mode_fixup, .mode_set = intel_dvo_mode_set, - .disable = intel_encoder_noop, }; static const struct drm_connector_funcs intel_dvo_connector_funcs = { diff --git a/drivers/gpu/drm/i915/intel_fb.c b/drivers/gpu/drm/i915/intel_fb.c index 7b30b5c2c4ee..981bdce3634e 100644 --- a/drivers/gpu/drm/i915/intel_fb.c +++ b/drivers/gpu/drm/i915/intel_fb.c @@ -57,9 +57,10 @@ static struct fb_ops intelfb_ops = { .fb_debug_leave = drm_fb_helper_debug_leave, }; -static int intelfb_create(struct intel_fbdev *ifbdev, +static int intelfb_create(struct drm_fb_helper *helper, struct drm_fb_helper_surface_size *sizes) { + struct intel_fbdev *ifbdev = (struct intel_fbdev *)helper; struct drm_device *dev = ifbdev->helper.dev; struct drm_i915_private *dev_priv = dev->dev_private; struct fb_info *info; @@ -83,7 +84,9 @@ static int intelfb_create(struct intel_fbdev *ifbdev, size = mode_cmd.pitches[0] * mode_cmd.height; size = ALIGN(size, PAGE_SIZE); - obj = i915_gem_alloc_object(dev, size); + obj = i915_gem_object_create_stolen(dev, size); + if (obj == NULL) + obj = i915_gem_alloc_object(dev, size); if (!obj) { DRM_ERROR("failed to allocate framebuffer\n"); ret = -ENOMEM; @@ -133,14 +136,13 @@ static int intelfb_create(struct intel_fbdev *ifbdev, goto out_unpin; } info->apertures->ranges[0].base = dev->mode_config.fb_base; - info->apertures->ranges[0].size = - dev_priv->mm.gtt->gtt_mappable_entries << PAGE_SHIFT; + info->apertures->ranges[0].size = dev_priv->gtt.mappable_end; info->fix.smem_start = dev->mode_config.fb_base + obj->gtt_offset; info->fix.smem_len = size; info->screen_base = - ioremap_wc(dev_priv->mm.gtt_base_addr + obj->gtt_offset, + ioremap_wc(dev_priv->gtt.mappable_base + obj->gtt_offset, size); if (!info->screen_base) { ret = -ENOSPC; @@ -153,6 +155,13 @@ static int intelfb_create(struct intel_fbdev *ifbdev, drm_fb_helper_fill_fix(info, fb->pitches[0], fb->depth); drm_fb_helper_fill_var(info, &ifbdev->helper, sizes->fb_width, sizes->fb_height); + /* If the object is shmemfs backed, it will have given us zeroed pages. + * If the object is stolen however, it will be full of whatever + * garbage was left in there. + */ + if (ifbdev->ifb.obj->stolen) + memset_io(info->screen_base, 0, info->screen_size); + /* Use default scratch pixmap (info->pixmap.flags = FB_PIXMAP_SYSTEM) */ DRM_DEBUG_KMS("allocated %dx%d fb: 0x%08x, bo %p\n", @@ -173,26 +182,10 @@ out: return ret; } -static int intel_fb_find_or_create_single(struct drm_fb_helper *helper, - struct drm_fb_helper_surface_size *sizes) -{ - struct intel_fbdev *ifbdev = (struct intel_fbdev *)helper; - int new_fb = 0; - int ret; - - if (!helper->fb) { - ret = intelfb_create(ifbdev, sizes); - if (ret) - return ret; - new_fb = 1; - } - return new_fb; -} - static struct drm_fb_helper_funcs intel_fb_helper_funcs = { .gamma_set = intel_crtc_fb_gamma_set, .gamma_get = intel_crtc_fb_gamma_get, - .fb_probe = intel_fb_find_or_create_single, + .fb_probe = intelfb_create, }; static void intel_fbdev_destroy(struct drm_device *dev, @@ -212,6 +205,7 @@ static void intel_fbdev_destroy(struct drm_device *dev, drm_fb_helper_fini(&ifbdev->helper); + drm_framebuffer_unregister_private(&ifb->base); drm_framebuffer_cleanup(&ifb->base); if (ifb->obj) { drm_gem_object_unreference_unlocked(&ifb->obj->base); @@ -241,10 +235,18 @@ int intel_fbdev_init(struct drm_device *dev) } drm_fb_helper_single_add_all_connectors(&ifbdev->helper); - drm_fb_helper_initial_config(&ifbdev->helper, 32); + return 0; } +void intel_fbdev_initial_config(struct drm_device *dev) +{ + drm_i915_private_t *dev_priv = dev->dev_private; + + /* Due to peculiar init order wrt to hpd handling this is separate. */ + drm_fb_helper_initial_config(&dev_priv->fbdev->helper, 32); +} + void intel_fbdev_fini(struct drm_device *dev) { drm_i915_private_t *dev_priv = dev->dev_private; @@ -280,7 +282,7 @@ void intel_fb_restore_mode(struct drm_device *dev) struct drm_mode_config *config = &dev->mode_config; struct drm_plane *plane; - mutex_lock(&dev->mode_config.mutex); + drm_modeset_lock_all(dev); ret = drm_fb_helper_restore_fbdev_mode(&dev_priv->fbdev->helper); if (ret) @@ -288,7 +290,8 @@ void intel_fb_restore_mode(struct drm_device *dev) /* Be sure to shut off any planes that may be active */ list_for_each_entry(plane, &config->plane_list, head) - plane->funcs->disable_plane(plane); + if (plane->enabled) + plane->funcs->disable_plane(plane); - mutex_unlock(&dev->mode_config.mutex); + drm_modeset_unlock_all(dev); } diff --git a/drivers/gpu/drm/i915/intel_hdmi.c b/drivers/gpu/drm/i915/intel_hdmi.c index 2ee9821b9d93..3ea0c8b6a00f 100644 --- a/drivers/gpu/drm/i915/intel_hdmi.c +++ b/drivers/gpu/drm/i915/intel_hdmi.c @@ -48,7 +48,7 @@ assert_hdmi_port_disabled(struct intel_hdmi *intel_hdmi) struct drm_i915_private *dev_priv = dev->dev_private; uint32_t enabled_bits; - enabled_bits = IS_HASWELL(dev) ? DDI_BUF_CTL_ENABLE : SDVO_ENABLE; + enabled_bits = HAS_DDI(dev) ? DDI_BUF_CTL_ENABLE : SDVO_ENABLE; WARN(I915_READ(intel_hdmi->sdvox_reg) & enabled_bits, "HDMI port enabled, expecting disabled\n"); @@ -331,6 +331,7 @@ static void intel_set_infoframe(struct drm_encoder *encoder, static void intel_hdmi_set_avi_infoframe(struct drm_encoder *encoder, struct drm_display_mode *adjusted_mode) { + struct intel_hdmi *intel_hdmi = enc_to_intel_hdmi(encoder); struct dip_infoframe avi_if = { .type = DIP_TYPE_AVI, .ver = DIP_VERSION_AVI, @@ -340,6 +341,13 @@ static void intel_hdmi_set_avi_infoframe(struct drm_encoder *encoder, if (adjusted_mode->flags & DRM_MODE_FLAG_DBLCLK) avi_if.body.avi.YQ_CN_PR |= DIP_AVI_PR_2; + if (intel_hdmi->rgb_quant_range_selectable) { + if (adjusted_mode->private_flags & INTEL_MODE_LIMITED_COLOR_RANGE) + avi_if.body.avi.ITC_EC_Q_SC |= DIP_AVI_RGB_QUANT_RANGE_LIMITED; + else + avi_if.body.avi.ITC_EC_Q_SC |= DIP_AVI_RGB_QUANT_RANGE_FULL; + } + avi_if.body.avi.VIC = drm_mode_cea_vic(adjusted_mode); intel_set_infoframe(encoder, &avi_if); @@ -364,7 +372,8 @@ static void g4x_set_infoframes(struct drm_encoder *encoder, struct drm_display_mode *adjusted_mode) { struct drm_i915_private *dev_priv = encoder->dev->dev_private; - struct intel_hdmi *intel_hdmi = enc_to_intel_hdmi(encoder); + struct intel_digital_port *intel_dig_port = enc_to_dig_port(encoder); + struct intel_hdmi *intel_hdmi = &intel_dig_port->hdmi; u32 reg = VIDEO_DIP_CTL; u32 val = I915_READ(reg); u32 port; @@ -391,11 +400,11 @@ static void g4x_set_infoframes(struct drm_encoder *encoder, return; } - switch (intel_hdmi->sdvox_reg) { - case SDVOB: + switch (intel_dig_port->port) { + case PORT_B: port = VIDEO_DIP_PORT_B; break; - case SDVOC: + case PORT_C: port = VIDEO_DIP_PORT_C; break; default: @@ -428,7 +437,8 @@ static void ibx_set_infoframes(struct drm_encoder *encoder, { struct drm_i915_private *dev_priv = encoder->dev->dev_private; struct intel_crtc *intel_crtc = to_intel_crtc(encoder->crtc); - struct intel_hdmi *intel_hdmi = enc_to_intel_hdmi(encoder); + struct intel_digital_port *intel_dig_port = enc_to_dig_port(encoder); + struct intel_hdmi *intel_hdmi = &intel_dig_port->hdmi; u32 reg = TVIDEO_DIP_CTL(intel_crtc->pipe); u32 val = I915_READ(reg); u32 port; @@ -447,14 +457,14 @@ static void ibx_set_infoframes(struct drm_encoder *encoder, return; } - switch (intel_hdmi->sdvox_reg) { - case HDMIB: + switch (intel_dig_port->port) { + case PORT_B: port = VIDEO_DIP_PORT_B; break; - case HDMIC: + case PORT_C: port = VIDEO_DIP_PORT_C; break; - case HDMID: + case PORT_D: port = VIDEO_DIP_PORT_D; break; default: @@ -766,6 +776,20 @@ bool intel_hdmi_mode_fixup(struct drm_encoder *encoder, const struct drm_display_mode *mode, struct drm_display_mode *adjusted_mode) { + struct intel_hdmi *intel_hdmi = enc_to_intel_hdmi(encoder); + + if (intel_hdmi->color_range_auto) { + /* See CEA-861-E - 5.1 Default Encoding Parameters */ + if (intel_hdmi->has_hdmi_sink && + drm_mode_cea_vic(adjusted_mode) > 1) + intel_hdmi->color_range = SDVO_COLOR_RANGE_16_235; + else + intel_hdmi->color_range = 0; + } + + if (intel_hdmi->color_range) + adjusted_mode->private_flags |= INTEL_MODE_LIMITED_COLOR_RANGE; + return true; } @@ -773,14 +797,15 @@ static bool g4x_hdmi_connected(struct intel_hdmi *intel_hdmi) { struct drm_device *dev = intel_hdmi_to_dev(intel_hdmi); struct drm_i915_private *dev_priv = dev->dev_private; + struct intel_digital_port *intel_dig_port = hdmi_to_dig_port(intel_hdmi); uint32_t bit; - switch (intel_hdmi->sdvox_reg) { - case SDVOB: - bit = HDMIB_HOTPLUG_LIVE_STATUS; + switch (intel_dig_port->port) { + case PORT_B: + bit = PORTB_HOTPLUG_LIVE_STATUS; break; - case SDVOC: - bit = HDMIC_HOTPLUG_LIVE_STATUS; + case PORT_C: + bit = PORTC_HOTPLUG_LIVE_STATUS; break; default: bit = 0; @@ -793,19 +818,25 @@ static bool g4x_hdmi_connected(struct intel_hdmi *intel_hdmi) static enum drm_connector_status intel_hdmi_detect(struct drm_connector *connector, bool force) { + struct drm_device *dev = connector->dev; struct intel_hdmi *intel_hdmi = intel_attached_hdmi(connector); struct intel_digital_port *intel_dig_port = hdmi_to_dig_port(intel_hdmi); struct intel_encoder *intel_encoder = &intel_dig_port->base; - struct drm_i915_private *dev_priv = connector->dev->dev_private; + struct drm_i915_private *dev_priv = dev->dev_private; struct edid *edid; enum drm_connector_status status = connector_status_disconnected; - if (IS_G4X(connector->dev) && !g4x_hdmi_connected(intel_hdmi)) + + if (IS_G4X(dev) && !g4x_hdmi_connected(intel_hdmi)) return status; + else if (HAS_PCH_SPLIT(dev) && + !ibx_digital_port_connected(dev_priv, intel_dig_port)) + return status; intel_hdmi->has_hdmi_sink = false; intel_hdmi->has_audio = false; + intel_hdmi->rgb_quant_range_selectable = false; edid = drm_get_edid(connector, intel_gmbus_get_adapter(dev_priv, intel_hdmi->ddc_bus)); @@ -817,6 +848,8 @@ intel_hdmi_detect(struct drm_connector *connector, bool force) intel_hdmi->has_hdmi_sink = drm_detect_hdmi_monitor(edid); intel_hdmi->has_audio = drm_detect_monitor_audio(edid); + intel_hdmi->rgb_quant_range_selectable = + drm_rgb_quant_range_selectable(edid); } kfree(edid); } @@ -902,21 +935,29 @@ intel_hdmi_set_property(struct drm_connector *connector, } if (property == dev_priv->broadcast_rgb_property) { - if (val == !!intel_hdmi->color_range) - return 0; - - intel_hdmi->color_range = val ? SDVO_COLOR_RANGE_16_235 : 0; + switch (val) { + case INTEL_BROADCAST_RGB_AUTO: + intel_hdmi->color_range_auto = true; + break; + case INTEL_BROADCAST_RGB_FULL: + intel_hdmi->color_range_auto = false; + intel_hdmi->color_range = 0; + break; + case INTEL_BROADCAST_RGB_LIMITED: + intel_hdmi->color_range_auto = false; + intel_hdmi->color_range = SDVO_COLOR_RANGE_16_235; + break; + default: + return -EINVAL; + } goto done; } return -EINVAL; done: - if (intel_dig_port->base.base.crtc) { - struct drm_crtc *crtc = intel_dig_port->base.base.crtc; - intel_set_mode(crtc, &crtc->mode, - crtc->x, crtc->y, crtc->fb); - } + if (intel_dig_port->base.base.crtc) + intel_crtc_restore_mode(intel_dig_port->base.base.crtc); return 0; } @@ -931,7 +972,6 @@ static void intel_hdmi_destroy(struct drm_connector *connector) static const struct drm_encoder_helper_funcs intel_hdmi_helper_funcs = { .mode_fixup = intel_hdmi_mode_fixup, .mode_set = intel_hdmi_mode_set, - .disable = intel_encoder_noop, }; static const struct drm_connector_funcs intel_hdmi_connector_funcs = { @@ -957,6 +997,7 @@ intel_hdmi_add_properties(struct intel_hdmi *intel_hdmi, struct drm_connector *c { intel_attach_force_audio_property(connector); intel_attach_broadcast_rgb_property(connector); + intel_hdmi->color_range_auto = true; } void intel_hdmi_init_connector(struct intel_digital_port *intel_dig_port, @@ -980,15 +1021,15 @@ void intel_hdmi_init_connector(struct intel_digital_port *intel_dig_port, switch (port) { case PORT_B: intel_hdmi->ddc_bus = GMBUS_PORT_DPB; - dev_priv->hotplug_supported_mask |= HDMIB_HOTPLUG_INT_STATUS; + dev_priv->hotplug_supported_mask |= PORTB_HOTPLUG_INT_STATUS; break; case PORT_C: intel_hdmi->ddc_bus = GMBUS_PORT_DPC; - dev_priv->hotplug_supported_mask |= HDMIC_HOTPLUG_INT_STATUS; + dev_priv->hotplug_supported_mask |= PORTC_HOTPLUG_INT_STATUS; break; case PORT_D: intel_hdmi->ddc_bus = GMBUS_PORT_DPD; - dev_priv->hotplug_supported_mask |= HDMID_HOTPLUG_INT_STATUS; + dev_priv->hotplug_supported_mask |= PORTD_HOTPLUG_INT_STATUS; break; case PORT_A: /* Internal port only for eDP. */ @@ -1013,7 +1054,7 @@ void intel_hdmi_init_connector(struct intel_digital_port *intel_dig_port, intel_hdmi->set_infoframes = cpt_set_infoframes; } - if (IS_HASWELL(dev)) + if (HAS_DDI(dev)) intel_connector->get_hw_state = intel_ddi_connector_get_hw_state; else intel_connector->get_hw_state = intel_connector_get_hw_state; diff --git a/drivers/gpu/drm/i915/intel_i2c.c b/drivers/gpu/drm/i915/intel_i2c.c index 3ef5af15b812..acf8aec9ada7 100644 --- a/drivers/gpu/drm/i915/intel_i2c.c +++ b/drivers/gpu/drm/i915/intel_i2c.c @@ -63,6 +63,7 @@ intel_i2c_reset(struct drm_device *dev) { struct drm_i915_private *dev_priv = dev->dev_private; I915_WRITE(dev_priv->gpio_mmio_base + GMBUS0, 0); + I915_WRITE(dev_priv->gpio_mmio_base + GMBUS4, 0); } static void intel_i2c_quirk_set(struct drm_i915_private *dev_priv, bool enable) @@ -202,6 +203,68 @@ intel_gpio_setup(struct intel_gmbus *bus, u32 pin) algo->data = bus; } +#define HAS_GMBUS_IRQ(dev) (INTEL_INFO(dev)->gen >= 4) +static int +gmbus_wait_hw_status(struct drm_i915_private *dev_priv, + u32 gmbus2_status, + u32 gmbus4_irq_en) +{ + int i; + int reg_offset = dev_priv->gpio_mmio_base; + u32 gmbus2 = 0; + DEFINE_WAIT(wait); + + /* Important: The hw handles only the first bit, so set only one! Since + * we also need to check for NAKs besides the hw ready/idle signal, we + * need to wake up periodically and check that ourselves. */ + I915_WRITE(GMBUS4 + reg_offset, gmbus4_irq_en); + + for (i = 0; i < msecs_to_jiffies(50) + 1; i++) { + prepare_to_wait(&dev_priv->gmbus_wait_queue, &wait, + TASK_UNINTERRUPTIBLE); + + gmbus2 = I915_READ_NOTRACE(GMBUS2 + reg_offset); + if (gmbus2 & (GMBUS_SATOER | gmbus2_status)) + break; + + schedule_timeout(1); + } + finish_wait(&dev_priv->gmbus_wait_queue, &wait); + + I915_WRITE(GMBUS4 + reg_offset, 0); + + if (gmbus2 & GMBUS_SATOER) + return -ENXIO; + if (gmbus2 & gmbus2_status) + return 0; + return -ETIMEDOUT; +} + +static int +gmbus_wait_idle(struct drm_i915_private *dev_priv) +{ + int ret; + int reg_offset = dev_priv->gpio_mmio_base; + +#define C ((I915_READ_NOTRACE(GMBUS2 + reg_offset) & GMBUS_ACTIVE) == 0) + + if (!HAS_GMBUS_IRQ(dev_priv->dev)) + return wait_for(C, 10); + + /* Important: The hw handles only the first bit, so set only one! */ + I915_WRITE(GMBUS4 + reg_offset, GMBUS_IDLE_EN); + + ret = wait_event_timeout(dev_priv->gmbus_wait_queue, C, 10); + + I915_WRITE(GMBUS4 + reg_offset, 0); + + if (ret) + return 0; + else + return -ETIMEDOUT; +#undef C +} + static int gmbus_xfer_read(struct drm_i915_private *dev_priv, struct i2c_msg *msg, u32 gmbus1_index) @@ -219,15 +282,11 @@ gmbus_xfer_read(struct drm_i915_private *dev_priv, struct i2c_msg *msg, while (len) { int ret; u32 val, loop = 0; - u32 gmbus2; - ret = wait_for((gmbus2 = I915_READ(GMBUS2 + reg_offset)) & - (GMBUS_SATOER | GMBUS_HW_RDY), - 50); + ret = gmbus_wait_hw_status(dev_priv, GMBUS_HW_RDY, + GMBUS_HW_RDY_EN); if (ret) - return -ETIMEDOUT; - if (gmbus2 & GMBUS_SATOER) - return -ENXIO; + return ret; val = I915_READ(GMBUS3 + reg_offset); do { @@ -261,7 +320,6 @@ gmbus_xfer_write(struct drm_i915_private *dev_priv, struct i2c_msg *msg) GMBUS_SLAVE_WRITE | GMBUS_SW_RDY); while (len) { int ret; - u32 gmbus2; val = loop = 0; do { @@ -270,13 +328,10 @@ gmbus_xfer_write(struct drm_i915_private *dev_priv, struct i2c_msg *msg) I915_WRITE(GMBUS3 + reg_offset, val); - ret = wait_for((gmbus2 = I915_READ(GMBUS2 + reg_offset)) & - (GMBUS_SATOER | GMBUS_HW_RDY), - 50); + ret = gmbus_wait_hw_status(dev_priv, GMBUS_HW_RDY, + GMBUS_HW_RDY_EN); if (ret) - return -ETIMEDOUT; - if (gmbus2 & GMBUS_SATOER) - return -ENXIO; + return ret; } return 0; } @@ -345,8 +400,6 @@ gmbus_xfer(struct i2c_adapter *adapter, I915_WRITE(GMBUS0 + reg_offset, bus->reg0); for (i = 0; i < num; i++) { - u32 gmbus2; - if (gmbus_is_index_read(msgs, i, num)) { ret = gmbus_xfer_index_read(dev_priv, &msgs[i]); i += 1; /* set i to the index of the read xfer */ @@ -361,13 +414,12 @@ gmbus_xfer(struct i2c_adapter *adapter, if (ret == -ENXIO) goto clear_err; - ret = wait_for((gmbus2 = I915_READ(GMBUS2 + reg_offset)) & - (GMBUS_SATOER | GMBUS_HW_WAIT_PHASE), - 50); + ret = gmbus_wait_hw_status(dev_priv, GMBUS_HW_WAIT_PHASE, + GMBUS_HW_WAIT_EN); + if (ret == -ENXIO) + goto clear_err; if (ret) goto timeout; - if (gmbus2 & GMBUS_SATOER) - goto clear_err; } /* Generate a STOP condition on the bus. Note that gmbus can't generata @@ -380,8 +432,7 @@ gmbus_xfer(struct i2c_adapter *adapter, * We will re-enable it at the start of the next xfer, * till then let it sleep. */ - if (wait_for((I915_READ(GMBUS2 + reg_offset) & GMBUS_ACTIVE) == 0, - 10)) { + if (gmbus_wait_idle(dev_priv)) { DRM_DEBUG_KMS("GMBUS [%s] timed out waiting for idle\n", adapter->name); ret = -ETIMEDOUT; @@ -405,8 +456,7 @@ clear_err: * it's slow responding and only answers on the 2nd retry. */ ret = -ENXIO; - if (wait_for((I915_READ(GMBUS2 + reg_offset) & GMBUS_ACTIVE) == 0, - 10)) { + if (gmbus_wait_idle(dev_priv)) { DRM_DEBUG_KMS("GMBUS [%s] timed out after NAK\n", adapter->name); ret = -ETIMEDOUT; @@ -465,10 +515,13 @@ int intel_setup_gmbus(struct drm_device *dev) if (HAS_PCH_SPLIT(dev)) dev_priv->gpio_mmio_base = PCH_GPIOA - GPIOA; + else if (IS_VALLEYVIEW(dev)) + dev_priv->gpio_mmio_base = VLV_DISPLAY_BASE; else dev_priv->gpio_mmio_base = 0; mutex_init(&dev_priv->gmbus_mutex); + init_waitqueue_head(&dev_priv->gmbus_wait_queue); for (i = 0; i < GMBUS_NUM_PORTS; i++) { struct intel_gmbus *bus = &dev_priv->gmbus[i]; diff --git a/drivers/gpu/drm/i915/intel_lvds.c b/drivers/gpu/drm/i915/intel_lvds.c index 17aee74258ad..3d1d97488cc9 100644 --- a/drivers/gpu/drm/i915/intel_lvds.c +++ b/drivers/gpu/drm/i915/intel_lvds.c @@ -51,7 +51,8 @@ struct intel_lvds_encoder { u32 pfit_control; u32 pfit_pgm_ratios; - bool pfit_dirty; + bool is_dual_link; + u32 reg; struct intel_lvds_connector *attached_connector; }; @@ -71,15 +72,10 @@ static bool intel_lvds_get_hw_state(struct intel_encoder *encoder, { struct drm_device *dev = encoder->base.dev; struct drm_i915_private *dev_priv = dev->dev_private; - u32 lvds_reg, tmp; - - if (HAS_PCH_SPLIT(dev)) { - lvds_reg = PCH_LVDS; - } else { - lvds_reg = LVDS; - } + struct intel_lvds_encoder *lvds_encoder = to_lvds_encoder(&encoder->base); + u32 tmp; - tmp = I915_READ(lvds_reg); + tmp = I915_READ(lvds_encoder->reg); if (!(tmp & LVDS_PORT_EN)) return false; @@ -92,6 +88,91 @@ static bool intel_lvds_get_hw_state(struct intel_encoder *encoder, return true; } +/* The LVDS pin pair needs to be on before the DPLLs are enabled. + * This is an exception to the general rule that mode_set doesn't turn + * things on. + */ +static void intel_pre_pll_enable_lvds(struct intel_encoder *encoder) +{ + struct intel_lvds_encoder *lvds_encoder = to_lvds_encoder(&encoder->base); + struct drm_device *dev = encoder->base.dev; + struct drm_i915_private *dev_priv = dev->dev_private; + struct intel_crtc *intel_crtc = to_intel_crtc(encoder->base.crtc); + struct drm_display_mode *fixed_mode = + lvds_encoder->attached_connector->base.panel.fixed_mode; + int pipe = intel_crtc->pipe; + u32 temp; + + temp = I915_READ(lvds_encoder->reg); + temp |= LVDS_PORT_EN | LVDS_A0A2_CLKA_POWER_UP; + + if (HAS_PCH_CPT(dev)) { + temp &= ~PORT_TRANS_SEL_MASK; + temp |= PORT_TRANS_SEL_CPT(pipe); + } else { + if (pipe == 1) { + temp |= LVDS_PIPEB_SELECT; + } else { + temp &= ~LVDS_PIPEB_SELECT; + } + } + + /* set the corresponsding LVDS_BORDER bit */ + temp |= dev_priv->lvds_border_bits; + /* Set the B0-B3 data pairs corresponding to whether we're going to + * set the DPLLs for dual-channel mode or not. + */ + if (lvds_encoder->is_dual_link) + temp |= LVDS_B0B3_POWER_UP | LVDS_CLKB_POWER_UP; + else + temp &= ~(LVDS_B0B3_POWER_UP | LVDS_CLKB_POWER_UP); + + /* It would be nice to set 24 vs 18-bit mode (LVDS_A3_POWER_UP) + * appropriately here, but we need to look more thoroughly into how + * panels behave in the two modes. + */ + + /* Set the dithering flag on LVDS as needed, note that there is no + * special lvds dither control bit on pch-split platforms, dithering is + * only controlled through the PIPECONF reg. */ + if (INTEL_INFO(dev)->gen == 4) { + if (dev_priv->lvds_dither) + temp |= LVDS_ENABLE_DITHER; + else + temp &= ~LVDS_ENABLE_DITHER; + } + temp &= ~(LVDS_HSYNC_POLARITY | LVDS_VSYNC_POLARITY); + if (fixed_mode->flags & DRM_MODE_FLAG_NHSYNC) + temp |= LVDS_HSYNC_POLARITY; + if (fixed_mode->flags & DRM_MODE_FLAG_NVSYNC) + temp |= LVDS_VSYNC_POLARITY; + + I915_WRITE(lvds_encoder->reg, temp); +} + +static void intel_pre_enable_lvds(struct intel_encoder *encoder) +{ + struct drm_device *dev = encoder->base.dev; + struct intel_lvds_encoder *enc = to_lvds_encoder(&encoder->base); + struct drm_i915_private *dev_priv = dev->dev_private; + + if (HAS_PCH_SPLIT(dev) || !enc->pfit_control) + return; + + /* + * Enable automatic panel scaling so that non-native modes + * fill the screen. The panel fitter should only be + * adjusted whilst the pipe is disabled, according to + * register description and PRM. + */ + DRM_DEBUG_KMS("applying panel-fitter: %x, %x\n", + enc->pfit_control, + enc->pfit_pgm_ratios); + + I915_WRITE(PFIT_PGM_RATIOS, enc->pfit_pgm_ratios); + I915_WRITE(PFIT_CONTROL, enc->pfit_control); +} + /** * Sets the power state for the panel. */ @@ -101,38 +182,20 @@ static void intel_enable_lvds(struct intel_encoder *encoder) struct intel_lvds_encoder *lvds_encoder = to_lvds_encoder(&encoder->base); struct intel_crtc *intel_crtc = to_intel_crtc(encoder->base.crtc); struct drm_i915_private *dev_priv = dev->dev_private; - u32 ctl_reg, lvds_reg, stat_reg; + u32 ctl_reg, stat_reg; if (HAS_PCH_SPLIT(dev)) { ctl_reg = PCH_PP_CONTROL; - lvds_reg = PCH_LVDS; stat_reg = PCH_PP_STATUS; } else { ctl_reg = PP_CONTROL; - lvds_reg = LVDS; stat_reg = PP_STATUS; } - I915_WRITE(lvds_reg, I915_READ(lvds_reg) | LVDS_PORT_EN); - - if (lvds_encoder->pfit_dirty) { - /* - * Enable automatic panel scaling so that non-native modes - * fill the screen. The panel fitter should only be - * adjusted whilst the pipe is disabled, according to - * register description and PRM. - */ - DRM_DEBUG_KMS("applying panel-fitter: %x, %x\n", - lvds_encoder->pfit_control, - lvds_encoder->pfit_pgm_ratios); - - I915_WRITE(PFIT_PGM_RATIOS, lvds_encoder->pfit_pgm_ratios); - I915_WRITE(PFIT_CONTROL, lvds_encoder->pfit_control); - lvds_encoder->pfit_dirty = false; - } + I915_WRITE(lvds_encoder->reg, I915_READ(lvds_encoder->reg) | LVDS_PORT_EN); I915_WRITE(ctl_reg, I915_READ(ctl_reg) | POWER_TARGET_ON); - POSTING_READ(lvds_reg); + POSTING_READ(lvds_encoder->reg); if (wait_for((I915_READ(stat_reg) & PP_ON) != 0, 1000)) DRM_ERROR("timed out waiting for panel to power on\n"); @@ -144,15 +207,13 @@ static void intel_disable_lvds(struct intel_encoder *encoder) struct drm_device *dev = encoder->base.dev; struct intel_lvds_encoder *lvds_encoder = to_lvds_encoder(&encoder->base); struct drm_i915_private *dev_priv = dev->dev_private; - u32 ctl_reg, lvds_reg, stat_reg; + u32 ctl_reg, stat_reg; if (HAS_PCH_SPLIT(dev)) { ctl_reg = PCH_PP_CONTROL; - lvds_reg = PCH_LVDS; stat_reg = PCH_PP_STATUS; } else { ctl_reg = PP_CONTROL; - lvds_reg = LVDS; stat_reg = PP_STATUS; } @@ -162,13 +223,8 @@ static void intel_disable_lvds(struct intel_encoder *encoder) if (wait_for((I915_READ(stat_reg) & PP_ON) == 0, 1000)) DRM_ERROR("timed out waiting for panel to power off\n"); - if (lvds_encoder->pfit_control) { - I915_WRITE(PFIT_CONTROL, 0); - lvds_encoder->pfit_dirty = true; - } - - I915_WRITE(lvds_reg, I915_READ(lvds_reg) & ~LVDS_PORT_EN); - POSTING_READ(lvds_reg); + I915_WRITE(lvds_encoder->reg, I915_READ(lvds_encoder->reg) & ~LVDS_PORT_EN); + POSTING_READ(lvds_encoder->reg); } static int intel_lvds_mode_valid(struct drm_connector *connector, @@ -406,7 +462,6 @@ out: pfit_pgm_ratios != lvds_encoder->pfit_pgm_ratios) { lvds_encoder->pfit_control = pfit_control; lvds_encoder->pfit_pgm_ratios = pfit_pgm_ratios; - lvds_encoder->pfit_dirty = true; } dev_priv->lvds_border_bits = border; @@ -492,13 +547,14 @@ static const struct dmi_system_id intel_no_modeset_on_lid[] = { }; /* - * Lid events. Note the use of 'modeset_on_lid': - * - we set it on lid close, and reset it on open + * Lid events. Note the use of 'modeset': + * - we set it to MODESET_ON_LID_OPEN on lid close, + * and set it to MODESET_DONE on open * - we use it as a "only once" bit (ie we ignore - * duplicate events where it was already properly - * set/reset) - * - the suspend/resume paths will also set it to - * zero, since they restore the mode ("lid open"). + * duplicate events where it was already properly set) + * - the suspend/resume paths will set it to + * MODESET_SUSPENDED and ignore the lid open event, + * because they restore the mode ("lid open"). */ static int intel_lid_notify(struct notifier_block *nb, unsigned long val, void *unused) @@ -512,6 +568,9 @@ static int intel_lid_notify(struct notifier_block *nb, unsigned long val, if (dev->switch_power_state != DRM_SWITCH_POWER_ON) return NOTIFY_OK; + mutex_lock(&dev_priv->modeset_restore_lock); + if (dev_priv->modeset_restore == MODESET_SUSPENDED) + goto exit; /* * check and update the status of LVDS connector after receiving * the LID nofication event. @@ -520,21 +579,24 @@ static int intel_lid_notify(struct notifier_block *nb, unsigned long val, /* Don't force modeset on machines where it causes a GPU lockup */ if (dmi_check_system(intel_no_modeset_on_lid)) - return NOTIFY_OK; + goto exit; if (!acpi_lid_open()) { - dev_priv->modeset_on_lid = 1; - return NOTIFY_OK; + /* do modeset on next lid open event */ + dev_priv->modeset_restore = MODESET_ON_LID_OPEN; + goto exit; } - if (!dev_priv->modeset_on_lid) - return NOTIFY_OK; - - dev_priv->modeset_on_lid = 0; + if (dev_priv->modeset_restore == MODESET_DONE) + goto exit; - mutex_lock(&dev->mode_config.mutex); + drm_modeset_lock_all(dev); intel_modeset_setup_hw_state(dev, true); - mutex_unlock(&dev->mode_config.mutex); + drm_modeset_unlock_all(dev); + + dev_priv->modeset_restore = MODESET_DONE; +exit: + mutex_unlock(&dev_priv->modeset_restore_lock); return NOTIFY_OK; } @@ -591,8 +653,7 @@ static int intel_lvds_set_property(struct drm_connector *connector, * If the CRTC is enabled, the display will be changed * according to the new panel fitting mode. */ - intel_set_mode(crtc, &crtc->mode, - crtc->x, crtc->y, crtc->fb); + intel_crtc_restore_mode(crtc); } } @@ -602,7 +663,6 @@ static int intel_lvds_set_property(struct drm_connector *connector, static const struct drm_encoder_helper_funcs intel_lvds_helper_funcs = { .mode_fixup = intel_lvds_mode_fixup, .mode_set = intel_lvds_mode_set, - .disable = intel_encoder_noop, }; static const struct drm_connector_helper_funcs intel_lvds_connector_helper_funcs = { @@ -895,6 +955,66 @@ static bool lvds_is_present_in_vbt(struct drm_device *dev, return false; } +static int intel_dual_link_lvds_callback(const struct dmi_system_id *id) +{ + DRM_INFO("Forcing lvds to dual link mode on %s\n", id->ident); + return 1; +} + +static const struct dmi_system_id intel_dual_link_lvds[] = { + { + .callback = intel_dual_link_lvds_callback, + .ident = "Apple MacBook Pro (Core i5/i7 Series)", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Apple Inc."), + DMI_MATCH(DMI_PRODUCT_NAME, "MacBookPro8,2"), + }, + }, + { } /* terminating entry */ +}; + +bool intel_is_dual_link_lvds(struct drm_device *dev) +{ + struct intel_encoder *encoder; + struct intel_lvds_encoder *lvds_encoder; + + list_for_each_entry(encoder, &dev->mode_config.encoder_list, + base.head) { + if (encoder->type == INTEL_OUTPUT_LVDS) { + lvds_encoder = to_lvds_encoder(&encoder->base); + + return lvds_encoder->is_dual_link; + } + } + + return false; +} + +static bool compute_is_dual_link_lvds(struct intel_lvds_encoder *lvds_encoder) +{ + struct drm_device *dev = lvds_encoder->base.base.dev; + unsigned int val; + struct drm_i915_private *dev_priv = dev->dev_private; + + /* use the module option value if specified */ + if (i915_lvds_channel_mode > 0) + return i915_lvds_channel_mode == 2; + + if (dmi_check_system(intel_dual_link_lvds)) + return true; + + /* BIOS should set the proper LVDS register value at boot, but + * in reality, it doesn't set the value when the lid is closed; + * we need to check "the value to be set" in VBT when LVDS + * register is uninitialized. + */ + val = I915_READ(lvds_encoder->reg); + if (!(val & ~(LVDS_PIPE_MASK | LVDS_DETECTED))) + val = dev_priv->bios_lvds_val; + + return (val & LVDS_CLKB_POWER_MASK) == LVDS_CLKB_POWER_UP; +} + static bool intel_lvds_supported(struct drm_device *dev) { /* With the introduction of the PCH we gained a dedicated @@ -980,6 +1100,8 @@ bool intel_lvds_init(struct drm_device *dev) DRM_MODE_ENCODER_LVDS); intel_encoder->enable = intel_enable_lvds; + intel_encoder->pre_enable = intel_pre_enable_lvds; + intel_encoder->pre_pll_enable = intel_pre_pll_enable_lvds; intel_encoder->disable = intel_disable_lvds; intel_encoder->get_hw_state = intel_lvds_get_hw_state; intel_connector->get_hw_state = intel_connector_get_hw_state; @@ -1001,6 +1123,12 @@ bool intel_lvds_init(struct drm_device *dev) connector->interlace_allowed = false; connector->doublescan_allowed = false; + if (HAS_PCH_SPLIT(dev)) { + lvds_encoder->reg = PCH_LVDS; + } else { + lvds_encoder->reg = LVDS; + } + /* create the scaling mode property */ drm_mode_create_scaling_mode_property(dev); drm_object_attach_property(&connector->base, @@ -1101,6 +1229,10 @@ bool intel_lvds_init(struct drm_device *dev) goto failed; out: + lvds_encoder->is_dual_link = compute_is_dual_link_lvds(lvds_encoder); + DRM_DEBUG_KMS("detected %s-link lvds configuration\n", + lvds_encoder->is_dual_link ? "dual" : "single"); + /* * Unlock registers and just * leave them unlocked diff --git a/drivers/gpu/drm/i915/intel_modes.c b/drivers/gpu/drm/i915/intel_modes.c index b00f1c83adce..0e860f39933d 100644 --- a/drivers/gpu/drm/i915/intel_modes.c +++ b/drivers/gpu/drm/i915/intel_modes.c @@ -28,7 +28,6 @@ #include <linux/fb.h> #include <drm/drm_edid.h> #include <drm/drmP.h> -#include <drm/drm_edid.h> #include "intel_drv.h" #include "i915_drv.h" @@ -101,8 +100,9 @@ intel_attach_force_audio_property(struct drm_connector *connector) } static const struct drm_prop_enum_list broadcast_rgb_names[] = { - { 0, "Full" }, - { 1, "Limited 16:235" }, + { INTEL_BROADCAST_RGB_AUTO, "Automatic" }, + { INTEL_BROADCAST_RGB_FULL, "Full" }, + { INTEL_BROADCAST_RGB_LIMITED, "Limited 16:235" }, }; void diff --git a/drivers/gpu/drm/i915/intel_opregion.c b/drivers/gpu/drm/i915/intel_opregion.c index 7741c22c934c..4d338740f2cb 100644 --- a/drivers/gpu/drm/i915/intel_opregion.c +++ b/drivers/gpu/drm/i915/intel_opregion.c @@ -347,7 +347,7 @@ static void intel_didl_outputs(struct drm_device *dev) int i = 0; handle = DEVICE_ACPI_HANDLE(&dev->pdev->dev); - if (!handle || ACPI_FAILURE(acpi_bus_get_device(handle, &acpi_dev))) + if (!handle || acpi_bus_get_device(handle, &acpi_dev)) return; if (acpi_is_video_device(acpi_dev)) diff --git a/drivers/gpu/drm/i915/intel_overlay.c b/drivers/gpu/drm/i915/intel_overlay.c index d7bc817f51a0..67a2501d519d 100644 --- a/drivers/gpu/drm/i915/intel_overlay.c +++ b/drivers/gpu/drm/i915/intel_overlay.c @@ -195,7 +195,7 @@ intel_overlay_map_regs(struct intel_overlay *overlay) if (OVERLAY_NEEDS_PHYSICAL(overlay->dev)) regs = (struct overlay_registers __iomem *)overlay->reg_bo->phys_obj->handle->vaddr; else - regs = io_mapping_map_wc(dev_priv->mm.gtt_mapping, + regs = io_mapping_map_wc(dev_priv->gtt.mappable, overlay->reg_bo->gtt_offset); return regs; @@ -1045,13 +1045,13 @@ int intel_overlay_put_image(struct drm_device *dev, void *data, } if (!(put_image_rec->flags & I915_OVERLAY_ENABLE)) { - mutex_lock(&dev->mode_config.mutex); + drm_modeset_lock_all(dev); mutex_lock(&dev->struct_mutex); ret = intel_overlay_switch_off(overlay); mutex_unlock(&dev->struct_mutex); - mutex_unlock(&dev->mode_config.mutex); + drm_modeset_unlock_all(dev); return ret; } @@ -1075,7 +1075,7 @@ int intel_overlay_put_image(struct drm_device *dev, void *data, goto out_free; } - mutex_lock(&dev->mode_config.mutex); + drm_modeset_lock_all(dev); mutex_lock(&dev->struct_mutex); if (new_bo->tiling_mode) { @@ -1157,7 +1157,7 @@ int intel_overlay_put_image(struct drm_device *dev, void *data, goto out_unlock; mutex_unlock(&dev->struct_mutex); - mutex_unlock(&dev->mode_config.mutex); + drm_modeset_unlock_all(dev); kfree(params); @@ -1165,7 +1165,7 @@ int intel_overlay_put_image(struct drm_device *dev, void *data, out_unlock: mutex_unlock(&dev->struct_mutex); - mutex_unlock(&dev->mode_config.mutex); + drm_modeset_unlock_all(dev); drm_gem_object_unreference_unlocked(&new_bo->base); out_free: kfree(params); @@ -1241,7 +1241,7 @@ int intel_overlay_attrs(struct drm_device *dev, void *data, return -ENODEV; } - mutex_lock(&dev->mode_config.mutex); + drm_modeset_lock_all(dev); mutex_lock(&dev->struct_mutex); ret = -EINVAL; @@ -1307,7 +1307,7 @@ int intel_overlay_attrs(struct drm_device *dev, void *data, ret = 0; out_unlock: mutex_unlock(&dev->struct_mutex); - mutex_unlock(&dev->mode_config.mutex); + drm_modeset_unlock_all(dev); return ret; } @@ -1333,8 +1333,10 @@ void intel_setup_overlay(struct drm_device *dev) overlay->dev = dev; - reg_bo = i915_gem_alloc_object(dev, PAGE_SIZE); - if (!reg_bo) + reg_bo = i915_gem_object_create_stolen(dev, PAGE_SIZE); + if (reg_bo == NULL) + reg_bo = i915_gem_alloc_object(dev, PAGE_SIZE); + if (reg_bo == NULL) goto out_free; overlay->reg_bo = reg_bo; @@ -1432,7 +1434,7 @@ intel_overlay_map_regs_atomic(struct intel_overlay *overlay) regs = (struct overlay_registers __iomem *) overlay->reg_bo->phys_obj->handle->vaddr; else - regs = io_mapping_map_atomic_wc(dev_priv->mm.gtt_mapping, + regs = io_mapping_map_atomic_wc(dev_priv->gtt.mappable, overlay->reg_bo->gtt_offset); return regs; diff --git a/drivers/gpu/drm/i915/intel_panel.c b/drivers/gpu/drm/i915/intel_panel.c index bee8cb6108a7..a3730e0289e5 100644 --- a/drivers/gpu/drm/i915/intel_panel.c +++ b/drivers/gpu/drm/i915/intel_panel.c @@ -321,6 +321,9 @@ void intel_panel_enable_backlight(struct drm_device *dev, if (dev_priv->backlight_level == 0) dev_priv->backlight_level = intel_panel_get_max_backlight(dev); + dev_priv->backlight_enabled = true; + intel_panel_actually_set_backlight(dev, dev_priv->backlight_level); + if (INTEL_INFO(dev)->gen >= 4) { uint32_t reg, tmp; @@ -356,12 +359,12 @@ void intel_panel_enable_backlight(struct drm_device *dev, } set_level: - /* Call below after setting BLC_PWM_CPU_CTL2 and BLC_PWM_PCH_CTL1. - * BLC_PWM_CPU_CTL may be cleared to zero automatically when these - * registers are set. + /* Check the current backlight level and try to set again if it's zero. + * On some machines, BLC_PWM_CPU_CTL is cleared to zero automatically + * when BLC_PWM_CPU_CTL2 and BLC_PWM_PCH_CTL1 are written. */ - dev_priv->backlight_enabled = true; - intel_panel_actually_set_backlight(dev, dev_priv->backlight_level); + if (!intel_panel_get_backlight(dev)) + intel_panel_actually_set_backlight(dev, dev_priv->backlight_level); } static void intel_panel_init_backlight(struct drm_device *dev) diff --git a/drivers/gpu/drm/i915/intel_pm.c b/drivers/gpu/drm/i915/intel_pm.c index 3280cffe50f4..61fee7fcdc2c 100644 --- a/drivers/gpu/drm/i915/intel_pm.c +++ b/drivers/gpu/drm/i915/intel_pm.c @@ -447,12 +447,6 @@ void intel_update_fbc(struct drm_device *dev) dev_priv->no_fbc_reason = FBC_MODULE_PARAM; goto out_disable; } - if (intel_fb->obj->base.size > dev_priv->cfb_size) { - DRM_DEBUG_KMS("framebuffer too large, disabling " - "compression\n"); - dev_priv->no_fbc_reason = FBC_STOLEN_TOO_SMALL; - goto out_disable; - } if ((crtc->mode.flags & DRM_MODE_FLAG_INTERLACE) || (crtc->mode.flags & DRM_MODE_FLAG_DBLSCAN)) { DRM_DEBUG_KMS("mode incompatible with compression, " @@ -486,6 +480,14 @@ void intel_update_fbc(struct drm_device *dev) if (in_dbg_master()) goto out_disable; + if (i915_gem_stolen_setup_compression(dev, intel_fb->obj->base.size)) { + DRM_INFO("not enough stolen space for compressed buffer (need %zd bytes), disabling\n", intel_fb->obj->base.size); + DRM_INFO("hint: you may be able to increase stolen memory size in the BIOS to avoid this\n"); + DRM_DEBUG_KMS("framebuffer too large, disabling compression\n"); + dev_priv->no_fbc_reason = FBC_STOLEN_TOO_SMALL; + goto out_disable; + } + /* If the scanout has not changed, don't modify the FBC settings. * Note that we make the fundamental assumption that the fb->obj * cannot be unpinned (and have its GTT offset and fence revoked) @@ -533,6 +535,7 @@ out_disable: DRM_DEBUG_KMS("unsupported config, disabling FBC\n"); intel_disable_fbc(dev); } + i915_gem_stolen_cleanup_compression(dev); } static void i915_pineview_get_mem_freq(struct drm_device *dev) @@ -2286,7 +2289,6 @@ err_unpin: i915_gem_object_unpin(ctx); err_unref: drm_gem_object_unreference(&ctx->base); - mutex_unlock(&dev->struct_mutex); return NULL; } @@ -3581,6 +3583,19 @@ static void cpt_init_clock_gating(struct drm_device *dev) } } +static void gen6_check_mch_setup(struct drm_device *dev) +{ + struct drm_i915_private *dev_priv = dev->dev_private; + uint32_t tmp; + + tmp = I915_READ(MCH_SSKPD); + if ((tmp & MCH_SSKPD_WM0_MASK) != MCH_SSKPD_WM0_VAL) { + DRM_INFO("Wrong MCH_SSKPD value: 0x%08x\n", tmp); + DRM_INFO("This can cause pipe underruns and display issues.\n"); + DRM_INFO("Please upgrade your BIOS to fix this.\n"); + } +} + static void gen6_init_clock_gating(struct drm_device *dev) { struct drm_i915_private *dev_priv = dev->dev_private; @@ -3673,6 +3688,8 @@ static void gen6_init_clock_gating(struct drm_device *dev) I915_WRITE(GEN6_GT_MODE, _MASKED_BIT_ENABLE(GEN6_GT_MODE_HI)); cpt_init_clock_gating(dev); + + gen6_check_mch_setup(dev); } static void gen7_setup_fixed_func_scheduler(struct drm_i915_private *dev_priv) @@ -3684,6 +3701,10 @@ static void gen7_setup_fixed_func_scheduler(struct drm_i915_private *dev_priv) reg |= GEN7_FF_VS_SCHED_HW; reg |= GEN7_FF_DS_SCHED_HW; + /* WaVSRefCountFullforceMissDisable */ + if (IS_HASWELL(dev_priv->dev)) + reg &= ~GEN7_FF_VS_REF_CNT_FFME; + I915_WRITE(GEN7_FF_THREAD_MODE, reg); } @@ -3854,6 +3875,8 @@ static void ivybridge_init_clock_gating(struct drm_device *dev) I915_WRITE(GEN6_MBCUNIT_SNPCR, snpcr); cpt_init_clock_gating(dev); + + gen6_check_mch_setup(dev); } static void valleyview_init_clock_gating(struct drm_device *dev) @@ -4047,35 +4070,57 @@ void intel_init_clock_gating(struct drm_device *dev) dev_priv->display.init_clock_gating(dev); } -/* Starting with Haswell, we have different power wells for - * different parts of the GPU. This attempts to enable them all. - */ -void intel_init_power_wells(struct drm_device *dev) +void intel_set_power_well(struct drm_device *dev, bool enable) { struct drm_i915_private *dev_priv = dev->dev_private; - unsigned long power_wells[] = { - HSW_PWR_WELL_CTL1, - HSW_PWR_WELL_CTL2, - HSW_PWR_WELL_CTL4 - }; - int i; + bool is_enabled, enable_requested; + uint32_t tmp; if (!IS_HASWELL(dev)) return; - mutex_lock(&dev->struct_mutex); + tmp = I915_READ(HSW_PWR_WELL_DRIVER); + is_enabled = tmp & HSW_PWR_WELL_STATE; + enable_requested = tmp & HSW_PWR_WELL_ENABLE; - for (i = 0; i < ARRAY_SIZE(power_wells); i++) { - int well = I915_READ(power_wells[i]); + if (enable) { + if (!enable_requested) + I915_WRITE(HSW_PWR_WELL_DRIVER, HSW_PWR_WELL_ENABLE); - if ((well & HSW_PWR_WELL_STATE) == 0) { - I915_WRITE(power_wells[i], well & HSW_PWR_WELL_ENABLE); - if (wait_for((I915_READ(power_wells[i]) & HSW_PWR_WELL_STATE), 20)) - DRM_ERROR("Error enabling power well %lx\n", power_wells[i]); + if (!is_enabled) { + DRM_DEBUG_KMS("Enabling power well\n"); + if (wait_for((I915_READ(HSW_PWR_WELL_DRIVER) & + HSW_PWR_WELL_STATE), 20)) + DRM_ERROR("Timeout enabling power well\n"); + } + } else { + if (enable_requested) { + I915_WRITE(HSW_PWR_WELL_DRIVER, 0); + DRM_DEBUG_KMS("Requesting to disable the power well\n"); } } +} - mutex_unlock(&dev->struct_mutex); +/* + * Starting with Haswell, we have a "Power Down Well" that can be turned off + * when not needed anymore. We have 4 registers that can request the power well + * to be enabled, and it will only be disabled if none of the registers is + * requesting it to be enabled. + */ +void intel_init_power_well(struct drm_device *dev) +{ + struct drm_i915_private *dev_priv = dev->dev_private; + + if (!IS_HASWELL(dev)) + return; + + /* For now, we need the power well to be always enabled. */ + intel_set_power_well(dev, true); + + /* We're taking over the BIOS, so clear any requests made by it since + * the driver is in charge now. */ + if (I915_READ(HSW_PWR_WELL_BIOS) & HSW_PWR_WELL_ENABLE) + I915_WRITE(HSW_PWR_WELL_BIOS, 0); } /* Set up chip specific power management-related functions */ diff --git a/drivers/gpu/drm/i915/intel_ringbuffer.c b/drivers/gpu/drm/i915/intel_ringbuffer.c index 42ff97d667d2..1d5d613eb6be 100644 --- a/drivers/gpu/drm/i915/intel_ringbuffer.c +++ b/drivers/gpu/drm/i915/intel_ringbuffer.c @@ -318,6 +318,7 @@ gen7_render_ring_flush(struct intel_ring_buffer *ring, * TLB invalidate requires a post-sync write. */ flags |= PIPE_CONTROL_QW_WRITE; + flags |= PIPE_CONTROL_GLOBAL_GTT_IVB; /* Workaround: we must issue a pipe_control with CS-stall bit * set before a pipe_control command that has the state cache @@ -331,7 +332,7 @@ gen7_render_ring_flush(struct intel_ring_buffer *ring, intel_ring_emit(ring, GFX_OP_PIPE_CONTROL(4)); intel_ring_emit(ring, flags); - intel_ring_emit(ring, scratch_addr | PIPE_CONTROL_GLOBAL_GTT); + intel_ring_emit(ring, scratch_addr); intel_ring_emit(ring, 0); intel_ring_advance(ring); @@ -467,6 +468,9 @@ init_pipe_control(struct intel_ring_buffer *ring) if (pc->cpu_page == NULL) goto err_unpin; + DRM_DEBUG_DRIVER("%s pipe control offset: 0x%08x\n", + ring->name, pc->gtt_offset); + pc->obj = obj; ring->private = pc; return 0; @@ -613,6 +617,13 @@ gen6_add_request(struct intel_ring_buffer *ring) return 0; } +static inline bool i915_gem_has_seqno_wrapped(struct drm_device *dev, + u32 seqno) +{ + struct drm_i915_private *dev_priv = dev->dev_private; + return dev_priv->last_seqno < seqno; +} + /** * intel_ring_sync - sync the waiter to the signaller on seqno * @@ -643,11 +654,20 @@ gen6_ring_sync(struct intel_ring_buffer *waiter, if (ret) return ret; - intel_ring_emit(waiter, - dw1 | signaller->semaphore_register[waiter->id]); - intel_ring_emit(waiter, seqno); - intel_ring_emit(waiter, 0); - intel_ring_emit(waiter, MI_NOOP); + /* If seqno wrap happened, omit the wait with no-ops */ + if (likely(!i915_gem_has_seqno_wrapped(waiter->dev, seqno))) { + intel_ring_emit(waiter, + dw1 | + signaller->semaphore_register[waiter->id]); + intel_ring_emit(waiter, seqno); + intel_ring_emit(waiter, 0); + intel_ring_emit(waiter, MI_NOOP); + } else { + intel_ring_emit(waiter, MI_NOOP); + intel_ring_emit(waiter, MI_NOOP); + intel_ring_emit(waiter, MI_NOOP); + intel_ring_emit(waiter, MI_NOOP); + } intel_ring_advance(waiter); return 0; @@ -728,6 +748,12 @@ ring_get_seqno(struct intel_ring_buffer *ring, bool lazy_coherency) return intel_read_status_page(ring, I915_GEM_HWS_INDEX); } +static void +ring_set_seqno(struct intel_ring_buffer *ring, u32 seqno) +{ + intel_write_status_page(ring, I915_GEM_HWS_INDEX, seqno); +} + static u32 pc_render_get_seqno(struct intel_ring_buffer *ring, bool lazy_coherency) { @@ -735,6 +761,13 @@ pc_render_get_seqno(struct intel_ring_buffer *ring, bool lazy_coherency) return pc->cpu_page[0]; } +static void +pc_render_set_seqno(struct intel_ring_buffer *ring, u32 seqno) +{ + struct pipe_control *pc = ring->private; + pc->cpu_page[0] = seqno; +} + static bool gen5_ring_get_irq(struct intel_ring_buffer *ring) { @@ -1164,7 +1197,11 @@ static int intel_init_ring_buffer(struct drm_device *dev, return ret; } - obj = i915_gem_alloc_object(dev, ring->size); + obj = NULL; + if (!HAS_LLC(dev)) + obj = i915_gem_object_create_stolen(dev, ring->size); + if (obj == NULL) + obj = i915_gem_alloc_object(dev, ring->size); if (obj == NULL) { DRM_ERROR("Failed to allocate ringbuffer\n"); ret = -ENOMEM; @@ -1182,7 +1219,7 @@ static int intel_init_ring_buffer(struct drm_device *dev, goto err_unpin; ring->virtual_start = - ioremap_wc(dev_priv->mm.gtt->gma_bus_addr + obj->gtt_offset, + ioremap_wc(dev_priv->gtt.mappable_base + obj->gtt_offset, ring->size); if (ring->virtual_start == NULL) { DRM_ERROR("Failed to map ringbuffer.\n"); @@ -1348,7 +1385,8 @@ static int ring_wait_for_space(struct intel_ring_buffer *ring, int n) msleep(1); - ret = i915_gem_check_wedge(dev_priv, dev_priv->mm.interruptible); + ret = i915_gem_check_wedge(&dev_priv->gpu_error, + dev_priv->mm.interruptible); if (ret) return ret; } while (!time_after(jiffies, end)); @@ -1410,14 +1448,35 @@ intel_ring_alloc_seqno(struct intel_ring_buffer *ring) return i915_gem_get_seqno(ring->dev, &ring->outstanding_lazy_request); } +static int __intel_ring_begin(struct intel_ring_buffer *ring, + int bytes) +{ + int ret; + + if (unlikely(ring->tail + bytes > ring->effective_size)) { + ret = intel_wrap_ring_buffer(ring); + if (unlikely(ret)) + return ret; + } + + if (unlikely(ring->space < bytes)) { + ret = ring_wait_for_space(ring, bytes); + if (unlikely(ret)) + return ret; + } + + ring->space -= bytes; + return 0; +} + int intel_ring_begin(struct intel_ring_buffer *ring, int num_dwords) { drm_i915_private_t *dev_priv = ring->dev->dev_private; - int n = 4*num_dwords; int ret; - ret = i915_gem_check_wedge(dev_priv, dev_priv->mm.interruptible); + ret = i915_gem_check_wedge(&dev_priv->gpu_error, + dev_priv->mm.interruptible); if (ret) return ret; @@ -1426,20 +1485,21 @@ int intel_ring_begin(struct intel_ring_buffer *ring, if (ret) return ret; - if (unlikely(ring->tail + n > ring->effective_size)) { - ret = intel_wrap_ring_buffer(ring); - if (unlikely(ret)) - return ret; - } + return __intel_ring_begin(ring, num_dwords * sizeof(uint32_t)); +} - if (unlikely(ring->space < n)) { - ret = ring_wait_for_space(ring, n); - if (unlikely(ret)) - return ret; +void intel_ring_init_seqno(struct intel_ring_buffer *ring, u32 seqno) +{ + struct drm_i915_private *dev_priv = ring->dev->dev_private; + + BUG_ON(ring->outstanding_lazy_request); + + if (INTEL_INFO(ring->dev)->gen >= 6) { + I915_WRITE(RING_SYNC_0(ring->mmio_base), 0); + I915_WRITE(RING_SYNC_1(ring->mmio_base), 0); } - ring->space -= n; - return 0; + ring->set_seqno(ring, seqno); } void intel_ring_advance(struct intel_ring_buffer *ring) @@ -1447,7 +1507,7 @@ void intel_ring_advance(struct intel_ring_buffer *ring) struct drm_i915_private *dev_priv = ring->dev->dev_private; ring->tail &= ring->size - 1; - if (dev_priv->stop_rings & intel_ring_flag(ring)) + if (dev_priv->gpu_error.stop_rings & intel_ring_flag(ring)) return; ring->write_tail(ring, ring->tail); } @@ -1604,6 +1664,7 @@ int intel_init_render_ring_buffer(struct drm_device *dev) ring->irq_put = gen6_ring_put_irq; ring->irq_enable_mask = GT_USER_INTERRUPT; ring->get_seqno = gen6_ring_get_seqno; + ring->set_seqno = ring_set_seqno; ring->sync_to = gen6_ring_sync; ring->semaphore_register[0] = MI_SEMAPHORE_SYNC_INVALID; ring->semaphore_register[1] = MI_SEMAPHORE_SYNC_RV; @@ -1614,6 +1675,7 @@ int intel_init_render_ring_buffer(struct drm_device *dev) ring->add_request = pc_render_add_request; ring->flush = gen4_render_ring_flush; ring->get_seqno = pc_render_get_seqno; + ring->set_seqno = pc_render_set_seqno; ring->irq_get = gen5_ring_get_irq; ring->irq_put = gen5_ring_put_irq; ring->irq_enable_mask = GT_USER_INTERRUPT | GT_PIPE_NOTIFY; @@ -1624,6 +1686,7 @@ int intel_init_render_ring_buffer(struct drm_device *dev) else ring->flush = gen4_render_ring_flush; ring->get_seqno = ring_get_seqno; + ring->set_seqno = ring_set_seqno; if (IS_GEN2(dev)) { ring->irq_get = i8xx_ring_get_irq; ring->irq_put = i8xx_ring_put_irq; @@ -1695,6 +1758,7 @@ int intel_render_ring_init_dri(struct drm_device *dev, u64 start, u32 size) else ring->flush = gen4_render_ring_flush; ring->get_seqno = ring_get_seqno; + ring->set_seqno = ring_set_seqno; if (IS_GEN2(dev)) { ring->irq_get = i8xx_ring_get_irq; ring->irq_put = i8xx_ring_put_irq; @@ -1755,6 +1819,7 @@ int intel_init_bsd_ring_buffer(struct drm_device *dev) ring->flush = gen6_ring_flush; ring->add_request = gen6_add_request; ring->get_seqno = gen6_ring_get_seqno; + ring->set_seqno = ring_set_seqno; ring->irq_enable_mask = GEN6_BSD_USER_INTERRUPT; ring->irq_get = gen6_ring_get_irq; ring->irq_put = gen6_ring_put_irq; @@ -1770,6 +1835,7 @@ int intel_init_bsd_ring_buffer(struct drm_device *dev) ring->flush = bsd_ring_flush; ring->add_request = i9xx_add_request; ring->get_seqno = ring_get_seqno; + ring->set_seqno = ring_set_seqno; if (IS_GEN5(dev)) { ring->irq_enable_mask = GT_BSD_USER_INTERRUPT; ring->irq_get = gen5_ring_get_irq; @@ -1799,6 +1865,7 @@ int intel_init_blt_ring_buffer(struct drm_device *dev) ring->flush = blt_ring_flush; ring->add_request = gen6_add_request; ring->get_seqno = gen6_ring_get_seqno; + ring->set_seqno = ring_set_seqno; ring->irq_enable_mask = GEN6_BLITTER_USER_INTERRUPT; ring->irq_get = gen6_ring_get_irq; ring->irq_put = gen6_ring_put_irq; diff --git a/drivers/gpu/drm/i915/intel_ringbuffer.h b/drivers/gpu/drm/i915/intel_ringbuffer.h index 6af87cd05725..d66208c2c48b 100644 --- a/drivers/gpu/drm/i915/intel_ringbuffer.h +++ b/drivers/gpu/drm/i915/intel_ringbuffer.h @@ -90,6 +90,8 @@ struct intel_ring_buffer { */ u32 (*get_seqno)(struct intel_ring_buffer *ring, bool lazy_coherency); + void (*set_seqno)(struct intel_ring_buffer *ring, + u32 seqno); int (*dispatch_execbuffer)(struct intel_ring_buffer *ring, u32 offset, u32 length, unsigned flags); @@ -178,6 +180,13 @@ intel_read_status_page(struct intel_ring_buffer *ring, return ring->status_page.page_addr[reg]; } +static inline void +intel_write_status_page(struct intel_ring_buffer *ring, + int reg, u32 value) +{ + ring->status_page.page_addr[reg] = value; +} + /** * Reads a dword out of the status page, which is written to from the command * queue by automatic updates, MI_REPORT_HEAD, MI_STORE_DATA_INDEX, or @@ -208,7 +217,7 @@ static inline void intel_ring_emit(struct intel_ring_buffer *ring, } void intel_ring_advance(struct intel_ring_buffer *ring); int __must_check intel_ring_idle(struct intel_ring_buffer *ring); - +void intel_ring_init_seqno(struct intel_ring_buffer *ring, u32 seqno); int intel_ring_flush_all_caches(struct intel_ring_buffer *ring); int intel_ring_invalidate_all_caches(struct intel_ring_buffer *ring); diff --git a/drivers/gpu/drm/i915/intel_sdvo.c b/drivers/gpu/drm/i915/intel_sdvo.c index c275bf0fa36d..33b46d9694ea 100644 --- a/drivers/gpu/drm/i915/intel_sdvo.c +++ b/drivers/gpu/drm/i915/intel_sdvo.c @@ -103,6 +103,7 @@ struct intel_sdvo { * It is only valid when using TMDS encoding and 8 bit per color mode. */ uint32_t color_range; + bool color_range_auto; /** * This is set if we're going to treat the device as TV-out. @@ -125,6 +126,7 @@ struct intel_sdvo { bool is_hdmi; bool has_hdmi_monitor; bool has_hdmi_audio; + bool rgb_quant_range_selectable; /** * This is set if we detect output of sdvo device as LVDS and @@ -946,7 +948,8 @@ static bool intel_sdvo_write_infoframe(struct intel_sdvo *intel_sdvo, &tx_rate, 1); } -static bool intel_sdvo_set_avi_infoframe(struct intel_sdvo *intel_sdvo) +static bool intel_sdvo_set_avi_infoframe(struct intel_sdvo *intel_sdvo, + const struct drm_display_mode *adjusted_mode) { struct dip_infoframe avi_if = { .type = DIP_TYPE_AVI, @@ -955,6 +958,13 @@ static bool intel_sdvo_set_avi_infoframe(struct intel_sdvo *intel_sdvo) }; uint8_t sdvo_data[4 + sizeof(avi_if.body.avi)]; + if (intel_sdvo->rgb_quant_range_selectable) { + if (adjusted_mode->private_flags & INTEL_MODE_LIMITED_COLOR_RANGE) + avi_if.body.avi.ITC_EC_Q_SC |= DIP_AVI_RGB_QUANT_RANGE_LIMITED; + else + avi_if.body.avi.ITC_EC_Q_SC |= DIP_AVI_RGB_QUANT_RANGE_FULL; + } + intel_dip_infoframe_csum(&avi_if); /* sdvo spec says that the ecc is handled by the hw, and it looks like @@ -1064,6 +1074,18 @@ static bool intel_sdvo_mode_fixup(struct drm_encoder *encoder, multiplier = intel_sdvo_get_pixel_multiplier(adjusted_mode); intel_mode_set_pixel_multiplier(adjusted_mode, multiplier); + if (intel_sdvo->color_range_auto) { + /* See CEA-861-E - 5.1 Default Encoding Parameters */ + if (intel_sdvo->has_hdmi_monitor && + drm_mode_cea_vic(adjusted_mode) > 1) + intel_sdvo->color_range = SDVO_COLOR_RANGE_16_235; + else + intel_sdvo->color_range = 0; + } + + if (intel_sdvo->color_range) + adjusted_mode->private_flags |= INTEL_MODE_LIMITED_COLOR_RANGE; + return true; } @@ -1121,7 +1143,7 @@ static void intel_sdvo_mode_set(struct drm_encoder *encoder, intel_sdvo_set_encode(intel_sdvo, SDVO_ENCODE_HDMI); intel_sdvo_set_colorimetry(intel_sdvo, SDVO_COLORIMETRY_RGB256); - intel_sdvo_set_avi_infoframe(intel_sdvo); + intel_sdvo_set_avi_infoframe(intel_sdvo, adjusted_mode); } else intel_sdvo_set_encode(intel_sdvo, SDVO_ENCODE_DVI); @@ -1153,7 +1175,7 @@ static void intel_sdvo_mode_set(struct drm_encoder *encoder, /* The real mode polarity is set by the SDVO commands, using * struct intel_sdvo_dtd. */ sdvox = SDVO_VSYNC_ACTIVE_HIGH | SDVO_HSYNC_ACTIVE_HIGH; - if (intel_sdvo->is_hdmi) + if (!HAS_PCH_SPLIT(dev) && intel_sdvo->is_hdmi) sdvox |= intel_sdvo->color_range; if (INTEL_INFO(dev)->gen < 5) sdvox |= SDVO_BORDER_ENABLE; @@ -1513,6 +1535,8 @@ intel_sdvo_tmds_sink_detect(struct drm_connector *connector) if (intel_sdvo->is_hdmi) { intel_sdvo->has_hdmi_monitor = drm_detect_hdmi_monitor(edid); intel_sdvo->has_hdmi_audio = drm_detect_monitor_audio(edid); + intel_sdvo->rgb_quant_range_selectable = + drm_rgb_quant_range_selectable(edid); } } else status = connector_status_disconnected; @@ -1564,6 +1588,7 @@ intel_sdvo_detect(struct drm_connector *connector, bool force) intel_sdvo->has_hdmi_monitor = false; intel_sdvo->has_hdmi_audio = false; + intel_sdvo->rgb_quant_range_selectable = false; if ((intel_sdvo_connector->output_flag & response) == 0) ret = connector_status_disconnected; @@ -1897,10 +1922,21 @@ intel_sdvo_set_property(struct drm_connector *connector, } if (property == dev_priv->broadcast_rgb_property) { - if (val == !!intel_sdvo->color_range) - return 0; - - intel_sdvo->color_range = val ? SDVO_COLOR_RANGE_16_235 : 0; + switch (val) { + case INTEL_BROADCAST_RGB_AUTO: + intel_sdvo->color_range_auto = true; + break; + case INTEL_BROADCAST_RGB_FULL: + intel_sdvo->color_range_auto = false; + intel_sdvo->color_range = 0; + break; + case INTEL_BROADCAST_RGB_LIMITED: + intel_sdvo->color_range_auto = false; + intel_sdvo->color_range = SDVO_COLOR_RANGE_16_235; + break; + default: + return -EINVAL; + } goto done; } @@ -1997,11 +2033,8 @@ set_value: done: - if (intel_sdvo->base.base.crtc) { - struct drm_crtc *crtc = intel_sdvo->base.base.crtc; - intel_set_mode(crtc, &crtc->mode, - crtc->x, crtc->y, crtc->fb); - } + if (intel_sdvo->base.base.crtc) + intel_crtc_restore_mode(intel_sdvo->base.base.crtc); return 0; #undef CHECK_PROPERTY @@ -2010,7 +2043,6 @@ done: static const struct drm_encoder_helper_funcs intel_sdvo_helper_funcs = { .mode_fixup = intel_sdvo_mode_fixup, .mode_set = intel_sdvo_mode_set, - .disable = intel_encoder_noop, }; static const struct drm_connector_funcs intel_sdvo_connector_funcs = { @@ -2200,13 +2232,16 @@ intel_sdvo_connector_init(struct intel_sdvo_connector *connector, } static void -intel_sdvo_add_hdmi_properties(struct intel_sdvo_connector *connector) +intel_sdvo_add_hdmi_properties(struct intel_sdvo *intel_sdvo, + struct intel_sdvo_connector *connector) { struct drm_device *dev = connector->base.base.dev; intel_attach_force_audio_property(&connector->base.base); - if (INTEL_INFO(dev)->gen >= 4 && IS_MOBILE(dev)) + if (INTEL_INFO(dev)->gen >= 4 && IS_MOBILE(dev)) { intel_attach_broadcast_rgb_property(&connector->base.base); + intel_sdvo->color_range_auto = true; + } } static bool @@ -2254,7 +2289,7 @@ intel_sdvo_dvi_init(struct intel_sdvo *intel_sdvo, int device) intel_sdvo_connector_init(intel_sdvo_connector, intel_sdvo); if (intel_sdvo->is_hdmi) - intel_sdvo_add_hdmi_properties(intel_sdvo_connector); + intel_sdvo_add_hdmi_properties(intel_sdvo, intel_sdvo_connector); return true; } diff --git a/drivers/gpu/drm/i915/intel_sprite.c b/drivers/gpu/drm/i915/intel_sprite.c index d7b060e0a231..d086e48a831a 100644 --- a/drivers/gpu/drm/i915/intel_sprite.c +++ b/drivers/gpu/drm/i915/intel_sprite.c @@ -50,6 +50,7 @@ ivb_update_plane(struct drm_plane *plane, struct drm_framebuffer *fb, u32 sprctl, sprscale = 0; unsigned long sprsurf_offset, linear_offset; int pixel_size = drm_format_plane_cpp(fb->pixel_format, 0); + bool scaling_was_enabled = dev_priv->sprite_scaling_enabled; sprctl = I915_READ(SPRCTL(pipe)); @@ -89,6 +90,9 @@ ivb_update_plane(struct drm_plane *plane, struct drm_framebuffer *fb, sprctl |= SPRITE_TRICKLE_FEED_DISABLE; sprctl |= SPRITE_ENABLE; + if (IS_HASWELL(dev)) + sprctl |= SPRITE_PIPE_CSC_ENABLE; + /* Sizes are 0 based */ src_w--; src_h--; @@ -103,19 +107,15 @@ ivb_update_plane(struct drm_plane *plane, struct drm_framebuffer *fb, * when scaling is disabled. */ if (crtc_w != src_w || crtc_h != src_h) { - if (!dev_priv->sprite_scaling_enabled) { - dev_priv->sprite_scaling_enabled = true; + dev_priv->sprite_scaling_enabled |= 1 << pipe; + + if (!scaling_was_enabled) { intel_update_watermarks(dev); intel_wait_for_vblank(dev, pipe); } sprscale = SPRITE_SCALE_ENABLE | (src_w << 16) | src_h; - } else { - if (dev_priv->sprite_scaling_enabled) { - dev_priv->sprite_scaling_enabled = false; - /* potentially re-enable LP watermarks */ - intel_update_watermarks(dev); - } - } + } else + dev_priv->sprite_scaling_enabled &= ~(1 << pipe); I915_WRITE(SPRSTRIDE(pipe), fb->pitches[0]); I915_WRITE(SPRPOS(pipe), (crtc_y << 16) | crtc_x); @@ -141,6 +141,10 @@ ivb_update_plane(struct drm_plane *plane, struct drm_framebuffer *fb, I915_WRITE(SPRCTL(pipe), sprctl); I915_MODIFY_DISPBASE(SPRSURF(pipe), obj->gtt_offset + sprsurf_offset); POSTING_READ(SPRSURF(pipe)); + + /* potentially re-enable LP watermarks */ + if (scaling_was_enabled && !dev_priv->sprite_scaling_enabled) + intel_update_watermarks(dev); } static void @@ -150,6 +154,7 @@ ivb_disable_plane(struct drm_plane *plane) struct drm_i915_private *dev_priv = dev->dev_private; struct intel_plane *intel_plane = to_intel_plane(plane); int pipe = intel_plane->pipe; + bool scaling_was_enabled = dev_priv->sprite_scaling_enabled; I915_WRITE(SPRCTL(pipe), I915_READ(SPRCTL(pipe)) & ~SPRITE_ENABLE); /* Can't leave the scaler enabled... */ @@ -159,8 +164,11 @@ ivb_disable_plane(struct drm_plane *plane) I915_MODIFY_DISPBASE(SPRSURF(pipe), 0); POSTING_READ(SPRSURF(pipe)); - dev_priv->sprite_scaling_enabled = false; - intel_update_watermarks(dev); + dev_priv->sprite_scaling_enabled &= ~(1 << pipe); + + /* potentially re-enable LP watermarks */ + if (scaling_was_enabled && !dev_priv->sprite_scaling_enabled) + intel_update_watermarks(dev); } static int @@ -593,7 +601,7 @@ int intel_sprite_set_colorkey(struct drm_device *dev, void *data, if ((set->flags & (I915_SET_COLORKEY_DESTINATION | I915_SET_COLORKEY_SOURCE)) == (I915_SET_COLORKEY_DESTINATION | I915_SET_COLORKEY_SOURCE)) return -EINVAL; - mutex_lock(&dev->mode_config.mutex); + drm_modeset_lock_all(dev); obj = drm_mode_object_find(dev, set->plane_id, DRM_MODE_OBJECT_PLANE); if (!obj) { @@ -606,7 +614,7 @@ int intel_sprite_set_colorkey(struct drm_device *dev, void *data, ret = intel_plane->update_colorkey(plane, set); out_unlock: - mutex_unlock(&dev->mode_config.mutex); + drm_modeset_unlock_all(dev); return ret; } @@ -622,7 +630,7 @@ int intel_sprite_get_colorkey(struct drm_device *dev, void *data, if (!drm_core_check_feature(dev, DRIVER_MODESET)) return -ENODEV; - mutex_lock(&dev->mode_config.mutex); + drm_modeset_lock_all(dev); obj = drm_mode_object_find(dev, get->plane_id, DRM_MODE_OBJECT_PLANE); if (!obj) { @@ -635,7 +643,7 @@ int intel_sprite_get_colorkey(struct drm_device *dev, void *data, intel_plane->get_colorkey(plane, get); out_unlock: - mutex_unlock(&dev->mode_config.mutex); + drm_modeset_unlock_all(dev); return ret; } diff --git a/drivers/gpu/drm/i915/intel_tv.c b/drivers/gpu/drm/i915/intel_tv.c index ea93520c1278..d808421c1c80 100644 --- a/drivers/gpu/drm/i915/intel_tv.c +++ b/drivers/gpu/drm/i915/intel_tv.c @@ -1479,8 +1479,7 @@ intel_tv_set_property(struct drm_connector *connector, struct drm_property *prop } if (changed && crtc) - intel_set_mode(crtc, &crtc->mode, - crtc->x, crtc->y, crtc->fb); + intel_crtc_restore_mode(crtc); out: return ret; } @@ -1488,7 +1487,6 @@ out: static const struct drm_encoder_helper_funcs intel_tv_helper_funcs = { .mode_fixup = intel_tv_mode_fixup, .mode_set = intel_tv_mode_set, - .disable = intel_encoder_noop, }; static const struct drm_connector_funcs intel_tv_connector_funcs = { diff --git a/drivers/gpu/drm/mgag200/mgag200_fb.c b/drivers/gpu/drm/mgag200/mgag200_fb.c index 2f486481d79a..d2253f639481 100644 --- a/drivers/gpu/drm/mgag200/mgag200_fb.c +++ b/drivers/gpu/drm/mgag200/mgag200_fb.c @@ -13,6 +13,7 @@ #include <linux/module.h> #include <drm/drmP.h> #include <drm/drm_fb_helper.h> +#include <drm/drm_crtc_helper.h> #include <linux/fb.h> @@ -120,9 +121,10 @@ static int mgag200fb_create_object(struct mga_fbdev *afbdev, return ret; } -static int mgag200fb_create(struct mga_fbdev *mfbdev, +static int mgag200fb_create(struct drm_fb_helper *helper, struct drm_fb_helper_surface_size *sizes) { + struct mga_fbdev *mfbdev = (struct mga_fbdev *)helper; struct drm_device *dev = mfbdev->helper.dev; struct drm_mode_fb_cmd2 mode_cmd; struct mga_device *mdev = dev->dev_private; @@ -209,23 +211,6 @@ out: return ret; } -static int mga_fb_find_or_create_single(struct drm_fb_helper *helper, - struct drm_fb_helper_surface_size - *sizes) -{ - struct mga_fbdev *mfbdev = (struct mga_fbdev *)helper; - int new_fb = 0; - int ret; - - if (!helper->fb) { - ret = mgag200fb_create(mfbdev, sizes); - if (ret) - return ret; - new_fb = 1; - } - return new_fb; -} - static int mga_fbdev_destroy(struct drm_device *dev, struct mga_fbdev *mfbdev) { @@ -247,6 +232,7 @@ static int mga_fbdev_destroy(struct drm_device *dev, } drm_fb_helper_fini(&mfbdev->helper); vfree(mfbdev->sysram); + drm_framebuffer_unregister_private(&mfb->base); drm_framebuffer_cleanup(&mfb->base); return 0; @@ -255,7 +241,7 @@ static int mga_fbdev_destroy(struct drm_device *dev, static struct drm_fb_helper_funcs mga_fb_helper_funcs = { .gamma_set = mga_crtc_fb_gamma_set, .gamma_get = mga_crtc_fb_gamma_get, - .fb_probe = mga_fb_find_or_create_single, + .fb_probe = mgag200fb_create, }; int mgag200_fbdev_init(struct mga_device *mdev) @@ -277,6 +263,10 @@ int mgag200_fbdev_init(struct mga_device *mdev) return ret; } drm_fb_helper_single_add_all_connectors(&mfbdev->helper); + + /* disable all the possible outputs/crtcs before entering KMS mode */ + drm_helper_disable_unused_functions(mdev->dev); + drm_fb_helper_initial_config(&mfbdev->helper, 32); return 0; diff --git a/drivers/gpu/drm/mgag200/mgag200_main.c b/drivers/gpu/drm/mgag200/mgag200_main.c index 70dd3c5529d4..64297c72464f 100644 --- a/drivers/gpu/drm/mgag200/mgag200_main.c +++ b/drivers/gpu/drm/mgag200/mgag200_main.c @@ -23,16 +23,8 @@ static void mga_user_framebuffer_destroy(struct drm_framebuffer *fb) kfree(fb); } -static int mga_user_framebuffer_create_handle(struct drm_framebuffer *fb, - struct drm_file *file_priv, - unsigned int *handle) -{ - return 0; -} - static const struct drm_framebuffer_funcs mga_fb_funcs = { .destroy = mga_user_framebuffer_destroy, - .create_handle = mga_user_framebuffer_create_handle, }; int mgag200_framebuffer_init(struct drm_device *dev, @@ -40,13 +32,15 @@ int mgag200_framebuffer_init(struct drm_device *dev, struct drm_mode_fb_cmd2 *mode_cmd, struct drm_gem_object *obj) { - int ret = drm_framebuffer_init(dev, &gfb->base, &mga_fb_funcs); + int ret; + + drm_helper_mode_fill_fb_struct(&gfb->base, mode_cmd); + gfb->obj = obj; + ret = drm_framebuffer_init(dev, &gfb->base, &mga_fb_funcs); if (ret) { DRM_ERROR("drm_framebuffer_init failed: %d\n", ret); return ret; } - drm_helper_mode_fill_fb_struct(&gfb->base, mode_cmd); - gfb->obj = obj; return 0; } diff --git a/drivers/gpu/drm/nouveau/Kconfig b/drivers/gpu/drm/nouveau/Kconfig index 8a55beeb8bdc..a7ff6d5a34b9 100644 --- a/drivers/gpu/drm/nouveau/Kconfig +++ b/drivers/gpu/drm/nouveau/Kconfig @@ -11,8 +11,9 @@ config DRM_NOUVEAU select FRAMEBUFFER_CONSOLE if !EXPERT select FB_BACKLIGHT if DRM_NOUVEAU_BACKLIGHT select ACPI_VIDEO if ACPI && X86 && BACKLIGHT_CLASS_DEVICE && VIDEO_OUTPUT_CONTROL && INPUT - select ACPI_WMI if ACPI - select MXM_WMI if ACPI + select X86_PLATFORM_DEVICES if ACPI && X86 + select ACPI_WMI if ACPI && X86 + select MXM_WMI if ACPI && X86 select POWER_SUPPLY help Choose this option for open-source nVidia support. @@ -52,26 +53,3 @@ config DRM_NOUVEAU_BACKLIGHT help Say Y here if you want to control the backlight of your display (e.g. a laptop panel). - -menu "I2C encoder or helper chips" - depends on DRM && DRM_KMS_HELPER && I2C - -config DRM_I2C_CH7006 - tristate "Chrontel ch7006 TV encoder" - default m if DRM_NOUVEAU - help - Support for Chrontel ch7006 and similar TV encoders, found - on some nVidia video cards. - - This driver is currently only useful if you're also using - the nouveau driver. - -config DRM_I2C_SIL164 - tristate "Silicon Image sil164 TMDS transmitter" - default m if DRM_NOUVEAU - help - Support for sil164 and similar single-link (or dual-link - when used in pairs) TMDS transmitters, used in some nVidia - video cards. - -endmenu diff --git a/drivers/gpu/drm/nouveau/Makefile b/drivers/gpu/drm/nouveau/Makefile index ab25752a0b1e..90f9140eeefd 100644 --- a/drivers/gpu/drm/nouveau/Makefile +++ b/drivers/gpu/drm/nouveau/Makefile @@ -11,6 +11,7 @@ nouveau-y := core/core/client.o nouveau-y += core/core/engctx.o nouveau-y += core/core/engine.o nouveau-y += core/core/enum.o +nouveau-y += core/core/event.o nouveau-y += core/core/falcon.o nouveau-y += core/core/gpuobj.o nouveau-y += core/core/handle.o @@ -40,6 +41,11 @@ nouveau-y += core/subdev/bios/mxm.o nouveau-y += core/subdev/bios/perf.o nouveau-y += core/subdev/bios/pll.o nouveau-y += core/subdev/bios/therm.o +nouveau-y += core/subdev/bios/xpio.o +nouveau-y += core/subdev/bus/nv04.o +nouveau-y += core/subdev/bus/nv31.o +nouveau-y += core/subdev/bus/nv50.o +nouveau-y += core/subdev/bus/nvc0.o nouveau-y += core/subdev/clock/nv04.o nouveau-y += core/subdev/clock/nv40.o nouveau-y += core/subdev/clock/nv50.o @@ -85,9 +91,16 @@ nouveau-y += core/subdev/gpio/base.o nouveau-y += core/subdev/gpio/nv10.o nouveau-y += core/subdev/gpio/nv50.o nouveau-y += core/subdev/gpio/nvd0.o +nouveau-y += core/subdev/gpio/nve0.o nouveau-y += core/subdev/i2c/base.o +nouveau-y += core/subdev/i2c/anx9805.o nouveau-y += core/subdev/i2c/aux.o nouveau-y += core/subdev/i2c/bit.o +nouveau-y += core/subdev/i2c/nv04.o +nouveau-y += core/subdev/i2c/nv4e.o +nouveau-y += core/subdev/i2c/nv50.o +nouveau-y += core/subdev/i2c/nv94.o +nouveau-y += core/subdev/i2c/nvd0.o nouveau-y += core/subdev/ibus/nvc0.o nouveau-y += core/subdev/ibus/nve0.o nouveau-y += core/subdev/instmem/base.o @@ -106,10 +119,15 @@ nouveau-y += core/subdev/mxm/mxms.o nouveau-y += core/subdev/mxm/nv50.o nouveau-y += core/subdev/therm/base.o nouveau-y += core/subdev/therm/fan.o +nouveau-y += core/subdev/therm/fannil.o +nouveau-y += core/subdev/therm/fanpwm.o +nouveau-y += core/subdev/therm/fantog.o nouveau-y += core/subdev/therm/ic.o +nouveau-y += core/subdev/therm/temp.o nouveau-y += core/subdev/therm/nv40.o nouveau-y += core/subdev/therm/nv50.o -nouveau-y += core/subdev/therm/temp.o +nouveau-y += core/subdev/therm/nva3.o +nouveau-y += core/subdev/therm/nvd0.o nouveau-y += core/subdev/timer/base.o nouveau-y += core/subdev/timer/nv04.o nouveau-y += core/subdev/vm/base.o @@ -132,6 +150,7 @@ nouveau-y += core/engine/copy/nvc0.o nouveau-y += core/engine/copy/nve0.o nouveau-y += core/engine/crypt/nv84.o nouveau-y += core/engine/crypt/nv98.o +nouveau-y += core/engine/disp/base.o nouveau-y += core/engine/disp/nv04.o nouveau-y += core/engine/disp/nv50.o nouveau-y += core/engine/disp/nv84.o @@ -141,11 +160,13 @@ nouveau-y += core/engine/disp/nva3.o nouveau-y += core/engine/disp/nvd0.o nouveau-y += core/engine/disp/nve0.o nouveau-y += core/engine/disp/dacnv50.o +nouveau-y += core/engine/disp/dport.o nouveau-y += core/engine/disp/hdanva3.o nouveau-y += core/engine/disp/hdanvd0.o nouveau-y += core/engine/disp/hdminv84.o nouveau-y += core/engine/disp/hdminva3.o nouveau-y += core/engine/disp/hdminvd0.o +nouveau-y += core/engine/disp/piornv50.o nouveau-y += core/engine/disp/sornv50.o nouveau-y += core/engine/disp/sornv94.o nouveau-y += core/engine/disp/sornvd0.o @@ -194,7 +215,8 @@ nouveau-y += nouveau_drm.o nouveau_chan.o nouveau_dma.o nouveau_fence.o nouveau-y += nouveau_irq.o nouveau_vga.o nouveau_agp.o nouveau-y += nouveau_ttm.o nouveau_sgdma.o nouveau_bo.o nouveau_gem.o nouveau-y += nouveau_prime.o nouveau_abi16.o -nouveau-y += nv04_fence.o nv10_fence.o nv50_fence.o nv84_fence.o nvc0_fence.o +nouveau-y += nv04_fence.o nv10_fence.o nv17_fence.o +nouveau-y += nv50_fence.o nv84_fence.o nvc0_fence.o # drm/kms nouveau-y += nouveau_bios.o nouveau_fbcon.o nouveau_display.o @@ -216,7 +238,10 @@ nouveau-y += nouveau_mem.o # other random bits nouveau-$(CONFIG_COMPAT) += nouveau_ioc32.o +ifdef CONFIG_X86 nouveau-$(CONFIG_ACPI) += nouveau_acpi.o +endif nouveau-$(CONFIG_DRM_NOUVEAU_BACKLIGHT) += nouveau_backlight.o +nouveau-$(CONFIG_DEBUG_FS) += nouveau_debugfs.o obj-$(CONFIG_DRM_NOUVEAU)+= nouveau.o diff --git a/drivers/gpu/drm/nouveau/core/core/client.c b/drivers/gpu/drm/nouveau/core/core/client.c index 8bbb58f94a19..295c22165eac 100644 --- a/drivers/gpu/drm/nouveau/core/core/client.c +++ b/drivers/gpu/drm/nouveau/core/core/client.c @@ -99,3 +99,13 @@ nouveau_client_fini(struct nouveau_client *client, bool suspend) nv_debug(client, "%s completed with %d\n", name[suspend], ret); return ret; } + +const char * +nouveau_client_name(void *obj) +{ + const char *client_name = "unknown"; + struct nouveau_client *client = nouveau_client(obj); + if (client) + client_name = client->name; + return client_name; +} diff --git a/drivers/gpu/drm/nouveau/core/core/enum.c b/drivers/gpu/drm/nouveau/core/core/enum.c index 7cc7133d82de..dd434790ccc4 100644 --- a/drivers/gpu/drm/nouveau/core/core/enum.c +++ b/drivers/gpu/drm/nouveau/core/core/enum.c @@ -40,14 +40,15 @@ nouveau_enum_find(const struct nouveau_enum *en, u32 value) return NULL; } -void +const struct nouveau_enum * nouveau_enum_print(const struct nouveau_enum *en, u32 value) { en = nouveau_enum_find(en, value); if (en) - printk("%s", en->name); + pr_cont("%s", en->name); else - printk("(unknown enum 0x%08x)", value); + pr_cont("(unknown enum 0x%08x)", value); + return en; } void @@ -55,7 +56,7 @@ nouveau_bitfield_print(const struct nouveau_bitfield *bf, u32 value) { while (bf->name) { if (value & bf->mask) { - printk(" %s", bf->name); + pr_cont(" %s", bf->name); value &= ~bf->mask; } @@ -63,5 +64,5 @@ nouveau_bitfield_print(const struct nouveau_bitfield *bf, u32 value) } if (value) - printk(" (unknown bits 0x%08x)", value); + pr_cont(" (unknown bits 0x%08x)", value); } diff --git a/drivers/gpu/drm/nouveau/core/core/event.c b/drivers/gpu/drm/nouveau/core/core/event.c new file mode 100644 index 000000000000..6d01e0f0fc8a --- /dev/null +++ b/drivers/gpu/drm/nouveau/core/core/event.c @@ -0,0 +1,106 @@ +/* + * Copyright 2013 Red Hat Inc. + * + * 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 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 + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) 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. + */ + +#include <core/os.h> +#include <core/event.h> + +static void +nouveau_event_put_locked(struct nouveau_event *event, int index, + struct nouveau_eventh *handler) +{ + if (!--event->index[index].refs) + event->disable(event, index); + list_del(&handler->head); +} + +void +nouveau_event_put(struct nouveau_event *event, int index, + struct nouveau_eventh *handler) +{ + unsigned long flags; + + spin_lock_irqsave(&event->lock, flags); + if (index < event->index_nr) + nouveau_event_put_locked(event, index, handler); + spin_unlock_irqrestore(&event->lock, flags); +} + +void +nouveau_event_get(struct nouveau_event *event, int index, + struct nouveau_eventh *handler) +{ + unsigned long flags; + + spin_lock_irqsave(&event->lock, flags); + if (index < event->index_nr) { + list_add(&handler->head, &event->index[index].list); + if (!event->index[index].refs++) + event->enable(event, index); + } + spin_unlock_irqrestore(&event->lock, flags); +} + +void +nouveau_event_trigger(struct nouveau_event *event, int index) +{ + struct nouveau_eventh *handler, *temp; + unsigned long flags; + + if (index >= event->index_nr) + return; + + spin_lock_irqsave(&event->lock, flags); + list_for_each_entry_safe(handler, temp, &event->index[index].list, head) { + if (handler->func(handler, index) == NVKM_EVENT_DROP) { + nouveau_event_put_locked(event, index, handler); + } + } + spin_unlock_irqrestore(&event->lock, flags); +} + +void +nouveau_event_destroy(struct nouveau_event **pevent) +{ + struct nouveau_event *event = *pevent; + if (event) { + kfree(event); + *pevent = NULL; + } +} + +int +nouveau_event_create(int index_nr, struct nouveau_event **pevent) +{ + struct nouveau_event *event; + int i; + + event = *pevent = kzalloc(sizeof(*event) + index_nr * + sizeof(event->index[0]), GFP_KERNEL); + if (!event) + return -ENOMEM; + + spin_lock_init(&event->lock); + for (i = 0; i < index_nr; i++) + INIT_LIST_HEAD(&event->index[i].list); + event->index_nr = index_nr; + return 0; +} diff --git a/drivers/gpu/drm/nouveau/core/engine/copy/nva3.c b/drivers/gpu/drm/nouveau/core/engine/copy/nva3.c index 283248c7b050..d6dc2a65ccd1 100644 --- a/drivers/gpu/drm/nouveau/core/engine/copy/nva3.c +++ b/drivers/gpu/drm/nouveau/core/engine/copy/nva3.c @@ -22,6 +22,7 @@ * Authors: Ben Skeggs */ +#include <core/client.h> #include <core/falcon.h> #include <core/class.h> #include <core/enum.h> @@ -100,8 +101,9 @@ nva3_copy_intr(struct nouveau_subdev *subdev) if (stat & 0x00000040) { nv_error(falcon, "DISPATCH_ERROR ["); nouveau_enum_print(nva3_copy_isr_error_name, ssta); - printk("] ch %d [0x%010llx] subc %d mthd 0x%04x data 0x%08x\n", - chid, inst << 12, subc, mthd, data); + pr_cont("] ch %d [0x%010llx %s] subc %d mthd 0x%04x data 0x%08x\n", + chid, inst << 12, nouveau_client_name(engctx), subc, + mthd, data); nv_wo32(falcon, 0x004, 0x00000040); stat &= ~0x00000040; } diff --git a/drivers/gpu/drm/nouveau/core/engine/crypt/nv84.c b/drivers/gpu/drm/nouveau/core/engine/crypt/nv84.c index b97490512723..5bc021f471f9 100644 --- a/drivers/gpu/drm/nouveau/core/engine/crypt/nv84.c +++ b/drivers/gpu/drm/nouveau/core/engine/crypt/nv84.c @@ -22,6 +22,7 @@ * Authors: Ben Skeggs */ +#include <core/client.h> #include <core/os.h> #include <core/enum.h> #include <core/class.h> @@ -126,10 +127,11 @@ nv84_crypt_intr(struct nouveau_subdev *subdev) chid = pfifo->chid(pfifo, engctx); if (stat) { - nv_error(priv, ""); + nv_error(priv, "%s", ""); nouveau_bitfield_print(nv84_crypt_intr_mask, stat); - printk(" ch %d [0x%010llx] mthd 0x%04x data 0x%08x\n", - chid, (u64)inst << 12, mthd, data); + pr_cont(" ch %d [0x%010llx %s] mthd 0x%04x data 0x%08x\n", + chid, (u64)inst << 12, nouveau_client_name(engctx), + mthd, data); } nv_wr32(priv, 0x102130, stat); diff --git a/drivers/gpu/drm/nouveau/core/engine/crypt/nv98.c b/drivers/gpu/drm/nouveau/core/engine/crypt/nv98.c index 21986f3bf0c8..8bf8955051d4 100644 --- a/drivers/gpu/drm/nouveau/core/engine/crypt/nv98.c +++ b/drivers/gpu/drm/nouveau/core/engine/crypt/nv98.c @@ -22,6 +22,7 @@ * Authors: Ben Skeggs */ +#include <core/client.h> #include <core/os.h> #include <core/enum.h> #include <core/class.h> @@ -102,8 +103,9 @@ nv98_crypt_intr(struct nouveau_subdev *subdev) if (stat & 0x00000040) { nv_error(priv, "DISPATCH_ERROR ["); nouveau_enum_print(nv98_crypt_isr_error_name, ssta); - printk("] ch %d [0x%010llx] subc %d mthd 0x%04x data 0x%08x\n", - chid, (u64)inst << 12, subc, mthd, data); + pr_cont("] ch %d [0x%010llx %s] subc %d mthd 0x%04x data 0x%08x\n", + chid, (u64)inst << 12, nouveau_client_name(engctx), + subc, mthd, data); nv_wr32(priv, 0x087004, 0x00000040); stat &= ~0x00000040; } diff --git a/drivers/gpu/drm/nouveau/core/engine/disp/base.c b/drivers/gpu/drm/nouveau/core/engine/disp/base.c new file mode 100644 index 000000000000..7a5cae42834f --- /dev/null +++ b/drivers/gpu/drm/nouveau/core/engine/disp/base.c @@ -0,0 +1,52 @@ +/* + * Copyright 2013 Red Hat Inc. + * + * 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 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 + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: Ben Skeggs + */ + +#include <engine/disp.h> + +void +_nouveau_disp_dtor(struct nouveau_object *object) +{ + struct nouveau_disp *disp = (void *)object; + nouveau_event_destroy(&disp->vblank); + nouveau_engine_destroy(&disp->base); +} + +int +nouveau_disp_create_(struct nouveau_object *parent, + struct nouveau_object *engine, + struct nouveau_oclass *oclass, int heads, + const char *intname, const char *extname, + int length, void **pobject) +{ + struct nouveau_disp *disp; + int ret; + + ret = nouveau_engine_create_(parent, engine, oclass, true, + intname, extname, length, pobject); + disp = *pobject; + if (ret) + return ret; + + return nouveau_event_create(heads, &disp->vblank); +} diff --git a/drivers/gpu/drm/nouveau/core/engine/disp/dport.c b/drivers/gpu/drm/nouveau/core/engine/disp/dport.c new file mode 100644 index 000000000000..fa27b02ff829 --- /dev/null +++ b/drivers/gpu/drm/nouveau/core/engine/disp/dport.c @@ -0,0 +1,346 @@ +/* + * Copyright 2013 Red Hat Inc. + * + * 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 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 + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: Ben Skeggs + */ + +#include <subdev/bios.h> +#include <subdev/bios/dcb.h> +#include <subdev/bios/dp.h> +#include <subdev/bios/init.h> +#include <subdev/i2c.h> + +#include <engine/disp.h> + +#include "dport.h" + +#define DBG(fmt, args...) nv_debug(dp->disp, "DP:%04x:%04x: " fmt, \ + dp->outp->hasht, dp->outp->hashm, ##args) +#define ERR(fmt, args...) nv_error(dp->disp, "DP:%04x:%04x: " fmt, \ + dp->outp->hasht, dp->outp->hashm, ##args) + +/****************************************************************************** + * link training + *****************************************************************************/ +struct dp_state { + const struct nouveau_dp_func *func; + struct nouveau_disp *disp; + struct dcb_output *outp; + struct nvbios_dpout info; + u8 version; + struct nouveau_i2c_port *aux; + int head; + u8 dpcd[4]; + int link_nr; + u32 link_bw; + u8 stat[6]; + u8 conf[4]; +}; + +static int +dp_set_link_config(struct dp_state *dp) +{ + struct nouveau_disp *disp = dp->disp; + struct nouveau_bios *bios = nouveau_bios(disp); + struct nvbios_init init = { + .subdev = nv_subdev(dp->disp), + .bios = bios, + .offset = 0x0000, + .outp = dp->outp, + .crtc = dp->head, + .execute = 1, + }; + u32 lnkcmp; + u8 sink[2]; + + DBG("%d lanes at %d KB/s\n", dp->link_nr, dp->link_bw); + + /* set desired link configuration on the sink */ + sink[0] = dp->link_bw / 27000; + sink[1] = dp->link_nr; + if (dp->dpcd[DPCD_RC02] & DPCD_RC02_ENHANCED_FRAME_CAP) + sink[1] |= DPCD_LC01_ENHANCED_FRAME_EN; + + nv_wraux(dp->aux, DPCD_LC00, sink, 2); + + /* set desired link configuration on the source */ + if ((lnkcmp = dp->info.lnkcmp)) { + if (dp->version < 0x30) { + while ((dp->link_bw / 10) < nv_ro16(bios, lnkcmp)) + lnkcmp += 4; + init.offset = nv_ro16(bios, lnkcmp + 2); + } else { + while ((dp->link_bw / 27000) < nv_ro08(bios, lnkcmp)) + lnkcmp += 3; + init.offset = nv_ro16(bios, lnkcmp + 1); + } + + nvbios_exec(&init); + } + + return dp->func->lnk_ctl(dp->disp, dp->outp, dp->head, + dp->link_nr, dp->link_bw / 27000, + dp->dpcd[DPCD_RC02] & + DPCD_RC02_ENHANCED_FRAME_CAP); +} + +static void +dp_set_training_pattern(struct dp_state *dp, u8 pattern) +{ + u8 sink_tp; + + DBG("training pattern %d\n", pattern); + dp->func->pattern(dp->disp, dp->outp, dp->head, pattern); + + nv_rdaux(dp->aux, DPCD_LC02, &sink_tp, 1); + sink_tp &= ~DPCD_LC02_TRAINING_PATTERN_SET; + sink_tp |= pattern; + nv_wraux(dp->aux, DPCD_LC02, &sink_tp, 1); +} + +static int +dp_link_train_commit(struct dp_state *dp) +{ + int i; + + for (i = 0; i < dp->link_nr; i++) { + u8 lane = (dp->stat[4 + (i >> 1)] >> ((i & 1) * 4)) & 0xf; + u8 lpre = (lane & 0x0c) >> 2; + u8 lvsw = (lane & 0x03) >> 0; + + dp->conf[i] = (lpre << 3) | lvsw; + if (lvsw == 3) + dp->conf[i] |= DPCD_LC03_MAX_SWING_REACHED; + if (lpre == 3) + dp->conf[i] |= DPCD_LC03_MAX_PRE_EMPHASIS_REACHED; + + DBG("config lane %d %02x\n", i, dp->conf[i]); + dp->func->drv_ctl(dp->disp, dp->outp, dp->head, i, lvsw, lpre); + } + + return nv_wraux(dp->aux, DPCD_LC03(0), dp->conf, 4); +} + +static int +dp_link_train_update(struct dp_state *dp, u32 delay) +{ + int ret; + + udelay(delay); + + ret = nv_rdaux(dp->aux, DPCD_LS02, dp->stat, 6); + if (ret) + return ret; + + DBG("status %*ph\n", 6, dp->stat); + return 0; +} + +static int +dp_link_train_cr(struct dp_state *dp) +{ + bool cr_done = false, abort = false; + int voltage = dp->conf[0] & DPCD_LC03_VOLTAGE_SWING_SET; + int tries = 0, i; + + dp_set_training_pattern(dp, 1); + + do { + if (dp_link_train_commit(dp) || + dp_link_train_update(dp, 100)) + break; + + cr_done = true; + for (i = 0; i < dp->link_nr; i++) { + u8 lane = (dp->stat[i >> 1] >> ((i & 1) * 4)) & 0xf; + if (!(lane & DPCD_LS02_LANE0_CR_DONE)) { + cr_done = false; + if (dp->conf[i] & DPCD_LC03_MAX_SWING_REACHED) + abort = true; + break; + } + } + + if ((dp->conf[0] & DPCD_LC03_VOLTAGE_SWING_SET) != voltage) { + voltage = dp->conf[0] & DPCD_LC03_VOLTAGE_SWING_SET; + tries = 0; + } + } while (!cr_done && !abort && ++tries < 5); + + return cr_done ? 0 : -1; +} + +static int +dp_link_train_eq(struct dp_state *dp) +{ + bool eq_done, cr_done = true; + int tries = 0, i; + + dp_set_training_pattern(dp, 2); + + do { + if (dp_link_train_update(dp, 400)) + break; + + eq_done = !!(dp->stat[2] & DPCD_LS04_INTERLANE_ALIGN_DONE); + for (i = 0; i < dp->link_nr && eq_done; i++) { + u8 lane = (dp->stat[i >> 1] >> ((i & 1) * 4)) & 0xf; + if (!(lane & DPCD_LS02_LANE0_CR_DONE)) + cr_done = false; + if (!(lane & DPCD_LS02_LANE0_CHANNEL_EQ_DONE) || + !(lane & DPCD_LS02_LANE0_SYMBOL_LOCKED)) + eq_done = false; + } + + if (dp_link_train_commit(dp)) + break; + } while (!eq_done && cr_done && ++tries <= 5); + + return eq_done ? 0 : -1; +} + +static void +dp_link_train_init(struct dp_state *dp, bool spread) +{ + struct nvbios_init init = { + .subdev = nv_subdev(dp->disp), + .bios = nouveau_bios(dp->disp), + .outp = dp->outp, + .crtc = dp->head, + .execute = 1, + }; + + /* set desired spread */ + if (spread) + init.offset = dp->info.script[2]; + else + init.offset = dp->info.script[3]; + nvbios_exec(&init); + + /* pre-train script */ + init.offset = dp->info.script[0]; + nvbios_exec(&init); +} + +static void +dp_link_train_fini(struct dp_state *dp) +{ + struct nvbios_init init = { + .subdev = nv_subdev(dp->disp), + .bios = nouveau_bios(dp->disp), + .outp = dp->outp, + .crtc = dp->head, + .execute = 1, + }; + + /* post-train script */ + init.offset = dp->info.script[1], + nvbios_exec(&init); +} + +int +nouveau_dp_train(struct nouveau_disp *disp, const struct nouveau_dp_func *func, + struct dcb_output *outp, int head, u32 datarate) +{ + struct nouveau_bios *bios = nouveau_bios(disp); + struct nouveau_i2c *i2c = nouveau_i2c(disp); + struct dp_state _dp = { + .disp = disp, + .func = func, + .outp = outp, + .head = head, + }, *dp = &_dp; + const u32 bw_list[] = { 270000, 162000, 0 }; + const u32 *link_bw = bw_list; + u8 hdr, cnt, len; + u32 data; + int ret; + + /* find the bios displayport data relevant to this output */ + data = nvbios_dpout_match(bios, outp->hasht, outp->hashm, &dp->version, + &hdr, &cnt, &len, &dp->info); + if (!data) { + ERR("bios data not found\n"); + return -EINVAL; + } + + /* acquire the aux channel and fetch some info about the display */ + if (outp->location) + dp->aux = i2c->find_type(i2c, NV_I2C_TYPE_EXTAUX(outp->extdev)); + else + dp->aux = i2c->find(i2c, NV_I2C_TYPE_DCBI2C(outp->i2c_index)); + if (!dp->aux) { + ERR("no aux channel?!\n"); + return -ENODEV; + } + + ret = nv_rdaux(dp->aux, 0x00000, dp->dpcd, sizeof(dp->dpcd)); + if (ret) { + ERR("failed to read DPCD\n"); + return ret; + } + + /* adjust required bandwidth for 8B/10B coding overhead */ + datarate = (datarate / 8) * 10; + + /* enable down-spreading and execute pre-train script from vbios */ + dp_link_train_init(dp, dp->dpcd[3] & 0x01); + + /* start off at highest link rate supported by encoder and display */ + while (*link_bw > (dp->dpcd[1] * 27000)) + link_bw++; + + while (link_bw[0]) { + /* find minimum required lane count at this link rate */ + dp->link_nr = dp->dpcd[2] & DPCD_RC02_MAX_LANE_COUNT; + while ((dp->link_nr >> 1) * link_bw[0] > datarate) + dp->link_nr >>= 1; + + /* drop link rate to minimum with this lane count */ + while ((link_bw[1] * dp->link_nr) > datarate) + link_bw++; + dp->link_bw = link_bw[0]; + + /* program selected link configuration */ + ret = dp_set_link_config(dp); + if (ret == 0) { + /* attempt to train the link at this configuration */ + memset(dp->stat, 0x00, sizeof(dp->stat)); + if (!dp_link_train_cr(dp) && + !dp_link_train_eq(dp)) + break; + } else + if (ret >= 1) { + /* dp_set_link_config() handled training */ + break; + } + + /* retry at lower rate */ + link_bw++; + } + + /* finish link training */ + dp_set_training_pattern(dp, 0); + + /* execute post-train script from vbios */ + dp_link_train_fini(dp); + return true; +} diff --git a/drivers/gpu/drm/nouveau/core/engine/disp/dport.h b/drivers/gpu/drm/nouveau/core/engine/disp/dport.h new file mode 100644 index 000000000000..0e1bbd18ff6c --- /dev/null +++ b/drivers/gpu/drm/nouveau/core/engine/disp/dport.h @@ -0,0 +1,78 @@ +#ifndef __NVKM_DISP_DPORT_H__ +#define __NVKM_DISP_DPORT_H__ + +/* DPCD Receiver Capabilities */ +#define DPCD_RC00 0x00000 +#define DPCD_RC00_DPCD_REV 0xff +#define DPCD_RC01 0x00001 +#define DPCD_RC01_MAX_LINK_RATE 0xff +#define DPCD_RC02 0x00002 +#define DPCD_RC02_ENHANCED_FRAME_CAP 0x80 +#define DPCD_RC02_MAX_LANE_COUNT 0x1f +#define DPCD_RC03 0x00003 +#define DPCD_RC03_MAX_DOWNSPREAD 0x01 + +/* DPCD Link Configuration */ +#define DPCD_LC00 0x00100 +#define DPCD_LC00_LINK_BW_SET 0xff +#define DPCD_LC01 0x00101 +#define DPCD_LC01_ENHANCED_FRAME_EN 0x80 +#define DPCD_LC01_LANE_COUNT_SET 0x1f +#define DPCD_LC02 0x00102 +#define DPCD_LC02_TRAINING_PATTERN_SET 0x03 +#define DPCD_LC03(l) ((l) + 0x00103) +#define DPCD_LC03_MAX_PRE_EMPHASIS_REACHED 0x20 +#define DPCD_LC03_PRE_EMPHASIS_SET 0x18 +#define DPCD_LC03_MAX_SWING_REACHED 0x04 +#define DPCD_LC03_VOLTAGE_SWING_SET 0x03 + +/* DPCD Link/Sink Status */ +#define DPCD_LS02 0x00202 +#define DPCD_LS02_LANE1_SYMBOL_LOCKED 0x40 +#define DPCD_LS02_LANE1_CHANNEL_EQ_DONE 0x20 +#define DPCD_LS02_LANE1_CR_DONE 0x10 +#define DPCD_LS02_LANE0_SYMBOL_LOCKED 0x04 +#define DPCD_LS02_LANE0_CHANNEL_EQ_DONE 0x02 +#define DPCD_LS02_LANE0_CR_DONE 0x01 +#define DPCD_LS03 0x00203 +#define DPCD_LS03_LANE3_SYMBOL_LOCKED 0x40 +#define DPCD_LS03_LANE3_CHANNEL_EQ_DONE 0x20 +#define DPCD_LS03_LANE3_CR_DONE 0x10 +#define DPCD_LS03_LANE2_SYMBOL_LOCKED 0x04 +#define DPCD_LS03_LANE2_CHANNEL_EQ_DONE 0x02 +#define DPCD_LS03_LANE2_CR_DONE 0x01 +#define DPCD_LS04 0x00204 +#define DPCD_LS04_LINK_STATUS_UPDATED 0x80 +#define DPCD_LS04_DOWNSTREAM_PORT_STATUS_CHANGED 0x40 +#define DPCD_LS04_INTERLANE_ALIGN_DONE 0x01 +#define DPCD_LS06 0x00206 +#define DPCD_LS06_LANE1_PRE_EMPHASIS 0xc0 +#define DPCD_LS06_LANE1_VOLTAGE_SWING 0x30 +#define DPCD_LS06_LANE0_PRE_EMPHASIS 0x0c +#define DPCD_LS06_LANE0_VOLTAGE_SWING 0x03 +#define DPCD_LS07 0x00207 +#define DPCD_LS07_LANE3_PRE_EMPHASIS 0xc0 +#define DPCD_LS07_LANE3_VOLTAGE_SWING 0x30 +#define DPCD_LS07_LANE2_PRE_EMPHASIS 0x0c +#define DPCD_LS07_LANE2_VOLTAGE_SWING 0x03 + +struct nouveau_disp; +struct dcb_output; + +struct nouveau_dp_func { + int (*pattern)(struct nouveau_disp *, struct dcb_output *, + int head, int pattern); + int (*lnk_ctl)(struct nouveau_disp *, struct dcb_output *, int head, + int link_nr, int link_bw, bool enh_frame); + int (*drv_ctl)(struct nouveau_disp *, struct dcb_output *, int head, + int lane, int swing, int preem); +}; + +extern const struct nouveau_dp_func nv94_sor_dp_func; +extern const struct nouveau_dp_func nvd0_sor_dp_func; +extern const struct nouveau_dp_func nv50_pior_dp_func; + +int nouveau_dp_train(struct nouveau_disp *, const struct nouveau_dp_func *, + struct dcb_output *, int, u32); + +#endif diff --git a/drivers/gpu/drm/nouveau/core/engine/disp/nv04.c b/drivers/gpu/drm/nouveau/core/engine/disp/nv04.c index 1c919f2af89f..05e903f08a36 100644 --- a/drivers/gpu/drm/nouveau/core/engine/disp/nv04.c +++ b/drivers/gpu/drm/nouveau/core/engine/disp/nv04.c @@ -24,21 +24,33 @@ #include <engine/disp.h> +#include <core/event.h> +#include <core/class.h> + struct nv04_disp_priv { struct nouveau_disp base; }; static struct nouveau_oclass nv04_disp_sclass[] = { + { NV04_DISP_CLASS, &nouveau_object_ofuncs }, {}, }; +/******************************************************************************* + * Display engine implementation + ******************************************************************************/ + +static void +nv04_disp_vblank_enable(struct nouveau_event *event, int head) +{ + nv_wr32(event->priv, 0x600140 + (head * 0x2000) , 0x00000001); +} + static void -nv04_disp_intr_vblank(struct nv04_disp_priv *priv, int crtc) +nv04_disp_vblank_disable(struct nouveau_event *event, int head) { - struct nouveau_disp *disp = &priv->base; - if (disp->vblank.notify) - disp->vblank.notify(disp->vblank.data, crtc); + nv_wr32(event->priv, 0x600140 + (head * 0x2000) , 0x00000000); } static void @@ -49,25 +61,25 @@ nv04_disp_intr(struct nouveau_subdev *subdev) u32 crtc1 = nv_rd32(priv, 0x602100); if (crtc0 & 0x00000001) { - nv04_disp_intr_vblank(priv, 0); + nouveau_event_trigger(priv->base.vblank, 0); nv_wr32(priv, 0x600100, 0x00000001); } if (crtc1 & 0x00000001) { - nv04_disp_intr_vblank(priv, 1); + nouveau_event_trigger(priv->base.vblank, 1); nv_wr32(priv, 0x602100, 0x00000001); } } static int nv04_disp_ctor(struct nouveau_object *parent, struct nouveau_object *engine, - struct nouveau_oclass *oclass, void *data, u32 size, - struct nouveau_object **pobject) + struct nouveau_oclass *oclass, void *data, u32 size, + struct nouveau_object **pobject) { struct nv04_disp_priv *priv; int ret; - ret = nouveau_disp_create(parent, engine, oclass, "DISPLAY", + ret = nouveau_disp_create(parent, engine, oclass, 2, "DISPLAY", "display", &priv); *pobject = nv_object(priv); if (ret) @@ -75,6 +87,9 @@ nv04_disp_ctor(struct nouveau_object *parent, struct nouveau_object *engine, nv_engine(priv)->sclass = nv04_disp_sclass; nv_subdev(priv)->intr = nv04_disp_intr; + priv->base.vblank->priv = priv; + priv->base.vblank->enable = nv04_disp_vblank_enable; + priv->base.vblank->disable = nv04_disp_vblank_disable; return 0; } diff --git a/drivers/gpu/drm/nouveau/core/engine/disp/nv50.c b/drivers/gpu/drm/nouveau/core/engine/disp/nv50.c index ca1a7d76a95b..5fa13267bd9f 100644 --- a/drivers/gpu/drm/nouveau/core/engine/disp/nv50.c +++ b/drivers/gpu/drm/nouveau/core/engine/disp/nv50.c @@ -27,7 +27,6 @@ #include <core/handle.h> #include <core/class.h> -#include <engine/software.h> #include <engine/disp.h> #include <subdev/bios.h> @@ -37,7 +36,6 @@ #include <subdev/bios/pll.h> #include <subdev/timer.h> #include <subdev/fb.h> -#include <subdev/bar.h> #include <subdev/clock.h> #include "nv50.h" @@ -335,7 +333,7 @@ nv50_disp_sync_ctor(struct nouveau_object *parent, struct nv50_disp_dmac *dmac; int ret; - if (size < sizeof(*data) || args->head > 1) + if (size < sizeof(*args) || args->head > 1) return -EINVAL; ret = nv50_disp_dmac_create_(parent, engine, oclass, args->pushbuf, @@ -374,7 +372,7 @@ nv50_disp_ovly_ctor(struct nouveau_object *parent, struct nv50_disp_dmac *dmac; int ret; - if (size < sizeof(*data) || args->head > 1) + if (size < sizeof(*args) || args->head > 1) return -EINVAL; ret = nv50_disp_dmac_create_(parent, engine, oclass, args->pushbuf, @@ -543,6 +541,18 @@ nv50_disp_curs_ofuncs = { * Base display object ******************************************************************************/ +static void +nv50_disp_base_vblank_enable(struct nouveau_event *event, int head) +{ + nv_mask(event->priv, 0x61002c, (1 << head), (1 << head)); +} + +static void +nv50_disp_base_vblank_disable(struct nouveau_event *event, int head) +{ + nv_mask(event->priv, 0x61002c, (1 << head), (0 << head)); +} + static int nv50_disp_base_ctor(struct nouveau_object *parent, struct nouveau_object *engine, @@ -559,6 +569,9 @@ nv50_disp_base_ctor(struct nouveau_object *parent, if (ret) return ret; + priv->base.vblank->priv = priv; + priv->base.vblank->enable = nv50_disp_base_vblank_enable; + priv->base.vblank->disable = nv50_disp_base_vblank_disable; return nouveau_ramht_new(parent, parent, 0x1000, 0, &base->ramht); } @@ -613,7 +626,7 @@ nv50_disp_base_init(struct nouveau_object *object) nv_wr32(priv, 0x6101e0 + (i * 0x04), tmp); } - /* ... EXT caps */ + /* ... PIOR caps */ for (i = 0; i < 3; i++) { tmp = nv_rd32(priv, 0x61e000 + (i * 0x800)); nv_wr32(priv, 0x6101f0 + (i * 0x04), tmp); @@ -665,6 +678,9 @@ nv50_disp_base_omthds[] = { { SOR_MTHD(NV50_DISP_SOR_LVDS_SCRIPT) , nv50_sor_mthd }, { DAC_MTHD(NV50_DISP_DAC_PWR) , nv50_dac_mthd }, { DAC_MTHD(NV50_DISP_DAC_LOAD) , nv50_dac_mthd }, + { PIOR_MTHD(NV50_DISP_PIOR_PWR) , nv50_pior_mthd }, + { PIOR_MTHD(NV50_DISP_PIOR_TMDS_PWR) , nv50_pior_mthd }, + { PIOR_MTHD(NV50_DISP_PIOR_DP_PWR) , nv50_pior_mthd }, {}, }; @@ -756,50 +772,6 @@ nv50_disp_intr_error(struct nv50_disp_priv *priv) } } -static void -nv50_disp_intr_vblank(struct nv50_disp_priv *priv, int crtc) -{ - struct nouveau_bar *bar = nouveau_bar(priv); - struct nouveau_disp *disp = &priv->base; - struct nouveau_software_chan *chan, *temp; - unsigned long flags; - - spin_lock_irqsave(&disp->vblank.lock, flags); - list_for_each_entry_safe(chan, temp, &disp->vblank.list, vblank.head) { - if (chan->vblank.crtc != crtc) - continue; - - if (nv_device(priv)->chipset >= 0xc0) { - nv_wr32(priv, 0x001718, 0x80000000 | chan->vblank.channel); - bar->flush(bar); - nv_wr32(priv, 0x06000c, - upper_32_bits(chan->vblank.offset)); - nv_wr32(priv, 0x060010, - lower_32_bits(chan->vblank.offset)); - nv_wr32(priv, 0x060014, chan->vblank.value); - } else { - nv_wr32(priv, 0x001704, chan->vblank.channel); - nv_wr32(priv, 0x001710, 0x80000000 | chan->vblank.ctxdma); - bar->flush(bar); - if (nv_device(priv)->chipset == 0x50) { - nv_wr32(priv, 0x001570, chan->vblank.offset); - nv_wr32(priv, 0x001574, chan->vblank.value); - } else { - nv_wr32(priv, 0x060010, chan->vblank.offset); - nv_wr32(priv, 0x060014, chan->vblank.value); - } - } - - list_del(&chan->vblank.head); - if (disp->vblank.put) - disp->vblank.put(disp->vblank.data, crtc); - } - spin_unlock_irqrestore(&disp->vblank.lock, flags); - - if (disp->vblank.notify) - disp->vblank.notify(disp->vblank.data, crtc); -} - static u16 exec_lookup(struct nv50_disp_priv *priv, int head, int outp, u32 ctrl, struct dcb_output *dcb, u8 *ver, u8 *hdr, u8 *cnt, u8 *len, @@ -811,8 +783,8 @@ exec_lookup(struct nv50_disp_priv *priv, int head, int outp, u32 ctrl, if (outp < 4) { type = DCB_OUTPUT_ANALOG; mask = 0; - } else { - outp -= 4; + } else + if (outp < 8) { switch (ctrl & 0x00000f00) { case 0x00000000: type = DCB_OUTPUT_LVDS; mask = 1; break; case 0x00000100: type = DCB_OUTPUT_TMDS; mask = 1; break; @@ -824,6 +796,17 @@ exec_lookup(struct nv50_disp_priv *priv, int head, int outp, u32 ctrl, nv_error(priv, "unknown SOR mc 0x%08x\n", ctrl); return 0x0000; } + outp -= 4; + } else { + outp = outp - 8; + type = 0x0010; + mask = 0; + switch (ctrl & 0x00000f00) { + case 0x00000000: type |= priv->pior.type[outp]; break; + default: + nv_error(priv, "unknown PIOR mc 0x%08x\n", ctrl); + return 0x0000; + } } mask = 0x00c0 & (mask << 6); @@ -834,6 +817,10 @@ exec_lookup(struct nv50_disp_priv *priv, int head, int outp, u32 ctrl, if (!data) return 0x0000; + /* off-chip encoders require matching the exact encoder type */ + if (dcb->location != 0) + type |= dcb->extdev << 8; + return nvbios_outp_match(bios, type, mask, ver, hdr, cnt, len, info); } @@ -848,9 +835,11 @@ exec_script(struct nv50_disp_priv *priv, int head, int id) u32 ctrl = 0x00000000; int i; + /* DAC */ for (i = 0; !(ctrl & (1 << head)) && i < 3; i++) ctrl = nv_rd32(priv, 0x610b5c + (i * 8)); + /* SOR */ if (!(ctrl & (1 << head))) { if (nv_device(priv)->chipset < 0x90 || nv_device(priv)->chipset == 0x92 || @@ -865,6 +854,13 @@ exec_script(struct nv50_disp_priv *priv, int head, int id) } } + /* PIOR */ + if (!(ctrl & (1 << head))) { + for (i = 0; !(ctrl & (1 << head)) && i < 3; i++) + ctrl = nv_rd32(priv, 0x610b84 + (i * 8)); + i += 8; + } + if (!(ctrl & (1 << head))) return false; i--; @@ -894,13 +890,15 @@ exec_clkcmp(struct nv50_disp_priv *priv, int head, int id, u32 pclk, struct nvbios_outp info1; struct nvbios_ocfg info2; u8 ver, hdr, cnt, len; - u16 data, conf; u32 ctrl = 0x00000000; + u32 data, conf = ~0; int i; + /* DAC */ for (i = 0; !(ctrl & (1 << head)) && i < 3; i++) ctrl = nv_rd32(priv, 0x610b58 + (i * 8)); + /* SOR */ if (!(ctrl & (1 << head))) { if (nv_device(priv)->chipset < 0x90 || nv_device(priv)->chipset == 0x92 || @@ -915,34 +913,46 @@ exec_clkcmp(struct nv50_disp_priv *priv, int head, int id, u32 pclk, } } + /* PIOR */ + if (!(ctrl & (1 << head))) { + for (i = 0; !(ctrl & (1 << head)) && i < 3; i++) + ctrl = nv_rd32(priv, 0x610b80 + (i * 8)); + i += 8; + } + if (!(ctrl & (1 << head))) - return 0x0000; + return conf; i--; data = exec_lookup(priv, head, i, ctrl, outp, &ver, &hdr, &cnt, &len, &info1); if (!data) - return 0x0000; - - switch (outp->type) { - case DCB_OUTPUT_TMDS: - conf = (ctrl & 0x00000f00) >> 8; - if (pclk >= 165000) - conf |= 0x0100; - break; - case DCB_OUTPUT_LVDS: - conf = priv->sor.lvdsconf; - break; - case DCB_OUTPUT_DP: + return conf; + + if (outp->location == 0) { + switch (outp->type) { + case DCB_OUTPUT_TMDS: + conf = (ctrl & 0x00000f00) >> 8; + if (pclk >= 165000) + conf |= 0x0100; + break; + case DCB_OUTPUT_LVDS: + conf = priv->sor.lvdsconf; + break; + case DCB_OUTPUT_DP: + conf = (ctrl & 0x00000f00) >> 8; + break; + case DCB_OUTPUT_ANALOG: + default: + conf = 0x00ff; + break; + } + } else { conf = (ctrl & 0x00000f00) >> 8; - break; - case DCB_OUTPUT_ANALOG: - default: - conf = 0x00ff; - break; + pclk = pclk / 2; } data = nvbios_ocfg_match(bios, data, conf, &ver, &hdr, &cnt, &len, &info2); - if (data) { + if (data && id < 0xff) { data = nvbios_oclk_match(bios, info2.clkcmp[id], pclk); if (data) { struct nvbios_init init = { @@ -954,32 +964,37 @@ exec_clkcmp(struct nv50_disp_priv *priv, int head, int id, u32 pclk, .execute = 1, }; - if (nvbios_exec(&init)) - return 0x0000; - return conf; + nvbios_exec(&init); } } - return 0x0000; + return conf; } static void -nv50_disp_intr_unk10(struct nv50_disp_priv *priv, u32 super) +nv50_disp_intr_unk10_0(struct nv50_disp_priv *priv, int head) { - int head = ffs((super & 0x00000060) >> 5) - 1; - if (head >= 0) { - head = ffs((super & 0x00000180) >> 7) - 1; - if (head >= 0) - exec_script(priv, head, 1); - } + exec_script(priv, head, 1); +} - nv_wr32(priv, 0x610024, 0x00000010); - nv_wr32(priv, 0x610030, 0x80000000); +static void +nv50_disp_intr_unk20_0(struct nv50_disp_priv *priv, int head) +{ + exec_script(priv, head, 2); +} + +static void +nv50_disp_intr_unk20_1(struct nv50_disp_priv *priv, int head) +{ + struct nouveau_clock *clk = nouveau_clock(priv); + u32 pclk = nv_rd32(priv, 0x610ad0 + (head * 0x540)) & 0x3fffff; + if (pclk) + clk->pll_set(clk, PLL_VPLL0 + head, pclk); } static void -nv50_disp_intr_unk20_dp(struct nv50_disp_priv *priv, - struct dcb_output *outp, u32 pclk) +nv50_disp_intr_unk20_2_dp(struct nv50_disp_priv *priv, + struct dcb_output *outp, u32 pclk) { const int link = !(outp->sorconf.link & 1); const int or = ffs(outp->or) - 1; @@ -1085,53 +1100,54 @@ nv50_disp_intr_unk20_dp(struct nv50_disp_priv *priv, } static void -nv50_disp_intr_unk20(struct nv50_disp_priv *priv, u32 super) +nv50_disp_intr_unk20_2(struct nv50_disp_priv *priv, int head) { struct dcb_output outp; - u32 addr, mask, data; - int head; + u32 pclk = nv_rd32(priv, 0x610ad0 + (head * 0x540)) & 0x3fffff; + u32 hval, hreg = 0x614200 + (head * 0x800); + u32 oval, oreg; + u32 conf = exec_clkcmp(priv, head, 0xff, pclk, &outp); + if (conf != ~0) { + if (outp.location == 0 && outp.type == DCB_OUTPUT_DP) { + u32 soff = (ffs(outp.or) - 1) * 0x08; + u32 ctrl = nv_rd32(priv, 0x610798 + soff); + u32 datarate; + + switch ((ctrl & 0x000f0000) >> 16) { + case 6: datarate = pclk * 30 / 8; break; + case 5: datarate = pclk * 24 / 8; break; + case 2: + default: + datarate = pclk * 18 / 8; + break; + } - /* finish detaching encoder? */ - head = ffs((super & 0x00000180) >> 7) - 1; - if (head >= 0) - exec_script(priv, head, 2); - - /* check whether a vpll change is required */ - head = ffs((super & 0x00000600) >> 9) - 1; - if (head >= 0) { - u32 pclk = nv_rd32(priv, 0x610ad0 + (head * 0x540)) & 0x3fffff; - if (pclk) { - struct nouveau_clock *clk = nouveau_clock(priv); - clk->pll_set(clk, PLL_VPLL0 + head, pclk); + nouveau_dp_train(&priv->base, priv->sor.dp, + &outp, head, datarate); } - nv_mask(priv, 0x614200 + head * 0x800, 0x0000000f, 0x00000000); - } - - /* (re)attach the relevant OR to the head */ - head = ffs((super & 0x00000180) >> 7) - 1; - if (head >= 0) { - u32 pclk = nv_rd32(priv, 0x610ad0 + (head * 0x540)) & 0x3fffff; - u32 conf = exec_clkcmp(priv, head, 0, pclk, &outp); - if (conf) { - if (outp.type == DCB_OUTPUT_ANALOG) { - addr = 0x614280 + (ffs(outp.or) - 1) * 0x800; - mask = 0xffffffff; - data = 0x00000000; - } else { - if (outp.type == DCB_OUTPUT_DP) - nv50_disp_intr_unk20_dp(priv, &outp, pclk); - addr = 0x614300 + (ffs(outp.or) - 1) * 0x800; - mask = 0x00000707; - data = (conf & 0x0100) ? 0x0101 : 0x0000; - } - - nv_mask(priv, addr, mask, data); + exec_clkcmp(priv, head, 0, pclk, &outp); + + if (!outp.location && outp.type == DCB_OUTPUT_ANALOG) { + oreg = 0x614280 + (ffs(outp.or) - 1) * 0x800; + oval = 0x00000000; + hval = 0x00000000; + } else + if (!outp.location) { + if (outp.type == DCB_OUTPUT_DP) + nv50_disp_intr_unk20_2_dp(priv, &outp, pclk); + oreg = 0x614300 + (ffs(outp.or) - 1) * 0x800; + oval = (conf & 0x0100) ? 0x00000101 : 0x00000000; + hval = 0x00000000; + } else { + oreg = 0x614380 + (ffs(outp.or) - 1) * 0x800; + oval = 0x00000001; + hval = 0x00000001; } - } - nv_wr32(priv, 0x610024, 0x00000020); - nv_wr32(priv, 0x610030, 0x80000000); + nv_mask(priv, hreg, 0x0000000f, hval); + nv_mask(priv, oreg, 0x00000707, oval); + } } /* If programming a TMDS output on a SOR that can also be configured for @@ -1143,7 +1159,7 @@ nv50_disp_intr_unk20(struct nv50_disp_priv *priv, u32 super) * programmed for DisplayPort. */ static void -nv50_disp_intr_unk40_tmds(struct nv50_disp_priv *priv, struct dcb_output *outp) +nv50_disp_intr_unk40_0_tmds(struct nv50_disp_priv *priv, struct dcb_output *outp) { struct nouveau_bios *bios = nouveau_bios(priv); const int link = !(outp->sorconf.link & 1); @@ -1157,35 +1173,79 @@ nv50_disp_intr_unk40_tmds(struct nv50_disp_priv *priv, struct dcb_output *outp) } static void -nv50_disp_intr_unk40(struct nv50_disp_priv *priv, u32 super) +nv50_disp_intr_unk40_0(struct nv50_disp_priv *priv, int head) { - int head = ffs((super & 0x00000180) >> 7) - 1; - if (head >= 0) { - struct dcb_output outp; - u32 pclk = nv_rd32(priv, 0x610ad0 + (head * 0x540)) & 0x3fffff; - if (pclk && exec_clkcmp(priv, head, 1, pclk, &outp)) { - if (outp.type == DCB_OUTPUT_TMDS) - nv50_disp_intr_unk40_tmds(priv, &outp); + struct dcb_output outp; + u32 pclk = nv_rd32(priv, 0x610ad0 + (head * 0x540)) & 0x3fffff; + if (exec_clkcmp(priv, head, 1, pclk, &outp) != ~0) { + if (outp.location == 0 && outp.type == DCB_OUTPUT_TMDS) + nv50_disp_intr_unk40_0_tmds(priv, &outp); + else + if (outp.location == 1 && outp.type == DCB_OUTPUT_DP) { + u32 soff = (ffs(outp.or) - 1) * 0x08; + u32 ctrl = nv_rd32(priv, 0x610b84 + soff); + u32 datarate; + + switch ((ctrl & 0x000f0000) >> 16) { + case 6: datarate = pclk * 30 / 8; break; + case 5: datarate = pclk * 24 / 8; break; + case 2: + default: + datarate = pclk * 18 / 8; + break; + } + + nouveau_dp_train(&priv->base, priv->pior.dp, + &outp, head, datarate); } } - - nv_wr32(priv, 0x610024, 0x00000040); - nv_wr32(priv, 0x610030, 0x80000000); } -static void -nv50_disp_intr_super(struct nv50_disp_priv *priv, u32 intr1) +void +nv50_disp_intr_supervisor(struct work_struct *work) { + struct nv50_disp_priv *priv = + container_of(work, struct nv50_disp_priv, supervisor); u32 super = nv_rd32(priv, 0x610030); + int head; - nv_debug(priv, "supervisor 0x%08x 0x%08x\n", intr1, super); + nv_debug(priv, "supervisor 0x%08x 0x%08x\n", priv->super, super); - if (intr1 & 0x00000010) - nv50_disp_intr_unk10(priv, super); - if (intr1 & 0x00000020) - nv50_disp_intr_unk20(priv, super); - if (intr1 & 0x00000040) - nv50_disp_intr_unk40(priv, super); + if (priv->super & 0x00000010) { + for (head = 0; head < priv->head.nr; head++) { + if (!(super & (0x00000020 << head))) + continue; + if (!(super & (0x00000080 << head))) + continue; + nv50_disp_intr_unk10_0(priv, head); + } + } else + if (priv->super & 0x00000020) { + for (head = 0; head < priv->head.nr; head++) { + if (!(super & (0x00000080 << head))) + continue; + nv50_disp_intr_unk20_0(priv, head); + } + for (head = 0; head < priv->head.nr; head++) { + if (!(super & (0x00000200 << head))) + continue; + nv50_disp_intr_unk20_1(priv, head); + } + for (head = 0; head < priv->head.nr; head++) { + if (!(super & (0x00000080 << head))) + continue; + nv50_disp_intr_unk20_2(priv, head); + } + } else + if (priv->super & 0x00000040) { + for (head = 0; head < priv->head.nr; head++) { + if (!(super & (0x00000080 << head))) + continue; + nv50_disp_intr_unk40_0(priv, head); + } + } + + nv_wr32(priv, 0x610030, 0x80000000); } void @@ -1201,19 +1261,21 @@ nv50_disp_intr(struct nouveau_subdev *subdev) } if (intr1 & 0x00000004) { - nv50_disp_intr_vblank(priv, 0); + nouveau_event_trigger(priv->base.vblank, 0); nv_wr32(priv, 0x610024, 0x00000004); intr1 &= ~0x00000004; } if (intr1 & 0x00000008) { - nv50_disp_intr_vblank(priv, 1); + nouveau_event_trigger(priv->base.vblank, 1); nv_wr32(priv, 0x610024, 0x00000008); intr1 &= ~0x00000008; } if (intr1 & 0x00000070) { - nv50_disp_intr_super(priv, intr1); + priv->super = (intr1 & 0x00000070); + schedule_work(&priv->supervisor); + nv_wr32(priv, 0x610024, priv->super); intr1 &= ~0x00000070; } } @@ -1226,7 +1288,7 @@ nv50_disp_ctor(struct nouveau_object *parent, struct nouveau_object *engine, struct nv50_disp_priv *priv; int ret; - ret = nouveau_disp_create(parent, engine, oclass, "PDISP", + ret = nouveau_disp_create(parent, engine, oclass, 2, "PDISP", "display", &priv); *pobject = nv_object(priv); if (ret) @@ -1235,16 +1297,17 @@ nv50_disp_ctor(struct nouveau_object *parent, struct nouveau_object *engine, nv_engine(priv)->sclass = nv50_disp_base_oclass; nv_engine(priv)->cclass = &nv50_disp_cclass; nv_subdev(priv)->intr = nv50_disp_intr; + INIT_WORK(&priv->supervisor, nv50_disp_intr_supervisor); priv->sclass = nv50_disp_sclass; priv->head.nr = 2; priv->dac.nr = 3; priv->sor.nr = 2; + priv->pior.nr = 3; priv->dac.power = nv50_dac_power; priv->dac.sense = nv50_dac_sense; priv->sor.power = nv50_sor_power; - - INIT_LIST_HEAD(&priv->base.vblank.list); - spin_lock_init(&priv->base.vblank.lock); + priv->pior.power = nv50_pior_power; + priv->pior.dp = &nv50_pior_dp_func; return 0; } diff --git a/drivers/gpu/drm/nouveau/core/engine/disp/nv50.h b/drivers/gpu/drm/nouveau/core/engine/disp/nv50.h index a6bb931450f1..1ae6ceb56704 100644 --- a/drivers/gpu/drm/nouveau/core/engine/disp/nv50.h +++ b/drivers/gpu/drm/nouveau/core/engine/disp/nv50.h @@ -3,16 +3,22 @@ #include <core/parent.h> #include <core/namedb.h> +#include <core/engctx.h> #include <core/ramht.h> +#include <core/event.h> #include <engine/dmaobj.h> #include <engine/disp.h> -struct dcb_output; +#include "dport.h" struct nv50_disp_priv { struct nouveau_disp base; struct nouveau_oclass *sclass; + + struct work_struct supervisor; + u32 super; + struct { int nr; } head; @@ -26,23 +32,15 @@ struct nv50_disp_priv { int (*power)(struct nv50_disp_priv *, int sor, u32 data); int (*hda_eld)(struct nv50_disp_priv *, int sor, u8 *, u32); int (*hdmi)(struct nv50_disp_priv *, int head, int sor, u32); - int (*dp_train_init)(struct nv50_disp_priv *, int sor, int link, - int head, u16 type, u16 mask, u32 data, - struct dcb_output *); - int (*dp_train_fini)(struct nv50_disp_priv *, int sor, int link, - int head, u16 type, u16 mask, u32 data, - struct dcb_output *); - int (*dp_train)(struct nv50_disp_priv *, int sor, int link, - u16 type, u16 mask, u32 data, - struct dcb_output *); - int (*dp_lnkctl)(struct nv50_disp_priv *, int sor, int link, - int head, u16 type, u16 mask, u32 data, - struct dcb_output *); - int (*dp_drvctl)(struct nv50_disp_priv *, int sor, int link, - int lane, u16 type, u16 mask, u32 data, - struct dcb_output *); u32 lvdsconf; + const struct nouveau_dp_func *dp; } sor; + struct { + int nr; + int (*power)(struct nv50_disp_priv *, int ext, u32 data); + u8 type[3]; + const struct nouveau_dp_func *dp; + } pior; }; #define DAC_MTHD(n) (n), (n) + 0x03 @@ -81,6 +79,11 @@ int nvd0_sor_dp_lnkctl(struct nv50_disp_priv *, int, int, int, u16, u16, u32, int nvd0_sor_dp_drvctl(struct nv50_disp_priv *, int, int, int, u16, u16, u32, struct dcb_output *); +#define PIOR_MTHD(n) (n), (n) + 0x03 + +int nv50_pior_mthd(struct nouveau_object *, u32, void *, u32); +int nv50_pior_power(struct nv50_disp_priv *, int, u32); + struct nv50_disp_base { struct nouveau_parent base; struct nouveau_ramht *ramht; @@ -124,6 +127,7 @@ extern struct nouveau_ofuncs nv50_disp_oimm_ofuncs; extern struct nouveau_ofuncs nv50_disp_curs_ofuncs; extern struct nouveau_ofuncs nv50_disp_base_ofuncs; extern struct nouveau_oclass nv50_disp_cclass; +void nv50_disp_intr_supervisor(struct work_struct *); void nv50_disp_intr(struct nouveau_subdev *); extern struct nouveau_omthds nv84_disp_base_omthds[]; @@ -137,6 +141,7 @@ extern struct nouveau_ofuncs nvd0_disp_oimm_ofuncs; extern struct nouveau_ofuncs nvd0_disp_curs_ofuncs; extern struct nouveau_ofuncs nvd0_disp_base_ofuncs; extern struct nouveau_oclass nvd0_disp_cclass; +void nvd0_disp_intr_supervisor(struct work_struct *); void nvd0_disp_intr(struct nouveau_subdev *); #endif diff --git a/drivers/gpu/drm/nouveau/core/engine/disp/nv84.c b/drivers/gpu/drm/nouveau/core/engine/disp/nv84.c index fc84eacdfbec..d8c74c0883a1 100644 --- a/drivers/gpu/drm/nouveau/core/engine/disp/nv84.c +++ b/drivers/gpu/drm/nouveau/core/engine/disp/nv84.c @@ -46,6 +46,9 @@ nv84_disp_base_omthds[] = { { SOR_MTHD(NV50_DISP_SOR_LVDS_SCRIPT) , nv50_sor_mthd }, { DAC_MTHD(NV50_DISP_DAC_PWR) , nv50_dac_mthd }, { DAC_MTHD(NV50_DISP_DAC_LOAD) , nv50_dac_mthd }, + { PIOR_MTHD(NV50_DISP_PIOR_PWR) , nv50_pior_mthd }, + { PIOR_MTHD(NV50_DISP_PIOR_TMDS_PWR) , nv50_pior_mthd }, + { PIOR_MTHD(NV50_DISP_PIOR_DP_PWR) , nv50_pior_mthd }, {}, }; @@ -63,7 +66,7 @@ nv84_disp_ctor(struct nouveau_object *parent, struct nouveau_object *engine, struct nv50_disp_priv *priv; int ret; - ret = nouveau_disp_create(parent, engine, oclass, "PDISP", + ret = nouveau_disp_create(parent, engine, oclass, 2, "PDISP", "display", &priv); *pobject = nv_object(priv); if (ret) @@ -72,17 +75,18 @@ nv84_disp_ctor(struct nouveau_object *parent, struct nouveau_object *engine, nv_engine(priv)->sclass = nv84_disp_base_oclass; nv_engine(priv)->cclass = &nv50_disp_cclass; nv_subdev(priv)->intr = nv50_disp_intr; + INIT_WORK(&priv->supervisor, nv50_disp_intr_supervisor); priv->sclass = nv84_disp_sclass; priv->head.nr = 2; priv->dac.nr = 3; priv->sor.nr = 2; + priv->pior.nr = 3; priv->dac.power = nv50_dac_power; priv->dac.sense = nv50_dac_sense; priv->sor.power = nv50_sor_power; priv->sor.hdmi = nv84_hdmi_ctrl; - - INIT_LIST_HEAD(&priv->base.vblank.list); - spin_lock_init(&priv->base.vblank.lock); + priv->pior.power = nv50_pior_power; + priv->pior.dp = &nv50_pior_dp_func; return 0; } diff --git a/drivers/gpu/drm/nouveau/core/engine/disp/nv94.c b/drivers/gpu/drm/nouveau/core/engine/disp/nv94.c index ba9dfd4669a2..a66f949c1f84 100644 --- a/drivers/gpu/drm/nouveau/core/engine/disp/nv94.c +++ b/drivers/gpu/drm/nouveau/core/engine/disp/nv94.c @@ -44,14 +44,11 @@ nv94_disp_base_omthds[] = { { SOR_MTHD(NV50_DISP_SOR_PWR) , nv50_sor_mthd }, { SOR_MTHD(NV84_DISP_SOR_HDMI_PWR) , nv50_sor_mthd }, { SOR_MTHD(NV50_DISP_SOR_LVDS_SCRIPT) , nv50_sor_mthd }, - { SOR_MTHD(NV94_DISP_SOR_DP_TRAIN) , nv50_sor_mthd }, - { SOR_MTHD(NV94_DISP_SOR_DP_LNKCTL) , nv50_sor_mthd }, - { SOR_MTHD(NV94_DISP_SOR_DP_DRVCTL(0)), nv50_sor_mthd }, - { SOR_MTHD(NV94_DISP_SOR_DP_DRVCTL(1)), nv50_sor_mthd }, - { SOR_MTHD(NV94_DISP_SOR_DP_DRVCTL(2)), nv50_sor_mthd }, - { SOR_MTHD(NV94_DISP_SOR_DP_DRVCTL(3)), nv50_sor_mthd }, { DAC_MTHD(NV50_DISP_DAC_PWR) , nv50_dac_mthd }, { DAC_MTHD(NV50_DISP_DAC_LOAD) , nv50_dac_mthd }, + { PIOR_MTHD(NV50_DISP_PIOR_PWR) , nv50_pior_mthd }, + { PIOR_MTHD(NV50_DISP_PIOR_TMDS_PWR) , nv50_pior_mthd }, + { PIOR_MTHD(NV50_DISP_PIOR_DP_PWR) , nv50_pior_mthd }, {}, }; @@ -69,7 +66,7 @@ nv94_disp_ctor(struct nouveau_object *parent, struct nouveau_object *engine, struct nv50_disp_priv *priv; int ret; - ret = nouveau_disp_create(parent, engine, oclass, "PDISP", + ret = nouveau_disp_create(parent, engine, oclass, 2, "PDISP", "display", &priv); *pobject = nv_object(priv); if (ret) @@ -78,22 +75,19 @@ nv94_disp_ctor(struct nouveau_object *parent, struct nouveau_object *engine, nv_engine(priv)->sclass = nv94_disp_base_oclass; nv_engine(priv)->cclass = &nv50_disp_cclass; nv_subdev(priv)->intr = nv50_disp_intr; + INIT_WORK(&priv->supervisor, nv50_disp_intr_supervisor); priv->sclass = nv94_disp_sclass; priv->head.nr = 2; priv->dac.nr = 3; priv->sor.nr = 4; + priv->pior.nr = 3; priv->dac.power = nv50_dac_power; priv->dac.sense = nv50_dac_sense; priv->sor.power = nv50_sor_power; priv->sor.hdmi = nv84_hdmi_ctrl; - priv->sor.dp_train = nv94_sor_dp_train; - priv->sor.dp_train_init = nv94_sor_dp_train_init; - priv->sor.dp_train_fini = nv94_sor_dp_train_fini; - priv->sor.dp_lnkctl = nv94_sor_dp_lnkctl; - priv->sor.dp_drvctl = nv94_sor_dp_drvctl; - - INIT_LIST_HEAD(&priv->base.vblank.list); - spin_lock_init(&priv->base.vblank.lock); + priv->sor.dp = &nv94_sor_dp_func; + priv->pior.power = nv50_pior_power; + priv->pior.dp = &nv50_pior_dp_func; return 0; } diff --git a/drivers/gpu/drm/nouveau/core/engine/disp/nva0.c b/drivers/gpu/drm/nouveau/core/engine/disp/nva0.c index 5d63902cdeda..6cf8eefac368 100644 --- a/drivers/gpu/drm/nouveau/core/engine/disp/nva0.c +++ b/drivers/gpu/drm/nouveau/core/engine/disp/nva0.c @@ -53,7 +53,7 @@ nva0_disp_ctor(struct nouveau_object *parent, struct nouveau_object *engine, struct nv50_disp_priv *priv; int ret; - ret = nouveau_disp_create(parent, engine, oclass, "PDISP", + ret = nouveau_disp_create(parent, engine, oclass, 2, "PDISP", "display", &priv); *pobject = nv_object(priv); if (ret) @@ -62,17 +62,18 @@ nva0_disp_ctor(struct nouveau_object *parent, struct nouveau_object *engine, nv_engine(priv)->sclass = nva0_disp_base_oclass; nv_engine(priv)->cclass = &nv50_disp_cclass; nv_subdev(priv)->intr = nv50_disp_intr; + INIT_WORK(&priv->supervisor, nv50_disp_intr_supervisor); priv->sclass = nva0_disp_sclass; priv->head.nr = 2; priv->dac.nr = 3; priv->sor.nr = 2; + priv->pior.nr = 3; priv->dac.power = nv50_dac_power; priv->dac.sense = nv50_dac_sense; priv->sor.power = nv50_sor_power; priv->sor.hdmi = nv84_hdmi_ctrl; - - INIT_LIST_HEAD(&priv->base.vblank.list); - spin_lock_init(&priv->base.vblank.lock); + priv->pior.power = nv50_pior_power; + priv->pior.dp = &nv50_pior_dp_func; return 0; } diff --git a/drivers/gpu/drm/nouveau/core/engine/disp/nva3.c b/drivers/gpu/drm/nouveau/core/engine/disp/nva3.c index e9192ca389fa..b75413169eae 100644 --- a/drivers/gpu/drm/nouveau/core/engine/disp/nva3.c +++ b/drivers/gpu/drm/nouveau/core/engine/disp/nva3.c @@ -45,14 +45,11 @@ nva3_disp_base_omthds[] = { { SOR_MTHD(NVA3_DISP_SOR_HDA_ELD) , nv50_sor_mthd }, { SOR_MTHD(NV84_DISP_SOR_HDMI_PWR) , nv50_sor_mthd }, { SOR_MTHD(NV50_DISP_SOR_LVDS_SCRIPT) , nv50_sor_mthd }, - { SOR_MTHD(NV94_DISP_SOR_DP_TRAIN) , nv50_sor_mthd }, - { SOR_MTHD(NV94_DISP_SOR_DP_LNKCTL) , nv50_sor_mthd }, - { SOR_MTHD(NV94_DISP_SOR_DP_DRVCTL(0)), nv50_sor_mthd }, - { SOR_MTHD(NV94_DISP_SOR_DP_DRVCTL(1)), nv50_sor_mthd }, - { SOR_MTHD(NV94_DISP_SOR_DP_DRVCTL(2)), nv50_sor_mthd }, - { SOR_MTHD(NV94_DISP_SOR_DP_DRVCTL(3)), nv50_sor_mthd }, { DAC_MTHD(NV50_DISP_DAC_PWR) , nv50_dac_mthd }, { DAC_MTHD(NV50_DISP_DAC_LOAD) , nv50_dac_mthd }, + { PIOR_MTHD(NV50_DISP_PIOR_PWR) , nv50_pior_mthd }, + { PIOR_MTHD(NV50_DISP_PIOR_TMDS_PWR) , nv50_pior_mthd }, + { PIOR_MTHD(NV50_DISP_PIOR_DP_PWR) , nv50_pior_mthd }, {}, }; @@ -70,7 +67,7 @@ nva3_disp_ctor(struct nouveau_object *parent, struct nouveau_object *engine, struct nv50_disp_priv *priv; int ret; - ret = nouveau_disp_create(parent, engine, oclass, "PDISP", + ret = nouveau_disp_create(parent, engine, oclass, 2, "PDISP", "display", &priv); *pobject = nv_object(priv); if (ret) @@ -79,23 +76,20 @@ nva3_disp_ctor(struct nouveau_object *parent, struct nouveau_object *engine, nv_engine(priv)->sclass = nva3_disp_base_oclass; nv_engine(priv)->cclass = &nv50_disp_cclass; nv_subdev(priv)->intr = nv50_disp_intr; + INIT_WORK(&priv->supervisor, nv50_disp_intr_supervisor); priv->sclass = nva3_disp_sclass; priv->head.nr = 2; priv->dac.nr = 3; priv->sor.nr = 4; + priv->pior.nr = 3; priv->dac.power = nv50_dac_power; priv->dac.sense = nv50_dac_sense; priv->sor.power = nv50_sor_power; priv->sor.hda_eld = nva3_hda_eld; priv->sor.hdmi = nva3_hdmi_ctrl; - priv->sor.dp_train = nv94_sor_dp_train; - priv->sor.dp_train_init = nv94_sor_dp_train_init; - priv->sor.dp_train_fini = nv94_sor_dp_train_fini; - priv->sor.dp_lnkctl = nv94_sor_dp_lnkctl; - priv->sor.dp_drvctl = nv94_sor_dp_drvctl; - - INIT_LIST_HEAD(&priv->base.vblank.list); - spin_lock_init(&priv->base.vblank.lock); + priv->sor.dp = &nv94_sor_dp_func; + priv->pior.power = nv50_pior_power; + priv->pior.dp = &nv50_pior_dp_func; return 0; } diff --git a/drivers/gpu/drm/nouveau/core/engine/disp/nvd0.c b/drivers/gpu/drm/nouveau/core/engine/disp/nvd0.c index 9e38ebff5fb3..788dd34ccb54 100644 --- a/drivers/gpu/drm/nouveau/core/engine/disp/nvd0.c +++ b/drivers/gpu/drm/nouveau/core/engine/disp/nvd0.c @@ -27,12 +27,10 @@ #include <core/handle.h> #include <core/class.h> -#include <engine/software.h> #include <engine/disp.h> #include <subdev/timer.h> #include <subdev/fb.h> -#include <subdev/bar.h> #include <subdev/clock.h> #include <subdev/bios.h> @@ -230,7 +228,7 @@ nvd0_disp_sync_ctor(struct nouveau_object *parent, struct nv50_disp_dmac *dmac; int ret; - if (size < sizeof(*data) || args->head >= priv->head.nr) + if (size < sizeof(*args) || args->head >= priv->head.nr) return -EINVAL; ret = nv50_disp_dmac_create_(parent, engine, oclass, args->pushbuf, @@ -270,7 +268,7 @@ nvd0_disp_ovly_ctor(struct nouveau_object *parent, struct nv50_disp_dmac *dmac; int ret; - if (size < sizeof(*data) || args->head >= priv->head.nr) + if (size < sizeof(*args) || args->head >= priv->head.nr) return -EINVAL; ret = nv50_disp_dmac_create_(parent, engine, oclass, args->pushbuf, @@ -443,6 +441,18 @@ nvd0_disp_curs_ofuncs = { * Base display object ******************************************************************************/ +static void +nvd0_disp_base_vblank_enable(struct nouveau_event *event, int head) +{ + nv_mask(event->priv, 0x6100c0 + (head * 0x800), 0x00000001, 0x00000001); +} + +static void +nvd0_disp_base_vblank_disable(struct nouveau_event *event, int head) +{ + nv_mask(event->priv, 0x6100c0 + (head * 0x800), 0x00000001, 0x00000000); +} + static int nvd0_disp_base_ctor(struct nouveau_object *parent, struct nouveau_object *engine, @@ -459,6 +469,10 @@ nvd0_disp_base_ctor(struct nouveau_object *parent, if (ret) return ret; + priv->base.vblank->priv = priv; + priv->base.vblank->enable = nvd0_disp_base_vblank_enable; + priv->base.vblank->disable = nvd0_disp_base_vblank_disable; + return nouveau_ramht_new(parent, parent, 0x1000, 0, &base->ramht); } @@ -609,13 +623,24 @@ exec_lookup(struct nv50_disp_priv *priv, int head, int outp, u32 ctrl, } static bool -exec_script(struct nv50_disp_priv *priv, int head, int outp, u32 ctrl, int id) +exec_script(struct nv50_disp_priv *priv, int head, int id) { struct nouveau_bios *bios = nouveau_bios(priv); struct nvbios_outp info; struct dcb_output dcb; u8 ver, hdr, cnt, len; + u32 ctrl = 0x00000000; u16 data; + int outp; + + for (outp = 0; !(ctrl & (1 << head)) && outp < 8; outp++) { + ctrl = nv_rd32(priv, 0x640180 + (outp * 0x20)); + if (ctrl & (1 << head)) + break; + } + + if (outp == 8) + return false; data = exec_lookup(priv, head, outp, ctrl, &dcb, &ver, &hdr, &cnt, &len, &info); if (data) { @@ -635,21 +660,31 @@ exec_script(struct nv50_disp_priv *priv, int head, int outp, u32 ctrl, int id) } static u32 -exec_clkcmp(struct nv50_disp_priv *priv, int head, int outp, - u32 ctrl, int id, u32 pclk) +exec_clkcmp(struct nv50_disp_priv *priv, int head, int id, + u32 pclk, struct dcb_output *dcb) { struct nouveau_bios *bios = nouveau_bios(priv); struct nvbios_outp info1; struct nvbios_ocfg info2; - struct dcb_output dcb; u8 ver, hdr, cnt, len; - u16 data, conf; + u32 ctrl = 0x00000000; + u32 data, conf = ~0; + int outp; - data = exec_lookup(priv, head, outp, ctrl, &dcb, &ver, &hdr, &cnt, &len, &info1); - if (data == 0x0000) + for (outp = 0; !(ctrl & (1 << head)) && outp < 8; outp++) { + ctrl = nv_rd32(priv, 0x660180 + (outp * 0x20)); + if (ctrl & (1 << head)) + break; + } + + if (outp == 8) return false; - switch (dcb.type) { + data = exec_lookup(priv, head, outp, ctrl, dcb, &ver, &hdr, &cnt, &len, &info1); + if (data == 0x0000) + return conf; + + switch (dcb->type) { case DCB_OUTPUT_TMDS: conf = (ctrl & 0x00000f00) >> 8; if (pclk >= 165000) @@ -668,46 +703,52 @@ exec_clkcmp(struct nv50_disp_priv *priv, int head, int outp, } data = nvbios_ocfg_match(bios, data, conf, &ver, &hdr, &cnt, &len, &info2); - if (data) { + if (data && id < 0xff) { data = nvbios_oclk_match(bios, info2.clkcmp[id], pclk); if (data) { struct nvbios_init init = { .subdev = nv_subdev(priv), .bios = bios, .offset = data, - .outp = &dcb, + .outp = dcb, .crtc = head, .execute = 1, }; - if (nvbios_exec(&init)) - return 0x0000; - return conf; + nvbios_exec(&init); } } - return 0x0000; + return conf; } static void -nvd0_display_unk1_handler(struct nv50_disp_priv *priv, u32 head, u32 mask) +nvd0_disp_intr_unk1_0(struct nv50_disp_priv *priv, int head) { - int i; + exec_script(priv, head, 1); +} - for (i = 0; mask && i < 8; i++) { - u32 mcc = nv_rd32(priv, 0x640180 + (i * 0x20)); - if (mcc & (1 << head)) - exec_script(priv, head, i, mcc, 1); - } +static void +nvd0_disp_intr_unk2_0(struct nv50_disp_priv *priv, int head) +{ + exec_script(priv, head, 2); +} - nv_wr32(priv, 0x6101d4, 0x00000000); - nv_wr32(priv, 0x6109d4, 0x00000000); - nv_wr32(priv, 0x6101d0, 0x80000000); +static void +nvd0_disp_intr_unk2_1(struct nv50_disp_priv *priv, int head) +{ + struct nouveau_clock *clk = nouveau_clock(priv); + u32 pclk = nv_rd32(priv, 0x660450 + (head * 0x300)) / 1000; + if (pclk) + clk->pll_set(clk, PLL_VPLL0 + head, pclk); + nv_wr32(priv, 0x612200 + (head * 0x800), 0x00000000); } static void -nvd0_display_unk2_calc_tu(struct nv50_disp_priv *priv, int head, int or) +nvd0_disp_intr_unk2_2_tu(struct nv50_disp_priv *priv, int head, + struct dcb_output *outp) { + const int or = ffs(outp->or) - 1; const u32 ctrl = nv_rd32(priv, 0x660200 + (or * 0x020)); const u32 conf = nv_rd32(priv, 0x660404 + (head * 0x300)); const u32 pclk = nv_rd32(priv, 0x660450 + (head * 0x300)) / 1000; @@ -750,105 +791,102 @@ nvd0_display_unk2_calc_tu(struct nv50_disp_priv *priv, int head, int or) } static void -nvd0_display_unk2_handler(struct nv50_disp_priv *priv, u32 head, u32 mask) +nvd0_disp_intr_unk2_2(struct nv50_disp_priv *priv, int head) { - u32 pclk; - int i; - - for (i = 0; mask && i < 8; i++) { - u32 mcc = nv_rd32(priv, 0x640180 + (i * 0x20)); - if (mcc & (1 << head)) - exec_script(priv, head, i, mcc, 2); - } + struct dcb_output outp; + u32 pclk = nv_rd32(priv, 0x660450 + (head * 0x300)) / 1000; + u32 conf = exec_clkcmp(priv, head, 0xff, pclk, &outp); + if (conf != ~0) { + u32 addr, data; + + if (outp.type == DCB_OUTPUT_DP) { + u32 sync = nv_rd32(priv, 0x660404 + (head * 0x300)); + switch ((sync & 0x000003c0) >> 6) { + case 6: pclk = pclk * 30 / 8; break; + case 5: pclk = pclk * 24 / 8; break; + case 2: + default: + pclk = pclk * 18 / 8; + break; + } - pclk = nv_rd32(priv, 0x660450 + (head * 0x300)) / 1000; - nv_debug(priv, "head %d pclk %d mask 0x%08x\n", head, pclk, mask); - if (pclk && (mask & 0x00010000)) { - struct nouveau_clock *clk = nouveau_clock(priv); - clk->pll_set(clk, PLL_VPLL0 + head, pclk); - } + nouveau_dp_train(&priv->base, priv->sor.dp, + &outp, head, pclk); + } - nv_wr32(priv, 0x612200 + (head * 0x800), 0x00000000); + exec_clkcmp(priv, head, 0, pclk, &outp); - for (i = 0; mask && i < 8; i++) { - u32 mcp = nv_rd32(priv, 0x660180 + (i * 0x20)), cfg; - if (mcp & (1 << head)) { - if ((cfg = exec_clkcmp(priv, head, i, mcp, 0, pclk))) { - u32 addr, mask, data = 0x00000000; - if (i < 4) { - addr = 0x612280 + ((i - 0) * 0x800); - mask = 0xffffffff; - } else { - switch (mcp & 0x00000f00) { - case 0x00000800: - case 0x00000900: - nvd0_display_unk2_calc_tu(priv, head, i - 4); - break; - default: - break; - } - - addr = 0x612300 + ((i - 4) * 0x800); - mask = 0x00000707; - if (cfg & 0x00000100) - data = 0x00000101; - } - nv_mask(priv, addr, mask, data); - } - break; + if (outp.type == DCB_OUTPUT_ANALOG) { + addr = 0x612280 + (ffs(outp.or) - 1) * 0x800; + data = 0x00000000; + } else { + if (outp.type == DCB_OUTPUT_DP) + nvd0_disp_intr_unk2_2_tu(priv, head, &outp); + addr = 0x612300 + (ffs(outp.or) - 1) * 0x800; + data = (conf & 0x0100) ? 0x00000101 : 0x00000000; } - } - nv_wr32(priv, 0x6101d4, 0x00000000); - nv_wr32(priv, 0x6109d4, 0x00000000); - nv_wr32(priv, 0x6101d0, 0x80000000); + nv_mask(priv, addr, 0x00000707, data); + } } static void -nvd0_display_unk4_handler(struct nv50_disp_priv *priv, u32 head, u32 mask) +nvd0_disp_intr_unk4_0(struct nv50_disp_priv *priv, int head) { - int pclk, i; - - pclk = nv_rd32(priv, 0x660450 + (head * 0x300)) / 1000; + struct dcb_output outp; + u32 pclk = nv_rd32(priv, 0x660450 + (head * 0x300)) / 1000; + exec_clkcmp(priv, head, 1, pclk, &outp); +} - for (i = 0; mask && i < 8; i++) { - u32 mcp = nv_rd32(priv, 0x660180 + (i * 0x20)); - if (mcp & (1 << head)) - exec_clkcmp(priv, head, i, mcp, 1, pclk); +void +nvd0_disp_intr_supervisor(struct work_struct *work) +{ + struct nv50_disp_priv *priv = + container_of(work, struct nv50_disp_priv, supervisor); + u32 mask[4]; + int head; + + nv_debug(priv, "supervisor %08x\n", priv->super); + for (head = 0; head < priv->head.nr; head++) { + mask[head] = nv_rd32(priv, 0x6101d4 + (head * 0x800)); + nv_debug(priv, "head %d: 0x%08x\n", head, mask[head]); } - nv_wr32(priv, 0x6101d4, 0x00000000); - nv_wr32(priv, 0x6109d4, 0x00000000); - nv_wr32(priv, 0x6101d0, 0x80000000); -} - -static void -nvd0_disp_intr_vblank(struct nv50_disp_priv *priv, int crtc) -{ - struct nouveau_bar *bar = nouveau_bar(priv); - struct nouveau_disp *disp = &priv->base; - struct nouveau_software_chan *chan, *temp; - unsigned long flags; - - spin_lock_irqsave(&disp->vblank.lock, flags); - list_for_each_entry_safe(chan, temp, &disp->vblank.list, vblank.head) { - if (chan->vblank.crtc != crtc) - continue; - - nv_wr32(priv, 0x001718, 0x80000000 | chan->vblank.channel); - bar->flush(bar); - nv_wr32(priv, 0x06000c, upper_32_bits(chan->vblank.offset)); - nv_wr32(priv, 0x060010, lower_32_bits(chan->vblank.offset)); - nv_wr32(priv, 0x060014, chan->vblank.value); - - list_del(&chan->vblank.head); - if (disp->vblank.put) - disp->vblank.put(disp->vblank.data, crtc); + if (priv->super & 0x00000001) { + for (head = 0; head < priv->head.nr; head++) { + if (!(mask[head] & 0x00001000)) + continue; + nvd0_disp_intr_unk1_0(priv, head); + } + } else + if (priv->super & 0x00000002) { + for (head = 0; head < priv->head.nr; head++) { + if (!(mask[head] & 0x00001000)) + continue; + nvd0_disp_intr_unk2_0(priv, head); + } + for (head = 0; head < priv->head.nr; head++) { + if (!(mask[head] & 0x00010000)) + continue; + nvd0_disp_intr_unk2_1(priv, head); + } + for (head = 0; head < priv->head.nr; head++) { + if (!(mask[head] & 0x00001000)) + continue; + nvd0_disp_intr_unk2_2(priv, head); + } + } else + if (priv->super & 0x00000004) { + for (head = 0; head < priv->head.nr; head++) { + if (!(mask[head] & 0x00001000)) + continue; + nvd0_disp_intr_unk4_0(priv, head); + } } - spin_unlock_irqrestore(&disp->vblank.lock, flags); - if (disp->vblank.notify) - disp->vblank.notify(disp->vblank.data, crtc); + for (head = 0; head < priv->head.nr; head++) + nv_wr32(priv, 0x6101d4 + (head * 0x800), 0x00000000); + nv_wr32(priv, 0x6101d0, 0x80000000); } void @@ -884,27 +922,11 @@ nvd0_disp_intr(struct nouveau_subdev *subdev) if (intr & 0x00100000) { u32 stat = nv_rd32(priv, 0x6100ac); - u32 mask = 0, crtc = ~0; - - while (!mask && ++crtc < priv->head.nr) - mask = nv_rd32(priv, 0x6101d4 + (crtc * 0x800)); - - if (stat & 0x00000001) { - nv_wr32(priv, 0x6100ac, 0x00000001); - nvd0_display_unk1_handler(priv, crtc, mask); - stat &= ~0x00000001; - } - - if (stat & 0x00000002) { - nv_wr32(priv, 0x6100ac, 0x00000002); - nvd0_display_unk2_handler(priv, crtc, mask); - stat &= ~0x00000002; - } - - if (stat & 0x00000004) { - nv_wr32(priv, 0x6100ac, 0x00000004); - nvd0_display_unk4_handler(priv, crtc, mask); - stat &= ~0x00000004; + if (stat & 0x00000007) { + priv->super = (stat & 0x00000007); + schedule_work(&priv->supervisor); + nv_wr32(priv, 0x6100ac, priv->super); + stat &= ~0x00000007; } if (stat) { @@ -920,7 +942,7 @@ nvd0_disp_intr(struct nouveau_subdev *subdev) if (mask & intr) { u32 stat = nv_rd32(priv, 0x6100bc + (i * 0x800)); if (stat & 0x00000001) - nvd0_disp_intr_vblank(priv, i); + nouveau_event_trigger(priv->base.vblank, i); nv_mask(priv, 0x6100bc + (i * 0x800), 0, 0); nv_rd32(priv, 0x6100c0 + (i * 0x800)); } @@ -933,10 +955,11 @@ nvd0_disp_ctor(struct nouveau_object *parent, struct nouveau_object *engine, struct nouveau_object **pobject) { struct nv50_disp_priv *priv; + int heads = nv_rd32(parent, 0x022448); int ret; - ret = nouveau_disp_create(parent, engine, oclass, "PDISP", - "display", &priv); + ret = nouveau_disp_create(parent, engine, oclass, heads, + "PDISP", "display", &priv); *pobject = nv_object(priv); if (ret) return ret; @@ -944,8 +967,9 @@ nvd0_disp_ctor(struct nouveau_object *parent, struct nouveau_object *engine, nv_engine(priv)->sclass = nvd0_disp_base_oclass; nv_engine(priv)->cclass = &nv50_disp_cclass; nv_subdev(priv)->intr = nvd0_disp_intr; + INIT_WORK(&priv->supervisor, nvd0_disp_intr_supervisor); priv->sclass = nvd0_disp_sclass; - priv->head.nr = nv_rd32(priv, 0x022448); + priv->head.nr = heads; priv->dac.nr = 3; priv->sor.nr = 4; priv->dac.power = nv50_dac_power; @@ -953,14 +977,7 @@ nvd0_disp_ctor(struct nouveau_object *parent, struct nouveau_object *engine, priv->sor.power = nv50_sor_power; priv->sor.hda_eld = nvd0_hda_eld; priv->sor.hdmi = nvd0_hdmi_ctrl; - priv->sor.dp_train = nvd0_sor_dp_train; - priv->sor.dp_train_init = nv94_sor_dp_train_init; - priv->sor.dp_train_fini = nv94_sor_dp_train_fini; - priv->sor.dp_lnkctl = nvd0_sor_dp_lnkctl; - priv->sor.dp_drvctl = nvd0_sor_dp_drvctl; - - INIT_LIST_HEAD(&priv->base.vblank.list); - spin_lock_init(&priv->base.vblank.lock); + priv->sor.dp = &nvd0_sor_dp_func; return 0; } diff --git a/drivers/gpu/drm/nouveau/core/engine/disp/nve0.c b/drivers/gpu/drm/nouveau/core/engine/disp/nve0.c index 259537c4587e..20725b363d58 100644 --- a/drivers/gpu/drm/nouveau/core/engine/disp/nve0.c +++ b/drivers/gpu/drm/nouveau/core/engine/disp/nve0.c @@ -51,10 +51,11 @@ nve0_disp_ctor(struct nouveau_object *parent, struct nouveau_object *engine, struct nouveau_object **pobject) { struct nv50_disp_priv *priv; + int heads = nv_rd32(parent, 0x022448); int ret; - ret = nouveau_disp_create(parent, engine, oclass, "PDISP", - "display", &priv); + ret = nouveau_disp_create(parent, engine, oclass, heads, + "PDISP", "display", &priv); *pobject = nv_object(priv); if (ret) return ret; @@ -62,8 +63,9 @@ nve0_disp_ctor(struct nouveau_object *parent, struct nouveau_object *engine, nv_engine(priv)->sclass = nve0_disp_base_oclass; nv_engine(priv)->cclass = &nv50_disp_cclass; nv_subdev(priv)->intr = nvd0_disp_intr; + INIT_WORK(&priv->supervisor, nvd0_disp_intr_supervisor); priv->sclass = nve0_disp_sclass; - priv->head.nr = nv_rd32(priv, 0x022448); + priv->head.nr = heads; priv->dac.nr = 3; priv->sor.nr = 4; priv->dac.power = nv50_dac_power; @@ -71,14 +73,7 @@ nve0_disp_ctor(struct nouveau_object *parent, struct nouveau_object *engine, priv->sor.power = nv50_sor_power; priv->sor.hda_eld = nvd0_hda_eld; priv->sor.hdmi = nvd0_hdmi_ctrl; - priv->sor.dp_train = nvd0_sor_dp_train; - priv->sor.dp_train_init = nv94_sor_dp_train_init; - priv->sor.dp_train_fini = nv94_sor_dp_train_fini; - priv->sor.dp_lnkctl = nvd0_sor_dp_lnkctl; - priv->sor.dp_drvctl = nvd0_sor_dp_drvctl; - - INIT_LIST_HEAD(&priv->base.vblank.list); - spin_lock_init(&priv->base.vblank.lock); + priv->sor.dp = &nvd0_sor_dp_func; return 0; } diff --git a/drivers/gpu/drm/nouveau/core/engine/disp/piornv50.c b/drivers/gpu/drm/nouveau/core/engine/disp/piornv50.c new file mode 100644 index 000000000000..2c8ce351b52d --- /dev/null +++ b/drivers/gpu/drm/nouveau/core/engine/disp/piornv50.c @@ -0,0 +1,140 @@ +/* + * Copyright 2012 Red Hat Inc. + * + * 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 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 + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: Ben Skeggs + */ + +#include <core/os.h> +#include <core/class.h> + +#include <subdev/bios.h> +#include <subdev/bios/dcb.h> +#include <subdev/timer.h> +#include <subdev/i2c.h> + +#include "nv50.h" + +/****************************************************************************** + * DisplayPort + *****************************************************************************/ +static struct nouveau_i2c_port * +nv50_pior_dp_find(struct nouveau_disp *disp, struct dcb_output *outp) +{ + struct nouveau_i2c *i2c = nouveau_i2c(disp); + return i2c->find_type(i2c, NV_I2C_TYPE_EXTAUX(outp->extdev)); +} + +static int +nv50_pior_dp_pattern(struct nouveau_disp *disp, struct dcb_output *outp, + int head, int pattern) +{ + struct nouveau_i2c_port *port; + int ret = -EINVAL; + + port = nv50_pior_dp_find(disp, outp); + if (port) { + if (port->func->pattern) + ret = port->func->pattern(port, pattern); + else + ret = 0; + } + + return ret; +} + +static int +nv50_pior_dp_lnk_ctl(struct nouveau_disp *disp, struct dcb_output *outp, + int head, int lane_nr, int link_bw, bool enh) +{ + struct nouveau_i2c_port *port; + int ret = -EINVAL; + + port = nv50_pior_dp_find(disp, outp); + if (port && port->func->lnk_ctl) + ret = port->func->lnk_ctl(port, lane_nr, link_bw, enh); + + return ret; +} + +static int +nv50_pior_dp_drv_ctl(struct nouveau_disp *disp, struct dcb_output *outp, + int head, int lane, int vsw, int pre) +{ + struct nouveau_i2c_port *port; + int ret = -EINVAL; + + port = nv50_pior_dp_find(disp, outp); + if (port) { + if (port->func->drv_ctl) + ret = port->func->drv_ctl(port, lane, vsw, pre); + else + ret = 0; + } + + return ret; +} + +const struct nouveau_dp_func +nv50_pior_dp_func = { + .pattern = nv50_pior_dp_pattern, + .lnk_ctl = nv50_pior_dp_lnk_ctl, + .drv_ctl = nv50_pior_dp_drv_ctl, +}; + +/****************************************************************************** + * General PIOR handling + *****************************************************************************/ +int +nv50_pior_power(struct nv50_disp_priv *priv, int or, u32 data) +{ + const u32 stat = data & NV50_DISP_PIOR_PWR_STATE; + const u32 soff = (or * 0x800); + nv_wait(priv, 0x61e004 + soff, 0x80000000, 0x00000000); + nv_mask(priv, 0x61e004 + soff, 0x80000101, 0x80000000 | stat); + nv_wait(priv, 0x61e004 + soff, 0x80000000, 0x00000000); + return 0; +} + +int +nv50_pior_mthd(struct nouveau_object *object, u32 mthd, void *args, u32 size) +{ + struct nv50_disp_priv *priv = (void *)object->engine; + const u8 type = (mthd & NV50_DISP_PIOR_MTHD_TYPE) >> 12; + const u8 or = (mthd & NV50_DISP_PIOR_MTHD_OR); + u32 *data = args; + int ret; + + if (size < sizeof(u32)) + return -EINVAL; + + mthd &= ~NV50_DISP_PIOR_MTHD_TYPE; + mthd &= ~NV50_DISP_PIOR_MTHD_OR; + switch (mthd) { + case NV50_DISP_PIOR_PWR: + ret = priv->pior.power(priv, or, data[0]); + priv->pior.type[or] = type; + break; + default: + return -EINVAL; + } + + return ret; +} diff --git a/drivers/gpu/drm/nouveau/core/engine/disp/sornv50.c b/drivers/gpu/drm/nouveau/core/engine/disp/sornv50.c index 39b6b67732d0..ab1e918469a8 100644 --- a/drivers/gpu/drm/nouveau/core/engine/disp/sornv50.c +++ b/drivers/gpu/drm/nouveau/core/engine/disp/sornv50.c @@ -79,31 +79,6 @@ nv50_sor_mthd(struct nouveau_object *object, u32 mthd, void *args, u32 size) priv->sor.lvdsconf = data & NV50_DISP_SOR_LVDS_SCRIPT_ID; ret = 0; break; - case NV94_DISP_SOR_DP_TRAIN: - switch (data & NV94_DISP_SOR_DP_TRAIN_OP) { - case NV94_DISP_SOR_DP_TRAIN_OP_PATTERN: - ret = priv->sor.dp_train(priv, or, link, type, mask, data, &outp); - break; - case NV94_DISP_SOR_DP_TRAIN_OP_INIT: - ret = priv->sor.dp_train_init(priv, or, link, head, type, mask, data, &outp); - break; - case NV94_DISP_SOR_DP_TRAIN_OP_FINI: - ret = priv->sor.dp_train_fini(priv, or, link, head, type, mask, data, &outp); - break; - default: - break; - } - break; - case NV94_DISP_SOR_DP_LNKCTL: - ret = priv->sor.dp_lnkctl(priv, or, link, head, type, mask, data, &outp); - break; - case NV94_DISP_SOR_DP_DRVCTL(0): - case NV94_DISP_SOR_DP_DRVCTL(1): - case NV94_DISP_SOR_DP_DRVCTL(2): - case NV94_DISP_SOR_DP_DRVCTL(3): - ret = priv->sor.dp_drvctl(priv, or, link, (mthd & 0xc0) >> 6, - type, mask, data, &outp); - break; default: BUG_ON(1); } diff --git a/drivers/gpu/drm/nouveau/core/engine/disp/sornv94.c b/drivers/gpu/drm/nouveau/core/engine/disp/sornv94.c index f6edd009762e..7ec4ee83fb64 100644 --- a/drivers/gpu/drm/nouveau/core/engine/disp/sornv94.c +++ b/drivers/gpu/drm/nouveau/core/engine/disp/sornv94.c @@ -33,124 +33,53 @@ #include "nv50.h" static inline u32 -nv94_sor_dp_lane_map(struct nv50_disp_priv *priv, u8 lane) +nv94_sor_soff(struct dcb_output *outp) { - static const u8 nvaf[] = { 24, 16, 8, 0 }; /* thanks, apple.. */ - static const u8 nv94[] = { 16, 8, 0, 24 }; - if (nv_device(priv)->chipset == 0xaf) - return nvaf[lane]; - return nv94[lane]; + return (ffs(outp->or) - 1) * 0x800; } -int -nv94_sor_dp_train_init(struct nv50_disp_priv *priv, int or, int link, int head, - u16 type, u16 mask, u32 data, struct dcb_output *dcbo) +static inline u32 +nv94_sor_loff(struct dcb_output *outp) { - struct nouveau_bios *bios = nouveau_bios(priv); - struct nvbios_dpout info; - u8 ver, hdr, cnt, len; - u16 outp; - - outp = nvbios_dpout_match(bios, type, mask, &ver, &hdr, &cnt, &len, &info); - if (outp) { - struct nvbios_init init = { - .subdev = nv_subdev(priv), - .bios = bios, - .outp = dcbo, - .crtc = head, - .execute = 1, - }; - - if (data & NV94_DISP_SOR_DP_TRAIN_INIT_SPREAD_ON) - init.offset = info.script[2]; - else - init.offset = info.script[3]; - nvbios_exec(&init); - - init.offset = info.script[0]; - nvbios_exec(&init); - } - - return 0; + return nv94_sor_soff(outp) + !(outp->sorconf.link & 1) * 0x80; } -int -nv94_sor_dp_train_fini(struct nv50_disp_priv *priv, int or, int link, int head, - u16 type, u16 mask, u32 data, struct dcb_output *dcbo) +static inline u32 +nv94_sor_dp_lane_map(struct nv50_disp_priv *priv, u8 lane) { - struct nouveau_bios *bios = nouveau_bios(priv); - struct nvbios_dpout info; - u8 ver, hdr, cnt, len; - u16 outp; - - outp = nvbios_dpout_match(bios, type, mask, &ver, &hdr, &cnt, &len, &info); - if (outp) { - struct nvbios_init init = { - .subdev = nv_subdev(priv), - .bios = bios, - .offset = info.script[1], - .outp = dcbo, - .crtc = head, - .execute = 1, - }; - - nvbios_exec(&init); - } - - return 0; + static const u8 nvaf[] = { 24, 16, 8, 0 }; /* thanks, apple.. */ + static const u8 nv94[] = { 16, 8, 0, 24 }; + if (nv_device(priv)->chipset == 0xaf) + return nvaf[lane]; + return nv94[lane]; } -int -nv94_sor_dp_train(struct nv50_disp_priv *priv, int or, int link, - u16 type, u16 mask, u32 data, struct dcb_output *info) +static int +nv94_sor_dp_pattern(struct nouveau_disp *disp, struct dcb_output *outp, + int head, int pattern) { - const u32 loff = (or * 0x800) + (link * 0x80); - const u32 patt = (data & NV94_DISP_SOR_DP_TRAIN_PATTERN); - nv_mask(priv, 0x61c10c + loff, 0x0f000000, patt << 24); + struct nv50_disp_priv *priv = (void *)disp; + const u32 loff = nv94_sor_loff(outp); + nv_mask(priv, 0x61c10c + loff, 0x0f000000, pattern << 24); return 0; } -int -nv94_sor_dp_lnkctl(struct nv50_disp_priv *priv, int or, int link, int head, - u16 type, u16 mask, u32 data, struct dcb_output *dcbo) +static int +nv94_sor_dp_lnk_ctl(struct nouveau_disp *disp, struct dcb_output *outp, + int head, int link_nr, int link_bw, bool enh_frame) { - struct nouveau_bios *bios = nouveau_bios(priv); - const u32 loff = (or * 0x800) + (link * 0x80); - const u32 soff = (or * 0x800); - u16 link_bw = (data & NV94_DISP_SOR_DP_LNKCTL_WIDTH) >> 8; - u8 link_nr = (data & NV94_DISP_SOR_DP_LNKCTL_COUNT); + struct nv50_disp_priv *priv = (void *)disp; + const u32 soff = nv94_sor_soff(outp); + const u32 loff = nv94_sor_loff(outp); u32 dpctrl = 0x00000000; u32 clksor = 0x00000000; - u32 outp, lane = 0; - u8 ver, hdr, cnt, len; - struct nvbios_dpout info; + u32 lane = 0; int i; - /* -> 10Khz units */ - link_bw *= 2700; - - outp = nvbios_dpout_match(bios, type, mask, &ver, &hdr, &cnt, &len, &info); - if (outp && info.lnkcmp) { - struct nvbios_init init = { - .subdev = nv_subdev(priv), - .bios = bios, - .offset = 0x0000, - .outp = dcbo, - .crtc = head, - .execute = 1, - }; - - while (link_bw < nv_ro16(bios, info.lnkcmp)) - info.lnkcmp += 4; - init.offset = nv_ro16(bios, info.lnkcmp + 2); - - nvbios_exec(&init); - } - dpctrl |= ((1 << link_nr) - 1) << 16; - if (data & NV94_DISP_SOR_DP_LNKCTL_FRAME_ENH) + if (enh_frame) dpctrl |= 0x00004000; - if (link_bw > 16200) + if (link_bw > 0x06) clksor |= 0x00040000; for (i = 0; i < link_nr; i++) @@ -162,24 +91,25 @@ nv94_sor_dp_lnkctl(struct nv50_disp_priv *priv, int or, int link, int head, return 0; } -int -nv94_sor_dp_drvctl(struct nv50_disp_priv *priv, int or, int link, int lane, - u16 type, u16 mask, u32 data, struct dcb_output *dcbo) +static int +nv94_sor_dp_drv_ctl(struct nouveau_disp *disp, struct dcb_output *outp, + int head, int lane, int swing, int preem) { - struct nouveau_bios *bios = nouveau_bios(priv); - const u32 loff = (or * 0x800) + (link * 0x80); - const u8 swing = (data & NV94_DISP_SOR_DP_DRVCTL_VS) >> 8; - const u8 preem = (data & NV94_DISP_SOR_DP_DRVCTL_PE); + struct nouveau_bios *bios = nouveau_bios(disp); + struct nv50_disp_priv *priv = (void *)disp; + const u32 loff = nv94_sor_loff(outp); u32 addr, shift = nv94_sor_dp_lane_map(priv, lane); u8 ver, hdr, cnt, len; - struct nvbios_dpout outp; + struct nvbios_dpout info; struct nvbios_dpcfg ocfg; - addr = nvbios_dpout_match(bios, type, mask, &ver, &hdr, &cnt, &len, &outp); + addr = nvbios_dpout_match(bios, outp->hasht, outp->hashm, + &ver, &hdr, &cnt, &len, &info); if (!addr) return -ENODEV; - addr = nvbios_dpcfg_match(bios, addr, 0, swing, preem, &ver, &hdr, &cnt, &len, &ocfg); + addr = nvbios_dpcfg_match(bios, addr, 0, swing, preem, + &ver, &hdr, &cnt, &len, &ocfg); if (!addr) return -EINVAL; @@ -188,3 +118,10 @@ nv94_sor_dp_drvctl(struct nv50_disp_priv *priv, int or, int link, int lane, nv_mask(priv, 0x61c130 + loff, 0x0000ff00, ocfg.unk << 8); return 0; } + +const struct nouveau_dp_func +nv94_sor_dp_func = { + .pattern = nv94_sor_dp_pattern, + .lnk_ctl = nv94_sor_dp_lnk_ctl, + .drv_ctl = nv94_sor_dp_drv_ctl, +}; diff --git a/drivers/gpu/drm/nouveau/core/engine/disp/sornvd0.c b/drivers/gpu/drm/nouveau/core/engine/disp/sornvd0.c index c37ce7e29f5d..9e1d435d7282 100644 --- a/drivers/gpu/drm/nouveau/core/engine/disp/sornvd0.c +++ b/drivers/gpu/drm/nouveau/core/engine/disp/sornvd0.c @@ -33,59 +33,49 @@ #include "nv50.h" static inline u32 +nvd0_sor_soff(struct dcb_output *outp) +{ + return (ffs(outp->or) - 1) * 0x800; +} + +static inline u32 +nvd0_sor_loff(struct dcb_output *outp) +{ + return nvd0_sor_soff(outp) + !(outp->sorconf.link & 1) * 0x80; +} + +static inline u32 nvd0_sor_dp_lane_map(struct nv50_disp_priv *priv, u8 lane) { static const u8 nvd0[] = { 16, 8, 0, 24 }; return nvd0[lane]; } -int -nvd0_sor_dp_train(struct nv50_disp_priv *priv, int or, int link, - u16 type, u16 mask, u32 data, struct dcb_output *info) +static int +nvd0_sor_dp_pattern(struct nouveau_disp *disp, struct dcb_output *outp, + int head, int pattern) { - const u32 loff = (or * 0x800) + (link * 0x80); - const u32 patt = (data & NV94_DISP_SOR_DP_TRAIN_PATTERN); - nv_mask(priv, 0x61c110 + loff, 0x0f0f0f0f, 0x01010101 * patt); + struct nv50_disp_priv *priv = (void *)disp; + const u32 loff = nvd0_sor_loff(outp); + nv_mask(priv, 0x61c110 + loff, 0x0f0f0f0f, 0x01010101 * pattern); return 0; } -int -nvd0_sor_dp_lnkctl(struct nv50_disp_priv *priv, int or, int link, int head, - u16 type, u16 mask, u32 data, struct dcb_output *dcbo) +static int +nvd0_sor_dp_lnk_ctl(struct nouveau_disp *disp, struct dcb_output *outp, + int head, int link_nr, int link_bw, bool enh_frame) { - struct nouveau_bios *bios = nouveau_bios(priv); - const u32 loff = (or * 0x800) + (link * 0x80); - const u32 soff = (or * 0x800); - const u8 link_bw = (data & NV94_DISP_SOR_DP_LNKCTL_WIDTH) >> 8; - const u8 link_nr = (data & NV94_DISP_SOR_DP_LNKCTL_COUNT); + struct nv50_disp_priv *priv = (void *)disp; + const u32 soff = nvd0_sor_soff(outp); + const u32 loff = nvd0_sor_loff(outp); u32 dpctrl = 0x00000000; u32 clksor = 0x00000000; - u32 outp, lane = 0; - u8 ver, hdr, cnt, len; - struct nvbios_dpout info; + u32 lane = 0; int i; - outp = nvbios_dpout_match(bios, type, mask, &ver, &hdr, &cnt, &len, &info); - if (outp && info.lnkcmp) { - struct nvbios_init init = { - .subdev = nv_subdev(priv), - .bios = bios, - .offset = 0x0000, - .outp = dcbo, - .crtc = head, - .execute = 1, - }; - - while (nv_ro08(bios, info.lnkcmp) < link_bw) - info.lnkcmp += 3; - init.offset = nv_ro16(bios, info.lnkcmp + 1); - - nvbios_exec(&init); - } - clksor |= link_bw << 18; dpctrl |= ((1 << link_nr) - 1) << 16; - if (data & NV94_DISP_SOR_DP_LNKCTL_FRAME_ENH) + if (enh_frame) dpctrl |= 0x00004000; for (i = 0; i < link_nr; i++) @@ -97,24 +87,25 @@ nvd0_sor_dp_lnkctl(struct nv50_disp_priv *priv, int or, int link, int head, return 0; } -int -nvd0_sor_dp_drvctl(struct nv50_disp_priv *priv, int or, int link, int lane, - u16 type, u16 mask, u32 data, struct dcb_output *dcbo) +static int +nvd0_sor_dp_drv_ctl(struct nouveau_disp *disp, struct dcb_output *outp, + int head, int lane, int swing, int preem) { - struct nouveau_bios *bios = nouveau_bios(priv); - const u32 loff = (or * 0x800) + (link * 0x80); - const u8 swing = (data & NV94_DISP_SOR_DP_DRVCTL_VS) >> 8; - const u8 preem = (data & NV94_DISP_SOR_DP_DRVCTL_PE); + struct nouveau_bios *bios = nouveau_bios(disp); + struct nv50_disp_priv *priv = (void *)disp; + const u32 loff = nvd0_sor_loff(outp); u32 addr, shift = nvd0_sor_dp_lane_map(priv, lane); u8 ver, hdr, cnt, len; - struct nvbios_dpout outp; + struct nvbios_dpout info; struct nvbios_dpcfg ocfg; - addr = nvbios_dpout_match(bios, type, mask, &ver, &hdr, &cnt, &len, &outp); + addr = nvbios_dpout_match(bios, outp->hasht, outp->hashm, + &ver, &hdr, &cnt, &len, &info); if (!addr) return -ENODEV; - addr = nvbios_dpcfg_match(bios, addr, 0, swing, preem, &ver, &hdr, &cnt, &len, &ocfg); + addr = nvbios_dpcfg_match(bios, addr, 0, swing, preem, + &ver, &hdr, &cnt, &len, &ocfg); if (!addr) return -EINVAL; @@ -124,3 +115,10 @@ nvd0_sor_dp_drvctl(struct nv50_disp_priv *priv, int or, int link, int lane, nv_mask(priv, 0x61c13c + loff, 0x00000000, 0x00000000); return 0; } + +const struct nouveau_dp_func +nvd0_sor_dp_func = { + .pattern = nvd0_sor_dp_pattern, + .lnk_ctl = nvd0_sor_dp_lnk_ctl, + .drv_ctl = nvd0_sor_dp_drv_ctl, +}; diff --git a/drivers/gpu/drm/nouveau/core/engine/fifo/base.c b/drivers/gpu/drm/nouveau/core/engine/fifo/base.c index c2b9db335816..7341ebe131fa 100644 --- a/drivers/gpu/drm/nouveau/core/engine/fifo/base.c +++ b/drivers/gpu/drm/nouveau/core/engine/fifo/base.c @@ -22,8 +22,10 @@ * Authors: Ben Skeggs */ +#include <core/client.h> #include <core/object.h> #include <core/handle.h> +#include <core/event.h> #include <core/class.h> #include <engine/dmaobj.h> @@ -146,10 +148,25 @@ nouveau_fifo_chid(struct nouveau_fifo *priv, struct nouveau_object *object) return -1; } +const char * +nouveau_client_name_for_fifo_chid(struct nouveau_fifo *fifo, u32 chid) +{ + struct nouveau_fifo_chan *chan = NULL; + unsigned long flags; + + spin_lock_irqsave(&fifo->lock, flags); + if (chid >= fifo->min && chid <= fifo->max) + chan = (void *)fifo->channel[chid]; + spin_unlock_irqrestore(&fifo->lock, flags); + + return nouveau_client_name(chan); +} + void nouveau_fifo_destroy(struct nouveau_fifo *priv) { kfree(priv->channel); + nouveau_event_destroy(&priv->uevent); nouveau_engine_destroy(&priv->base); } @@ -174,6 +191,10 @@ nouveau_fifo_create_(struct nouveau_object *parent, if (!priv->channel) return -ENOMEM; + ret = nouveau_event_create(1, &priv->uevent); + if (ret) + return ret; + priv->chid = nouveau_fifo_chid; spin_lock_init(&priv->lock); return 0; diff --git a/drivers/gpu/drm/nouveau/core/engine/fifo/nv04.c b/drivers/gpu/drm/nouveau/core/engine/fifo/nv04.c index a47a8548f9e0..f877bd524a92 100644 --- a/drivers/gpu/drm/nouveau/core/engine/fifo/nv04.c +++ b/drivers/gpu/drm/nouveau/core/engine/fifo/nv04.c @@ -28,6 +28,7 @@ #include <core/namedb.h> #include <core/handle.h> #include <core/ramht.h> +#include <core/event.h> #include <subdev/instmem.h> #include <subdev/instmem/nv04.h> @@ -398,6 +399,98 @@ out: return handled; } +static void +nv04_fifo_cache_error(struct nouveau_device *device, + struct nv04_fifo_priv *priv, u32 chid, u32 get) +{ + u32 mthd, data; + int ptr; + + /* NV_PFIFO_CACHE1_GET actually goes to 0xffc before wrapping on my + * G80 chips, but CACHE1 isn't big enough for this much data.. Tests + * show that it wraps around to the start at GET=0x800.. No clue as to + * why.. + */ + ptr = (get & 0x7ff) >> 2; + + if (device->card_type < NV_40) { + mthd = nv_rd32(priv, NV04_PFIFO_CACHE1_METHOD(ptr)); + data = nv_rd32(priv, NV04_PFIFO_CACHE1_DATA(ptr)); + } else { + mthd = nv_rd32(priv, NV40_PFIFO_CACHE1_METHOD(ptr)); + data = nv_rd32(priv, NV40_PFIFO_CACHE1_DATA(ptr)); + } + + if (!nv04_fifo_swmthd(priv, chid, mthd, data)) { + const char *client_name = + nouveau_client_name_for_fifo_chid(&priv->base, chid); + nv_error(priv, + "CACHE_ERROR - ch %d [%s] subc %d mthd 0x%04x data 0x%08x\n", + chid, client_name, (mthd >> 13) & 7, mthd & 0x1ffc, + data); + } + + nv_wr32(priv, NV04_PFIFO_CACHE1_DMA_PUSH, 0); + nv_wr32(priv, NV03_PFIFO_INTR_0, NV_PFIFO_INTR_CACHE_ERROR); + + nv_wr32(priv, NV03_PFIFO_CACHE1_PUSH0, + nv_rd32(priv, NV03_PFIFO_CACHE1_PUSH0) & ~1); + nv_wr32(priv, NV03_PFIFO_CACHE1_GET, get + 4); + nv_wr32(priv, NV03_PFIFO_CACHE1_PUSH0, + nv_rd32(priv, NV03_PFIFO_CACHE1_PUSH0) | 1); + nv_wr32(priv, NV04_PFIFO_CACHE1_HASH, 0); + + nv_wr32(priv, NV04_PFIFO_CACHE1_DMA_PUSH, + nv_rd32(priv, NV04_PFIFO_CACHE1_DMA_PUSH) | 1); + nv_wr32(priv, NV04_PFIFO_CACHE1_PULL0, 1); +} + +static void +nv04_fifo_dma_pusher(struct nouveau_device *device, struct nv04_fifo_priv *priv, + u32 chid) +{ + const char *client_name; + u32 dma_get = nv_rd32(priv, 0x003244); + u32 dma_put = nv_rd32(priv, 0x003240); + u32 push = nv_rd32(priv, 0x003220); + u32 state = nv_rd32(priv, 0x003228); + + client_name = nouveau_client_name_for_fifo_chid(&priv->base, chid); + + if (device->card_type == NV_50) { + u32 ho_get = nv_rd32(priv, 0x003328); + u32 ho_put = nv_rd32(priv, 0x003320); + u32 ib_get = nv_rd32(priv, 0x003334); + u32 ib_put = nv_rd32(priv, 0x003330); + + nv_error(priv, + "DMA_PUSHER - ch %d [%s] get 0x%02x%08x put 0x%02x%08x ib_get 0x%08x ib_put 0x%08x state 0x%08x (err: %s) push 0x%08x\n", + chid, client_name, ho_get, dma_get, ho_put, dma_put, + ib_get, ib_put, state, nv_dma_state_err(state), push); + + /* METHOD_COUNT, in DMA_STATE on earlier chipsets */ + nv_wr32(priv, 0x003364, 0x00000000); + if (dma_get != dma_put || ho_get != ho_put) { + nv_wr32(priv, 0x003244, dma_put); + nv_wr32(priv, 0x003328, ho_put); + } else + if (ib_get != ib_put) + nv_wr32(priv, 0x003334, ib_put); + } else { + nv_error(priv, + "DMA_PUSHER - ch %d [%s] get 0x%08x put 0x%08x state 0x%08x (err: %s) push 0x%08x\n", + chid, client_name, dma_get, dma_put, state, + nv_dma_state_err(state), push); + + if (dma_get != dma_put) + nv_wr32(priv, 0x003244, dma_put); + } + + nv_wr32(priv, 0x003228, 0x00000000); + nv_wr32(priv, 0x003220, 0x00000001); + nv_wr32(priv, 0x002100, NV_PFIFO_INTR_DMA_PUSHER); +} + void nv04_fifo_intr(struct nouveau_subdev *subdev) { @@ -416,96 +509,12 @@ nv04_fifo_intr(struct nouveau_subdev *subdev) get = nv_rd32(priv, NV03_PFIFO_CACHE1_GET); if (status & NV_PFIFO_INTR_CACHE_ERROR) { - uint32_t mthd, data; - int ptr; - - /* NV_PFIFO_CACHE1_GET actually goes to 0xffc before - * wrapping on my G80 chips, but CACHE1 isn't big - * enough for this much data.. Tests show that it - * wraps around to the start at GET=0x800.. No clue - * as to why.. - */ - ptr = (get & 0x7ff) >> 2; - - if (device->card_type < NV_40) { - mthd = nv_rd32(priv, - NV04_PFIFO_CACHE1_METHOD(ptr)); - data = nv_rd32(priv, - NV04_PFIFO_CACHE1_DATA(ptr)); - } else { - mthd = nv_rd32(priv, - NV40_PFIFO_CACHE1_METHOD(ptr)); - data = nv_rd32(priv, - NV40_PFIFO_CACHE1_DATA(ptr)); - } - - if (!nv04_fifo_swmthd(priv, chid, mthd, data)) { - nv_error(priv, "CACHE_ERROR - Ch %d/%d " - "Mthd 0x%04x Data 0x%08x\n", - chid, (mthd >> 13) & 7, mthd & 0x1ffc, - data); - } - - nv_wr32(priv, NV04_PFIFO_CACHE1_DMA_PUSH, 0); - nv_wr32(priv, NV03_PFIFO_INTR_0, - NV_PFIFO_INTR_CACHE_ERROR); - - nv_wr32(priv, NV03_PFIFO_CACHE1_PUSH0, - nv_rd32(priv, NV03_PFIFO_CACHE1_PUSH0) & ~1); - nv_wr32(priv, NV03_PFIFO_CACHE1_GET, get + 4); - nv_wr32(priv, NV03_PFIFO_CACHE1_PUSH0, - nv_rd32(priv, NV03_PFIFO_CACHE1_PUSH0) | 1); - nv_wr32(priv, NV04_PFIFO_CACHE1_HASH, 0); - - nv_wr32(priv, NV04_PFIFO_CACHE1_DMA_PUSH, - nv_rd32(priv, NV04_PFIFO_CACHE1_DMA_PUSH) | 1); - nv_wr32(priv, NV04_PFIFO_CACHE1_PULL0, 1); - + nv04_fifo_cache_error(device, priv, chid, get); status &= ~NV_PFIFO_INTR_CACHE_ERROR; } if (status & NV_PFIFO_INTR_DMA_PUSHER) { - u32 dma_get = nv_rd32(priv, 0x003244); - u32 dma_put = nv_rd32(priv, 0x003240); - u32 push = nv_rd32(priv, 0x003220); - u32 state = nv_rd32(priv, 0x003228); - - if (device->card_type == NV_50) { - u32 ho_get = nv_rd32(priv, 0x003328); - u32 ho_put = nv_rd32(priv, 0x003320); - u32 ib_get = nv_rd32(priv, 0x003334); - u32 ib_put = nv_rd32(priv, 0x003330); - - nv_error(priv, "DMA_PUSHER - Ch %d Get 0x%02x%08x " - "Put 0x%02x%08x IbGet 0x%08x IbPut 0x%08x " - "State 0x%08x (err: %s) Push 0x%08x\n", - chid, ho_get, dma_get, ho_put, - dma_put, ib_get, ib_put, state, - nv_dma_state_err(state), - push); - - /* METHOD_COUNT, in DMA_STATE on earlier chipsets */ - nv_wr32(priv, 0x003364, 0x00000000); - if (dma_get != dma_put || ho_get != ho_put) { - nv_wr32(priv, 0x003244, dma_put); - nv_wr32(priv, 0x003328, ho_put); - } else - if (ib_get != ib_put) { - nv_wr32(priv, 0x003334, ib_put); - } - } else { - nv_error(priv, "DMA_PUSHER - Ch %d Get 0x%08x " - "Put 0x%08x State 0x%08x (err: %s) Push 0x%08x\n", - chid, dma_get, dma_put, state, - nv_dma_state_err(state), push); - - if (dma_get != dma_put) - nv_wr32(priv, 0x003244, dma_put); - } - - nv_wr32(priv, 0x003228, 0x00000000); - nv_wr32(priv, 0x003220, 0x00000001); - nv_wr32(priv, 0x002100, NV_PFIFO_INTR_DMA_PUSHER); + nv04_fifo_dma_pusher(device, priv, chid); status &= ~NV_PFIFO_INTR_DMA_PUSHER; } @@ -528,6 +537,12 @@ nv04_fifo_intr(struct nouveau_subdev *subdev) status &= ~0x00000010; nv_wr32(priv, 0x002100, 0x00000010); } + + if (status & 0x40000000) { + nouveau_event_trigger(priv->base.uevent, 0); + nv_wr32(priv, 0x002100, 0x40000000); + status &= ~0x40000000; + } } if (status) { diff --git a/drivers/gpu/drm/nouveau/core/engine/fifo/nv50.c b/drivers/gpu/drm/nouveau/core/engine/fifo/nv50.c index bd096364f680..840af6172788 100644 --- a/drivers/gpu/drm/nouveau/core/engine/fifo/nv50.c +++ b/drivers/gpu/drm/nouveau/core/engine/fifo/nv50.c @@ -129,7 +129,8 @@ nv50_fifo_context_detach(struct nouveau_object *parent, bool suspend, /* do the kickoff... */ nv_wr32(priv, 0x0032fc, nv_gpuobj(base)->addr >> 12); if (!nv_wait_ne(priv, 0x0032fc, 0xffffffff, 0xffffffff)) { - nv_error(priv, "channel %d unload timeout\n", chan->base.chid); + nv_error(priv, "channel %d [%s] unload timeout\n", + chan->base.chid, nouveau_client_name(chan)); if (suspend) ret = -EBUSY; } @@ -480,7 +481,7 @@ nv50_fifo_init(struct nouveau_object *object) nv_wr32(priv, 0x002044, 0x01003fff); nv_wr32(priv, 0x002100, 0xffffffff); - nv_wr32(priv, 0x002140, 0xffffffff); + nv_wr32(priv, 0x002140, 0xbfffffff); for (i = 0; i < 128; i++) nv_wr32(priv, 0x002600 + (i * 4), 0x00000000); diff --git a/drivers/gpu/drm/nouveau/core/engine/fifo/nv84.c b/drivers/gpu/drm/nouveau/core/engine/fifo/nv84.c index 1eb1c512f503..094000e87871 100644 --- a/drivers/gpu/drm/nouveau/core/engine/fifo/nv84.c +++ b/drivers/gpu/drm/nouveau/core/engine/fifo/nv84.c @@ -26,6 +26,7 @@ #include <core/client.h> #include <core/engctx.h> #include <core/ramht.h> +#include <core/event.h> #include <core/class.h> #include <core/math.h> @@ -100,7 +101,8 @@ nv84_fifo_context_detach(struct nouveau_object *parent, bool suspend, done = nv_wait_ne(priv, 0x0032fc, 0xffffffff, 0xffffffff); nv_wr32(priv, 0x002520, save); if (!done) { - nv_error(priv, "channel %d unload timeout\n", chan->base.chid); + nv_error(priv, "channel %d [%s] unload timeout\n", + chan->base.chid, nouveau_client_name(chan)); if (suspend) return -EBUSY; } @@ -378,6 +380,20 @@ nv84_fifo_cclass = { * PFIFO engine ******************************************************************************/ +static void +nv84_fifo_uevent_enable(struct nouveau_event *event, int index) +{ + struct nv84_fifo_priv *priv = event->priv; + nv_mask(priv, 0x002140, 0x40000000, 0x40000000); +} + +static void +nv84_fifo_uevent_disable(struct nouveau_event *event, int index) +{ + struct nv84_fifo_priv *priv = event->priv; + nv_mask(priv, 0x002140, 0x40000000, 0x00000000); +} + static int nv84_fifo_ctor(struct nouveau_object *parent, struct nouveau_object *engine, struct nouveau_oclass *oclass, void *data, u32 size, @@ -401,6 +417,10 @@ nv84_fifo_ctor(struct nouveau_object *parent, struct nouveau_object *engine, if (ret) return ret; + priv->base.uevent->enable = nv84_fifo_uevent_enable; + priv->base.uevent->disable = nv84_fifo_uevent_disable; + priv->base.uevent->priv = priv; + nv_subdev(priv)->unit = 0x00000100; nv_subdev(priv)->intr = nv04_fifo_intr; nv_engine(priv)->cclass = &nv84_fifo_cclass; diff --git a/drivers/gpu/drm/nouveau/core/engine/fifo/nvc0.c b/drivers/gpu/drm/nouveau/core/engine/fifo/nvc0.c index b4365dde1859..4f226afb5591 100644 --- a/drivers/gpu/drm/nouveau/core/engine/fifo/nvc0.c +++ b/drivers/gpu/drm/nouveau/core/engine/fifo/nvc0.c @@ -27,6 +27,7 @@ #include <core/namedb.h> #include <core/gpuobj.h> #include <core/engctx.h> +#include <core/event.h> #include <core/class.h> #include <core/math.h> #include <core/enum.h> @@ -149,7 +150,8 @@ nvc0_fifo_context_detach(struct nouveau_object *parent, bool suspend, nv_wr32(priv, 0x002634, chan->base.chid); if (!nv_wait(priv, 0x002634, 0xffffffff, chan->base.chid)) { - nv_error(priv, "channel %d kick timeout\n", chan->base.chid); + nv_error(priv, "channel %d [%s] kick timeout\n", + chan->base.chid, nouveau_client_name(chan)); if (suspend) return -EBUSY; } @@ -333,17 +335,17 @@ nvc0_fifo_cclass = { ******************************************************************************/ static const struct nouveau_enum nvc0_fifo_fault_unit[] = { - { 0x00, "PGRAPH" }, + { 0x00, "PGRAPH", NULL, NVDEV_ENGINE_GR }, { 0x03, "PEEPHOLE" }, { 0x04, "BAR1" }, { 0x05, "BAR3" }, - { 0x07, "PFIFO" }, - { 0x10, "PBSP" }, - { 0x11, "PPPP" }, + { 0x07, "PFIFO", NULL, NVDEV_ENGINE_FIFO }, + { 0x10, "PBSP", NULL, NVDEV_ENGINE_BSP }, + { 0x11, "PPPP", NULL, NVDEV_ENGINE_PPP }, { 0x13, "PCOUNTER" }, - { 0x14, "PVP" }, - { 0x15, "PCOPY0" }, - { 0x16, "PCOPY1" }, + { 0x14, "PVP", NULL, NVDEV_ENGINE_VP }, + { 0x15, "PCOPY0", NULL, NVDEV_ENGINE_COPY0 }, + { 0x16, "PCOPY1", NULL, NVDEV_ENGINE_COPY1 }, { 0x17, "PDAEMON" }, {} }; @@ -402,6 +404,9 @@ nvc0_fifo_isr_vm_fault(struct nvc0_fifo_priv *priv, int unit) u32 vahi = nv_rd32(priv, 0x002808 + (unit * 0x10)); u32 stat = nv_rd32(priv, 0x00280c + (unit * 0x10)); u32 client = (stat & 0x00001f00) >> 8; + const struct nouveau_enum *en; + struct nouveau_engine *engine; + struct nouveau_object *engctx = NULL; switch (unit) { case 3: /* PEEPHOLE */ @@ -420,16 +425,26 @@ nvc0_fifo_isr_vm_fault(struct nvc0_fifo_priv *priv, int unit) nv_error(priv, "%s fault at 0x%010llx [", (stat & 0x00000080) ? "write" : "read", (u64)vahi << 32 | valo); nouveau_enum_print(nvc0_fifo_fault_reason, stat & 0x0000000f); - printk("] from "); - nouveau_enum_print(nvc0_fifo_fault_unit, unit); + pr_cont("] from "); + en = nouveau_enum_print(nvc0_fifo_fault_unit, unit); if (stat & 0x00000040) { - printk("/"); + pr_cont("/"); nouveau_enum_print(nvc0_fifo_fault_hubclient, client); } else { - printk("/GPC%d/", (stat & 0x1f000000) >> 24); + pr_cont("/GPC%d/", (stat & 0x1f000000) >> 24); nouveau_enum_print(nvc0_fifo_fault_gpcclient, client); } - printk(" on channel 0x%010llx\n", (u64)inst << 12); + + if (en && en->data2) { + engine = nouveau_engine(priv, en->data2); + if (engine) + engctx = nouveau_engctx_get(engine, inst); + + } + pr_cont(" on channel 0x%010llx [%s]\n", (u64)inst << 12, + nouveau_client_name(engctx)); + + nouveau_engctx_put(engctx); } static int @@ -484,10 +499,12 @@ nvc0_fifo_isr_subfifo_intr(struct nvc0_fifo_priv *priv, int unit) if (show) { nv_error(priv, "SUBFIFO%d:", unit); nouveau_bitfield_print(nvc0_fifo_subfifo_intr, show); - printk("\n"); - nv_error(priv, "SUBFIFO%d: ch %d subc %d mthd 0x%04x " - "data 0x%08x\n", - unit, chid, subc, mthd, data); + pr_cont("\n"); + nv_error(priv, + "SUBFIFO%d: ch %d [%s] subc %d mthd 0x%04x data 0x%08x\n", + unit, chid, + nouveau_client_name_for_fifo_chid(&priv->base, chid), + subc, mthd, data); } nv_wr32(priv, 0x0400c0 + (unit * 0x2000), 0x80600008); @@ -501,12 +518,34 @@ nvc0_fifo_intr(struct nouveau_subdev *subdev) u32 mask = nv_rd32(priv, 0x002140); u32 stat = nv_rd32(priv, 0x002100) & mask; + if (stat & 0x00000001) { + u32 intr = nv_rd32(priv, 0x00252c); + nv_warn(priv, "INTR 0x00000001: 0x%08x\n", intr); + nv_wr32(priv, 0x002100, 0x00000001); + stat &= ~0x00000001; + } + if (stat & 0x00000100) { - nv_warn(priv, "unknown status 0x00000100\n"); + u32 intr = nv_rd32(priv, 0x00254c); + nv_warn(priv, "INTR 0x00000100: 0x%08x\n", intr); nv_wr32(priv, 0x002100, 0x00000100); stat &= ~0x00000100; } + if (stat & 0x00010000) { + u32 intr = nv_rd32(priv, 0x00256c); + nv_warn(priv, "INTR 0x00010000: 0x%08x\n", intr); + nv_wr32(priv, 0x002100, 0x00010000); + stat &= ~0x00010000; + } + + if (stat & 0x01000000) { + u32 intr = nv_rd32(priv, 0x00258c); + nv_warn(priv, "INTR 0x01000000: 0x%08x\n", intr); + nv_wr32(priv, 0x002100, 0x01000000); + stat &= ~0x01000000; + } + if (stat & 0x10000000) { u32 units = nv_rd32(priv, 0x00259c); u32 u = units; @@ -536,11 +575,20 @@ nvc0_fifo_intr(struct nouveau_subdev *subdev) } if (stat & 0x40000000) { - nv_warn(priv, "unknown status 0x40000000\n"); - nv_mask(priv, 0x002a00, 0x00000000, 0x00000000); + u32 intr0 = nv_rd32(priv, 0x0025a4); + u32 intr1 = nv_mask(priv, 0x002a00, 0x00000000, 0x00000); + nv_debug(priv, "INTR 0x40000000: 0x%08x 0x%08x\n", + intr0, intr1); stat &= ~0x40000000; } + if (stat & 0x80000000) { + u32 intr = nv_mask(priv, 0x0025a8, 0x00000000, 0x00000000); + nouveau_event_trigger(priv->base.uevent, 0); + nv_debug(priv, "INTR 0x80000000: 0x%08x\n", intr); + stat &= ~0x80000000; + } + if (stat) { nv_fatal(priv, "unhandled status 0x%08x\n", stat); nv_wr32(priv, 0x002100, stat); @@ -548,6 +596,20 @@ nvc0_fifo_intr(struct nouveau_subdev *subdev) } } +static void +nvc0_fifo_uevent_enable(struct nouveau_event *event, int index) +{ + struct nvc0_fifo_priv *priv = event->priv; + nv_mask(priv, 0x002140, 0x80000000, 0x80000000); +} + +static void +nvc0_fifo_uevent_disable(struct nouveau_event *event, int index) +{ + struct nvc0_fifo_priv *priv = event->priv; + nv_mask(priv, 0x002140, 0x80000000, 0x00000000); +} + static int nvc0_fifo_ctor(struct nouveau_object *parent, struct nouveau_object *engine, struct nouveau_oclass *oclass, void *data, u32 size, @@ -581,6 +643,10 @@ nvc0_fifo_ctor(struct nouveau_object *parent, struct nouveau_object *engine, if (ret) return ret; + priv->base.uevent->enable = nvc0_fifo_uevent_enable; + priv->base.uevent->disable = nvc0_fifo_uevent_disable; + priv->base.uevent->priv = priv; + nv_subdev(priv)->unit = 0x00000100; nv_subdev(priv)->intr = nvc0_fifo_intr; nv_engine(priv)->cclass = &nvc0_fifo_cclass; @@ -639,7 +705,8 @@ nvc0_fifo_init(struct nouveau_object *object) nv_wr32(priv, 0x002a00, 0xffffffff); /* clears PFIFO.INTR bit 30 */ nv_wr32(priv, 0x002100, 0xffffffff); - nv_wr32(priv, 0x002140, 0xbfffffff); + nv_wr32(priv, 0x002140, 0x3fffffff); + nv_wr32(priv, 0x002628, 0x00000001); /* makes mthd 0x20 work */ return 0; } diff --git a/drivers/gpu/drm/nouveau/core/engine/fifo/nve0.c b/drivers/gpu/drm/nouveau/core/engine/fifo/nve0.c index c930da99c2c1..4419e40d88e9 100644 --- a/drivers/gpu/drm/nouveau/core/engine/fifo/nve0.c +++ b/drivers/gpu/drm/nouveau/core/engine/fifo/nve0.c @@ -27,6 +27,7 @@ #include <core/namedb.h> #include <core/gpuobj.h> #include <core/engctx.h> +#include <core/event.h> #include <core/class.h> #include <core/math.h> #include <core/enum.h> @@ -184,7 +185,8 @@ nve0_fifo_context_detach(struct nouveau_object *parent, bool suspend, nv_wr32(priv, 0x002634, chan->base.chid); if (!nv_wait(priv, 0x002634, 0xffffffff, chan->base.chid)) { - nv_error(priv, "channel %d kick timeout\n", chan->base.chid); + nv_error(priv, "channel %d [%s] kick timeout\n", + chan->base.chid, nouveau_client_name(chan)); if (suspend) return -EBUSY; } @@ -412,20 +414,34 @@ nve0_fifo_isr_vm_fault(struct nve0_fifo_priv *priv, int unit) u32 vahi = nv_rd32(priv, 0x2808 + (unit * 0x10)); u32 stat = nv_rd32(priv, 0x280c + (unit * 0x10)); u32 client = (stat & 0x00001f00) >> 8; + const struct nouveau_enum *en; + struct nouveau_engine *engine; + struct nouveau_object *engctx = NULL; nv_error(priv, "PFIFO: %s fault at 0x%010llx [", (stat & 0x00000080) ? "write" : "read", (u64)vahi << 32 | valo); nouveau_enum_print(nve0_fifo_fault_reason, stat & 0x0000000f); - printk("] from "); - nouveau_enum_print(nve0_fifo_fault_unit, unit); + pr_cont("] from "); + en = nouveau_enum_print(nve0_fifo_fault_unit, unit); if (stat & 0x00000040) { - printk("/"); + pr_cont("/"); nouveau_enum_print(nve0_fifo_fault_hubclient, client); } else { - printk("/GPC%d/", (stat & 0x1f000000) >> 24); + pr_cont("/GPC%d/", (stat & 0x1f000000) >> 24); nouveau_enum_print(nve0_fifo_fault_gpcclient, client); } - printk(" on channel 0x%010llx\n", (u64)inst << 12); + + if (en && en->data2) { + engine = nouveau_engine(priv, en->data2); + if (engine) + engctx = nouveau_engctx_get(engine, inst); + + } + + pr_cont(" on channel 0x%010llx [%s]\n", (u64)inst << 12, + nouveau_client_name(engctx)); + + nouveau_engctx_put(engctx); } static int @@ -480,10 +496,12 @@ nve0_fifo_isr_subfifo_intr(struct nve0_fifo_priv *priv, int unit) if (show) { nv_error(priv, "SUBFIFO%d:", unit); nouveau_bitfield_print(nve0_fifo_subfifo_intr, show); - printk("\n"); - nv_error(priv, "SUBFIFO%d: ch %d subc %d mthd 0x%04x " - "data 0x%08x\n", - unit, chid, subc, mthd, data); + pr_cont("\n"); + nv_error(priv, + "SUBFIFO%d: ch %d [%s] subc %d mthd 0x%04x data 0x%08x\n", + unit, chid, + nouveau_client_name_for_fifo_chid(&priv->base, chid), + subc, mthd, data); } nv_wr32(priv, 0x0400c0 + (unit * 0x2000), 0x80600008); @@ -537,6 +555,12 @@ nve0_fifo_intr(struct nouveau_subdev *subdev) stat &= ~0x40000000; } + if (stat & 0x80000000) { + nouveau_event_trigger(priv->base.uevent, 0); + nv_wr32(priv, 0x002100, 0x80000000); + stat &= ~0x80000000; + } + if (stat) { nv_fatal(priv, "unhandled status 0x%08x\n", stat); nv_wr32(priv, 0x002100, stat); @@ -544,6 +568,20 @@ nve0_fifo_intr(struct nouveau_subdev *subdev) } } +static void +nve0_fifo_uevent_enable(struct nouveau_event *event, int index) +{ + struct nve0_fifo_priv *priv = event->priv; + nv_mask(priv, 0x002140, 0x80000000, 0x80000000); +} + +static void +nve0_fifo_uevent_disable(struct nouveau_event *event, int index) +{ + struct nve0_fifo_priv *priv = event->priv; + nv_mask(priv, 0x002140, 0x80000000, 0x00000000); +} + static int nve0_fifo_ctor(struct nouveau_object *parent, struct nouveau_object *engine, struct nouveau_oclass *oclass, void *data, u32 size, @@ -567,6 +605,10 @@ nve0_fifo_ctor(struct nouveau_object *parent, struct nouveau_object *engine, if (ret) return ret; + priv->base.uevent->enable = nve0_fifo_uevent_enable; + priv->base.uevent->disable = nve0_fifo_uevent_disable; + priv->base.uevent->priv = priv; + nv_subdev(priv)->unit = 0x00000100; nv_subdev(priv)->intr = nve0_fifo_intr; nv_engine(priv)->cclass = &nve0_fifo_cclass; @@ -617,7 +659,7 @@ nve0_fifo_init(struct nouveau_object *object) nv_wr32(priv, 0x002a00, 0xffffffff); nv_wr32(priv, 0x002100, 0xffffffff); - nv_wr32(priv, 0x002140, 0xbfffffff); + nv_wr32(priv, 0x002140, 0x3fffffff); return 0; } diff --git a/drivers/gpu/drm/nouveau/core/engine/graph/nv04.c b/drivers/gpu/drm/nouveau/core/engine/graph/nv04.c index e30a9c5ff1fc..ad13dcdd15f9 100644 --- a/drivers/gpu/drm/nouveau/core/engine/graph/nv04.c +++ b/drivers/gpu/drm/nouveau/core/engine/graph/nv04.c @@ -22,6 +22,7 @@ * DEALINGS IN THE SOFTWARE. */ +#include <core/client.h> #include <core/os.h> #include <core/class.h> #include <core/handle.h> @@ -1297,16 +1298,17 @@ nv04_graph_intr(struct nouveau_subdev *subdev) nv_wr32(priv, NV04_PGRAPH_FIFO, 0x00000001); if (show) { - nv_error(priv, ""); + nv_error(priv, "%s", ""); nouveau_bitfield_print(nv04_graph_intr_name, show); - printk(" nsource:"); + pr_cont(" nsource:"); nouveau_bitfield_print(nv04_graph_nsource, nsource); - printk(" nstatus:"); + pr_cont(" nstatus:"); nouveau_bitfield_print(nv04_graph_nstatus, nstatus); - printk("\n"); - nv_error(priv, "ch %d/%d class 0x%04x " - "mthd 0x%04x data 0x%08x\n", - chid, subc, class, mthd, data); + pr_cont("\n"); + nv_error(priv, + "ch %d [%s] subc %d class 0x%04x mthd 0x%04x data 0x%08x\n", + chid, nouveau_client_name(chan), subc, class, mthd, + data); } nouveau_namedb_put(handle); diff --git a/drivers/gpu/drm/nouveau/core/engine/graph/nv10.c b/drivers/gpu/drm/nouveau/core/engine/graph/nv10.c index 5c0f843ea249..23c143aaa556 100644 --- a/drivers/gpu/drm/nouveau/core/engine/graph/nv10.c +++ b/drivers/gpu/drm/nouveau/core/engine/graph/nv10.c @@ -22,6 +22,7 @@ * DEALINGS IN THE SOFTWARE. */ +#include <core/client.h> #include <core/os.h> #include <core/class.h> #include <core/handle.h> @@ -1193,16 +1194,17 @@ nv10_graph_intr(struct nouveau_subdev *subdev) nv_wr32(priv, NV04_PGRAPH_FIFO, 0x00000001); if (show) { - nv_error(priv, ""); + nv_error(priv, "%s", ""); nouveau_bitfield_print(nv10_graph_intr_name, show); - printk(" nsource:"); + pr_cont(" nsource:"); nouveau_bitfield_print(nv04_graph_nsource, nsource); - printk(" nstatus:"); + pr_cont(" nstatus:"); nouveau_bitfield_print(nv10_graph_nstatus, nstatus); - printk("\n"); - nv_error(priv, "ch %d/%d class 0x%04x " - "mthd 0x%04x data 0x%08x\n", - chid, subc, class, mthd, data); + pr_cont("\n"); + nv_error(priv, + "ch %d [%s] subc %d class 0x%04x mthd 0x%04x data 0x%08x\n", + chid, nouveau_client_name(chan), subc, class, mthd, + data); } nouveau_namedb_put(handle); diff --git a/drivers/gpu/drm/nouveau/core/engine/graph/nv20.c b/drivers/gpu/drm/nouveau/core/engine/graph/nv20.c index 5b20401bf911..0607b9801748 100644 --- a/drivers/gpu/drm/nouveau/core/engine/graph/nv20.c +++ b/drivers/gpu/drm/nouveau/core/engine/graph/nv20.c @@ -1,3 +1,4 @@ +#include <core/client.h> #include <core/os.h> #include <core/class.h> #include <core/engctx.h> @@ -224,15 +225,17 @@ nv20_graph_intr(struct nouveau_subdev *subdev) nv_wr32(priv, NV04_PGRAPH_FIFO, 0x00000001); if (show) { - nv_error(priv, ""); + nv_error(priv, "%s", ""); nouveau_bitfield_print(nv10_graph_intr_name, show); - printk(" nsource:"); + pr_cont(" nsource:"); nouveau_bitfield_print(nv04_graph_nsource, nsource); - printk(" nstatus:"); + pr_cont(" nstatus:"); nouveau_bitfield_print(nv10_graph_nstatus, nstatus); - printk("\n"); - nv_error(priv, "ch %d/%d class 0x%04x mthd 0x%04x data 0x%08x\n", - chid, subc, class, mthd, data); + pr_cont("\n"); + nv_error(priv, + "ch %d [%s] subc %d class 0x%04x mthd 0x%04x data 0x%08x\n", + chid, nouveau_client_name(engctx), subc, class, mthd, + data); } nouveau_engctx_put(engctx); diff --git a/drivers/gpu/drm/nouveau/core/engine/graph/nv40.c b/drivers/gpu/drm/nouveau/core/engine/graph/nv40.c index 0b36dd3deebd..17049d5c723d 100644 --- a/drivers/gpu/drm/nouveau/core/engine/graph/nv40.c +++ b/drivers/gpu/drm/nouveau/core/engine/graph/nv40.c @@ -22,6 +22,7 @@ * Authors: Ben Skeggs */ +#include <core/client.h> #include <core/os.h> #include <core/class.h> #include <core/handle.h> @@ -321,16 +322,17 @@ nv40_graph_intr(struct nouveau_subdev *subdev) nv_wr32(priv, NV04_PGRAPH_FIFO, 0x00000001); if (show) { - nv_error(priv, ""); + nv_error(priv, "%s", ""); nouveau_bitfield_print(nv10_graph_intr_name, show); - printk(" nsource:"); + pr_cont(" nsource:"); nouveau_bitfield_print(nv04_graph_nsource, nsource); - printk(" nstatus:"); + pr_cont(" nstatus:"); nouveau_bitfield_print(nv10_graph_nstatus, nstatus); - printk("\n"); - nv_error(priv, "ch %d [0x%08x] subc %d class 0x%04x " - "mthd 0x%04x data 0x%08x\n", - chid, inst << 4, subc, class, mthd, data); + pr_cont("\n"); + nv_error(priv, + "ch %d [0x%08x %s] subc %d class 0x%04x mthd 0x%04x data 0x%08x\n", + chid, inst << 4, nouveau_client_name(engctx), subc, + class, mthd, data); } nouveau_engctx_put(engctx); diff --git a/drivers/gpu/drm/nouveau/core/engine/graph/nv50.c b/drivers/gpu/drm/nouveau/core/engine/graph/nv50.c index b1c3d835b4c2..f2b1a7a124f2 100644 --- a/drivers/gpu/drm/nouveau/core/engine/graph/nv50.c +++ b/drivers/gpu/drm/nouveau/core/engine/graph/nv50.c @@ -24,6 +24,7 @@ #include <core/os.h> #include <core/class.h> +#include <core/client.h> #include <core/handle.h> #include <core/engctx.h> #include <core/enum.h> @@ -418,7 +419,7 @@ nv50_priv_mp_trap(struct nv50_graph_priv *priv, int tpid, int display) nv_error(priv, "TRAP_MP_EXEC - " "TP %d MP %d: ", tpid, i); nouveau_enum_print(nv50_mp_exec_error_names, status); - printk(" at %06x warp %d, opcode %08x %08x\n", + pr_cont(" at %06x warp %d, opcode %08x %08x\n", pc&0xffffff, pc >> 24, oplow, ophigh); } @@ -532,7 +533,7 @@ nv50_priv_tp_trap(struct nv50_graph_priv *priv, int type, u32 ustatus_old, static int nv50_graph_trap_handler(struct nv50_graph_priv *priv, u32 display, - int chid, u64 inst) + int chid, u64 inst, struct nouveau_object *engctx) { u32 status = nv_rd32(priv, 0x400108); u32 ustatus; @@ -565,12 +566,11 @@ nv50_graph_trap_handler(struct nv50_graph_priv *priv, u32 display, nv_error(priv, "TRAP DISPATCH_FAULT\n"); if (display && (addr & 0x80000000)) { - nv_error(priv, "ch %d [0x%010llx] " - "subc %d class 0x%04x mthd 0x%04x " - "data 0x%08x%08x " - "400808 0x%08x 400848 0x%08x\n", - chid, inst, subc, class, mthd, datah, - datal, addr, r848); + nv_error(priv, + "ch %d [0x%010llx %s] subc %d class 0x%04x mthd 0x%04x data 0x%08x%08x 400808 0x%08x 400848 0x%08x\n", + chid, inst, + nouveau_client_name(engctx), subc, + class, mthd, datah, datal, addr, r848); } else if (display) { nv_error(priv, "no stuck command?\n"); @@ -591,11 +591,11 @@ nv50_graph_trap_handler(struct nv50_graph_priv *priv, u32 display, nv_error(priv, "TRAP DISPATCH_QUERY\n"); if (display && (addr & 0x80000000)) { - nv_error(priv, "ch %d [0x%010llx] " - "subc %d class 0x%04x mthd 0x%04x " - "data 0x%08x 40084c 0x%08x\n", - chid, inst, subc, class, mthd, - data, addr); + nv_error(priv, + "ch %d [0x%010llx %s] subc %d class 0x%04x mthd 0x%04x data 0x%08x 40084c 0x%08x\n", + chid, inst, + nouveau_client_name(engctx), subc, + class, mthd, data, addr); } else if (display) { nv_error(priv, "no stuck command?\n"); @@ -623,7 +623,7 @@ nv50_graph_trap_handler(struct nv50_graph_priv *priv, u32 display, if (display) { nv_error(priv, "TRAP_M2MF"); nouveau_bitfield_print(nv50_graph_trap_m2mf, ustatus); - printk("\n"); + pr_cont("\n"); nv_error(priv, "TRAP_M2MF %08x %08x %08x %08x\n", nv_rd32(priv, 0x406804), nv_rd32(priv, 0x406808), nv_rd32(priv, 0x40680c), nv_rd32(priv, 0x406810)); @@ -644,7 +644,7 @@ nv50_graph_trap_handler(struct nv50_graph_priv *priv, u32 display, if (display) { nv_error(priv, "TRAP_VFETCH"); nouveau_bitfield_print(nv50_graph_trap_vfetch, ustatus); - printk("\n"); + pr_cont("\n"); nv_error(priv, "TRAP_VFETCH %08x %08x %08x %08x\n", nv_rd32(priv, 0x400c00), nv_rd32(priv, 0x400c08), nv_rd32(priv, 0x400c0c), nv_rd32(priv, 0x400c10)); @@ -661,7 +661,7 @@ nv50_graph_trap_handler(struct nv50_graph_priv *priv, u32 display, if (display) { nv_error(priv, "TRAP_STRMOUT"); nouveau_bitfield_print(nv50_graph_trap_strmout, ustatus); - printk("\n"); + pr_cont("\n"); nv_error(priv, "TRAP_STRMOUT %08x %08x %08x %08x\n", nv_rd32(priv, 0x401804), nv_rd32(priv, 0x401808), nv_rd32(priv, 0x40180c), nv_rd32(priv, 0x401810)); @@ -682,7 +682,7 @@ nv50_graph_trap_handler(struct nv50_graph_priv *priv, u32 display, if (display) { nv_error(priv, "TRAP_CCACHE"); nouveau_bitfield_print(nv50_graph_trap_ccache, ustatus); - printk("\n"); + pr_cont("\n"); nv_error(priv, "TRAP_CCACHE %08x %08x %08x %08x" " %08x %08x %08x\n", nv_rd32(priv, 0x405000), nv_rd32(priv, 0x405004), @@ -774,11 +774,12 @@ nv50_graph_intr(struct nouveau_subdev *subdev) u32 ecode = nv_rd32(priv, 0x400110); nv_error(priv, "DATA_ERROR "); nouveau_enum_print(nv50_data_error_names, ecode); - printk("\n"); + pr_cont("\n"); } if (stat & 0x00200000) { - if (!nv50_graph_trap_handler(priv, show, chid, (u64)inst << 12)) + if (!nv50_graph_trap_handler(priv, show, chid, (u64)inst << 12, + engctx)) show &= ~0x00200000; } @@ -786,12 +787,13 @@ nv50_graph_intr(struct nouveau_subdev *subdev) nv_wr32(priv, 0x400500, 0x00010001); if (show) { - nv_error(priv, ""); + nv_error(priv, "%s", ""); nouveau_bitfield_print(nv50_graph_intr_name, show); - printk("\n"); - nv_error(priv, "ch %d [0x%010llx] subc %d class 0x%04x " - "mthd 0x%04x data 0x%08x\n", - chid, (u64)inst << 12, subc, class, mthd, data); + pr_cont("\n"); + nv_error(priv, + "ch %d [0x%010llx %s] subc %d class 0x%04x mthd 0x%04x data 0x%08x\n", + chid, (u64)inst << 12, nouveau_client_name(engctx), + subc, class, mthd, data); } if (nv_rd32(priv, 0x400824) & (1 << 31)) @@ -907,9 +909,8 @@ nv50_graph_init(struct nouveau_object *object) nv_wr32(priv, 0x400828, 0x00000000); nv_wr32(priv, 0x40082c, 0x00000000); nv_wr32(priv, 0x400830, 0x00000000); - nv_wr32(priv, 0x400724, 0x00000000); nv_wr32(priv, 0x40032c, 0x00000000); - nv_wr32(priv, 0x400320, 4); /* CTXCTL_CMD = NEWCTXDMA */ + nv_wr32(priv, 0x400330, 0x00000000); /* some unknown zcull magic */ switch (nv_device(priv)->chipset & 0xf0) { diff --git a/drivers/gpu/drm/nouveau/core/engine/graph/nvc0.c b/drivers/gpu/drm/nouveau/core/engine/graph/nvc0.c index 45aff5f5085a..0de0dd724aff 100644 --- a/drivers/gpu/drm/nouveau/core/engine/graph/nvc0.c +++ b/drivers/gpu/drm/nouveau/core/engine/graph/nvc0.c @@ -433,10 +433,10 @@ nvc0_graph_intr(struct nouveau_subdev *subdev) if (stat & 0x00000010) { handle = nouveau_handle_get_class(engctx, class); if (!handle || nv_call(handle->object, mthd, data)) { - nv_error(priv, "ILLEGAL_MTHD ch %d [0x%010llx] " - "subc %d class 0x%04x mthd 0x%04x " - "data 0x%08x\n", - chid, inst << 12, subc, class, mthd, data); + nv_error(priv, + "ILLEGAL_MTHD ch %d [0x%010llx %s] subc %d class 0x%04x mthd 0x%04x data 0x%08x\n", + chid, inst << 12, nouveau_client_name(engctx), + subc, class, mthd, data); } nouveau_handle_put(handle); nv_wr32(priv, 0x400100, 0x00000010); @@ -444,9 +444,10 @@ nvc0_graph_intr(struct nouveau_subdev *subdev) } if (stat & 0x00000020) { - nv_error(priv, "ILLEGAL_CLASS ch %d [0x%010llx] subc %d " - "class 0x%04x mthd 0x%04x data 0x%08x\n", - chid, inst << 12, subc, class, mthd, data); + nv_error(priv, + "ILLEGAL_CLASS ch %d [0x%010llx %s] subc %d class 0x%04x mthd 0x%04x data 0x%08x\n", + chid, inst << 12, nouveau_client_name(engctx), subc, + class, mthd, data); nv_wr32(priv, 0x400100, 0x00000020); stat &= ~0x00000020; } @@ -454,15 +455,16 @@ nvc0_graph_intr(struct nouveau_subdev *subdev) if (stat & 0x00100000) { nv_error(priv, "DATA_ERROR ["); nouveau_enum_print(nv50_data_error_names, code); - printk("] ch %d [0x%010llx] subc %d class 0x%04x " - "mthd 0x%04x data 0x%08x\n", - chid, inst << 12, subc, class, mthd, data); + pr_cont("] ch %d [0x%010llx %s] subc %d class 0x%04x mthd 0x%04x data 0x%08x\n", + chid, inst << 12, nouveau_client_name(engctx), subc, + class, mthd, data); nv_wr32(priv, 0x400100, 0x00100000); stat &= ~0x00100000; } if (stat & 0x00200000) { - nv_error(priv, "TRAP ch %d [0x%010llx]\n", chid, inst << 12); + nv_error(priv, "TRAP ch %d [0x%010llx %s]\n", chid, inst << 12, + nouveau_client_name(engctx)); nvc0_graph_trap_intr(priv); nv_wr32(priv, 0x400100, 0x00200000); stat &= ~0x00200000; @@ -611,10 +613,8 @@ nvc0_graph_ctor(struct nouveau_object *parent, struct nouveau_object *engine, static void nvc0_graph_dtor_fw(struct nvc0_graph_fuc *fuc) { - if (fuc->data) { - kfree(fuc->data); - fuc->data = NULL; - } + kfree(fuc->data); + fuc->data = NULL; } void @@ -622,8 +622,7 @@ nvc0_graph_dtor(struct nouveau_object *object) { struct nvc0_graph_priv *priv = (void *)object; - if (priv->data) - kfree(priv->data); + kfree(priv->data); nvc0_graph_dtor_fw(&priv->fuc409c); nvc0_graph_dtor_fw(&priv->fuc409d); diff --git a/drivers/gpu/drm/nouveau/core/engine/graph/nve0.c b/drivers/gpu/drm/nouveau/core/engine/graph/nve0.c index 9f82e9702b46..61cec0f6ff1c 100644 --- a/drivers/gpu/drm/nouveau/core/engine/graph/nve0.c +++ b/drivers/gpu/drm/nouveau/core/engine/graph/nve0.c @@ -78,15 +78,16 @@ nve0_graph_ctxctl_isr(struct nvc0_graph_priv *priv) } static void -nve0_graph_trap_isr(struct nvc0_graph_priv *priv, int chid, u64 inst) +nve0_graph_trap_isr(struct nvc0_graph_priv *priv, int chid, u64 inst, + struct nouveau_object *engctx) { u32 trap = nv_rd32(priv, 0x400108); int rop; if (trap & 0x00000001) { u32 stat = nv_rd32(priv, 0x404000); - nv_error(priv, "DISPATCH ch %d [0x%010llx] 0x%08x\n", - chid, inst, stat); + nv_error(priv, "DISPATCH ch %d [0x%010llx %s] 0x%08x\n", + chid, inst, nouveau_client_name(engctx), stat); nv_wr32(priv, 0x404000, 0xc0000000); nv_wr32(priv, 0x400108, 0x00000001); trap &= ~0x00000001; @@ -94,8 +95,8 @@ nve0_graph_trap_isr(struct nvc0_graph_priv *priv, int chid, u64 inst) if (trap & 0x00000010) { u32 stat = nv_rd32(priv, 0x405840); - nv_error(priv, "SHADER ch %d [0x%010llx] 0x%08x\n", - chid, inst, stat); + nv_error(priv, "SHADER ch %d [0x%010llx %s] 0x%08x\n", + chid, inst, nouveau_client_name(engctx), stat); nv_wr32(priv, 0x405840, 0xc0000000); nv_wr32(priv, 0x400108, 0x00000010); trap &= ~0x00000010; @@ -105,8 +106,10 @@ nve0_graph_trap_isr(struct nvc0_graph_priv *priv, int chid, u64 inst) for (rop = 0; rop < priv->rop_nr; rop++) { u32 statz = nv_rd32(priv, ROP_UNIT(rop, 0x070)); u32 statc = nv_rd32(priv, ROP_UNIT(rop, 0x144)); - nv_error(priv, "ROP%d ch %d [0x%010llx] 0x%08x 0x%08x\n", - rop, chid, inst, statz, statc); + nv_error(priv, + "ROP%d ch %d [0x%010llx %s] 0x%08x 0x%08x\n", + rop, chid, inst, nouveau_client_name(engctx), + statz, statc); nv_wr32(priv, ROP_UNIT(rop, 0x070), 0xc0000000); nv_wr32(priv, ROP_UNIT(rop, 0x144), 0xc0000000); } @@ -115,8 +118,8 @@ nve0_graph_trap_isr(struct nvc0_graph_priv *priv, int chid, u64 inst) } if (trap) { - nv_error(priv, "TRAP ch %d [0x%010llx] 0x%08x\n", - chid, inst, trap); + nv_error(priv, "TRAP ch %d [0x%010llx %s] 0x%08x\n", + chid, inst, nouveau_client_name(engctx), trap); nv_wr32(priv, 0x400108, trap); } } @@ -145,10 +148,10 @@ nve0_graph_intr(struct nouveau_subdev *subdev) if (stat & 0x00000010) { handle = nouveau_handle_get_class(engctx, class); if (!handle || nv_call(handle->object, mthd, data)) { - nv_error(priv, "ILLEGAL_MTHD ch %d [0x%010llx] " - "subc %d class 0x%04x mthd 0x%04x " - "data 0x%08x\n", - chid, inst, subc, class, mthd, data); + nv_error(priv, + "ILLEGAL_MTHD ch %d [0x%010llx %s] subc %d class 0x%04x mthd 0x%04x data 0x%08x\n", + chid, inst, nouveau_client_name(engctx), subc, + class, mthd, data); } nouveau_handle_put(handle); nv_wr32(priv, 0x400100, 0x00000010); @@ -156,9 +159,10 @@ nve0_graph_intr(struct nouveau_subdev *subdev) } if (stat & 0x00000020) { - nv_error(priv, "ILLEGAL_CLASS ch %d [0x%010llx] subc %d " - "class 0x%04x mthd 0x%04x data 0x%08x\n", - chid, inst, subc, class, mthd, data); + nv_error(priv, + "ILLEGAL_CLASS ch %d [0x%010llx %s] subc %d class 0x%04x mthd 0x%04x data 0x%08x\n", + chid, inst, nouveau_client_name(engctx), subc, class, + mthd, data); nv_wr32(priv, 0x400100, 0x00000020); stat &= ~0x00000020; } @@ -166,15 +170,15 @@ nve0_graph_intr(struct nouveau_subdev *subdev) if (stat & 0x00100000) { nv_error(priv, "DATA_ERROR ["); nouveau_enum_print(nv50_data_error_names, code); - printk("] ch %d [0x%010llx] subc %d class 0x%04x " - "mthd 0x%04x data 0x%08x\n", - chid, inst, subc, class, mthd, data); + pr_cont("] ch %d [0x%010llx %s] subc %d class 0x%04x mthd 0x%04x data 0x%08x\n", + chid, inst, nouveau_client_name(engctx), subc, class, + mthd, data); nv_wr32(priv, 0x400100, 0x00100000); stat &= ~0x00100000; } if (stat & 0x00200000) { - nve0_graph_trap_isr(priv, chid, inst); + nve0_graph_trap_isr(priv, chid, inst, engctx); nv_wr32(priv, 0x400100, 0x00200000); stat &= ~0x00200000; } diff --git a/drivers/gpu/drm/nouveau/core/engine/mpeg/nv31.c b/drivers/gpu/drm/nouveau/core/engine/mpeg/nv31.c index 9fd86375f4c4..49ecbb859b25 100644 --- a/drivers/gpu/drm/nouveau/core/engine/mpeg/nv31.c +++ b/drivers/gpu/drm/nouveau/core/engine/mpeg/nv31.c @@ -22,6 +22,7 @@ * Authors: Ben Skeggs */ +#include <core/client.h> #include <core/os.h> #include <core/class.h> #include <core/engctx.h> @@ -231,8 +232,10 @@ nv31_mpeg_intr(struct nouveau_subdev *subdev) nv_wr32(priv, 0x00b230, 0x00000001); if (show) { - nv_error(priv, "ch %d [0x%08x] 0x%08x 0x%08x 0x%08x 0x%08x\n", - chid, inst << 4, stat, type, mthd, data); + nv_error(priv, + "ch %d [0x%08x %s] 0x%08x 0x%08x 0x%08x 0x%08x\n", + chid, inst << 4, nouveau_client_name(engctx), stat, + type, mthd, data); } nouveau_engctx_put(engctx); diff --git a/drivers/gpu/drm/nouveau/core/engine/software/nv50.c b/drivers/gpu/drm/nouveau/core/engine/software/nv50.c index b0e7e1c01ce6..c48e74953771 100644 --- a/drivers/gpu/drm/nouveau/core/engine/software/nv50.c +++ b/drivers/gpu/drm/nouveau/core/engine/software/nv50.c @@ -28,6 +28,9 @@ #include <core/namedb.h> #include <core/handle.h> #include <core/gpuobj.h> +#include <core/event.h> + +#include <subdev/bar.h> #include <engine/software.h> #include <engine/disp.h> @@ -90,18 +93,11 @@ nv50_software_mthd_vblsem_release(struct nouveau_object *object, u32 mthd, { struct nv50_software_chan *chan = (void *)nv_engctx(object->parent); struct nouveau_disp *disp = nouveau_disp(object); - unsigned long flags; u32 crtc = *(u32 *)args; - if (crtc > 1) return -EINVAL; - disp->vblank.get(disp->vblank.data, crtc); - - spin_lock_irqsave(&disp->vblank.lock, flags); - list_add(&chan->base.vblank.head, &disp->vblank.list); - chan->base.vblank.crtc = crtc; - spin_unlock_irqrestore(&disp->vblank.lock, flags); + nouveau_event_get(disp->vblank, crtc, &chan->base.vblank.event); return 0; } @@ -136,6 +132,29 @@ nv50_software_sclass[] = { ******************************************************************************/ static int +nv50_software_vblsem_release(struct nouveau_eventh *event, int head) +{ + struct nouveau_software_chan *chan = + container_of(event, struct nouveau_software_chan, vblank.event); + struct nv50_software_priv *priv = (void *)nv_object(chan)->engine; + struct nouveau_bar *bar = nouveau_bar(priv); + + nv_wr32(priv, 0x001704, chan->vblank.channel); + nv_wr32(priv, 0x001710, 0x80000000 | chan->vblank.ctxdma); + bar->flush(bar); + + if (nv_device(priv)->chipset == 0x50) { + nv_wr32(priv, 0x001570, chan->vblank.offset); + nv_wr32(priv, 0x001574, chan->vblank.value); + } else { + nv_wr32(priv, 0x060010, chan->vblank.offset); + nv_wr32(priv, 0x060014, chan->vblank.value); + } + + return NVKM_EVENT_DROP; +} + +static int nv50_software_context_ctor(struct nouveau_object *parent, struct nouveau_object *engine, struct nouveau_oclass *oclass, void *data, u32 size, @@ -150,6 +169,7 @@ nv50_software_context_ctor(struct nouveau_object *parent, return ret; chan->base.vblank.channel = nv_gpuobj(parent->parent)->addr >> 12; + chan->base.vblank.event.func = nv50_software_vblsem_release; return 0; } @@ -170,8 +190,8 @@ nv50_software_cclass = { static int nv50_software_ctor(struct nouveau_object *parent, struct nouveau_object *engine, - struct nouveau_oclass *oclass, void *data, u32 size, - struct nouveau_object **pobject) + struct nouveau_oclass *oclass, void *data, u32 size, + struct nouveau_object **pobject) { struct nv50_software_priv *priv; int ret; diff --git a/drivers/gpu/drm/nouveau/core/engine/software/nvc0.c b/drivers/gpu/drm/nouveau/core/engine/software/nvc0.c index 282a1cd1bc2f..a523eaad47e3 100644 --- a/drivers/gpu/drm/nouveau/core/engine/software/nvc0.c +++ b/drivers/gpu/drm/nouveau/core/engine/software/nvc0.c @@ -25,6 +25,9 @@ #include <core/os.h> #include <core/class.h> #include <core/engctx.h> +#include <core/event.h> + +#include <subdev/bar.h> #include <engine/software.h> #include <engine/disp.h> @@ -72,18 +75,12 @@ nvc0_software_mthd_vblsem_release(struct nouveau_object *object, u32 mthd, { struct nvc0_software_chan *chan = (void *)nv_engctx(object->parent); struct nouveau_disp *disp = nouveau_disp(object); - unsigned long flags; u32 crtc = *(u32 *)args; if ((nv_device(object)->card_type < NV_E0 && crtc > 1) || crtc > 3) return -EINVAL; - disp->vblank.get(disp->vblank.data, crtc); - - spin_lock_irqsave(&disp->vblank.lock, flags); - list_add(&chan->base.vblank.head, &disp->vblank.list); - chan->base.vblank.crtc = crtc; - spin_unlock_irqrestore(&disp->vblank.lock, flags); + nouveau_event_get(disp->vblank, crtc, &chan->base.vblank.event); return 0; } @@ -118,6 +115,23 @@ nvc0_software_sclass[] = { ******************************************************************************/ static int +nvc0_software_vblsem_release(struct nouveau_eventh *event, int head) +{ + struct nouveau_software_chan *chan = + container_of(event, struct nouveau_software_chan, vblank.event); + struct nvc0_software_priv *priv = (void *)nv_object(chan)->engine; + struct nouveau_bar *bar = nouveau_bar(priv); + + nv_wr32(priv, 0x001718, 0x80000000 | chan->vblank.channel); + bar->flush(bar); + nv_wr32(priv, 0x06000c, upper_32_bits(chan->vblank.offset)); + nv_wr32(priv, 0x060010, lower_32_bits(chan->vblank.offset)); + nv_wr32(priv, 0x060014, chan->vblank.value); + + return NVKM_EVENT_DROP; +} + +static int nvc0_software_context_ctor(struct nouveau_object *parent, struct nouveau_object *engine, struct nouveau_oclass *oclass, void *data, u32 size, @@ -132,6 +146,7 @@ nvc0_software_context_ctor(struct nouveau_object *parent, return ret; chan->base.vblank.channel = nv_gpuobj(parent->parent)->addr >> 12; + chan->base.vblank.event.func = nvc0_software_vblsem_release; return 0; } diff --git a/drivers/gpu/drm/nouveau/core/include/core/class.h b/drivers/gpu/drm/nouveau/core/include/core/class.h index 47c4b3a5bd3a..92d3ab11d962 100644 --- a/drivers/gpu/drm/nouveau/core/include/core/class.h +++ b/drivers/gpu/drm/nouveau/core/include/core/class.h @@ -154,6 +154,14 @@ struct nve0_channel_ind_class { u32 engine; }; +/* 0046: NV04_DISP + */ + +#define NV04_DISP_CLASS 0x00000046 + +struct nv04_display_class { +}; + /* 5070: NV50_DISP * 8270: NV84_DISP * 8370: NVA0_DISP @@ -190,25 +198,6 @@ struct nve0_channel_ind_class { #define NV84_DISP_SOR_HDMI_PWR_REKEY 0x0000007f #define NV50_DISP_SOR_LVDS_SCRIPT 0x00013000 #define NV50_DISP_SOR_LVDS_SCRIPT_ID 0x0000ffff -#define NV94_DISP_SOR_DP_TRAIN 0x00016000 -#define NV94_DISP_SOR_DP_TRAIN_OP 0xf0000000 -#define NV94_DISP_SOR_DP_TRAIN_OP_PATTERN 0x00000000 -#define NV94_DISP_SOR_DP_TRAIN_OP_INIT 0x10000000 -#define NV94_DISP_SOR_DP_TRAIN_OP_FINI 0x20000000 -#define NV94_DISP_SOR_DP_TRAIN_INIT_SPREAD 0x00000001 -#define NV94_DISP_SOR_DP_TRAIN_INIT_SPREAD_OFF 0x00000000 -#define NV94_DISP_SOR_DP_TRAIN_INIT_SPREAD_ON 0x00000001 -#define NV94_DISP_SOR_DP_TRAIN_PATTERN 0x00000003 -#define NV94_DISP_SOR_DP_TRAIN_PATTERN_DISABLED 0x00000000 -#define NV94_DISP_SOR_DP_LNKCTL 0x00016040 -#define NV94_DISP_SOR_DP_LNKCTL_FRAME 0x80000000 -#define NV94_DISP_SOR_DP_LNKCTL_FRAME_STD 0x00000000 -#define NV94_DISP_SOR_DP_LNKCTL_FRAME_ENH 0x80000000 -#define NV94_DISP_SOR_DP_LNKCTL_WIDTH 0x00001f00 -#define NV94_DISP_SOR_DP_LNKCTL_COUNT 0x00000007 -#define NV94_DISP_SOR_DP_DRVCTL(l) ((l) * 0x40 + 0x00016100) -#define NV94_DISP_SOR_DP_DRVCTL_VS 0x00000300 -#define NV94_DISP_SOR_DP_DRVCTL_PE 0x00000003 #define NV50_DISP_DAC_MTHD 0x00020000 #define NV50_DISP_DAC_MTHD_TYPE 0x0000f000 @@ -230,6 +219,23 @@ struct nve0_channel_ind_class { #define NV50_DISP_DAC_LOAD 0x0002000c #define NV50_DISP_DAC_LOAD_VALUE 0x00000007 +#define NV50_DISP_PIOR_MTHD 0x00030000 +#define NV50_DISP_PIOR_MTHD_TYPE 0x0000f000 +#define NV50_DISP_PIOR_MTHD_OR 0x00000003 + +#define NV50_DISP_PIOR_PWR 0x00030000 +#define NV50_DISP_PIOR_PWR_STATE 0x00000001 +#define NV50_DISP_PIOR_PWR_STATE_ON 0x00000001 +#define NV50_DISP_PIOR_PWR_STATE_OFF 0x00000000 +#define NV50_DISP_PIOR_TMDS_PWR 0x00032000 +#define NV50_DISP_PIOR_TMDS_PWR_STATE 0x00000001 +#define NV50_DISP_PIOR_TMDS_PWR_STATE_ON 0x00000001 +#define NV50_DISP_PIOR_TMDS_PWR_STATE_OFF 0x00000000 +#define NV50_DISP_PIOR_DP_PWR 0x00036000 +#define NV50_DISP_PIOR_DP_PWR_STATE 0x00000001 +#define NV50_DISP_PIOR_DP_PWR_STATE_ON 0x00000001 +#define NV50_DISP_PIOR_DP_PWR_STATE_OFF 0x00000000 + struct nv50_display_class { }; diff --git a/drivers/gpu/drm/nouveau/core/include/core/client.h b/drivers/gpu/drm/nouveau/core/include/core/client.h index 63acc0346ff2..c66eac513803 100644 --- a/drivers/gpu/drm/nouveau/core/include/core/client.h +++ b/drivers/gpu/drm/nouveau/core/include/core/client.h @@ -7,7 +7,7 @@ struct nouveau_client { struct nouveau_namedb base; struct nouveau_handle *root; struct nouveau_object *device; - char name[16]; + char name[32]; u32 debug; struct nouveau_vm *vm; }; @@ -41,5 +41,6 @@ int nouveau_client_create_(const char *name, u64 device, const char *cfg, int nouveau_client_init(struct nouveau_client *); int nouveau_client_fini(struct nouveau_client *, bool suspend); +const char *nouveau_client_name(void *obj); #endif diff --git a/drivers/gpu/drm/nouveau/core/include/core/device.h b/drivers/gpu/drm/nouveau/core/include/core/device.h index e58b6f0984c1..d351a4e5819c 100644 --- a/drivers/gpu/drm/nouveau/core/include/core/device.h +++ b/drivers/gpu/drm/nouveau/core/include/core/device.h @@ -26,6 +26,7 @@ enum nv_subdev_type { */ NVDEV_SUBDEV_MXM, NVDEV_SUBDEV_MC, + NVDEV_SUBDEV_BUS, NVDEV_SUBDEV_TIMER, NVDEV_SUBDEV_FB, NVDEV_SUBDEV_LTCG, diff --git a/drivers/gpu/drm/nouveau/core/include/core/enum.h b/drivers/gpu/drm/nouveau/core/include/core/enum.h index e7b1e181943b..4fc62bb8c1f0 100644 --- a/drivers/gpu/drm/nouveau/core/include/core/enum.h +++ b/drivers/gpu/drm/nouveau/core/include/core/enum.h @@ -5,12 +5,13 @@ struct nouveau_enum { u32 value; const char *name; const void *data; + u32 data2; }; const struct nouveau_enum * nouveau_enum_find(const struct nouveau_enum *, u32 value); -void +const struct nouveau_enum * nouveau_enum_print(const struct nouveau_enum *en, u32 value); struct nouveau_bitfield { diff --git a/drivers/gpu/drm/nouveau/core/include/core/event.h b/drivers/gpu/drm/nouveau/core/include/core/event.h new file mode 100644 index 000000000000..9e094408f14e --- /dev/null +++ b/drivers/gpu/drm/nouveau/core/include/core/event.h @@ -0,0 +1,36 @@ +#ifndef __NVKM_EVENT_H__ +#define __NVKM_EVENT_H__ + +/* return codes from event handlers */ +#define NVKM_EVENT_DROP 0 +#define NVKM_EVENT_KEEP 1 + +struct nouveau_eventh { + struct list_head head; + int (*func)(struct nouveau_eventh *, int index); +}; + +struct nouveau_event { + spinlock_t lock; + + void *priv; + void (*enable)(struct nouveau_event *, int index); + void (*disable)(struct nouveau_event *, int index); + + int index_nr; + struct { + struct list_head list; + int refs; + } index[]; +}; + +int nouveau_event_create(int index_nr, struct nouveau_event **); +void nouveau_event_destroy(struct nouveau_event **); +void nouveau_event_trigger(struct nouveau_event *, int index); + +void nouveau_event_get(struct nouveau_event *, int index, + struct nouveau_eventh *); +void nouveau_event_put(struct nouveau_event *, int index, + struct nouveau_eventh *); + +#endif diff --git a/drivers/gpu/drm/nouveau/core/include/core/object.h b/drivers/gpu/drm/nouveau/core/include/core/object.h index 5982935ee23a..6a902672f6f4 100644 --- a/drivers/gpu/drm/nouveau/core/include/core/object.h +++ b/drivers/gpu/drm/nouveau/core/include/core/object.h @@ -133,7 +133,7 @@ static inline u8 nv_ro08(void *obj, u64 addr) { u8 data = nv_ofuncs(obj)->rd08(obj, addr); - nv_spam(obj, "nv_ro08 0x%08x 0x%02x\n", addr, data); + nv_spam(obj, "nv_ro08 0x%08llx 0x%02x\n", addr, data); return data; } @@ -141,7 +141,7 @@ static inline u16 nv_ro16(void *obj, u64 addr) { u16 data = nv_ofuncs(obj)->rd16(obj, addr); - nv_spam(obj, "nv_ro16 0x%08x 0x%04x\n", addr, data); + nv_spam(obj, "nv_ro16 0x%08llx 0x%04x\n", addr, data); return data; } @@ -149,28 +149,28 @@ static inline u32 nv_ro32(void *obj, u64 addr) { u32 data = nv_ofuncs(obj)->rd32(obj, addr); - nv_spam(obj, "nv_ro32 0x%08x 0x%08x\n", addr, data); + nv_spam(obj, "nv_ro32 0x%08llx 0x%08x\n", addr, data); return data; } static inline void nv_wo08(void *obj, u64 addr, u8 data) { - nv_spam(obj, "nv_wo08 0x%08x 0x%02x\n", addr, data); + nv_spam(obj, "nv_wo08 0x%08llx 0x%02x\n", addr, data); nv_ofuncs(obj)->wr08(obj, addr, data); } static inline void nv_wo16(void *obj, u64 addr, u16 data) { - nv_spam(obj, "nv_wo16 0x%08x 0x%04x\n", addr, data); + nv_spam(obj, "nv_wo16 0x%08llx 0x%04x\n", addr, data); nv_ofuncs(obj)->wr16(obj, addr, data); } static inline void nv_wo32(void *obj, u64 addr, u32 data) { - nv_spam(obj, "nv_wo32 0x%08x 0x%08x\n", addr, data); + nv_spam(obj, "nv_wo32 0x%08llx 0x%08x\n", addr, data); nv_ofuncs(obj)->wr32(obj, addr, data); } diff --git a/drivers/gpu/drm/nouveau/core/include/core/printk.h b/drivers/gpu/drm/nouveau/core/include/core/printk.h index 1d629664f32d..febed2ea5c80 100644 --- a/drivers/gpu/drm/nouveau/core/include/core/printk.h +++ b/drivers/gpu/drm/nouveau/core/include/core/printk.h @@ -15,7 +15,8 @@ struct nouveau_object; #define NV_PRINTK_TRACE KERN_DEBUG #define NV_PRINTK_SPAM KERN_DEBUG -void nv_printk_(struct nouveau_object *, const char *, int, const char *, ...); +void __printf(4, 5) +nv_printk_(struct nouveau_object *, const char *, int, const char *, ...); #define nv_printk(o,l,f,a...) do { \ if (NV_DBG_##l <= CONFIG_NOUVEAU_DEBUG) \ diff --git a/drivers/gpu/drm/nouveau/core/include/engine/disp.h b/drivers/gpu/drm/nouveau/core/include/engine/disp.h index 46948285f3e7..28da6772c095 100644 --- a/drivers/gpu/drm/nouveau/core/include/engine/disp.h +++ b/drivers/gpu/drm/nouveau/core/include/engine/disp.h @@ -4,18 +4,11 @@ #include <core/object.h> #include <core/engine.h> #include <core/device.h> +#include <core/event.h> struct nouveau_disp { struct nouveau_engine base; - - struct { - struct list_head list; - spinlock_t lock; - void (*notify)(void *, int); - void (*get)(void *, int); - void (*put)(void *, int); - void *data; - } vblank; + struct nouveau_event *vblank; }; static inline struct nouveau_disp * @@ -24,16 +17,22 @@ nouveau_disp(void *obj) return (void *)nv_device(obj)->subdev[NVDEV_ENGINE_DISP]; } -#define nouveau_disp_create(p,e,c,i,x,d) \ - nouveau_engine_create((p), (e), (c), true, (i), (x), (d)) -#define nouveau_disp_destroy(d) \ - nouveau_engine_destroy(&(d)->base) +#define nouveau_disp_create(p,e,c,h,i,x,d) \ + nouveau_disp_create_((p), (e), (c), (h), (i), (x), \ + sizeof(**d), (void **)d) +#define nouveau_disp_destroy(d) ({ \ + struct nouveau_disp *disp = (d); \ + _nouveau_disp_dtor(nv_object(disp)); \ +}) #define nouveau_disp_init(d) \ nouveau_engine_init(&(d)->base) #define nouveau_disp_fini(d,s) \ nouveau_engine_fini(&(d)->base, (s)) -#define _nouveau_disp_dtor _nouveau_engine_dtor +int nouveau_disp_create_(struct nouveau_object *, struct nouveau_object *, + struct nouveau_oclass *, int heads, + const char *, const char *, int, void **); +void _nouveau_disp_dtor(struct nouveau_object *); #define _nouveau_disp_init _nouveau_engine_init #define _nouveau_disp_fini _nouveau_engine_fini diff --git a/drivers/gpu/drm/nouveau/core/include/engine/fifo.h b/drivers/gpu/drm/nouveau/core/include/engine/fifo.h index f18846c8c6fe..b46c197709f3 100644 --- a/drivers/gpu/drm/nouveau/core/include/engine/fifo.h +++ b/drivers/gpu/drm/nouveau/core/include/engine/fifo.h @@ -65,6 +65,8 @@ struct nouveau_fifo_base { struct nouveau_fifo { struct nouveau_engine base; + struct nouveau_event *uevent; + struct nouveau_object **channel; spinlock_t lock; u16 min; @@ -92,6 +94,8 @@ int nouveau_fifo_create_(struct nouveau_object *, struct nouveau_object *, struct nouveau_oclass *, int min, int max, int size, void **); void nouveau_fifo_destroy(struct nouveau_fifo *); +const char * +nouveau_client_name_for_fifo_chid(struct nouveau_fifo *fifo, u32 chid); #define _nouveau_fifo_init _nouveau_engine_init #define _nouveau_fifo_fini _nouveau_engine_fini diff --git a/drivers/gpu/drm/nouveau/core/include/engine/software.h b/drivers/gpu/drm/nouveau/core/include/engine/software.h index c945691c8564..45799487e573 100644 --- a/drivers/gpu/drm/nouveau/core/include/engine/software.h +++ b/drivers/gpu/drm/nouveau/core/include/engine/software.h @@ -3,17 +3,17 @@ #include <core/engine.h> #include <core/engctx.h> +#include <core/event.h> struct nouveau_software_chan { struct nouveau_engctx base; struct { - struct list_head head; + struct nouveau_eventh event; u32 channel; u32 ctxdma; u64 offset; u32 value; - u32 crtc; } vblank; int (*flip)(void *); diff --git a/drivers/gpu/drm/nouveau/core/include/subdev/bios/dcb.h b/drivers/gpu/drm/nouveau/core/include/subdev/bios/dcb.h index b79025da581e..123270e9813a 100644 --- a/drivers/gpu/drm/nouveau/core/include/subdev/bios/dcb.h +++ b/drivers/gpu/drm/nouveau/core/include/subdev/bios/dcb.h @@ -16,6 +16,8 @@ enum dcb_output_type { struct dcb_output { int index; /* may not be raw dcb index if merging has happened */ + u16 hasht; + u16 hashm; enum dcb_output_type type; uint8_t i2c_index; uint8_t heads; @@ -25,6 +27,7 @@ struct dcb_output { uint8_t or; uint8_t link; bool duallink_possible; + uint8_t extdev; union { struct sor_conf { int link; diff --git a/drivers/gpu/drm/nouveau/core/include/subdev/bios/gpio.h b/drivers/gpu/drm/nouveau/core/include/subdev/bios/gpio.h index e6563b5cb08e..96d3364f6db3 100644 --- a/drivers/gpu/drm/nouveau/core/include/subdev/bios/gpio.h +++ b/drivers/gpu/drm/nouveau/core/include/subdev/bios/gpio.h @@ -1,17 +1,22 @@ #ifndef __NVBIOS_GPIO_H__ #define __NVBIOS_GPIO_H__ -struct nouveau_bios; - enum dcb_gpio_func_name { DCB_GPIO_PANEL_POWER = 0x01, DCB_GPIO_TVDAC0 = 0x0c, DCB_GPIO_TVDAC1 = 0x2d, - DCB_GPIO_PWM_FAN = 0x09, + DCB_GPIO_FAN = 0x09, DCB_GPIO_FAN_SENSE = 0x3d, DCB_GPIO_UNUSED = 0xff }; +#define DCB_GPIO_LOG_DIR 0x02 +#define DCB_GPIO_LOG_DIR_OUT 0x00 +#define DCB_GPIO_LOG_DIR_IN 0x02 +#define DCB_GPIO_LOG_VAL 0x01 +#define DCB_GPIO_LOG_VAL_LO 0x00 +#define DCB_GPIO_LOG_VAL_HI 0x01 + struct dcb_gpio_func { u8 func; u8 line; diff --git a/drivers/gpu/drm/nouveau/core/include/subdev/bios/i2c.h b/drivers/gpu/drm/nouveau/core/include/subdev/bios/i2c.h index 5079bedfd985..10b57a19a7de 100644 --- a/drivers/gpu/drm/nouveau/core/include/subdev/bios/i2c.h +++ b/drivers/gpu/drm/nouveau/core/include/subdev/bios/i2c.h @@ -15,7 +15,7 @@ struct dcb_i2c_entry { enum dcb_i2c_type type; u8 drive; u8 sense; - u32 data; + u8 share; }; u16 dcb_i2c_table(struct nouveau_bios *, u8 *ver, u8 *hdr, u8 *cnt, u8 *len); diff --git a/drivers/gpu/drm/nouveau/core/include/subdev/bios/therm.h b/drivers/gpu/drm/nouveau/core/include/subdev/bios/therm.h index a2c4296fc5f6..083541dbe9c8 100644 --- a/drivers/gpu/drm/nouveau/core/include/subdev/bios/therm.h +++ b/drivers/gpu/drm/nouveau/core/include/subdev/bios/therm.h @@ -23,11 +23,27 @@ struct nvbios_therm_sensor { struct nvbios_therm_threshold thrs_shutdown; }; +/* no vbios have more than 6 */ +#define NOUVEAU_TEMP_FAN_TRIP_MAX 10 +struct nouveau_therm_trip_point { + int fan_duty; + int temp; + int hysteresis; +}; + struct nvbios_therm_fan { u16 pwm_freq; u8 min_duty; u8 max_duty; + + u16 bump_period; + u16 slow_down_period; + + struct nouveau_therm_trip_point trip[NOUVEAU_TEMP_FAN_TRIP_MAX]; + u8 nr_fan_trip; + u8 linear_min_temp; + u8 linear_max_temp; }; enum nvbios_therm_domain { diff --git a/drivers/gpu/drm/nouveau/core/include/subdev/bios/xpio.h b/drivers/gpu/drm/nouveau/core/include/subdev/bios/xpio.h new file mode 100644 index 000000000000..360baab52e4c --- /dev/null +++ b/drivers/gpu/drm/nouveau/core/include/subdev/bios/xpio.h @@ -0,0 +1,19 @@ +#ifndef __NVBIOS_XPIO_H__ +#define __NVBIOS_XPIO_H__ + +#define NVBIOS_XPIO_FLAG_AUX 0x10 +#define NVBIOS_XPIO_FLAG_AUX0 0x00 +#define NVBIOS_XPIO_FLAG_AUX1 0x10 + +struct nvbios_xpio { + u8 type; + u8 addr; + u8 flags; +}; + +u16 dcb_xpio_table(struct nouveau_bios *, u8 idx, + u8 *ver, u8 *hdr, u8 *cnt, u8 *len); +u16 dcb_xpio_parse(struct nouveau_bios *, u8 idx, + u8 *ver, u8 *hdr, u8 *cnt, u8 *len, struct nvbios_xpio *); + +#endif diff --git a/drivers/gpu/drm/nouveau/core/include/subdev/bus.h b/drivers/gpu/drm/nouveau/core/include/subdev/bus.h new file mode 100644 index 000000000000..7d88ec4a6d06 --- /dev/null +++ b/drivers/gpu/drm/nouveau/core/include/subdev/bus.h @@ -0,0 +1,41 @@ +#ifndef __NOUVEAU_BUS_H__ +#define __NOUVEAU_BUS_H__ + +#include <core/subdev.h> +#include <core/device.h> + +struct nouveau_bus_intr { + u32 stat; + u32 unit; +}; + +struct nouveau_bus { + struct nouveau_subdev base; +}; + +static inline struct nouveau_bus * +nouveau_bus(void *obj) +{ + return (void *)nv_device(obj)->subdev[NVDEV_SUBDEV_BUS]; +} + +#define nouveau_bus_create(p, e, o, d) \ + nouveau_subdev_create_((p), (e), (o), 0, "PBUS", "master", \ + sizeof(**d), (void **)d) +#define nouveau_bus_destroy(p) \ + nouveau_subdev_destroy(&(p)->base) +#define nouveau_bus_init(p) \ + nouveau_subdev_init(&(p)->base) +#define nouveau_bus_fini(p, s) \ + nouveau_subdev_fini(&(p)->base, (s)) + +#define _nouveau_bus_dtor _nouveau_subdev_dtor +#define _nouveau_bus_init _nouveau_subdev_init +#define _nouveau_bus_fini _nouveau_subdev_fini + +extern struct nouveau_oclass nv04_bus_oclass; +extern struct nouveau_oclass nv31_bus_oclass; +extern struct nouveau_oclass nv50_bus_oclass; +extern struct nouveau_oclass nvc0_bus_oclass; + +#endif diff --git a/drivers/gpu/drm/nouveau/core/include/subdev/gpio.h b/drivers/gpu/drm/nouveau/core/include/subdev/gpio.h index b75e8f18e52c..c85b9f1579ad 100644 --- a/drivers/gpu/drm/nouveau/core/include/subdev/gpio.h +++ b/drivers/gpu/drm/nouveau/core/include/subdev/gpio.h @@ -3,6 +3,7 @@ #include <core/subdev.h> #include <core/device.h> +#include <core/event.h> #include <subdev/bios.h> #include <subdev/bios/gpio.h> @@ -10,28 +11,18 @@ struct nouveau_gpio { struct nouveau_subdev base; + struct nouveau_event *events; + /* hardware interfaces */ void (*reset)(struct nouveau_gpio *, u8 func); int (*drive)(struct nouveau_gpio *, int line, int dir, int out); int (*sense)(struct nouveau_gpio *, int line); - void (*irq_enable)(struct nouveau_gpio *, int line, bool); /* software interfaces */ int (*find)(struct nouveau_gpio *, int idx, u8 tag, u8 line, struct dcb_gpio_func *); int (*set)(struct nouveau_gpio *, int idx, u8 tag, u8 line, int state); int (*get)(struct nouveau_gpio *, int idx, u8 tag, u8 line); - int (*irq)(struct nouveau_gpio *, int idx, u8 tag, u8 line, bool on); - - /* interrupt handling */ - struct list_head isr; - spinlock_t lock; - - void (*isr_run)(struct nouveau_gpio *, int idx, u32 mask); - int (*isr_add)(struct nouveau_gpio *, int idx, u8 tag, u8 line, - void (*)(void *, int state), void *data); - void (*isr_del)(struct nouveau_gpio *, int idx, u8 tag, u8 line, - void (*)(void *, int state), void *data); }; static inline struct nouveau_gpio * @@ -40,25 +31,23 @@ nouveau_gpio(void *obj) return (void *)nv_device(obj)->subdev[NVDEV_SUBDEV_GPIO]; } -#define nouveau_gpio_create(p,e,o,d) \ - nouveau_gpio_create_((p), (e), (o), sizeof(**d), (void **)d) -#define nouveau_gpio_destroy(p) \ - nouveau_subdev_destroy(&(p)->base) +#define nouveau_gpio_create(p,e,o,l,d) \ + nouveau_gpio_create_((p), (e), (o), (l), sizeof(**d), (void **)d) +#define nouveau_gpio_destroy(p) ({ \ + struct nouveau_gpio *gpio = (p); \ + _nouveau_gpio_dtor(nv_object(gpio)); \ +}) #define nouveau_gpio_fini(p,s) \ nouveau_subdev_fini(&(p)->base, (s)) -int nouveau_gpio_create_(struct nouveau_object *, struct nouveau_object *, - struct nouveau_oclass *, int, void **); -int nouveau_gpio_init(struct nouveau_gpio *); +int nouveau_gpio_create_(struct nouveau_object *, struct nouveau_object *, + struct nouveau_oclass *, int, int, void **); +void _nouveau_gpio_dtor(struct nouveau_object *); +int nouveau_gpio_init(struct nouveau_gpio *); extern struct nouveau_oclass nv10_gpio_oclass; extern struct nouveau_oclass nv50_gpio_oclass; extern struct nouveau_oclass nvd0_gpio_oclass; - -void nv50_gpio_dtor(struct nouveau_object *); -int nv50_gpio_init(struct nouveau_object *); -int nv50_gpio_fini(struct nouveau_object *, bool); -void nv50_gpio_intr(struct nouveau_subdev *); -void nv50_gpio_irq_enable(struct nouveau_gpio *, int line, bool); +extern struct nouveau_oclass nve0_gpio_oclass; #endif diff --git a/drivers/gpu/drm/nouveau/core/include/subdev/i2c.h b/drivers/gpu/drm/nouveau/core/include/subdev/i2c.h index b93ab01e3785..888384c0bed8 100644 --- a/drivers/gpu/drm/nouveau/core/include/subdev/i2c.h +++ b/drivers/gpu/drm/nouveau/core/include/subdev/i2c.h @@ -10,23 +10,59 @@ #define NV_I2C_PORT(n) (0x00 + (n)) #define NV_I2C_DEFAULT(n) (0x80 + (n)) +#define NV_I2C_TYPE_DCBI2C(n) (0x0000 | (n)) +#define NV_I2C_TYPE_EXTDDC(e) (0x0005 | (e) << 8) +#define NV_I2C_TYPE_EXTAUX(e) (0x0006 | (e) << 8) + struct nouveau_i2c_port { + struct nouveau_object base; struct i2c_adapter adapter; - struct nouveau_i2c *i2c; - struct i2c_algo_bit_data bit; + struct list_head head; u8 index; - u8 type; - u32 dcb; - u32 drive; - u32 sense; - u32 state; + + const struct nouveau_i2c_func *func; +}; + +struct nouveau_i2c_func { + void (*acquire)(struct nouveau_i2c_port *); + void (*release)(struct nouveau_i2c_port *); + + void (*drive_scl)(struct nouveau_i2c_port *, int); + void (*drive_sda)(struct nouveau_i2c_port *, int); + int (*sense_scl)(struct nouveau_i2c_port *); + int (*sense_sda)(struct nouveau_i2c_port *); + + int (*aux)(struct nouveau_i2c_port *, u8, u32, u8 *, u8); + int (*pattern)(struct nouveau_i2c_port *, int pattern); + int (*lnk_ctl)(struct nouveau_i2c_port *, int nr, int bw, bool enh); + int (*drv_ctl)(struct nouveau_i2c_port *, int lane, int sw, int pe); }; +#define nouveau_i2c_port_create(p,e,o,i,a,d) \ + nouveau_i2c_port_create_((p), (e), (o), (i), (a), \ + sizeof(**d), (void **)d) +#define nouveau_i2c_port_destroy(p) ({ \ + struct nouveau_i2c_port *port = (p); \ + _nouveau_i2c_port_dtor(nv_object(i2c)); \ +}) +#define nouveau_i2c_port_init(p) \ + nouveau_object_init(&(p)->base) +#define nouveau_i2c_port_fini(p,s) \ + nouveau_object_fini(&(p)->base, (s)) + +int nouveau_i2c_port_create_(struct nouveau_object *, struct nouveau_object *, + struct nouveau_oclass *, u8, + const struct i2c_algorithm *, int, void **); +void _nouveau_i2c_port_dtor(struct nouveau_object *); +#define _nouveau_i2c_port_init nouveau_object_init +#define _nouveau_i2c_port_fini nouveau_object_fini + struct nouveau_i2c { struct nouveau_subdev base; struct nouveau_i2c_port *(*find)(struct nouveau_i2c *, u8 index); + struct nouveau_i2c_port *(*find_type)(struct nouveau_i2c *, u16 type); int (*identify)(struct nouveau_i2c *, int index, const char *what, struct i2c_board_info *, bool (*match)(struct nouveau_i2c_port *, @@ -40,21 +76,76 @@ nouveau_i2c(void *obj) return (void *)nv_device(obj)->subdev[NVDEV_SUBDEV_I2C]; } -extern struct nouveau_oclass nouveau_i2c_oclass; +#define nouveau_i2c_create(p,e,o,s,d) \ + nouveau_i2c_create_((p), (e), (o), (s), sizeof(**d), (void **)d) +#define nouveau_i2c_destroy(p) ({ \ + struct nouveau_i2c *i2c = (p); \ + _nouveau_i2c_dtor(nv_object(i2c)); \ +}) +#define nouveau_i2c_init(p) ({ \ + struct nouveau_i2c *i2c = (p); \ + _nouveau_i2c_init(nv_object(i2c)); \ +}) +#define nouveau_i2c_fini(p,s) ({ \ + struct nouveau_i2c *i2c = (p); \ + _nouveau_i2c_fini(nv_object(i2c), (s)); \ +}) -void nouveau_i2c_drive_scl(void *, int); -void nouveau_i2c_drive_sda(void *, int); -int nouveau_i2c_sense_scl(void *); -int nouveau_i2c_sense_sda(void *); +int nouveau_i2c_create_(struct nouveau_object *, struct nouveau_object *, + struct nouveau_oclass *, struct nouveau_oclass *, + int, void **); +void _nouveau_i2c_dtor(struct nouveau_object *); +int _nouveau_i2c_init(struct nouveau_object *); +int _nouveau_i2c_fini(struct nouveau_object *, bool); -int nv_rdi2cr(struct nouveau_i2c_port *, u8 addr, u8 reg); -int nv_wri2cr(struct nouveau_i2c_port *, u8 addr, u8 reg, u8 val); -bool nv_probe_i2c(struct nouveau_i2c_port *, u8 addr); - -int nv_rdaux(struct nouveau_i2c_port *, u32 addr, u8 *data, u8 size); -int nv_wraux(struct nouveau_i2c_port *, u32 addr, u8 *data, u8 size); +extern struct nouveau_oclass nv04_i2c_oclass; +extern struct nouveau_oclass nv4e_i2c_oclass; +extern struct nouveau_oclass nv50_i2c_oclass; +extern struct nouveau_oclass nv94_i2c_oclass; +extern struct nouveau_oclass nvd0_i2c_oclass; +extern struct nouveau_oclass nouveau_anx9805_sclass[]; extern const struct i2c_algorithm nouveau_i2c_bit_algo; extern const struct i2c_algorithm nouveau_i2c_aux_algo; +static inline int +nv_rdi2cr(struct nouveau_i2c_port *port, u8 addr, u8 reg) +{ + u8 val; + struct i2c_msg msgs[] = { + { .addr = addr, .flags = 0, .len = 1, .buf = ® }, + { .addr = addr, .flags = I2C_M_RD, .len = 1, .buf = &val }, + }; + + int ret = i2c_transfer(&port->adapter, msgs, 2); + if (ret != 2) + return -EIO; + + return val; +} + +static inline int +nv_wri2cr(struct nouveau_i2c_port *port, u8 addr, u8 reg, u8 val) +{ + u8 buf[2] = { reg, val }; + struct i2c_msg msgs[] = { + { .addr = addr, .flags = 0, .len = 2, .buf = buf }, + }; + + int ret = i2c_transfer(&port->adapter, msgs, 1); + if (ret != 1) + return -EIO; + + return 0; +} + +static inline bool +nv_probe_i2c(struct nouveau_i2c_port *port, u8 addr) +{ + return nv_rdi2cr(port, addr, 0) >= 0; +} + +int nv_rdaux(struct nouveau_i2c_port *, u32 addr, u8 *data, u8 size); +int nv_wraux(struct nouveau_i2c_port *, u32 addr, u8 *data, u8 size); + #endif diff --git a/drivers/gpu/drm/nouveau/core/include/subdev/therm.h b/drivers/gpu/drm/nouveau/core/include/subdev/therm.h index faee569fd458..6b17b614629f 100644 --- a/drivers/gpu/drm/nouveau/core/include/subdev/therm.h +++ b/drivers/gpu/drm/nouveau/core/include/subdev/therm.h @@ -4,10 +4,10 @@ #include <core/device.h> #include <core/subdev.h> -enum nouveau_therm_fan_mode { - FAN_CONTROL_NONE = 0, - FAN_CONTROL_MANUAL = 1, - FAN_CONTROL_NR, +enum nouveau_therm_mode { + NOUVEAU_THERM_CTRL_NONE = 0, + NOUVEAU_THERM_CTRL_MANUAL = 1, + NOUVEAU_THERM_CTRL_AUTO = 2, }; enum nouveau_therm_attr_type { @@ -28,6 +28,11 @@ enum nouveau_therm_attr_type { struct nouveau_therm { struct nouveau_subdev base; + int (*pwm_ctrl)(struct nouveau_therm *, int line, bool); + int (*pwm_get)(struct nouveau_therm *, int line, u32 *, u32 *); + int (*pwm_set)(struct nouveau_therm *, int line, u32, u32); + int (*pwm_clock)(struct nouveau_therm *); + int (*fan_get)(struct nouveau_therm *); int (*fan_set)(struct nouveau_therm *, int); int (*fan_sense)(struct nouveau_therm *); @@ -46,13 +51,29 @@ nouveau_therm(void *obj) } #define nouveau_therm_create(p,e,o,d) \ - nouveau_subdev_create((p), (e), (o), 0, "THERM", "therm", d) -#define nouveau_therm_destroy(p) \ - nouveau_subdev_destroy(&(p)->base) + nouveau_therm_create_((p), (e), (o), sizeof(**d), (void **)d) +#define nouveau_therm_destroy(p) ({ \ + struct nouveau_therm *therm = (p); \ + _nouveau_therm_dtor(nv_object(therm)); \ +}) +#define nouveau_therm_init(p) ({ \ + struct nouveau_therm *therm = (p); \ + _nouveau_therm_init(nv_object(therm)); \ +}) +#define nouveau_therm_fini(p,s) ({ \ + struct nouveau_therm *therm = (p); \ + _nouveau_therm_init(nv_object(therm), (s)); \ +}) -#define _nouveau_therm_dtor _nouveau_subdev_dtor +int nouveau_therm_create_(struct nouveau_object *, struct nouveau_object *, + struct nouveau_oclass *, int, void **); +void _nouveau_therm_dtor(struct nouveau_object *); +int _nouveau_therm_init(struct nouveau_object *); +int _nouveau_therm_fini(struct nouveau_object *, bool); extern struct nouveau_oclass nv40_therm_oclass; extern struct nouveau_oclass nv50_therm_oclass; +extern struct nouveau_oclass nva3_therm_oclass; +extern struct nouveau_oclass nvd0_therm_oclass; #endif diff --git a/drivers/gpu/drm/nouveau/core/include/subdev/timer.h b/drivers/gpu/drm/nouveau/core/include/subdev/timer.h index c24ec8ab3db4..e465d158d352 100644 --- a/drivers/gpu/drm/nouveau/core/include/subdev/timer.h +++ b/drivers/gpu/drm/nouveau/core/include/subdev/timer.h @@ -10,6 +10,14 @@ struct nouveau_alarm { void (*func)(struct nouveau_alarm *); }; +static inline void +nouveau_alarm_init(struct nouveau_alarm *alarm, + void (*func)(struct nouveau_alarm *)) +{ + INIT_LIST_HEAD(&alarm->head); + alarm->func = func; +} + bool nouveau_timer_wait_eq(void *, u64 nsec, u32 addr, u32 mask, u32 data); bool nouveau_timer_wait_ne(void *, u64 nsec, u32 addr, u32 mask, u32 data); bool nouveau_timer_wait_cb(void *, u64 nsec, bool (*func)(void *), void *data); diff --git a/drivers/gpu/drm/nouveau/core/os.h b/drivers/gpu/drm/nouveau/core/os.h index cfe3b9cad156..eb496033b55c 100644 --- a/drivers/gpu/drm/nouveau/core/os.h +++ b/drivers/gpu/drm/nouveau/core/os.h @@ -16,6 +16,7 @@ #include <linux/vmalloc.h> #include <linux/acpi.h> #include <linux/dmi.h> +#include <linux/reboot.h> #include <asm/unaligned.h> diff --git a/drivers/gpu/drm/nouveau/core/subdev/bios/base.c b/drivers/gpu/drm/nouveau/core/subdev/bios/base.c index f621f69fa1a2..e816f06637a7 100644 --- a/drivers/gpu/drm/nouveau/core/subdev/bios/base.c +++ b/drivers/gpu/drm/nouveau/core/subdev/bios/base.c @@ -172,7 +172,7 @@ out: nv_wr32(bios, pcireg, access); } -#if defined(CONFIG_ACPI) +#if defined(CONFIG_ACPI) && defined(CONFIG_X86) int nouveau_acpi_get_bios_chunk(uint8_t *bios, int offset, int len); bool nouveau_acpi_rom_supported(struct pci_dev *pdev); #else diff --git a/drivers/gpu/drm/nouveau/core/subdev/bios/dcb.c b/drivers/gpu/drm/nouveau/core/subdev/bios/dcb.c index 0fd87df99dd6..2d9b9d7a7992 100644 --- a/drivers/gpu/drm/nouveau/core/subdev/bios/dcb.c +++ b/drivers/gpu/drm/nouveau/core/subdev/bios/dcb.c @@ -107,6 +107,18 @@ dcb_outp(struct nouveau_bios *bios, u8 idx, u8 *ver, u8 *len) return 0x0000; } +static inline u16 +dcb_outp_hasht(struct dcb_output *outp) +{ + return (outp->extdev << 8) | (outp->location << 4) | outp->type; +} + +static inline u16 +dcb_outp_hashm(struct dcb_output *outp) +{ + return (outp->heads << 8) | (outp->link << 6) | outp->or; +} + u16 dcb_outp_parse(struct nouveau_bios *bios, u8 idx, u8 *ver, u8 *len, struct dcb_output *outp) @@ -135,34 +147,28 @@ dcb_outp_parse(struct nouveau_bios *bios, u8 idx, u8 *ver, u8 *len, case DCB_OUTPUT_DP: outp->link = (conf & 0x00000030) >> 4; outp->sorconf.link = outp->link; /*XXX*/ + outp->extdev = 0x00; + if (outp->location != 0) + outp->extdev = (conf & 0x0000ff00) >> 8; break; default: break; } } + + outp->hasht = dcb_outp_hasht(outp); + outp->hashm = dcb_outp_hashm(outp); } return dcb; } -static inline u16 -dcb_outp_hasht(struct dcb_output *outp) -{ - return outp->type; -} - -static inline u16 -dcb_outp_hashm(struct dcb_output *outp) -{ - return (outp->heads << 8) | (outp->link << 6) | outp->or; -} - u16 dcb_outp_match(struct nouveau_bios *bios, u16 type, u16 mask, u8 *ver, u8 *len, struct dcb_output *outp) { u16 dcb, idx = 0; while ((dcb = dcb_outp_parse(bios, idx++, ver, len, outp))) { - if (dcb_outp_hasht(outp) == type) { + if ((dcb_outp_hasht(outp) & 0x00ff) == (type & 0x00ff)) { if ((dcb_outp_hashm(outp) & mask) == mask) break; } diff --git a/drivers/gpu/drm/nouveau/core/subdev/bios/extdev.c b/drivers/gpu/drm/nouveau/core/subdev/bios/extdev.c index 5afb568b2d69..b2a676e53580 100644 --- a/drivers/gpu/drm/nouveau/core/subdev/bios/extdev.c +++ b/drivers/gpu/drm/nouveau/core/subdev/bios/extdev.c @@ -48,7 +48,7 @@ extdev_table(struct nouveau_bios *bios, u8 *ver, u8 *hdr, u8 *len, u8 *cnt) return extdev + *hdr; } -u16 +static u16 nvbios_extdev_entry(struct nouveau_bios *bios, int idx, u8 *ver, u8 *len) { u8 hdr, cnt; diff --git a/drivers/gpu/drm/nouveau/core/subdev/bios/gpio.c b/drivers/gpu/drm/nouveau/core/subdev/bios/gpio.c index c84e93fa6d95..172a4f999990 100644 --- a/drivers/gpu/drm/nouveau/core/subdev/bios/gpio.c +++ b/drivers/gpu/drm/nouveau/core/subdev/bios/gpio.c @@ -25,6 +25,7 @@ #include <subdev/bios.h> #include <subdev/bios/dcb.h> #include <subdev/bios/gpio.h> +#include <subdev/bios/xpio.h> u16 dcb_gpio_table(struct nouveau_bios *bios, u8 *ver, u8 *hdr, u8 *cnt, u8 *len) @@ -60,8 +61,14 @@ dcb_gpio_table(struct nouveau_bios *bios, u8 *ver, u8 *hdr, u8 *cnt, u8 *len) u16 dcb_gpio_entry(struct nouveau_bios *bios, int idx, int ent, u8 *ver, u8 *len) { - u8 hdr, cnt; - u16 gpio = !idx ? dcb_gpio_table(bios, ver, &hdr, &cnt, len) : 0x0000; + u8 hdr, cnt, xver; /* use gpio version for xpio entry parsing */ + u16 gpio; + + if (!idx--) + gpio = dcb_gpio_table(bios, ver, &hdr, &cnt, len); + else + gpio = dcb_xpio_table(bios, idx, &xver, &hdr, &cnt, len); + if (gpio && ent < cnt) return gpio + hdr + (ent * *len); return 0x0000; diff --git a/drivers/gpu/drm/nouveau/core/subdev/bios/i2c.c b/drivers/gpu/drm/nouveau/core/subdev/bios/i2c.c index ad577db83766..cfb9288c6d28 100644 --- a/drivers/gpu/drm/nouveau/core/subdev/bios/i2c.c +++ b/drivers/gpu/drm/nouveau/core/subdev/bios/i2c.c @@ -70,12 +70,12 @@ dcb_i2c_parse(struct nouveau_bios *bios, u8 idx, struct dcb_i2c_entry *info) u8 ver, len; u16 ent = dcb_i2c_entry(bios, idx, &ver, &len); if (ent) { - info->data = nv_ro32(bios, ent + 0); - info->type = nv_ro08(bios, ent + 3); + info->type = nv_ro08(bios, ent + 3); + info->share = DCB_I2C_UNUSED; if (ver < 0x30) { info->type &= 0x07; if (info->type == 0x07) - info->type = 0xff; + info->type = DCB_I2C_UNUSED; } switch (info->type) { @@ -88,7 +88,11 @@ dcb_i2c_parse(struct nouveau_bios *bios, u8 idx, struct dcb_i2c_entry *info) return 0; case DCB_I2C_NVIO_BIT: case DCB_I2C_NVIO_AUX: - info->drive = nv_ro08(bios, ent + 0); + info->drive = nv_ro08(bios, ent + 0) & 0x0f; + if (nv_ro08(bios, ent + 1) & 0x01) { + info->share = nv_ro08(bios, ent + 1) >> 1; + info->share &= 0x0f; + } return 0; case DCB_I2C_UNUSED: return 0; @@ -121,7 +125,8 @@ dcb_i2c_parse(struct nouveau_bios *bios, u8 idx, struct dcb_i2c_entry *info) if (!info->sense) info->sense = 0x36; } - info->type = DCB_I2C_NV04_BIT; + info->type = DCB_I2C_NV04_BIT; + info->share = DCB_I2C_UNUSED; return 0; } diff --git a/drivers/gpu/drm/nouveau/core/subdev/bios/init.c b/drivers/gpu/drm/nouveau/core/subdev/bios/init.c index 690ed438b2ad..2cc1e6a5eb6a 100644 --- a/drivers/gpu/drm/nouveau/core/subdev/bios/init.c +++ b/drivers/gpu/drm/nouveau/core/subdev/bios/init.c @@ -231,6 +231,11 @@ init_i2c(struct nvbios_init *init, int index) return NULL; } + if (index == -2 && init->outp->location) { + index = NV_I2C_TYPE_EXTAUX(init->outp->extdev); + return i2c->find_type(i2c, index); + } + index = init->outp->i2c_index; } @@ -258,7 +263,7 @@ init_wri2cr(struct nvbios_init *init, u8 index, u8 addr, u8 reg, u8 val) static int init_rdauxr(struct nvbios_init *init, u32 addr) { - struct nouveau_i2c_port *port = init_i2c(init, -1); + struct nouveau_i2c_port *port = init_i2c(init, -2); u8 data; if (port && init_exec(init)) { @@ -274,7 +279,7 @@ init_rdauxr(struct nvbios_init *init, u32 addr) static int init_wrauxr(struct nvbios_init *init, u32 addr, u8 data) { - struct nouveau_i2c_port *port = init_i2c(init, -1); + struct nouveau_i2c_port *port = init_i2c(init, -2); if (port && init_exec(init)) return nv_wraux(port, addr, &data, 1); return -ENODEV; @@ -1816,7 +1821,7 @@ init_ram_restrict_zm_reg_group(struct nvbios_init *init) u8 i, j; trace("RAM_RESTRICT_ZM_REG_GROUP\t" - "R[%08x] 0x%02x 0x%02x\n", addr, incr, num); + "R[0x%08x] 0x%02x 0x%02x\n", addr, incr, num); init->offset += 7; for (i = 0; i < num; i++) { @@ -1849,7 +1854,7 @@ init_copy_zm_reg(struct nvbios_init *init) u32 sreg = nv_ro32(bios, init->offset + 1); u32 dreg = nv_ro32(bios, init->offset + 5); - trace("COPY_ZM_REG\tR[0x%06x] = R[0x%06x]\n", sreg, dreg); + trace("COPY_ZM_REG\tR[0x%06x] = R[0x%06x]\n", dreg, sreg); init->offset += 9; init_wr32(init, dreg, init_rd32(init, sreg)); @@ -1866,7 +1871,7 @@ init_zm_reg_group(struct nvbios_init *init) u32 addr = nv_ro32(bios, init->offset + 1); u8 count = nv_ro08(bios, init->offset + 5); - trace("ZM_REG_GROUP\tR[0x%06x] =\n"); + trace("ZM_REG_GROUP\tR[0x%06x] =\n", addr); init->offset += 6; while (count--) { diff --git a/drivers/gpu/drm/nouveau/core/subdev/bios/therm.c b/drivers/gpu/drm/nouveau/core/subdev/bios/therm.c index 862a08a2ae27..22a20573ed1b 100644 --- a/drivers/gpu/drm/nouveau/core/subdev/bios/therm.c +++ b/drivers/gpu/drm/nouveau/core/subdev/bios/therm.c @@ -55,7 +55,7 @@ therm_table(struct nouveau_bios *bios, u8 *ver, u8 *hdr, u8 *len, u8 *cnt) return therm + nv_ro08(bios, therm + 1); } -u16 +static u16 nvbios_therm_entry(struct nouveau_bios *bios, int idx, u8 *ver, u8 *len) { u8 hdr, cnt; @@ -155,10 +155,15 @@ int nvbios_therm_fan_parse(struct nouveau_bios *bios, struct nvbios_therm_fan *fan) { + struct nouveau_therm_trip_point *cur_trip = NULL; u8 ver, len, i; u16 entry; + uint8_t duty_lut[] = { 0, 0, 25, 0, 40, 0, 50, 0, + 75, 0, 85, 0, 100, 0, 100, 0 }; + i = 0; + fan->nr_fan_trip = 0; while ((entry = nvbios_therm_entry(bios, i++, &ver, &len))) { s16 value = nv_ro16(bios, entry + 1); @@ -167,9 +172,30 @@ nvbios_therm_fan_parse(struct nouveau_bios *bios, fan->min_duty = value & 0xff; fan->max_duty = (value & 0xff00) >> 8; break; + case 0x24: + fan->nr_fan_trip++; + cur_trip = &fan->trip[fan->nr_fan_trip - 1]; + cur_trip->hysteresis = value & 0xf; + cur_trip->temp = (value & 0xff0) >> 4; + cur_trip->fan_duty = duty_lut[(value & 0xf000) >> 12]; + break; + case 0x25: + cur_trip = &fan->trip[fan->nr_fan_trip - 1]; + cur_trip->fan_duty = value; + break; case 0x26: fan->pwm_freq = value; break; + case 0x3b: + fan->bump_period = value; + break; + case 0x3c: + fan->slow_down_period = value; + break; + case 0x46: + fan->linear_min_temp = nv_ro08(bios, entry + 1); + fan->linear_max_temp = nv_ro08(bios, entry + 2); + break; } } diff --git a/drivers/gpu/drm/nouveau/core/subdev/bios/xpio.c b/drivers/gpu/drm/nouveau/core/subdev/bios/xpio.c new file mode 100644 index 000000000000..e9b8e5d30a7a --- /dev/null +++ b/drivers/gpu/drm/nouveau/core/subdev/bios/xpio.c @@ -0,0 +1,76 @@ +/* + * Copyright 2012 Red Hat Inc. + * + * 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 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 + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: Ben Skeggs + */ + +#include <subdev/bios.h> +#include <subdev/bios/gpio.h> +#include <subdev/bios/xpio.h> + +static u16 +dcb_xpiod_table(struct nouveau_bios *bios, u8 *ver, u8 *hdr, u8 *cnt, u8 *len) +{ + u16 data = dcb_gpio_table(bios, ver, hdr, cnt, len); + if (data && *ver >= 0x40 && *hdr >= 0x06) { + u16 xpio = nv_ro16(bios, data + 0x04); + if (xpio) { + *ver = nv_ro08(bios, data + 0x00); + *hdr = nv_ro08(bios, data + 0x01); + *cnt = nv_ro08(bios, data + 0x02); + *len = nv_ro08(bios, data + 0x03); + return xpio; + } + } + return 0x0000; +} + +u16 +dcb_xpio_table(struct nouveau_bios *bios, u8 idx, + u8 *ver, u8 *hdr, u8 *cnt, u8 *len) +{ + u16 data = dcb_xpiod_table(bios, ver, hdr, cnt, len); + if (data && idx < *cnt) { + u16 xpio = nv_ro16(bios, data + *hdr + (idx * *len)); + if (xpio) { + *ver = nv_ro08(bios, data + 0x00); + *hdr = nv_ro08(bios, data + 0x01); + *cnt = nv_ro08(bios, data + 0x02); + *len = nv_ro08(bios, data + 0x03); + return xpio; + } + } + return 0x0000; +} + +u16 +dcb_xpio_parse(struct nouveau_bios *bios, u8 idx, + u8 *ver, u8 *hdr, u8 *cnt, u8 *len, + struct nvbios_xpio *info) +{ + u16 data = dcb_xpio_table(bios, idx, ver, hdr, cnt, len); + if (data && *len >= 6) { + info->type = nv_ro08(bios, data + 0x04); + info->addr = nv_ro08(bios, data + 0x05); + info->flags = nv_ro08(bios, data + 0x06); + } + return 0x0000; +} diff --git a/drivers/gpu/drm/nouveau/core/subdev/bus/nv04.c b/drivers/gpu/drm/nouveau/core/subdev/bus/nv04.c new file mode 100644 index 000000000000..8c7f8057a185 --- /dev/null +++ b/drivers/gpu/drm/nouveau/core/subdev/bus/nv04.c @@ -0,0 +1,95 @@ +/* + * Copyright 2012 Nouveau Community + * + * 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 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 + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: Martin Peres <martin.peres@labri.fr> + * Ben Skeggs + */ + +#include <subdev/bus.h> + +struct nv04_bus_priv { + struct nouveau_bus base; +}; + +static void +nv04_bus_intr(struct nouveau_subdev *subdev) +{ + struct nouveau_bus *pbus = nouveau_bus(subdev); + u32 stat = nv_rd32(pbus, 0x001100) & nv_rd32(pbus, 0x001140); + + if (stat & 0x00000001) { + nv_error(pbus, "BUS ERROR\n"); + stat &= ~0x00000001; + nv_wr32(pbus, 0x001100, 0x00000001); + } + + if (stat & 0x00000110) { + subdev = nouveau_subdev(subdev, NVDEV_SUBDEV_GPIO); + if (subdev && subdev->intr) + subdev->intr(subdev); + stat &= ~0x00000110; + nv_wr32(pbus, 0x001100, 0x00000110); + } + + if (stat) { + nv_error(pbus, "unknown intr 0x%08x\n", stat); + nv_mask(pbus, 0x001140, stat, 0x00000000); + } +} + +static int +nv04_bus_ctor(struct nouveau_object *parent, struct nouveau_object *engine, + struct nouveau_oclass *oclass, void *data, u32 size, + struct nouveau_object **pobject) +{ + struct nv04_bus_priv *priv; + int ret; + + ret = nouveau_bus_create(parent, engine, oclass, &priv); + *pobject = nv_object(priv); + if (ret) + return ret; + + nv_subdev(priv)->intr = nv04_bus_intr; + return 0; +} + +static int +nv04_bus_init(struct nouveau_object *object) +{ + struct nv04_bus_priv *priv = (void *)object; + + nv_wr32(priv, 0x001100, 0xffffffff); + nv_wr32(priv, 0x001140, 0x00000111); + + return nouveau_bus_init(&priv->base); +} + +struct nouveau_oclass +nv04_bus_oclass = { + .handle = NV_SUBDEV(BUS, 0x04), + .ofuncs = &(struct nouveau_ofuncs) { + .ctor = nv04_bus_ctor, + .dtor = _nouveau_bus_dtor, + .init = nv04_bus_init, + .fini = _nouveau_bus_fini, + }, +}; diff --git a/drivers/gpu/drm/nouveau/core/subdev/bus/nv31.c b/drivers/gpu/drm/nouveau/core/subdev/bus/nv31.c new file mode 100644 index 000000000000..34132aef34e1 --- /dev/null +++ b/drivers/gpu/drm/nouveau/core/subdev/bus/nv31.c @@ -0,0 +1,112 @@ +/* + * Copyright 2012 Nouveau Community + * + * 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 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 + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: Martin Peres <martin.peres@labri.fr> + * Ben Skeggs + */ + +#include <subdev/bus.h> + +struct nv31_bus_priv { + struct nouveau_bus base; +}; + +static void +nv31_bus_intr(struct nouveau_subdev *subdev) +{ + struct nouveau_bus *pbus = nouveau_bus(subdev); + u32 stat = nv_rd32(pbus, 0x001100) & nv_rd32(pbus, 0x001140); + u32 gpio = nv_rd32(pbus, 0x001104) & nv_rd32(pbus, 0x001144); + + if (gpio) { + subdev = nouveau_subdev(pbus, NVDEV_SUBDEV_GPIO); + if (subdev && subdev->intr) + subdev->intr(subdev); + } + + if (stat & 0x00000008) { /* NV41- */ + u32 addr = nv_rd32(pbus, 0x009084); + u32 data = nv_rd32(pbus, 0x009088); + + nv_error(pbus, "MMIO %s of 0x%08x FAULT at 0x%06x\n", + (addr & 0x00000002) ? "write" : "read", data, + (addr & 0x00fffffc)); + + stat &= ~0x00000008; + nv_wr32(pbus, 0x001100, 0x00000008); + } + + if (stat & 0x00070000) { + subdev = nouveau_subdev(pbus, NVDEV_SUBDEV_THERM); + if (subdev && subdev->intr) + subdev->intr(subdev); + stat &= ~0x00070000; + nv_wr32(pbus, 0x001100, 0x00070000); + } + + if (stat) { + nv_error(pbus, "unknown intr 0x%08x\n", stat); + nv_mask(pbus, 0x001140, stat, 0x00000000); + } +} + +static int +nv31_bus_init(struct nouveau_object *object) +{ + struct nv31_bus_priv *priv = (void *)object; + int ret; + + ret = nouveau_bus_init(&priv->base); + if (ret) + return ret; + + nv_wr32(priv, 0x001100, 0xffffffff); + nv_wr32(priv, 0x001140, 0x00070008); + return 0; +} + +static int +nv31_bus_ctor(struct nouveau_object *parent, struct nouveau_object *engine, + struct nouveau_oclass *oclass, void *data, u32 size, + struct nouveau_object **pobject) +{ + struct nv31_bus_priv *priv; + int ret; + + ret = nouveau_bus_create(parent, engine, oclass, &priv); + *pobject = nv_object(priv); + if (ret) + return ret; + + nv_subdev(priv)->intr = nv31_bus_intr; + return 0; +} + +struct nouveau_oclass +nv31_bus_oclass = { + .handle = NV_SUBDEV(BUS, 0x31), + .ofuncs = &(struct nouveau_ofuncs) { + .ctor = nv31_bus_ctor, + .dtor = _nouveau_bus_dtor, + .init = nv31_bus_init, + .fini = _nouveau_bus_fini, + }, +}; diff --git a/drivers/gpu/drm/nouveau/core/subdev/bus/nv50.c b/drivers/gpu/drm/nouveau/core/subdev/bus/nv50.c new file mode 100644 index 000000000000..f5b2117fa8c6 --- /dev/null +++ b/drivers/gpu/drm/nouveau/core/subdev/bus/nv50.c @@ -0,0 +1,105 @@ +/* + * Copyright 2012 Nouveau Community + * + * 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 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 + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: Martin Peres <martin.peres@labri.fr> + * Ben Skeggs + */ + +#include <subdev/bus.h> + +struct nv50_bus_priv { + struct nouveau_bus base; +}; + +static void +nv50_bus_intr(struct nouveau_subdev *subdev) +{ + struct nouveau_bus *pbus = nouveau_bus(subdev); + u32 stat = nv_rd32(pbus, 0x001100) & nv_rd32(pbus, 0x001140); + + if (stat & 0x00000008) { + u32 addr = nv_rd32(pbus, 0x009084); + u32 data = nv_rd32(pbus, 0x009088); + + nv_error(pbus, "MMIO %s of 0x%08x FAULT at 0x%06x\n", + (addr & 0x00000002) ? "write" : "read", data, + (addr & 0x00fffffc)); + + stat &= ~0x00000008; + nv_wr32(pbus, 0x001100, 0x00000008); + } + + if (stat & 0x00010000) { + subdev = nouveau_subdev(pbus, NVDEV_SUBDEV_THERM); + if (subdev && subdev->intr) + subdev->intr(subdev); + stat &= ~0x00010000; + nv_wr32(pbus, 0x001100, 0x00010000); + } + + if (stat) { + nv_error(pbus, "unknown intr 0x%08x\n", stat); + nv_mask(pbus, 0x001140, stat, 0); + } +} + +static int +nv50_bus_init(struct nouveau_object *object) +{ + struct nv50_bus_priv *priv = (void *)object; + int ret; + + ret = nouveau_bus_init(&priv->base); + if (ret) + return ret; + + nv_wr32(priv, 0x001100, 0xffffffff); + nv_wr32(priv, 0x001140, 0x00010008); + return 0; +} + +static int +nv50_bus_ctor(struct nouveau_object *parent, struct nouveau_object *engine, + struct nouveau_oclass *oclass, void *data, u32 size, + struct nouveau_object **pobject) +{ + struct nv50_bus_priv *priv; + int ret; + + ret = nouveau_bus_create(parent, engine, oclass, &priv); + *pobject = nv_object(priv); + if (ret) + return ret; + + nv_subdev(priv)->intr = nv50_bus_intr; + return 0; +} + +struct nouveau_oclass +nv50_bus_oclass = { + .handle = NV_SUBDEV(BUS, 0x50), + .ofuncs = &(struct nouveau_ofuncs) { + .ctor = nv50_bus_ctor, + .dtor = _nouveau_bus_dtor, + .init = nv50_bus_init, + .fini = _nouveau_bus_fini, + }, +}; diff --git a/drivers/gpu/drm/nouveau/core/subdev/bus/nvc0.c b/drivers/gpu/drm/nouveau/core/subdev/bus/nvc0.c new file mode 100644 index 000000000000..b192d6246363 --- /dev/null +++ b/drivers/gpu/drm/nouveau/core/subdev/bus/nvc0.c @@ -0,0 +1,101 @@ +/* + * Copyright 2012 Nouveau Community + * + * 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 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 + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: Martin Peres <martin.peres@labri.fr> + * Ben Skeggs + */ + +#include <subdev/bus.h> + +struct nvc0_bus_priv { + struct nouveau_bus base; +}; + +static void +nvc0_bus_intr(struct nouveau_subdev *subdev) +{ + struct nouveau_bus *pbus = nouveau_bus(subdev); + u32 stat = nv_rd32(pbus, 0x001100) & nv_rd32(pbus, 0x001140); + + if (stat & 0x0000000e) { + u32 addr = nv_rd32(pbus, 0x009084); + u32 data = nv_rd32(pbus, 0x009088); + + nv_error(pbus, "MMIO %s of 0x%08x FAULT at 0x%06x [ %s%s%s]\n", + (addr & 0x00000002) ? "write" : "read", data, + (addr & 0x00fffffc), + (stat & 0x00000002) ? "!ENGINE " : "", + (stat & 0x00000004) ? "IBUS " : "", + (stat & 0x00000008) ? "TIMEOUT " : ""); + + nv_wr32(pbus, 0x009084, 0x00000000); + nv_wr32(pbus, 0x001100, (stat & 0x0000000e)); + stat &= ~0x0000000e; + } + + if (stat) { + nv_error(pbus, "unknown intr 0x%08x\n", stat); + nv_mask(pbus, 0x001140, stat, 0x00000000); + } +} + +static int +nvc0_bus_init(struct nouveau_object *object) +{ + struct nvc0_bus_priv *priv = (void *)object; + int ret; + + ret = nouveau_bus_init(&priv->base); + if (ret) + return ret; + + nv_wr32(priv, 0x001100, 0xffffffff); + nv_wr32(priv, 0x001140, 0x0000000e); + return 0; +} + +static int +nvc0_bus_ctor(struct nouveau_object *parent, struct nouveau_object *engine, + struct nouveau_oclass *oclass, void *data, u32 size, + struct nouveau_object **pobject) +{ + struct nvc0_bus_priv *priv; + int ret; + + ret = nouveau_bus_create(parent, engine, oclass, &priv); + *pobject = nv_object(priv); + if (ret) + return ret; + + nv_subdev(priv)->intr = nvc0_bus_intr; + return 0; +} + +struct nouveau_oclass +nvc0_bus_oclass = { + .handle = NV_SUBDEV(BUS, 0xc0), + .ofuncs = &(struct nouveau_ofuncs) { + .ctor = nvc0_bus_ctor, + .dtor = _nouveau_bus_dtor, + .init = nvc0_bus_init, + .fini = _nouveau_bus_fini, + }, +}; diff --git a/drivers/gpu/drm/nouveau/core/subdev/device/base.c b/drivers/gpu/drm/nouveau/core/subdev/device/base.c index f8a7ed4166cf..3937ced5c753 100644 --- a/drivers/gpu/drm/nouveau/core/subdev/device/base.c +++ b/drivers/gpu/drm/nouveau/core/subdev/device/base.c @@ -66,6 +66,7 @@ static const u64 disable_map[] = { [NVDEV_SUBDEV_CLOCK] = NV_DEVICE_DISABLE_CORE, [NVDEV_SUBDEV_MXM] = NV_DEVICE_DISABLE_CORE, [NVDEV_SUBDEV_MC] = NV_DEVICE_DISABLE_CORE, + [NVDEV_SUBDEV_BUS] = NV_DEVICE_DISABLE_CORE, [NVDEV_SUBDEV_TIMER] = NV_DEVICE_DISABLE_CORE, [NVDEV_SUBDEV_FB] = NV_DEVICE_DISABLE_CORE, [NVDEV_SUBDEV_LTCG] = NV_DEVICE_DISABLE_CORE, @@ -103,8 +104,8 @@ nouveau_devobj_ctor(struct nouveau_object *parent, struct nouveau_device *device; struct nouveau_devobj *devobj; struct nv_device_class *args = data; - u64 disable, boot0, strap; - u64 mmio_base, mmio_size; + u32 boot0, strap; + u64 disable, mmio_base, mmio_size; void __iomem *map; int ret, i, c; diff --git a/drivers/gpu/drm/nouveau/core/subdev/device/nv04.c b/drivers/gpu/drm/nouveau/core/subdev/device/nv04.c index 8626d0d6cbbc..473c5c03d3c9 100644 --- a/drivers/gpu/drm/nouveau/core/subdev/device/nv04.c +++ b/drivers/gpu/drm/nouveau/core/subdev/device/nv04.c @@ -24,6 +24,7 @@ #include <subdev/device.h> #include <subdev/bios.h> +#include <subdev/bus.h> #include <subdev/i2c.h> #include <subdev/clock.h> #include <subdev/devinit.h> @@ -46,10 +47,11 @@ nv04_identify(struct nouveau_device *device) case 0x04: device->cname = "NV04"; device->oclass[NVDEV_SUBDEV_VBIOS ] = &nouveau_bios_oclass; - device->oclass[NVDEV_SUBDEV_I2C ] = &nouveau_i2c_oclass; + device->oclass[NVDEV_SUBDEV_I2C ] = &nv04_i2c_oclass; device->oclass[NVDEV_SUBDEV_CLOCK ] = &nv04_clock_oclass; device->oclass[NVDEV_SUBDEV_DEVINIT] = &nv04_devinit_oclass; device->oclass[NVDEV_SUBDEV_MC ] = &nv04_mc_oclass; + device->oclass[NVDEV_SUBDEV_BUS ] = &nv04_bus_oclass; device->oclass[NVDEV_SUBDEV_TIMER ] = &nv04_timer_oclass; device->oclass[NVDEV_SUBDEV_FB ] = &nv04_fb_oclass; device->oclass[NVDEV_SUBDEV_INSTMEM] = &nv04_instmem_oclass; @@ -63,10 +65,11 @@ nv04_identify(struct nouveau_device *device) case 0x05: device->cname = "NV05"; device->oclass[NVDEV_SUBDEV_VBIOS ] = &nouveau_bios_oclass; - device->oclass[NVDEV_SUBDEV_I2C ] = &nouveau_i2c_oclass; + device->oclass[NVDEV_SUBDEV_I2C ] = &nv04_i2c_oclass; device->oclass[NVDEV_SUBDEV_CLOCK ] = &nv04_clock_oclass; device->oclass[NVDEV_SUBDEV_DEVINIT] = &nv05_devinit_oclass; device->oclass[NVDEV_SUBDEV_MC ] = &nv04_mc_oclass; + device->oclass[NVDEV_SUBDEV_BUS ] = &nv04_bus_oclass; device->oclass[NVDEV_SUBDEV_TIMER ] = &nv04_timer_oclass; device->oclass[NVDEV_SUBDEV_FB ] = &nv04_fb_oclass; device->oclass[NVDEV_SUBDEV_INSTMEM] = &nv04_instmem_oclass; diff --git a/drivers/gpu/drm/nouveau/core/subdev/device/nv10.c b/drivers/gpu/drm/nouveau/core/subdev/device/nv10.c index 9c40b0fb23f6..d0774f5bebe1 100644 --- a/drivers/gpu/drm/nouveau/core/subdev/device/nv10.c +++ b/drivers/gpu/drm/nouveau/core/subdev/device/nv10.c @@ -24,6 +24,7 @@ #include <subdev/device.h> #include <subdev/bios.h> +#include <subdev/bus.h> #include <subdev/gpio.h> #include <subdev/i2c.h> #include <subdev/clock.h> @@ -48,10 +49,11 @@ nv10_identify(struct nouveau_device *device) device->cname = "NV10"; device->oclass[NVDEV_SUBDEV_VBIOS ] = &nouveau_bios_oclass; device->oclass[NVDEV_SUBDEV_GPIO ] = &nv10_gpio_oclass; - device->oclass[NVDEV_SUBDEV_I2C ] = &nouveau_i2c_oclass; + device->oclass[NVDEV_SUBDEV_I2C ] = &nv04_i2c_oclass; device->oclass[NVDEV_SUBDEV_CLOCK ] = &nv04_clock_oclass; device->oclass[NVDEV_SUBDEV_DEVINIT] = &nv10_devinit_oclass; device->oclass[NVDEV_SUBDEV_MC ] = &nv04_mc_oclass; + device->oclass[NVDEV_SUBDEV_BUS ] = &nv04_bus_oclass; device->oclass[NVDEV_SUBDEV_TIMER ] = &nv04_timer_oclass; device->oclass[NVDEV_SUBDEV_FB ] = &nv10_fb_oclass; device->oclass[NVDEV_SUBDEV_INSTMEM] = &nv04_instmem_oclass; @@ -64,10 +66,11 @@ nv10_identify(struct nouveau_device *device) device->cname = "NV15"; device->oclass[NVDEV_SUBDEV_VBIOS ] = &nouveau_bios_oclass; device->oclass[NVDEV_SUBDEV_GPIO ] = &nv10_gpio_oclass; - device->oclass[NVDEV_SUBDEV_I2C ] = &nouveau_i2c_oclass; + device->oclass[NVDEV_SUBDEV_I2C ] = &nv04_i2c_oclass; device->oclass[NVDEV_SUBDEV_CLOCK ] = &nv04_clock_oclass; device->oclass[NVDEV_SUBDEV_DEVINIT] = &nv10_devinit_oclass; device->oclass[NVDEV_SUBDEV_MC ] = &nv04_mc_oclass; + device->oclass[NVDEV_SUBDEV_BUS ] = &nv04_bus_oclass; device->oclass[NVDEV_SUBDEV_TIMER ] = &nv04_timer_oclass; device->oclass[NVDEV_SUBDEV_FB ] = &nv10_fb_oclass; device->oclass[NVDEV_SUBDEV_INSTMEM] = &nv04_instmem_oclass; @@ -82,10 +85,11 @@ nv10_identify(struct nouveau_device *device) device->cname = "NV16"; device->oclass[NVDEV_SUBDEV_VBIOS ] = &nouveau_bios_oclass; device->oclass[NVDEV_SUBDEV_GPIO ] = &nv10_gpio_oclass; - device->oclass[NVDEV_SUBDEV_I2C ] = &nouveau_i2c_oclass; + device->oclass[NVDEV_SUBDEV_I2C ] = &nv04_i2c_oclass; device->oclass[NVDEV_SUBDEV_CLOCK ] = &nv04_clock_oclass; device->oclass[NVDEV_SUBDEV_DEVINIT] = &nv10_devinit_oclass; device->oclass[NVDEV_SUBDEV_MC ] = &nv04_mc_oclass; + device->oclass[NVDEV_SUBDEV_BUS ] = &nv04_bus_oclass; device->oclass[NVDEV_SUBDEV_TIMER ] = &nv04_timer_oclass; device->oclass[NVDEV_SUBDEV_FB ] = &nv10_fb_oclass; device->oclass[NVDEV_SUBDEV_INSTMEM] = &nv04_instmem_oclass; @@ -100,10 +104,11 @@ nv10_identify(struct nouveau_device *device) device->cname = "nForce"; device->oclass[NVDEV_SUBDEV_VBIOS ] = &nouveau_bios_oclass; device->oclass[NVDEV_SUBDEV_GPIO ] = &nv10_gpio_oclass; - device->oclass[NVDEV_SUBDEV_I2C ] = &nouveau_i2c_oclass; + device->oclass[NVDEV_SUBDEV_I2C ] = &nv04_i2c_oclass; device->oclass[NVDEV_SUBDEV_CLOCK ] = &nv04_clock_oclass; device->oclass[NVDEV_SUBDEV_DEVINIT] = &nv1a_devinit_oclass; device->oclass[NVDEV_SUBDEV_MC ] = &nv04_mc_oclass; + device->oclass[NVDEV_SUBDEV_BUS ] = &nv04_bus_oclass; device->oclass[NVDEV_SUBDEV_TIMER ] = &nv04_timer_oclass; device->oclass[NVDEV_SUBDEV_FB ] = &nv1a_fb_oclass; device->oclass[NVDEV_SUBDEV_INSTMEM] = &nv04_instmem_oclass; @@ -118,10 +123,11 @@ nv10_identify(struct nouveau_device *device) device->cname = "NV11"; device->oclass[NVDEV_SUBDEV_VBIOS ] = &nouveau_bios_oclass; device->oclass[NVDEV_SUBDEV_GPIO ] = &nv10_gpio_oclass; - device->oclass[NVDEV_SUBDEV_I2C ] = &nouveau_i2c_oclass; + device->oclass[NVDEV_SUBDEV_I2C ] = &nv04_i2c_oclass; device->oclass[NVDEV_SUBDEV_CLOCK ] = &nv04_clock_oclass; device->oclass[NVDEV_SUBDEV_DEVINIT] = &nv10_devinit_oclass; device->oclass[NVDEV_SUBDEV_MC ] = &nv04_mc_oclass; + device->oclass[NVDEV_SUBDEV_BUS ] = &nv04_bus_oclass; device->oclass[NVDEV_SUBDEV_TIMER ] = &nv04_timer_oclass; device->oclass[NVDEV_SUBDEV_FB ] = &nv10_fb_oclass; device->oclass[NVDEV_SUBDEV_INSTMEM] = &nv04_instmem_oclass; @@ -136,10 +142,11 @@ nv10_identify(struct nouveau_device *device) device->cname = "NV17"; device->oclass[NVDEV_SUBDEV_VBIOS ] = &nouveau_bios_oclass; device->oclass[NVDEV_SUBDEV_GPIO ] = &nv10_gpio_oclass; - device->oclass[NVDEV_SUBDEV_I2C ] = &nouveau_i2c_oclass; + device->oclass[NVDEV_SUBDEV_I2C ] = &nv04_i2c_oclass; device->oclass[NVDEV_SUBDEV_CLOCK ] = &nv04_clock_oclass; device->oclass[NVDEV_SUBDEV_DEVINIT] = &nv10_devinit_oclass; device->oclass[NVDEV_SUBDEV_MC ] = &nv04_mc_oclass; + device->oclass[NVDEV_SUBDEV_BUS ] = &nv04_bus_oclass; device->oclass[NVDEV_SUBDEV_TIMER ] = &nv04_timer_oclass; device->oclass[NVDEV_SUBDEV_FB ] = &nv10_fb_oclass; device->oclass[NVDEV_SUBDEV_INSTMEM] = &nv04_instmem_oclass; @@ -154,10 +161,11 @@ nv10_identify(struct nouveau_device *device) device->cname = "nForce2"; device->oclass[NVDEV_SUBDEV_VBIOS ] = &nouveau_bios_oclass; device->oclass[NVDEV_SUBDEV_GPIO ] = &nv10_gpio_oclass; - device->oclass[NVDEV_SUBDEV_I2C ] = &nouveau_i2c_oclass; + device->oclass[NVDEV_SUBDEV_I2C ] = &nv04_i2c_oclass; device->oclass[NVDEV_SUBDEV_CLOCK ] = &nv04_clock_oclass; device->oclass[NVDEV_SUBDEV_DEVINIT] = &nv1a_devinit_oclass; device->oclass[NVDEV_SUBDEV_MC ] = &nv04_mc_oclass; + device->oclass[NVDEV_SUBDEV_BUS ] = &nv04_bus_oclass; device->oclass[NVDEV_SUBDEV_TIMER ] = &nv04_timer_oclass; device->oclass[NVDEV_SUBDEV_FB ] = &nv1a_fb_oclass; device->oclass[NVDEV_SUBDEV_INSTMEM] = &nv04_instmem_oclass; @@ -172,10 +180,11 @@ nv10_identify(struct nouveau_device *device) device->cname = "NV18"; device->oclass[NVDEV_SUBDEV_VBIOS ] = &nouveau_bios_oclass; device->oclass[NVDEV_SUBDEV_GPIO ] = &nv10_gpio_oclass; - device->oclass[NVDEV_SUBDEV_I2C ] = &nouveau_i2c_oclass; + device->oclass[NVDEV_SUBDEV_I2C ] = &nv04_i2c_oclass; device->oclass[NVDEV_SUBDEV_CLOCK ] = &nv04_clock_oclass; device->oclass[NVDEV_SUBDEV_DEVINIT] = &nv10_devinit_oclass; device->oclass[NVDEV_SUBDEV_MC ] = &nv04_mc_oclass; + device->oclass[NVDEV_SUBDEV_BUS ] = &nv04_bus_oclass; device->oclass[NVDEV_SUBDEV_TIMER ] = &nv04_timer_oclass; device->oclass[NVDEV_SUBDEV_FB ] = &nv10_fb_oclass; device->oclass[NVDEV_SUBDEV_INSTMEM] = &nv04_instmem_oclass; diff --git a/drivers/gpu/drm/nouveau/core/subdev/device/nv20.c b/drivers/gpu/drm/nouveau/core/subdev/device/nv20.c index 74f88f48e1c2..ab920e0dc45b 100644 --- a/drivers/gpu/drm/nouveau/core/subdev/device/nv20.c +++ b/drivers/gpu/drm/nouveau/core/subdev/device/nv20.c @@ -24,6 +24,7 @@ #include <subdev/device.h> #include <subdev/bios.h> +#include <subdev/bus.h> #include <subdev/gpio.h> #include <subdev/i2c.h> #include <subdev/clock.h> @@ -49,10 +50,11 @@ nv20_identify(struct nouveau_device *device) device->cname = "NV20"; device->oclass[NVDEV_SUBDEV_VBIOS ] = &nouveau_bios_oclass; device->oclass[NVDEV_SUBDEV_GPIO ] = &nv10_gpio_oclass; - device->oclass[NVDEV_SUBDEV_I2C ] = &nouveau_i2c_oclass; + device->oclass[NVDEV_SUBDEV_I2C ] = &nv04_i2c_oclass; device->oclass[NVDEV_SUBDEV_CLOCK ] = &nv04_clock_oclass; device->oclass[NVDEV_SUBDEV_DEVINIT] = &nv20_devinit_oclass; device->oclass[NVDEV_SUBDEV_MC ] = &nv04_mc_oclass; + device->oclass[NVDEV_SUBDEV_BUS ] = &nv04_bus_oclass; device->oclass[NVDEV_SUBDEV_TIMER ] = &nv04_timer_oclass; device->oclass[NVDEV_SUBDEV_FB ] = &nv20_fb_oclass; device->oclass[NVDEV_SUBDEV_INSTMEM] = &nv04_instmem_oclass; @@ -67,10 +69,11 @@ nv20_identify(struct nouveau_device *device) device->cname = "NV25"; device->oclass[NVDEV_SUBDEV_VBIOS ] = &nouveau_bios_oclass; device->oclass[NVDEV_SUBDEV_GPIO ] = &nv10_gpio_oclass; - device->oclass[NVDEV_SUBDEV_I2C ] = &nouveau_i2c_oclass; + device->oclass[NVDEV_SUBDEV_I2C ] = &nv04_i2c_oclass; device->oclass[NVDEV_SUBDEV_CLOCK ] = &nv04_clock_oclass; device->oclass[NVDEV_SUBDEV_DEVINIT] = &nv20_devinit_oclass; device->oclass[NVDEV_SUBDEV_MC ] = &nv04_mc_oclass; + device->oclass[NVDEV_SUBDEV_BUS ] = &nv04_bus_oclass; device->oclass[NVDEV_SUBDEV_TIMER ] = &nv04_timer_oclass; device->oclass[NVDEV_SUBDEV_FB ] = &nv25_fb_oclass; device->oclass[NVDEV_SUBDEV_INSTMEM] = &nv04_instmem_oclass; @@ -85,10 +88,11 @@ nv20_identify(struct nouveau_device *device) device->cname = "NV28"; device->oclass[NVDEV_SUBDEV_VBIOS ] = &nouveau_bios_oclass; device->oclass[NVDEV_SUBDEV_GPIO ] = &nv10_gpio_oclass; - device->oclass[NVDEV_SUBDEV_I2C ] = &nouveau_i2c_oclass; + device->oclass[NVDEV_SUBDEV_I2C ] = &nv04_i2c_oclass; device->oclass[NVDEV_SUBDEV_CLOCK ] = &nv04_clock_oclass; device->oclass[NVDEV_SUBDEV_DEVINIT] = &nv20_devinit_oclass; device->oclass[NVDEV_SUBDEV_MC ] = &nv04_mc_oclass; + device->oclass[NVDEV_SUBDEV_BUS ] = &nv04_bus_oclass; device->oclass[NVDEV_SUBDEV_TIMER ] = &nv04_timer_oclass; device->oclass[NVDEV_SUBDEV_FB ] = &nv25_fb_oclass; device->oclass[NVDEV_SUBDEV_INSTMEM] = &nv04_instmem_oclass; @@ -103,10 +107,11 @@ nv20_identify(struct nouveau_device *device) device->cname = "NV2A"; device->oclass[NVDEV_SUBDEV_VBIOS ] = &nouveau_bios_oclass; device->oclass[NVDEV_SUBDEV_GPIO ] = &nv10_gpio_oclass; - device->oclass[NVDEV_SUBDEV_I2C ] = &nouveau_i2c_oclass; + device->oclass[NVDEV_SUBDEV_I2C ] = &nv04_i2c_oclass; device->oclass[NVDEV_SUBDEV_CLOCK ] = &nv04_clock_oclass; device->oclass[NVDEV_SUBDEV_DEVINIT] = &nv20_devinit_oclass; device->oclass[NVDEV_SUBDEV_MC ] = &nv04_mc_oclass; + device->oclass[NVDEV_SUBDEV_BUS ] = &nv04_bus_oclass; device->oclass[NVDEV_SUBDEV_TIMER ] = &nv04_timer_oclass; device->oclass[NVDEV_SUBDEV_FB ] = &nv25_fb_oclass; device->oclass[NVDEV_SUBDEV_INSTMEM] = &nv04_instmem_oclass; diff --git a/drivers/gpu/drm/nouveau/core/subdev/device/nv30.c b/drivers/gpu/drm/nouveau/core/subdev/device/nv30.c index 0ac1b2c4f61d..5f2110261b04 100644 --- a/drivers/gpu/drm/nouveau/core/subdev/device/nv30.c +++ b/drivers/gpu/drm/nouveau/core/subdev/device/nv30.c @@ -24,6 +24,7 @@ #include <subdev/device.h> #include <subdev/bios.h> +#include <subdev/bus.h> #include <subdev/gpio.h> #include <subdev/i2c.h> #include <subdev/clock.h> @@ -49,10 +50,11 @@ nv30_identify(struct nouveau_device *device) device->cname = "NV30"; device->oclass[NVDEV_SUBDEV_VBIOS ] = &nouveau_bios_oclass; device->oclass[NVDEV_SUBDEV_GPIO ] = &nv10_gpio_oclass; - device->oclass[NVDEV_SUBDEV_I2C ] = &nouveau_i2c_oclass; + device->oclass[NVDEV_SUBDEV_I2C ] = &nv04_i2c_oclass; device->oclass[NVDEV_SUBDEV_CLOCK ] = &nv04_clock_oclass; device->oclass[NVDEV_SUBDEV_DEVINIT] = &nv20_devinit_oclass; device->oclass[NVDEV_SUBDEV_MC ] = &nv04_mc_oclass; + device->oclass[NVDEV_SUBDEV_BUS ] = &nv04_bus_oclass; device->oclass[NVDEV_SUBDEV_TIMER ] = &nv04_timer_oclass; device->oclass[NVDEV_SUBDEV_FB ] = &nv30_fb_oclass; device->oclass[NVDEV_SUBDEV_INSTMEM] = &nv04_instmem_oclass; @@ -67,10 +69,11 @@ nv30_identify(struct nouveau_device *device) device->cname = "NV35"; device->oclass[NVDEV_SUBDEV_VBIOS ] = &nouveau_bios_oclass; device->oclass[NVDEV_SUBDEV_GPIO ] = &nv10_gpio_oclass; - device->oclass[NVDEV_SUBDEV_I2C ] = &nouveau_i2c_oclass; + device->oclass[NVDEV_SUBDEV_I2C ] = &nv04_i2c_oclass; device->oclass[NVDEV_SUBDEV_CLOCK ] = &nv04_clock_oclass; device->oclass[NVDEV_SUBDEV_DEVINIT] = &nv20_devinit_oclass; device->oclass[NVDEV_SUBDEV_MC ] = &nv04_mc_oclass; + device->oclass[NVDEV_SUBDEV_BUS ] = &nv04_bus_oclass; device->oclass[NVDEV_SUBDEV_TIMER ] = &nv04_timer_oclass; device->oclass[NVDEV_SUBDEV_FB ] = &nv35_fb_oclass; device->oclass[NVDEV_SUBDEV_INSTMEM] = &nv04_instmem_oclass; @@ -85,10 +88,11 @@ nv30_identify(struct nouveau_device *device) device->cname = "NV31"; device->oclass[NVDEV_SUBDEV_VBIOS ] = &nouveau_bios_oclass; device->oclass[NVDEV_SUBDEV_GPIO ] = &nv10_gpio_oclass; - device->oclass[NVDEV_SUBDEV_I2C ] = &nouveau_i2c_oclass; + device->oclass[NVDEV_SUBDEV_I2C ] = &nv04_i2c_oclass; device->oclass[NVDEV_SUBDEV_CLOCK ] = &nv04_clock_oclass; device->oclass[NVDEV_SUBDEV_DEVINIT] = &nv20_devinit_oclass; device->oclass[NVDEV_SUBDEV_MC ] = &nv04_mc_oclass; + device->oclass[NVDEV_SUBDEV_BUS ] = &nv31_bus_oclass; device->oclass[NVDEV_SUBDEV_TIMER ] = &nv04_timer_oclass; device->oclass[NVDEV_SUBDEV_FB ] = &nv30_fb_oclass; device->oclass[NVDEV_SUBDEV_INSTMEM] = &nv04_instmem_oclass; @@ -104,10 +108,11 @@ nv30_identify(struct nouveau_device *device) device->cname = "NV36"; device->oclass[NVDEV_SUBDEV_VBIOS ] = &nouveau_bios_oclass; device->oclass[NVDEV_SUBDEV_GPIO ] = &nv10_gpio_oclass; - device->oclass[NVDEV_SUBDEV_I2C ] = &nouveau_i2c_oclass; + device->oclass[NVDEV_SUBDEV_I2C ] = &nv04_i2c_oclass; device->oclass[NVDEV_SUBDEV_CLOCK ] = &nv04_clock_oclass; device->oclass[NVDEV_SUBDEV_DEVINIT] = &nv20_devinit_oclass; device->oclass[NVDEV_SUBDEV_MC ] = &nv04_mc_oclass; + device->oclass[NVDEV_SUBDEV_BUS ] = &nv31_bus_oclass; device->oclass[NVDEV_SUBDEV_TIMER ] = &nv04_timer_oclass; device->oclass[NVDEV_SUBDEV_FB ] = &nv36_fb_oclass; device->oclass[NVDEV_SUBDEV_INSTMEM] = &nv04_instmem_oclass; @@ -123,10 +128,11 @@ nv30_identify(struct nouveau_device *device) device->cname = "NV34"; device->oclass[NVDEV_SUBDEV_VBIOS ] = &nouveau_bios_oclass; device->oclass[NVDEV_SUBDEV_GPIO ] = &nv10_gpio_oclass; - device->oclass[NVDEV_SUBDEV_I2C ] = &nouveau_i2c_oclass; + device->oclass[NVDEV_SUBDEV_I2C ] = &nv04_i2c_oclass; device->oclass[NVDEV_SUBDEV_CLOCK ] = &nv04_clock_oclass; device->oclass[NVDEV_SUBDEV_DEVINIT] = &nv10_devinit_oclass; device->oclass[NVDEV_SUBDEV_MC ] = &nv04_mc_oclass; + device->oclass[NVDEV_SUBDEV_BUS ] = &nv31_bus_oclass; device->oclass[NVDEV_SUBDEV_TIMER ] = &nv04_timer_oclass; device->oclass[NVDEV_SUBDEV_FB ] = &nv10_fb_oclass; device->oclass[NVDEV_SUBDEV_INSTMEM] = &nv04_instmem_oclass; diff --git a/drivers/gpu/drm/nouveau/core/subdev/device/nv40.c b/drivers/gpu/drm/nouveau/core/subdev/device/nv40.c index 41d59689a021..f3d55efe9ac9 100644 --- a/drivers/gpu/drm/nouveau/core/subdev/device/nv40.c +++ b/drivers/gpu/drm/nouveau/core/subdev/device/nv40.c @@ -24,6 +24,8 @@ #include <subdev/device.h> #include <subdev/bios.h> +#include <subdev/bus.h> +#include <subdev/vm.h> #include <subdev/gpio.h> #include <subdev/i2c.h> #include <subdev/clock.h> @@ -50,11 +52,12 @@ nv40_identify(struct nouveau_device *device) device->cname = "NV40"; device->oclass[NVDEV_SUBDEV_VBIOS ] = &nouveau_bios_oclass; device->oclass[NVDEV_SUBDEV_GPIO ] = &nv10_gpio_oclass; - device->oclass[NVDEV_SUBDEV_I2C ] = &nouveau_i2c_oclass; + device->oclass[NVDEV_SUBDEV_I2C ] = &nv04_i2c_oclass; device->oclass[NVDEV_SUBDEV_CLOCK ] = &nv40_clock_oclass; device->oclass[NVDEV_SUBDEV_THERM ] = &nv40_therm_oclass; device->oclass[NVDEV_SUBDEV_DEVINIT] = &nv1a_devinit_oclass; device->oclass[NVDEV_SUBDEV_MC ] = &nv04_mc_oclass; + device->oclass[NVDEV_SUBDEV_BUS ] = &nv31_bus_oclass; device->oclass[NVDEV_SUBDEV_TIMER ] = &nv04_timer_oclass; device->oclass[NVDEV_SUBDEV_FB ] = &nv40_fb_oclass; device->oclass[NVDEV_SUBDEV_INSTMEM] = &nv40_instmem_oclass; @@ -70,11 +73,12 @@ nv40_identify(struct nouveau_device *device) device->cname = "NV41"; device->oclass[NVDEV_SUBDEV_VBIOS ] = &nouveau_bios_oclass; device->oclass[NVDEV_SUBDEV_GPIO ] = &nv10_gpio_oclass; - device->oclass[NVDEV_SUBDEV_I2C ] = &nouveau_i2c_oclass; + device->oclass[NVDEV_SUBDEV_I2C ] = &nv04_i2c_oclass; device->oclass[NVDEV_SUBDEV_CLOCK ] = &nv40_clock_oclass; device->oclass[NVDEV_SUBDEV_THERM ] = &nv40_therm_oclass; device->oclass[NVDEV_SUBDEV_DEVINIT] = &nv1a_devinit_oclass; device->oclass[NVDEV_SUBDEV_MC ] = &nv04_mc_oclass; + device->oclass[NVDEV_SUBDEV_BUS ] = &nv31_bus_oclass; device->oclass[NVDEV_SUBDEV_TIMER ] = &nv04_timer_oclass; device->oclass[NVDEV_SUBDEV_FB ] = &nv41_fb_oclass; device->oclass[NVDEV_SUBDEV_INSTMEM] = &nv40_instmem_oclass; @@ -90,11 +94,12 @@ nv40_identify(struct nouveau_device *device) device->cname = "NV42"; device->oclass[NVDEV_SUBDEV_VBIOS ] = &nouveau_bios_oclass; device->oclass[NVDEV_SUBDEV_GPIO ] = &nv10_gpio_oclass; - device->oclass[NVDEV_SUBDEV_I2C ] = &nouveau_i2c_oclass; + device->oclass[NVDEV_SUBDEV_I2C ] = &nv04_i2c_oclass; device->oclass[NVDEV_SUBDEV_CLOCK ] = &nv40_clock_oclass; device->oclass[NVDEV_SUBDEV_THERM ] = &nv40_therm_oclass; device->oclass[NVDEV_SUBDEV_DEVINIT] = &nv1a_devinit_oclass; device->oclass[NVDEV_SUBDEV_MC ] = &nv04_mc_oclass; + device->oclass[NVDEV_SUBDEV_BUS ] = &nv31_bus_oclass; device->oclass[NVDEV_SUBDEV_TIMER ] = &nv04_timer_oclass; device->oclass[NVDEV_SUBDEV_FB ] = &nv41_fb_oclass; device->oclass[NVDEV_SUBDEV_INSTMEM] = &nv40_instmem_oclass; @@ -110,11 +115,12 @@ nv40_identify(struct nouveau_device *device) device->cname = "NV43"; device->oclass[NVDEV_SUBDEV_VBIOS ] = &nouveau_bios_oclass; device->oclass[NVDEV_SUBDEV_GPIO ] = &nv10_gpio_oclass; - device->oclass[NVDEV_SUBDEV_I2C ] = &nouveau_i2c_oclass; + device->oclass[NVDEV_SUBDEV_I2C ] = &nv04_i2c_oclass; device->oclass[NVDEV_SUBDEV_CLOCK ] = &nv40_clock_oclass; device->oclass[NVDEV_SUBDEV_THERM ] = &nv40_therm_oclass; device->oclass[NVDEV_SUBDEV_DEVINIT] = &nv1a_devinit_oclass; device->oclass[NVDEV_SUBDEV_MC ] = &nv04_mc_oclass; + device->oclass[NVDEV_SUBDEV_BUS ] = &nv31_bus_oclass; device->oclass[NVDEV_SUBDEV_TIMER ] = &nv04_timer_oclass; device->oclass[NVDEV_SUBDEV_FB ] = &nv41_fb_oclass; device->oclass[NVDEV_SUBDEV_INSTMEM] = &nv40_instmem_oclass; @@ -130,11 +136,12 @@ nv40_identify(struct nouveau_device *device) device->cname = "NV45"; device->oclass[NVDEV_SUBDEV_VBIOS ] = &nouveau_bios_oclass; device->oclass[NVDEV_SUBDEV_GPIO ] = &nv10_gpio_oclass; - device->oclass[NVDEV_SUBDEV_I2C ] = &nouveau_i2c_oclass; + device->oclass[NVDEV_SUBDEV_I2C ] = &nv04_i2c_oclass; device->oclass[NVDEV_SUBDEV_CLOCK ] = &nv40_clock_oclass; device->oclass[NVDEV_SUBDEV_THERM ] = &nv40_therm_oclass; device->oclass[NVDEV_SUBDEV_DEVINIT] = &nv1a_devinit_oclass; device->oclass[NVDEV_SUBDEV_MC ] = &nv04_mc_oclass; + device->oclass[NVDEV_SUBDEV_BUS ] = &nv31_bus_oclass; device->oclass[NVDEV_SUBDEV_TIMER ] = &nv04_timer_oclass; device->oclass[NVDEV_SUBDEV_FB ] = &nv40_fb_oclass; device->oclass[NVDEV_SUBDEV_INSTMEM] = &nv40_instmem_oclass; @@ -150,11 +157,12 @@ nv40_identify(struct nouveau_device *device) device->cname = "G70"; device->oclass[NVDEV_SUBDEV_VBIOS ] = &nouveau_bios_oclass; device->oclass[NVDEV_SUBDEV_GPIO ] = &nv10_gpio_oclass; - device->oclass[NVDEV_SUBDEV_I2C ] = &nouveau_i2c_oclass; + device->oclass[NVDEV_SUBDEV_I2C ] = &nv04_i2c_oclass; device->oclass[NVDEV_SUBDEV_CLOCK ] = &nv40_clock_oclass; device->oclass[NVDEV_SUBDEV_THERM ] = &nv40_therm_oclass; device->oclass[NVDEV_SUBDEV_DEVINIT] = &nv1a_devinit_oclass; device->oclass[NVDEV_SUBDEV_MC ] = &nv04_mc_oclass; + device->oclass[NVDEV_SUBDEV_BUS ] = &nv31_bus_oclass; device->oclass[NVDEV_SUBDEV_TIMER ] = &nv04_timer_oclass; device->oclass[NVDEV_SUBDEV_FB ] = &nv47_fb_oclass; device->oclass[NVDEV_SUBDEV_INSTMEM] = &nv40_instmem_oclass; @@ -170,11 +178,12 @@ nv40_identify(struct nouveau_device *device) device->cname = "G71"; device->oclass[NVDEV_SUBDEV_VBIOS ] = &nouveau_bios_oclass; device->oclass[NVDEV_SUBDEV_GPIO ] = &nv10_gpio_oclass; - device->oclass[NVDEV_SUBDEV_I2C ] = &nouveau_i2c_oclass; + device->oclass[NVDEV_SUBDEV_I2C ] = &nv04_i2c_oclass; device->oclass[NVDEV_SUBDEV_CLOCK ] = &nv40_clock_oclass; device->oclass[NVDEV_SUBDEV_THERM ] = &nv40_therm_oclass; device->oclass[NVDEV_SUBDEV_DEVINIT] = &nv1a_devinit_oclass; device->oclass[NVDEV_SUBDEV_MC ] = &nv04_mc_oclass; + device->oclass[NVDEV_SUBDEV_BUS ] = &nv31_bus_oclass; device->oclass[NVDEV_SUBDEV_TIMER ] = &nv04_timer_oclass; device->oclass[NVDEV_SUBDEV_FB ] = &nv49_fb_oclass; device->oclass[NVDEV_SUBDEV_INSTMEM] = &nv40_instmem_oclass; @@ -190,11 +199,12 @@ nv40_identify(struct nouveau_device *device) device->cname = "G73"; device->oclass[NVDEV_SUBDEV_VBIOS ] = &nouveau_bios_oclass; device->oclass[NVDEV_SUBDEV_GPIO ] = &nv10_gpio_oclass; - device->oclass[NVDEV_SUBDEV_I2C ] = &nouveau_i2c_oclass; + device->oclass[NVDEV_SUBDEV_I2C ] = &nv04_i2c_oclass; device->oclass[NVDEV_SUBDEV_CLOCK ] = &nv40_clock_oclass; device->oclass[NVDEV_SUBDEV_THERM ] = &nv40_therm_oclass; device->oclass[NVDEV_SUBDEV_DEVINIT] = &nv1a_devinit_oclass; device->oclass[NVDEV_SUBDEV_MC ] = &nv04_mc_oclass; + device->oclass[NVDEV_SUBDEV_BUS ] = &nv31_bus_oclass; device->oclass[NVDEV_SUBDEV_TIMER ] = &nv04_timer_oclass; device->oclass[NVDEV_SUBDEV_FB ] = &nv49_fb_oclass; device->oclass[NVDEV_SUBDEV_INSTMEM] = &nv40_instmem_oclass; @@ -210,11 +220,12 @@ nv40_identify(struct nouveau_device *device) device->cname = "NV44"; device->oclass[NVDEV_SUBDEV_VBIOS ] = &nouveau_bios_oclass; device->oclass[NVDEV_SUBDEV_GPIO ] = &nv10_gpio_oclass; - device->oclass[NVDEV_SUBDEV_I2C ] = &nouveau_i2c_oclass; + device->oclass[NVDEV_SUBDEV_I2C ] = &nv04_i2c_oclass; device->oclass[NVDEV_SUBDEV_CLOCK ] = &nv40_clock_oclass; device->oclass[NVDEV_SUBDEV_THERM ] = &nv40_therm_oclass; device->oclass[NVDEV_SUBDEV_DEVINIT] = &nv1a_devinit_oclass; device->oclass[NVDEV_SUBDEV_MC ] = &nv44_mc_oclass; + device->oclass[NVDEV_SUBDEV_BUS ] = &nv31_bus_oclass; device->oclass[NVDEV_SUBDEV_TIMER ] = &nv04_timer_oclass; device->oclass[NVDEV_SUBDEV_FB ] = &nv44_fb_oclass; device->oclass[NVDEV_SUBDEV_INSTMEM] = &nv40_instmem_oclass; @@ -230,11 +241,12 @@ nv40_identify(struct nouveau_device *device) device->cname = "G72"; device->oclass[NVDEV_SUBDEV_VBIOS ] = &nouveau_bios_oclass; device->oclass[NVDEV_SUBDEV_GPIO ] = &nv10_gpio_oclass; - device->oclass[NVDEV_SUBDEV_I2C ] = &nouveau_i2c_oclass; + device->oclass[NVDEV_SUBDEV_I2C ] = &nv04_i2c_oclass; device->oclass[NVDEV_SUBDEV_CLOCK ] = &nv40_clock_oclass; device->oclass[NVDEV_SUBDEV_THERM ] = &nv40_therm_oclass; device->oclass[NVDEV_SUBDEV_DEVINIT] = &nv1a_devinit_oclass; device->oclass[NVDEV_SUBDEV_MC ] = &nv44_mc_oclass; + device->oclass[NVDEV_SUBDEV_BUS ] = &nv31_bus_oclass; device->oclass[NVDEV_SUBDEV_TIMER ] = &nv04_timer_oclass; device->oclass[NVDEV_SUBDEV_FB ] = &nv46_fb_oclass; device->oclass[NVDEV_SUBDEV_INSTMEM] = &nv40_instmem_oclass; @@ -250,11 +262,12 @@ nv40_identify(struct nouveau_device *device) device->cname = "NV44A"; device->oclass[NVDEV_SUBDEV_VBIOS ] = &nouveau_bios_oclass; device->oclass[NVDEV_SUBDEV_GPIO ] = &nv10_gpio_oclass; - device->oclass[NVDEV_SUBDEV_I2C ] = &nouveau_i2c_oclass; + device->oclass[NVDEV_SUBDEV_I2C ] = &nv04_i2c_oclass; device->oclass[NVDEV_SUBDEV_CLOCK ] = &nv40_clock_oclass; device->oclass[NVDEV_SUBDEV_THERM ] = &nv40_therm_oclass; device->oclass[NVDEV_SUBDEV_DEVINIT] = &nv1a_devinit_oclass; device->oclass[NVDEV_SUBDEV_MC ] = &nv44_mc_oclass; + device->oclass[NVDEV_SUBDEV_BUS ] = &nv31_bus_oclass; device->oclass[NVDEV_SUBDEV_TIMER ] = &nv04_timer_oclass; device->oclass[NVDEV_SUBDEV_FB ] = &nv44_fb_oclass; device->oclass[NVDEV_SUBDEV_INSTMEM] = &nv40_instmem_oclass; @@ -270,11 +283,12 @@ nv40_identify(struct nouveau_device *device) device->cname = "C61"; device->oclass[NVDEV_SUBDEV_VBIOS ] = &nouveau_bios_oclass; device->oclass[NVDEV_SUBDEV_GPIO ] = &nv10_gpio_oclass; - device->oclass[NVDEV_SUBDEV_I2C ] = &nouveau_i2c_oclass; + device->oclass[NVDEV_SUBDEV_I2C ] = &nv04_i2c_oclass; device->oclass[NVDEV_SUBDEV_CLOCK ] = &nv40_clock_oclass; device->oclass[NVDEV_SUBDEV_THERM ] = &nv40_therm_oclass; device->oclass[NVDEV_SUBDEV_DEVINIT] = &nv1a_devinit_oclass; device->oclass[NVDEV_SUBDEV_MC ] = &nv44_mc_oclass; + device->oclass[NVDEV_SUBDEV_BUS ] = &nv31_bus_oclass; device->oclass[NVDEV_SUBDEV_TIMER ] = &nv04_timer_oclass; device->oclass[NVDEV_SUBDEV_FB ] = &nv46_fb_oclass; device->oclass[NVDEV_SUBDEV_INSTMEM] = &nv40_instmem_oclass; @@ -290,11 +304,12 @@ nv40_identify(struct nouveau_device *device) device->cname = "C51"; device->oclass[NVDEV_SUBDEV_VBIOS ] = &nouveau_bios_oclass; device->oclass[NVDEV_SUBDEV_GPIO ] = &nv10_gpio_oclass; - device->oclass[NVDEV_SUBDEV_I2C ] = &nouveau_i2c_oclass; + device->oclass[NVDEV_SUBDEV_I2C ] = &nv4e_i2c_oclass; device->oclass[NVDEV_SUBDEV_CLOCK ] = &nv40_clock_oclass; device->oclass[NVDEV_SUBDEV_THERM ] = &nv40_therm_oclass; device->oclass[NVDEV_SUBDEV_DEVINIT] = &nv1a_devinit_oclass; device->oclass[NVDEV_SUBDEV_MC ] = &nv44_mc_oclass; + device->oclass[NVDEV_SUBDEV_BUS ] = &nv31_bus_oclass; device->oclass[NVDEV_SUBDEV_TIMER ] = &nv04_timer_oclass; device->oclass[NVDEV_SUBDEV_FB ] = &nv4e_fb_oclass; device->oclass[NVDEV_SUBDEV_INSTMEM] = &nv40_instmem_oclass; @@ -310,11 +325,12 @@ nv40_identify(struct nouveau_device *device) device->cname = "C73"; device->oclass[NVDEV_SUBDEV_VBIOS ] = &nouveau_bios_oclass; device->oclass[NVDEV_SUBDEV_GPIO ] = &nv10_gpio_oclass; - device->oclass[NVDEV_SUBDEV_I2C ] = &nouveau_i2c_oclass; + device->oclass[NVDEV_SUBDEV_I2C ] = &nv04_i2c_oclass; device->oclass[NVDEV_SUBDEV_CLOCK ] = &nv40_clock_oclass; device->oclass[NVDEV_SUBDEV_THERM ] = &nv40_therm_oclass; device->oclass[NVDEV_SUBDEV_DEVINIT] = &nv1a_devinit_oclass; device->oclass[NVDEV_SUBDEV_MC ] = &nv44_mc_oclass; + device->oclass[NVDEV_SUBDEV_BUS ] = &nv31_bus_oclass; device->oclass[NVDEV_SUBDEV_TIMER ] = &nv04_timer_oclass; device->oclass[NVDEV_SUBDEV_FB ] = &nv46_fb_oclass; device->oclass[NVDEV_SUBDEV_INSTMEM] = &nv40_instmem_oclass; @@ -330,11 +346,12 @@ nv40_identify(struct nouveau_device *device) device->cname = "C67"; device->oclass[NVDEV_SUBDEV_VBIOS ] = &nouveau_bios_oclass; device->oclass[NVDEV_SUBDEV_GPIO ] = &nv10_gpio_oclass; - device->oclass[NVDEV_SUBDEV_I2C ] = &nouveau_i2c_oclass; + device->oclass[NVDEV_SUBDEV_I2C ] = &nv04_i2c_oclass; device->oclass[NVDEV_SUBDEV_CLOCK ] = &nv40_clock_oclass; device->oclass[NVDEV_SUBDEV_THERM ] = &nv40_therm_oclass; device->oclass[NVDEV_SUBDEV_DEVINIT] = &nv1a_devinit_oclass; device->oclass[NVDEV_SUBDEV_MC ] = &nv44_mc_oclass; + device->oclass[NVDEV_SUBDEV_BUS ] = &nv31_bus_oclass; device->oclass[NVDEV_SUBDEV_TIMER ] = &nv04_timer_oclass; device->oclass[NVDEV_SUBDEV_FB ] = &nv46_fb_oclass; device->oclass[NVDEV_SUBDEV_INSTMEM] = &nv40_instmem_oclass; @@ -350,11 +367,12 @@ nv40_identify(struct nouveau_device *device) device->cname = "C68"; device->oclass[NVDEV_SUBDEV_VBIOS ] = &nouveau_bios_oclass; device->oclass[NVDEV_SUBDEV_GPIO ] = &nv10_gpio_oclass; - device->oclass[NVDEV_SUBDEV_I2C ] = &nouveau_i2c_oclass; + device->oclass[NVDEV_SUBDEV_I2C ] = &nv04_i2c_oclass; device->oclass[NVDEV_SUBDEV_CLOCK ] = &nv40_clock_oclass; device->oclass[NVDEV_SUBDEV_THERM ] = &nv40_therm_oclass; device->oclass[NVDEV_SUBDEV_DEVINIT] = &nv1a_devinit_oclass; device->oclass[NVDEV_SUBDEV_MC ] = &nv44_mc_oclass; + device->oclass[NVDEV_SUBDEV_BUS ] = &nv31_bus_oclass; device->oclass[NVDEV_SUBDEV_TIMER ] = &nv04_timer_oclass; device->oclass[NVDEV_SUBDEV_FB ] = &nv46_fb_oclass; device->oclass[NVDEV_SUBDEV_INSTMEM] = &nv40_instmem_oclass; diff --git a/drivers/gpu/drm/nouveau/core/subdev/device/nv50.c b/drivers/gpu/drm/nouveau/core/subdev/device/nv50.c index 6ccfd8585ba2..5ed2fa51ddc2 100644 --- a/drivers/gpu/drm/nouveau/core/subdev/device/nv50.c +++ b/drivers/gpu/drm/nouveau/core/subdev/device/nv50.c @@ -24,6 +24,7 @@ #include <subdev/device.h> #include <subdev/bios.h> +#include <subdev/bus.h> #include <subdev/gpio.h> #include <subdev/i2c.h> #include <subdev/clock.h> @@ -57,12 +58,13 @@ nv50_identify(struct nouveau_device *device) device->cname = "G80"; device->oclass[NVDEV_SUBDEV_VBIOS ] = &nouveau_bios_oclass; device->oclass[NVDEV_SUBDEV_GPIO ] = &nv50_gpio_oclass; - device->oclass[NVDEV_SUBDEV_I2C ] = &nouveau_i2c_oclass; + device->oclass[NVDEV_SUBDEV_I2C ] = &nv50_i2c_oclass; device->oclass[NVDEV_SUBDEV_CLOCK ] = &nv50_clock_oclass; device->oclass[NVDEV_SUBDEV_THERM ] = &nv50_therm_oclass; device->oclass[NVDEV_SUBDEV_MXM ] = &nv50_mxm_oclass; device->oclass[NVDEV_SUBDEV_DEVINIT] = &nv50_devinit_oclass; device->oclass[NVDEV_SUBDEV_MC ] = &nv50_mc_oclass; + device->oclass[NVDEV_SUBDEV_BUS ] = &nv50_bus_oclass; device->oclass[NVDEV_SUBDEV_TIMER ] = &nv04_timer_oclass; device->oclass[NVDEV_SUBDEV_FB ] = &nv50_fb_oclass; device->oclass[NVDEV_SUBDEV_INSTMEM] = &nv50_instmem_oclass; @@ -79,12 +81,13 @@ nv50_identify(struct nouveau_device *device) device->cname = "G84"; device->oclass[NVDEV_SUBDEV_VBIOS ] = &nouveau_bios_oclass; device->oclass[NVDEV_SUBDEV_GPIO ] = &nv50_gpio_oclass; - device->oclass[NVDEV_SUBDEV_I2C ] = &nouveau_i2c_oclass; + device->oclass[NVDEV_SUBDEV_I2C ] = &nv50_i2c_oclass; device->oclass[NVDEV_SUBDEV_CLOCK ] = &nv50_clock_oclass; device->oclass[NVDEV_SUBDEV_THERM ] = &nv50_therm_oclass; device->oclass[NVDEV_SUBDEV_MXM ] = &nv50_mxm_oclass; device->oclass[NVDEV_SUBDEV_DEVINIT] = &nv50_devinit_oclass; device->oclass[NVDEV_SUBDEV_MC ] = &nv50_mc_oclass; + device->oclass[NVDEV_SUBDEV_BUS ] = &nv50_bus_oclass; device->oclass[NVDEV_SUBDEV_TIMER ] = &nv04_timer_oclass; device->oclass[NVDEV_SUBDEV_FB ] = &nv50_fb_oclass; device->oclass[NVDEV_SUBDEV_INSTMEM] = &nv50_instmem_oclass; @@ -104,12 +107,13 @@ nv50_identify(struct nouveau_device *device) device->cname = "G86"; device->oclass[NVDEV_SUBDEV_VBIOS ] = &nouveau_bios_oclass; device->oclass[NVDEV_SUBDEV_GPIO ] = &nv50_gpio_oclass; - device->oclass[NVDEV_SUBDEV_I2C ] = &nouveau_i2c_oclass; + device->oclass[NVDEV_SUBDEV_I2C ] = &nv50_i2c_oclass; device->oclass[NVDEV_SUBDEV_CLOCK ] = &nv50_clock_oclass; device->oclass[NVDEV_SUBDEV_THERM ] = &nv50_therm_oclass; device->oclass[NVDEV_SUBDEV_MXM ] = &nv50_mxm_oclass; device->oclass[NVDEV_SUBDEV_DEVINIT] = &nv50_devinit_oclass; device->oclass[NVDEV_SUBDEV_MC ] = &nv50_mc_oclass; + device->oclass[NVDEV_SUBDEV_BUS ] = &nv50_bus_oclass; device->oclass[NVDEV_SUBDEV_TIMER ] = &nv04_timer_oclass; device->oclass[NVDEV_SUBDEV_FB ] = &nv50_fb_oclass; device->oclass[NVDEV_SUBDEV_INSTMEM] = &nv50_instmem_oclass; @@ -129,12 +133,13 @@ nv50_identify(struct nouveau_device *device) device->cname = "G92"; device->oclass[NVDEV_SUBDEV_VBIOS ] = &nouveau_bios_oclass; device->oclass[NVDEV_SUBDEV_GPIO ] = &nv50_gpio_oclass; - device->oclass[NVDEV_SUBDEV_I2C ] = &nouveau_i2c_oclass; + device->oclass[NVDEV_SUBDEV_I2C ] = &nv50_i2c_oclass; device->oclass[NVDEV_SUBDEV_CLOCK ] = &nv50_clock_oclass; device->oclass[NVDEV_SUBDEV_THERM ] = &nv50_therm_oclass; device->oclass[NVDEV_SUBDEV_MXM ] = &nv50_mxm_oclass; device->oclass[NVDEV_SUBDEV_DEVINIT] = &nv50_devinit_oclass; device->oclass[NVDEV_SUBDEV_MC ] = &nv50_mc_oclass; + device->oclass[NVDEV_SUBDEV_BUS ] = &nv50_bus_oclass; device->oclass[NVDEV_SUBDEV_TIMER ] = &nv04_timer_oclass; device->oclass[NVDEV_SUBDEV_FB ] = &nv50_fb_oclass; device->oclass[NVDEV_SUBDEV_INSTMEM] = &nv50_instmem_oclass; @@ -154,12 +159,13 @@ nv50_identify(struct nouveau_device *device) device->cname = "G94"; device->oclass[NVDEV_SUBDEV_VBIOS ] = &nouveau_bios_oclass; device->oclass[NVDEV_SUBDEV_GPIO ] = &nv50_gpio_oclass; - device->oclass[NVDEV_SUBDEV_I2C ] = &nouveau_i2c_oclass; + device->oclass[NVDEV_SUBDEV_I2C ] = &nv94_i2c_oclass; device->oclass[NVDEV_SUBDEV_CLOCK ] = &nv50_clock_oclass; device->oclass[NVDEV_SUBDEV_THERM ] = &nv50_therm_oclass; device->oclass[NVDEV_SUBDEV_MXM ] = &nv50_mxm_oclass; device->oclass[NVDEV_SUBDEV_DEVINIT] = &nv50_devinit_oclass; device->oclass[NVDEV_SUBDEV_MC ] = &nv50_mc_oclass; + device->oclass[NVDEV_SUBDEV_BUS ] = &nv50_bus_oclass; device->oclass[NVDEV_SUBDEV_TIMER ] = &nv04_timer_oclass; device->oclass[NVDEV_SUBDEV_FB ] = &nv50_fb_oclass; device->oclass[NVDEV_SUBDEV_INSTMEM] = &nv50_instmem_oclass; @@ -179,12 +185,13 @@ nv50_identify(struct nouveau_device *device) device->cname = "G96"; device->oclass[NVDEV_SUBDEV_VBIOS ] = &nouveau_bios_oclass; device->oclass[NVDEV_SUBDEV_GPIO ] = &nv50_gpio_oclass; - device->oclass[NVDEV_SUBDEV_I2C ] = &nouveau_i2c_oclass; + device->oclass[NVDEV_SUBDEV_I2C ] = &nv94_i2c_oclass; device->oclass[NVDEV_SUBDEV_CLOCK ] = &nv50_clock_oclass; device->oclass[NVDEV_SUBDEV_THERM ] = &nv50_therm_oclass; device->oclass[NVDEV_SUBDEV_MXM ] = &nv50_mxm_oclass; device->oclass[NVDEV_SUBDEV_DEVINIT] = &nv50_devinit_oclass; device->oclass[NVDEV_SUBDEV_MC ] = &nv50_mc_oclass; + device->oclass[NVDEV_SUBDEV_BUS ] = &nv50_bus_oclass; device->oclass[NVDEV_SUBDEV_TIMER ] = &nv04_timer_oclass; device->oclass[NVDEV_SUBDEV_FB ] = &nv50_fb_oclass; device->oclass[NVDEV_SUBDEV_INSTMEM] = &nv50_instmem_oclass; @@ -204,12 +211,13 @@ nv50_identify(struct nouveau_device *device) device->cname = "G98"; device->oclass[NVDEV_SUBDEV_VBIOS ] = &nouveau_bios_oclass; device->oclass[NVDEV_SUBDEV_GPIO ] = &nv50_gpio_oclass; - device->oclass[NVDEV_SUBDEV_I2C ] = &nouveau_i2c_oclass; + device->oclass[NVDEV_SUBDEV_I2C ] = &nv94_i2c_oclass; device->oclass[NVDEV_SUBDEV_CLOCK ] = &nv50_clock_oclass; device->oclass[NVDEV_SUBDEV_THERM ] = &nv50_therm_oclass; device->oclass[NVDEV_SUBDEV_MXM ] = &nv50_mxm_oclass; device->oclass[NVDEV_SUBDEV_DEVINIT] = &nv50_devinit_oclass; device->oclass[NVDEV_SUBDEV_MC ] = &nv98_mc_oclass; + device->oclass[NVDEV_SUBDEV_BUS ] = &nv50_bus_oclass; device->oclass[NVDEV_SUBDEV_TIMER ] = &nv04_timer_oclass; device->oclass[NVDEV_SUBDEV_FB ] = &nv50_fb_oclass; device->oclass[NVDEV_SUBDEV_INSTMEM] = &nv50_instmem_oclass; @@ -229,12 +237,13 @@ nv50_identify(struct nouveau_device *device) device->cname = "G200"; device->oclass[NVDEV_SUBDEV_VBIOS ] = &nouveau_bios_oclass; device->oclass[NVDEV_SUBDEV_GPIO ] = &nv50_gpio_oclass; - device->oclass[NVDEV_SUBDEV_I2C ] = &nouveau_i2c_oclass; + device->oclass[NVDEV_SUBDEV_I2C ] = &nv50_i2c_oclass; device->oclass[NVDEV_SUBDEV_CLOCK ] = &nv50_clock_oclass; device->oclass[NVDEV_SUBDEV_THERM ] = &nv50_therm_oclass; device->oclass[NVDEV_SUBDEV_MXM ] = &nv50_mxm_oclass; device->oclass[NVDEV_SUBDEV_DEVINIT] = &nv50_devinit_oclass; device->oclass[NVDEV_SUBDEV_MC ] = &nv98_mc_oclass; + device->oclass[NVDEV_SUBDEV_BUS ] = &nv50_bus_oclass; device->oclass[NVDEV_SUBDEV_TIMER ] = &nv04_timer_oclass; device->oclass[NVDEV_SUBDEV_FB ] = &nv50_fb_oclass; device->oclass[NVDEV_SUBDEV_INSTMEM] = &nv50_instmem_oclass; @@ -254,12 +263,13 @@ nv50_identify(struct nouveau_device *device) device->cname = "MCP77/MCP78"; device->oclass[NVDEV_SUBDEV_VBIOS ] = &nouveau_bios_oclass; device->oclass[NVDEV_SUBDEV_GPIO ] = &nv50_gpio_oclass; - device->oclass[NVDEV_SUBDEV_I2C ] = &nouveau_i2c_oclass; + device->oclass[NVDEV_SUBDEV_I2C ] = &nv94_i2c_oclass; device->oclass[NVDEV_SUBDEV_CLOCK ] = &nv50_clock_oclass; device->oclass[NVDEV_SUBDEV_THERM ] = &nv50_therm_oclass; device->oclass[NVDEV_SUBDEV_MXM ] = &nv50_mxm_oclass; device->oclass[NVDEV_SUBDEV_DEVINIT] = &nv50_devinit_oclass; device->oclass[NVDEV_SUBDEV_MC ] = &nv98_mc_oclass; + device->oclass[NVDEV_SUBDEV_BUS ] = &nv50_bus_oclass; device->oclass[NVDEV_SUBDEV_TIMER ] = &nv04_timer_oclass; device->oclass[NVDEV_SUBDEV_FB ] = &nv50_fb_oclass; device->oclass[NVDEV_SUBDEV_INSTMEM] = &nv50_instmem_oclass; @@ -279,12 +289,13 @@ nv50_identify(struct nouveau_device *device) device->cname = "MCP79/MCP7A"; device->oclass[NVDEV_SUBDEV_VBIOS ] = &nouveau_bios_oclass; device->oclass[NVDEV_SUBDEV_GPIO ] = &nv50_gpio_oclass; - device->oclass[NVDEV_SUBDEV_I2C ] = &nouveau_i2c_oclass; + device->oclass[NVDEV_SUBDEV_I2C ] = &nv94_i2c_oclass; device->oclass[NVDEV_SUBDEV_CLOCK ] = &nv50_clock_oclass; device->oclass[NVDEV_SUBDEV_THERM ] = &nv50_therm_oclass; device->oclass[NVDEV_SUBDEV_MXM ] = &nv50_mxm_oclass; device->oclass[NVDEV_SUBDEV_DEVINIT] = &nv50_devinit_oclass; device->oclass[NVDEV_SUBDEV_MC ] = &nv98_mc_oclass; + device->oclass[NVDEV_SUBDEV_BUS ] = &nv50_bus_oclass; device->oclass[NVDEV_SUBDEV_TIMER ] = &nv04_timer_oclass; device->oclass[NVDEV_SUBDEV_FB ] = &nv50_fb_oclass; device->oclass[NVDEV_SUBDEV_INSTMEM] = &nv50_instmem_oclass; @@ -304,12 +315,13 @@ nv50_identify(struct nouveau_device *device) device->cname = "GT215"; device->oclass[NVDEV_SUBDEV_VBIOS ] = &nouveau_bios_oclass; device->oclass[NVDEV_SUBDEV_GPIO ] = &nv50_gpio_oclass; - device->oclass[NVDEV_SUBDEV_I2C ] = &nouveau_i2c_oclass; + device->oclass[NVDEV_SUBDEV_I2C ] = &nv94_i2c_oclass; device->oclass[NVDEV_SUBDEV_CLOCK ] = &nva3_clock_oclass; - device->oclass[NVDEV_SUBDEV_THERM ] = &nv50_therm_oclass; + device->oclass[NVDEV_SUBDEV_THERM ] = &nva3_therm_oclass; device->oclass[NVDEV_SUBDEV_MXM ] = &nv50_mxm_oclass; device->oclass[NVDEV_SUBDEV_DEVINIT] = &nv50_devinit_oclass; device->oclass[NVDEV_SUBDEV_MC ] = &nv98_mc_oclass; + device->oclass[NVDEV_SUBDEV_BUS ] = &nv50_bus_oclass; device->oclass[NVDEV_SUBDEV_TIMER ] = &nv04_timer_oclass; device->oclass[NVDEV_SUBDEV_FB ] = &nv50_fb_oclass; device->oclass[NVDEV_SUBDEV_INSTMEM] = &nv50_instmem_oclass; @@ -330,12 +342,13 @@ nv50_identify(struct nouveau_device *device) device->cname = "GT216"; device->oclass[NVDEV_SUBDEV_VBIOS ] = &nouveau_bios_oclass; device->oclass[NVDEV_SUBDEV_GPIO ] = &nv50_gpio_oclass; - device->oclass[NVDEV_SUBDEV_I2C ] = &nouveau_i2c_oclass; + device->oclass[NVDEV_SUBDEV_I2C ] = &nv94_i2c_oclass; device->oclass[NVDEV_SUBDEV_CLOCK ] = &nva3_clock_oclass; - device->oclass[NVDEV_SUBDEV_THERM ] = &nv50_therm_oclass; + device->oclass[NVDEV_SUBDEV_THERM ] = &nva3_therm_oclass; device->oclass[NVDEV_SUBDEV_MXM ] = &nv50_mxm_oclass; device->oclass[NVDEV_SUBDEV_DEVINIT] = &nv50_devinit_oclass; device->oclass[NVDEV_SUBDEV_MC ] = &nv98_mc_oclass; + device->oclass[NVDEV_SUBDEV_BUS ] = &nv50_bus_oclass; device->oclass[NVDEV_SUBDEV_TIMER ] = &nv04_timer_oclass; device->oclass[NVDEV_SUBDEV_FB ] = &nv50_fb_oclass; device->oclass[NVDEV_SUBDEV_INSTMEM] = &nv50_instmem_oclass; @@ -355,12 +368,13 @@ nv50_identify(struct nouveau_device *device) device->cname = "GT218"; device->oclass[NVDEV_SUBDEV_VBIOS ] = &nouveau_bios_oclass; device->oclass[NVDEV_SUBDEV_GPIO ] = &nv50_gpio_oclass; - device->oclass[NVDEV_SUBDEV_I2C ] = &nouveau_i2c_oclass; + device->oclass[NVDEV_SUBDEV_I2C ] = &nv94_i2c_oclass; device->oclass[NVDEV_SUBDEV_CLOCK ] = &nva3_clock_oclass; - device->oclass[NVDEV_SUBDEV_THERM ] = &nv50_therm_oclass; + device->oclass[NVDEV_SUBDEV_THERM ] = &nva3_therm_oclass; device->oclass[NVDEV_SUBDEV_MXM ] = &nv50_mxm_oclass; device->oclass[NVDEV_SUBDEV_DEVINIT] = &nv50_devinit_oclass; device->oclass[NVDEV_SUBDEV_MC ] = &nv98_mc_oclass; + device->oclass[NVDEV_SUBDEV_BUS ] = &nv50_bus_oclass; device->oclass[NVDEV_SUBDEV_TIMER ] = &nv04_timer_oclass; device->oclass[NVDEV_SUBDEV_FB ] = &nv50_fb_oclass; device->oclass[NVDEV_SUBDEV_INSTMEM] = &nv50_instmem_oclass; @@ -380,12 +394,13 @@ nv50_identify(struct nouveau_device *device) device->cname = "MCP89"; device->oclass[NVDEV_SUBDEV_VBIOS ] = &nouveau_bios_oclass; device->oclass[NVDEV_SUBDEV_GPIO ] = &nv50_gpio_oclass; - device->oclass[NVDEV_SUBDEV_I2C ] = &nouveau_i2c_oclass; + device->oclass[NVDEV_SUBDEV_I2C ] = &nv94_i2c_oclass; device->oclass[NVDEV_SUBDEV_CLOCK ] = &nva3_clock_oclass; - device->oclass[NVDEV_SUBDEV_THERM ] = &nv50_therm_oclass; + device->oclass[NVDEV_SUBDEV_THERM ] = &nva3_therm_oclass; device->oclass[NVDEV_SUBDEV_MXM ] = &nv50_mxm_oclass; device->oclass[NVDEV_SUBDEV_DEVINIT] = &nv50_devinit_oclass; device->oclass[NVDEV_SUBDEV_MC ] = &nv98_mc_oclass; + device->oclass[NVDEV_SUBDEV_BUS ] = &nv50_bus_oclass; device->oclass[NVDEV_SUBDEV_TIMER ] = &nv04_timer_oclass; device->oclass[NVDEV_SUBDEV_FB ] = &nv50_fb_oclass; device->oclass[NVDEV_SUBDEV_INSTMEM] = &nv50_instmem_oclass; diff --git a/drivers/gpu/drm/nouveau/core/subdev/device/nvc0.c b/drivers/gpu/drm/nouveau/core/subdev/device/nvc0.c index f0461685a422..4393eb4d6564 100644 --- a/drivers/gpu/drm/nouveau/core/subdev/device/nvc0.c +++ b/drivers/gpu/drm/nouveau/core/subdev/device/nvc0.c @@ -24,6 +24,7 @@ #include <subdev/device.h> #include <subdev/bios.h> +#include <subdev/bus.h> #include <subdev/gpio.h> #include <subdev/i2c.h> #include <subdev/clock.h> @@ -57,12 +58,13 @@ nvc0_identify(struct nouveau_device *device) device->cname = "GF100"; device->oclass[NVDEV_SUBDEV_VBIOS ] = &nouveau_bios_oclass; device->oclass[NVDEV_SUBDEV_GPIO ] = &nv50_gpio_oclass; - device->oclass[NVDEV_SUBDEV_I2C ] = &nouveau_i2c_oclass; + device->oclass[NVDEV_SUBDEV_I2C ] = &nv94_i2c_oclass; device->oclass[NVDEV_SUBDEV_CLOCK ] = &nvc0_clock_oclass; - device->oclass[NVDEV_SUBDEV_THERM ] = &nv50_therm_oclass; + device->oclass[NVDEV_SUBDEV_THERM ] = &nva3_therm_oclass; device->oclass[NVDEV_SUBDEV_MXM ] = &nv50_mxm_oclass; device->oclass[NVDEV_SUBDEV_DEVINIT] = &nv50_devinit_oclass; device->oclass[NVDEV_SUBDEV_MC ] = &nvc0_mc_oclass; + device->oclass[NVDEV_SUBDEV_BUS ] = &nvc0_bus_oclass; device->oclass[NVDEV_SUBDEV_TIMER ] = &nv04_timer_oclass; device->oclass[NVDEV_SUBDEV_FB ] = &nvc0_fb_oclass; device->oclass[NVDEV_SUBDEV_LTCG ] = &nvc0_ltcg_oclass; @@ -85,12 +87,13 @@ nvc0_identify(struct nouveau_device *device) device->cname = "GF104"; device->oclass[NVDEV_SUBDEV_VBIOS ] = &nouveau_bios_oclass; device->oclass[NVDEV_SUBDEV_GPIO ] = &nv50_gpio_oclass; - device->oclass[NVDEV_SUBDEV_I2C ] = &nouveau_i2c_oclass; + device->oclass[NVDEV_SUBDEV_I2C ] = &nv94_i2c_oclass; device->oclass[NVDEV_SUBDEV_CLOCK ] = &nvc0_clock_oclass; - device->oclass[NVDEV_SUBDEV_THERM ] = &nv50_therm_oclass; + device->oclass[NVDEV_SUBDEV_THERM ] = &nva3_therm_oclass; device->oclass[NVDEV_SUBDEV_MXM ] = &nv50_mxm_oclass; device->oclass[NVDEV_SUBDEV_DEVINIT] = &nv50_devinit_oclass; device->oclass[NVDEV_SUBDEV_MC ] = &nvc0_mc_oclass; + device->oclass[NVDEV_SUBDEV_BUS ] = &nvc0_bus_oclass; device->oclass[NVDEV_SUBDEV_TIMER ] = &nv04_timer_oclass; device->oclass[NVDEV_SUBDEV_FB ] = &nvc0_fb_oclass; device->oclass[NVDEV_SUBDEV_LTCG ] = &nvc0_ltcg_oclass; @@ -113,12 +116,13 @@ nvc0_identify(struct nouveau_device *device) device->cname = "GF106"; device->oclass[NVDEV_SUBDEV_VBIOS ] = &nouveau_bios_oclass; device->oclass[NVDEV_SUBDEV_GPIO ] = &nv50_gpio_oclass; - device->oclass[NVDEV_SUBDEV_I2C ] = &nouveau_i2c_oclass; + device->oclass[NVDEV_SUBDEV_I2C ] = &nv94_i2c_oclass; device->oclass[NVDEV_SUBDEV_CLOCK ] = &nvc0_clock_oclass; - device->oclass[NVDEV_SUBDEV_THERM ] = &nv50_therm_oclass; + device->oclass[NVDEV_SUBDEV_THERM ] = &nva3_therm_oclass; device->oclass[NVDEV_SUBDEV_MXM ] = &nv50_mxm_oclass; device->oclass[NVDEV_SUBDEV_DEVINIT] = &nv50_devinit_oclass; device->oclass[NVDEV_SUBDEV_MC ] = &nvc0_mc_oclass; + device->oclass[NVDEV_SUBDEV_BUS ] = &nvc0_bus_oclass; device->oclass[NVDEV_SUBDEV_TIMER ] = &nv04_timer_oclass; device->oclass[NVDEV_SUBDEV_FB ] = &nvc0_fb_oclass; device->oclass[NVDEV_SUBDEV_LTCG ] = &nvc0_ltcg_oclass; @@ -141,12 +145,13 @@ nvc0_identify(struct nouveau_device *device) device->cname = "GF114"; device->oclass[NVDEV_SUBDEV_VBIOS ] = &nouveau_bios_oclass; device->oclass[NVDEV_SUBDEV_GPIO ] = &nv50_gpio_oclass; - device->oclass[NVDEV_SUBDEV_I2C ] = &nouveau_i2c_oclass; + device->oclass[NVDEV_SUBDEV_I2C ] = &nv94_i2c_oclass; device->oclass[NVDEV_SUBDEV_CLOCK ] = &nvc0_clock_oclass; - device->oclass[NVDEV_SUBDEV_THERM ] = &nv50_therm_oclass; + device->oclass[NVDEV_SUBDEV_THERM ] = &nva3_therm_oclass; device->oclass[NVDEV_SUBDEV_MXM ] = &nv50_mxm_oclass; device->oclass[NVDEV_SUBDEV_DEVINIT] = &nv50_devinit_oclass; device->oclass[NVDEV_SUBDEV_MC ] = &nvc0_mc_oclass; + device->oclass[NVDEV_SUBDEV_BUS ] = &nvc0_bus_oclass; device->oclass[NVDEV_SUBDEV_TIMER ] = &nv04_timer_oclass; device->oclass[NVDEV_SUBDEV_FB ] = &nvc0_fb_oclass; device->oclass[NVDEV_SUBDEV_LTCG ] = &nvc0_ltcg_oclass; @@ -169,12 +174,13 @@ nvc0_identify(struct nouveau_device *device) device->cname = "GF116"; device->oclass[NVDEV_SUBDEV_VBIOS ] = &nouveau_bios_oclass; device->oclass[NVDEV_SUBDEV_GPIO ] = &nv50_gpio_oclass; - device->oclass[NVDEV_SUBDEV_I2C ] = &nouveau_i2c_oclass; + device->oclass[NVDEV_SUBDEV_I2C ] = &nv94_i2c_oclass; device->oclass[NVDEV_SUBDEV_CLOCK ] = &nvc0_clock_oclass; - device->oclass[NVDEV_SUBDEV_THERM ] = &nv50_therm_oclass; + device->oclass[NVDEV_SUBDEV_THERM ] = &nva3_therm_oclass; device->oclass[NVDEV_SUBDEV_MXM ] = &nv50_mxm_oclass; device->oclass[NVDEV_SUBDEV_DEVINIT] = &nv50_devinit_oclass; device->oclass[NVDEV_SUBDEV_MC ] = &nvc0_mc_oclass; + device->oclass[NVDEV_SUBDEV_BUS ] = &nvc0_bus_oclass; device->oclass[NVDEV_SUBDEV_TIMER ] = &nv04_timer_oclass; device->oclass[NVDEV_SUBDEV_FB ] = &nvc0_fb_oclass; device->oclass[NVDEV_SUBDEV_LTCG ] = &nvc0_ltcg_oclass; @@ -197,12 +203,13 @@ nvc0_identify(struct nouveau_device *device) device->cname = "GF108"; device->oclass[NVDEV_SUBDEV_VBIOS ] = &nouveau_bios_oclass; device->oclass[NVDEV_SUBDEV_GPIO ] = &nv50_gpio_oclass; - device->oclass[NVDEV_SUBDEV_I2C ] = &nouveau_i2c_oclass; + device->oclass[NVDEV_SUBDEV_I2C ] = &nv94_i2c_oclass; device->oclass[NVDEV_SUBDEV_CLOCK ] = &nvc0_clock_oclass; - device->oclass[NVDEV_SUBDEV_THERM ] = &nv50_therm_oclass; + device->oclass[NVDEV_SUBDEV_THERM ] = &nva3_therm_oclass; device->oclass[NVDEV_SUBDEV_MXM ] = &nv50_mxm_oclass; device->oclass[NVDEV_SUBDEV_DEVINIT] = &nv50_devinit_oclass; device->oclass[NVDEV_SUBDEV_MC ] = &nvc0_mc_oclass; + device->oclass[NVDEV_SUBDEV_BUS ] = &nvc0_bus_oclass; device->oclass[NVDEV_SUBDEV_TIMER ] = &nv04_timer_oclass; device->oclass[NVDEV_SUBDEV_FB ] = &nvc0_fb_oclass; device->oclass[NVDEV_SUBDEV_LTCG ] = &nvc0_ltcg_oclass; @@ -225,12 +232,13 @@ nvc0_identify(struct nouveau_device *device) device->cname = "GF110"; device->oclass[NVDEV_SUBDEV_VBIOS ] = &nouveau_bios_oclass; device->oclass[NVDEV_SUBDEV_GPIO ] = &nv50_gpio_oclass; - device->oclass[NVDEV_SUBDEV_I2C ] = &nouveau_i2c_oclass; + device->oclass[NVDEV_SUBDEV_I2C ] = &nv94_i2c_oclass; device->oclass[NVDEV_SUBDEV_CLOCK ] = &nvc0_clock_oclass; - device->oclass[NVDEV_SUBDEV_THERM ] = &nv50_therm_oclass; + device->oclass[NVDEV_SUBDEV_THERM ] = &nva3_therm_oclass; device->oclass[NVDEV_SUBDEV_MXM ] = &nv50_mxm_oclass; device->oclass[NVDEV_SUBDEV_DEVINIT] = &nv50_devinit_oclass; device->oclass[NVDEV_SUBDEV_MC ] = &nvc0_mc_oclass; + device->oclass[NVDEV_SUBDEV_BUS ] = &nvc0_bus_oclass; device->oclass[NVDEV_SUBDEV_TIMER ] = &nv04_timer_oclass; device->oclass[NVDEV_SUBDEV_FB ] = &nvc0_fb_oclass; device->oclass[NVDEV_SUBDEV_LTCG ] = &nvc0_ltcg_oclass; @@ -253,12 +261,13 @@ nvc0_identify(struct nouveau_device *device) device->cname = "GF119"; device->oclass[NVDEV_SUBDEV_VBIOS ] = &nouveau_bios_oclass; device->oclass[NVDEV_SUBDEV_GPIO ] = &nvd0_gpio_oclass; - device->oclass[NVDEV_SUBDEV_I2C ] = &nouveau_i2c_oclass; + device->oclass[NVDEV_SUBDEV_I2C ] = &nvd0_i2c_oclass; device->oclass[NVDEV_SUBDEV_CLOCK ] = &nvc0_clock_oclass; - device->oclass[NVDEV_SUBDEV_THERM ] = &nv50_therm_oclass; + device->oclass[NVDEV_SUBDEV_THERM ] = &nvd0_therm_oclass; device->oclass[NVDEV_SUBDEV_MXM ] = &nv50_mxm_oclass; device->oclass[NVDEV_SUBDEV_DEVINIT] = &nv50_devinit_oclass; device->oclass[NVDEV_SUBDEV_MC ] = &nvc0_mc_oclass; + device->oclass[NVDEV_SUBDEV_BUS ] = &nvc0_bus_oclass; device->oclass[NVDEV_SUBDEV_TIMER ] = &nv04_timer_oclass; device->oclass[NVDEV_SUBDEV_FB ] = &nvc0_fb_oclass; device->oclass[NVDEV_SUBDEV_LTCG ] = &nvc0_ltcg_oclass; @@ -282,4 +291,4 @@ nvc0_identify(struct nouveau_device *device) } return 0; -} + } diff --git a/drivers/gpu/drm/nouveau/core/subdev/device/nve0.c b/drivers/gpu/drm/nouveau/core/subdev/device/nve0.c index 03a652876e73..5c12391619fd 100644 --- a/drivers/gpu/drm/nouveau/core/subdev/device/nve0.c +++ b/drivers/gpu/drm/nouveau/core/subdev/device/nve0.c @@ -24,6 +24,7 @@ #include <subdev/device.h> #include <subdev/bios.h> +#include <subdev/bus.h> #include <subdev/gpio.h> #include <subdev/i2c.h> #include <subdev/clock.h> @@ -56,13 +57,14 @@ nve0_identify(struct nouveau_device *device) case 0xe4: device->cname = "GK104"; device->oclass[NVDEV_SUBDEV_VBIOS ] = &nouveau_bios_oclass; - device->oclass[NVDEV_SUBDEV_GPIO ] = &nvd0_gpio_oclass; - device->oclass[NVDEV_SUBDEV_I2C ] = &nouveau_i2c_oclass; + device->oclass[NVDEV_SUBDEV_GPIO ] = &nve0_gpio_oclass; + device->oclass[NVDEV_SUBDEV_I2C ] = &nvd0_i2c_oclass; device->oclass[NVDEV_SUBDEV_CLOCK ] = &nvc0_clock_oclass; - device->oclass[NVDEV_SUBDEV_THERM ] = &nv50_therm_oclass; + device->oclass[NVDEV_SUBDEV_THERM ] = &nvd0_therm_oclass; device->oclass[NVDEV_SUBDEV_MXM ] = &nv50_mxm_oclass; device->oclass[NVDEV_SUBDEV_DEVINIT] = &nv50_devinit_oclass; device->oclass[NVDEV_SUBDEV_MC ] = &nvc0_mc_oclass; + device->oclass[NVDEV_SUBDEV_BUS ] = &nvc0_bus_oclass; device->oclass[NVDEV_SUBDEV_TIMER ] = &nv04_timer_oclass; device->oclass[NVDEV_SUBDEV_FB ] = &nvc0_fb_oclass; device->oclass[NVDEV_SUBDEV_LTCG ] = &nvc0_ltcg_oclass; @@ -84,13 +86,14 @@ nve0_identify(struct nouveau_device *device) case 0xe7: device->cname = "GK107"; device->oclass[NVDEV_SUBDEV_VBIOS ] = &nouveau_bios_oclass; - device->oclass[NVDEV_SUBDEV_GPIO ] = &nvd0_gpio_oclass; - device->oclass[NVDEV_SUBDEV_I2C ] = &nouveau_i2c_oclass; + device->oclass[NVDEV_SUBDEV_GPIO ] = &nve0_gpio_oclass; + device->oclass[NVDEV_SUBDEV_I2C ] = &nvd0_i2c_oclass; device->oclass[NVDEV_SUBDEV_CLOCK ] = &nvc0_clock_oclass; - device->oclass[NVDEV_SUBDEV_THERM ] = &nv50_therm_oclass; + device->oclass[NVDEV_SUBDEV_THERM ] = &nvd0_therm_oclass; device->oclass[NVDEV_SUBDEV_MXM ] = &nv50_mxm_oclass; device->oclass[NVDEV_SUBDEV_DEVINIT] = &nv50_devinit_oclass; device->oclass[NVDEV_SUBDEV_MC ] = &nvc0_mc_oclass; + device->oclass[NVDEV_SUBDEV_BUS ] = &nvc0_bus_oclass; device->oclass[NVDEV_SUBDEV_TIMER ] = &nv04_timer_oclass; device->oclass[NVDEV_SUBDEV_FB ] = &nvc0_fb_oclass; device->oclass[NVDEV_SUBDEV_LTCG ] = &nvc0_ltcg_oclass; @@ -112,13 +115,14 @@ nve0_identify(struct nouveau_device *device) case 0xe6: device->cname = "GK106"; device->oclass[NVDEV_SUBDEV_VBIOS ] = &nouveau_bios_oclass; - device->oclass[NVDEV_SUBDEV_GPIO ] = &nvd0_gpio_oclass; - device->oclass[NVDEV_SUBDEV_I2C ] = &nouveau_i2c_oclass; + device->oclass[NVDEV_SUBDEV_GPIO ] = &nve0_gpio_oclass; + device->oclass[NVDEV_SUBDEV_I2C ] = &nvd0_i2c_oclass; device->oclass[NVDEV_SUBDEV_CLOCK ] = &nvc0_clock_oclass; - device->oclass[NVDEV_SUBDEV_THERM ] = &nv50_therm_oclass; + device->oclass[NVDEV_SUBDEV_THERM ] = &nvd0_therm_oclass; device->oclass[NVDEV_SUBDEV_MXM ] = &nv50_mxm_oclass; device->oclass[NVDEV_SUBDEV_DEVINIT] = &nv50_devinit_oclass; device->oclass[NVDEV_SUBDEV_MC ] = &nvc0_mc_oclass; + device->oclass[NVDEV_SUBDEV_BUS ] = &nvc0_bus_oclass; device->oclass[NVDEV_SUBDEV_TIMER ] = &nv04_timer_oclass; device->oclass[NVDEV_SUBDEV_FB ] = &nvc0_fb_oclass; device->oclass[NVDEV_SUBDEV_LTCG ] = &nvc0_ltcg_oclass; diff --git a/drivers/gpu/drm/nouveau/core/subdev/devinit/nv50.c b/drivers/gpu/drm/nouveau/core/subdev/devinit/nv50.c index ae7249b09797..4a8577838417 100644 --- a/drivers/gpu/drm/nouveau/core/subdev/devinit/nv50.c +++ b/drivers/gpu/drm/nouveau/core/subdev/devinit/nv50.c @@ -78,12 +78,13 @@ nv50_devinit_init(struct nouveau_object *object) if (ret) return ret; - /* if we ran the init tables, execute first script pointer for each - * display table output entry that has a matching dcb entry. + /* if we ran the init tables, we have to execute the first script + * pointer of each dcb entry's display encoder table in order + * to properly initialise each encoder. */ - while (priv->base.post && ver) { - u16 data = nvbios_outp_parse(bios, i++, &ver, &hdr, &cnt, &len, &info); - if (data && dcb_outp_match(bios, info.type, info.mask, &ver, &len, &outp)) { + while (priv->base.post && dcb_outp_parse(bios, i, &ver, &hdr, &outp)) { + if (nvbios_outp_match(bios, outp.hasht, outp.hashm, + &ver, &hdr, &cnt, &len, &info)) { struct nvbios_init init = { .subdev = nv_subdev(priv), .bios = bios, @@ -95,7 +96,8 @@ nv50_devinit_init(struct nouveau_object *object) nvbios_exec(&init); } - }; + i++; + } return 0; } diff --git a/drivers/gpu/drm/nouveau/core/subdev/fb/nv50.c b/drivers/gpu/drm/nouveau/core/subdev/fb/nv50.c index 487cb8c6c204..a4338d92b02e 100644 --- a/drivers/gpu/drm/nouveau/core/subdev/fb/nv50.c +++ b/drivers/gpu/drm/nouveau/core/subdev/fb/nv50.c @@ -22,8 +22,10 @@ * Authors: Ben Skeggs */ -#include <core/object.h> +#include <core/client.h> #include <core/enum.h> +#include <core/engctx.h> +#include <core/object.h> #include <subdev/fb.h> #include <subdev/bios.h> @@ -302,17 +304,18 @@ static const struct nouveau_enum vm_client[] = { }; static const struct nouveau_enum vm_engine[] = { - { 0x00000000, "PGRAPH", NULL }, - { 0x00000001, "PVP", NULL }, + { 0x00000000, "PGRAPH", NULL, NVDEV_ENGINE_GR }, + { 0x00000001, "PVP", NULL, NVDEV_ENGINE_VP }, { 0x00000004, "PEEPHOLE", NULL }, - { 0x00000005, "PFIFO", vm_pfifo_subclients }, + { 0x00000005, "PFIFO", vm_pfifo_subclients, NVDEV_ENGINE_FIFO }, { 0x00000006, "BAR", vm_bar_subclients }, - { 0x00000008, "PPPP", NULL }, - { 0x00000009, "PBSP", NULL }, - { 0x0000000a, "PCRYPT", NULL }, + { 0x00000008, "PPPP", NULL, NVDEV_ENGINE_PPP }, + { 0x00000008, "PMPEG", NULL, NVDEV_ENGINE_MPEG }, + { 0x00000009, "PBSP", NULL, NVDEV_ENGINE_BSP }, + { 0x0000000a, "PCRYPT", NULL, NVDEV_ENGINE_CRYPT }, { 0x0000000b, "PCOUNTER", NULL }, { 0x0000000c, "SEMAPHORE_BG", NULL }, - { 0x0000000d, "PCOPY", NULL }, + { 0x0000000d, "PCOPY", NULL, NVDEV_ENGINE_COPY0 }, { 0x0000000e, "PDAEMON", NULL }, {} }; @@ -334,8 +337,10 @@ static void nv50_fb_intr(struct nouveau_subdev *subdev) { struct nouveau_device *device = nv_device(subdev); + struct nouveau_engine *engine; struct nv50_fb_priv *priv = (void *)subdev; const struct nouveau_enum *en, *cl; + struct nouveau_object *engctx = NULL; u32 trap[6], idx, chan; u8 st0, st1, st2, st3; int i; @@ -366,36 +371,55 @@ nv50_fb_intr(struct nouveau_subdev *subdev) } chan = (trap[2] << 16) | trap[1]; - nv_error(priv, "trapped %s at 0x%02x%04x%04x on channel 0x%08x ", + en = nouveau_enum_find(vm_engine, st0); + + if (en && en->data2) { + const struct nouveau_enum *orig_en = en; + while (en->name && en->value == st0 && en->data2) { + engine = nouveau_engine(subdev, en->data2); + if (engine) { + engctx = nouveau_engctx_get(engine, chan); + if (engctx) + break; + } + en++; + } + if (!engctx) + en = orig_en; + } + + nv_error(priv, "trapped %s at 0x%02x%04x%04x on channel 0x%08x [%s] ", (trap[5] & 0x00000100) ? "read" : "write", - trap[5] & 0xff, trap[4] & 0xffff, trap[3] & 0xffff, chan); + trap[5] & 0xff, trap[4] & 0xffff, trap[3] & 0xffff, chan, + nouveau_client_name(engctx)); + + nouveau_engctx_put(engctx); - en = nouveau_enum_find(vm_engine, st0); if (en) - printk("%s/", en->name); + pr_cont("%s/", en->name); else - printk("%02x/", st0); + pr_cont("%02x/", st0); cl = nouveau_enum_find(vm_client, st2); if (cl) - printk("%s/", cl->name); + pr_cont("%s/", cl->name); else - printk("%02x/", st2); + pr_cont("%02x/", st2); if (cl && cl->data) cl = nouveau_enum_find(cl->data, st3); else if (en && en->data) cl = nouveau_enum_find(en->data, st3); else cl = NULL; if (cl) - printk("%s", cl->name); + pr_cont("%s", cl->name); else - printk("%02x", st3); + pr_cont("%02x", st3); - printk(" reason: "); + pr_cont(" reason: "); en = nouveau_enum_find(vm_fault, st1); if (en) - printk("%s\n", en->name); + pr_cont("%s\n", en->name); else - printk("0x%08x\n", st1); + pr_cont("0x%08x\n", st1); } static int diff --git a/drivers/gpu/drm/nouveau/core/subdev/gpio/base.c b/drivers/gpu/drm/nouveau/core/subdev/gpio/base.c index 9fb0f9b92d49..d422acc9af15 100644 --- a/drivers/gpu/drm/nouveau/core/subdev/gpio/base.c +++ b/drivers/gpu/drm/nouveau/core/subdev/gpio/base.c @@ -102,135 +102,19 @@ nouveau_gpio_get(struct nouveau_gpio *gpio, int idx, u8 tag, u8 line) return ret; } -static int -nouveau_gpio_irq(struct nouveau_gpio *gpio, int idx, u8 tag, u8 line, bool on) -{ - struct dcb_gpio_func func; - int ret; - - ret = nouveau_gpio_find(gpio, idx, tag, line, &func); - if (ret == 0) { - if (idx == 0 && gpio->irq_enable) - gpio->irq_enable(gpio, func.line, on); - else - ret = -ENODEV; - } - - return ret; -} - -struct gpio_isr { - struct nouveau_gpio *gpio; - struct list_head head; - struct work_struct work; - int idx; - struct dcb_gpio_func func; - void (*handler)(void *, int); - void *data; - bool inhibit; -}; - -static void -nouveau_gpio_isr_bh(struct work_struct *work) -{ - struct gpio_isr *isr = container_of(work, struct gpio_isr, work); - struct nouveau_gpio *gpio = isr->gpio; - unsigned long flags; - int state; - - state = nouveau_gpio_get(gpio, isr->idx, isr->func.func, - isr->func.line); - if (state >= 0) - isr->handler(isr->data, state); - - spin_lock_irqsave(&gpio->lock, flags); - isr->inhibit = false; - spin_unlock_irqrestore(&gpio->lock, flags); -} - -static void -nouveau_gpio_isr_run(struct nouveau_gpio *gpio, int idx, u32 line_mask) -{ - struct gpio_isr *isr; - - if (idx != 0) - return; - - spin_lock(&gpio->lock); - list_for_each_entry(isr, &gpio->isr, head) { - if (line_mask & (1 << isr->func.line)) { - if (isr->inhibit) - continue; - isr->inhibit = true; - schedule_work(&isr->work); - } - } - spin_unlock(&gpio->lock); -} - -static int -nouveau_gpio_isr_add(struct nouveau_gpio *gpio, int idx, u8 tag, u8 line, - void (*handler)(void *, int), void *data) -{ - struct gpio_isr *isr; - unsigned long flags; - int ret; - - isr = kzalloc(sizeof(*isr), GFP_KERNEL); - if (!isr) - return -ENOMEM; - - ret = nouveau_gpio_find(gpio, idx, tag, line, &isr->func); - if (ret) { - kfree(isr); - return ret; - } - - INIT_WORK(&isr->work, nouveau_gpio_isr_bh); - isr->gpio = gpio; - isr->handler = handler; - isr->data = data; - isr->idx = idx; - - spin_lock_irqsave(&gpio->lock, flags); - list_add(&isr->head, &gpio->isr); - spin_unlock_irqrestore(&gpio->lock, flags); - return 0; -} - -static void -nouveau_gpio_isr_del(struct nouveau_gpio *gpio, int idx, u8 tag, u8 line, - void (*handler)(void *, int), void *data) +void +_nouveau_gpio_dtor(struct nouveau_object *object) { - struct gpio_isr *isr, *tmp; - struct dcb_gpio_func func; - unsigned long flags; - LIST_HEAD(tofree); - int ret; - - ret = nouveau_gpio_find(gpio, idx, tag, line, &func); - if (ret == 0) { - spin_lock_irqsave(&gpio->lock, flags); - list_for_each_entry_safe(isr, tmp, &gpio->isr, head) { - if (memcmp(&isr->func, &func, sizeof(func)) || - isr->idx != idx || - isr->handler != handler || isr->data != data) - continue; - list_move_tail(&isr->head, &tofree); - } - spin_unlock_irqrestore(&gpio->lock, flags); - - list_for_each_entry_safe(isr, tmp, &tofree, head) { - flush_work(&isr->work); - kfree(isr); - } - } + struct nouveau_gpio *gpio = (void *)object; + nouveau_event_destroy(&gpio->events); + nouveau_subdev_destroy(&gpio->base); } int nouveau_gpio_create_(struct nouveau_object *parent, struct nouveau_object *engine, - struct nouveau_oclass *oclass, int length, void **pobject) + struct nouveau_oclass *oclass, int lines, + int length, void **pobject) { struct nouveau_gpio *gpio; int ret; @@ -241,15 +125,13 @@ nouveau_gpio_create_(struct nouveau_object *parent, if (ret) return ret; + ret = nouveau_event_create(lines, &gpio->events); + if (ret) + return ret; + gpio->find = nouveau_gpio_find; gpio->set = nouveau_gpio_set; gpio->get = nouveau_gpio_get; - gpio->irq = nouveau_gpio_irq; - gpio->isr_run = nouveau_gpio_isr_run; - gpio->isr_add = nouveau_gpio_isr_add; - gpio->isr_del = nouveau_gpio_isr_del; - INIT_LIST_HEAD(&gpio->isr); - spin_lock_init(&gpio->lock); return 0; } diff --git a/drivers/gpu/drm/nouveau/core/subdev/gpio/nv10.c b/drivers/gpu/drm/nouveau/core/subdev/gpio/nv10.c index 168d16a9a8e9..76d5d5465ddd 100644 --- a/drivers/gpu/drm/nouveau/core/subdev/gpio/nv10.c +++ b/drivers/gpu/drm/nouveau/core/subdev/gpio/nv10.c @@ -24,7 +24,7 @@ * */ -#include <subdev/gpio.h> +#include "priv.h" struct nv10_gpio_priv { struct nouveau_gpio base; @@ -83,27 +83,36 @@ nv10_gpio_drive(struct nouveau_gpio *gpio, int line, int dir, int out) } static void -nv10_gpio_irq_enable(struct nouveau_gpio *gpio, int line, bool on) -{ - u32 mask = 0x00010001 << line; - - nv_wr32(gpio, 0x001104, mask); - nv_mask(gpio, 0x001144, mask, on ? mask : 0); -} - -static void nv10_gpio_intr(struct nouveau_subdev *subdev) { struct nv10_gpio_priv *priv = (void *)subdev; u32 intr = nv_rd32(priv, 0x001104); u32 hi = (intr & 0x0000ffff) >> 0; u32 lo = (intr & 0xffff0000) >> 16; + int i; - priv->base.isr_run(&priv->base, 0, hi | lo); + for (i = 0; (hi | lo) && i < 32; i++) { + if ((hi | lo) & (1 << i)) + nouveau_event_trigger(priv->base.events, i); + } nv_wr32(priv, 0x001104, intr); } +static void +nv10_gpio_intr_enable(struct nouveau_event *event, int line) +{ + nv_wr32(event->priv, 0x001104, 0x00010001 << line); + nv_mask(event->priv, 0x001144, 0x00010001 << line, 0x00010001 << line); +} + +static void +nv10_gpio_intr_disable(struct nouveau_event *event, int line) +{ + nv_wr32(event->priv, 0x001104, 0x00010001 << line); + nv_mask(event->priv, 0x001144, 0x00010001 << line, 0x00000000); +} + static int nv10_gpio_ctor(struct nouveau_object *parent, struct nouveau_object *engine, struct nouveau_oclass *oclass, void *data, u32 size, @@ -112,14 +121,16 @@ nv10_gpio_ctor(struct nouveau_object *parent, struct nouveau_object *engine, struct nv10_gpio_priv *priv; int ret; - ret = nouveau_gpio_create(parent, engine, oclass, &priv); + ret = nouveau_gpio_create(parent, engine, oclass, 16, &priv); *pobject = nv_object(priv); if (ret) return ret; priv->base.drive = nv10_gpio_drive; priv->base.sense = nv10_gpio_sense; - priv->base.irq_enable = nv10_gpio_irq_enable; + priv->base.events->priv = priv; + priv->base.events->enable = nv10_gpio_intr_enable; + priv->base.events->disable = nv10_gpio_intr_disable; nv_subdev(priv)->intr = nv10_gpio_intr; return 0; } @@ -141,8 +152,6 @@ nv10_gpio_init(struct nouveau_object *object) if (ret) return ret; - nv_wr32(priv, 0x001140, 0x00000000); - nv_wr32(priv, 0x001100, 0xffffffff); nv_wr32(priv, 0x001144, 0x00000000); nv_wr32(priv, 0x001104, 0xffffffff); return 0; @@ -152,7 +161,6 @@ static int nv10_gpio_fini(struct nouveau_object *object, bool suspend) { struct nv10_gpio_priv *priv = (void *)object; - nv_wr32(priv, 0x001140, 0x00000000); nv_wr32(priv, 0x001144, 0x00000000); return nouveau_gpio_fini(&priv->base, suspend); } diff --git a/drivers/gpu/drm/nouveau/core/subdev/gpio/nv50.c b/drivers/gpu/drm/nouveau/core/subdev/gpio/nv50.c index bf13a1200f26..bf489dcf46e2 100644 --- a/drivers/gpu/drm/nouveau/core/subdev/gpio/nv50.c +++ b/drivers/gpu/drm/nouveau/core/subdev/gpio/nv50.c @@ -22,7 +22,7 @@ * Authors: Ben Skeggs */ -#include <subdev/gpio.h> +#include "priv.h" struct nv50_gpio_priv { struct nouveau_gpio base; @@ -95,21 +95,12 @@ nv50_gpio_sense(struct nouveau_gpio *gpio, int line) } void -nv50_gpio_irq_enable(struct nouveau_gpio *gpio, int line, bool on) -{ - u32 reg = line < 16 ? 0xe050 : 0xe070; - u32 mask = 0x00010001 << (line & 0xf); - - nv_wr32(gpio, reg + 4, mask); - nv_mask(gpio, reg + 0, mask, on ? mask : 0); -} - -void nv50_gpio_intr(struct nouveau_subdev *subdev) { struct nv50_gpio_priv *priv = (void *)subdev; u32 intr0, intr1 = 0; u32 hi, lo; + int i; intr0 = nv_rd32(priv, 0xe054) & nv_rd32(priv, 0xe050); if (nv_device(priv)->chipset >= 0x90) @@ -117,13 +108,35 @@ nv50_gpio_intr(struct nouveau_subdev *subdev) hi = (intr0 & 0x0000ffff) | (intr1 << 16); lo = (intr0 >> 16) | (intr1 & 0xffff0000); - priv->base.isr_run(&priv->base, 0, hi | lo); + + for (i = 0; (hi | lo) && i < 32; i++) { + if ((hi | lo) & (1 << i)) + nouveau_event_trigger(priv->base.events, i); + } nv_wr32(priv, 0xe054, intr0); if (nv_device(priv)->chipset >= 0x90) nv_wr32(priv, 0xe074, intr1); } +void +nv50_gpio_intr_enable(struct nouveau_event *event, int line) +{ + const u32 addr = line < 16 ? 0xe050 : 0xe070; + const u32 mask = 0x00010001 << (line & 0xf); + nv_wr32(event->priv, addr + 0x04, mask); + nv_mask(event->priv, addr + 0x00, mask, mask); +} + +void +nv50_gpio_intr_disable(struct nouveau_event *event, int line) +{ + const u32 addr = line < 16 ? 0xe050 : 0xe070; + const u32 mask = 0x00010001 << (line & 0xf); + nv_wr32(event->priv, addr + 0x04, mask); + nv_mask(event->priv, addr + 0x00, mask, 0x00000000); +} + static int nv50_gpio_ctor(struct nouveau_object *parent, struct nouveau_object *engine, struct nouveau_oclass *oclass, void *data, u32 size, @@ -132,7 +145,9 @@ nv50_gpio_ctor(struct nouveau_object *parent, struct nouveau_object *engine, struct nv50_gpio_priv *priv; int ret; - ret = nouveau_gpio_create(parent, engine, oclass, &priv); + ret = nouveau_gpio_create(parent, engine, oclass, + nv_device(parent)->chipset >= 0x90 ? 32 : 16, + &priv); *pobject = nv_object(priv); if (ret) return ret; @@ -140,7 +155,9 @@ nv50_gpio_ctor(struct nouveau_object *parent, struct nouveau_object *engine, priv->base.reset = nv50_gpio_reset; priv->base.drive = nv50_gpio_drive; priv->base.sense = nv50_gpio_sense; - priv->base.irq_enable = nv50_gpio_irq_enable; + priv->base.events->priv = priv; + priv->base.events->enable = nv50_gpio_intr_enable; + priv->base.events->disable = nv50_gpio_intr_disable; nv_subdev(priv)->intr = nv50_gpio_intr; return 0; } diff --git a/drivers/gpu/drm/nouveau/core/subdev/gpio/nvd0.c b/drivers/gpu/drm/nouveau/core/subdev/gpio/nvd0.c index 83e8b8f16e6a..010431e3acec 100644 --- a/drivers/gpu/drm/nouveau/core/subdev/gpio/nvd0.c +++ b/drivers/gpu/drm/nouveau/core/subdev/gpio/nvd0.c @@ -22,13 +22,13 @@ * Authors: Ben Skeggs */ -#include <subdev/gpio.h> +#include "priv.h" struct nvd0_gpio_priv { struct nouveau_gpio base; }; -static void +void nvd0_gpio_reset(struct nouveau_gpio *gpio, u8 match) { struct nouveau_bios *bios = nouveau_bios(gpio); @@ -57,7 +57,7 @@ nvd0_gpio_reset(struct nouveau_gpio *gpio, u8 match) } } -static int +int nvd0_gpio_drive(struct nouveau_gpio *gpio, int line, int dir, int out) { u32 data = ((dir ^ 1) << 13) | (out << 12); @@ -66,7 +66,7 @@ nvd0_gpio_drive(struct nouveau_gpio *gpio, int line, int dir, int out) return 0; } -static int +int nvd0_gpio_sense(struct nouveau_gpio *gpio, int line) { return !!(nv_rd32(gpio, 0x00d610 + (line * 4)) & 0x00004000); @@ -80,7 +80,7 @@ nvd0_gpio_ctor(struct nouveau_object *parent, struct nouveau_object *engine, struct nvd0_gpio_priv *priv; int ret; - ret = nouveau_gpio_create(parent, engine, oclass, &priv); + ret = nouveau_gpio_create(parent, engine, oclass, 32, &priv); *pobject = nv_object(priv); if (ret) return ret; @@ -88,7 +88,9 @@ nvd0_gpio_ctor(struct nouveau_object *parent, struct nouveau_object *engine, priv->base.reset = nvd0_gpio_reset; priv->base.drive = nvd0_gpio_drive; priv->base.sense = nvd0_gpio_sense; - priv->base.irq_enable = nv50_gpio_irq_enable; + priv->base.events->priv = priv; + priv->base.events->enable = nv50_gpio_intr_enable; + priv->base.events->disable = nv50_gpio_intr_disable; nv_subdev(priv)->intr = nv50_gpio_intr; return 0; } diff --git a/drivers/gpu/drm/nouveau/core/subdev/gpio/nve0.c b/drivers/gpu/drm/nouveau/core/subdev/gpio/nve0.c new file mode 100644 index 000000000000..16b8c5bf5efa --- /dev/null +++ b/drivers/gpu/drm/nouveau/core/subdev/gpio/nve0.c @@ -0,0 +1,131 @@ +/* + * Copyright 2012 Red Hat Inc. + * + * 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 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 + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: Ben Skeggs + */ + +#include "priv.h" + +struct nve0_gpio_priv { + struct nouveau_gpio base; +}; + +void +nve0_gpio_intr(struct nouveau_subdev *subdev) +{ + struct nve0_gpio_priv *priv = (void *)subdev; + u32 intr0 = nv_rd32(priv, 0xdc00) & nv_rd32(priv, 0xdc08); + u32 intr1 = nv_rd32(priv, 0xdc80) & nv_rd32(priv, 0xdc88); + u32 hi = (intr0 & 0x0000ffff) | (intr1 << 16); + u32 lo = (intr0 >> 16) | (intr1 & 0xffff0000); + int i; + + for (i = 0; (hi | lo) && i < 32; i++) { + if ((hi | lo) & (1 << i)) + nouveau_event_trigger(priv->base.events, i); + } + + nv_wr32(priv, 0xdc00, intr0); + nv_wr32(priv, 0xdc88, intr1); +} + +void +nve0_gpio_intr_enable(struct nouveau_event *event, int line) +{ + const u32 addr = line < 16 ? 0xdc00 : 0xdc80; + const u32 mask = 0x00010001 << (line & 0xf); + nv_wr32(event->priv, addr + 0x08, mask); + nv_mask(event->priv, addr + 0x00, mask, mask); +} + +void +nve0_gpio_intr_disable(struct nouveau_event *event, int line) +{ + const u32 addr = line < 16 ? 0xdc00 : 0xdc80; + const u32 mask = 0x00010001 << (line & 0xf); + nv_wr32(event->priv, addr + 0x08, mask); + nv_mask(event->priv, addr + 0x00, mask, 0x00000000); +} + +int +nve0_gpio_fini(struct nouveau_object *object, bool suspend) +{ + struct nve0_gpio_priv *priv = (void *)object; + nv_wr32(priv, 0xdc08, 0x00000000); + nv_wr32(priv, 0xdc88, 0x00000000); + return nouveau_gpio_fini(&priv->base, suspend); +} + +int +nve0_gpio_init(struct nouveau_object *object) +{ + struct nve0_gpio_priv *priv = (void *)object; + int ret; + + ret = nouveau_gpio_init(&priv->base); + if (ret) + return ret; + + nv_wr32(priv, 0xdc00, 0xffffffff); + nv_wr32(priv, 0xdc80, 0xffffffff); + return 0; +} + +void +nve0_gpio_dtor(struct nouveau_object *object) +{ + struct nve0_gpio_priv *priv = (void *)object; + nouveau_gpio_destroy(&priv->base); +} + +static int +nve0_gpio_ctor(struct nouveau_object *parent, struct nouveau_object *engine, + struct nouveau_oclass *oclass, void *data, u32 size, + struct nouveau_object **pobject) +{ + struct nve0_gpio_priv *priv; + int ret; + + ret = nouveau_gpio_create(parent, engine, oclass, 32, &priv); + *pobject = nv_object(priv); + if (ret) + return ret; + + priv->base.reset = nvd0_gpio_reset; + priv->base.drive = nvd0_gpio_drive; + priv->base.sense = nvd0_gpio_sense; + priv->base.events->priv = priv; + priv->base.events->enable = nve0_gpio_intr_enable; + priv->base.events->disable = nve0_gpio_intr_disable; + nv_subdev(priv)->intr = nve0_gpio_intr; + return 0; +} + +struct nouveau_oclass +nve0_gpio_oclass = { + .handle = NV_SUBDEV(GPIO, 0xe0), + .ofuncs = &(struct nouveau_ofuncs) { + .ctor = nve0_gpio_ctor, + .dtor = nv50_gpio_dtor, + .init = nve0_gpio_init, + .fini = nve0_gpio_fini, + }, +}; diff --git a/drivers/gpu/drm/nouveau/core/subdev/gpio/priv.h b/drivers/gpu/drm/nouveau/core/subdev/gpio/priv.h new file mode 100644 index 000000000000..2ee1c895c782 --- /dev/null +++ b/drivers/gpu/drm/nouveau/core/subdev/gpio/priv.h @@ -0,0 +1,17 @@ +#ifndef __NVKM_GPIO_H__ +#define __NVKM_GPIO_H__ + +#include <subdev/gpio.h> + +void nv50_gpio_dtor(struct nouveau_object *); +int nv50_gpio_init(struct nouveau_object *); +int nv50_gpio_fini(struct nouveau_object *, bool); +void nv50_gpio_intr(struct nouveau_subdev *); +void nv50_gpio_intr_enable(struct nouveau_event *, int line); +void nv50_gpio_intr_disable(struct nouveau_event *, int line); + +void nvd0_gpio_reset(struct nouveau_gpio *, u8); +int nvd0_gpio_drive(struct nouveau_gpio *, int, int, int); +int nvd0_gpio_sense(struct nouveau_gpio *, int); + +#endif diff --git a/drivers/gpu/drm/nouveau/core/subdev/i2c/anx9805.c b/drivers/gpu/drm/nouveau/core/subdev/i2c/anx9805.c new file mode 100644 index 000000000000..dec94e9d776a --- /dev/null +++ b/drivers/gpu/drm/nouveau/core/subdev/i2c/anx9805.c @@ -0,0 +1,279 @@ +/* + * Copyright 2013 Red Hat Inc. + * + * 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 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 + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: Ben Skeggs <bskeggs@redhat.com> + */ + +#include <subdev/i2c.h> + +struct anx9805_i2c_port { + struct nouveau_i2c_port base; + u32 addr; + u32 ctrl; +}; + +static int +anx9805_train(struct nouveau_i2c_port *port, int link_nr, int link_bw, bool enh) +{ + struct anx9805_i2c_port *chan = (void *)port; + struct nouveau_i2c_port *mast = (void *)nv_object(chan)->parent; + u8 tmp, i; + + nv_wri2cr(mast, chan->addr, 0xa0, link_bw); + nv_wri2cr(mast, chan->addr, 0xa1, link_nr | (enh ? 0x80 : 0x00)); + nv_wri2cr(mast, chan->addr, 0xa2, 0x01); + nv_wri2cr(mast, chan->addr, 0xa8, 0x01); + + i = 0; + while ((tmp = nv_rdi2cr(mast, chan->addr, 0xa8)) & 0x01) { + mdelay(5); + if (i++ == 100) { + nv_error(port, "link training timed out\n"); + return -ETIMEDOUT; + } + } + + if (tmp & 0x70) { + nv_error(port, "link training failed: 0x%02x\n", tmp); + return -EIO; + } + + return 1; +} + +static int +anx9805_aux(struct nouveau_i2c_port *port, u8 type, u32 addr, u8 *data, u8 size) +{ + struct anx9805_i2c_port *chan = (void *)port; + struct nouveau_i2c_port *mast = (void *)nv_object(chan)->parent; + int i, ret = -ETIMEDOUT; + u8 tmp; + + tmp = nv_rdi2cr(mast, chan->ctrl, 0x07) & ~0x04; + nv_wri2cr(mast, chan->ctrl, 0x07, tmp | 0x04); + nv_wri2cr(mast, chan->ctrl, 0x07, tmp); + nv_wri2cr(mast, chan->ctrl, 0xf7, 0x01); + + nv_wri2cr(mast, chan->addr, 0xe4, 0x80); + for (i = 0; !(type & 1) && i < size; i++) + nv_wri2cr(mast, chan->addr, 0xf0 + i, data[i]); + nv_wri2cr(mast, chan->addr, 0xe5, ((size - 1) << 4) | type); + nv_wri2cr(mast, chan->addr, 0xe6, (addr & 0x000ff) >> 0); + nv_wri2cr(mast, chan->addr, 0xe7, (addr & 0x0ff00) >> 8); + nv_wri2cr(mast, chan->addr, 0xe8, (addr & 0xf0000) >> 16); + nv_wri2cr(mast, chan->addr, 0xe9, 0x01); + + i = 0; + while ((tmp = nv_rdi2cr(mast, chan->addr, 0xe9)) & 0x01) { + mdelay(5); + if (i++ == 32) + goto done; + } + + if ((tmp = nv_rdi2cr(mast, chan->ctrl, 0xf7)) & 0x01) { + ret = -EIO; + goto done; + } + + for (i = 0; (type & 1) && i < size; i++) + data[i] = nv_rdi2cr(mast, chan->addr, 0xf0 + i); + ret = 0; +done: + nv_wri2cr(mast, chan->ctrl, 0xf7, 0x01); + return ret; +} + +static const struct nouveau_i2c_func +anx9805_aux_func = { + .aux = anx9805_aux, + .lnk_ctl = anx9805_train, +}; + +static int +anx9805_aux_chan_ctor(struct nouveau_object *parent, + struct nouveau_object *engine, + struct nouveau_oclass *oclass, void *data, u32 index, + struct nouveau_object **pobject) +{ + struct nouveau_i2c_port *mast = (void *)parent; + struct anx9805_i2c_port *chan; + int ret; + + ret = nouveau_i2c_port_create(parent, engine, oclass, index, + &nouveau_i2c_aux_algo, &chan); + *pobject = nv_object(chan); + if (ret) + return ret; + + switch ((oclass->handle & 0xff00) >> 8) { + case 0x0d: + chan->addr = 0x38; + chan->ctrl = 0x39; + break; + case 0x0e: + chan->addr = 0x3c; + chan->ctrl = 0x3b; + break; + default: + BUG_ON(1); + } + + if (mast->adapter.algo == &i2c_bit_algo) { + struct i2c_algo_bit_data *algo = mast->adapter.algo_data; + algo->udelay = max(algo->udelay, 40); + } + + chan->base.func = &anx9805_aux_func; + return 0; +} + +static struct nouveau_ofuncs +anx9805_aux_ofuncs = { + .ctor = anx9805_aux_chan_ctor, + .dtor = _nouveau_i2c_port_dtor, + .init = _nouveau_i2c_port_init, + .fini = _nouveau_i2c_port_fini, +}; + +static int +anx9805_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num) +{ + struct anx9805_i2c_port *port = adap->algo_data; + struct nouveau_i2c_port *mast = (void *)nv_object(port)->parent; + struct i2c_msg *msg = msgs; + int ret = -ETIMEDOUT; + int i, j, cnt = num; + u8 seg = 0x00, off = 0x00, tmp; + + tmp = nv_rdi2cr(mast, port->ctrl, 0x07) & ~0x10; + nv_wri2cr(mast, port->ctrl, 0x07, tmp | 0x10); + nv_wri2cr(mast, port->ctrl, 0x07, tmp); + nv_wri2cr(mast, port->addr, 0x43, 0x05); + mdelay(5); + + while (cnt--) { + if ( (msg->flags & I2C_M_RD) && msg->addr == 0x50) { + nv_wri2cr(mast, port->addr, 0x40, msg->addr << 1); + nv_wri2cr(mast, port->addr, 0x41, seg); + nv_wri2cr(mast, port->addr, 0x42, off); + nv_wri2cr(mast, port->addr, 0x44, msg->len); + nv_wri2cr(mast, port->addr, 0x45, 0x00); + nv_wri2cr(mast, port->addr, 0x43, 0x01); + for (i = 0; i < msg->len; i++) { + j = 0; + while (nv_rdi2cr(mast, port->addr, 0x46) & 0x10) { + mdelay(5); + if (j++ == 32) + goto done; + } + msg->buf[i] = nv_rdi2cr(mast, port->addr, 0x47); + } + } else + if (!(msg->flags & I2C_M_RD)) { + if (msg->addr == 0x50 && msg->len == 0x01) { + off = msg->buf[0]; + } else + if (msg->addr == 0x30 && msg->len == 0x01) { + seg = msg->buf[0]; + } else + goto done; + } else { + goto done; + } + msg++; + } + + ret = num; +done: + nv_wri2cr(mast, port->addr, 0x43, 0x00); + return ret; +} + +static u32 +anx9805_func(struct i2c_adapter *adap) +{ + return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL; +} + +static const struct i2c_algorithm +anx9805_i2c_algo = { + .master_xfer = anx9805_xfer, + .functionality = anx9805_func +}; + +static const struct nouveau_i2c_func +anx9805_i2c_func = { +}; + +static int +anx9805_ddc_port_ctor(struct nouveau_object *parent, + struct nouveau_object *engine, + struct nouveau_oclass *oclass, void *data, u32 index, + struct nouveau_object **pobject) +{ + struct nouveau_i2c_port *mast = (void *)parent; + struct anx9805_i2c_port *port; + int ret; + + ret = nouveau_i2c_port_create(parent, engine, oclass, index, + &anx9805_i2c_algo, &port); + *pobject = nv_object(port); + if (ret) + return ret; + + switch ((oclass->handle & 0xff00) >> 8) { + case 0x0d: + port->addr = 0x3d; + port->ctrl = 0x39; + break; + case 0x0e: + port->addr = 0x3f; + port->ctrl = 0x3b; + break; + default: + BUG_ON(1); + } + + if (mast->adapter.algo == &i2c_bit_algo) { + struct i2c_algo_bit_data *algo = mast->adapter.algo_data; + algo->udelay = max(algo->udelay, 40); + } + + port->base.func = &anx9805_i2c_func; + return 0; +} + +static struct nouveau_ofuncs +anx9805_ddc_ofuncs = { + .ctor = anx9805_ddc_port_ctor, + .dtor = _nouveau_i2c_port_dtor, + .init = _nouveau_i2c_port_init, + .fini = _nouveau_i2c_port_fini, +}; + +struct nouveau_oclass +nouveau_anx9805_sclass[] = { + { .handle = NV_I2C_TYPE_EXTDDC(0x0d), .ofuncs = &anx9805_ddc_ofuncs }, + { .handle = NV_I2C_TYPE_EXTAUX(0x0d), .ofuncs = &anx9805_aux_ofuncs }, + { .handle = NV_I2C_TYPE_EXTDDC(0x0e), .ofuncs = &anx9805_ddc_ofuncs }, + { .handle = NV_I2C_TYPE_EXTAUX(0x0e), .ofuncs = &anx9805_aux_ofuncs }, + {} +}; diff --git a/drivers/gpu/drm/nouveau/core/subdev/i2c/aux.c b/drivers/gpu/drm/nouveau/core/subdev/i2c/aux.c index dc27e794a851..5de074ad170b 100644 --- a/drivers/gpu/drm/nouveau/core/subdev/i2c/aux.c +++ b/drivers/gpu/drm/nouveau/core/subdev/i2c/aux.c @@ -24,151 +24,40 @@ #include <subdev/i2c.h> -/****************************************************************************** - * aux channel util functions - *****************************************************************************/ -#define AUX_DBG(fmt, args...) nv_debug(aux, "AUXCH(%d): " fmt, ch, ##args) -#define AUX_ERR(fmt, args...) nv_error(aux, "AUXCH(%d): " fmt, ch, ##args) - -static void -auxch_fini(struct nouveau_i2c *aux, int ch) -{ - nv_mask(aux, 0x00e4e4 + (ch * 0x50), 0x00310000, 0x00000000); -} - -static int -auxch_init(struct nouveau_i2c *aux, int ch) -{ - const u32 unksel = 1; /* nfi which to use, or if it matters.. */ - const u32 ureq = unksel ? 0x00100000 : 0x00200000; - const u32 urep = unksel ? 0x01000000 : 0x02000000; - u32 ctrl, timeout; - - /* wait up to 1ms for any previous transaction to be done... */ - timeout = 1000; - do { - ctrl = nv_rd32(aux, 0x00e4e4 + (ch * 0x50)); - udelay(1); - if (!timeout--) { - AUX_ERR("begin idle timeout 0x%08x\n", ctrl); - return -EBUSY; - } - } while (ctrl & 0x03010000); - - /* set some magic, and wait up to 1ms for it to appear */ - nv_mask(aux, 0x00e4e4 + (ch * 0x50), 0x00300000, ureq); - timeout = 1000; - do { - ctrl = nv_rd32(aux, 0x00e4e4 + (ch * 0x50)); - udelay(1); - if (!timeout--) { - AUX_ERR("magic wait 0x%08x\n", ctrl); - auxch_fini(aux, ch); - return -EBUSY; - } - } while ((ctrl & 0x03000000) != urep); - - return 0; -} - -static int -auxch_tx(struct nouveau_i2c *aux, int ch, u8 type, u32 addr, u8 *data, u8 size) -{ - u32 ctrl, stat, timeout, retries; - u32 xbuf[4] = {}; - int ret, i; - - AUX_DBG("%d: 0x%08x %d\n", type, addr, size); - - ret = auxch_init(aux, ch); - if (ret) - goto out; - - stat = nv_rd32(aux, 0x00e4e8 + (ch * 0x50)); - if (!(stat & 0x10000000)) { - AUX_DBG("sink not detected\n"); - ret = -ENXIO; - goto out; - } - - if (!(type & 1)) { - memcpy(xbuf, data, size); - for (i = 0; i < 16; i += 4) { - AUX_DBG("wr 0x%08x\n", xbuf[i / 4]); - nv_wr32(aux, 0x00e4c0 + (ch * 0x50) + i, xbuf[i / 4]); - } - } - - ctrl = nv_rd32(aux, 0x00e4e4 + (ch * 0x50)); - ctrl &= ~0x0001f0ff; - ctrl |= type << 12; - ctrl |= size - 1; - nv_wr32(aux, 0x00e4e0 + (ch * 0x50), addr); - - /* retry transaction a number of times on failure... */ - ret = -EREMOTEIO; - for (retries = 0; retries < 32; retries++) { - /* reset, and delay a while if this is a retry */ - nv_wr32(aux, 0x00e4e4 + (ch * 0x50), 0x80000000 | ctrl); - nv_wr32(aux, 0x00e4e4 + (ch * 0x50), 0x00000000 | ctrl); - if (retries) - udelay(400); - - /* transaction request, wait up to 1ms for it to complete */ - nv_wr32(aux, 0x00e4e4 + (ch * 0x50), 0x00010000 | ctrl); - - timeout = 1000; - do { - ctrl = nv_rd32(aux, 0x00e4e4 + (ch * 0x50)); - udelay(1); - if (!timeout--) { - AUX_ERR("tx req timeout 0x%08x\n", ctrl); - goto out; - } - } while (ctrl & 0x00010000); - - /* read status, and check if transaction completed ok */ - stat = nv_mask(aux, 0x00e4e8 + (ch * 0x50), 0, 0); - if (!(stat & 0x000f0f00)) { - ret = 0; - break; - } - - AUX_DBG("%02d 0x%08x 0x%08x\n", retries, ctrl, stat); - } - - if (type & 1) { - for (i = 0; i < 16; i += 4) { - xbuf[i / 4] = nv_rd32(aux, 0x00e4d0 + (ch * 0x50) + i); - AUX_DBG("rd 0x%08x\n", xbuf[i / 4]); - } - memcpy(data, xbuf, size); - } - -out: - auxch_fini(aux, ch); - return ret; -} - int -nv_rdaux(struct nouveau_i2c_port *auxch, u32 addr, u8 *data, u8 size) +nv_rdaux(struct nouveau_i2c_port *port, u32 addr, u8 *data, u8 size) { - return auxch_tx(auxch->i2c, auxch->drive, 9, addr, data, size); + if (port->func->aux) { + if (port->func->acquire) + port->func->acquire(port); + return port->func->aux(port, 9, addr, data, size); + } + return -ENODEV; } int -nv_wraux(struct nouveau_i2c_port *auxch, u32 addr, u8 *data, u8 size) +nv_wraux(struct nouveau_i2c_port *port, u32 addr, u8 *data, u8 size) { - return auxch_tx(auxch->i2c, auxch->drive, 8, addr, data, size); + if (port->func->aux) { + if (port->func->acquire) + port->func->acquire(port); + return port->func->aux(port, 8, addr, data, size); + } + return -ENODEV; } static int aux_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num) { - struct nouveau_i2c_port *auxch = (struct nouveau_i2c_port *)adap; + struct nouveau_i2c_port *port = adap->algo_data; struct i2c_msg *msg = msgs; int ret, mcnt = num; + if (!port->func->aux) + return -ENODEV; + if ( port->func->acquire) + port->func->acquire(port); + while (mcnt--) { u8 remaining = msg->len; u8 *ptr = msg->buf; @@ -185,8 +74,7 @@ aux_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num) if (mcnt || remaining > 16) cmd |= 4; /* MOT */ - ret = auxch_tx(auxch->i2c, auxch->drive, cmd, - msg->addr, ptr, cnt); + ret = port->func->aux(port, cmd, msg->addr, ptr, cnt); if (ret < 0) return ret; diff --git a/drivers/gpu/drm/nouveau/core/subdev/i2c/base.c b/drivers/gpu/drm/nouveau/core/subdev/i2c/base.c index dbfc2abf0cfe..a114a0ed7e98 100644 --- a/drivers/gpu/drm/nouveau/core/subdev/i2c/base.c +++ b/drivers/gpu/drm/nouveau/core/subdev/i2c/base.c @@ -1,5 +1,5 @@ /* - * Copyright 2012 Red Hat Inc. + * Copyright 2013 Red Hat Inc. * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), @@ -22,64 +22,136 @@ * Authors: Ben Skeggs */ -#include "core/option.h" +#include <core/option.h> -#include "subdev/i2c.h" -#include "subdev/vga.h" +#include <subdev/bios.h> +#include <subdev/bios/dcb.h> +#include <subdev/bios/i2c.h> +#include <subdev/i2c.h> +#include <subdev/vga.h> -int -nv_rdi2cr(struct nouveau_i2c_port *port, u8 addr, u8 reg) +/****************************************************************************** + * interface to linux i2c bit-banging algorithm + *****************************************************************************/ + +#ifdef CONFIG_NOUVEAU_I2C_INTERNAL_DEFAULT +#define CSTMSEL true +#else +#define CSTMSEL false +#endif + +static int +nouveau_i2c_pre_xfer(struct i2c_adapter *adap) { - u8 val; - struct i2c_msg msgs[] = { - { .addr = addr, .flags = 0, .len = 1, .buf = ® }, - { .addr = addr, .flags = I2C_M_RD, .len = 1, .buf = &val }, - }; + struct i2c_algo_bit_data *bit = adap->algo_data; + struct nouveau_i2c_port *port = bit->data; + if (port->func->acquire) + port->func->acquire(port); + return 0; +} - int ret = i2c_transfer(&port->adapter, msgs, 2); - if (ret != 2) - return -EIO; +static void +nouveau_i2c_setscl(void *data, int state) +{ + struct nouveau_i2c_port *port = data; + port->func->drive_scl(port, state); +} - return val; +static void +nouveau_i2c_setsda(void *data, int state) +{ + struct nouveau_i2c_port *port = data; + port->func->drive_sda(port, state); } -int -nv_wri2cr(struct nouveau_i2c_port *port, u8 addr, u8 reg, u8 val) +static int +nouveau_i2c_getscl(void *data) { - struct i2c_msg msgs[] = { - { .addr = addr, .flags = 0, .len = 1, .buf = ® }, - { .addr = addr, .flags = 0, .len = 1, .buf = &val }, - }; + struct nouveau_i2c_port *port = data; + return port->func->sense_scl(port); +} - int ret = i2c_transfer(&port->adapter, msgs, 2); - if (ret != 2) - return -EIO; +static int +nouveau_i2c_getsda(void *data) +{ + struct nouveau_i2c_port *port = data; + return port->func->sense_sda(port); +} - return 0; +/****************************************************************************** + * base i2c "port" class implementation + *****************************************************************************/ + +void +_nouveau_i2c_port_dtor(struct nouveau_object *object) +{ + struct nouveau_i2c_port *port = (void *)object; + i2c_del_adapter(&port->adapter); + nouveau_object_destroy(&port->base); } -bool -nv_probe_i2c(struct nouveau_i2c_port *port, u8 addr) +int +nouveau_i2c_port_create_(struct nouveau_object *parent, + struct nouveau_object *engine, + struct nouveau_oclass *oclass, u8 index, + const struct i2c_algorithm *algo, + int size, void **pobject) { - u8 buf[] = { 0 }; - struct i2c_msg msgs[] = { - { - .addr = addr, - .flags = 0, - .len = 1, - .buf = buf, - }, - { - .addr = addr, - .flags = I2C_M_RD, - .len = 1, - .buf = buf, - } - }; + struct nouveau_device *device = nv_device(parent); + struct nouveau_i2c *i2c = (void *)engine; + struct nouveau_i2c_port *port; + int ret; - return i2c_transfer(&port->adapter, msgs, 2) == 2; + ret = nouveau_object_create_(parent, engine, oclass, 0, size, pobject); + port = *pobject; + if (ret) + return ret; + + snprintf(port->adapter.name, sizeof(port->adapter.name), + "nouveau-%s-%d", device->name, index); + port->adapter.owner = THIS_MODULE; + port->adapter.dev.parent = &device->pdev->dev; + port->index = index; + i2c_set_adapdata(&port->adapter, i2c); + + if ( algo == &nouveau_i2c_bit_algo && + !nouveau_boolopt(device->cfgopt, "NvI2C", CSTMSEL)) { + struct i2c_algo_bit_data *bit; + + bit = kzalloc(sizeof(*bit), GFP_KERNEL); + if (!bit) + return -ENOMEM; + + bit->udelay = 10; + bit->timeout = usecs_to_jiffies(2200); + bit->data = port; + bit->pre_xfer = nouveau_i2c_pre_xfer; + bit->setsda = nouveau_i2c_setsda; + bit->setscl = nouveau_i2c_setscl; + bit->getsda = nouveau_i2c_getsda; + bit->getscl = nouveau_i2c_getscl; + + port->adapter.algo_data = bit; + ret = i2c_bit_add_bus(&port->adapter); + } else { + port->adapter.algo_data = port; + port->adapter.algo = algo; + ret = i2c_add_adapter(&port->adapter); + } + + /* drop port's i2c subdev refcount, i2c handles this itself */ + if (ret == 0) { + list_add_tail(&port->head, &i2c->ports); + atomic_dec(&engine->refcount); + } + + return ret; } +/****************************************************************************** + * base i2c subdev class implementation + *****************************************************************************/ + static struct nouveau_i2c_port * nouveau_i2c_find(struct nouveau_i2c *i2c, u8 index) { @@ -103,29 +175,23 @@ nouveau_i2c_find(struct nouveau_i2c *i2c, u8 index) list_for_each_entry(port, &i2c->ports, head) { if (port->index == index) - break; + return port; } - if (&port->head == &i2c->ports) - return NULL; + return NULL; +} - if (nv_device(i2c)->card_type >= NV_50 && (port->dcb & 0x00000100)) { - u32 reg = 0x00e500, val; - if (port->type == 6) { - reg += port->drive * 0x50; - val = 0x2002; - } else { - reg += ((port->dcb & 0x1e00) >> 9) * 0x50; - val = 0xe001; - } +static struct nouveau_i2c_port * +nouveau_i2c_find_type(struct nouveau_i2c *i2c, u16 type) +{ + struct nouveau_i2c_port *port; - /* nfi, but neither auxch or i2c work if it's 1 */ - nv_mask(i2c, reg + 0x0c, 0x00000001, 0x00000000); - /* nfi, but switches auxch vs normal i2c */ - nv_mask(i2c, reg + 0x00, 0x0000f003, val); + list_for_each_entry(port, &i2c->ports, head) { + if (nv_hclass(port) == type) + return port; } - return port; + return NULL; } static int @@ -155,109 +221,86 @@ nouveau_i2c_identify(struct nouveau_i2c *i2c, int index, const char *what, return -ENODEV; } -void -nouveau_i2c_drive_scl(void *data, int state) +int +_nouveau_i2c_fini(struct nouveau_object *object, bool suspend) { - struct nouveau_i2c_port *port = data; + struct nouveau_i2c *i2c = (void *)object; + struct nouveau_i2c_port *port; + int ret; - if (port->type == DCB_I2C_NV04_BIT) { - u8 val = nv_rdvgac(port->i2c, 0, port->drive); - if (state) val |= 0x20; - else val &= 0xdf; - nv_wrvgac(port->i2c, 0, port->drive, val | 0x01); - } else - if (port->type == DCB_I2C_NV4E_BIT) { - nv_mask(port->i2c, port->drive, 0x2f, state ? 0x21 : 0x01); - } else - if (port->type == DCB_I2C_NVIO_BIT) { - if (state) port->state |= 0x01; - else port->state &= 0xfe; - nv_wr32(port->i2c, port->drive, 4 | port->state); + list_for_each_entry(port, &i2c->ports, head) { + ret = nv_ofuncs(port)->fini(nv_object(port), suspend); + if (ret && suspend) + goto fail; } -} - -void -nouveau_i2c_drive_sda(void *data, int state) -{ - struct nouveau_i2c_port *port = data; - if (port->type == DCB_I2C_NV04_BIT) { - u8 val = nv_rdvgac(port->i2c, 0, port->drive); - if (state) val |= 0x10; - else val &= 0xef; - nv_wrvgac(port->i2c, 0, port->drive, val | 0x01); - } else - if (port->type == DCB_I2C_NV4E_BIT) { - nv_mask(port->i2c, port->drive, 0x1f, state ? 0x11 : 0x01); - } else - if (port->type == DCB_I2C_NVIO_BIT) { - if (state) port->state |= 0x02; - else port->state &= 0xfd; - nv_wr32(port->i2c, port->drive, 4 | port->state); + return nouveau_subdev_fini(&i2c->base, suspend); +fail: + list_for_each_entry_continue_reverse(port, &i2c->ports, head) { + nv_ofuncs(port)->init(nv_object(port)); } + + return ret; } int -nouveau_i2c_sense_scl(void *data) +_nouveau_i2c_init(struct nouveau_object *object) { - struct nouveau_i2c_port *port = data; - struct nouveau_device *device = nv_device(port->i2c); - - if (port->type == DCB_I2C_NV04_BIT) { - return !!(nv_rdvgac(port->i2c, 0, port->sense) & 0x04); - } else - if (port->type == DCB_I2C_NV4E_BIT) { - return !!(nv_rd32(port->i2c, port->sense) & 0x00040000); - } else - if (port->type == DCB_I2C_NVIO_BIT) { - if (device->card_type < NV_D0) - return !!(nv_rd32(port->i2c, port->sense) & 0x01); - else - return !!(nv_rd32(port->i2c, port->sense) & 0x10); + struct nouveau_i2c *i2c = (void *)object; + struct nouveau_i2c_port *port; + int ret; + + ret = nouveau_subdev_init(&i2c->base); + if (ret == 0) { + list_for_each_entry(port, &i2c->ports, head) { + ret = nv_ofuncs(port)->init(nv_object(port)); + if (ret) + goto fail; + } } - return 0; + return ret; +fail: + list_for_each_entry_continue_reverse(port, &i2c->ports, head) { + nv_ofuncs(port)->fini(nv_object(port), false); + } + + return ret; } -int -nouveau_i2c_sense_sda(void *data) +void +_nouveau_i2c_dtor(struct nouveau_object *object) { - struct nouveau_i2c_port *port = data; - struct nouveau_device *device = nv_device(port->i2c); - - if (port->type == DCB_I2C_NV04_BIT) { - return !!(nv_rdvgac(port->i2c, 0, port->sense) & 0x08); - } else - if (port->type == DCB_I2C_NV4E_BIT) { - return !!(nv_rd32(port->i2c, port->sense) & 0x00080000); - } else - if (port->type == DCB_I2C_NVIO_BIT) { - if (device->card_type < NV_D0) - return !!(nv_rd32(port->i2c, port->sense) & 0x02); - else - return !!(nv_rd32(port->i2c, port->sense) & 0x20); + struct nouveau_i2c *i2c = (void *)object; + struct nouveau_i2c_port *port, *temp; + + list_for_each_entry_safe(port, temp, &i2c->ports, head) { + nouveau_object_ref(NULL, (struct nouveau_object **)&port); } - return 0; + nouveau_subdev_destroy(&i2c->base); } -static const u32 nv50_i2c_port[] = { - 0x00e138, 0x00e150, 0x00e168, 0x00e180, - 0x00e254, 0x00e274, 0x00e764, 0x00e780, - 0x00e79c, 0x00e7b8 +static struct nouveau_oclass * +nouveau_i2c_extdev_sclass[] = { + nouveau_anx9805_sclass, }; -static int -nouveau_i2c_ctor(struct nouveau_object *parent, struct nouveau_object *engine, - struct nouveau_oclass *oclass, void *data, u32 size, - struct nouveau_object **pobject) +int +nouveau_i2c_create_(struct nouveau_object *parent, + struct nouveau_object *engine, + struct nouveau_oclass *oclass, + struct nouveau_oclass *sclass, + int length, void **pobject) { - struct nouveau_device *device = nv_device(parent); struct nouveau_bios *bios = nouveau_bios(parent); - struct nouveau_i2c_port *port; struct nouveau_i2c *i2c; + struct nouveau_object *object; struct dcb_i2c_entry info; - int ret, i = -1; + int ret, i, j, index = -1; + struct dcb_output outp; + u8 ver, hdr; + u32 data; ret = nouveau_subdev_create(parent, engine, oclass, 0, "I2C", "i2c", &i2c); @@ -266,142 +309,60 @@ nouveau_i2c_ctor(struct nouveau_object *parent, struct nouveau_object *engine, return ret; i2c->find = nouveau_i2c_find; + i2c->find_type = nouveau_i2c_find_type; i2c->identify = nouveau_i2c_identify; INIT_LIST_HEAD(&i2c->ports); - while (!dcb_i2c_parse(bios, ++i, &info)) { + while (!dcb_i2c_parse(bios, ++index, &info)) { if (info.type == DCB_I2C_UNUSED) continue; - port = kzalloc(sizeof(*port), GFP_KERNEL); - if (!port) { - nv_error(i2c, "failed port memory alloc at %d\n", i); - break; - } - - port->type = info.type; - switch (port->type) { - case DCB_I2C_NV04_BIT: - port->drive = info.drive; - port->sense = info.sense; - break; - case DCB_I2C_NV4E_BIT: - port->drive = 0x600800 + info.drive; - port->sense = port->drive; - break; - case DCB_I2C_NVIO_BIT: - port->drive = info.drive & 0x0f; - if (device->card_type < NV_D0) { - if (port->drive >= ARRAY_SIZE(nv50_i2c_port)) - break; - port->drive = nv50_i2c_port[port->drive]; - port->sense = port->drive; - } else { - port->drive = 0x00d014 + (port->drive * 0x20); - port->sense = port->drive; + oclass = sclass; + do { + ret = -EINVAL; + if (oclass->handle == info.type) { + ret = nouveau_object_ctor(*pobject, *pobject, + oclass, &info, + index, &object); } + } while (ret && (++oclass)->handle); + } + + /* in addition to the busses specified in the i2c table, there + * may be ddc/aux channels hiding behind external tmds/dp/etc + * transmitters. + */ + index = ((index + 0x0f) / 0x10) * 0x10; + i = -1; + while ((data = dcb_outp_parse(bios, ++i, &ver, &hdr, &outp))) { + if (!outp.location || !outp.extdev) + continue; + + switch (outp.type) { + case DCB_OUTPUT_TMDS: + info.type = NV_I2C_TYPE_EXTDDC(outp.extdev); break; - case DCB_I2C_NVIO_AUX: - port->drive = info.drive & 0x0f; - port->sense = port->drive; - port->adapter.algo = &nouveau_i2c_aux_algo; + case DCB_OUTPUT_DP: + info.type = NV_I2C_TYPE_EXTAUX(outp.extdev); break; default: - break; - } - - if (!port->adapter.algo && !port->drive) { - nv_error(i2c, "I2C%d: type %d index %x/%x unknown\n", - i, port->type, port->drive, port->sense); - kfree(port); continue; } - snprintf(port->adapter.name, sizeof(port->adapter.name), - "nouveau-%s-%d", device->name, i); - port->adapter.owner = THIS_MODULE; - port->adapter.dev.parent = &device->pdev->dev; - port->i2c = i2c; - port->index = i; - port->dcb = info.data; - i2c_set_adapdata(&port->adapter, i2c); - - if (port->adapter.algo != &nouveau_i2c_aux_algo) { - nouveau_i2c_drive_scl(port, 0); - nouveau_i2c_drive_sda(port, 1); - nouveau_i2c_drive_scl(port, 1); - -#ifdef CONFIG_NOUVEAU_I2C_INTERNAL_DEFAULT - if (nouveau_boolopt(device->cfgopt, "NvI2C", true)) { -#else - if (nouveau_boolopt(device->cfgopt, "NvI2C", false)) { -#endif - port->adapter.algo = &nouveau_i2c_bit_algo; - ret = i2c_add_adapter(&port->adapter); - } else { - port->adapter.algo_data = &port->bit; - port->bit.udelay = 10; - port->bit.timeout = usecs_to_jiffies(2200); - port->bit.data = port; - port->bit.setsda = nouveau_i2c_drive_sda; - port->bit.setscl = nouveau_i2c_drive_scl; - port->bit.getsda = nouveau_i2c_sense_sda; - port->bit.getscl = nouveau_i2c_sense_scl; - ret = i2c_bit_add_bus(&port->adapter); - } - } else { - port->adapter.algo = &nouveau_i2c_aux_algo; - ret = i2c_add_adapter(&port->adapter); - } - - if (ret) { - nv_error(i2c, "I2C%d: failed register: %d\n", i, ret); - kfree(port); - continue; + ret = -ENODEV; + j = -1; + while (ret && ++j < ARRAY_SIZE(nouveau_i2c_extdev_sclass)) { + parent = nv_object(i2c->find(i2c, outp.i2c_index)); + oclass = nouveau_i2c_extdev_sclass[j]; + do { + if (oclass->handle != info.type) + continue; + ret = nouveau_object_ctor(parent, *pobject, + oclass, NULL, + index++, &object); + } while (ret && (++oclass)->handle); } - - list_add_tail(&port->head, &i2c->ports); } return 0; } - -static void -nouveau_i2c_dtor(struct nouveau_object *object) -{ - struct nouveau_i2c *i2c = (void *)object; - struct nouveau_i2c_port *port, *temp; - - list_for_each_entry_safe(port, temp, &i2c->ports, head) { - i2c_del_adapter(&port->adapter); - list_del(&port->head); - kfree(port); - } - - nouveau_subdev_destroy(&i2c->base); -} - -static int -nouveau_i2c_init(struct nouveau_object *object) -{ - struct nouveau_i2c *i2c = (void *)object; - return nouveau_subdev_init(&i2c->base); -} - -static int -nouveau_i2c_fini(struct nouveau_object *object, bool suspend) -{ - struct nouveau_i2c *i2c = (void *)object; - return nouveau_subdev_fini(&i2c->base, suspend); -} - -struct nouveau_oclass -nouveau_i2c_oclass = { - .handle = NV_SUBDEV(I2C, 0x00), - .ofuncs = &(struct nouveau_ofuncs) { - .ctor = nouveau_i2c_ctor, - .dtor = nouveau_i2c_dtor, - .init = nouveau_i2c_init, - .fini = nouveau_i2c_fini, - }, -}; diff --git a/drivers/gpu/drm/nouveau/core/subdev/i2c/bit.c b/drivers/gpu/drm/nouveau/core/subdev/i2c/bit.c index 1c4c9a5c8e2e..a6e72d3b06b5 100644 --- a/drivers/gpu/drm/nouveau/core/subdev/i2c/bit.c +++ b/drivers/gpu/drm/nouveau/core/subdev/i2c/bit.c @@ -32,25 +32,25 @@ static inline void i2c_drive_scl(struct nouveau_i2c_port *port, int state) { - nouveau_i2c_drive_scl(port, state); + port->func->drive_scl(port, state); } static inline void i2c_drive_sda(struct nouveau_i2c_port *port, int state) { - nouveau_i2c_drive_sda(port, state); + port->func->drive_sda(port, state); } static inline int i2c_sense_scl(struct nouveau_i2c_port *port) { - return nouveau_i2c_sense_scl(port); + return port->func->sense_scl(port); } static inline int i2c_sense_sda(struct nouveau_i2c_port *port) { - return nouveau_i2c_sense_sda(port); + return port->func->sense_sda(port); } static void @@ -77,9 +77,8 @@ i2c_start(struct nouveau_i2c_port *port) { int ret = 0; - port->state = i2c_sense_scl(port); - port->state |= i2c_sense_sda(port) << 1; - if (port->state != 3) { + if (!i2c_sense_scl(port) || + !i2c_sense_sda(port)) { i2c_drive_scl(port, 0); i2c_drive_sda(port, 1); if (!i2c_raise_scl(port)) @@ -184,10 +183,13 @@ i2c_addr(struct nouveau_i2c_port *port, struct i2c_msg *msg) static int i2c_bit_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num) { - struct nouveau_i2c_port *port = (struct nouveau_i2c_port *)adap; + struct nouveau_i2c_port *port = adap->algo_data; struct i2c_msg *msg = msgs; int ret = 0, mcnt = num; + if (port->func->acquire) + port->func->acquire(port); + while (!ret && mcnt--) { u8 remaining = msg->len; u8 *ptr = msg->buf; diff --git a/drivers/gpu/drm/nouveau/core/subdev/i2c/nv04.c b/drivers/gpu/drm/nouveau/core/subdev/i2c/nv04.c new file mode 100644 index 000000000000..2ad18840fe63 --- /dev/null +++ b/drivers/gpu/drm/nouveau/core/subdev/i2c/nv04.c @@ -0,0 +1,143 @@ +/* + * Copyright 2012 Red Hat Inc. + * + * 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 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 + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: Ben Skeggs + */ + +#include <subdev/i2c.h> +#include <subdev/vga.h> + +struct nv04_i2c_priv { + struct nouveau_i2c base; +}; + +struct nv04_i2c_port { + struct nouveau_i2c_port base; + u8 drive; + u8 sense; +}; + +static void +nv04_i2c_drive_scl(struct nouveau_i2c_port *base, int state) +{ + struct nv04_i2c_priv *priv = (void *)nv_object(base)->engine; + struct nv04_i2c_port *port = (void *)base; + u8 val = nv_rdvgac(priv, 0, port->drive); + if (state) val |= 0x20; + else val &= 0xdf; + nv_wrvgac(priv, 0, port->drive, val | 0x01); +} + +static void +nv04_i2c_drive_sda(struct nouveau_i2c_port *base, int state) +{ + struct nv04_i2c_priv *priv = (void *)nv_object(base)->engine; + struct nv04_i2c_port *port = (void *)base; + u8 val = nv_rdvgac(priv, 0, port->drive); + if (state) val |= 0x10; + else val &= 0xef; + nv_wrvgac(priv, 0, port->drive, val | 0x01); +} + +static int +nv04_i2c_sense_scl(struct nouveau_i2c_port *base) +{ + struct nv04_i2c_priv *priv = (void *)nv_object(base)->engine; + struct nv04_i2c_port *port = (void *)base; + return !!(nv_rdvgac(priv, 0, port->sense) & 0x04); +} + +static int +nv04_i2c_sense_sda(struct nouveau_i2c_port *base) +{ + struct nv04_i2c_priv *priv = (void *)nv_object(base)->engine; + struct nv04_i2c_port *port = (void *)base; + return !!(nv_rdvgac(priv, 0, port->sense) & 0x08); +} + +static const struct nouveau_i2c_func +nv04_i2c_func = { + .drive_scl = nv04_i2c_drive_scl, + .drive_sda = nv04_i2c_drive_sda, + .sense_scl = nv04_i2c_sense_scl, + .sense_sda = nv04_i2c_sense_sda, +}; + +static int +nv04_i2c_port_ctor(struct nouveau_object *parent, struct nouveau_object *engine, + struct nouveau_oclass *oclass, void *data, u32 index, + struct nouveau_object **pobject) +{ + struct dcb_i2c_entry *info = data; + struct nv04_i2c_port *port; + int ret; + + ret = nouveau_i2c_port_create(parent, engine, oclass, index, + &nouveau_i2c_bit_algo, &port); + *pobject = nv_object(port); + if (ret) + return ret; + + port->base.func = &nv04_i2c_func; + port->drive = info->drive; + port->sense = info->sense; + return 0; +} + +static struct nouveau_oclass +nv04_i2c_sclass[] = { + { .handle = NV_I2C_TYPE_DCBI2C(DCB_I2C_NV04_BIT), + .ofuncs = &(struct nouveau_ofuncs) { + .ctor = nv04_i2c_port_ctor, + .dtor = _nouveau_i2c_port_dtor, + .init = _nouveau_i2c_port_init, + .fini = _nouveau_i2c_port_fini, + }, + }, + {} +}; + +static int +nv04_i2c_ctor(struct nouveau_object *parent, struct nouveau_object *engine, + struct nouveau_oclass *oclass, void *data, u32 size, + struct nouveau_object **pobject) +{ + struct nv04_i2c_priv *priv; + int ret; + + ret = nouveau_i2c_create(parent, engine, oclass, nv04_i2c_sclass, &priv); + *pobject = nv_object(priv); + if (ret) + return ret; + + return 0; +} + +struct nouveau_oclass +nv04_i2c_oclass = { + .handle = NV_SUBDEV(I2C, 0x04), + .ofuncs = &(struct nouveau_ofuncs) { + .ctor = nv04_i2c_ctor, + .dtor = _nouveau_i2c_dtor, + .init = _nouveau_i2c_init, + .fini = _nouveau_i2c_fini, + }, +}; diff --git a/drivers/gpu/drm/nouveau/core/subdev/i2c/nv4e.c b/drivers/gpu/drm/nouveau/core/subdev/i2c/nv4e.c new file mode 100644 index 000000000000..f501ae25dbb3 --- /dev/null +++ b/drivers/gpu/drm/nouveau/core/subdev/i2c/nv4e.c @@ -0,0 +1,135 @@ +/* + * Copyright 2012 Red Hat Inc. + * + * 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 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 + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: Ben Skeggs + */ + +#include <subdev/i2c.h> +#include <subdev/vga.h> + +struct nv4e_i2c_priv { + struct nouveau_i2c base; +}; + +struct nv4e_i2c_port { + struct nouveau_i2c_port base; + u32 addr; +}; + +static void +nv4e_i2c_drive_scl(struct nouveau_i2c_port *base, int state) +{ + struct nv4e_i2c_priv *priv = (void *)nv_object(base)->engine; + struct nv4e_i2c_port *port = (void *)base; + nv_mask(priv, port->addr, 0x2f, state ? 0x21 : 0x01); +} + +static void +nv4e_i2c_drive_sda(struct nouveau_i2c_port *base, int state) +{ + struct nv4e_i2c_priv *priv = (void *)nv_object(base)->engine; + struct nv4e_i2c_port *port = (void *)base; + nv_mask(priv, port->addr, 0x1f, state ? 0x11 : 0x01); +} + +static int +nv4e_i2c_sense_scl(struct nouveau_i2c_port *base) +{ + struct nv4e_i2c_priv *priv = (void *)nv_object(base)->engine; + struct nv4e_i2c_port *port = (void *)base; + return !!(nv_rd32(priv, port->addr) & 0x00040000); +} + +static int +nv4e_i2c_sense_sda(struct nouveau_i2c_port *base) +{ + struct nv4e_i2c_priv *priv = (void *)nv_object(base)->engine; + struct nv4e_i2c_port *port = (void *)base; + return !!(nv_rd32(priv, port->addr) & 0x00080000); +} + +static const struct nouveau_i2c_func +nv4e_i2c_func = { + .drive_scl = nv4e_i2c_drive_scl, + .drive_sda = nv4e_i2c_drive_sda, + .sense_scl = nv4e_i2c_sense_scl, + .sense_sda = nv4e_i2c_sense_sda, +}; + +static int +nv4e_i2c_port_ctor(struct nouveau_object *parent, struct nouveau_object *engine, + struct nouveau_oclass *oclass, void *data, u32 index, + struct nouveau_object **pobject) +{ + struct dcb_i2c_entry *info = data; + struct nv4e_i2c_port *port; + int ret; + + ret = nouveau_i2c_port_create(parent, engine, oclass, index, + &nouveau_i2c_bit_algo, &port); + *pobject = nv_object(port); + if (ret) + return ret; + + port->base.func = &nv4e_i2c_func; + port->addr = 0x600800 + info->drive; + return 0; +} + +static struct nouveau_oclass +nv4e_i2c_sclass[] = { + { .handle = NV_I2C_TYPE_DCBI2C(DCB_I2C_NV4E_BIT), + .ofuncs = &(struct nouveau_ofuncs) { + .ctor = nv4e_i2c_port_ctor, + .dtor = _nouveau_i2c_port_dtor, + .init = _nouveau_i2c_port_init, + .fini = _nouveau_i2c_port_fini, + }, + }, + {} +}; + +static int +nv4e_i2c_ctor(struct nouveau_object *parent, struct nouveau_object *engine, + struct nouveau_oclass *oclass, void *data, u32 size, + struct nouveau_object **pobject) +{ + struct nv4e_i2c_priv *priv; + int ret; + + ret = nouveau_i2c_create(parent, engine, oclass, nv4e_i2c_sclass, &priv); + *pobject = nv_object(priv); + if (ret) + return ret; + + return 0; +} + +struct nouveau_oclass +nv4e_i2c_oclass = { + .handle = NV_SUBDEV(I2C, 0x4e), + .ofuncs = &(struct nouveau_ofuncs) { + .ctor = nv4e_i2c_ctor, + .dtor = _nouveau_i2c_dtor, + .init = _nouveau_i2c_init, + .fini = _nouveau_i2c_fini, + }, +}; diff --git a/drivers/gpu/drm/nouveau/core/subdev/i2c/nv50.c b/drivers/gpu/drm/nouveau/core/subdev/i2c/nv50.c new file mode 100644 index 000000000000..378dfa324e5f --- /dev/null +++ b/drivers/gpu/drm/nouveau/core/subdev/i2c/nv50.c @@ -0,0 +1,149 @@ +/* + * Copyright 2012 Red Hat Inc. + * + * 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 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 + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: Ben Skeggs + */ + +#include "nv50.h" + +void +nv50_i2c_drive_scl(struct nouveau_i2c_port *base, int state) +{ + struct nv50_i2c_priv *priv = (void *)nv_object(base)->engine; + struct nv50_i2c_port *port = (void *)base; + if (state) port->state |= 0x01; + else port->state &= 0xfe; + nv_wr32(priv, port->addr, port->state); +} + +void +nv50_i2c_drive_sda(struct nouveau_i2c_port *base, int state) +{ + struct nv50_i2c_priv *priv = (void *)nv_object(base)->engine; + struct nv50_i2c_port *port = (void *)base; + if (state) port->state |= 0x02; + else port->state &= 0xfd; + nv_wr32(priv, port->addr, port->state); +} + +int +nv50_i2c_sense_scl(struct nouveau_i2c_port *base) +{ + struct nv50_i2c_priv *priv = (void *)nv_object(base)->engine; + struct nv50_i2c_port *port = (void *)base; + return !!(nv_rd32(priv, port->addr) & 0x00000001); +} + +int +nv50_i2c_sense_sda(struct nouveau_i2c_port *base) +{ + struct nv50_i2c_priv *priv = (void *)nv_object(base)->engine; + struct nv50_i2c_port *port = (void *)base; + return !!(nv_rd32(priv, port->addr) & 0x00000002); +} + +static const struct nouveau_i2c_func +nv50_i2c_func = { + .drive_scl = nv50_i2c_drive_scl, + .drive_sda = nv50_i2c_drive_sda, + .sense_scl = nv50_i2c_sense_scl, + .sense_sda = nv50_i2c_sense_sda, +}; + +const u32 nv50_i2c_addr[] = { + 0x00e138, 0x00e150, 0x00e168, 0x00e180, + 0x00e254, 0x00e274, 0x00e764, 0x00e780, + 0x00e79c, 0x00e7b8 +}; +const int nv50_i2c_addr_nr = ARRAY_SIZE(nv50_i2c_addr); + +static int +nv50_i2c_port_ctor(struct nouveau_object *parent, struct nouveau_object *engine, + struct nouveau_oclass *oclass, void *data, u32 index, + struct nouveau_object **pobject) +{ + struct dcb_i2c_entry *info = data; + struct nv50_i2c_port *port; + int ret; + + ret = nouveau_i2c_port_create(parent, engine, oclass, index, + &nouveau_i2c_bit_algo, &port); + *pobject = nv_object(port); + if (ret) + return ret; + + if (info->drive >= nv50_i2c_addr_nr) + return -EINVAL; + + port->base.func = &nv50_i2c_func; + port->state = 0x00000007; + port->addr = nv50_i2c_addr[info->drive]; + return 0; +} + +int +nv50_i2c_port_init(struct nouveau_object *object) +{ + struct nv50_i2c_priv *priv = (void *)object->engine; + struct nv50_i2c_port *port = (void *)object; + nv_wr32(priv, port->addr, port->state); + return nouveau_i2c_port_init(&port->base); +} + +static struct nouveau_oclass +nv50_i2c_sclass[] = { + { .handle = NV_I2C_TYPE_DCBI2C(DCB_I2C_NVIO_BIT), + .ofuncs = &(struct nouveau_ofuncs) { + .ctor = nv50_i2c_port_ctor, + .dtor = _nouveau_i2c_port_dtor, + .init = nv50_i2c_port_init, + .fini = _nouveau_i2c_port_fini, + }, + }, + {} +}; + +static int +nv50_i2c_ctor(struct nouveau_object *parent, struct nouveau_object *engine, + struct nouveau_oclass *oclass, void *data, u32 size, + struct nouveau_object **pobject) +{ + struct nv50_i2c_priv *priv; + int ret; + + ret = nouveau_i2c_create(parent, engine, oclass, nv50_i2c_sclass, &priv); + *pobject = nv_object(priv); + if (ret) + return ret; + + return 0; +} + +struct nouveau_oclass +nv50_i2c_oclass = { + .handle = NV_SUBDEV(I2C, 0x50), + .ofuncs = &(struct nouveau_ofuncs) { + .ctor = nv50_i2c_ctor, + .dtor = _nouveau_i2c_dtor, + .init = _nouveau_i2c_init, + .fini = _nouveau_i2c_fini, + }, +}; diff --git a/drivers/gpu/drm/nouveau/core/subdev/i2c/nv50.h b/drivers/gpu/drm/nouveau/core/subdev/i2c/nv50.h new file mode 100644 index 000000000000..4e5ba48ebf5a --- /dev/null +++ b/drivers/gpu/drm/nouveau/core/subdev/i2c/nv50.h @@ -0,0 +1,32 @@ +#ifndef __NV50_I2C_H__ +#define __NV50_I2C_H__ + +#include <subdev/i2c.h> + +struct nv50_i2c_priv { + struct nouveau_i2c base; +}; + +struct nv50_i2c_port { + struct nouveau_i2c_port base; + u32 addr; + u32 ctrl; + u32 data; + u32 state; +}; + +extern const u32 nv50_i2c_addr[]; +extern const int nv50_i2c_addr_nr; +int nv50_i2c_port_init(struct nouveau_object *); +int nv50_i2c_sense_scl(struct nouveau_i2c_port *); +int nv50_i2c_sense_sda(struct nouveau_i2c_port *); +void nv50_i2c_drive_scl(struct nouveau_i2c_port *, int state); +void nv50_i2c_drive_sda(struct nouveau_i2c_port *, int state); + +int nv94_aux_port_ctor(struct nouveau_object *, struct nouveau_object *, + struct nouveau_oclass *, void *, u32, + struct nouveau_object **); +void nv94_i2c_acquire(struct nouveau_i2c_port *); +void nv94_i2c_release(struct nouveau_i2c_port *); + +#endif diff --git a/drivers/gpu/drm/nouveau/core/subdev/i2c/nv94.c b/drivers/gpu/drm/nouveau/core/subdev/i2c/nv94.c new file mode 100644 index 000000000000..61b771670bfe --- /dev/null +++ b/drivers/gpu/drm/nouveau/core/subdev/i2c/nv94.c @@ -0,0 +1,285 @@ +/* + * Copyright 2012 Red Hat Inc. + * + * 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 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 + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: Ben Skeggs + */ + +#include "nv50.h" + +#define AUX_DBG(fmt, args...) nv_debug(aux, "AUXCH(%d): " fmt, ch, ##args) +#define AUX_ERR(fmt, args...) nv_error(aux, "AUXCH(%d): " fmt, ch, ##args) + +static void +auxch_fini(struct nouveau_i2c *aux, int ch) +{ + nv_mask(aux, 0x00e4e4 + (ch * 0x50), 0x00310000, 0x00000000); +} + +static int +auxch_init(struct nouveau_i2c *aux, int ch) +{ + const u32 unksel = 1; /* nfi which to use, or if it matters.. */ + const u32 ureq = unksel ? 0x00100000 : 0x00200000; + const u32 urep = unksel ? 0x01000000 : 0x02000000; + u32 ctrl, timeout; + + /* wait up to 1ms for any previous transaction to be done... */ + timeout = 1000; + do { + ctrl = nv_rd32(aux, 0x00e4e4 + (ch * 0x50)); + udelay(1); + if (!timeout--) { + AUX_ERR("begin idle timeout 0x%08x\n", ctrl); + return -EBUSY; + } + } while (ctrl & 0x03010000); + + /* set some magic, and wait up to 1ms for it to appear */ + nv_mask(aux, 0x00e4e4 + (ch * 0x50), 0x00300000, ureq); + timeout = 1000; + do { + ctrl = nv_rd32(aux, 0x00e4e4 + (ch * 0x50)); + udelay(1); + if (!timeout--) { + AUX_ERR("magic wait 0x%08x\n", ctrl); + auxch_fini(aux, ch); + return -EBUSY; + } + } while ((ctrl & 0x03000000) != urep); + + return 0; +} + +int +nv94_aux(struct nouveau_i2c_port *base, u8 type, u32 addr, u8 *data, u8 size) +{ + struct nouveau_i2c *aux = nouveau_i2c(base); + struct nv50_i2c_port *port = (void *)base; + u32 ctrl, stat, timeout, retries; + u32 xbuf[4] = {}; + int ch = port->addr; + int ret, i; + + AUX_DBG("%d: 0x%08x %d\n", type, addr, size); + + ret = auxch_init(aux, ch); + if (ret) + goto out; + + stat = nv_rd32(aux, 0x00e4e8 + (ch * 0x50)); + if (!(stat & 0x10000000)) { + AUX_DBG("sink not detected\n"); + ret = -ENXIO; + goto out; + } + + if (!(type & 1)) { + memcpy(xbuf, data, size); + for (i = 0; i < 16; i += 4) { + AUX_DBG("wr 0x%08x\n", xbuf[i / 4]); + nv_wr32(aux, 0x00e4c0 + (ch * 0x50) + i, xbuf[i / 4]); + } + } + + ctrl = nv_rd32(aux, 0x00e4e4 + (ch * 0x50)); + ctrl &= ~0x0001f0ff; + ctrl |= type << 12; + ctrl |= size - 1; + nv_wr32(aux, 0x00e4e0 + (ch * 0x50), addr); + + /* retry transaction a number of times on failure... */ + ret = -EREMOTEIO; + for (retries = 0; retries < 32; retries++) { + /* reset, and delay a while if this is a retry */ + nv_wr32(aux, 0x00e4e4 + (ch * 0x50), 0x80000000 | ctrl); + nv_wr32(aux, 0x00e4e4 + (ch * 0x50), 0x00000000 | ctrl); + if (retries) + udelay(400); + + /* transaction request, wait up to 1ms for it to complete */ + nv_wr32(aux, 0x00e4e4 + (ch * 0x50), 0x00010000 | ctrl); + + timeout = 1000; + do { + ctrl = nv_rd32(aux, 0x00e4e4 + (ch * 0x50)); + udelay(1); + if (!timeout--) { + AUX_ERR("tx req timeout 0x%08x\n", ctrl); + goto out; + } + } while (ctrl & 0x00010000); + + /* read status, and check if transaction completed ok */ + stat = nv_mask(aux, 0x00e4e8 + (ch * 0x50), 0, 0); + if (!(stat & 0x000f0f00)) { + ret = 0; + break; + } + + AUX_DBG("%02d 0x%08x 0x%08x\n", retries, ctrl, stat); + } + + if (type & 1) { + for (i = 0; i < 16; i += 4) { + xbuf[i / 4] = nv_rd32(aux, 0x00e4d0 + (ch * 0x50) + i); + AUX_DBG("rd 0x%08x\n", xbuf[i / 4]); + } + memcpy(data, xbuf, size); + } + +out: + auxch_fini(aux, ch); + return ret; +} + +void +nv94_i2c_acquire(struct nouveau_i2c_port *base) +{ + struct nv50_i2c_priv *priv = (void *)nv_object(base)->engine; + struct nv50_i2c_port *port = (void *)base; + if (port->ctrl) { + nv_mask(priv, port->ctrl + 0x0c, 0x00000001, 0x00000000); + nv_mask(priv, port->ctrl + 0x00, 0x0000f003, port->data); + } +} + +void +nv94_i2c_release(struct nouveau_i2c_port *base) +{ +} + +static const struct nouveau_i2c_func +nv94_i2c_func = { + .acquire = nv94_i2c_acquire, + .release = nv94_i2c_release, + .drive_scl = nv50_i2c_drive_scl, + .drive_sda = nv50_i2c_drive_sda, + .sense_scl = nv50_i2c_sense_scl, + .sense_sda = nv50_i2c_sense_sda, +}; + +static int +nv94_i2c_port_ctor(struct nouveau_object *parent, struct nouveau_object *engine, + struct nouveau_oclass *oclass, void *data, u32 index, + struct nouveau_object **pobject) +{ + struct dcb_i2c_entry *info = data; + struct nv50_i2c_port *port; + int ret; + + ret = nouveau_i2c_port_create(parent, engine, oclass, index, + &nouveau_i2c_bit_algo, &port); + *pobject = nv_object(port); + if (ret) + return ret; + + if (info->drive >= nv50_i2c_addr_nr) + return -EINVAL; + + port->base.func = &nv94_i2c_func; + port->state = 7; + port->addr = nv50_i2c_addr[info->drive]; + if (info->share != DCB_I2C_UNUSED) { + port->ctrl = 0x00e500 + (info->share * 0x50); + port->data = 0x0000e001; + } + return 0; +} + +static const struct nouveau_i2c_func +nv94_aux_func = { + .acquire = nv94_i2c_acquire, + .release = nv94_i2c_release, + .aux = nv94_aux, +}; + +int +nv94_aux_port_ctor(struct nouveau_object *parent, struct nouveau_object *engine, + struct nouveau_oclass *oclass, void *data, u32 index, + struct nouveau_object **pobject) +{ + struct dcb_i2c_entry *info = data; + struct nv50_i2c_port *port; + int ret; + + ret = nouveau_i2c_port_create(parent, engine, oclass, index, + &nouveau_i2c_aux_algo, &port); + *pobject = nv_object(port); + if (ret) + return ret; + + port->base.func = &nv94_aux_func; + port->addr = info->drive; + if (info->share != DCB_I2C_UNUSED) { + port->ctrl = 0x00e500 + (info->drive * 0x50); + port->data = 0x00002002; + } + + return 0; +} + +static struct nouveau_oclass +nv94_i2c_sclass[] = { + { .handle = NV_I2C_TYPE_DCBI2C(DCB_I2C_NVIO_BIT), + .ofuncs = &(struct nouveau_ofuncs) { + .ctor = nv94_i2c_port_ctor, + .dtor = _nouveau_i2c_port_dtor, + .init = nv50_i2c_port_init, + .fini = _nouveau_i2c_port_fini, + }, + }, + { .handle = NV_I2C_TYPE_DCBI2C(DCB_I2C_NVIO_AUX), + .ofuncs = &(struct nouveau_ofuncs) { + .ctor = nv94_aux_port_ctor, + .dtor = _nouveau_i2c_port_dtor, + .init = _nouveau_i2c_port_init, + .fini = _nouveau_i2c_port_fini, + }, + }, + {} +}; + +static int +nv94_i2c_ctor(struct nouveau_object *parent, struct nouveau_object *engine, + struct nouveau_oclass *oclass, void *data, u32 size, + struct nouveau_object **pobject) +{ + struct nv50_i2c_priv *priv; + int ret; + + ret = nouveau_i2c_create(parent, engine, oclass, nv94_i2c_sclass, &priv); + *pobject = nv_object(priv); + if (ret) + return ret; + + return 0; +} + +struct nouveau_oclass +nv94_i2c_oclass = { + .handle = NV_SUBDEV(I2C, 0x94), + .ofuncs = &(struct nouveau_ofuncs) { + .ctor = nv94_i2c_ctor, + .dtor = _nouveau_i2c_dtor, + .init = _nouveau_i2c_init, + .fini = _nouveau_i2c_fini, + }, +}; diff --git a/drivers/gpu/drm/nouveau/core/subdev/i2c/nvd0.c b/drivers/gpu/drm/nouveau/core/subdev/i2c/nvd0.c new file mode 100644 index 000000000000..f761b8a610f1 --- /dev/null +++ b/drivers/gpu/drm/nouveau/core/subdev/i2c/nvd0.c @@ -0,0 +1,124 @@ +/* + * Copyright 2012 Red Hat Inc. + * + * 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 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 + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: Ben Skeggs + */ + +#include "nv50.h" + +static int +nvd0_i2c_sense_scl(struct nouveau_i2c_port *base) +{ + struct nv50_i2c_priv *priv = (void *)nv_object(base)->engine; + struct nv50_i2c_port *port = (void *)base; + return !!(nv_rd32(priv, port->addr) & 0x00000010); +} + +static int +nvd0_i2c_sense_sda(struct nouveau_i2c_port *base) +{ + struct nv50_i2c_priv *priv = (void *)nv_object(base)->engine; + struct nv50_i2c_port *port = (void *)base; + return !!(nv_rd32(priv, port->addr) & 0x00000020); +} + +static const struct nouveau_i2c_func +nvd0_i2c_func = { + .acquire = nv94_i2c_acquire, + .release = nv94_i2c_release, + .drive_scl = nv50_i2c_drive_scl, + .drive_sda = nv50_i2c_drive_sda, + .sense_scl = nvd0_i2c_sense_scl, + .sense_sda = nvd0_i2c_sense_sda, +}; + +static int +nvd0_i2c_port_ctor(struct nouveau_object *parent, struct nouveau_object *engine, + struct nouveau_oclass *oclass, void *data, u32 index, + struct nouveau_object **pobject) +{ + struct dcb_i2c_entry *info = data; + struct nv50_i2c_port *port; + int ret; + + ret = nouveau_i2c_port_create(parent, engine, oclass, index, + &nouveau_i2c_bit_algo, &port); + *pobject = nv_object(port); + if (ret) + return ret; + + port->base.func = &nvd0_i2c_func; + port->state = 0x00000007; + port->addr = 0x00d014 + (info->drive * 0x20); + if (info->share != DCB_I2C_UNUSED) { + port->ctrl = 0x00e500 + (info->share * 0x50); + port->data = 0x0000e001; + } + return 0; +} + +static struct nouveau_oclass +nvd0_i2c_sclass[] = { + { .handle = NV_I2C_TYPE_DCBI2C(DCB_I2C_NVIO_BIT), + .ofuncs = &(struct nouveau_ofuncs) { + .ctor = nvd0_i2c_port_ctor, + .dtor = _nouveau_i2c_port_dtor, + .init = nv50_i2c_port_init, + .fini = _nouveau_i2c_port_fini, + }, + }, + { .handle = NV_I2C_TYPE_DCBI2C(DCB_I2C_NVIO_AUX), + .ofuncs = &(struct nouveau_ofuncs) { + .ctor = nv94_aux_port_ctor, + .dtor = _nouveau_i2c_port_dtor, + .init = _nouveau_i2c_port_init, + .fini = _nouveau_i2c_port_fini, + }, + }, + {} +}; + +static int +nvd0_i2c_ctor(struct nouveau_object *parent, struct nouveau_object *engine, + struct nouveau_oclass *oclass, void *data, u32 size, + struct nouveau_object **pobject) +{ + struct nv50_i2c_priv *priv; + int ret; + + ret = nouveau_i2c_create(parent, engine, oclass, nvd0_i2c_sclass, &priv); + *pobject = nv_object(priv); + if (ret) + return ret; + + return 0; +} + +struct nouveau_oclass +nvd0_i2c_oclass = { + .handle = NV_SUBDEV(I2C, 0xd0), + .ofuncs = &(struct nouveau_ofuncs) { + .ctor = nvd0_i2c_ctor, + .dtor = _nouveau_i2c_dtor, + .init = _nouveau_i2c_init, + .fini = _nouveau_i2c_fini, + }, +}; diff --git a/drivers/gpu/drm/nouveau/core/subdev/mc/nv04.c b/drivers/gpu/drm/nouveau/core/subdev/mc/nv04.c index 23ebe477a6f0..89da8fa7ea0f 100644 --- a/drivers/gpu/drm/nouveau/core/subdev/mc/nv04.c +++ b/drivers/gpu/drm/nouveau/core/subdev/mc/nv04.c @@ -37,7 +37,7 @@ nv04_mc_intr[] = { { 0x00100000, NVDEV_SUBDEV_TIMER }, { 0x01000000, NVDEV_ENGINE_DISP }, /* NV04- PCRTC0 */ { 0x02000000, NVDEV_ENGINE_DISP }, /* NV11- PCRTC1 */ - { 0x10000000, NVDEV_SUBDEV_GPIO }, /* PBUS */ + { 0x10000000, NVDEV_SUBDEV_BUS }, { 0x80000000, NVDEV_ENGINE_SW }, {} }; diff --git a/drivers/gpu/drm/nouveau/core/subdev/mc/nv50.c b/drivers/gpu/drm/nouveau/core/subdev/mc/nv50.c index 8d759f830323..5965add6daee 100644 --- a/drivers/gpu/drm/nouveau/core/subdev/mc/nv50.c +++ b/drivers/gpu/drm/nouveau/core/subdev/mc/nv50.c @@ -38,6 +38,7 @@ nv50_mc_intr[] = { { 0x00100000, NVDEV_SUBDEV_TIMER }, { 0x00200000, NVDEV_SUBDEV_GPIO }, { 0x04000000, NVDEV_ENGINE_DISP }, + { 0x10000000, NVDEV_SUBDEV_BUS }, { 0x80000000, NVDEV_ENGINE_SW }, { 0x0000d101, NVDEV_SUBDEV_FB }, {}, diff --git a/drivers/gpu/drm/nouveau/core/subdev/mc/nv98.c b/drivers/gpu/drm/nouveau/core/subdev/mc/nv98.c index ceb5c83f9459..3a80b29dce0f 100644 --- a/drivers/gpu/drm/nouveau/core/subdev/mc/nv98.c +++ b/drivers/gpu/drm/nouveau/core/subdev/mc/nv98.c @@ -35,10 +35,12 @@ nv98_mc_intr[] = { { 0x00001000, NVDEV_ENGINE_GR }, { 0x00004000, NVDEV_ENGINE_CRYPT }, /* NV84:NVA3 */ { 0x00008000, NVDEV_ENGINE_BSP }, + { 0x00080000, NVDEV_SUBDEV_THERM }, /* NVA3:NVC0 */ { 0x00100000, NVDEV_SUBDEV_TIMER }, { 0x00200000, NVDEV_SUBDEV_GPIO }, { 0x00400000, NVDEV_ENGINE_COPY0 }, /* NVA3- */ { 0x04000000, NVDEV_ENGINE_DISP }, + { 0x10000000, NVDEV_SUBDEV_BUS }, { 0x80000000, NVDEV_ENGINE_SW }, { 0x0040d101, NVDEV_SUBDEV_FB }, {}, diff --git a/drivers/gpu/drm/nouveau/core/subdev/mc/nvc0.c b/drivers/gpu/drm/nouveau/core/subdev/mc/nvc0.c index 92796682722d..42bbf72023a8 100644 --- a/drivers/gpu/drm/nouveau/core/subdev/mc/nvc0.c +++ b/drivers/gpu/drm/nouveau/core/subdev/mc/nvc0.c @@ -36,11 +36,13 @@ nvc0_mc_intr[] = { { 0x00000100, NVDEV_ENGINE_FIFO }, { 0x00001000, NVDEV_ENGINE_GR }, { 0x00008000, NVDEV_ENGINE_BSP }, + { 0x00040000, NVDEV_SUBDEV_THERM }, { 0x00020000, NVDEV_ENGINE_VP }, { 0x00100000, NVDEV_SUBDEV_TIMER }, { 0x00200000, NVDEV_SUBDEV_GPIO }, { 0x02000000, NVDEV_SUBDEV_LTCG }, { 0x04000000, NVDEV_ENGINE_DISP }, + { 0x10000000, NVDEV_SUBDEV_BUS }, { 0x40000000, NVDEV_SUBDEV_IBUS }, { 0x80000000, NVDEV_ENGINE_SW }, {}, diff --git a/drivers/gpu/drm/nouveau/core/subdev/mxm/mxms.c b/drivers/gpu/drm/nouveau/core/subdev/mxm/mxms.c index 839ca1edc132..4bde7f7f7b81 100644 --- a/drivers/gpu/drm/nouveau/core/subdev/mxm/mxms.c +++ b/drivers/gpu/drm/nouveau/core/subdev/mxm/mxms.c @@ -156,15 +156,15 @@ mxms_foreach(struct nouveau_mxm *mxm, u8 types, nv_debug(mxm, "%4s: ", mxms_desc_name[type]); for (j = headerlen - 1; j >= 0; j--) - printk("%02x", dump[j]); - printk("\n"); + pr_cont("%02x", dump[j]); + pr_cont("\n"); dump += headerlen; for (i = 0; i < entries; i++, dump += recordlen) { nv_debug(mxm, " "); for (j = recordlen - 1; j >= 0; j--) - printk("%02x", dump[j]); - printk("\n"); + pr_cont("%02x", dump[j]); + pr_cont("\n"); } } diff --git a/drivers/gpu/drm/nouveau/core/subdev/therm/base.c b/drivers/gpu/drm/nouveau/core/subdev/therm/base.c index 1674c74a76c8..f794dc89a3b2 100644 --- a/drivers/gpu/drm/nouveau/core/subdev/therm/base.c +++ b/drivers/gpu/drm/nouveau/core/subdev/therm/base.c @@ -29,6 +29,134 @@ #include "priv.h" +static int +nouveau_therm_update_trip(struct nouveau_therm *therm) +{ + struct nouveau_therm_priv *priv = (void *)therm; + struct nouveau_therm_trip_point *trip = priv->fan->bios.trip, + *cur_trip = NULL, + *last_trip = priv->last_trip; + u8 temp = therm->temp_get(therm); + u16 duty, i; + + /* look for the trip point corresponding to the current temperature */ + cur_trip = NULL; + for (i = 0; i < priv->fan->bios.nr_fan_trip; i++) { + if (temp >= trip[i].temp) + cur_trip = &trip[i]; + } + + /* account for the hysteresis cycle */ + if (last_trip && temp <= (last_trip->temp) && + temp > (last_trip->temp - last_trip->hysteresis)) + cur_trip = last_trip; + + if (cur_trip) { + duty = cur_trip->fan_duty; + priv->last_trip = cur_trip; + } else { + duty = 0; + priv->last_trip = NULL; + } + + return duty; +} + +static int +nouveau_therm_update_linear(struct nouveau_therm *therm) +{ + struct nouveau_therm_priv *priv = (void *)therm; + u8 linear_min_temp = priv->fan->bios.linear_min_temp; + u8 linear_max_temp = priv->fan->bios.linear_max_temp; + u8 temp = therm->temp_get(therm); + u16 duty; + + /* handle the non-linear part first */ + if (temp < linear_min_temp) + return priv->fan->bios.min_duty; + else if (temp > linear_max_temp) + return priv->fan->bios.max_duty; + + /* we are in the linear zone */ + duty = (temp - linear_min_temp); + duty *= (priv->fan->bios.max_duty - priv->fan->bios.min_duty); + duty /= (linear_max_temp - linear_min_temp); + duty += priv->fan->bios.min_duty; + + return duty; +} + +static void +nouveau_therm_update(struct nouveau_therm *therm, int mode) +{ + struct nouveau_timer *ptimer = nouveau_timer(therm); + struct nouveau_therm_priv *priv = (void *)therm; + unsigned long flags; + int duty; + + spin_lock_irqsave(&priv->lock, flags); + if (mode < 0) + mode = priv->mode; + priv->mode = mode; + + switch (mode) { + case NOUVEAU_THERM_CTRL_MANUAL: + duty = nouveau_therm_fan_get(therm); + if (duty < 0) + duty = 100; + break; + case NOUVEAU_THERM_CTRL_AUTO: + if (priv->fan->bios.nr_fan_trip) + duty = nouveau_therm_update_trip(therm); + else + duty = nouveau_therm_update_linear(therm); + break; + case NOUVEAU_THERM_CTRL_NONE: + default: + goto done; + } + + nv_debug(therm, "FAN target request: %d%%\n", duty); + nouveau_therm_fan_set(therm, (mode != NOUVEAU_THERM_CTRL_AUTO), duty); + +done: + if (list_empty(&priv->alarm.head) && (mode == NOUVEAU_THERM_CTRL_AUTO)) + ptimer->alarm(ptimer, 1000000000ULL, &priv->alarm); + spin_unlock_irqrestore(&priv->lock, flags); +} + +static void +nouveau_therm_alarm(struct nouveau_alarm *alarm) +{ + struct nouveau_therm_priv *priv = + container_of(alarm, struct nouveau_therm_priv, alarm); + nouveau_therm_update(&priv->base, -1); +} + +int +nouveau_therm_mode(struct nouveau_therm *therm, int mode) +{ + struct nouveau_therm_priv *priv = (void *)therm; + struct nouveau_device *device = nv_device(therm); + static const char *name[] = { + "disabled", + "manual", + "automatic" + }; + + /* The default PDAEMON ucode interferes with fan management */ + if ((mode >= ARRAY_SIZE(name)) || + (mode != NOUVEAU_THERM_CTRL_NONE && device->card_type >= NV_C0)) + return -EINVAL; + + if (priv->mode == mode) + return 0; + + nv_info(therm, "Thermal management: %s\n", name[mode]); + nouveau_therm_update(therm, mode); + return 0; +} + int nouveau_therm_attr_get(struct nouveau_therm *therm, enum nouveau_therm_attr_type type) @@ -37,11 +165,11 @@ nouveau_therm_attr_get(struct nouveau_therm *therm, switch (type) { case NOUVEAU_THERM_ATTR_FAN_MIN_DUTY: - return priv->bios_fan.min_duty; + return priv->fan->bios.min_duty; case NOUVEAU_THERM_ATTR_FAN_MAX_DUTY: - return priv->bios_fan.max_duty; + return priv->fan->bios.max_duty; case NOUVEAU_THERM_ATTR_FAN_MODE: - return priv->fan.mode; + return priv->mode; case NOUVEAU_THERM_ATTR_THRS_FAN_BOOST: return priv->bios_sensor.thrs_fan_boost.temp; case NOUVEAU_THERM_ATTR_THRS_FAN_BOOST_HYST: @@ -73,42 +201,50 @@ nouveau_therm_attr_set(struct nouveau_therm *therm, case NOUVEAU_THERM_ATTR_FAN_MIN_DUTY: if (value < 0) value = 0; - if (value > priv->bios_fan.max_duty) - value = priv->bios_fan.max_duty; - priv->bios_fan.min_duty = value; + if (value > priv->fan->bios.max_duty) + value = priv->fan->bios.max_duty; + priv->fan->bios.min_duty = value; return 0; case NOUVEAU_THERM_ATTR_FAN_MAX_DUTY: if (value < 0) value = 0; - if (value < priv->bios_fan.min_duty) - value = priv->bios_fan.min_duty; - priv->bios_fan.max_duty = value; + if (value < priv->fan->bios.min_duty) + value = priv->fan->bios.min_duty; + priv->fan->bios.max_duty = value; return 0; case NOUVEAU_THERM_ATTR_FAN_MODE: - return nouveau_therm_fan_set_mode(therm, value); + return nouveau_therm_mode(therm, value); case NOUVEAU_THERM_ATTR_THRS_FAN_BOOST: priv->bios_sensor.thrs_fan_boost.temp = value; + priv->sensor.program_alarms(therm); return 0; case NOUVEAU_THERM_ATTR_THRS_FAN_BOOST_HYST: priv->bios_sensor.thrs_fan_boost.hysteresis = value; + priv->sensor.program_alarms(therm); return 0; case NOUVEAU_THERM_ATTR_THRS_DOWN_CLK: priv->bios_sensor.thrs_down_clock.temp = value; + priv->sensor.program_alarms(therm); return 0; case NOUVEAU_THERM_ATTR_THRS_DOWN_CLK_HYST: priv->bios_sensor.thrs_down_clock.hysteresis = value; + priv->sensor.program_alarms(therm); return 0; case NOUVEAU_THERM_ATTR_THRS_CRITICAL: priv->bios_sensor.thrs_critical.temp = value; + priv->sensor.program_alarms(therm); return 0; case NOUVEAU_THERM_ATTR_THRS_CRITICAL_HYST: priv->bios_sensor.thrs_critical.hysteresis = value; + priv->sensor.program_alarms(therm); return 0; case NOUVEAU_THERM_ATTR_THRS_SHUTDOWN: priv->bios_sensor.thrs_shutdown.temp = value; + priv->sensor.program_alarms(therm); return 0; case NOUVEAU_THERM_ATTR_THRS_SHUTDOWN_HYST: priv->bios_sensor.thrs_shutdown.hysteresis = value; + priv->sensor.program_alarms(therm); return 0; } @@ -116,7 +252,7 @@ nouveau_therm_attr_set(struct nouveau_therm *therm, } int -nouveau_therm_init(struct nouveau_object *object) +_nouveau_therm_init(struct nouveau_object *object) { struct nouveau_therm *therm = (void *)object; struct nouveau_therm_priv *priv = (void *)therm; @@ -126,19 +262,69 @@ nouveau_therm_init(struct nouveau_object *object) if (ret) return ret; - if (priv->fan.percent >= 0) - therm->fan_set(therm, priv->fan.percent); - + if (priv->suspend >= 0) + nouveau_therm_mode(therm, priv->mode); + priv->sensor.program_alarms(therm); return 0; } int -nouveau_therm_fini(struct nouveau_object *object, bool suspend) +_nouveau_therm_fini(struct nouveau_object *object, bool suspend) { struct nouveau_therm *therm = (void *)object; struct nouveau_therm_priv *priv = (void *)therm; - priv->fan.percent = therm->fan_get(therm); + if (suspend) { + priv->suspend = priv->mode; + priv->mode = NOUVEAU_THERM_CTRL_NONE; + } return nouveau_subdev_fini(&therm->base, suspend); } + +int +nouveau_therm_create_(struct nouveau_object *parent, + struct nouveau_object *engine, + struct nouveau_oclass *oclass, + int length, void **pobject) +{ + struct nouveau_therm_priv *priv; + int ret; + + ret = nouveau_subdev_create_(parent, engine, oclass, 0, "PTHERM", + "therm", length, pobject); + priv = *pobject; + if (ret) + return ret; + + nouveau_alarm_init(&priv->alarm, nouveau_therm_alarm); + spin_lock_init(&priv->lock); + spin_lock_init(&priv->sensor.alarm_program_lock); + + priv->base.fan_get = nouveau_therm_fan_user_get; + priv->base.fan_set = nouveau_therm_fan_user_set; + priv->base.fan_sense = nouveau_therm_fan_sense; + priv->base.attr_get = nouveau_therm_attr_get; + priv->base.attr_set = nouveau_therm_attr_set; + priv->mode = priv->suspend = -1; /* undefined */ + return 0; +} + +int +nouveau_therm_preinit(struct nouveau_therm *therm) +{ + nouveau_therm_ic_ctor(therm); + nouveau_therm_sensor_ctor(therm); + nouveau_therm_fan_ctor(therm); + + nouveau_therm_mode(therm, NOUVEAU_THERM_CTRL_NONE); + return 0; +} + +void +_nouveau_therm_dtor(struct nouveau_object *object) +{ + struct nouveau_therm_priv *priv = (void *)object; + kfree(priv->fan); + nouveau_subdev_destroy(&priv->base.base); +} diff --git a/drivers/gpu/drm/nouveau/core/subdev/therm/fan.c b/drivers/gpu/drm/nouveau/core/subdev/therm/fan.c index 523178685180..c728380d3d62 100644 --- a/drivers/gpu/drm/nouveau/core/subdev/therm/fan.c +++ b/drivers/gpu/drm/nouveau/core/subdev/therm/fan.c @@ -27,90 +27,107 @@ #include <core/object.h> #include <core/device.h> + #include <subdev/gpio.h> #include <subdev/timer.h> -int -nouveau_therm_fan_get(struct nouveau_therm *therm) +static int +nouveau_fan_update(struct nouveau_fan *fan, bool immediate, int target) { + struct nouveau_therm *therm = fan->parent; struct nouveau_therm_priv *priv = (void *)therm; - struct nouveau_gpio *gpio = nouveau_gpio(therm); - struct dcb_gpio_func func; - int card_type = nv_device(therm)->card_type; - u32 divs, duty; - int ret; - - if (!priv->fan.pwm_get) - return -ENODEV; + struct nouveau_timer *ptimer = nouveau_timer(priv); + unsigned long flags; + int ret = 0; + int duty; + + /* update target fan speed, restricting to allowed range */ + spin_lock_irqsave(&fan->lock, flags); + if (target < 0) + target = fan->percent; + target = max_t(u8, target, fan->bios.min_duty); + target = min_t(u8, target, fan->bios.max_duty); + if (fan->percent != target) { + nv_debug(therm, "FAN target: %d\n", target); + fan->percent = target; + } - ret = gpio->find(gpio, 0, DCB_GPIO_PWM_FAN, 0xff, &func); - if (ret == 0) { - ret = priv->fan.pwm_get(therm, func.line, &divs, &duty); - if (ret == 0 && divs) { - divs = max(divs, duty); - if (card_type <= NV_40 || (func.log[0] & 1)) - duty = divs - duty; - return (duty * 100) / divs; - } + /* check that we're not already at the target duty cycle */ + duty = fan->get(therm); + if (duty == target) + goto done; + + /* smooth out the fanspeed increase/decrease */ + if (!immediate && duty >= 0) { + /* the constant "3" is a rough approximation taken from + * nvidia's behaviour. + * it is meant to bump the fan speed more incrementally + */ + if (duty < target) + duty = min(duty + 3, target); + else if (duty > target) + duty = max(duty - 3, target); + } else { + duty = target; + } - return gpio->get(gpio, 0, func.func, func.line) * 100; + nv_debug(therm, "FAN update: %d\n", duty); + ret = fan->set(therm, duty); + if (ret) + goto done; + + /* schedule next fan update, if not at target speed already */ + if (list_empty(&fan->alarm.head) && target != duty) { + u16 bump_period = fan->bios.bump_period; + u16 slow_down_period = fan->bios.slow_down_period; + u64 delay; + + if (duty > target) + delay = slow_down_period; + else if (duty == target) + delay = min(bump_period, slow_down_period) ; + else + delay = bump_period; + + ptimer->alarm(ptimer, delay * 1000 * 1000, &fan->alarm); } - return -ENODEV; +done: + spin_unlock_irqrestore(&fan->lock, flags); + return ret; +} + +static void +nouveau_fan_alarm(struct nouveau_alarm *alarm) +{ + struct nouveau_fan *fan = container_of(alarm, struct nouveau_fan, alarm); + nouveau_fan_update(fan, false, -1); } int -nouveau_therm_fan_set(struct nouveau_therm *therm, int percent) +nouveau_therm_fan_get(struct nouveau_therm *therm) { struct nouveau_therm_priv *priv = (void *)therm; - struct nouveau_gpio *gpio = nouveau_gpio(therm); - struct dcb_gpio_func func; - int card_type = nv_device(therm)->card_type; - u32 divs, duty; - int ret; - - if (priv->fan.mode == FAN_CONTROL_NONE) - return -EINVAL; - - if (!priv->fan.pwm_set) - return -ENODEV; - - if (percent < priv->bios_fan.min_duty) - percent = priv->bios_fan.min_duty; - if (percent > priv->bios_fan.max_duty) - percent = priv->bios_fan.max_duty; - - ret = gpio->find(gpio, 0, DCB_GPIO_PWM_FAN, 0xff, &func); - if (ret == 0) { - divs = priv->bios_perf_fan.pwm_divisor; - if (priv->bios_fan.pwm_freq) { - divs = 1; - if (priv->fan.pwm_clock) - divs = priv->fan.pwm_clock(therm); - divs /= priv->bios_fan.pwm_freq; - } - - duty = ((divs * percent) + 99) / 100; - if (card_type <= NV_40 || (func.log[0] & 1)) - duty = divs - duty; - - ret = priv->fan.pwm_set(therm, func.line, divs, duty); - return ret; - } + return priv->fan->get(therm); +} - return -ENODEV; +int +nouveau_therm_fan_set(struct nouveau_therm *therm, bool immediate, int percent) +{ + struct nouveau_therm_priv *priv = (void *)therm; + return nouveau_fan_update(priv->fan, immediate, percent); } int nouveau_therm_fan_sense(struct nouveau_therm *therm) { + struct nouveau_therm_priv *priv = (void *)therm; struct nouveau_timer *ptimer = nouveau_timer(therm); struct nouveau_gpio *gpio = nouveau_gpio(therm); - struct dcb_gpio_func func; u32 cycles, cur, prev; u64 start, end, tach; - if (gpio->find(gpio, 0, DCB_GPIO_FAN_SENSE, 0xff, &func)) + if (priv->fan->tach.func == DCB_GPIO_UNUSED) return -ENODEV; /* Time a complete rotation and extrapolate to RPM: @@ -118,12 +135,12 @@ nouveau_therm_fan_sense(struct nouveau_therm *therm) * We get 4 changes (0 -> 1 -> 0 -> 1) per complete rotation. */ start = ptimer->read(ptimer); - prev = gpio->get(gpio, 0, func.func, func.line); + prev = gpio->get(gpio, 0, priv->fan->tach.func, priv->fan->tach.line); cycles = 0; do { usleep_range(500, 1000); /* supports 0 < rpm < 7500 */ - cur = gpio->get(gpio, 0, func.func, func.line); + cur = gpio->get(gpio, 0, priv->fan->tach.func, priv->fan->tach.line); if (prev != cur) { if (!start) start = ptimer->read(ptimer); @@ -142,34 +159,6 @@ nouveau_therm_fan_sense(struct nouveau_therm *therm) } int -nouveau_therm_fan_set_mode(struct nouveau_therm *therm, - enum nouveau_therm_fan_mode mode) -{ - struct nouveau_therm_priv *priv = (void *)therm; - - if (priv->fan.mode == mode) - return 0; - - if (mode < FAN_CONTROL_NONE || mode >= FAN_CONTROL_NR) - return -EINVAL; - - switch (mode) - { - case FAN_CONTROL_NONE: - nv_info(therm, "switch fan to no-control mode\n"); - break; - case FAN_CONTROL_MANUAL: - nv_info(therm, "switch fan to manual mode\n"); - break; - case FAN_CONTROL_NR: - break; - } - - priv->fan.mode = mode; - return 0; -} - -int nouveau_therm_fan_user_get(struct nouveau_therm *therm) { return nouveau_therm_fan_get(therm); @@ -180,55 +169,86 @@ nouveau_therm_fan_user_set(struct nouveau_therm *therm, int percent) { struct nouveau_therm_priv *priv = (void *)therm; - if (priv->fan.mode != FAN_CONTROL_MANUAL) + if (priv->mode != NOUVEAU_THERM_CTRL_MANUAL) return -EINVAL; - return nouveau_therm_fan_set(therm, percent); + return nouveau_therm_fan_set(therm, true, percent); } -void +static void nouveau_therm_fan_set_defaults(struct nouveau_therm *therm) { struct nouveau_therm_priv *priv = (void *)therm; - priv->bios_fan.pwm_freq = 0; - priv->bios_fan.min_duty = 0; - priv->bios_fan.max_duty = 100; + priv->fan->bios.pwm_freq = 0; + priv->fan->bios.min_duty = 0; + priv->fan->bios.max_duty = 100; + priv->fan->bios.bump_period = 500; + priv->fan->bios.slow_down_period = 2000; + priv->fan->bios.linear_min_temp = 40; + priv->fan->bios.linear_max_temp = 85; } - static void nouveau_therm_fan_safety_checks(struct nouveau_therm *therm) { struct nouveau_therm_priv *priv = (void *)therm; - if (priv->bios_fan.min_duty > 100) - priv->bios_fan.min_duty = 100; - if (priv->bios_fan.max_duty > 100) - priv->bios_fan.max_duty = 100; + if (priv->fan->bios.min_duty > 100) + priv->fan->bios.min_duty = 100; + if (priv->fan->bios.max_duty > 100) + priv->fan->bios.max_duty = 100; - if (priv->bios_fan.min_duty > priv->bios_fan.max_duty) - priv->bios_fan.min_duty = priv->bios_fan.max_duty; -} - -int nouveau_fan_pwm_clock_dummy(struct nouveau_therm *therm) -{ - return 1; + if (priv->fan->bios.min_duty > priv->fan->bios.max_duty) + priv->fan->bios.min_duty = priv->fan->bios.max_duty; } int nouveau_therm_fan_ctor(struct nouveau_therm *therm) { struct nouveau_therm_priv *priv = (void *)therm; + struct nouveau_gpio *gpio = nouveau_gpio(therm); struct nouveau_bios *bios = nouveau_bios(therm); + struct dcb_gpio_func func; + int ret; + /* attempt to locate a drivable fan, and determine control method */ + ret = gpio->find(gpio, 0, DCB_GPIO_FAN, 0xff, &func); + if (ret == 0) { + if (func.log[0] & DCB_GPIO_LOG_DIR_IN) { + nv_debug(therm, "GPIO_FAN is in input mode\n"); + ret = -EINVAL; + } else { + ret = nouveau_fanpwm_create(therm, &func); + if (ret != 0) + ret = nouveau_fantog_create(therm, &func); + } + } + + /* no controllable fan found, create a dummy fan module */ + if (ret != 0) { + ret = nouveau_fannil_create(therm); + if (ret) + return ret; + } + + nv_info(therm, "FAN control: %s\n", priv->fan->type); + + /* attempt to detect a tachometer connection */ + ret = gpio->find(gpio, 0, DCB_GPIO_FAN_SENSE, 0xff, &priv->fan->tach); + if (ret) + priv->fan->tach.func = DCB_GPIO_UNUSED; + + /* initialise fan bump/slow update handling */ + priv->fan->parent = therm; + nouveau_alarm_init(&priv->fan->alarm, nouveau_fan_alarm); + spin_lock_init(&priv->fan->lock); + + /* other random init... */ nouveau_therm_fan_set_defaults(therm); - nvbios_perf_fan_parse(bios, &priv->bios_perf_fan); - if (nvbios_therm_fan_parse(bios, &priv->bios_fan)) + nvbios_perf_fan_parse(bios, &priv->fan->perf); + if (nvbios_therm_fan_parse(bios, &priv->fan->bios)) nv_error(therm, "parsing the thermal table failed\n"); nouveau_therm_fan_safety_checks(therm); - - nouveau_therm_fan_set_mode(therm, FAN_CONTROL_NONE); - return 0; } diff --git a/drivers/gpu/drm/nouveau/core/subdev/therm/fannil.c b/drivers/gpu/drm/nouveau/core/subdev/therm/fannil.c new file mode 100644 index 000000000000..b78c182e1d51 --- /dev/null +++ b/drivers/gpu/drm/nouveau/core/subdev/therm/fannil.c @@ -0,0 +1,54 @@ +/* + * Copyright 2012 Red Hat Inc. + * + * 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 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 + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: Ben Skeggs + */ + +#include "priv.h" + +static int +nouveau_fannil_get(struct nouveau_therm *therm) +{ + return -ENODEV; +} + +static int +nouveau_fannil_set(struct nouveau_therm *therm, int percent) +{ + return -ENODEV; +} + +int +nouveau_fannil_create(struct nouveau_therm *therm) +{ + struct nouveau_therm_priv *tpriv = (void *)therm; + struct nouveau_fan *priv; + + priv = kzalloc(sizeof(*priv), GFP_KERNEL); + tpriv->fan = priv; + if (!priv) + return -ENOMEM; + + priv->type = "none / external"; + priv->get = nouveau_fannil_get; + priv->set = nouveau_fannil_set; + return 0; +} diff --git a/drivers/gpu/drm/nouveau/core/subdev/therm/fanpwm.c b/drivers/gpu/drm/nouveau/core/subdev/therm/fanpwm.c new file mode 100644 index 000000000000..5f71db8e8992 --- /dev/null +++ b/drivers/gpu/drm/nouveau/core/subdev/therm/fanpwm.c @@ -0,0 +1,107 @@ +/* + * Copyright 2012 Red Hat Inc. + * + * 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 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 + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: Ben Skeggs + * Martin Peres + */ + +#include <core/option.h> +#include <subdev/gpio.h> + +#include "priv.h" + +struct nouveau_fanpwm_priv { + struct nouveau_fan base; + struct dcb_gpio_func func; +}; + +static int +nouveau_fanpwm_get(struct nouveau_therm *therm) +{ + struct nouveau_therm_priv *tpriv = (void *)therm; + struct nouveau_fanpwm_priv *priv = (void *)tpriv->fan; + struct nouveau_gpio *gpio = nouveau_gpio(therm); + int card_type = nv_device(therm)->card_type; + u32 divs, duty; + int ret; + + ret = therm->pwm_get(therm, priv->func.line, &divs, &duty); + if (ret == 0 && divs) { + divs = max(divs, duty); + if (card_type <= NV_40 || (priv->func.log[0] & 1)) + duty = divs - duty; + return (duty * 100) / divs; + } + + return gpio->get(gpio, 0, priv->func.func, priv->func.line) * 100; +} + +static int +nouveau_fanpwm_set(struct nouveau_therm *therm, int percent) +{ + struct nouveau_therm_priv *tpriv = (void *)therm; + struct nouveau_fanpwm_priv *priv = (void *)tpriv->fan; + int card_type = nv_device(therm)->card_type; + u32 divs, duty; + int ret; + + divs = priv->base.perf.pwm_divisor; + if (priv->base.bios.pwm_freq) { + divs = 1; + if (therm->pwm_clock) + divs = therm->pwm_clock(therm); + divs /= priv->base.bios.pwm_freq; + } + + duty = ((divs * percent) + 99) / 100; + if (card_type <= NV_40 || (priv->func.log[0] & 1)) + duty = divs - duty; + + ret = therm->pwm_set(therm, priv->func.line, divs, duty); + if (ret == 0) + ret = therm->pwm_ctrl(therm, priv->func.line, true); + return ret; +} + +int +nouveau_fanpwm_create(struct nouveau_therm *therm, struct dcb_gpio_func *func) +{ + struct nouveau_device *device = nv_device(therm); + struct nouveau_therm_priv *tpriv = (void *)therm; + struct nouveau_fanpwm_priv *priv; + u32 divs, duty; + + if (!nouveau_boolopt(device->cfgopt, "NvFanPWM", func->param) || + !therm->pwm_ctrl || + therm->pwm_get(therm, func->line, &divs, &duty) == -ENODEV) + return -ENODEV; + + priv = kzalloc(sizeof(*priv), GFP_KERNEL); + tpriv->fan = &priv->base; + if (!priv) + return -ENOMEM; + + priv->base.type = "PWM"; + priv->base.get = nouveau_fanpwm_get; + priv->base.set = nouveau_fanpwm_set; + priv->func = *func; + return 0; +} diff --git a/drivers/gpu/drm/nouveau/core/subdev/therm/fantog.c b/drivers/gpu/drm/nouveau/core/subdev/therm/fantog.c new file mode 100644 index 000000000000..e601773ee475 --- /dev/null +++ b/drivers/gpu/drm/nouveau/core/subdev/therm/fantog.c @@ -0,0 +1,115 @@ +/* + * Copyright 2012 The Nouveau community + * + * 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 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 + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: Martin Peres + */ + +#include "priv.h" + +#include <core/object.h> +#include <core/device.h> + +#include <subdev/gpio.h> +#include <subdev/timer.h> + +struct nouveau_fantog_priv { + struct nouveau_fan base; + struct nouveau_alarm alarm; + spinlock_t lock; + u32 period_us; + u32 percent; + struct dcb_gpio_func func; +}; + +static void +nouveau_fantog_update(struct nouveau_fantog_priv *priv, int percent) +{ + struct nouveau_therm_priv *tpriv = (void *)priv->base.parent; + struct nouveau_timer *ptimer = nouveau_timer(tpriv); + struct nouveau_gpio *gpio = nouveau_gpio(tpriv); + unsigned long flags; + int duty; + + spin_lock_irqsave(&priv->lock, flags); + if (percent < 0) + percent = priv->percent; + priv->percent = percent; + + duty = !gpio->get(gpio, 0, DCB_GPIO_FAN, 0xff); + gpio->set(gpio, 0, DCB_GPIO_FAN, 0xff, duty); + + if (list_empty(&priv->alarm.head) && percent != (duty * 100)) { + u64 next_change = (percent * priv->period_us) / 100; + if (!duty) + next_change = priv->period_us - next_change; + ptimer->alarm(ptimer, next_change * 1000, &priv->alarm); + } + spin_unlock_irqrestore(&priv->lock, flags); +} + +static void +nouveau_fantog_alarm(struct nouveau_alarm *alarm) +{ + struct nouveau_fantog_priv *priv = + container_of(alarm, struct nouveau_fantog_priv, alarm); + nouveau_fantog_update(priv, -1); +} + +static int +nouveau_fantog_get(struct nouveau_therm *therm) +{ + struct nouveau_therm_priv *tpriv = (void *)therm; + struct nouveau_fantog_priv *priv = (void *)tpriv->fan; + return priv->percent; +} + +static int +nouveau_fantog_set(struct nouveau_therm *therm, int percent) +{ + struct nouveau_therm_priv *tpriv = (void *)therm; + struct nouveau_fantog_priv *priv = (void *)tpriv->fan; + if (therm->pwm_ctrl) + therm->pwm_ctrl(therm, priv->func.line, false); + nouveau_fantog_update(priv, percent); + return 0; +} + +int +nouveau_fantog_create(struct nouveau_therm *therm, struct dcb_gpio_func *func) +{ + struct nouveau_therm_priv *tpriv = (void *)therm; + struct nouveau_fantog_priv *priv; + + priv = kzalloc(sizeof(*priv), GFP_KERNEL); + tpriv->fan = &priv->base; + if (!priv) + return -ENOMEM; + + priv->base.type = "toggle"; + priv->base.get = nouveau_fantog_get; + priv->base.set = nouveau_fantog_set; + nouveau_alarm_init(&priv->alarm, nouveau_fantog_alarm); + priv->period_us = 100000; /* 10Hz */ + priv->percent = 100; + priv->func = *func; + spin_lock_init(&priv->lock); + return 0; +} diff --git a/drivers/gpu/drm/nouveau/core/subdev/therm/ic.c b/drivers/gpu/drm/nouveau/core/subdev/therm/ic.c index e512ff0aae60..e24090bac195 100644 --- a/drivers/gpu/drm/nouveau/core/subdev/therm/ic.c +++ b/drivers/gpu/drm/nouveau/core/subdev/therm/ic.c @@ -31,7 +31,7 @@ static bool probe_monitoring_device(struct nouveau_i2c_port *i2c, struct i2c_board_info *info) { - struct nouveau_therm_priv *priv = (void *)nouveau_therm(i2c->i2c); + struct nouveau_therm_priv *priv = (void *)nouveau_therm(i2c); struct i2c_client *client; request_module("%s%s", I2C_MODULE_PREFIX, info->type); @@ -53,6 +53,31 @@ probe_monitoring_device(struct nouveau_i2c_port *i2c, return true; } +static struct i2c_board_info +nv_board_infos[] = { + { I2C_BOARD_INFO("w83l785ts", 0x2d) }, + { I2C_BOARD_INFO("w83781d", 0x2d) }, + { I2C_BOARD_INFO("adt7473", 0x2e) }, + { I2C_BOARD_INFO("adt7473", 0x2d) }, + { I2C_BOARD_INFO("adt7473", 0x2c) }, + { I2C_BOARD_INFO("f75375", 0x2e) }, + { I2C_BOARD_INFO("lm99", 0x4c) }, + { I2C_BOARD_INFO("lm90", 0x4c) }, + { I2C_BOARD_INFO("lm90", 0x4d) }, + { I2C_BOARD_INFO("adm1021", 0x18) }, + { I2C_BOARD_INFO("adm1021", 0x19) }, + { I2C_BOARD_INFO("adm1021", 0x1a) }, + { I2C_BOARD_INFO("adm1021", 0x29) }, + { I2C_BOARD_INFO("adm1021", 0x2a) }, + { I2C_BOARD_INFO("adm1021", 0x2b) }, + { I2C_BOARD_INFO("adm1021", 0x4c) }, + { I2C_BOARD_INFO("adm1021", 0x4d) }, + { I2C_BOARD_INFO("adm1021", 0x4e) }, + { I2C_BOARD_INFO("lm63", 0x18) }, + { I2C_BOARD_INFO("lm63", 0x4e) }, + { } +}; + void nouveau_therm_ic_ctor(struct nouveau_therm *therm) { @@ -60,29 +85,6 @@ nouveau_therm_ic_ctor(struct nouveau_therm *therm) struct nouveau_bios *bios = nouveau_bios(therm); struct nouveau_i2c *i2c = nouveau_i2c(therm); struct nvbios_extdev_func extdev_entry; - struct i2c_board_info info[] = { - { I2C_BOARD_INFO("w83l785ts", 0x2d) }, - { I2C_BOARD_INFO("w83781d", 0x2d) }, - { I2C_BOARD_INFO("adt7473", 0x2e) }, - { I2C_BOARD_INFO("adt7473", 0x2d) }, - { I2C_BOARD_INFO("adt7473", 0x2c) }, - { I2C_BOARD_INFO("f75375", 0x2e) }, - { I2C_BOARD_INFO("lm99", 0x4c) }, - { I2C_BOARD_INFO("lm90", 0x4c) }, - { I2C_BOARD_INFO("lm90", 0x4d) }, - { I2C_BOARD_INFO("adm1021", 0x18) }, - { I2C_BOARD_INFO("adm1021", 0x19) }, - { I2C_BOARD_INFO("adm1021", 0x1a) }, - { I2C_BOARD_INFO("adm1021", 0x29) }, - { I2C_BOARD_INFO("adm1021", 0x2a) }, - { I2C_BOARD_INFO("adm1021", 0x2b) }, - { I2C_BOARD_INFO("adm1021", 0x4c) }, - { I2C_BOARD_INFO("adm1021", 0x4d) }, - { I2C_BOARD_INFO("adm1021", 0x4e) }, - { I2C_BOARD_INFO("lm63", 0x18) }, - { I2C_BOARD_INFO("lm63", 0x4e) }, - { } - }; if (!nvbios_extdev_find(bios, NVBIOS_EXTDEV_LM89, &extdev_entry)) { struct i2c_board_info board[] = { @@ -111,6 +113,6 @@ nouveau_therm_ic_ctor(struct nouveau_therm *therm) /* The vbios doesn't provide the address of an exisiting monitoring device. Let's try our static list. */ - i2c->identify(i2c, NV_I2C_DEFAULT(0), "monitoring device", info, - probe_monitoring_device); + i2c->identify(i2c, NV_I2C_DEFAULT(0), "monitoring device", + nv_board_infos, probe_monitoring_device); } diff --git a/drivers/gpu/drm/nouveau/core/subdev/therm/nv40.c b/drivers/gpu/drm/nouveau/core/subdev/therm/nv40.c index fcf2cfe731d6..0f5363edb964 100644 --- a/drivers/gpu/drm/nouveau/core/subdev/therm/nv40.c +++ b/drivers/gpu/drm/nouveau/core/subdev/therm/nv40.c @@ -25,6 +25,10 @@ #include "priv.h" +struct nv40_therm_priv { + struct nouveau_therm_priv base; +}; + static int nv40_sensor_setup(struct nouveau_therm *therm) { @@ -34,6 +38,7 @@ nv40_sensor_setup(struct nouveau_therm *therm) if (device->chipset >= 0x46) { nv_mask(therm, 0x15b8, 0x80000000, 0); nv_wr32(therm, 0x15b0, 0x80003fff); + mdelay(10); /* wait for the temperature to stabilize */ return nv_rd32(therm, 0x15b4) & 0x3fff; } else { nv_wr32(therm, 0x15b0, 0xff); @@ -75,7 +80,20 @@ nv40_temp_get(struct nouveau_therm *therm) return core_temp; } -int +static int +nv40_fan_pwm_ctrl(struct nouveau_therm *therm, int line, bool enable) +{ + u32 mask = enable ? 0x80000000 : 0x0000000; + if (line == 2) nv_mask(therm, 0x0010f0, 0x80000000, mask); + else if (line == 9) nv_mask(therm, 0x0015f4, 0x80000000, mask); + else { + nv_error(therm, "unknown pwm ctrl for gpio %d\n", line); + return -ENODEV; + } + return 0; +} + +static int nv40_fan_pwm_get(struct nouveau_therm *therm, int line, u32 *divs, u32 *duty) { if (line == 2) { @@ -101,15 +119,15 @@ nv40_fan_pwm_get(struct nouveau_therm *therm, int line, u32 *divs, u32 *duty) return -EINVAL; } -int +static int nv40_fan_pwm_set(struct nouveau_therm *therm, int line, u32 divs, u32 duty) { if (line == 2) { - nv_wr32(therm, 0x0010f0, 0x80000000 | (duty << 16) | divs); + nv_mask(therm, 0x0010f0, 0x7fff7fff, (duty << 16) | divs); } else if (line == 9) { nv_wr32(therm, 0x0015f8, divs); - nv_wr32(therm, 0x0015f4, duty | 0x80000000); + nv_mask(therm, 0x0015f4, 0x7fffffff, duty); } else { nv_error(therm, "unknown pwm ctrl for gpio %d\n", line); return -ENODEV; @@ -118,37 +136,51 @@ nv40_fan_pwm_set(struct nouveau_therm *therm, int line, u32 divs, u32 duty) return 0; } +static void +nv40_therm_intr(struct nouveau_subdev *subdev) +{ + struct nouveau_therm *therm = nouveau_therm(subdev); + uint32_t stat = nv_rd32(therm, 0x1100); + + /* traitement */ + + /* ack all IRQs */ + nv_wr32(therm, 0x1100, 0x70000); + + nv_error(therm, "THERM received an IRQ: stat = %x\n", stat); +} + static int nv40_therm_ctor(struct nouveau_object *parent, - struct nouveau_object *engine, - struct nouveau_oclass *oclass, void *data, u32 size, - struct nouveau_object **pobject) + struct nouveau_object *engine, + struct nouveau_oclass *oclass, void *data, u32 size, + struct nouveau_object **pobject) { - struct nouveau_therm_priv *priv; - struct nouveau_therm *therm; + struct nv40_therm_priv *priv; int ret; ret = nouveau_therm_create(parent, engine, oclass, &priv); *pobject = nv_object(priv); - therm = (void *) priv; if (ret) return ret; - nouveau_therm_ic_ctor(therm); - nouveau_therm_sensor_ctor(therm); - nouveau_therm_fan_ctor(therm); + priv->base.base.pwm_ctrl = nv40_fan_pwm_ctrl; + priv->base.base.pwm_get = nv40_fan_pwm_get; + priv->base.base.pwm_set = nv40_fan_pwm_set; + priv->base.base.temp_get = nv40_temp_get; + priv->base.sensor.program_alarms = nouveau_therm_program_alarms_polling; + nv_subdev(priv)->intr = nv40_therm_intr; + return nouveau_therm_preinit(&priv->base.base); +} - priv->fan.pwm_get = nv40_fan_pwm_get; - priv->fan.pwm_set = nv40_fan_pwm_set; +static int +nv40_therm_init(struct nouveau_object *object) +{ + struct nouveau_therm *therm = (void *)object; - therm->temp_get = nv40_temp_get; - therm->fan_get = nouveau_therm_fan_user_get; - therm->fan_set = nouveau_therm_fan_user_set; - therm->fan_sense = nouveau_therm_fan_sense; - therm->attr_get = nouveau_therm_attr_get; - therm->attr_set = nouveau_therm_attr_set; + nv40_sensor_setup(therm); - return 0; + return _nouveau_therm_init(object); } struct nouveau_oclass @@ -157,7 +189,7 @@ nv40_therm_oclass = { .ofuncs = &(struct nouveau_ofuncs) { .ctor = nv40_therm_ctor, .dtor = _nouveau_therm_dtor, - .init = nouveau_therm_init, - .fini = nouveau_therm_fini, + .init = nv40_therm_init, + .fini = _nouveau_therm_fini, }, -};
\ No newline at end of file +}; diff --git a/drivers/gpu/drm/nouveau/core/subdev/therm/nv50.c b/drivers/gpu/drm/nouveau/core/subdev/therm/nv50.c index 9360ddd469e7..86632cbd65ce 100644 --- a/drivers/gpu/drm/nouveau/core/subdev/therm/nv50.c +++ b/drivers/gpu/drm/nouveau/core/subdev/therm/nv50.c @@ -25,6 +25,10 @@ #include "priv.h" +struct nv50_therm_priv { + struct nouveau_therm_priv base; +}; + static int pwm_info(struct nouveau_therm *therm, int *line, int *ctrl, int *indx) { @@ -51,6 +55,16 @@ pwm_info(struct nouveau_therm *therm, int *line, int *ctrl, int *indx) } int +nv50_fan_pwm_ctrl(struct nouveau_therm *therm, int line, bool enable) +{ + u32 data = enable ? 0x00000001 : 0x00000000; + int ctrl, id, ret = pwm_info(therm, &line, &ctrl, &id); + if (ret == 0) + nv_mask(therm, ctrl, 0x00010001 << line, data << line); + return ret; +} + +int nv50_fan_pwm_get(struct nouveau_therm *therm, int line, u32 *divs, u32 *duty) { int ctrl, id, ret = pwm_info(therm, &line, &ctrl, &id); @@ -73,7 +87,6 @@ nv50_fan_pwm_set(struct nouveau_therm *therm, int line, u32 divs, u32 duty) if (ret) return ret; - nv_mask(therm, ctrl, 0x00010001 << line, 0x00000001 << line); nv_wr32(therm, 0x00e114 + (id * 8), divs); nv_wr32(therm, 0x00e118 + (id * 8), duty | 0x80000000); return 0; @@ -111,38 +124,178 @@ nv50_temp_get(struct nouveau_therm *therm) return nv_rd32(therm, 0x20400); } +static void +nv50_therm_program_alarms(struct nouveau_therm *therm) +{ + struct nouveau_therm_priv *priv = (void *)therm; + struct nvbios_therm_sensor *sensor = &priv->bios_sensor; + unsigned long flags; + + spin_lock_irqsave(&priv->sensor.alarm_program_lock, flags); + + /* enable RISING and FALLING IRQs for shutdown, THRS 0, 1, 2 and 4 */ + nv_wr32(therm, 0x20000, 0x000003ff); + + /* shutdown: The computer should be shutdown when reached */ + nv_wr32(therm, 0x20484, sensor->thrs_shutdown.hysteresis); + nv_wr32(therm, 0x20480, sensor->thrs_shutdown.temp); + + /* THRS_1 : fan boost*/ + nv_wr32(therm, 0x204c4, sensor->thrs_fan_boost.temp); + + /* THRS_2 : critical */ + nv_wr32(therm, 0x204c0, sensor->thrs_critical.temp); + + /* THRS_4 : down clock */ + nv_wr32(therm, 0x20414, sensor->thrs_down_clock.temp); + spin_unlock_irqrestore(&priv->sensor.alarm_program_lock, flags); + + nv_info(therm, + "Programmed thresholds [ %d(%d), %d(%d), %d(%d), %d(%d) ]\n", + sensor->thrs_fan_boost.temp, sensor->thrs_fan_boost.hysteresis, + sensor->thrs_down_clock.temp, + sensor->thrs_down_clock.hysteresis, + sensor->thrs_critical.temp, sensor->thrs_critical.hysteresis, + sensor->thrs_shutdown.temp, sensor->thrs_shutdown.hysteresis); + +} + +/* must be called with alarm_program_lock taken ! */ +static void +nv50_therm_threshold_hyst_emulation(struct nouveau_therm *therm, + uint32_t thrs_reg, u8 status_bit, + const struct nvbios_therm_threshold *thrs, + enum nouveau_therm_thrs thrs_name) +{ + enum nouveau_therm_thrs_direction direction; + enum nouveau_therm_thrs_state prev_state, new_state; + int temp, cur; + + prev_state = nouveau_therm_sensor_get_threshold_state(therm, thrs_name); + temp = nv_rd32(therm, thrs_reg); + + /* program the next threshold */ + if (temp == thrs->temp) { + nv_wr32(therm, thrs_reg, thrs->temp - thrs->hysteresis); + new_state = NOUVEAU_THERM_THRS_HIGHER; + } else { + nv_wr32(therm, thrs_reg, thrs->temp); + new_state = NOUVEAU_THERM_THRS_LOWER; + } + + /* fix the state (in case someone reprogrammed the alarms) */ + cur = therm->temp_get(therm); + if (new_state == NOUVEAU_THERM_THRS_LOWER && cur > thrs->temp) + new_state = NOUVEAU_THERM_THRS_HIGHER; + else if (new_state == NOUVEAU_THERM_THRS_HIGHER && + cur < thrs->temp - thrs->hysteresis) + new_state = NOUVEAU_THERM_THRS_LOWER; + nouveau_therm_sensor_set_threshold_state(therm, thrs_name, new_state); + + /* find the direction */ + if (prev_state < new_state) + direction = NOUVEAU_THERM_THRS_RISING; + else if (prev_state > new_state) + direction = NOUVEAU_THERM_THRS_FALLING; + else + return; + + /* advertise a change in direction */ + nouveau_therm_sensor_event(therm, thrs_name, direction); +} + +static void +nv50_therm_intr(struct nouveau_subdev *subdev) +{ + struct nouveau_therm *therm = nouveau_therm(subdev); + struct nouveau_therm_priv *priv = (void *)therm; + struct nvbios_therm_sensor *sensor = &priv->bios_sensor; + unsigned long flags; + uint32_t intr; + + spin_lock_irqsave(&priv->sensor.alarm_program_lock, flags); + + intr = nv_rd32(therm, 0x20100); + + /* THRS_4: downclock */ + if (intr & 0x002) { + nv50_therm_threshold_hyst_emulation(therm, 0x20414, 24, + &sensor->thrs_down_clock, + NOUVEAU_THERM_THRS_DOWNCLOCK); + intr &= ~0x002; + } + + /* shutdown */ + if (intr & 0x004) { + nv50_therm_threshold_hyst_emulation(therm, 0x20480, 20, + &sensor->thrs_shutdown, + NOUVEAU_THERM_THRS_SHUTDOWN); + intr &= ~0x004; + } + + /* THRS_1 : fan boost */ + if (intr & 0x008) { + nv50_therm_threshold_hyst_emulation(therm, 0x204c4, 21, + &sensor->thrs_fan_boost, + NOUVEAU_THERM_THRS_FANBOOST); + intr &= ~0x008; + } + + /* THRS_2 : critical */ + if (intr & 0x010) { + nv50_therm_threshold_hyst_emulation(therm, 0x204c0, 22, + &sensor->thrs_critical, + NOUVEAU_THERM_THRS_CRITICAL); + intr &= ~0x010; + } + + if (intr) + nv_error(therm, "unhandled intr 0x%08x\n", intr); + + /* ACK everything */ + nv_wr32(therm, 0x20100, 0xffffffff); + nv_wr32(therm, 0x1100, 0x10000); /* PBUS */ + + spin_unlock_irqrestore(&priv->sensor.alarm_program_lock, flags); +} + static int nv50_therm_ctor(struct nouveau_object *parent, - struct nouveau_object *engine, - struct nouveau_oclass *oclass, void *data, u32 size, - struct nouveau_object **pobject) + struct nouveau_object *engine, + struct nouveau_oclass *oclass, void *data, u32 size, + struct nouveau_object **pobject) { - struct nouveau_therm_priv *priv; - struct nouveau_therm *therm; + struct nv50_therm_priv *priv; int ret; ret = nouveau_therm_create(parent, engine, oclass, &priv); *pobject = nv_object(priv); - therm = (void *) priv; if (ret) return ret; - nouveau_therm_ic_ctor(therm); - nouveau_therm_sensor_ctor(therm); - nouveau_therm_fan_ctor(therm); + priv->base.base.pwm_ctrl = nv50_fan_pwm_ctrl; + priv->base.base.pwm_get = nv50_fan_pwm_get; + priv->base.base.pwm_set = nv50_fan_pwm_set; + priv->base.base.pwm_clock = nv50_fan_pwm_clock; + priv->base.base.temp_get = nv50_temp_get; + priv->base.sensor.program_alarms = nv50_therm_program_alarms; + nv_subdev(priv)->intr = nv50_therm_intr; - priv->fan.pwm_get = nv50_fan_pwm_get; - priv->fan.pwm_set = nv50_fan_pwm_set; - priv->fan.pwm_clock = nv50_fan_pwm_clock; + /* init the thresholds */ + nouveau_therm_sensor_set_threshold_state(&priv->base.base, + NOUVEAU_THERM_THRS_SHUTDOWN, + NOUVEAU_THERM_THRS_LOWER); + nouveau_therm_sensor_set_threshold_state(&priv->base.base, + NOUVEAU_THERM_THRS_FANBOOST, + NOUVEAU_THERM_THRS_LOWER); + nouveau_therm_sensor_set_threshold_state(&priv->base.base, + NOUVEAU_THERM_THRS_CRITICAL, + NOUVEAU_THERM_THRS_LOWER); + nouveau_therm_sensor_set_threshold_state(&priv->base.base, + NOUVEAU_THERM_THRS_DOWNCLOCK, + NOUVEAU_THERM_THRS_LOWER); - therm->temp_get = nv50_temp_get; - therm->fan_get = nouveau_therm_fan_user_get; - therm->fan_set = nouveau_therm_fan_user_set; - therm->fan_sense = nouveau_therm_fan_sense; - therm->attr_get = nouveau_therm_attr_get; - therm->attr_set = nouveau_therm_attr_set; - - return 0; + return nouveau_therm_preinit(&priv->base.base); } struct nouveau_oclass @@ -151,7 +304,7 @@ nv50_therm_oclass = { .ofuncs = &(struct nouveau_ofuncs) { .ctor = nv50_therm_ctor, .dtor = _nouveau_therm_dtor, - .init = nouveau_therm_init, - .fini = nouveau_therm_fini, + .init = _nouveau_therm_init, + .fini = _nouveau_therm_fini, }, }; diff --git a/drivers/gpu/drm/nouveau/core/subdev/therm/nva3.c b/drivers/gpu/drm/nouveau/core/subdev/therm/nva3.c new file mode 100644 index 000000000000..2dcc5437116a --- /dev/null +++ b/drivers/gpu/drm/nouveau/core/subdev/therm/nva3.c @@ -0,0 +1,99 @@ +/* + * Copyright 2012 Red Hat Inc. + * + * 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 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 + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: Ben Skeggs + */ + +#include <subdev/gpio.h> + +#include "priv.h" + +struct nva3_therm_priv { + struct nouveau_therm_priv base; +}; + +int +nva3_therm_fan_sense(struct nouveau_therm *therm) +{ + u32 tach = nv_rd32(therm, 0x00e728) & 0x0000ffff; + u32 ctrl = nv_rd32(therm, 0x00e720); + if (ctrl & 0x00000001) + return tach * 60; + return -ENODEV; +} + +static int +nva3_therm_init(struct nouveau_object *object) +{ + struct nva3_therm_priv *priv = (void *)object; + struct dcb_gpio_func *tach = &priv->base.fan->tach; + int ret; + + ret = nouveau_therm_init(&priv->base.base); + if (ret) + return ret; + + /* enable fan tach, count revolutions per-second */ + nv_mask(priv, 0x00e720, 0x00000003, 0x00000002); + if (tach->func != DCB_GPIO_UNUSED) { + nv_wr32(priv, 0x00e724, nv_device(priv)->crystal * 1000); + nv_mask(priv, 0x00e720, 0x001f0000, tach->line << 16); + nv_mask(priv, 0x00e720, 0x00000001, 0x00000001); + } + nv_mask(priv, 0x00e720, 0x00000002, 0x00000000); + + return 0; +} + +static int +nva3_therm_ctor(struct nouveau_object *parent, + struct nouveau_object *engine, + struct nouveau_oclass *oclass, void *data, u32 size, + struct nouveau_object **pobject) +{ + struct nva3_therm_priv *priv; + int ret; + + ret = nouveau_therm_create(parent, engine, oclass, &priv); + *pobject = nv_object(priv); + if (ret) + return ret; + + priv->base.base.pwm_ctrl = nv50_fan_pwm_ctrl; + priv->base.base.pwm_get = nv50_fan_pwm_get; + priv->base.base.pwm_set = nv50_fan_pwm_set; + priv->base.base.pwm_clock = nv50_fan_pwm_clock; + priv->base.base.temp_get = nv50_temp_get; + priv->base.base.fan_sense = nva3_therm_fan_sense; + priv->base.sensor.program_alarms = nouveau_therm_program_alarms_polling; + return nouveau_therm_preinit(&priv->base.base); +} + +struct nouveau_oclass +nva3_therm_oclass = { + .handle = NV_SUBDEV(THERM, 0xa3), + .ofuncs = &(struct nouveau_ofuncs) { + .ctor = nva3_therm_ctor, + .dtor = _nouveau_therm_dtor, + .init = nva3_therm_init, + .fini = _nouveau_therm_fini, + }, +}; diff --git a/drivers/gpu/drm/nouveau/core/subdev/therm/nvd0.c b/drivers/gpu/drm/nouveau/core/subdev/therm/nvd0.c new file mode 100644 index 000000000000..d7d30ee8332e --- /dev/null +++ b/drivers/gpu/drm/nouveau/core/subdev/therm/nvd0.c @@ -0,0 +1,153 @@ +/* + * Copyright 2012 Red Hat Inc. + * + * 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 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 + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: Ben Skeggs + */ + +#include "priv.h" + +struct nvd0_therm_priv { + struct nouveau_therm_priv base; +}; + +static int +pwm_info(struct nouveau_therm *therm, int line) +{ + u32 gpio = nv_rd32(therm, 0x00d610 + (line * 0x04)); + switch (gpio & 0x000000c0) { + case 0x00000000: /* normal mode, possibly pwm forced off by us */ + case 0x00000040: /* nvio special */ + switch (gpio & 0x0000001f) { + case 0x19: return 1; + case 0x1c: return 0; + default: + break; + } + default: + break; + } + + nv_error(therm, "GPIO %d unknown PWM: 0x%08x\n", line, gpio); + return -ENODEV; +} + +static int +nvd0_fan_pwm_ctrl(struct nouveau_therm *therm, int line, bool enable) +{ + u32 data = enable ? 0x00000040 : 0x00000000; + int indx = pwm_info(therm, line); + if (indx < 0) + return indx; + + nv_mask(therm, 0x00d610 + (line * 0x04), 0x000000c0, data); + return 0; +} + +static int +nvd0_fan_pwm_get(struct nouveau_therm *therm, int line, u32 *divs, u32 *duty) +{ + int indx = pwm_info(therm, line); + if (indx < 0) + return indx; + + if (nv_rd32(therm, 0x00d610 + (line * 0x04)) & 0x00000040) { + *divs = nv_rd32(therm, 0x00e114 + (indx * 8)); + *duty = nv_rd32(therm, 0x00e118 + (indx * 8)); + return 0; + } + + return -EINVAL; +} + +static int +nvd0_fan_pwm_set(struct nouveau_therm *therm, int line, u32 divs, u32 duty) +{ + int indx = pwm_info(therm, line); + if (indx < 0) + return indx; + + nv_wr32(therm, 0x00e114 + (indx * 8), divs); + nv_wr32(therm, 0x00e118 + (indx * 8), duty | 0x80000000); + return 0; +} + +static int +nvd0_fan_pwm_clock(struct nouveau_therm *therm) +{ + return (nv_device(therm)->crystal * 1000) / 20; +} + +static int +nvd0_therm_init(struct nouveau_object *object) +{ + struct nvd0_therm_priv *priv = (void *)object; + int ret; + + ret = nouveau_therm_init(&priv->base.base); + if (ret) + return ret; + + /* enable fan tach, count revolutions per-second */ + nv_mask(priv, 0x00e720, 0x00000003, 0x00000002); + if (priv->base.fan->tach.func != DCB_GPIO_UNUSED) { + nv_mask(priv, 0x00d79c, 0x000000ff, priv->base.fan->tach.line); + nv_wr32(priv, 0x00e724, nv_device(priv)->crystal * 1000); + nv_mask(priv, 0x00e720, 0x00000001, 0x00000001); + } + nv_mask(priv, 0x00e720, 0x00000002, 0x00000000); + + return 0; +} + +static int +nvd0_therm_ctor(struct nouveau_object *parent, + struct nouveau_object *engine, + struct nouveau_oclass *oclass, void *data, u32 size, + struct nouveau_object **pobject) +{ + struct nvd0_therm_priv *priv; + int ret; + + ret = nouveau_therm_create(parent, engine, oclass, &priv); + *pobject = nv_object(priv); + if (ret) + return ret; + + priv->base.base.pwm_ctrl = nvd0_fan_pwm_ctrl; + priv->base.base.pwm_get = nvd0_fan_pwm_get; + priv->base.base.pwm_set = nvd0_fan_pwm_set; + priv->base.base.pwm_clock = nvd0_fan_pwm_clock; + priv->base.base.temp_get = nv50_temp_get; + priv->base.base.fan_sense = nva3_therm_fan_sense; + priv->base.sensor.program_alarms = nouveau_therm_program_alarms_polling; + return nouveau_therm_preinit(&priv->base.base); +} + +struct nouveau_oclass +nvd0_therm_oclass = { + .handle = NV_SUBDEV(THERM, 0xd0), + .ofuncs = &(struct nouveau_ofuncs) { + .ctor = nvd0_therm_ctor, + .dtor = _nouveau_therm_dtor, + .init = nvd0_therm_init, + .fini = _nouveau_therm_fini, + }, +}; diff --git a/drivers/gpu/drm/nouveau/core/subdev/therm/priv.h b/drivers/gpu/drm/nouveau/core/subdev/therm/priv.h index 1c3cd6abc36e..06b98706b3fc 100644 --- a/drivers/gpu/drm/nouveau/core/subdev/therm/priv.h +++ b/drivers/gpu/drm/nouveau/core/subdev/therm/priv.h @@ -1,3 +1,6 @@ +#ifndef __NVTHERM_PRIV_H__ +#define __NVTHERM_PRIV_H__ + /* * Copyright 2012 The Nouveau community * @@ -25,33 +28,81 @@ #include <subdev/therm.h> #include <subdev/bios/extdev.h> +#include <subdev/bios/gpio.h> #include <subdev/bios/perf.h> #include <subdev/bios/therm.h> +#include <subdev/timer.h> + +struct nouveau_fan { + struct nouveau_therm *parent; + const char *type; + + struct nvbios_therm_fan bios; + struct nvbios_perf_fan perf; + + struct nouveau_alarm alarm; + spinlock_t lock; + int percent; + + int (*get)(struct nouveau_therm *therm); + int (*set)(struct nouveau_therm *therm, int percent); + + struct dcb_gpio_func tach; +}; + +enum nouveau_therm_thrs_direction { + NOUVEAU_THERM_THRS_FALLING = 0, + NOUVEAU_THERM_THRS_RISING = 1 +}; + +enum nouveau_therm_thrs_state { + NOUVEAU_THERM_THRS_LOWER = 0, + NOUVEAU_THERM_THRS_HIGHER = 1 +}; + +enum nouveau_therm_thrs { + NOUVEAU_THERM_THRS_FANBOOST = 0, + NOUVEAU_THERM_THRS_DOWNCLOCK = 1, + NOUVEAU_THERM_THRS_CRITICAL = 2, + NOUVEAU_THERM_THRS_SHUTDOWN = 3, + NOUVEAU_THERM_THRS_NR +}; struct nouveau_therm_priv { struct nouveau_therm base; + /* automatic thermal management */ + struct nouveau_alarm alarm; + spinlock_t lock; + struct nouveau_therm_trip_point *last_trip; + int mode; + int suspend; + /* bios */ struct nvbios_therm_sensor bios_sensor; - struct nvbios_therm_fan bios_fan; - struct nvbios_perf_fan bios_perf_fan; /* fan priv */ + struct nouveau_fan *fan; + + /* alarms priv */ struct { - enum nouveau_therm_fan_mode mode; - int percent; + spinlock_t alarm_program_lock; + struct nouveau_alarm therm_poll_alarm; + enum nouveau_therm_thrs_state alarm_state[NOUVEAU_THERM_THRS_NR]; + void (*program_alarms)(struct nouveau_therm *); + } sensor; - int (*pwm_get)(struct nouveau_therm *, int line, u32*, u32*); - int (*pwm_set)(struct nouveau_therm *, int line, u32, u32); - int (*pwm_clock)(struct nouveau_therm *); - } fan; + /* what should be done if the card overheats */ + struct { + void (*downclock)(struct nouveau_therm *, bool active); + void (*pause)(struct nouveau_therm *, bool active); + } emergency; /* ic */ struct i2c_client *ic; }; -int nouveau_therm_init(struct nouveau_object *object); -int nouveau_therm_fini(struct nouveau_object *object, bool suspend); +int nouveau_therm_mode(struct nouveau_therm *therm, int mode); int nouveau_therm_attr_get(struct nouveau_therm *therm, enum nouveau_therm_attr_type type); int nouveau_therm_attr_set(struct nouveau_therm *therm, @@ -63,11 +114,35 @@ int nouveau_therm_sensor_ctor(struct nouveau_therm *therm); int nouveau_therm_fan_ctor(struct nouveau_therm *therm); int nouveau_therm_fan_get(struct nouveau_therm *therm); -int nouveau_therm_fan_set(struct nouveau_therm *therm, int percent); +int nouveau_therm_fan_set(struct nouveau_therm *therm, bool now, int percent); int nouveau_therm_fan_user_get(struct nouveau_therm *therm); int nouveau_therm_fan_user_set(struct nouveau_therm *therm, int percent); -int nouveau_therm_fan_set_mode(struct nouveau_therm *therm, - enum nouveau_therm_fan_mode mode); - int nouveau_therm_fan_sense(struct nouveau_therm *therm); + +int nouveau_therm_preinit(struct nouveau_therm *); + +void nouveau_therm_sensor_set_threshold_state(struct nouveau_therm *therm, + enum nouveau_therm_thrs thrs, + enum nouveau_therm_thrs_state st); +enum nouveau_therm_thrs_state +nouveau_therm_sensor_get_threshold_state(struct nouveau_therm *therm, + enum nouveau_therm_thrs thrs); +void nouveau_therm_sensor_event(struct nouveau_therm *therm, + enum nouveau_therm_thrs thrs, + enum nouveau_therm_thrs_direction dir); +void nouveau_therm_program_alarms_polling(struct nouveau_therm *therm); + +int nv50_fan_pwm_ctrl(struct nouveau_therm *, int, bool); +int nv50_fan_pwm_get(struct nouveau_therm *, int, u32 *, u32 *); +int nv50_fan_pwm_set(struct nouveau_therm *, int, u32, u32); +int nv50_fan_pwm_clock(struct nouveau_therm *); +int nv50_temp_get(struct nouveau_therm *therm); + +int nva3_therm_fan_sense(struct nouveau_therm *); + +int nouveau_fanpwm_create(struct nouveau_therm *, struct dcb_gpio_func *); +int nouveau_fantog_create(struct nouveau_therm *, struct dcb_gpio_func *); +int nouveau_fannil_create(struct nouveau_therm *); + +#endif diff --git a/drivers/gpu/drm/nouveau/core/subdev/therm/temp.c b/drivers/gpu/drm/nouveau/core/subdev/therm/temp.c index 204282301fb1..b37624af8297 100644 --- a/drivers/gpu/drm/nouveau/core/subdev/therm/temp.c +++ b/drivers/gpu/drm/nouveau/core/subdev/therm/temp.c @@ -58,11 +58,171 @@ static void nouveau_therm_temp_safety_checks(struct nouveau_therm *therm) { struct nouveau_therm_priv *priv = (void *)therm; + struct nvbios_therm_sensor *s = &priv->bios_sensor; if (!priv->bios_sensor.slope_div) priv->bios_sensor.slope_div = 1; if (!priv->bios_sensor.offset_den) priv->bios_sensor.offset_den = 1; + + /* enforce a minimum hysteresis on thresholds */ + s->thrs_fan_boost.hysteresis = max_t(u8, s->thrs_fan_boost.hysteresis, 2); + s->thrs_down_clock.hysteresis = max_t(u8, s->thrs_down_clock.hysteresis, 2); + s->thrs_critical.hysteresis = max_t(u8, s->thrs_critical.hysteresis, 2); + s->thrs_shutdown.hysteresis = max_t(u8, s->thrs_shutdown.hysteresis, 2); +} + +/* must be called with alarm_program_lock taken ! */ +void nouveau_therm_sensor_set_threshold_state(struct nouveau_therm *therm, + enum nouveau_therm_thrs thrs, + enum nouveau_therm_thrs_state st) +{ + struct nouveau_therm_priv *priv = (void *)therm; + priv->sensor.alarm_state[thrs] = st; +} + +/* must be called with alarm_program_lock taken ! */ +enum nouveau_therm_thrs_state +nouveau_therm_sensor_get_threshold_state(struct nouveau_therm *therm, + enum nouveau_therm_thrs thrs) +{ + struct nouveau_therm_priv *priv = (void *)therm; + return priv->sensor.alarm_state[thrs]; +} + +static void +nv_poweroff_work(struct work_struct *work) +{ + orderly_poweroff(true); + kfree(work); +} + +void nouveau_therm_sensor_event(struct nouveau_therm *therm, + enum nouveau_therm_thrs thrs, + enum nouveau_therm_thrs_direction dir) +{ + struct nouveau_therm_priv *priv = (void *)therm; + bool active; + const char *thresolds[] = { + "fanboost", "downclock", "critical", "shutdown" + }; + uint8_t temperature = therm->temp_get(therm); + + if (thrs < 0 || thrs > 3) + return; + + if (dir == NOUVEAU_THERM_THRS_FALLING) + nv_info(therm, "temperature (%u C) went below the '%s' threshold\n", + temperature, thresolds[thrs]); + else + nv_info(therm, "temperature (%u C) hit the '%s' threshold\n", + temperature, thresolds[thrs]); + + active = (dir == NOUVEAU_THERM_THRS_RISING); + switch (thrs) { + case NOUVEAU_THERM_THRS_FANBOOST: + if (active) { + nouveau_therm_fan_set(therm, true, 100); + nouveau_therm_mode(therm, NOUVEAU_THERM_CTRL_AUTO); + } + break; + case NOUVEAU_THERM_THRS_DOWNCLOCK: + if (priv->emergency.downclock) + priv->emergency.downclock(therm, active); + break; + case NOUVEAU_THERM_THRS_CRITICAL: + if (priv->emergency.pause) + priv->emergency.pause(therm, active); + break; + case NOUVEAU_THERM_THRS_SHUTDOWN: + if (active) { + struct work_struct *work; + + work = kmalloc(sizeof(*work), GFP_ATOMIC); + if (work) { + INIT_WORK(work, nv_poweroff_work); + schedule_work(work); + } + } + break; + case NOUVEAU_THERM_THRS_NR: + break; + } + +} + +/* must be called with alarm_program_lock taken ! */ +static void +nouveau_therm_threshold_hyst_polling(struct nouveau_therm *therm, + const struct nvbios_therm_threshold *thrs, + enum nouveau_therm_thrs thrs_name) +{ + enum nouveau_therm_thrs_direction direction; + enum nouveau_therm_thrs_state prev_state, new_state; + int temp = therm->temp_get(therm); + + prev_state = nouveau_therm_sensor_get_threshold_state(therm, thrs_name); + + if (temp >= thrs->temp && prev_state == NOUVEAU_THERM_THRS_LOWER) { + direction = NOUVEAU_THERM_THRS_RISING; + new_state = NOUVEAU_THERM_THRS_HIGHER; + } else if (temp <= thrs->temp - thrs->hysteresis && + prev_state == NOUVEAU_THERM_THRS_HIGHER) { + direction = NOUVEAU_THERM_THRS_FALLING; + new_state = NOUVEAU_THERM_THRS_LOWER; + } else + return; /* nothing to do */ + + nouveau_therm_sensor_set_threshold_state(therm, thrs_name, new_state); + nouveau_therm_sensor_event(therm, thrs_name, direction); +} + +static void +alarm_timer_callback(struct nouveau_alarm *alarm) +{ + struct nouveau_therm_priv *priv = + container_of(alarm, struct nouveau_therm_priv, sensor.therm_poll_alarm); + struct nvbios_therm_sensor *sensor = &priv->bios_sensor; + struct nouveau_timer *ptimer = nouveau_timer(priv); + struct nouveau_therm *therm = &priv->base; + unsigned long flags; + + spin_lock_irqsave(&priv->sensor.alarm_program_lock, flags); + + nouveau_therm_threshold_hyst_polling(therm, &sensor->thrs_fan_boost, + NOUVEAU_THERM_THRS_FANBOOST); + + nouveau_therm_threshold_hyst_polling(therm, &sensor->thrs_down_clock, + NOUVEAU_THERM_THRS_DOWNCLOCK); + + nouveau_therm_threshold_hyst_polling(therm, &sensor->thrs_critical, + NOUVEAU_THERM_THRS_CRITICAL); + + nouveau_therm_threshold_hyst_polling(therm, &sensor->thrs_shutdown, + NOUVEAU_THERM_THRS_SHUTDOWN); + + /* schedule the next poll in one second */ + if (list_empty(&alarm->head)) + ptimer->alarm(ptimer, 1000 * 1000 * 1000, alarm); + + spin_unlock_irqrestore(&priv->sensor.alarm_program_lock, flags); +} + +void +nouveau_therm_program_alarms_polling(struct nouveau_therm *therm) +{ + struct nouveau_therm_priv *priv = (void *)therm; + struct nvbios_therm_sensor *sensor = &priv->bios_sensor; + + nv_info(therm, + "programmed thresholds [ %d(%d), %d(%d), %d(%d), %d(%d) ]\n", + sensor->thrs_fan_boost.temp, sensor->thrs_fan_boost.hysteresis, + sensor->thrs_down_clock.temp, + sensor->thrs_down_clock.hysteresis, + sensor->thrs_critical.temp, sensor->thrs_critical.hysteresis, + sensor->thrs_shutdown.temp, sensor->thrs_shutdown.hysteresis); + + alarm_timer_callback(&priv->sensor.therm_poll_alarm); } int @@ -71,6 +231,8 @@ nouveau_therm_sensor_ctor(struct nouveau_therm *therm) struct nouveau_therm_priv *priv = (void *)therm; struct nouveau_bios *bios = nouveau_bios(therm); + nouveau_alarm_init(&priv->sensor.therm_poll_alarm, alarm_timer_callback); + nouveau_therm_temp_set_defaults(therm); if (nvbios_therm_sensor_parse(bios, NVBIOS_THERM_DOMAIN_CORE, &priv->bios_sensor)) diff --git a/drivers/gpu/drm/nouveau/core/subdev/timer/nv04.c b/drivers/gpu/drm/nouveau/core/subdev/timer/nv04.c index c26ca9bef671..8e1bae4f12e8 100644 --- a/drivers/gpu/drm/nouveau/core/subdev/timer/nv04.c +++ b/drivers/gpu/drm/nouveau/core/subdev/timer/nv04.c @@ -79,7 +79,7 @@ nv04_timer_alarm_trigger(struct nouveau_timer *ptimer) /* execute any pending alarm handlers */ list_for_each_entry_safe(alarm, atemp, &exec, head) { - list_del(&alarm->head); + list_del_init(&alarm->head); alarm->func(alarm); } } diff --git a/drivers/gpu/drm/nouveau/nouveau_acpi.h b/drivers/gpu/drm/nouveau/nouveau_acpi.h index d0da230d7706..74acf0f87785 100644 --- a/drivers/gpu/drm/nouveau/nouveau_acpi.h +++ b/drivers/gpu/drm/nouveau/nouveau_acpi.h @@ -3,7 +3,7 @@ #define ROM_BIOS_PAGE 4096 -#if defined(CONFIG_ACPI) +#if defined(CONFIG_ACPI) && defined(CONFIG_X86) bool nouveau_is_optimus(void); bool nouveau_is_v1_dsm(void); void nouveau_register_dsm_handler(void); diff --git a/drivers/gpu/drm/nouveau/nouveau_backlight.c b/drivers/gpu/drm/nouveau/nouveau_backlight.c index f65b20a375f6..5d940302d2aa 100644 --- a/drivers/gpu/drm/nouveau/nouveau_backlight.c +++ b/drivers/gpu/drm/nouveau/nouveau_backlight.c @@ -84,6 +84,8 @@ nv40_backlight_init(struct drm_connector *connector) props.max_brightness = 31; bd = backlight_device_register("nv_backlight", &connector->kdev, drm, &nv40_bl_ops, &props); + if (IS_ERR(bd)) + return PTR_ERR(bd); drm->backlight = bd; bd->props.brightness = nv40_get_intensity(bd); backlight_update_status(bd); diff --git a/drivers/gpu/drm/nouveau/nouveau_bios.c b/drivers/gpu/drm/nouveau/nouveau_bios.c index 865eddfa30a7..50a6dd02f7c5 100644 --- a/drivers/gpu/drm/nouveau/nouveau_bios.c +++ b/drivers/gpu/drm/nouveau/nouveau_bios.c @@ -678,23 +678,6 @@ int run_tmds_table(struct drm_device *dev, struct dcb_output *dcbent, int head, return 0; } -static void parse_bios_version(struct drm_device *dev, struct nvbios *bios, uint16_t offset) -{ - /* - * offset + 0 (8 bits): Micro version - * offset + 1 (8 bits): Minor version - * offset + 2 (8 bits): Chip version - * offset + 3 (8 bits): Major version - */ - struct nouveau_drm *drm = nouveau_drm(dev); - - bios->major_version = bios->data[offset + 3]; - bios->chip_version = bios->data[offset + 2]; - NV_INFO(drm, "Bios version %02x.%02x.%02x.%02x\n", - bios->data[offset + 3], bios->data[offset + 2], - bios->data[offset + 1], bios->data[offset]); -} - static void parse_script_table_pointers(struct nvbios *bios, uint16_t offset) { /* @@ -710,12 +693,6 @@ static void parse_script_table_pointers(struct nvbios *bios, uint16_t offset) */ bios->init_script_tbls_ptr = ROM16(bios->data[offset]); - bios->macro_index_tbl_ptr = ROM16(bios->data[offset + 2]); - bios->macro_tbl_ptr = ROM16(bios->data[offset + 4]); - bios->condition_tbl_ptr = ROM16(bios->data[offset + 6]); - bios->io_condition_tbl_ptr = ROM16(bios->data[offset + 8]); - bios->io_flag_condition_tbl_ptr = ROM16(bios->data[offset + 10]); - bios->init_function_tbl_ptr = ROM16(bios->data[offset + 12]); } static int parse_bit_A_tbl_entry(struct drm_device *dev, struct nvbios *bios, struct bit_entry *bitentry) @@ -765,25 +742,6 @@ static int parse_bit_A_tbl_entry(struct drm_device *dev, struct nvbios *bios, st return 0; } -static int parse_bit_C_tbl_entry(struct drm_device *dev, struct nvbios *bios, struct bit_entry *bitentry) -{ - /* - * offset + 8 (16 bits): PLL limits table pointer - * - * There's more in here, but that's unknown. - */ - struct nouveau_drm *drm = nouveau_drm(dev); - - if (bitentry->length < 10) { - NV_ERROR(drm, "Do not understand BIT C table\n"); - return -EINVAL; - } - - bios->pll_limit_tbl_ptr = ROM16(bios->data[bitentry->offset + 8]); - - return 0; -} - static int parse_bit_display_tbl_entry(struct drm_device *dev, struct nvbios *bios, struct bit_entry *bitentry) { /* @@ -821,12 +779,6 @@ static int parse_bit_init_tbl_entry(struct drm_device *dev, struct nvbios *bios, } parse_script_table_pointers(bios, bitentry->offset); - - if (bitentry->length >= 16) - bios->some_script_ptr = ROM16(bios->data[bitentry->offset + 14]); - if (bitentry->length >= 18) - bios->init96_tbl_ptr = ROM16(bios->data[bitentry->offset + 16]); - return 0; } @@ -852,8 +804,6 @@ static int parse_bit_i_tbl_entry(struct drm_device *dev, struct nvbios *bios, st return -EINVAL; } - parse_bios_version(dev, bios, bitentry->offset); - /* * bit 4 seems to indicate a mobile bios (doesn't suffer from BMP's * Quadro identity crisis), other bits possibly as for BMP feature byte @@ -1078,9 +1028,6 @@ parse_bit_structure(struct nvbios *bios, const uint16_t bitoffset) return ret; if (bios->major_version >= 0x60) /* g80+ */ parse_bit_table(bios, bitoffset, &BIT_TABLE('A', A)); - ret = parse_bit_table(bios, bitoffset, &BIT_TABLE('C', C)); - if (ret) - return ret; parse_bit_table(bios, bitoffset, &BIT_TABLE('D', display)); ret = parse_bit_table(bios, bitoffset, &BIT_TABLE('I', init)); if (ret) @@ -1228,8 +1175,6 @@ static int parse_bmp_structure(struct drm_device *dev, struct nvbios *bios, unsi */ bios->feature_byte = bmp[9]; - parse_bios_version(dev, bios, offset + 10); - if (bmp_version_major < 5 || bmp_version_minor < 0x10) bios->old_style_init = true; legacy_scripts_offset = 18; @@ -1276,8 +1221,10 @@ static int parse_bmp_structure(struct drm_device *dev, struct nvbios *bios, unsi bios->fp.lvdsmanufacturerpointer = ROM16(bmp[117]); bios->fp.fpxlatemanufacturertableptr = ROM16(bmp[119]); } +#if 0 if (bmplength > 143) bios->pll_limit_tbl_ptr = ROM16(bmp[142]); +#endif if (bmplength > 157) bios->fp.duallink_transition_clk = ROM16(bmp[156]) * 10; @@ -1522,6 +1469,7 @@ parse_dcb20_entry(struct drm_device *dev, struct dcb_table *dcb, } case DCB_OUTPUT_DP: entry->dpconf.sor.link = (conf & 0x00000030) >> 4; + entry->extdev = (conf & 0x0000ff00) >> 8; switch ((conf & 0x00e00000) >> 21) { case 0: entry->dpconf.link_bw = 162000; @@ -1543,8 +1491,10 @@ parse_dcb20_entry(struct drm_device *dev, struct dcb_table *dcb, } break; case DCB_OUTPUT_TMDS: - if (dcb->version >= 0x40) + if (dcb->version >= 0x40) { entry->tmdsconf.sor.link = (conf & 0x00000030) >> 4; + entry->extdev = (conf & 0x0000ff00) >> 8; + } else if (dcb->version >= 0x30) entry->tmdsconf.slave_addr = (conf & 0x00000700) >> 8; else if (dcb->version >= 0x22) @@ -1937,9 +1887,9 @@ parse_dcb_table(struct drm_device *dev, struct nvbios *bios) if (conn[0] != 0xff) { NV_INFO(drm, "DCB conn %02d: ", idx); if (olddcb_conntab(dev)[3] < 4) - printk("%04x\n", ROM16(conn[0])); + pr_cont("%04x\n", ROM16(conn[0])); else - printk("%08x\n", ROM32(conn[0])); + pr_cont("%08x\n", ROM32(conn[0])); } } dcb_fake_connectors(bios); @@ -2052,45 +2002,29 @@ uint8_t *nouveau_bios_embedded_edid(struct drm_device *dev) static bool NVInitVBIOS(struct drm_device *dev) { struct nouveau_drm *drm = nouveau_drm(dev); - struct nvbios *bios = &drm->vbios; - - memset(bios, 0, sizeof(struct nvbios)); - spin_lock_init(&bios->lock); - bios->dev = dev; - - bios->data = nouveau_bios(drm->device)->data; - bios->length = nouveau_bios(drm->device)->size; - return true; -} + struct nouveau_bios *bios = nouveau_bios(drm->device); + struct nvbios *legacy = &drm->vbios; + + memset(legacy, 0, sizeof(struct nvbios)); + spin_lock_init(&legacy->lock); + legacy->dev = dev; + + legacy->data = bios->data; + legacy->length = bios->size; + legacy->major_version = bios->version.major; + legacy->chip_version = bios->version.chip; + if (bios->bit_offset) { + legacy->type = NVBIOS_BIT; + legacy->offset = bios->bit_offset; + return !parse_bit_structure(legacy, legacy->offset + 6); + } else + if (bios->bmp_offset) { + legacy->type = NVBIOS_BMP; + legacy->offset = bios->bmp_offset; + return !parse_bmp_structure(dev, legacy, legacy->offset); + } -static int nouveau_parse_vbios_struct(struct drm_device *dev) -{ - struct nouveau_drm *drm = nouveau_drm(dev); - struct nvbios *bios = &drm->vbios; - const uint8_t bit_signature[] = { 0xff, 0xb8, 'B', 'I', 'T' }; - const uint8_t bmp_signature[] = { 0xff, 0x7f, 'N', 'V', 0x0 }; - int offset; - - offset = findstr(bios->data, bios->length, - bit_signature, sizeof(bit_signature)); - if (offset) { - NV_INFO(drm, "BIT BIOS found\n"); - bios->type = NVBIOS_BIT; - bios->offset = offset; - return parse_bit_structure(bios, offset + 6); - } - - offset = findstr(bios->data, bios->length, - bmp_signature, sizeof(bmp_signature)); - if (offset) { - NV_INFO(drm, "BMP BIOS found\n"); - bios->type = NVBIOS_BMP; - bios->offset = offset; - return parse_bmp_structure(dev, bios, offset); - } - - NV_ERROR(drm, "No known BIOS signature found\n"); - return -ENODEV; + return false; } int @@ -2146,10 +2080,6 @@ nouveau_bios_init(struct drm_device *dev) if (!NVInitVBIOS(dev)) return -ENODEV; - ret = nouveau_parse_vbios_struct(dev); - if (ret) - return ret; - ret = parse_dcb_table(dev, bios); if (ret) return ret; diff --git a/drivers/gpu/drm/nouveau/nouveau_bios.h b/drivers/gpu/drm/nouveau/nouveau_bios.h index f68c54ca422f..7ccd28f11adf 100644 --- a/drivers/gpu/drm/nouveau/nouveau_bios.h +++ b/drivers/gpu/drm/nouveau/nouveau_bios.h @@ -107,20 +107,10 @@ struct nvbios { bool old_style_init; uint16_t init_script_tbls_ptr; uint16_t extra_init_script_tbl_ptr; - uint16_t macro_index_tbl_ptr; - uint16_t macro_tbl_ptr; - uint16_t condition_tbl_ptr; - uint16_t io_condition_tbl_ptr; - uint16_t io_flag_condition_tbl_ptr; - uint16_t init_function_tbl_ptr; - - uint16_t pll_limit_tbl_ptr; + uint16_t ram_restrict_tbl_ptr; uint8_t ram_restrict_group_count; - uint16_t some_script_ptr; /* BIT I + 14 */ - uint16_t init96_tbl_ptr; /* BIT I + 16 */ - struct dcb_table dcb; struct { diff --git a/drivers/gpu/drm/nouveau/nouveau_bo.c b/drivers/gpu/drm/nouveau/nouveau_bo.c index 69d7b1d0b9d6..11ca82148edc 100644 --- a/drivers/gpu/drm/nouveau/nouveau_bo.c +++ b/drivers/gpu/drm/nouveau/nouveau_bo.c @@ -28,6 +28,7 @@ */ #include <core/engine.h> +#include <linux/swiotlb.h> #include <subdev/fb.h> #include <subdev/vm.h> @@ -300,17 +301,18 @@ nouveau_bo_pin(struct nouveau_bo *nvbo, uint32_t memtype) struct ttm_buffer_object *bo = &nvbo->bo; int ret; + ret = ttm_bo_reserve(bo, false, false, false, 0); + if (ret) + goto out; + if (nvbo->pin_refcnt && !(memtype & (1 << bo->mem.mem_type))) { NV_ERROR(drm, "bo %p pinned elsewhere: 0x%08x vs 0x%08x\n", bo, 1 << bo->mem.mem_type, memtype); - return -EINVAL; + ret = -EINVAL; + goto out; } if (nvbo->pin_refcnt++) - return 0; - - ret = ttm_bo_reserve(bo, false, false, false, 0); - if (ret) goto out; nouveau_bo_placement_set(nvbo, memtype, 0); @@ -328,10 +330,8 @@ nouveau_bo_pin(struct nouveau_bo *nvbo, uint32_t memtype) break; } } - ttm_bo_unreserve(bo); out: - if (unlikely(ret)) - nvbo->pin_refcnt--; + ttm_bo_unreserve(bo); return ret; } @@ -342,13 +342,13 @@ nouveau_bo_unpin(struct nouveau_bo *nvbo) struct ttm_buffer_object *bo = &nvbo->bo; int ret; - if (--nvbo->pin_refcnt) - return 0; - ret = ttm_bo_reserve(bo, false, false, false, 0); if (ret) return ret; + if (--nvbo->pin_refcnt) + goto out; + nouveau_bo_placement_set(nvbo, bo->mem.placement, 0); ret = nouveau_bo_validate(nvbo, false, false); @@ -365,6 +365,7 @@ nouveau_bo_unpin(struct nouveau_bo *nvbo) } } +out: ttm_bo_unreserve(bo); return ret; } @@ -561,7 +562,7 @@ nouveau_bo_move_accel_cleanup(struct nouveau_channel *chan, struct nouveau_fence *fence = NULL; int ret; - ret = nouveau_fence_new(chan, &fence); + ret = nouveau_fence_new(chan, false, &fence); if (ret) return ret; diff --git a/drivers/gpu/drm/nouveau/nouveau_bo.h b/drivers/gpu/drm/nouveau/nouveau_bo.h index 25ca37989d2c..653dbbbd4fa1 100644 --- a/drivers/gpu/drm/nouveau/nouveau_bo.h +++ b/drivers/gpu/drm/nouveau/nouveau_bo.h @@ -28,10 +28,11 @@ struct nouveau_bo { struct nouveau_drm_tile *tile; struct drm_gem_object *gem; + + /* protect by the ttm reservation lock */ int pin_refcnt; struct ttm_bo_kmap_obj dma_buf_vmap; - int vmapping_count; }; static inline struct nouveau_bo * diff --git a/drivers/gpu/drm/nouveau/nouveau_chan.c b/drivers/gpu/drm/nouveau/nouveau_chan.c index 174300b6a02e..eaa80a2b81ee 100644 --- a/drivers/gpu/drm/nouveau/nouveau_chan.c +++ b/drivers/gpu/drm/nouveau/nouveau_chan.c @@ -51,14 +51,15 @@ nouveau_channel_idle(struct nouveau_channel *chan) struct nouveau_fence *fence = NULL; int ret; - ret = nouveau_fence_new(chan, &fence); + ret = nouveau_fence_new(chan, false, &fence); if (!ret) { ret = nouveau_fence_wait(fence, false, false); nouveau_fence_unref(&fence); } if (ret) - NV_ERROR(cli, "failed to idle channel 0x%08x\n", chan->handle); + NV_ERROR(cli, "failed to idle channel 0x%08x [%s]\n", + chan->handle, cli->base.name); return ret; } diff --git a/drivers/gpu/drm/nouveau/nouveau_connector.c b/drivers/gpu/drm/nouveau/nouveau_connector.c index e620ba8271b4..4dd7ae2ac6c6 100644 --- a/drivers/gpu/drm/nouveau/nouveau_connector.c +++ b/drivers/gpu/drm/nouveau/nouveau_connector.c @@ -55,8 +55,6 @@ MODULE_PARM_DESC(duallink, "Allow dual-link TMDS (default: enabled)"); static int nouveau_duallink = 1; module_param_named(duallink, nouveau_duallink, int, 0400); -static void nouveau_connector_hotplug(void *, int); - struct nouveau_encoder * find_encoder(struct drm_connector *connector, int type) { @@ -100,22 +98,6 @@ static void nouveau_connector_destroy(struct drm_connector *connector) { struct nouveau_connector *nv_connector = nouveau_connector(connector); - struct nouveau_gpio *gpio; - struct nouveau_drm *drm; - struct drm_device *dev; - - if (!nv_connector) - return; - - dev = nv_connector->base.dev; - drm = nouveau_drm(dev); - gpio = nouveau_gpio(drm->device); - - if (gpio && nv_connector->hpd != DCB_GPIO_UNUSED) { - gpio->isr_del(gpio, 0, nv_connector->hpd, 0xff, - nouveau_connector_hotplug, connector); - } - kfree(nv_connector->edid); drm_sysfs_connector_remove(connector); drm_connector_cleanup(connector); @@ -130,7 +112,6 @@ nouveau_connector_ddc_detect(struct drm_connector *connector, struct nouveau_connector *nv_connector = nouveau_connector(connector); struct nouveau_drm *drm = nouveau_drm(dev); struct nouveau_gpio *gpio = nouveau_gpio(drm->device); - struct nouveau_i2c *i2c = nouveau_i2c(drm->device); struct nouveau_i2c_port *port = NULL; int i, panel = -ENODEV; @@ -160,8 +141,7 @@ nouveau_connector_ddc_detect(struct drm_connector *connector, continue; nv_encoder = nouveau_encoder(obj_to_encoder(obj)); - if (nv_encoder->dcb->i2c_index < 0xf) - port = i2c->find(i2c, nv_encoder->dcb->i2c_index); + port = nv_encoder->i2c; if (port && nv_probe_i2c(port, 0x50)) { *pnv_encoder = nv_encoder; break; @@ -399,9 +379,10 @@ nouveau_connector_detect_lvds(struct drm_connector *connector, bool force) struct edid *edid = (struct edid *)nouveau_bios_embedded_edid(dev); if (edid) { - nv_connector->edid = kmalloc(EDID_LENGTH, GFP_KERNEL); - *(nv_connector->edid) = *edid; - status = connector_status_connected; + nv_connector->edid = + kmemdup(edid, EDID_LENGTH, GFP_KERNEL); + if (nv_connector->edid) + status = connector_status_connected; } } @@ -911,6 +892,37 @@ nouveau_connector_funcs_lvds = { .force = nouveau_connector_force }; +static void +nouveau_connector_hotplug_work(struct work_struct *work) +{ + struct nouveau_connector *nv_connector = + container_of(work, struct nouveau_connector, hpd_work); + struct drm_connector *connector = &nv_connector->base; + struct drm_device *dev = connector->dev; + struct nouveau_drm *drm = nouveau_drm(dev); + struct nouveau_gpio *gpio = nouveau_gpio(drm->device); + bool plugged = gpio->get(gpio, 0, nv_connector->hpd.func, 0xff); + + NV_DEBUG(drm, "%splugged %s\n", plugged ? "" : "un", + drm_get_connector_name(connector)); + + if (plugged) + drm_helper_connector_dpms(connector, DRM_MODE_DPMS_ON); + else + drm_helper_connector_dpms(connector, DRM_MODE_DPMS_OFF); + + drm_helper_hpd_irq_event(dev); +} + +static int +nouveau_connector_hotplug(struct nouveau_eventh *event, int index) +{ + struct nouveau_connector *nv_connector = + container_of(event, struct nouveau_connector, hpd_func); + schedule_work(&nv_connector->hpd_work); + return NVKM_EVENT_KEEP; +} + static int drm_conntype_from_dcb(enum dcb_connector_type dcb) { @@ -961,6 +973,7 @@ nouveau_connector_create(struct drm_device *dev, int index) return ERR_PTR(-ENOMEM); connector = &nv_connector->base; + INIT_WORK(&nv_connector->hpd_work, nouveau_connector_hotplug_work); nv_connector->index = index; /* attempt to parse vbios connector type and hotplug gpio */ @@ -975,8 +988,11 @@ nouveau_connector_create(struct drm_device *dev, int index) if (olddcb_conntab(dev)[3] >= 4) entry |= (u32)ROM16(nv_connector->dcb[2]) << 16; - nv_connector->hpd = ffs((entry & 0x07033000) >> 12); - nv_connector->hpd = hpd[nv_connector->hpd]; + ret = gpio->find(gpio, 0, hpd[ffs((entry & 0x07033000) >> 12)], + DCB_GPIO_UNUSED, &nv_connector->hpd); + nv_connector->hpd_func.func = nouveau_connector_hotplug; + if (ret) + nv_connector->hpd.func = DCB_GPIO_UNUSED; nv_connector->type = nv_connector->dcb[0]; if (drm_conntype_from_dcb(nv_connector->type) == @@ -999,7 +1015,7 @@ nouveau_connector_create(struct drm_device *dev, int index) } } else { nv_connector->type = DCB_CONNECTOR_NONE; - nv_connector->hpd = DCB_GPIO_UNUSED; + nv_connector->hpd.func = DCB_GPIO_UNUSED; } /* no vbios data, or an unknown dcb connector type - attempt to @@ -1126,31 +1142,9 @@ nouveau_connector_create(struct drm_device *dev, int index) } connector->polled = DRM_CONNECTOR_POLL_CONNECT; - if (gpio && nv_connector->hpd != DCB_GPIO_UNUSED) { - ret = gpio->isr_add(gpio, 0, nv_connector->hpd, 0xff, - nouveau_connector_hotplug, connector); - if (ret == 0) - connector->polled = DRM_CONNECTOR_POLL_HPD; - } + if (nv_connector->hpd.func != DCB_GPIO_UNUSED) + connector->polled = DRM_CONNECTOR_POLL_HPD; drm_sysfs_connector_add(connector); return connector; } - -static void -nouveau_connector_hotplug(void *data, int plugged) -{ - struct drm_connector *connector = data; - struct drm_device *dev = connector->dev; - struct nouveau_drm *drm = nouveau_drm(dev); - - NV_DEBUG(drm, "%splugged %s\n", plugged ? "" : "un", - drm_get_connector_name(connector)); - - if (plugged) - drm_helper_connector_dpms(connector, DRM_MODE_DPMS_ON); - else - drm_helper_connector_dpms(connector, DRM_MODE_DPMS_OFF); - - drm_helper_hpd_irq_event(dev); -} diff --git a/drivers/gpu/drm/nouveau/nouveau_connector.h b/drivers/gpu/drm/nouveau/nouveau_connector.h index 20eb84cce9e6..6e399aad491a 100644 --- a/drivers/gpu/drm/nouveau/nouveau_connector.h +++ b/drivers/gpu/drm/nouveau/nouveau_connector.h @@ -30,6 +30,11 @@ #include <drm/drm_edid.h> #include "nouveau_crtc.h" +#include <core/event.h> + +#include <subdev/bios.h> +#include <subdev/bios/gpio.h> + struct nouveau_i2c_port; enum nouveau_underscan_type { @@ -61,7 +66,10 @@ struct nouveau_connector { enum dcb_connector_type type; u8 index; u8 *dcb; - u8 hpd; + + struct dcb_gpio_func hpd; + struct work_struct hpd_work; + struct nouveau_eventh hpd_func; int dithering_mode; int dithering_depth; diff --git a/drivers/gpu/drm/nouveau/nouveau_debugfs.c b/drivers/gpu/drm/nouveau/nouveau_debugfs.c new file mode 100644 index 000000000000..5392e07edfc6 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nouveau_debugfs.c @@ -0,0 +1,64 @@ +/* + * Copyright (C) 2009 Red Hat <bskeggs@redhat.com> + * + * 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 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. + * + */ + +/* + * Authors: + * Ben Skeggs <bskeggs@redhat.com> + */ + +#include "nouveau_debugfs.h" +#include "nouveau_drm.h" + +static int +nouveau_debugfs_vbios_image(struct seq_file *m, void *data) +{ + struct drm_info_node *node = (struct drm_info_node *) m->private; + struct nouveau_drm *drm = nouveau_drm(node->minor->dev); + int i; + + for (i = 0; i < drm->vbios.length; i++) + seq_printf(m, "%c", drm->vbios.data[i]); + return 0; +} + +static struct drm_info_list nouveau_debugfs_list[] = { + { "vbios.rom", nouveau_debugfs_vbios_image, 0, NULL }, +}; +#define NOUVEAU_DEBUGFS_ENTRIES ARRAY_SIZE(nouveau_debugfs_list) + +int +nouveau_debugfs_init(struct drm_minor *minor) +{ + drm_debugfs_create_files(nouveau_debugfs_list, NOUVEAU_DEBUGFS_ENTRIES, + minor->debugfs_root, minor); + return 0; +} + +void +nouveau_debugfs_takedown(struct drm_minor *minor) +{ + drm_debugfs_remove_files(nouveau_debugfs_list, NOUVEAU_DEBUGFS_ENTRIES, + minor); +} diff --git a/drivers/gpu/drm/nouveau/nouveau_debugfs.h b/drivers/gpu/drm/nouveau/nouveau_debugfs.h new file mode 100644 index 000000000000..a62af6fb5f99 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nouveau_debugfs.h @@ -0,0 +1,22 @@ +#ifndef __NOUVEAU_DEBUGFS_H__ +#define __NOUVEAU_DEBUGFS_H__ + +#include <drm/drmP.h> + +#if defined(CONFIG_DEBUG_FS) +extern int nouveau_debugfs_init(struct drm_minor *); +extern void nouveau_debugfs_takedown(struct drm_minor *); +#else +static inline int +nouveau_debugfs_init(struct drm_minor *minor) +{ + return 0; +} + +static inline void nouveau_debugfs_takedown(struct drm_minor *minor) +{ +} + +#endif + +#endif diff --git a/drivers/gpu/drm/nouveau/nouveau_display.c b/drivers/gpu/drm/nouveau/nouveau_display.c index 508b00a2ce0d..4610c3a29bbe 100644 --- a/drivers/gpu/drm/nouveau/nouveau_display.c +++ b/drivers/gpu/drm/nouveau/nouveau_display.c @@ -41,6 +41,8 @@ #include <subdev/gpio.h> #include <engine/disp.h> +#include <core/class.h> + static void nouveau_user_framebuffer_destroy(struct drm_framebuffer *drm_fb) { @@ -78,11 +80,6 @@ nouveau_framebuffer_init(struct drm_device *dev, struct drm_framebuffer *fb = &nv_fb->base; int ret; - ret = drm_framebuffer_init(dev, fb, &nouveau_framebuffer_funcs); - if (ret) { - return ret; - } - drm_helper_mode_fill_fb_struct(fb, mode_cmd); nv_fb->nvbo = nvbo; @@ -125,6 +122,11 @@ nouveau_framebuffer_init(struct drm_device *dev, } } + ret = drm_framebuffer_init(dev, fb, &nouveau_framebuffer_funcs); + if (ret) { + return ret; + } + return 0; } @@ -231,8 +233,10 @@ nouveau_display_init(struct drm_device *dev) /* enable hotplug interrupts */ list_for_each_entry(connector, &dev->mode_config.connector_list, head) { struct nouveau_connector *conn = nouveau_connector(connector); - if (gpio) - gpio->irq(gpio, 0, conn->hpd, 0xff, true); + if (gpio && conn->hpd.func != DCB_GPIO_UNUSED) { + nouveau_event_get(gpio->events, conn->hpd.line, + &conn->hpd_func); + } } return ret; @@ -249,37 +253,20 @@ nouveau_display_fini(struct drm_device *dev) /* disable hotplug interrupts */ list_for_each_entry(connector, &dev->mode_config.connector_list, head) { struct nouveau_connector *conn = nouveau_connector(connector); - if (gpio) - gpio->irq(gpio, 0, conn->hpd, 0xff, false); + if (gpio && conn->hpd.func != DCB_GPIO_UNUSED) { + nouveau_event_put(gpio->events, conn->hpd.line, + &conn->hpd_func); + } } drm_kms_helper_poll_disable(dev); disp->fini(dev); } -static void -nouveau_display_vblank_notify(void *data, int crtc) -{ - drm_handle_vblank(data, crtc); -} - -static void -nouveau_display_vblank_get(void *data, int crtc) -{ - drm_vblank_get(data, crtc); -} - -static void -nouveau_display_vblank_put(void *data, int crtc) -{ - drm_vblank_put(data, crtc); -} - int nouveau_display_create(struct drm_device *dev) { struct nouveau_drm *drm = nouveau_drm(dev); - struct nouveau_disp *pdisp = nouveau_disp(drm->device); struct nouveau_display *disp; u32 pclass = dev->pdev->class >> 8; int ret, gen; @@ -288,11 +275,6 @@ nouveau_display_create(struct drm_device *dev) if (!disp) return -ENOMEM; - pdisp->vblank.data = dev; - pdisp->vblank.notify = nouveau_display_vblank_notify; - pdisp->vblank.get = nouveau_display_vblank_get; - pdisp->vblank.put = nouveau_display_vblank_put; - drm_mode_config_init(dev); drm_mode_create_scaling_mode_property(dev); drm_mode_create_dvi_i_properties(dev); @@ -316,17 +298,13 @@ nouveau_display_create(struct drm_device *dev) drm_property_create_range(dev, 0, "underscan vborder", 0, 128); if (gen >= 1) { + /* -90..+90 */ disp->vibrant_hue_property = - drm_property_create(dev, DRM_MODE_PROP_RANGE, - "vibrant hue", 2); - disp->vibrant_hue_property->values[0] = 0; - disp->vibrant_hue_property->values[1] = 180; /* -90..+90 */ + drm_property_create_range(dev, 0, "vibrant hue", 0, 180); + /* -100..+100 */ disp->color_vibrance_property = - drm_property_create(dev, DRM_MODE_PROP_RANGE, - "color vibrance", 2); - disp->color_vibrance_property->values[0] = 0; - disp->color_vibrance_property->values[1] = 200; /* -100..+100 */ + drm_property_create_range(dev, 0, "color vibrance", 0, 200); } dev->mode_config.funcs = &nouveau_mode_config_funcs; @@ -478,39 +456,6 @@ nouveau_display_resume(struct drm_device *dev) } } -int -nouveau_vblank_enable(struct drm_device *dev, int crtc) -{ - struct nouveau_device *device = nouveau_dev(dev); - - if (device->card_type >= NV_D0) - nv_mask(device, 0x6100c0 + (crtc * 0x800), 1, 1); - else - if (device->card_type >= NV_50) - nv_mask(device, NV50_PDISPLAY_INTR_EN_1, 0, - NV50_PDISPLAY_INTR_EN_1_VBLANK_CRTC_(crtc)); - else - NVWriteCRTC(dev, crtc, NV_PCRTC_INTR_EN_0, - NV_PCRTC_INTR_0_VBLANK); - - return 0; -} - -void -nouveau_vblank_disable(struct drm_device *dev, int crtc) -{ - struct nouveau_device *device = nouveau_dev(dev); - - if (device->card_type >= NV_D0) - nv_mask(device, 0x6100c0 + (crtc * 0x800), 1, 0); - else - if (device->card_type >= NV_50) - nv_mask(device, NV50_PDISPLAY_INTR_EN_1, - NV50_PDISPLAY_INTR_EN_1_VBLANK_CRTC_(crtc), 0); - else - NVWriteCRTC(dev, crtc, NV_PCRTC_INTR_EN_0, 0); -} - static int nouveau_page_flip_reserve(struct nouveau_bo *old_bo, struct nouveau_bo *new_bo) @@ -595,7 +540,7 @@ nouveau_page_flip_emit(struct nouveau_channel *chan, } FIRE_RING (chan); - ret = nouveau_fence_new(chan, pfence); + ret = nouveau_fence_new(chan, false, pfence); if (ret) goto fail; diff --git a/drivers/gpu/drm/nouveau/nouveau_display.h b/drivers/gpu/drm/nouveau/nouveau_display.h index 722548bb3bd3..1ea3e4734b62 100644 --- a/drivers/gpu/drm/nouveau/nouveau_display.h +++ b/drivers/gpu/drm/nouveau/nouveau_display.h @@ -59,9 +59,6 @@ void nouveau_display_fini(struct drm_device *dev); int nouveau_display_suspend(struct drm_device *dev); void nouveau_display_resume(struct drm_device *dev); -int nouveau_vblank_enable(struct drm_device *dev, int crtc); -void nouveau_vblank_disable(struct drm_device *dev, int crtc); - int nouveau_crtc_page_flip(struct drm_crtc *crtc, struct drm_framebuffer *fb, struct drm_pending_vblank_event *event); int nouveau_finish_page_flip(struct nouveau_channel *, diff --git a/drivers/gpu/drm/nouveau/nouveau_dma.h b/drivers/gpu/drm/nouveau/nouveau_dma.h index 5c2e22932d1c..690d5930ce32 100644 --- a/drivers/gpu/drm/nouveau/nouveau_dma.h +++ b/drivers/gpu/drm/nouveau/nouveau_dma.h @@ -191,7 +191,7 @@ WIND_RING(struct nouveau_channel *chan) #define NV84_SUBCHAN_SEMAPHORE_TRIGGER_WRITE_LONG 0x00000002 #define NV84_SUBCHAN_SEMAPHORE_TRIGGER_ACQUIRE_GEQUAL 0x00000004 #define NVC0_SUBCHAN_SEMAPHORE_TRIGGER_YIELD 0x00001000 -#define NV84_SUBCHAN_NOTIFY_INTR 0x00000020 +#define NV84_SUBCHAN_UEVENT 0x00000020 #define NV84_SUBCHAN_WRCACHE_FLUSH 0x00000024 #define NV10_SUBCHAN_REF_CNT 0x00000050 #define NVSW_SUBCHAN_PAGE_FLIP 0x00000054 diff --git a/drivers/gpu/drm/nouveau/nouveau_dp.c b/drivers/gpu/drm/nouveau/nouveau_dp.c index 59838651ee8f..36fd22500569 100644 --- a/drivers/gpu/drm/nouveau/nouveau_dp.c +++ b/drivers/gpu/drm/nouveau/nouveau_dp.c @@ -35,300 +35,6 @@ #include <subdev/gpio.h> #include <subdev/i2c.h> -/****************************************************************************** - * link training - *****************************************************************************/ -struct dp_state { - struct nouveau_i2c_port *auxch; - struct nouveau_object *core; - struct dcb_output *dcb; - int crtc; - u8 *dpcd; - int link_nr; - u32 link_bw; - u8 stat[6]; - u8 conf[4]; -}; - -static void -dp_set_link_config(struct drm_device *dev, struct dp_state *dp) -{ - struct nouveau_drm *drm = nouveau_drm(dev); - struct dcb_output *dcb = dp->dcb; - const u32 or = ffs(dcb->or) - 1, link = !(dcb->sorconf.link & 1); - const u32 moff = (dp->crtc << 3) | (link << 2) | or; - u8 sink[2]; - u32 data; - - NV_DEBUG(drm, "%d lanes at %d KB/s\n", dp->link_nr, dp->link_bw); - - /* set desired link configuration on the source */ - data = ((dp->link_bw / 27000) << 8) | dp->link_nr; - if (dp->dpcd[2] & DP_ENHANCED_FRAME_CAP) - data |= NV94_DISP_SOR_DP_LNKCTL_FRAME_ENH; - - nv_call(dp->core, NV94_DISP_SOR_DP_LNKCTL + moff, data); - - /* inform the sink of the new configuration */ - sink[0] = dp->link_bw / 27000; - sink[1] = dp->link_nr; - if (dp->dpcd[2] & DP_ENHANCED_FRAME_CAP) - sink[1] |= DP_LANE_COUNT_ENHANCED_FRAME_EN; - - nv_wraux(dp->auxch, DP_LINK_BW_SET, sink, 2); -} - -static void -dp_set_training_pattern(struct drm_device *dev, struct dp_state *dp, u8 pattern) -{ - struct nouveau_drm *drm = nouveau_drm(dev); - struct dcb_output *dcb = dp->dcb; - const u32 or = ffs(dcb->or) - 1, link = !(dcb->sorconf.link & 1); - const u32 moff = (dp->crtc << 3) | (link << 2) | or; - u8 sink_tp; - - NV_DEBUG(drm, "training pattern %d\n", pattern); - - nv_call(dp->core, NV94_DISP_SOR_DP_TRAIN + moff, pattern); - - nv_rdaux(dp->auxch, DP_TRAINING_PATTERN_SET, &sink_tp, 1); - sink_tp &= ~DP_TRAINING_PATTERN_MASK; - sink_tp |= pattern; - nv_wraux(dp->auxch, DP_TRAINING_PATTERN_SET, &sink_tp, 1); -} - -static int -dp_link_train_commit(struct drm_device *dev, struct dp_state *dp) -{ - struct nouveau_drm *drm = nouveau_drm(dev); - struct dcb_output *dcb = dp->dcb; - const u32 or = ffs(dcb->or) - 1, link = !(dcb->sorconf.link & 1); - const u32 moff = (dp->crtc << 3) | (link << 2) | or; - int i; - - for (i = 0; i < dp->link_nr; i++) { - u8 lane = (dp->stat[4 + (i >> 1)] >> ((i & 1) * 4)) & 0xf; - u8 lpre = (lane & 0x0c) >> 2; - u8 lvsw = (lane & 0x03) >> 0; - - dp->conf[i] = (lpre << 3) | lvsw; - if (lvsw == DP_TRAIN_VOLTAGE_SWING_1200) - dp->conf[i] |= DP_TRAIN_MAX_SWING_REACHED; - if ((lpre << 3) == DP_TRAIN_PRE_EMPHASIS_9_5) - dp->conf[i] |= DP_TRAIN_MAX_PRE_EMPHASIS_REACHED; - - NV_DEBUG(drm, "config lane %d %02x\n", i, dp->conf[i]); - - nv_call(dp->core, NV94_DISP_SOR_DP_DRVCTL(i) + moff, (lvsw << 8) | lpre); - } - - return nv_wraux(dp->auxch, DP_TRAINING_LANE0_SET, dp->conf, 4); -} - -static int -dp_link_train_update(struct drm_device *dev, struct dp_state *dp, u32 delay) -{ - struct nouveau_drm *drm = nouveau_drm(dev); - int ret; - - udelay(delay); - - ret = nv_rdaux(dp->auxch, DP_LANE0_1_STATUS, dp->stat, 6); - if (ret) - return ret; - - NV_DEBUG(drm, "status %*ph\n", 6, dp->stat); - return 0; -} - -static int -dp_link_train_cr(struct drm_device *dev, struct dp_state *dp) -{ - bool cr_done = false, abort = false; - int voltage = dp->conf[0] & DP_TRAIN_VOLTAGE_SWING_MASK; - int tries = 0, i; - - dp_set_training_pattern(dev, dp, DP_TRAINING_PATTERN_1); - - do { - if (dp_link_train_commit(dev, dp) || - dp_link_train_update(dev, dp, 100)) - break; - - cr_done = true; - for (i = 0; i < dp->link_nr; i++) { - u8 lane = (dp->stat[i >> 1] >> ((i & 1) * 4)) & 0xf; - if (!(lane & DP_LANE_CR_DONE)) { - cr_done = false; - if (dp->conf[i] & DP_TRAIN_MAX_SWING_REACHED) - abort = true; - break; - } - } - - if ((dp->conf[0] & DP_TRAIN_VOLTAGE_SWING_MASK) != voltage) { - voltage = dp->conf[0] & DP_TRAIN_VOLTAGE_SWING_MASK; - tries = 0; - } - } while (!cr_done && !abort && ++tries < 5); - - return cr_done ? 0 : -1; -} - -static int -dp_link_train_eq(struct drm_device *dev, struct dp_state *dp) -{ - bool eq_done, cr_done = true; - int tries = 0, i; - - dp_set_training_pattern(dev, dp, DP_TRAINING_PATTERN_2); - - do { - if (dp_link_train_update(dev, dp, 400)) - break; - - eq_done = !!(dp->stat[2] & DP_INTERLANE_ALIGN_DONE); - for (i = 0; i < dp->link_nr && eq_done; i++) { - u8 lane = (dp->stat[i >> 1] >> ((i & 1) * 4)) & 0xf; - if (!(lane & DP_LANE_CR_DONE)) - cr_done = false; - if (!(lane & DP_LANE_CHANNEL_EQ_DONE) || - !(lane & DP_LANE_SYMBOL_LOCKED)) - eq_done = false; - } - - if (dp_link_train_commit(dev, dp)) - break; - } while (!eq_done && cr_done && ++tries <= 5); - - return eq_done ? 0 : -1; -} - -static void -dp_link_train_init(struct drm_device *dev, struct dp_state *dp, bool spread) -{ - struct dcb_output *dcb = dp->dcb; - const u32 or = ffs(dcb->or) - 1, link = !(dcb->sorconf.link & 1); - const u32 moff = (dp->crtc << 3) | (link << 2) | or; - - nv_call(dp->core, NV94_DISP_SOR_DP_TRAIN + moff, (spread ? - NV94_DISP_SOR_DP_TRAIN_INIT_SPREAD_ON : - NV94_DISP_SOR_DP_TRAIN_INIT_SPREAD_OFF) | - NV94_DISP_SOR_DP_TRAIN_OP_INIT); -} - -static void -dp_link_train_fini(struct drm_device *dev, struct dp_state *dp) -{ - struct dcb_output *dcb = dp->dcb; - const u32 or = ffs(dcb->or) - 1, link = !(dcb->sorconf.link & 1); - const u32 moff = (dp->crtc << 3) | (link << 2) | or; - - nv_call(dp->core, NV94_DISP_SOR_DP_TRAIN + moff, - NV94_DISP_SOR_DP_TRAIN_OP_FINI); -} - -static bool -nouveau_dp_link_train(struct drm_encoder *encoder, u32 datarate, - struct nouveau_object *core) -{ - struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder); - struct nouveau_crtc *nv_crtc = nouveau_crtc(encoder->crtc); - struct nouveau_connector *nv_connector = - nouveau_encoder_connector_get(nv_encoder); - struct drm_device *dev = encoder->dev; - struct nouveau_drm *drm = nouveau_drm(dev); - struct nouveau_i2c *i2c = nouveau_i2c(drm->device); - struct nouveau_gpio *gpio = nouveau_gpio(drm->device); - const u32 bw_list[] = { 270000, 162000, 0 }; - const u32 *link_bw = bw_list; - struct dp_state dp; - - dp.auxch = i2c->find(i2c, nv_encoder->dcb->i2c_index); - if (!dp.auxch) - return false; - - dp.core = core; - dp.dcb = nv_encoder->dcb; - dp.crtc = nv_crtc->index; - dp.dpcd = nv_encoder->dp.dpcd; - - /* adjust required bandwidth for 8B/10B coding overhead */ - datarate = (datarate / 8) * 10; - - /* some sinks toggle hotplug in response to some of the actions - * we take during link training (DP_SET_POWER is one), we need - * to ignore them for the moment to avoid races. - */ - gpio->irq(gpio, 0, nv_connector->hpd, 0xff, false); - - /* enable down-spreading and execute pre-train script from vbios */ - dp_link_train_init(dev, &dp, nv_encoder->dp.dpcd[3] & 1); - - /* start off at highest link rate supported by encoder and display */ - while (*link_bw > nv_encoder->dp.link_bw) - link_bw++; - - while (link_bw[0]) { - /* find minimum required lane count at this link rate */ - dp.link_nr = nv_encoder->dp.link_nr; - while ((dp.link_nr >> 1) * link_bw[0] > datarate) - dp.link_nr >>= 1; - - /* drop link rate to minimum with this lane count */ - while ((link_bw[1] * dp.link_nr) > datarate) - link_bw++; - dp.link_bw = link_bw[0]; - - /* program selected link configuration */ - dp_set_link_config(dev, &dp); - - /* attempt to train the link at this configuration */ - memset(dp.stat, 0x00, sizeof(dp.stat)); - if (!dp_link_train_cr(dev, &dp) && - !dp_link_train_eq(dev, &dp)) - break; - - /* retry at lower rate */ - link_bw++; - } - - /* finish link training */ - dp_set_training_pattern(dev, &dp, DP_TRAINING_PATTERN_DISABLE); - - /* execute post-train script from vbios */ - dp_link_train_fini(dev, &dp); - - /* re-enable hotplug detect */ - gpio->irq(gpio, 0, nv_connector->hpd, 0xff, true); - return true; -} - -void -nouveau_dp_dpms(struct drm_encoder *encoder, int mode, u32 datarate, - struct nouveau_object *core) -{ - struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder); - struct nouveau_drm *drm = nouveau_drm(encoder->dev); - struct nouveau_i2c *i2c = nouveau_i2c(drm->device); - struct nouveau_i2c_port *auxch; - u8 status; - - auxch = i2c->find(i2c, nv_encoder->dcb->i2c_index); - if (!auxch) - return; - - if (mode == DRM_MODE_DPMS_ON) - status = DP_SET_POWER_D0; - else - status = DP_SET_POWER_D3; - - nv_wraux(auxch, DP_SET_POWER, &status, 1); - - if (mode == DRM_MODE_DPMS_ON) - nouveau_dp_link_train(encoder, datarate, core); -} - static void nouveau_dp_probe_oui(struct drm_device *dev, struct nouveau_i2c_port *auxch, u8 *dpcd) @@ -355,12 +61,11 @@ nouveau_dp_detect(struct drm_encoder *encoder) struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder); struct drm_device *dev = encoder->dev; struct nouveau_drm *drm = nouveau_drm(dev); - struct nouveau_i2c *i2c = nouveau_i2c(drm->device); struct nouveau_i2c_port *auxch; u8 *dpcd = nv_encoder->dp.dpcd; int ret; - auxch = i2c->find(i2c, nv_encoder->dcb->i2c_index); + auxch = nv_encoder->i2c; if (!auxch) return false; diff --git a/drivers/gpu/drm/nouveau/nouveau_drm.c b/drivers/gpu/drm/nouveau/nouveau_drm.c index 8b090f1eb51d..8a03c58ae988 100644 --- a/drivers/gpu/drm/nouveau/nouveau_drm.c +++ b/drivers/gpu/drm/nouveau/nouveau_drm.c @@ -34,6 +34,8 @@ #include <subdev/device.h> #include <subdev/vm.h> +#include <engine/disp.h> + #include "nouveau_drm.h" #include "nouveau_irq.h" #include "nouveau_dma.h" @@ -48,6 +50,7 @@ #include "nouveau_abi16.h" #include "nouveau_fbcon.h" #include "nouveau_fence.h" +#include "nouveau_debugfs.h" MODULE_PARM_DESC(config, "option string to pass to driver core"); static char *nouveau_config; @@ -68,6 +71,32 @@ module_param_named(modeset, nouveau_modeset, int, 0400); static struct drm_driver driver; +static int +nouveau_drm_vblank_enable(struct drm_device *dev, int head) +{ + struct nouveau_drm *drm = nouveau_drm(dev); + struct nouveau_disp *pdisp = nouveau_disp(drm->device); + nouveau_event_get(pdisp->vblank, head, &drm->vblank); + return 0; +} + +static void +nouveau_drm_vblank_disable(struct drm_device *dev, int head) +{ + struct nouveau_drm *drm = nouveau_drm(dev); + struct nouveau_disp *pdisp = nouveau_disp(drm->device); + nouveau_event_put(pdisp->vblank, head, &drm->vblank); +} + +static int +nouveau_drm_vblank_handler(struct nouveau_eventh *event, int head) +{ + struct nouveau_drm *drm = + container_of(event, struct nouveau_drm, vblank); + drm_handle_vblank(drm->dev, head); + return NVKM_EVENT_KEEP; +} + static u64 nouveau_name(struct pci_dev *pdev) { @@ -132,7 +161,8 @@ nouveau_accel_init(struct nouveau_drm *drm) /* initialise synchronisation routines */ if (device->card_type < NV_10) ret = nv04_fence_create(drm); - else if (device->card_type < NV_50) ret = nv10_fence_create(drm); + else if (device->chipset < 0x17) ret = nv10_fence_create(drm); + else if (device->card_type < NV_50) ret = nv17_fence_create(drm); else if (device->chipset < 0x84) ret = nv50_fence_create(drm); else if (device->card_type < NV_C0) ret = nv84_fence_create(drm); else ret = nvc0_fence_create(drm); @@ -259,6 +289,7 @@ nouveau_drm_load(struct drm_device *dev, unsigned long flags) dev->dev_private = drm; drm->dev = dev; + drm->vblank.func = nouveau_drm_vblank_handler; INIT_LIST_HEAD(&drm->clients); spin_lock_init(&drm->tile.lock); @@ -398,7 +429,7 @@ nouveau_drm_remove(struct pci_dev *pdev) nouveau_object_debug(); } -int +static int nouveau_do_suspend(struct drm_device *dev) { struct nouveau_drm *drm = nouveau_drm(dev); @@ -469,7 +500,7 @@ int nouveau_pmops_suspend(struct device *dev) return 0; } -int +static int nouveau_do_resume(struct drm_device *dev) { struct nouveau_drm *drm = nouveau_drm(dev); @@ -543,10 +574,11 @@ nouveau_drm_open(struct drm_device *dev, struct drm_file *fpriv) struct pci_dev *pdev = dev->pdev; struct nouveau_drm *drm = nouveau_drm(dev); struct nouveau_cli *cli; - char name[16]; + char name[32], tmpname[TASK_COMM_LEN]; int ret; - snprintf(name, sizeof(name), "%d", pid_nr(fpriv->pid)); + get_task_comm(tmpname, current); + snprintf(name, sizeof(name), "%s[%d]", tmpname, pid_nr(fpriv->pid)); ret = nouveau_cli_create(pdev, name, sizeof(*cli), (void **)&cli); if (ret) @@ -636,22 +668,32 @@ driver = { .postclose = nouveau_drm_postclose, .lastclose = nouveau_vga_lastclose, +#if defined(CONFIG_DEBUG_FS) + .debugfs_init = nouveau_debugfs_init, + .debugfs_cleanup = nouveau_debugfs_takedown, +#endif + .irq_preinstall = nouveau_irq_preinstall, .irq_postinstall = nouveau_irq_postinstall, .irq_uninstall = nouveau_irq_uninstall, .irq_handler = nouveau_irq_handler, .get_vblank_counter = drm_vblank_count, - .enable_vblank = nouveau_vblank_enable, - .disable_vblank = nouveau_vblank_disable, + .enable_vblank = nouveau_drm_vblank_enable, + .disable_vblank = nouveau_drm_vblank_disable, .ioctls = nouveau_ioctls, .fops = &nouveau_driver_fops, .prime_handle_to_fd = drm_gem_prime_handle_to_fd, .prime_fd_to_handle = drm_gem_prime_fd_to_handle, - .gem_prime_export = nouveau_gem_prime_export, - .gem_prime_import = nouveau_gem_prime_import, + .gem_prime_export = drm_gem_prime_export, + .gem_prime_import = drm_gem_prime_import, + .gem_prime_pin = nouveau_gem_prime_pin, + .gem_prime_get_sg_table = nouveau_gem_prime_get_sg_table, + .gem_prime_import_sg_table = nouveau_gem_prime_import_sg_table, + .gem_prime_vmap = nouveau_gem_prime_vmap, + .gem_prime_vunmap = nouveau_gem_prime_vunmap, .gem_init_object = nouveau_gem_object_new, .gem_free_object = nouveau_gem_object_del, diff --git a/drivers/gpu/drm/nouveau/nouveau_drm.h b/drivers/gpu/drm/nouveau/nouveau_drm.h index aa89eb938b47..b25df374c901 100644 --- a/drivers/gpu/drm/nouveau/nouveau_drm.h +++ b/drivers/gpu/drm/nouveau/nouveau_drm.h @@ -13,6 +13,7 @@ #define DRIVER_PATCHLEVEL 0 #include <core/client.h> +#include <core/event.h> #include <subdev/vm.h> @@ -112,6 +113,7 @@ struct nouveau_drm { struct nvbios vbios; struct nouveau_display *display; struct backlight_device *backlight; + struct nouveau_eventh vblank; /* power management */ struct nouveau_pm *pm; diff --git a/drivers/gpu/drm/nouveau/nouveau_encoder.h b/drivers/gpu/drm/nouveau/nouveau_encoder.h index d0d95bd511ab..e24341229d5e 100644 --- a/drivers/gpu/drm/nouveau/nouveau_encoder.h +++ b/drivers/gpu/drm/nouveau/nouveau_encoder.h @@ -36,19 +36,12 @@ struct nouveau_i2c_port; -struct dp_train_func { - void (*link_set)(struct drm_device *, struct dcb_output *, int crtc, - int nr, u32 bw, bool enhframe); - void (*train_set)(struct drm_device *, struct dcb_output *, u8 pattern); - void (*train_adj)(struct drm_device *, struct dcb_output *, - u8 lane, u8 swing, u8 preem); -}; - struct nouveau_encoder { struct drm_encoder_slave base; struct dcb_output *dcb; int or; + struct nouveau_i2c_port *i2c; /* different to drm_encoder.crtc, this reflects what's * actually programmed on the hw, not the proposed crtc */ diff --git a/drivers/gpu/drm/nouveau/nouveau_fbcon.c b/drivers/gpu/drm/nouveau/nouveau_fbcon.c index 67a1a069de28..b03531781580 100644 --- a/drivers/gpu/drm/nouveau/nouveau_fbcon.c +++ b/drivers/gpu/drm/nouveau/nouveau_fbcon.c @@ -251,9 +251,10 @@ nouveau_fbcon_zfill(struct drm_device *dev, struct nouveau_fbdev *fbcon) } static int -nouveau_fbcon_create(struct nouveau_fbdev *fbcon, +nouveau_fbcon_create(struct drm_fb_helper *helper, struct drm_fb_helper_surface_size *sizes) { + struct nouveau_fbdev *fbcon = (struct nouveau_fbdev *)helper; struct drm_device *dev = fbcon->dev; struct nouveau_drm *drm = nouveau_drm(dev); struct nouveau_device *device = nv_device(drm->device); @@ -388,23 +389,6 @@ out: return ret; } -static int -nouveau_fbcon_find_or_create_single(struct drm_fb_helper *helper, - struct drm_fb_helper_surface_size *sizes) -{ - struct nouveau_fbdev *fbcon = (struct nouveau_fbdev *)helper; - int new_fb = 0; - int ret; - - if (!helper->fb) { - ret = nouveau_fbcon_create(fbcon, sizes); - if (ret) - return ret; - new_fb = 1; - } - return new_fb; -} - void nouveau_fbcon_output_poll_changed(struct drm_device *dev) { @@ -433,6 +417,7 @@ nouveau_fbcon_destroy(struct drm_device *dev, struct nouveau_fbdev *fbcon) nouveau_fb->nvbo = NULL; } drm_fb_helper_fini(&fbcon->helper); + drm_framebuffer_unregister_private(&nouveau_fb->base); drm_framebuffer_cleanup(&nouveau_fb->base); return 0; } @@ -449,7 +434,7 @@ void nouveau_fbcon_gpu_lockup(struct fb_info *info) static struct drm_fb_helper_funcs nouveau_fbcon_helper_funcs = { .gamma_set = nouveau_fbcon_gamma_set, .gamma_get = nouveau_fbcon_gamma_get, - .fb_probe = nouveau_fbcon_find_or_create_single, + .fb_probe = nouveau_fbcon_create, }; @@ -490,6 +475,9 @@ nouveau_fbcon_init(struct drm_device *dev) else preferred_bpp = 32; + /* disable all the possible outputs/crtcs before entering KMS mode */ + drm_helper_disable_unused_functions(dev); + drm_fb_helper_initial_config(&fbcon->helper, preferred_bpp); return 0; } diff --git a/drivers/gpu/drm/nouveau/nouveau_fence.c b/drivers/gpu/drm/nouveau/nouveau_fence.c index 1d049be79f74..6c946837a0aa 100644 --- a/drivers/gpu/drm/nouveau/nouveau_fence.c +++ b/drivers/gpu/drm/nouveau/nouveau_fence.c @@ -33,14 +33,14 @@ #include "nouveau_dma.h" #include "nouveau_fence.h" +#include <engine/fifo.h> + void nouveau_fence_context_del(struct nouveau_fence_chan *fctx) { struct nouveau_fence *fence, *fnext; spin_lock(&fctx->lock); list_for_each_entry_safe(fence, fnext, &fctx->pending, head) { - if (fence->work) - fence->work(fence->priv, false); fence->channel = NULL; list_del(&fence->head); nouveau_fence_unref(&fence); @@ -59,17 +59,14 @@ nouveau_fence_context_new(struct nouveau_fence_chan *fctx) static void nouveau_fence_update(struct nouveau_channel *chan) { - struct nouveau_fence_priv *priv = chan->drm->fence; struct nouveau_fence_chan *fctx = chan->fence; struct nouveau_fence *fence, *fnext; spin_lock(&fctx->lock); list_for_each_entry_safe(fence, fnext, &fctx->pending, head) { - if (priv->read(chan) < fence->sequence) + if (fctx->read(chan) < fence->sequence) break; - if (fence->work) - fence->work(fence->priv, true); fence->channel = NULL; list_del(&fence->head); nouveau_fence_unref(&fence); @@ -80,7 +77,6 @@ nouveau_fence_update(struct nouveau_channel *chan) int nouveau_fence_emit(struct nouveau_fence *fence, struct nouveau_channel *chan) { - struct nouveau_fence_priv *priv = chan->drm->fence; struct nouveau_fence_chan *fctx = chan->fence; int ret; @@ -88,7 +84,7 @@ nouveau_fence_emit(struct nouveau_fence *fence, struct nouveau_channel *chan) fence->timeout = jiffies + (3 * DRM_HZ); fence->sequence = ++fctx->sequence; - ret = priv->emit(fence); + ret = fctx->emit(fence); if (!ret) { kref_get(&fence->kref); spin_lock(&fctx->lock); @@ -107,13 +103,87 @@ nouveau_fence_done(struct nouveau_fence *fence) return !fence->channel; } +struct nouveau_fence_uevent { + struct nouveau_eventh handler; + struct nouveau_fence_priv *priv; +}; + +static int +nouveau_fence_wait_uevent_handler(struct nouveau_eventh *event, int index) +{ + struct nouveau_fence_uevent *uevent = + container_of(event, struct nouveau_fence_uevent, handler); + wake_up_all(&uevent->priv->waiting); + return NVKM_EVENT_KEEP; +} + +static int +nouveau_fence_wait_uevent(struct nouveau_fence *fence, bool intr) + +{ + struct nouveau_channel *chan = fence->channel; + struct nouveau_fifo *pfifo = nouveau_fifo(chan->drm->device); + struct nouveau_fence_priv *priv = chan->drm->fence; + struct nouveau_fence_uevent uevent = { + .handler.func = nouveau_fence_wait_uevent_handler, + .priv = priv, + }; + int ret = 0; + + nouveau_event_get(pfifo->uevent, 0, &uevent.handler); + + if (fence->timeout) { + unsigned long timeout = fence->timeout - jiffies; + + if (time_before(jiffies, fence->timeout)) { + if (intr) { + ret = wait_event_interruptible_timeout( + priv->waiting, + nouveau_fence_done(fence), + timeout); + } else { + ret = wait_event_timeout(priv->waiting, + nouveau_fence_done(fence), + timeout); + } + } + + if (ret >= 0) { + fence->timeout = jiffies + ret; + if (time_after_eq(jiffies, fence->timeout)) + ret = -EBUSY; + } + } else { + if (intr) { + ret = wait_event_interruptible(priv->waiting, + nouveau_fence_done(fence)); + } else { + wait_event(priv->waiting, nouveau_fence_done(fence)); + } + } + + nouveau_event_put(pfifo->uevent, 0, &uevent.handler); + if (unlikely(ret < 0)) + return ret; + + return 0; +} + int nouveau_fence_wait(struct nouveau_fence *fence, bool lazy, bool intr) { + struct nouveau_channel *chan = fence->channel; + struct nouveau_fence_priv *priv = chan ? chan->drm->fence : NULL; unsigned long sleep_time = NSEC_PER_MSEC / 1000; ktime_t t; int ret = 0; + while (priv && priv->uevent && lazy && !nouveau_fence_done(fence)) { + ret = nouveau_fence_wait_uevent(fence, intr); + if (ret < 0) + return ret; + } + while (!nouveau_fence_done(fence)) { if (fence->timeout && time_after_eq(jiffies, fence->timeout)) { ret = -EBUSY; @@ -143,14 +213,14 @@ nouveau_fence_wait(struct nouveau_fence *fence, bool lazy, bool intr) int nouveau_fence_sync(struct nouveau_fence *fence, struct nouveau_channel *chan) { - struct nouveau_fence_priv *priv = chan->drm->fence; + struct nouveau_fence_chan *fctx = chan->fence; struct nouveau_channel *prev; int ret = 0; prev = fence ? fence->channel : NULL; if (prev) { if (unlikely(prev != chan && !nouveau_fence_done(fence))) { - ret = priv->sync(fence, prev, chan); + ret = fctx->sync(fence, prev, chan); if (unlikely(ret)) ret = nouveau_fence_wait(fence, true, false); } @@ -182,7 +252,8 @@ nouveau_fence_ref(struct nouveau_fence *fence) } int -nouveau_fence_new(struct nouveau_channel *chan, struct nouveau_fence **pfence) +nouveau_fence_new(struct nouveau_channel *chan, bool sysmem, + struct nouveau_fence **pfence) { struct nouveau_fence *fence; int ret = 0; @@ -193,13 +264,13 @@ nouveau_fence_new(struct nouveau_channel *chan, struct nouveau_fence **pfence) fence = kzalloc(sizeof(*fence), GFP_KERNEL); if (!fence) return -ENOMEM; + + fence->sysmem = sysmem; kref_init(&fence->kref); - if (chan) { - ret = nouveau_fence_emit(fence, chan); - if (ret) - nouveau_fence_unref(&fence); - } + ret = nouveau_fence_emit(fence, chan); + if (ret) + nouveau_fence_unref(&fence); *pfence = fence; return ret; diff --git a/drivers/gpu/drm/nouveau/nouveau_fence.h b/drivers/gpu/drm/nouveau/nouveau_fence.h index cdb83acdffe2..c89943407b52 100644 --- a/drivers/gpu/drm/nouveau/nouveau_fence.h +++ b/drivers/gpu/drm/nouveau/nouveau_fence.h @@ -7,15 +7,15 @@ struct nouveau_fence { struct list_head head; struct kref kref; + bool sysmem; + struct nouveau_channel *channel; unsigned long timeout; u32 sequence; - - void (*work)(void *priv, bool signalled); - void *priv; }; -int nouveau_fence_new(struct nouveau_channel *, struct nouveau_fence **); +int nouveau_fence_new(struct nouveau_channel *, bool sysmem, + struct nouveau_fence **); struct nouveau_fence * nouveau_fence_ref(struct nouveau_fence *); void nouveau_fence_unref(struct nouveau_fence **); @@ -29,6 +29,13 @@ struct nouveau_fence_chan { struct list_head pending; struct list_head flip; + int (*emit)(struct nouveau_fence *); + int (*sync)(struct nouveau_fence *, struct nouveau_channel *, + struct nouveau_channel *); + u32 (*read)(struct nouveau_channel *); + int (*emit32)(struct nouveau_channel *, u64, u32); + int (*sync32)(struct nouveau_channel *, u64, u32); + spinlock_t lock; u32 sequence; }; @@ -39,10 +46,9 @@ struct nouveau_fence_priv { void (*resume)(struct nouveau_drm *); int (*context_new)(struct nouveau_channel *); void (*context_del)(struct nouveau_channel *); - int (*emit)(struct nouveau_fence *); - int (*sync)(struct nouveau_fence *, struct nouveau_channel *, - struct nouveau_channel *); - u32 (*read)(struct nouveau_channel *); + + wait_queue_head_t waiting; + bool uevent; }; #define nouveau_fence(drm) ((struct nouveau_fence_priv *)(drm)->fence) @@ -60,13 +66,31 @@ u32 nv10_fence_read(struct nouveau_channel *); void nv10_fence_context_del(struct nouveau_channel *); void nv10_fence_destroy(struct nouveau_drm *); int nv10_fence_create(struct nouveau_drm *); + +int nv17_fence_create(struct nouveau_drm *); void nv17_fence_resume(struct nouveau_drm *drm); int nv50_fence_create(struct nouveau_drm *); int nv84_fence_create(struct nouveau_drm *); int nvc0_fence_create(struct nouveau_drm *); -u64 nvc0_fence_crtc(struct nouveau_channel *, int crtc); int nouveau_flip_complete(void *chan); +struct nv84_fence_chan { + struct nouveau_fence_chan base; + struct nouveau_vma vma; + struct nouveau_vma vma_gart; + struct nouveau_vma dispc_vma[4]; +}; + +struct nv84_fence_priv { + struct nouveau_fence_priv base; + struct nouveau_bo *bo; + struct nouveau_bo *bo_gart; + u32 *suspend; +}; + +u64 nv84_fence_crtc(struct nouveau_channel *, int); +int nv84_fence_context_new(struct nouveau_channel *); + #endif diff --git a/drivers/gpu/drm/nouveau/nouveau_gem.c b/drivers/gpu/drm/nouveau/nouveau_gem.c index 8bf695c52f95..b4b4d0c1f4af 100644 --- a/drivers/gpu/drm/nouveau/nouveau_gem.c +++ b/drivers/gpu/drm/nouveau/nouveau_gem.c @@ -24,8 +24,6 @@ * */ -#include <linux/dma-buf.h> - #include <subdev/fb.h> #include "nouveau_drm.h" @@ -205,6 +203,7 @@ nouveau_gem_ioctl_new(struct drm_device *dev, void *data, struct drm_file *file_priv) { struct nouveau_drm *drm = nouveau_drm(dev); + struct nouveau_cli *cli = nouveau_cli(file_priv); struct nouveau_fb *pfb = nouveau_fb(drm->device); struct drm_nouveau_gem_new *req = data; struct nouveau_bo *nvbo = NULL; @@ -213,7 +212,7 @@ nouveau_gem_ioctl_new(struct drm_device *dev, void *data, drm->ttm.bdev.dev_mapping = drm->dev->dev_mapping; if (!pfb->memtype_valid(pfb, req->info.tile_flags)) { - NV_ERROR(drm, "bad page flags: 0x%08x\n", req->info.tile_flags); + NV_ERROR(cli, "bad page flags: 0x%08x\n", req->info.tile_flags); return -EINVAL; } @@ -315,16 +314,18 @@ validate_init(struct nouveau_channel *chan, struct drm_file *file_priv, struct drm_nouveau_gem_pushbuf_bo *pbbo, int nr_buffers, struct validate_op *op) { + struct nouveau_cli *cli = nouveau_cli(file_priv); struct drm_device *dev = chan->drm->dev; struct nouveau_drm *drm = nouveau_drm(dev); uint32_t sequence; int trycnt = 0; int ret, i; + struct nouveau_bo *res_bo = NULL; sequence = atomic_add_return(1, &drm->ttm.validate_sequence); retry: if (++trycnt > 100000) { - NV_ERROR(drm, "%s failed and gave up.\n", __func__); + NV_ERROR(cli, "%s failed and gave up.\n", __func__); return -EINVAL; } @@ -335,14 +336,19 @@ retry: gem = drm_gem_object_lookup(dev, file_priv, b->handle); if (!gem) { - NV_ERROR(drm, "Unknown handle 0x%08x\n", b->handle); + NV_ERROR(cli, "Unknown handle 0x%08x\n", b->handle); validate_fini(op, NULL); return -ENOENT; } nvbo = gem->driver_private; + if (nvbo == res_bo) { + res_bo = NULL; + drm_gem_object_unreference_unlocked(gem); + continue; + } if (nvbo->reserved_by && nvbo->reserved_by == file_priv) { - NV_ERROR(drm, "multiple instances of buffer %d on " + NV_ERROR(cli, "multiple instances of buffer %d on " "validation list\n", b->handle); drm_gem_object_unreference_unlocked(gem); validate_fini(op, NULL); @@ -352,15 +358,19 @@ retry: ret = ttm_bo_reserve(&nvbo->bo, true, false, true, sequence); if (ret) { validate_fini(op, NULL); - if (unlikely(ret == -EAGAIN)) - ret = ttm_bo_wait_unreserved(&nvbo->bo, true); - drm_gem_object_unreference_unlocked(gem); + if (unlikely(ret == -EAGAIN)) { + sequence = atomic_add_return(1, &drm->ttm.validate_sequence); + ret = ttm_bo_reserve_slowpath(&nvbo->bo, true, + sequence); + if (!ret) + res_bo = nvbo; + } if (unlikely(ret)) { + drm_gem_object_unreference_unlocked(gem); if (ret != -ERESTARTSYS) - NV_ERROR(drm, "fail reserve\n"); + NV_ERROR(cli, "fail reserve\n"); return ret; } - goto retry; } b->user_priv = (uint64_t)(unsigned long)nvbo; @@ -376,12 +386,14 @@ retry: if (b->valid_domains & NOUVEAU_GEM_DOMAIN_GART) list_add_tail(&nvbo->entry, &op->gart_list); else { - NV_ERROR(drm, "invalid valid domains: 0x%08x\n", + NV_ERROR(cli, "invalid valid domains: 0x%08x\n", b->valid_domains); list_add_tail(&nvbo->entry, &op->both_list); validate_fini(op, NULL); return -EINVAL; } + if (nvbo == res_bo) + goto retry; } return 0; @@ -407,8 +419,9 @@ validate_sync(struct nouveau_channel *chan, struct nouveau_bo *nvbo) } static int -validate_list(struct nouveau_channel *chan, struct list_head *list, - struct drm_nouveau_gem_pushbuf_bo *pbbo, uint64_t user_pbbo_ptr) +validate_list(struct nouveau_channel *chan, struct nouveau_cli *cli, + struct list_head *list, struct drm_nouveau_gem_pushbuf_bo *pbbo, + uint64_t user_pbbo_ptr) { struct nouveau_drm *drm = chan->drm; struct drm_nouveau_gem_pushbuf_bo __user *upbbo = @@ -421,7 +434,7 @@ validate_list(struct nouveau_channel *chan, struct list_head *list, ret = validate_sync(chan, nvbo); if (unlikely(ret)) { - NV_ERROR(drm, "fail pre-validate sync\n"); + NV_ERROR(cli, "fail pre-validate sync\n"); return ret; } @@ -429,20 +442,20 @@ validate_list(struct nouveau_channel *chan, struct list_head *list, b->write_domains, b->valid_domains); if (unlikely(ret)) { - NV_ERROR(drm, "fail set_domain\n"); + NV_ERROR(cli, "fail set_domain\n"); return ret; } ret = nouveau_bo_validate(nvbo, true, false); if (unlikely(ret)) { if (ret != -ERESTARTSYS) - NV_ERROR(drm, "fail ttm_validate\n"); + NV_ERROR(cli, "fail ttm_validate\n"); return ret; } ret = validate_sync(chan, nvbo); if (unlikely(ret)) { - NV_ERROR(drm, "fail post-validate sync\n"); + NV_ERROR(cli, "fail post-validate sync\n"); return ret; } @@ -478,7 +491,7 @@ nouveau_gem_pushbuf_validate(struct nouveau_channel *chan, uint64_t user_buffers, int nr_buffers, struct validate_op *op, int *apply_relocs) { - struct nouveau_drm *drm = chan->drm; + struct nouveau_cli *cli = nouveau_cli(file_priv); int ret, relocs = 0; INIT_LIST_HEAD(&op->vram_list); @@ -491,32 +504,32 @@ nouveau_gem_pushbuf_validate(struct nouveau_channel *chan, ret = validate_init(chan, file_priv, pbbo, nr_buffers, op); if (unlikely(ret)) { if (ret != -ERESTARTSYS) - NV_ERROR(drm, "validate_init\n"); + NV_ERROR(cli, "validate_init\n"); return ret; } - ret = validate_list(chan, &op->vram_list, pbbo, user_buffers); + ret = validate_list(chan, cli, &op->vram_list, pbbo, user_buffers); if (unlikely(ret < 0)) { if (ret != -ERESTARTSYS) - NV_ERROR(drm, "validate vram_list\n"); + NV_ERROR(cli, "validate vram_list\n"); validate_fini(op, NULL); return ret; } relocs += ret; - ret = validate_list(chan, &op->gart_list, pbbo, user_buffers); + ret = validate_list(chan, cli, &op->gart_list, pbbo, user_buffers); if (unlikely(ret < 0)) { if (ret != -ERESTARTSYS) - NV_ERROR(drm, "validate gart_list\n"); + NV_ERROR(cli, "validate gart_list\n"); validate_fini(op, NULL); return ret; } relocs += ret; - ret = validate_list(chan, &op->both_list, pbbo, user_buffers); + ret = validate_list(chan, cli, &op->both_list, pbbo, user_buffers); if (unlikely(ret < 0)) { if (ret != -ERESTARTSYS) - NV_ERROR(drm, "validate both_list\n"); + NV_ERROR(cli, "validate both_list\n"); validate_fini(op, NULL); return ret; } @@ -545,11 +558,10 @@ u_memcpya(uint64_t user, unsigned nmemb, unsigned size) } static int -nouveau_gem_pushbuf_reloc_apply(struct drm_device *dev, +nouveau_gem_pushbuf_reloc_apply(struct nouveau_cli *cli, struct drm_nouveau_gem_pushbuf *req, struct drm_nouveau_gem_pushbuf_bo *bo) { - struct nouveau_drm *drm = nouveau_drm(dev); struct drm_nouveau_gem_pushbuf_reloc *reloc = NULL; int ret = 0; unsigned i; @@ -565,7 +577,7 @@ nouveau_gem_pushbuf_reloc_apply(struct drm_device *dev, uint32_t data; if (unlikely(r->bo_index > req->nr_buffers)) { - NV_ERROR(drm, "reloc bo index invalid\n"); + NV_ERROR(cli, "reloc bo index invalid\n"); ret = -EINVAL; break; } @@ -575,7 +587,7 @@ nouveau_gem_pushbuf_reloc_apply(struct drm_device *dev, continue; if (unlikely(r->reloc_bo_index > req->nr_buffers)) { - NV_ERROR(drm, "reloc container bo index invalid\n"); + NV_ERROR(cli, "reloc container bo index invalid\n"); ret = -EINVAL; break; } @@ -583,7 +595,7 @@ nouveau_gem_pushbuf_reloc_apply(struct drm_device *dev, if (unlikely(r->reloc_bo_offset + 4 > nvbo->bo.mem.num_pages << PAGE_SHIFT)) { - NV_ERROR(drm, "reloc outside of bo\n"); + NV_ERROR(cli, "reloc outside of bo\n"); ret = -EINVAL; break; } @@ -592,7 +604,7 @@ nouveau_gem_pushbuf_reloc_apply(struct drm_device *dev, ret = ttm_bo_kmap(&nvbo->bo, 0, nvbo->bo.mem.num_pages, &nvbo->kmap); if (ret) { - NV_ERROR(drm, "failed kmap for reloc\n"); + NV_ERROR(cli, "failed kmap for reloc\n"); break; } nvbo->validate_mapped = true; @@ -617,7 +629,7 @@ nouveau_gem_pushbuf_reloc_apply(struct drm_device *dev, ret = ttm_bo_wait(&nvbo->bo, false, false, false); spin_unlock(&nvbo->bo.bdev->fence_lock); if (ret) { - NV_ERROR(drm, "reloc wait_idle failed: %d\n", ret); + NV_ERROR(cli, "reloc wait_idle failed: %d\n", ret); break; } @@ -633,6 +645,7 @@ nouveau_gem_ioctl_pushbuf(struct drm_device *dev, void *data, struct drm_file *file_priv) { struct nouveau_abi16 *abi16 = nouveau_abi16_get(file_priv, dev); + struct nouveau_cli *cli = nouveau_cli(file_priv); struct nouveau_abi16_chan *temp; struct nouveau_drm *drm = nouveau_drm(dev); struct drm_nouveau_gem_pushbuf *req = data; @@ -662,19 +675,19 @@ nouveau_gem_ioctl_pushbuf(struct drm_device *dev, void *data, goto out_next; if (unlikely(req->nr_push > NOUVEAU_GEM_MAX_PUSH)) { - NV_ERROR(drm, "pushbuf push count exceeds limit: %d max %d\n", + NV_ERROR(cli, "pushbuf push count exceeds limit: %d max %d\n", req->nr_push, NOUVEAU_GEM_MAX_PUSH); return nouveau_abi16_put(abi16, -EINVAL); } if (unlikely(req->nr_buffers > NOUVEAU_GEM_MAX_BUFFERS)) { - NV_ERROR(drm, "pushbuf bo count exceeds limit: %d max %d\n", + NV_ERROR(cli, "pushbuf bo count exceeds limit: %d max %d\n", req->nr_buffers, NOUVEAU_GEM_MAX_BUFFERS); return nouveau_abi16_put(abi16, -EINVAL); } if (unlikely(req->nr_relocs > NOUVEAU_GEM_MAX_RELOCS)) { - NV_ERROR(drm, "pushbuf reloc count exceeds limit: %d max %d\n", + NV_ERROR(cli, "pushbuf reloc count exceeds limit: %d max %d\n", req->nr_relocs, NOUVEAU_GEM_MAX_RELOCS); return nouveau_abi16_put(abi16, -EINVAL); } @@ -692,7 +705,7 @@ nouveau_gem_ioctl_pushbuf(struct drm_device *dev, void *data, /* Ensure all push buffers are on validate list */ for (i = 0; i < req->nr_push; i++) { if (push[i].bo_index >= req->nr_buffers) { - NV_ERROR(drm, "push %d buffer not in list\n", i); + NV_ERROR(cli, "push %d buffer not in list\n", i); ret = -EINVAL; goto out_prevalid; } @@ -703,15 +716,15 @@ nouveau_gem_ioctl_pushbuf(struct drm_device *dev, void *data, req->nr_buffers, &op, &do_reloc); if (ret) { if (ret != -ERESTARTSYS) - NV_ERROR(drm, "validate: %d\n", ret); + NV_ERROR(cli, "validate: %d\n", ret); goto out_prevalid; } /* Apply any relocations that are required */ if (do_reloc) { - ret = nouveau_gem_pushbuf_reloc_apply(dev, req, bo); + ret = nouveau_gem_pushbuf_reloc_apply(cli, req, bo); if (ret) { - NV_ERROR(drm, "reloc apply: %d\n", ret); + NV_ERROR(cli, "reloc apply: %d\n", ret); goto out; } } @@ -719,7 +732,7 @@ nouveau_gem_ioctl_pushbuf(struct drm_device *dev, void *data, if (chan->dma.ib_max) { ret = nouveau_dma_wait(chan, req->nr_push + 1, 16); if (ret) { - NV_ERROR(drm, "nv50cal_space: %d\n", ret); + NV_ERROR(cli, "nv50cal_space: %d\n", ret); goto out; } @@ -734,7 +747,7 @@ nouveau_gem_ioctl_pushbuf(struct drm_device *dev, void *data, if (nv_device(drm->device)->chipset >= 0x25) { ret = RING_SPACE(chan, req->nr_push * 2); if (ret) { - NV_ERROR(drm, "cal_space: %d\n", ret); + NV_ERROR(cli, "cal_space: %d\n", ret); goto out; } @@ -748,7 +761,7 @@ nouveau_gem_ioctl_pushbuf(struct drm_device *dev, void *data, } else { ret = RING_SPACE(chan, req->nr_push * (2 + NOUVEAU_DMA_SKIPS)); if (ret) { - NV_ERROR(drm, "jmp_space: %d\n", ret); + NV_ERROR(cli, "jmp_space: %d\n", ret); goto out; } @@ -784,9 +797,9 @@ nouveau_gem_ioctl_pushbuf(struct drm_device *dev, void *data, } } - ret = nouveau_fence_new(chan, &fence); + ret = nouveau_fence_new(chan, false, &fence); if (ret) { - NV_ERROR(drm, "error fencing pushbuf: %d\n", ret); + NV_ERROR(cli, "error fencing pushbuf: %d\n", ret); WIND_RING(chan); goto out; } diff --git a/drivers/gpu/drm/nouveau/nouveau_gem.h b/drivers/gpu/drm/nouveau/nouveau_gem.h index 5c1049236d22..8d7a3f0aeb86 100644 --- a/drivers/gpu/drm/nouveau/nouveau_gem.h +++ b/drivers/gpu/drm/nouveau/nouveau_gem.h @@ -35,9 +35,11 @@ extern int nouveau_gem_ioctl_cpu_fini(struct drm_device *, void *, extern int nouveau_gem_ioctl_info(struct drm_device *, void *, struct drm_file *); -extern struct dma_buf *nouveau_gem_prime_export(struct drm_device *dev, - struct drm_gem_object *obj, int flags); -extern struct drm_gem_object *nouveau_gem_prime_import(struct drm_device *dev, - struct dma_buf *dma_buf); +extern int nouveau_gem_prime_pin(struct drm_gem_object *); +extern struct sg_table *nouveau_gem_prime_get_sg_table(struct drm_gem_object *); +extern struct drm_gem_object *nouveau_gem_prime_import_sg_table( + struct drm_device *, size_t size, struct sg_table *); +extern void *nouveau_gem_prime_vmap(struct drm_gem_object *); +extern void nouveau_gem_prime_vunmap(struct drm_gem_object *, void *); #endif diff --git a/drivers/gpu/drm/nouveau/nouveau_pm.c b/drivers/gpu/drm/nouveau/nouveau_pm.c index a701ff5ffa5b..bb54098c6d97 100644 --- a/drivers/gpu/drm/nouveau/nouveau_pm.c +++ b/drivers/gpu/drm/nouveau/nouveau_pm.c @@ -409,6 +409,81 @@ static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO, nouveau_hwmon_show_temp, NULL, 0); static ssize_t +nouveau_hwmon_show_temp1_auto_point1_pwm(struct device *d, + struct device_attribute *a, char *buf) +{ + return snprintf(buf, PAGE_SIZE, "%d\n", 100); +} +static SENSOR_DEVICE_ATTR(temp1_auto_point1_pwm, S_IRUGO, + nouveau_hwmon_show_temp1_auto_point1_pwm, NULL, 0); + +static ssize_t +nouveau_hwmon_temp1_auto_point1_temp(struct device *d, + struct device_attribute *a, char *buf) +{ + struct drm_device *dev = dev_get_drvdata(d); + struct nouveau_drm *drm = nouveau_drm(dev); + struct nouveau_therm *therm = nouveau_therm(drm->device); + + return snprintf(buf, PAGE_SIZE, "%d\n", + therm->attr_get(therm, NOUVEAU_THERM_ATTR_THRS_FAN_BOOST) * 1000); +} +static ssize_t +nouveau_hwmon_set_temp1_auto_point1_temp(struct device *d, + struct device_attribute *a, + const char *buf, size_t count) +{ + struct drm_device *dev = dev_get_drvdata(d); + struct nouveau_drm *drm = nouveau_drm(dev); + struct nouveau_therm *therm = nouveau_therm(drm->device); + long value; + + if (kstrtol(buf, 10, &value) == -EINVAL) + return count; + + therm->attr_set(therm, NOUVEAU_THERM_ATTR_THRS_FAN_BOOST, + value / 1000); + + return count; +} +static SENSOR_DEVICE_ATTR(temp1_auto_point1_temp, S_IRUGO | S_IWUSR, + nouveau_hwmon_temp1_auto_point1_temp, + nouveau_hwmon_set_temp1_auto_point1_temp, 0); + +static ssize_t +nouveau_hwmon_temp1_auto_point1_temp_hyst(struct device *d, + struct device_attribute *a, char *buf) +{ + struct drm_device *dev = dev_get_drvdata(d); + struct nouveau_drm *drm = nouveau_drm(dev); + struct nouveau_therm *therm = nouveau_therm(drm->device); + + return snprintf(buf, PAGE_SIZE, "%d\n", + therm->attr_get(therm, NOUVEAU_THERM_ATTR_THRS_FAN_BOOST_HYST) * 1000); +} +static ssize_t +nouveau_hwmon_set_temp1_auto_point1_temp_hyst(struct device *d, + struct device_attribute *a, + const char *buf, size_t count) +{ + struct drm_device *dev = dev_get_drvdata(d); + struct nouveau_drm *drm = nouveau_drm(dev); + struct nouveau_therm *therm = nouveau_therm(drm->device); + long value; + + if (kstrtol(buf, 10, &value) == -EINVAL) + return count; + + therm->attr_set(therm, NOUVEAU_THERM_ATTR_THRS_FAN_BOOST_HYST, + value / 1000); + + return count; +} +static SENSOR_DEVICE_ATTR(temp1_auto_point1_temp_hyst, S_IRUGO | S_IWUSR, + nouveau_hwmon_temp1_auto_point1_temp_hyst, + nouveau_hwmon_set_temp1_auto_point1_temp_hyst, 0); + +static ssize_t nouveau_hwmon_max_temp(struct device *d, struct device_attribute *a, char *buf) { struct drm_device *dev = dev_get_drvdata(d); @@ -439,6 +514,38 @@ static SENSOR_DEVICE_ATTR(temp1_max, S_IRUGO | S_IWUSR, nouveau_hwmon_max_temp, 0); static ssize_t +nouveau_hwmon_max_temp_hyst(struct device *d, struct device_attribute *a, + char *buf) +{ + struct drm_device *dev = dev_get_drvdata(d); + struct nouveau_drm *drm = nouveau_drm(dev); + struct nouveau_therm *therm = nouveau_therm(drm->device); + + return snprintf(buf, PAGE_SIZE, "%d\n", + therm->attr_get(therm, NOUVEAU_THERM_ATTR_THRS_DOWN_CLK_HYST) * 1000); +} +static ssize_t +nouveau_hwmon_set_max_temp_hyst(struct device *d, struct device_attribute *a, + const char *buf, size_t count) +{ + struct drm_device *dev = dev_get_drvdata(d); + struct nouveau_drm *drm = nouveau_drm(dev); + struct nouveau_therm *therm = nouveau_therm(drm->device); + long value; + + if (kstrtol(buf, 10, &value) == -EINVAL) + return count; + + therm->attr_set(therm, NOUVEAU_THERM_ATTR_THRS_DOWN_CLK_HYST, + value / 1000); + + return count; +} +static SENSOR_DEVICE_ATTR(temp1_max_hyst, S_IRUGO | S_IWUSR, + nouveau_hwmon_max_temp_hyst, + nouveau_hwmon_set_max_temp_hyst, 0); + +static ssize_t nouveau_hwmon_critical_temp(struct device *d, struct device_attribute *a, char *buf) { @@ -471,6 +578,107 @@ static SENSOR_DEVICE_ATTR(temp1_crit, S_IRUGO | S_IWUSR, nouveau_hwmon_set_critical_temp, 0); +static ssize_t +nouveau_hwmon_critical_temp_hyst(struct device *d, struct device_attribute *a, + char *buf) +{ + struct drm_device *dev = dev_get_drvdata(d); + struct nouveau_drm *drm = nouveau_drm(dev); + struct nouveau_therm *therm = nouveau_therm(drm->device); + + return snprintf(buf, PAGE_SIZE, "%d\n", + therm->attr_get(therm, NOUVEAU_THERM_ATTR_THRS_CRITICAL_HYST) * 1000); +} +static ssize_t +nouveau_hwmon_set_critical_temp_hyst(struct device *d, + struct device_attribute *a, + const char *buf, + size_t count) +{ + struct drm_device *dev = dev_get_drvdata(d); + struct nouveau_drm *drm = nouveau_drm(dev); + struct nouveau_therm *therm = nouveau_therm(drm->device); + long value; + + if (kstrtol(buf, 10, &value) == -EINVAL) + return count; + + therm->attr_set(therm, NOUVEAU_THERM_ATTR_THRS_CRITICAL_HYST, + value / 1000); + + return count; +} +static SENSOR_DEVICE_ATTR(temp1_crit_hyst, S_IRUGO | S_IWUSR, + nouveau_hwmon_critical_temp_hyst, + nouveau_hwmon_set_critical_temp_hyst, 0); +static ssize_t +nouveau_hwmon_emergency_temp(struct device *d, struct device_attribute *a, + char *buf) +{ + struct drm_device *dev = dev_get_drvdata(d); + struct nouveau_drm *drm = nouveau_drm(dev); + struct nouveau_therm *therm = nouveau_therm(drm->device); + + return snprintf(buf, PAGE_SIZE, "%d\n", + therm->attr_get(therm, NOUVEAU_THERM_ATTR_THRS_SHUTDOWN) * 1000); +} +static ssize_t +nouveau_hwmon_set_emergency_temp(struct device *d, struct device_attribute *a, + const char *buf, + size_t count) +{ + struct drm_device *dev = dev_get_drvdata(d); + struct nouveau_drm *drm = nouveau_drm(dev); + struct nouveau_therm *therm = nouveau_therm(drm->device); + long value; + + if (kstrtol(buf, 10, &value) == -EINVAL) + return count; + + therm->attr_set(therm, NOUVEAU_THERM_ATTR_THRS_SHUTDOWN, value / 1000); + + return count; +} +static SENSOR_DEVICE_ATTR(temp1_emergency, S_IRUGO | S_IWUSR, + nouveau_hwmon_emergency_temp, + nouveau_hwmon_set_emergency_temp, + 0); + +static ssize_t +nouveau_hwmon_emergency_temp_hyst(struct device *d, struct device_attribute *a, + char *buf) +{ + struct drm_device *dev = dev_get_drvdata(d); + struct nouveau_drm *drm = nouveau_drm(dev); + struct nouveau_therm *therm = nouveau_therm(drm->device); + + return snprintf(buf, PAGE_SIZE, "%d\n", + therm->attr_get(therm, NOUVEAU_THERM_ATTR_THRS_SHUTDOWN_HYST) * 1000); +} +static ssize_t +nouveau_hwmon_set_emergency_temp_hyst(struct device *d, + struct device_attribute *a, + const char *buf, + size_t count) +{ + struct drm_device *dev = dev_get_drvdata(d); + struct nouveau_drm *drm = nouveau_drm(dev); + struct nouveau_therm *therm = nouveau_therm(drm->device); + long value; + + if (kstrtol(buf, 10, &value) == -EINVAL) + return count; + + therm->attr_set(therm, NOUVEAU_THERM_ATTR_THRS_SHUTDOWN_HYST, + value / 1000); + + return count; +} +static SENSOR_DEVICE_ATTR(temp1_emergency_hyst, S_IRUGO | S_IWUSR, + nouveau_hwmon_emergency_temp_hyst, + nouveau_hwmon_set_emergency_temp_hyst, + 0); + static ssize_t nouveau_hwmon_show_name(struct device *dev, struct device_attribute *attr, char *buf) @@ -490,7 +698,7 @@ static SENSOR_DEVICE_ATTR(update_rate, S_IRUGO, NULL, 0); static ssize_t -nouveau_hwmon_show_fan0_input(struct device *d, struct device_attribute *attr, +nouveau_hwmon_show_fan1_input(struct device *d, struct device_attribute *attr, char *buf) { struct drm_device *dev = dev_get_drvdata(d); @@ -499,7 +707,7 @@ nouveau_hwmon_show_fan0_input(struct device *d, struct device_attribute *attr, return snprintf(buf, PAGE_SIZE, "%d\n", therm->fan_sense(therm)); } -static SENSOR_DEVICE_ATTR(fan0_input, S_IRUGO, nouveau_hwmon_show_fan0_input, +static SENSOR_DEVICE_ATTR(fan1_input, S_IRUGO, nouveau_hwmon_show_fan1_input, NULL, 0); static ssize_t @@ -665,14 +873,21 @@ static SENSOR_DEVICE_ATTR(pwm1_max, S_IRUGO | S_IWUSR, static struct attribute *hwmon_attributes[] = { &sensor_dev_attr_temp1_input.dev_attr.attr, + &sensor_dev_attr_temp1_auto_point1_pwm.dev_attr.attr, + &sensor_dev_attr_temp1_auto_point1_temp.dev_attr.attr, + &sensor_dev_attr_temp1_auto_point1_temp_hyst.dev_attr.attr, &sensor_dev_attr_temp1_max.dev_attr.attr, + &sensor_dev_attr_temp1_max_hyst.dev_attr.attr, &sensor_dev_attr_temp1_crit.dev_attr.attr, + &sensor_dev_attr_temp1_crit_hyst.dev_attr.attr, + &sensor_dev_attr_temp1_emergency.dev_attr.attr, + &sensor_dev_attr_temp1_emergency_hyst.dev_attr.attr, &sensor_dev_attr_name.dev_attr.attr, &sensor_dev_attr_update_rate.dev_attr.attr, NULL }; static struct attribute *hwmon_fan_rpm_attributes[] = { - &sensor_dev_attr_fan0_input.dev_attr.attr, + &sensor_dev_attr_fan1_input.dev_attr.attr, NULL }; static struct attribute *hwmon_pwm_fan_attributes[] = { @@ -717,7 +932,7 @@ nouveau_hwmon_init(struct drm_device *dev) dev_set_drvdata(hwmon_dev, dev); /* default sysfs entries */ - ret = sysfs_create_group(&dev->pdev->dev.kobj, &hwmon_attrgroup); + ret = sysfs_create_group(&hwmon_dev->kobj, &hwmon_attrgroup); if (ret) { if (ret) goto error; @@ -728,7 +943,7 @@ nouveau_hwmon_init(struct drm_device *dev) * the gpio entries for pwm fan control even when there's no * actual fan connected to it... therm table? */ if (therm->fan_get && therm->fan_get(therm) >= 0) { - ret = sysfs_create_group(&dev->pdev->dev.kobj, + ret = sysfs_create_group(&hwmon_dev->kobj, &hwmon_pwm_fan_attrgroup); if (ret) goto error; @@ -736,7 +951,7 @@ nouveau_hwmon_init(struct drm_device *dev) /* if the card can read the fan rpm */ if (therm->fan_sense(therm) >= 0) { - ret = sysfs_create_group(&dev->pdev->dev.kobj, + ret = sysfs_create_group(&hwmon_dev->kobj, &hwmon_fan_rpm_attrgroup); if (ret) goto error; @@ -764,10 +979,10 @@ nouveau_hwmon_fini(struct drm_device *dev) struct nouveau_pm *pm = nouveau_pm(dev); if (pm->hwmon) { - sysfs_remove_group(&dev->pdev->dev.kobj, &hwmon_attrgroup); - sysfs_remove_group(&dev->pdev->dev.kobj, + sysfs_remove_group(&pm->hwmon->kobj, &hwmon_attrgroup); + sysfs_remove_group(&pm->hwmon->kobj, &hwmon_pwm_fan_attrgroup); - sysfs_remove_group(&dev->pdev->dev.kobj, + sysfs_remove_group(&pm->hwmon->kobj, &hwmon_fan_rpm_attrgroup); hwmon_device_unregister(pm->hwmon); diff --git a/drivers/gpu/drm/nouveau/nouveau_prime.c b/drivers/gpu/drm/nouveau/nouveau_prime.c index b8e05ae38212..f53e10874cae 100644 --- a/drivers/gpu/drm/nouveau/nouveau_prime.c +++ b/drivers/gpu/drm/nouveau/nouveau_prime.c @@ -22,126 +22,42 @@ * Authors: Dave Airlie */ -#include <linux/dma-buf.h> - #include <drm/drmP.h> #include "nouveau_drm.h" #include "nouveau_gem.h" -static struct sg_table *nouveau_gem_map_dma_buf(struct dma_buf_attachment *attachment, - enum dma_data_direction dir) +struct sg_table *nouveau_gem_prime_get_sg_table(struct drm_gem_object *obj) { - struct nouveau_bo *nvbo = attachment->dmabuf->priv; - struct drm_device *dev = nvbo->gem->dev; + struct nouveau_bo *nvbo = nouveau_gem_object(obj); int npages = nvbo->bo.num_pages; - struct sg_table *sg; - int nents; - - mutex_lock(&dev->struct_mutex); - sg = drm_prime_pages_to_sg(nvbo->bo.ttm->pages, npages); - nents = dma_map_sg(attachment->dev, sg->sgl, sg->nents, dir); - mutex_unlock(&dev->struct_mutex); - return sg; -} - -static void nouveau_gem_unmap_dma_buf(struct dma_buf_attachment *attachment, - struct sg_table *sg, enum dma_data_direction dir) -{ - dma_unmap_sg(attachment->dev, sg->sgl, sg->nents, dir); - sg_free_table(sg); - kfree(sg); -} - -static void nouveau_gem_dmabuf_release(struct dma_buf *dma_buf) -{ - struct nouveau_bo *nvbo = dma_buf->priv; - - if (nvbo->gem->export_dma_buf == dma_buf) { - nvbo->gem->export_dma_buf = NULL; - drm_gem_object_unreference_unlocked(nvbo->gem); - } -} - -static void *nouveau_gem_kmap_atomic(struct dma_buf *dma_buf, unsigned long page_num) -{ - return NULL; -} - -static void nouveau_gem_kunmap_atomic(struct dma_buf *dma_buf, unsigned long page_num, void *addr) -{ -} -static void *nouveau_gem_kmap(struct dma_buf *dma_buf, unsigned long page_num) -{ - return NULL; + return drm_prime_pages_to_sg(nvbo->bo.ttm->pages, npages); } -static void nouveau_gem_kunmap(struct dma_buf *dma_buf, unsigned long page_num, void *addr) +void *nouveau_gem_prime_vmap(struct drm_gem_object *obj) { - -} - -static int nouveau_gem_prime_mmap(struct dma_buf *dma_buf, struct vm_area_struct *vma) -{ - return -EINVAL; -} - -static void *nouveau_gem_prime_vmap(struct dma_buf *dma_buf) -{ - struct nouveau_bo *nvbo = dma_buf->priv; - struct drm_device *dev = nvbo->gem->dev; + struct nouveau_bo *nvbo = nouveau_gem_object(obj); int ret; - mutex_lock(&dev->struct_mutex); - if (nvbo->vmapping_count) { - nvbo->vmapping_count++; - goto out_unlock; - } - ret = ttm_bo_kmap(&nvbo->bo, 0, nvbo->bo.num_pages, &nvbo->dma_buf_vmap); - if (ret) { - mutex_unlock(&dev->struct_mutex); + if (ret) return ERR_PTR(ret); - } - nvbo->vmapping_count = 1; -out_unlock: - mutex_unlock(&dev->struct_mutex); + return nvbo->dma_buf_vmap.virtual; } -static void nouveau_gem_prime_vunmap(struct dma_buf *dma_buf, void *vaddr) +void nouveau_gem_prime_vunmap(struct drm_gem_object *obj, void *vaddr) { - struct nouveau_bo *nvbo = dma_buf->priv; - struct drm_device *dev = nvbo->gem->dev; + struct nouveau_bo *nvbo = nouveau_gem_object(obj); - mutex_lock(&dev->struct_mutex); - nvbo->vmapping_count--; - if (nvbo->vmapping_count == 0) { - ttm_bo_kunmap(&nvbo->dma_buf_vmap); - } - mutex_unlock(&dev->struct_mutex); + ttm_bo_kunmap(&nvbo->dma_buf_vmap); } -static const struct dma_buf_ops nouveau_dmabuf_ops = { - .map_dma_buf = nouveau_gem_map_dma_buf, - .unmap_dma_buf = nouveau_gem_unmap_dma_buf, - .release = nouveau_gem_dmabuf_release, - .kmap = nouveau_gem_kmap, - .kmap_atomic = nouveau_gem_kmap_atomic, - .kunmap = nouveau_gem_kunmap, - .kunmap_atomic = nouveau_gem_kunmap_atomic, - .mmap = nouveau_gem_prime_mmap, - .vmap = nouveau_gem_prime_vmap, - .vunmap = nouveau_gem_prime_vunmap, -}; - -static int -nouveau_prime_new(struct drm_device *dev, - size_t size, - struct sg_table *sg, - struct nouveau_bo **pnvbo) +struct drm_gem_object *nouveau_gem_prime_import_sg_table(struct drm_device *dev, + size_t size, + struct sg_table *sg) { struct nouveau_bo *nvbo; u32 flags = 0; @@ -150,24 +66,22 @@ nouveau_prime_new(struct drm_device *dev, flags = TTM_PL_FLAG_TT; ret = nouveau_bo_new(dev, size, 0, flags, 0, 0, - sg, pnvbo); + sg, &nvbo); if (ret) - return ret; - nvbo = *pnvbo; + return ERR_PTR(ret); nvbo->valid_domains = NOUVEAU_GEM_DOMAIN_GART; nvbo->gem = drm_gem_object_alloc(dev, nvbo->bo.mem.size); if (!nvbo->gem) { - nouveau_bo_ref(NULL, pnvbo); - return -ENOMEM; + nouveau_bo_ref(NULL, &nvbo); + return ERR_PTR(-ENOMEM); } nvbo->gem->driver_private = nvbo; - return 0; + return nvbo->gem; } -struct dma_buf *nouveau_gem_prime_export(struct drm_device *dev, - struct drm_gem_object *obj, int flags) +int nouveau_gem_prime_pin(struct drm_gem_object *obj) { struct nouveau_bo *nvbo = nouveau_gem_object(obj); int ret = 0; @@ -175,52 +89,7 @@ struct dma_buf *nouveau_gem_prime_export(struct drm_device *dev, /* pin buffer into GTT */ ret = nouveau_bo_pin(nvbo, TTM_PL_FLAG_TT); if (ret) - return ERR_PTR(-EINVAL); - - return dma_buf_export(nvbo, &nouveau_dmabuf_ops, obj->size, flags); -} - -struct drm_gem_object *nouveau_gem_prime_import(struct drm_device *dev, - struct dma_buf *dma_buf) -{ - struct dma_buf_attachment *attach; - struct sg_table *sg; - struct nouveau_bo *nvbo; - int ret; - - if (dma_buf->ops == &nouveau_dmabuf_ops) { - nvbo = dma_buf->priv; - if (nvbo->gem) { - if (nvbo->gem->dev == dev) { - drm_gem_object_reference(nvbo->gem); - dma_buf_put(dma_buf); - return nvbo->gem; - } - } - } - /* need to attach */ - attach = dma_buf_attach(dma_buf, dev->dev); - if (IS_ERR(attach)) - return ERR_PTR(PTR_ERR(attach)); - - sg = dma_buf_map_attachment(attach, DMA_BIDIRECTIONAL); - if (IS_ERR(sg)) { - ret = PTR_ERR(sg); - goto fail_detach; - } - - ret = nouveau_prime_new(dev, dma_buf->size, sg, &nvbo); - if (ret) - goto fail_unmap; - - nvbo->gem->import_attach = attach; - - return nvbo->gem; + return -EINVAL; -fail_unmap: - dma_buf_unmap_attachment(attach, sg, DMA_BIDIRECTIONAL); -fail_detach: - dma_buf_detach(dma_buf, attach); - return ERR_PTR(ret); + return 0; } - diff --git a/drivers/gpu/drm/nouveau/nv04_dfp.c b/drivers/gpu/drm/nouveau/nv04_dfp.c index 39ffc07f906b..7e24cdf1cb39 100644 --- a/drivers/gpu/drm/nouveau/nv04_dfp.c +++ b/drivers/gpu/drm/nouveau/nv04_dfp.c @@ -490,8 +490,8 @@ static void nv04_dfp_update_backlight(struct drm_encoder *encoder, int mode) /* BIOS scripts usually take care of the backlight, thanks * Apple for your consistency. */ - if (dev->pci_device == 0x0179 || dev->pci_device == 0x0189 || - dev->pci_device == 0x0329) { + if (dev->pci_device == 0x0174 || dev->pci_device == 0x0179 || + dev->pci_device == 0x0189 || dev->pci_device == 0x0329) { if (mode == DRM_MODE_DPMS_ON) { nv_mask(device, NV_PBUS_DEBUG_DUALHEAD_CTL, 0, 1 << 31); nv_mask(device, NV_PCRTC_GPIO_EXT, 3, 1); diff --git a/drivers/gpu/drm/nouveau/nv04_display.c b/drivers/gpu/drm/nouveau/nv04_display.c index 2cd6fb8c548e..ad48444c385c 100644 --- a/drivers/gpu/drm/nouveau/nv04_display.c +++ b/drivers/gpu/drm/nouveau/nv04_display.c @@ -22,6 +22,9 @@ * Author: Ben Skeggs */ +#include <core/object.h> +#include <core/class.h> + #include <drm/drmP.h> #include <drm/drm_crtc_helper.h> @@ -31,6 +34,8 @@ #include "nouveau_encoder.h" #include "nouveau_connector.h" +#include <subdev/i2c.h> + int nv04_display_early_init(struct drm_device *dev) { @@ -53,6 +58,7 @@ int nv04_display_create(struct drm_device *dev) { struct nouveau_drm *drm = nouveau_drm(dev); + struct nouveau_i2c *i2c = nouveau_i2c(drm->device); struct dcb_table *dcb = &drm->vbios.dcb; struct drm_connector *connector, *ct; struct drm_encoder *encoder; @@ -71,6 +77,11 @@ nv04_display_create(struct drm_device *dev) nouveau_hw_save_vga_fonts(dev, 1); + ret = nouveau_object_new(nv_object(drm), NVDRM_DEVICE, 0xd1500000, + NV04_DISP_CLASS, NULL, 0, &disp->core); + if (ret) + return ret; + nv04_crtc_create(dev, 0); if (nv_two_heads(dev)) nv04_crtc_create(dev, 1); @@ -114,6 +125,11 @@ nv04_display_create(struct drm_device *dev) } } + list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) { + struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder); + nv_encoder->i2c = i2c->find(i2c, nv_encoder->dcb->i2c_index); + } + /* Save previous state */ list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) crtc->funcs->save(crtc); @@ -140,7 +156,7 @@ nv04_display_destroy(struct drm_device *dev) .crtc = crtc, }; - crtc->funcs->set_config(&modeset); + drm_mode_set_config_internal(&modeset); } /* Restore state */ diff --git a/drivers/gpu/drm/nouveau/nv04_display.h b/drivers/gpu/drm/nouveau/nv04_display.h index 45322802e37d..a0a031dad13f 100644 --- a/drivers/gpu/drm/nouveau/nv04_display.h +++ b/drivers/gpu/drm/nouveau/nv04_display.h @@ -80,6 +80,7 @@ struct nv04_display { struct nv04_mode_state saved_reg; uint32_t saved_vga_font[4][16384]; uint32_t dac_users[4]; + struct nouveau_object *core; }; static inline struct nv04_display * diff --git a/drivers/gpu/drm/nouveau/nv04_fence.c b/drivers/gpu/drm/nouveau/nv04_fence.c index a220b94ba9f2..94eadd1dd10a 100644 --- a/drivers/gpu/drm/nouveau/nv04_fence.c +++ b/drivers/gpu/drm/nouveau/nv04_fence.c @@ -78,6 +78,9 @@ nv04_fence_context_new(struct nouveau_channel *chan) struct nv04_fence_chan *fctx = kzalloc(sizeof(*fctx), GFP_KERNEL); if (fctx) { nouveau_fence_context_new(&fctx->base); + fctx->base.emit = nv04_fence_emit; + fctx->base.sync = nv04_fence_sync; + fctx->base.read = nv04_fence_read; chan->fence = fctx; return 0; } @@ -104,8 +107,5 @@ nv04_fence_create(struct nouveau_drm *drm) priv->base.dtor = nv04_fence_destroy; priv->base.context_new = nv04_fence_context_new; priv->base.context_del = nv04_fence_context_del; - priv->base.emit = nv04_fence_emit; - priv->base.sync = nv04_fence_sync; - priv->base.read = nv04_fence_read; return 0; } diff --git a/drivers/gpu/drm/nouveau/nv10_fence.c b/drivers/gpu/drm/nouveau/nv10_fence.c index 03017f24d593..06f434f03fba 100644 --- a/drivers/gpu/drm/nouveau/nv10_fence.c +++ b/drivers/gpu/drm/nouveau/nv10_fence.c @@ -27,18 +27,7 @@ #include "nouveau_drm.h" #include "nouveau_dma.h" -#include "nouveau_fence.h" - -struct nv10_fence_chan { - struct nouveau_fence_chan base; -}; - -struct nv10_fence_priv { - struct nouveau_fence_priv base; - struct nouveau_bo *bo; - spinlock_t lock; - u32 sequence; -}; +#include "nv10_fence.h" int nv10_fence_emit(struct nouveau_fence *fence) @@ -61,45 +50,6 @@ nv10_fence_sync(struct nouveau_fence *fence, return -ENODEV; } -int -nv17_fence_sync(struct nouveau_fence *fence, - struct nouveau_channel *prev, struct nouveau_channel *chan) -{ - struct nv10_fence_priv *priv = chan->drm->fence; - u32 value; - int ret; - - if (!mutex_trylock(&prev->cli->mutex)) - return -EBUSY; - - spin_lock(&priv->lock); - value = priv->sequence; - priv->sequence += 2; - spin_unlock(&priv->lock); - - ret = RING_SPACE(prev, 5); - if (!ret) { - BEGIN_NV04(prev, 0, NV11_SUBCHAN_DMA_SEMAPHORE, 4); - OUT_RING (prev, NvSema); - OUT_RING (prev, 0); - OUT_RING (prev, value + 0); - OUT_RING (prev, value + 1); - FIRE_RING (prev); - } - - if (!ret && !(ret = RING_SPACE(chan, 5))) { - BEGIN_NV04(chan, 0, NV11_SUBCHAN_DMA_SEMAPHORE, 4); - OUT_RING (chan, NvSema); - OUT_RING (chan, 0); - OUT_RING (chan, value + 1); - OUT_RING (chan, value + 2); - FIRE_RING (chan); - } - - mutex_unlock(&prev->cli->mutex); - return 0; -} - u32 nv10_fence_read(struct nouveau_channel *chan) { @@ -115,39 +65,20 @@ nv10_fence_context_del(struct nouveau_channel *chan) kfree(fctx); } -static int +int nv10_fence_context_new(struct nouveau_channel *chan) { - struct nv10_fence_priv *priv = chan->drm->fence; struct nv10_fence_chan *fctx; - int ret = 0; fctx = chan->fence = kzalloc(sizeof(*fctx), GFP_KERNEL); if (!fctx) return -ENOMEM; nouveau_fence_context_new(&fctx->base); - - if (priv->bo) { - struct ttm_mem_reg *mem = &priv->bo->bo.mem; - struct nouveau_object *object; - u32 start = mem->start * PAGE_SIZE; - u32 limit = mem->start + mem->size - 1; - - ret = nouveau_object_new(nv_object(chan->cli), chan->handle, - NvSema, 0x0002, - &(struct nv_dma_class) { - .flags = NV_DMA_TARGET_VRAM | - NV_DMA_ACCESS_RDWR, - .start = start, - .limit = limit, - }, sizeof(struct nv_dma_class), - &object); - } - - if (ret) - nv10_fence_context_del(chan); - return ret; + fctx->base.emit = nv10_fence_emit; + fctx->base.read = nv10_fence_read; + fctx->base.sync = nv10_fence_sync; + return 0; } void @@ -162,18 +93,10 @@ nv10_fence_destroy(struct nouveau_drm *drm) kfree(priv); } -void nv17_fence_resume(struct nouveau_drm *drm) -{ - struct nv10_fence_priv *priv = drm->fence; - - nouveau_bo_wr32(priv->bo, 0, priv->sequence); -} - int nv10_fence_create(struct nouveau_drm *drm) { struct nv10_fence_priv *priv; - int ret = 0; priv = drm->fence = kzalloc(sizeof(*priv), GFP_KERNEL); if (!priv) @@ -182,33 +105,6 @@ nv10_fence_create(struct nouveau_drm *drm) priv->base.dtor = nv10_fence_destroy; priv->base.context_new = nv10_fence_context_new; priv->base.context_del = nv10_fence_context_del; - priv->base.emit = nv10_fence_emit; - priv->base.read = nv10_fence_read; - priv->base.sync = nv10_fence_sync; spin_lock_init(&priv->lock); - - if (nv_device(drm->device)->chipset >= 0x17) { - ret = nouveau_bo_new(drm->dev, 4096, 0x1000, TTM_PL_FLAG_VRAM, - 0, 0x0000, NULL, &priv->bo); - if (!ret) { - ret = nouveau_bo_pin(priv->bo, TTM_PL_FLAG_VRAM); - if (!ret) { - ret = nouveau_bo_map(priv->bo); - if (ret) - nouveau_bo_unpin(priv->bo); - } - if (ret) - nouveau_bo_ref(NULL, &priv->bo); - } - - if (ret == 0) { - nouveau_bo_wr32(priv->bo, 0x000, 0x00000000); - priv->base.sync = nv17_fence_sync; - priv->base.resume = nv17_fence_resume; - } - } - - if (ret) - nv10_fence_destroy(drm); - return ret; + return 0; } diff --git a/drivers/gpu/drm/nouveau/nv10_fence.h b/drivers/gpu/drm/nouveau/nv10_fence.h new file mode 100644 index 000000000000..e5d9204826c2 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nv10_fence.h @@ -0,0 +1,19 @@ +#ifndef __NV10_FENCE_H_ +#define __NV10_FENCE_H_ + +#include <core/os.h> +#include "nouveau_fence.h" +#include "nouveau_bo.h" + +struct nv10_fence_chan { + struct nouveau_fence_chan base; +}; + +struct nv10_fence_priv { + struct nouveau_fence_priv base; + struct nouveau_bo *bo; + spinlock_t lock; + u32 sequence; +}; + +#endif diff --git a/drivers/gpu/drm/nouveau/nv17_fence.c b/drivers/gpu/drm/nouveau/nv17_fence.c new file mode 100644 index 000000000000..8e47a9bae8c3 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nv17_fence.c @@ -0,0 +1,149 @@ +/* + * Copyright 2012 Red Hat Inc. + * + * 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 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 + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: Ben Skeggs <bskeggs@redhat.com> + */ + +#include <core/object.h> +#include <core/class.h> + +#include "nouveau_drm.h" +#include "nouveau_dma.h" +#include "nv10_fence.h" + +int +nv17_fence_sync(struct nouveau_fence *fence, + struct nouveau_channel *prev, struct nouveau_channel *chan) +{ + struct nv10_fence_priv *priv = chan->drm->fence; + u32 value; + int ret; + + if (!mutex_trylock(&prev->cli->mutex)) + return -EBUSY; + + spin_lock(&priv->lock); + value = priv->sequence; + priv->sequence += 2; + spin_unlock(&priv->lock); + + ret = RING_SPACE(prev, 5); + if (!ret) { + BEGIN_NV04(prev, 0, NV11_SUBCHAN_DMA_SEMAPHORE, 4); + OUT_RING (prev, NvSema); + OUT_RING (prev, 0); + OUT_RING (prev, value + 0); + OUT_RING (prev, value + 1); + FIRE_RING (prev); + } + + if (!ret && !(ret = RING_SPACE(chan, 5))) { + BEGIN_NV04(chan, 0, NV11_SUBCHAN_DMA_SEMAPHORE, 4); + OUT_RING (chan, NvSema); + OUT_RING (chan, 0); + OUT_RING (chan, value + 1); + OUT_RING (chan, value + 2); + FIRE_RING (chan); + } + + mutex_unlock(&prev->cli->mutex); + return 0; +} + +static int +nv17_fence_context_new(struct nouveau_channel *chan) +{ + struct nv10_fence_priv *priv = chan->drm->fence; + struct nv10_fence_chan *fctx; + struct ttm_mem_reg *mem = &priv->bo->bo.mem; + struct nouveau_object *object; + u32 start = mem->start * PAGE_SIZE; + u32 limit = mem->start + mem->size - 1; + int ret = 0; + + fctx = chan->fence = kzalloc(sizeof(*fctx), GFP_KERNEL); + if (!fctx) + return -ENOMEM; + + nouveau_fence_context_new(&fctx->base); + fctx->base.emit = nv10_fence_emit; + fctx->base.read = nv10_fence_read; + fctx->base.sync = nv17_fence_sync; + + ret = nouveau_object_new(nv_object(chan->cli), chan->handle, + NvSema, 0x0002, + &(struct nv_dma_class) { + .flags = NV_DMA_TARGET_VRAM | + NV_DMA_ACCESS_RDWR, + .start = start, + .limit = limit, + }, sizeof(struct nv_dma_class), + &object); + if (ret) + nv10_fence_context_del(chan); + return ret; +} + +void +nv17_fence_resume(struct nouveau_drm *drm) +{ + struct nv10_fence_priv *priv = drm->fence; + + nouveau_bo_wr32(priv->bo, 0, priv->sequence); +} + +int +nv17_fence_create(struct nouveau_drm *drm) +{ + struct nv10_fence_priv *priv; + int ret = 0; + + priv = drm->fence = kzalloc(sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + priv->base.dtor = nv10_fence_destroy; + priv->base.resume = nv17_fence_resume; + priv->base.context_new = nv17_fence_context_new; + priv->base.context_del = nv10_fence_context_del; + spin_lock_init(&priv->lock); + + ret = nouveau_bo_new(drm->dev, 4096, 0x1000, TTM_PL_FLAG_VRAM, + 0, 0x0000, NULL, &priv->bo); + if (!ret) { + ret = nouveau_bo_pin(priv->bo, TTM_PL_FLAG_VRAM); + if (!ret) { + ret = nouveau_bo_map(priv->bo); + if (ret) + nouveau_bo_unpin(priv->bo); + } + if (ret) + nouveau_bo_ref(NULL, &priv->bo); + } + + if (ret) { + nv10_fence_destroy(drm); + return ret; + } + + nouveau_bo_wr32(priv->bo, 0x000, 0x00000000); + return ret; +} diff --git a/drivers/gpu/drm/nouveau/nv17_tv.c b/drivers/gpu/drm/nouveau/nv17_tv.c index 2ca276ada507..977e42be2050 100644 --- a/drivers/gpu/drm/nouveau/nv17_tv.c +++ b/drivers/gpu/drm/nouveau/nv17_tv.c @@ -768,7 +768,7 @@ static int nv17_tv_set_property(struct drm_encoder *encoder, .crtc = crtc, }; - crtc->funcs->set_config(&modeset); + drm_mode_set_config_internal(&modeset); } } diff --git a/drivers/gpu/drm/nouveau/nv50_display.c b/drivers/gpu/drm/nouveau/nv50_display.c index 35874085a61e..a6237c9cbbc3 100644 --- a/drivers/gpu/drm/nouveau/nv50_display.c +++ b/drivers/gpu/drm/nouveau/nv50_display.c @@ -43,6 +43,7 @@ #include <subdev/timer.h> #include <subdev/bar.h> #include <subdev/fb.h> +#include <subdev/i2c.h> #define EVO_DMA_NR 9 @@ -128,6 +129,11 @@ struct nv50_dmac { struct nv50_chan base; dma_addr_t handle; u32 *ptr; + + /* Protects against concurrent pushbuf access to this channel, lock is + * grabbed by evo_wait (if the pushbuf reservation is successful) and + * dropped again by evo_kick. */ + struct mutex lock; }; static void @@ -271,6 +277,8 @@ nv50_dmac_create(struct nouveau_object *core, u32 bclass, u8 head, u32 pushbuf = *(u32 *)data; int ret; + mutex_init(&dmac->lock); + dmac->ptr = pci_alloc_consistent(nv_device(core)->pdev, PAGE_SIZE, &dmac->handle); if (!dmac->ptr) @@ -395,11 +403,13 @@ evo_wait(void *evoc, int nr) struct nv50_dmac *dmac = evoc; u32 put = nv_ro32(dmac->base.user, 0x0000) / 4; + mutex_lock(&dmac->lock); if (put + nr >= (PAGE_SIZE / 4) - 8) { dmac->ptr[put] = 0x20000000; nv_wo32(dmac->base.user, 0x0000, 0x00000000); if (!nv_wait(dmac->base.user, 0x0004, ~0, 0x00000000)) { + mutex_unlock(&dmac->lock); NV_ERROR(dmac->base.user, "channel stalled\n"); return NULL; } @@ -415,6 +425,7 @@ evo_kick(u32 *push, void *evoc) { struct nv50_dmac *dmac = evoc; nv_wo32(dmac->base.user, 0x0000, (push - dmac->ptr) << 2); + mutex_unlock(&dmac->lock); } #define evo_mthd(p,m,s) *((p)++) = (((s) << 18) | (m)) @@ -423,7 +434,10 @@ evo_kick(u32 *push, void *evoc) static bool evo_sync_wait(void *data) { - return nouveau_bo_rd32(data, EVO_MAST_NTFY) != 0x00000000; + if (nouveau_bo_rd32(data, EVO_MAST_NTFY) != 0x00000000) + return true; + usleep_range(1, 2); + return false; } static int @@ -502,7 +516,7 @@ nv50_display_flip_next(struct drm_crtc *crtc, struct drm_framebuffer *fb, if (ret) return ret; - if (nv_mclass(chan->object) < NVC0_CHANNEL_IND_CLASS) { + if (nv_mclass(chan->object) < NV84_CHANNEL_IND_CLASS) { BEGIN_NV04(chan, 0, NV11_SUBCHAN_DMA_SEMAPHORE, 2); OUT_RING (chan, NvEvoSema0 + nv_crtc->index); OUT_RING (chan, sync->sem.offset); @@ -512,24 +526,36 @@ nv50_display_flip_next(struct drm_crtc *crtc, struct drm_framebuffer *fb, OUT_RING (chan, sync->sem.offset ^ 0x10); OUT_RING (chan, 0x74b1e000); BEGIN_NV04(chan, 0, NV11_SUBCHAN_DMA_SEMAPHORE, 1); - if (nv_mclass(chan->object) < NV84_CHANNEL_DMA_CLASS) - OUT_RING (chan, NvSema); - else - OUT_RING (chan, chan->vram); + OUT_RING (chan, NvSema); + } else + if (nv_mclass(chan->object) < NVC0_CHANNEL_IND_CLASS) { + u64 offset = nv84_fence_crtc(chan, nv_crtc->index); + offset += sync->sem.offset; + + BEGIN_NV04(chan, 0, NV84_SUBCHAN_SEMAPHORE_ADDRESS_HIGH, 4); + OUT_RING (chan, upper_32_bits(offset)); + OUT_RING (chan, lower_32_bits(offset)); + OUT_RING (chan, 0xf00d0000 | sync->sem.value); + OUT_RING (chan, 0x00000002); + BEGIN_NV04(chan, 0, NV84_SUBCHAN_SEMAPHORE_ADDRESS_HIGH, 4); + OUT_RING (chan, upper_32_bits(offset)); + OUT_RING (chan, lower_32_bits(offset ^ 0x10)); + OUT_RING (chan, 0x74b1e000); + OUT_RING (chan, 0x00000001); } else { - u64 offset = nvc0_fence_crtc(chan, nv_crtc->index); + u64 offset = nv84_fence_crtc(chan, nv_crtc->index); offset += sync->sem.offset; BEGIN_NVC0(chan, 0, NV84_SUBCHAN_SEMAPHORE_ADDRESS_HIGH, 4); OUT_RING (chan, upper_32_bits(offset)); OUT_RING (chan, lower_32_bits(offset)); OUT_RING (chan, 0xf00d0000 | sync->sem.value); - OUT_RING (chan, 0x1002); + OUT_RING (chan, 0x00001002); BEGIN_NVC0(chan, 0, NV84_SUBCHAN_SEMAPHORE_ADDRESS_HIGH, 4); OUT_RING (chan, upper_32_bits(offset)); OUT_RING (chan, lower_32_bits(offset ^ 0x10)); OUT_RING (chan, 0x74b1e000); - OUT_RING (chan, 0x1001); + OUT_RING (chan, 0x00001001); } FIRE_RING (chan); @@ -1493,9 +1519,6 @@ nv50_dac_disconnect(struct drm_encoder *encoder) evo_mthd(push, 0x0180 + (or * 0x020), 1); evo_data(push, 0x00000000); } - - evo_mthd(push, 0x0080, 1); - evo_data(push, 0x00000000); evo_kick(push, mast); } } @@ -1542,20 +1565,23 @@ static const struct drm_encoder_funcs nv50_dac_func = { static int nv50_dac_create(struct drm_connector *connector, struct dcb_output *dcbe) { - struct drm_device *dev = connector->dev; + struct nouveau_drm *drm = nouveau_drm(connector->dev); + struct nouveau_i2c *i2c = nouveau_i2c(drm->device); struct nouveau_encoder *nv_encoder; struct drm_encoder *encoder; + int type = DRM_MODE_ENCODER_DAC; nv_encoder = kzalloc(sizeof(*nv_encoder), GFP_KERNEL); if (!nv_encoder) return -ENOMEM; nv_encoder->dcb = dcbe; nv_encoder->or = ffs(dcbe->or) - 1; + nv_encoder->i2c = i2c->find(i2c, dcbe->i2c_index); encoder = to_drm_encoder(nv_encoder); encoder->possible_crtcs = dcbe->heads; encoder->possible_clones = 0; - drm_encoder_init(dev, encoder, &nv50_dac_func, DRM_MODE_ENCODER_DAC); + drm_encoder_init(connector->dev, encoder, &nv50_dac_func, type); drm_encoder_helper_add(encoder, &nv50_dac_hfunc); drm_mode_connector_attach_encoder(connector, encoder); @@ -1664,9 +1690,6 @@ nv50_sor_dpms(struct drm_encoder *encoder, int mode) } nv_call(disp->core, NV50_DISP_SOR_PWR + or, (mode == DRM_MODE_DPMS_ON)); - - if (nv_encoder->dcb->type == DCB_OUTPUT_DP) - nouveau_dp_dpms(encoder, mode, nv_encoder->dp.datarate, disp->core); } static bool @@ -1709,9 +1732,6 @@ nv50_sor_disconnect(struct drm_encoder *encoder) evo_mthd(push, 0x0200 + (or * 0x20), 1); evo_data(push, 0x00000000); } - - evo_mthd(push, 0x0080, 1); - evo_data(push, 0x00000000); evo_kick(push, mast); } @@ -1723,14 +1743,6 @@ nv50_sor_disconnect(struct drm_encoder *encoder) } static void -nv50_sor_prepare(struct drm_encoder *encoder) -{ - nv50_sor_disconnect(encoder); - if (nouveau_encoder(encoder)->dcb->type == DCB_OUTPUT_DP) - evo_sync(encoder->dev); -} - -static void nv50_sor_commit(struct drm_encoder *encoder) { } @@ -1825,8 +1837,13 @@ nv50_sor_mode_set(struct drm_encoder *encoder, struct drm_display_mode *umode, push = evo_wait(nv50_mast(dev), 8); if (push) { if (nv50_vers(mast) < NVD0_DISP_CLASS) { + u32 ctrl = (depth << 16) | (proto << 8) | owner; + if (mode->flags & DRM_MODE_FLAG_NHSYNC) + ctrl |= 0x00001000; + if (mode->flags & DRM_MODE_FLAG_NVSYNC) + ctrl |= 0x00002000; evo_mthd(push, 0x0600 + (nv_encoder->or * 0x040), 1); - evo_data(push, (depth << 16) | (proto << 8) | owner); + evo_data(push, ctrl); } else { u32 magic = 0x31ec6000 | (nv_crtc->index << 25); u32 syncs = 0x00000001; @@ -1862,7 +1879,7 @@ nv50_sor_destroy(struct drm_encoder *encoder) static const struct drm_encoder_helper_funcs nv50_sor_hfunc = { .dpms = nv50_sor_dpms, .mode_fixup = nv50_sor_mode_fixup, - .prepare = nv50_sor_prepare, + .prepare = nv50_sor_disconnect, .commit = nv50_sor_commit, .mode_set = nv50_sor_mode_set, .disable = nv50_sor_disconnect, @@ -1876,21 +1893,33 @@ static const struct drm_encoder_funcs nv50_sor_func = { static int nv50_sor_create(struct drm_connector *connector, struct dcb_output *dcbe) { - struct drm_device *dev = connector->dev; + struct nouveau_drm *drm = nouveau_drm(connector->dev); + struct nouveau_i2c *i2c = nouveau_i2c(drm->device); struct nouveau_encoder *nv_encoder; struct drm_encoder *encoder; + int type; + + switch (dcbe->type) { + case DCB_OUTPUT_LVDS: type = DRM_MODE_ENCODER_LVDS; break; + case DCB_OUTPUT_TMDS: + case DCB_OUTPUT_DP: + default: + type = DRM_MODE_ENCODER_TMDS; + break; + } nv_encoder = kzalloc(sizeof(*nv_encoder), GFP_KERNEL); if (!nv_encoder) return -ENOMEM; nv_encoder->dcb = dcbe; nv_encoder->or = ffs(dcbe->or) - 1; + nv_encoder->i2c = i2c->find(i2c, dcbe->i2c_index); nv_encoder->last_dpms = DRM_MODE_DPMS_OFF; encoder = to_drm_encoder(nv_encoder); encoder->possible_crtcs = dcbe->heads; encoder->possible_clones = 0; - drm_encoder_init(dev, encoder, &nv50_sor_func, DRM_MODE_ENCODER_TMDS); + drm_encoder_init(connector->dev, encoder, &nv50_sor_func, type); drm_encoder_helper_add(encoder, &nv50_sor_hfunc); drm_mode_connector_attach_encoder(connector, encoder); @@ -1898,6 +1927,181 @@ nv50_sor_create(struct drm_connector *connector, struct dcb_output *dcbe) } /****************************************************************************** + * PIOR + *****************************************************************************/ + +static void +nv50_pior_dpms(struct drm_encoder *encoder, int mode) +{ + struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder); + struct nv50_disp *disp = nv50_disp(encoder->dev); + u32 mthd = (nv_encoder->dcb->type << 12) | nv_encoder->or; + u32 ctrl = (mode == DRM_MODE_DPMS_ON); + nv_call(disp->core, NV50_DISP_PIOR_PWR + mthd, ctrl); +} + +static bool +nv50_pior_mode_fixup(struct drm_encoder *encoder, + const struct drm_display_mode *mode, + struct drm_display_mode *adjusted_mode) +{ + struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder); + struct nouveau_connector *nv_connector; + + nv_connector = nouveau_encoder_connector_get(nv_encoder); + if (nv_connector && nv_connector->native_mode) { + if (nv_connector->scaling_mode != DRM_MODE_SCALE_NONE) { + int id = adjusted_mode->base.id; + *adjusted_mode = *nv_connector->native_mode; + adjusted_mode->base.id = id; + } + } + + adjusted_mode->clock *= 2; + return true; +} + +static void +nv50_pior_commit(struct drm_encoder *encoder) +{ +} + +static void +nv50_pior_mode_set(struct drm_encoder *encoder, struct drm_display_mode *mode, + struct drm_display_mode *adjusted_mode) +{ + struct nv50_mast *mast = nv50_mast(encoder->dev); + struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder); + struct nouveau_crtc *nv_crtc = nouveau_crtc(encoder->crtc); + struct nouveau_connector *nv_connector; + u8 owner = 1 << nv_crtc->index; + u8 proto, depth; + u32 *push; + + nv_connector = nouveau_encoder_connector_get(nv_encoder); + switch (nv_connector->base.display_info.bpc) { + case 10: depth = 0x6; break; + case 8: depth = 0x5; break; + case 6: depth = 0x2; break; + default: depth = 0x0; break; + } + + switch (nv_encoder->dcb->type) { + case DCB_OUTPUT_TMDS: + case DCB_OUTPUT_DP: + proto = 0x0; + break; + default: + BUG_ON(1); + break; + } + + nv50_pior_dpms(encoder, DRM_MODE_DPMS_ON); + + push = evo_wait(mast, 8); + if (push) { + if (nv50_vers(mast) < NVD0_DISP_MAST_CLASS) { + u32 ctrl = (depth << 16) | (proto << 8) | owner; + if (mode->flags & DRM_MODE_FLAG_NHSYNC) + ctrl |= 0x00001000; + if (mode->flags & DRM_MODE_FLAG_NVSYNC) + ctrl |= 0x00002000; + evo_mthd(push, 0x0700 + (nv_encoder->or * 0x040), 1); + evo_data(push, ctrl); + } + + evo_kick(push, mast); + } + + nv_encoder->crtc = encoder->crtc; +} + +static void +nv50_pior_disconnect(struct drm_encoder *encoder) +{ + struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder); + struct nv50_mast *mast = nv50_mast(encoder->dev); + const int or = nv_encoder->or; + u32 *push; + + if (nv_encoder->crtc) { + nv50_crtc_prepare(nv_encoder->crtc); + + push = evo_wait(mast, 4); + if (push) { + if (nv50_vers(mast) < NVD0_DISP_MAST_CLASS) { + evo_mthd(push, 0x0700 + (or * 0x040), 1); + evo_data(push, 0x00000000); + } + evo_kick(push, mast); + } + } + + nv_encoder->crtc = NULL; +} + +static void +nv50_pior_destroy(struct drm_encoder *encoder) +{ + drm_encoder_cleanup(encoder); + kfree(encoder); +} + +static const struct drm_encoder_helper_funcs nv50_pior_hfunc = { + .dpms = nv50_pior_dpms, + .mode_fixup = nv50_pior_mode_fixup, + .prepare = nv50_pior_disconnect, + .commit = nv50_pior_commit, + .mode_set = nv50_pior_mode_set, + .disable = nv50_pior_disconnect, + .get_crtc = nv50_display_crtc_get, +}; + +static const struct drm_encoder_funcs nv50_pior_func = { + .destroy = nv50_pior_destroy, +}; + +static int +nv50_pior_create(struct drm_connector *connector, struct dcb_output *dcbe) +{ + struct nouveau_drm *drm = nouveau_drm(connector->dev); + struct nouveau_i2c *i2c = nouveau_i2c(drm->device); + struct nouveau_i2c_port *ddc = NULL; + struct nouveau_encoder *nv_encoder; + struct drm_encoder *encoder; + int type; + + switch (dcbe->type) { + case DCB_OUTPUT_TMDS: + ddc = i2c->find_type(i2c, NV_I2C_TYPE_EXTDDC(dcbe->extdev)); + type = DRM_MODE_ENCODER_TMDS; + break; + case DCB_OUTPUT_DP: + ddc = i2c->find_type(i2c, NV_I2C_TYPE_EXTAUX(dcbe->extdev)); + type = DRM_MODE_ENCODER_TMDS; + break; + default: + return -ENODEV; + } + + nv_encoder = kzalloc(sizeof(*nv_encoder), GFP_KERNEL); + if (!nv_encoder) + return -ENOMEM; + nv_encoder->dcb = dcbe; + nv_encoder->or = ffs(dcbe->or) - 1; + nv_encoder->i2c = ddc; + + encoder = to_drm_encoder(nv_encoder); + encoder->possible_crtcs = dcbe->heads; + encoder->possible_clones = 0; + drm_encoder_init(connector->dev, encoder, &nv50_pior_func, type); + drm_encoder_helper_add(encoder, &nv50_pior_hfunc); + + drm_mode_connector_attach_encoder(connector, encoder); + return 0; +} + +/****************************************************************************** * Init *****************************************************************************/ void @@ -1913,7 +2117,7 @@ nv50_display_init(struct drm_device *dev) evo_mthd(push, 0x0088, 1); evo_data(push, NvEvoSync); evo_kick(push, nv50_mast(dev)); - return evo_sync(dev); + return 0; } return -EBUSY; @@ -2019,25 +2223,28 @@ nv50_display_create(struct drm_device *dev) if (IS_ERR(connector)) continue; - if (dcbe->location != DCB_LOC_ON_CHIP) { - NV_WARN(drm, "skipping off-chip encoder %d/%d\n", - dcbe->type, ffs(dcbe->or) - 1); - continue; + if (dcbe->location == DCB_LOC_ON_CHIP) { + switch (dcbe->type) { + case DCB_OUTPUT_TMDS: + case DCB_OUTPUT_LVDS: + case DCB_OUTPUT_DP: + ret = nv50_sor_create(connector, dcbe); + break; + case DCB_OUTPUT_ANALOG: + ret = nv50_dac_create(connector, dcbe); + break; + default: + ret = -ENODEV; + break; + } + } else { + ret = nv50_pior_create(connector, dcbe); } - switch (dcbe->type) { - case DCB_OUTPUT_TMDS: - case DCB_OUTPUT_LVDS: - case DCB_OUTPUT_DP: - nv50_sor_create(connector, dcbe); - break; - case DCB_OUTPUT_ANALOG: - nv50_dac_create(connector, dcbe); - break; - default: - NV_WARN(drm, "skipping unsupported encoder %d/%d\n", - dcbe->type, ffs(dcbe->or) - 1); - continue; + if (ret) { + NV_WARN(drm, "failed to create encoder %d/%d/%d: %d\n", + dcbe->location, dcbe->type, + ffs(dcbe->or) - 1, ret); } } diff --git a/drivers/gpu/drm/nouveau/nv50_fence.c b/drivers/gpu/drm/nouveau/nv50_fence.c index d889f3ac0d41..f9701e567db8 100644 --- a/drivers/gpu/drm/nouveau/nv50_fence.c +++ b/drivers/gpu/drm/nouveau/nv50_fence.c @@ -27,27 +27,16 @@ #include "nouveau_drm.h" #include "nouveau_dma.h" -#include "nouveau_fence.h" +#include "nv10_fence.h" #include "nv50_display.h" -struct nv50_fence_chan { - struct nouveau_fence_chan base; -}; - -struct nv50_fence_priv { - struct nouveau_fence_priv base; - struct nouveau_bo *bo; - spinlock_t lock; - u32 sequence; -}; - static int nv50_fence_context_new(struct nouveau_channel *chan) { struct drm_device *dev = chan->drm->dev; - struct nv50_fence_priv *priv = chan->drm->fence; - struct nv50_fence_chan *fctx; + struct nv10_fence_priv *priv = chan->drm->fence; + struct nv10_fence_chan *fctx; struct ttm_mem_reg *mem = &priv->bo->bo.mem; struct nouveau_object *object; int ret, i; @@ -57,6 +46,9 @@ nv50_fence_context_new(struct nouveau_channel *chan) return -ENOMEM; nouveau_fence_context_new(&fctx->base); + fctx->base.emit = nv10_fence_emit; + fctx->base.read = nv10_fence_read; + fctx->base.sync = nv17_fence_sync; ret = nouveau_object_new(nv_object(chan->cli), chan->handle, NvSema, 0x0002, @@ -91,7 +83,7 @@ nv50_fence_context_new(struct nouveau_channel *chan) int nv50_fence_create(struct nouveau_drm *drm) { - struct nv50_fence_priv *priv; + struct nv10_fence_priv *priv; int ret = 0; priv = drm->fence = kzalloc(sizeof(*priv), GFP_KERNEL); @@ -99,11 +91,9 @@ nv50_fence_create(struct nouveau_drm *drm) return -ENOMEM; priv->base.dtor = nv10_fence_destroy; + priv->base.resume = nv17_fence_resume; priv->base.context_new = nv50_fence_context_new; priv->base.context_del = nv10_fence_context_del; - priv->base.emit = nv10_fence_emit; - priv->base.read = nv10_fence_read; - priv->base.sync = nv17_fence_sync; spin_lock_init(&priv->lock); ret = nouveau_bo_new(drm->dev, 4096, 0x1000, TTM_PL_FLAG_VRAM, @@ -119,13 +109,11 @@ nv50_fence_create(struct nouveau_drm *drm) nouveau_bo_ref(NULL, &priv->bo); } - if (ret == 0) { - nouveau_bo_wr32(priv->bo, 0x000, 0x00000000); - priv->base.sync = nv17_fence_sync; - priv->base.resume = nv17_fence_resume; + if (ret) { + nv10_fence_destroy(drm); + return ret; } - if (ret) - nv10_fence_destroy(drm); + nouveau_bo_wr32(priv->bo, 0x000, 0x00000000); return ret; } diff --git a/drivers/gpu/drm/nouveau/nv84_fence.c b/drivers/gpu/drm/nouveau/nv84_fence.c index c686650584b6..9fd475c89820 100644 --- a/drivers/gpu/drm/nouveau/nv84_fence.c +++ b/drivers/gpu/drm/nouveau/nv84_fence.c @@ -23,6 +23,7 @@ */ #include <core/object.h> +#include <core/client.h> #include <core/class.h> #include <engine/fifo.h> @@ -33,79 +34,115 @@ #include "nv50_display.h" -struct nv84_fence_chan { - struct nouveau_fence_chan base; -}; - -struct nv84_fence_priv { - struct nouveau_fence_priv base; - struct nouveau_gpuobj *mem; -}; +u64 +nv84_fence_crtc(struct nouveau_channel *chan, int crtc) +{ + struct nv84_fence_chan *fctx = chan->fence; + return fctx->dispc_vma[crtc].offset; +} static int -nv84_fence_emit(struct nouveau_fence *fence) +nv84_fence_emit32(struct nouveau_channel *chan, u64 virtual, u32 sequence) { - struct nouveau_channel *chan = fence->channel; - struct nouveau_fifo_chan *fifo = (void *)chan->object; - int ret = RING_SPACE(chan, 7); + int ret = RING_SPACE(chan, 8); if (ret == 0) { BEGIN_NV04(chan, 0, NV11_SUBCHAN_DMA_SEMAPHORE, 1); - OUT_RING (chan, NvSema); - BEGIN_NV04(chan, 0, NV84_SUBCHAN_SEMAPHORE_ADDRESS_HIGH, 4); - OUT_RING (chan, upper_32_bits(fifo->chid * 16)); - OUT_RING (chan, lower_32_bits(fifo->chid * 16)); - OUT_RING (chan, fence->sequence); + OUT_RING (chan, chan->vram); + BEGIN_NV04(chan, 0, NV84_SUBCHAN_SEMAPHORE_ADDRESS_HIGH, 5); + OUT_RING (chan, upper_32_bits(virtual)); + OUT_RING (chan, lower_32_bits(virtual)); + OUT_RING (chan, sequence); OUT_RING (chan, NV84_SUBCHAN_SEMAPHORE_TRIGGER_WRITE_LONG); + OUT_RING (chan, 0x00000000); FIRE_RING (chan); } return ret; } - static int -nv84_fence_sync(struct nouveau_fence *fence, - struct nouveau_channel *prev, struct nouveau_channel *chan) +nv84_fence_sync32(struct nouveau_channel *chan, u64 virtual, u32 sequence) { - struct nouveau_fifo_chan *fifo = (void *)prev->object; int ret = RING_SPACE(chan, 7); if (ret == 0) { BEGIN_NV04(chan, 0, NV11_SUBCHAN_DMA_SEMAPHORE, 1); - OUT_RING (chan, NvSema); + OUT_RING (chan, chan->vram); BEGIN_NV04(chan, 0, NV84_SUBCHAN_SEMAPHORE_ADDRESS_HIGH, 4); - OUT_RING (chan, upper_32_bits(fifo->chid * 16)); - OUT_RING (chan, lower_32_bits(fifo->chid * 16)); - OUT_RING (chan, fence->sequence); + OUT_RING (chan, upper_32_bits(virtual)); + OUT_RING (chan, lower_32_bits(virtual)); + OUT_RING (chan, sequence); OUT_RING (chan, NV84_SUBCHAN_SEMAPHORE_TRIGGER_ACQUIRE_GEQUAL); FIRE_RING (chan); } return ret; } +static int +nv84_fence_emit(struct nouveau_fence *fence) +{ + struct nouveau_channel *chan = fence->channel; + struct nv84_fence_chan *fctx = chan->fence; + struct nouveau_fifo_chan *fifo = (void *)chan->object; + u64 addr = fifo->chid * 16; + + if (fence->sysmem) + addr += fctx->vma_gart.offset; + else + addr += fctx->vma.offset; + + return fctx->base.emit32(chan, addr, fence->sequence); +} + +static int +nv84_fence_sync(struct nouveau_fence *fence, + struct nouveau_channel *prev, struct nouveau_channel *chan) +{ + struct nv84_fence_chan *fctx = chan->fence; + struct nouveau_fifo_chan *fifo = (void *)prev->object; + u64 addr = fifo->chid * 16; + + if (fence->sysmem) + addr += fctx->vma_gart.offset; + else + addr += fctx->vma.offset; + + return fctx->base.sync32(chan, addr, fence->sequence); +} + static u32 nv84_fence_read(struct nouveau_channel *chan) { struct nouveau_fifo_chan *fifo = (void *)chan->object; struct nv84_fence_priv *priv = chan->drm->fence; - return nv_ro32(priv->mem, fifo->chid * 16); + return nouveau_bo_rd32(priv->bo, fifo->chid * 16/4); } static void nv84_fence_context_del(struct nouveau_channel *chan) { + struct drm_device *dev = chan->drm->dev; + struct nv84_fence_priv *priv = chan->drm->fence; struct nv84_fence_chan *fctx = chan->fence; + int i; + + for (i = 0; i < dev->mode_config.num_crtc; i++) { + struct nouveau_bo *bo = nv50_display_crtc_sema(dev, i); + nouveau_bo_vma_del(bo, &fctx->dispc_vma[i]); + } + + nouveau_bo_vma_del(priv->bo, &fctx->vma_gart); + nouveau_bo_vma_del(priv->bo, &fctx->vma); nouveau_fence_context_del(&fctx->base); chan->fence = NULL; kfree(fctx); } -static int +int nv84_fence_context_new(struct nouveau_channel *chan) { - struct drm_device *dev = chan->drm->dev; struct nouveau_fifo_chan *fifo = (void *)chan->object; + struct nouveau_client *client = nouveau_client(fifo); struct nv84_fence_priv *priv = chan->drm->fence; struct nv84_fence_chan *fctx; - struct nouveau_object *object; int ret, i; fctx = chan->fence = kzalloc(sizeof(*fctx), GFP_KERNEL); @@ -113,44 +150,74 @@ nv84_fence_context_new(struct nouveau_channel *chan) return -ENOMEM; nouveau_fence_context_new(&fctx->base); + fctx->base.emit = nv84_fence_emit; + fctx->base.sync = nv84_fence_sync; + fctx->base.read = nv84_fence_read; + fctx->base.emit32 = nv84_fence_emit32; + fctx->base.sync32 = nv84_fence_sync32; - ret = nouveau_object_new(nv_object(chan->cli), chan->handle, - NvSema, 0x0002, - &(struct nv_dma_class) { - .flags = NV_DMA_TARGET_VRAM | - NV_DMA_ACCESS_RDWR, - .start = priv->mem->addr, - .limit = priv->mem->addr + - priv->mem->size - 1, - }, sizeof(struct nv_dma_class), - &object); - - /* dma objects for display sync channel semaphore blocks */ - for (i = 0; !ret && i < dev->mode_config.num_crtc; i++) { - struct nouveau_bo *bo = nv50_display_crtc_sema(dev, i); + ret = nouveau_bo_vma_add(priv->bo, client->vm, &fctx->vma); + if (ret == 0) { + ret = nouveau_bo_vma_add(priv->bo_gart, client->vm, + &fctx->vma_gart); + } - ret = nouveau_object_new(nv_object(chan->cli), chan->handle, - NvEvoSema0 + i, 0x003d, - &(struct nv_dma_class) { - .flags = NV_DMA_TARGET_VRAM | - NV_DMA_ACCESS_RDWR, - .start = bo->bo.offset, - .limit = bo->bo.offset + 0xfff, - }, sizeof(struct nv_dma_class), - &object); + /* map display semaphore buffers into channel's vm */ + for (i = 0; !ret && i < chan->drm->dev->mode_config.num_crtc; i++) { + struct nouveau_bo *bo = nv50_display_crtc_sema(chan->drm->dev, i); + ret = nouveau_bo_vma_add(bo, client->vm, &fctx->dispc_vma[i]); } + nouveau_bo_wr32(priv->bo, fifo->chid * 16/4, 0x00000000); + if (ret) nv84_fence_context_del(chan); - nv_wo32(priv->mem, fifo->chid * 16, 0x00000000); return ret; } +static bool +nv84_fence_suspend(struct nouveau_drm *drm) +{ + struct nouveau_fifo *pfifo = nouveau_fifo(drm->device); + struct nv84_fence_priv *priv = drm->fence; + int i; + + priv->suspend = vmalloc((pfifo->max + 1) * sizeof(u32)); + if (priv->suspend) { + for (i = 0; i <= pfifo->max; i++) + priv->suspend[i] = nouveau_bo_rd32(priv->bo, i*4); + } + + return priv->suspend != NULL; +} + +static void +nv84_fence_resume(struct nouveau_drm *drm) +{ + struct nouveau_fifo *pfifo = nouveau_fifo(drm->device); + struct nv84_fence_priv *priv = drm->fence; + int i; + + if (priv->suspend) { + for (i = 0; i <= pfifo->max; i++) + nouveau_bo_wr32(priv->bo, i*4, priv->suspend[i]); + vfree(priv->suspend); + priv->suspend = NULL; + } +} + static void nv84_fence_destroy(struct nouveau_drm *drm) { struct nv84_fence_priv *priv = drm->fence; - nouveau_gpuobj_ref(NULL, &priv->mem); + nouveau_bo_unmap(priv->bo_gart); + if (priv->bo_gart) + nouveau_bo_unpin(priv->bo_gart); + nouveau_bo_ref(NULL, &priv->bo_gart); + nouveau_bo_unmap(priv->bo); + if (priv->bo) + nouveau_bo_unpin(priv->bo); + nouveau_bo_ref(NULL, &priv->bo); drm->fence = NULL; kfree(priv); } @@ -160,7 +227,6 @@ nv84_fence_create(struct nouveau_drm *drm) { struct nouveau_fifo *pfifo = nouveau_fifo(drm->device); struct nv84_fence_priv *priv; - u32 chan = pfifo->max + 1; int ret; priv = drm->fence = kzalloc(sizeof(*priv), GFP_KERNEL); @@ -168,14 +234,42 @@ nv84_fence_create(struct nouveau_drm *drm) return -ENOMEM; priv->base.dtor = nv84_fence_destroy; + priv->base.suspend = nv84_fence_suspend; + priv->base.resume = nv84_fence_resume; priv->base.context_new = nv84_fence_context_new; priv->base.context_del = nv84_fence_context_del; - priv->base.emit = nv84_fence_emit; - priv->base.sync = nv84_fence_sync; - priv->base.read = nv84_fence_read; - ret = nouveau_gpuobj_new(drm->device, NULL, chan * 16, 0x1000, 0, - &priv->mem); + init_waitqueue_head(&priv->base.waiting); + priv->base.uevent = true; + + ret = nouveau_bo_new(drm->dev, 16 * (pfifo->max + 1), 0, + TTM_PL_FLAG_VRAM, 0, 0, NULL, &priv->bo); + if (ret == 0) { + ret = nouveau_bo_pin(priv->bo, TTM_PL_FLAG_VRAM); + if (ret == 0) { + ret = nouveau_bo_map(priv->bo); + if (ret) + nouveau_bo_unpin(priv->bo); + } + if (ret) + nouveau_bo_ref(NULL, &priv->bo); + } + + if (ret == 0) + ret = nouveau_bo_new(drm->dev, 16 * (pfifo->max + 1), 0, + TTM_PL_FLAG_TT, 0, 0, NULL, + &priv->bo_gart); + if (ret == 0) { + ret = nouveau_bo_pin(priv->bo_gart, TTM_PL_FLAG_TT); + if (ret == 0) { + ret = nouveau_bo_map(priv->bo_gart); + if (ret) + nouveau_bo_unpin(priv->bo_gart); + } + if (ret) + nouveau_bo_ref(NULL, &priv->bo_gart); + } + if (ret) nv84_fence_destroy(drm); return ret; diff --git a/drivers/gpu/drm/nouveau/nvc0_fence.c b/drivers/gpu/drm/nouveau/nvc0_fence.c index 2a56b1b551cb..9566267fbc42 100644 --- a/drivers/gpu/drm/nouveau/nvc0_fence.c +++ b/drivers/gpu/drm/nouveau/nvc0_fence.c @@ -34,203 +34,57 @@ #include "nv50_display.h" -struct nvc0_fence_priv { - struct nouveau_fence_priv base; - struct nouveau_bo *bo; - u32 *suspend; -}; - -struct nvc0_fence_chan { - struct nouveau_fence_chan base; - struct nouveau_vma vma; - struct nouveau_vma dispc_vma[4]; -}; - -u64 -nvc0_fence_crtc(struct nouveau_channel *chan, int crtc) -{ - struct nvc0_fence_chan *fctx = chan->fence; - return fctx->dispc_vma[crtc].offset; -} - static int -nvc0_fence_emit(struct nouveau_fence *fence) +nvc0_fence_emit32(struct nouveau_channel *chan, u64 virtual, u32 sequence) { - struct nouveau_channel *chan = fence->channel; - struct nvc0_fence_chan *fctx = chan->fence; - struct nouveau_fifo_chan *fifo = (void *)chan->object; - u64 addr = fctx->vma.offset + fifo->chid * 16; - int ret; - - ret = RING_SPACE(chan, 5); + int ret = RING_SPACE(chan, 6); if (ret == 0) { - BEGIN_NVC0(chan, 0, NV84_SUBCHAN_SEMAPHORE_ADDRESS_HIGH, 4); - OUT_RING (chan, upper_32_bits(addr)); - OUT_RING (chan, lower_32_bits(addr)); - OUT_RING (chan, fence->sequence); + BEGIN_NVC0(chan, 0, NV84_SUBCHAN_SEMAPHORE_ADDRESS_HIGH, 5); + OUT_RING (chan, upper_32_bits(virtual)); + OUT_RING (chan, lower_32_bits(virtual)); + OUT_RING (chan, sequence); OUT_RING (chan, NV84_SUBCHAN_SEMAPHORE_TRIGGER_WRITE_LONG); + OUT_RING (chan, 0x00000000); FIRE_RING (chan); } - return ret; } static int -nvc0_fence_sync(struct nouveau_fence *fence, - struct nouveau_channel *prev, struct nouveau_channel *chan) +nvc0_fence_sync32(struct nouveau_channel *chan, u64 virtual, u32 sequence) { - struct nvc0_fence_chan *fctx = chan->fence; - struct nouveau_fifo_chan *fifo = (void *)prev->object; - u64 addr = fctx->vma.offset + fifo->chid * 16; - int ret; - - ret = RING_SPACE(chan, 5); + int ret = RING_SPACE(chan, 5); if (ret == 0) { BEGIN_NVC0(chan, 0, NV84_SUBCHAN_SEMAPHORE_ADDRESS_HIGH, 4); - OUT_RING (chan, upper_32_bits(addr)); - OUT_RING (chan, lower_32_bits(addr)); - OUT_RING (chan, fence->sequence); + OUT_RING (chan, upper_32_bits(virtual)); + OUT_RING (chan, lower_32_bits(virtual)); + OUT_RING (chan, sequence); OUT_RING (chan, NV84_SUBCHAN_SEMAPHORE_TRIGGER_ACQUIRE_GEQUAL | NVC0_SUBCHAN_SEMAPHORE_TRIGGER_YIELD); FIRE_RING (chan); } - return ret; } -static u32 -nvc0_fence_read(struct nouveau_channel *chan) -{ - struct nouveau_fifo_chan *fifo = (void *)chan->object; - struct nvc0_fence_priv *priv = chan->drm->fence; - return nouveau_bo_rd32(priv->bo, fifo->chid * 16/4); -} - -static void -nvc0_fence_context_del(struct nouveau_channel *chan) -{ - struct drm_device *dev = chan->drm->dev; - struct nvc0_fence_priv *priv = chan->drm->fence; - struct nvc0_fence_chan *fctx = chan->fence; - int i; - - for (i = 0; i < dev->mode_config.num_crtc; i++) { - struct nouveau_bo *bo = nv50_display_crtc_sema(dev, i); - nouveau_bo_vma_del(bo, &fctx->dispc_vma[i]); - } - - nouveau_bo_vma_del(priv->bo, &fctx->vma); - nouveau_fence_context_del(&fctx->base); - chan->fence = NULL; - kfree(fctx); -} - static int nvc0_fence_context_new(struct nouveau_channel *chan) { - struct nouveau_fifo_chan *fifo = (void *)chan->object; - struct nouveau_client *client = nouveau_client(fifo); - struct nvc0_fence_priv *priv = chan->drm->fence; - struct nvc0_fence_chan *fctx; - int ret, i; - - fctx = chan->fence = kzalloc(sizeof(*fctx), GFP_KERNEL); - if (!fctx) - return -ENOMEM; - - nouveau_fence_context_new(&fctx->base); - - ret = nouveau_bo_vma_add(priv->bo, client->vm, &fctx->vma); - if (ret) - nvc0_fence_context_del(chan); - - /* map display semaphore buffers into channel's vm */ - for (i = 0; !ret && i < chan->drm->dev->mode_config.num_crtc; i++) { - struct nouveau_bo *bo = nv50_display_crtc_sema(chan->drm->dev, i); - ret = nouveau_bo_vma_add(bo, client->vm, &fctx->dispc_vma[i]); + int ret = nv84_fence_context_new(chan); + if (ret == 0) { + struct nv84_fence_chan *fctx = chan->fence; + fctx->base.emit32 = nvc0_fence_emit32; + fctx->base.sync32 = nvc0_fence_sync32; } - - nouveau_bo_wr32(priv->bo, fifo->chid * 16/4, 0x00000000); return ret; } -static bool -nvc0_fence_suspend(struct nouveau_drm *drm) -{ - struct nouveau_fifo *pfifo = nouveau_fifo(drm->device); - struct nvc0_fence_priv *priv = drm->fence; - int i; - - priv->suspend = vmalloc((pfifo->max + 1) * sizeof(u32)); - if (priv->suspend) { - for (i = 0; i <= pfifo->max; i++) - priv->suspend[i] = nouveau_bo_rd32(priv->bo, i); - } - - return priv->suspend != NULL; -} - -static void -nvc0_fence_resume(struct nouveau_drm *drm) -{ - struct nouveau_fifo *pfifo = nouveau_fifo(drm->device); - struct nvc0_fence_priv *priv = drm->fence; - int i; - - if (priv->suspend) { - for (i = 0; i <= pfifo->max; i++) - nouveau_bo_wr32(priv->bo, i, priv->suspend[i]); - vfree(priv->suspend); - priv->suspend = NULL; - } -} - -static void -nvc0_fence_destroy(struct nouveau_drm *drm) -{ - struct nvc0_fence_priv *priv = drm->fence; - nouveau_bo_unmap(priv->bo); - if (priv->bo) - nouveau_bo_unpin(priv->bo); - nouveau_bo_ref(NULL, &priv->bo); - drm->fence = NULL; - kfree(priv); -} - int nvc0_fence_create(struct nouveau_drm *drm) { - struct nouveau_fifo *pfifo = nouveau_fifo(drm->device); - struct nvc0_fence_priv *priv; - int ret; - - priv = drm->fence = kzalloc(sizeof(*priv), GFP_KERNEL); - if (!priv) - return -ENOMEM; - - priv->base.dtor = nvc0_fence_destroy; - priv->base.suspend = nvc0_fence_suspend; - priv->base.resume = nvc0_fence_resume; - priv->base.context_new = nvc0_fence_context_new; - priv->base.context_del = nvc0_fence_context_del; - priv->base.emit = nvc0_fence_emit; - priv->base.sync = nvc0_fence_sync; - priv->base.read = nvc0_fence_read; - - ret = nouveau_bo_new(drm->dev, 16 * (pfifo->max + 1), 0, - TTM_PL_FLAG_VRAM, 0, 0, NULL, &priv->bo); + int ret = nv84_fence_create(drm); if (ret == 0) { - ret = nouveau_bo_pin(priv->bo, TTM_PL_FLAG_VRAM); - if (ret == 0) { - ret = nouveau_bo_map(priv->bo); - if (ret) - nouveau_bo_unpin(priv->bo); - } - if (ret) - nouveau_bo_ref(NULL, &priv->bo); + struct nv84_fence_priv *priv = drm->fence; + priv->base.context_new = nvc0_fence_context_new; } - - if (ret) - nvc0_fence_destroy(drm); return ret; } diff --git a/drivers/staging/omapdrm/Kconfig b/drivers/gpu/drm/omapdrm/Kconfig index b724a4131435..b724a4131435 100644 --- a/drivers/staging/omapdrm/Kconfig +++ b/drivers/gpu/drm/omapdrm/Kconfig diff --git a/drivers/staging/omapdrm/Makefile b/drivers/gpu/drm/omapdrm/Makefile index 1ca0e0016de4..d85e058f2845 100644 --- a/drivers/staging/omapdrm/Makefile +++ b/drivers/gpu/drm/omapdrm/Makefile @@ -5,6 +5,7 @@ ccflags-y := -Iinclude/drm -Werror omapdrm-y := omap_drv.o \ + omap_irq.o \ omap_debugfs.o \ omap_crtc.o \ omap_plane.o \ diff --git a/drivers/gpu/drm/omapdrm/TODO b/drivers/gpu/drm/omapdrm/TODO new file mode 100644 index 000000000000..4d8c18aa5dd7 --- /dev/null +++ b/drivers/gpu/drm/omapdrm/TODO @@ -0,0 +1,23 @@ +TODO +. Where should we do eviction (detatch_pages())? We aren't necessarily + accessing the pages via a GART, so maybe we need some other threshold + to put a cap on the # of pages that can be pin'd. + . Use mm_shrinker to trigger unpinning pages. + . This is mainly theoretical since most of these devices don't actually + have swap or harddrive. +. GEM/shmem backed pages can have existing mappings (kernel linear map, + etc..), which isn't really ideal. +. Revisit GEM sync object infrastructure.. TTM has some framework for this + already. Possibly this could be refactored out and made more common? + There should be some way to do this with less wheel-reinvention. + . This can be handled by the dma-buf fence/reservation stuff when it + lands + +Userspace: +. git://anongit.freedesktop.org/xorg/driver/xf86-video-omap + +Currently tested on +. OMAP3530 beagleboard +. OMAP4430 pandaboard +. OMAP4460 pandaboard +. OMAP5432 uEVM diff --git a/drivers/staging/omapdrm/omap_connector.c b/drivers/gpu/drm/omapdrm/omap_connector.c index 91edb3f96972..44284fd981fc 100644 --- a/drivers/staging/omapdrm/omap_connector.c +++ b/drivers/gpu/drm/omapdrm/omap_connector.c @@ -1,5 +1,5 @@ /* - * drivers/staging/omapdrm/omap_connector.c + * drivers/gpu/drm/omapdrm/omap_connector.c * * Copyright (C) 2011 Texas Instruments * Author: Rob Clark <rob@ti.com> @@ -31,9 +31,10 @@ struct omap_connector { struct drm_connector base; struct omap_dss_device *dssdev; + struct drm_encoder *encoder; }; -static inline void copy_timings_omap_to_drm(struct drm_display_mode *mode, +void copy_timings_omap_to_drm(struct drm_display_mode *mode, struct omap_video_timings *timings) { mode->clock = timings->pixel_clock; @@ -64,7 +65,7 @@ static inline void copy_timings_omap_to_drm(struct drm_display_mode *mode, mode->flags |= DRM_MODE_FLAG_NVSYNC; } -static inline void copy_timings_drm_to_omap(struct omap_video_timings *timings, +void copy_timings_drm_to_omap(struct omap_video_timings *timings, struct drm_display_mode *mode) { timings->pixel_clock = mode->clock; @@ -96,48 +97,7 @@ static inline void copy_timings_drm_to_omap(struct omap_video_timings *timings, timings->sync_pclk_edge = OMAPDSS_DRIVE_SIG_OPPOSITE_EDGES; } -static void omap_connector_dpms(struct drm_connector *connector, int mode) -{ - struct omap_connector *omap_connector = to_omap_connector(connector); - struct omap_dss_device *dssdev = omap_connector->dssdev; - int old_dpms; - - DBG("%s: %d", dssdev->name, mode); - - old_dpms = connector->dpms; - - /* from off to on, do from crtc to connector */ - if (mode < old_dpms) - drm_helper_connector_dpms(connector, mode); - - if (mode == DRM_MODE_DPMS_ON) { - /* store resume info for suspended displays */ - switch (dssdev->state) { - case OMAP_DSS_DISPLAY_SUSPENDED: - dssdev->activate_after_resume = true; - break; - case OMAP_DSS_DISPLAY_DISABLED: { - int ret = dssdev->driver->enable(dssdev); - if (ret) { - DBG("%s: failed to enable: %d", - dssdev->name, ret); - dssdev->driver->disable(dssdev); - } - break; - } - default: - break; - } - } else { - /* TODO */ - } - - /* from on to off, do from connector to crtc */ - if (mode > old_dpms) - drm_helper_connector_dpms(connector, mode); -} - -enum drm_connector_status omap_connector_detect( +static enum drm_connector_status omap_connector_detect( struct drm_connector *connector, bool force) { struct omap_connector *omap_connector = to_omap_connector(connector); @@ -164,8 +124,6 @@ static void omap_connector_destroy(struct drm_connector *connector) struct omap_connector *omap_connector = to_omap_connector(connector); struct omap_dss_device *dssdev = omap_connector->dssdev; - dssdev->driver->disable(dssdev); - DBG("%s", omap_connector->dssdev->name); drm_sysfs_connector_remove(connector); drm_connector_cleanup(connector); @@ -261,36 +219,12 @@ static int omap_connector_mode_valid(struct drm_connector *connector, struct drm_encoder *omap_connector_attached_encoder( struct drm_connector *connector) { - int i; struct omap_connector *omap_connector = to_omap_connector(connector); - - for (i = 0; i < DRM_CONNECTOR_MAX_ENCODER; i++) { - struct drm_mode_object *obj; - - if (connector->encoder_ids[i] == 0) - break; - - obj = drm_mode_object_find(connector->dev, - connector->encoder_ids[i], - DRM_MODE_OBJECT_ENCODER); - - if (obj) { - struct drm_encoder *encoder = obj_to_encoder(obj); - struct omap_overlay_manager *mgr = - omap_encoder_get_manager(encoder); - DBG("%s: found %s", omap_connector->dssdev->name, - mgr->name); - return encoder; - } - } - - DBG("%s: no encoder", omap_connector->dssdev->name); - - return NULL; + return omap_connector->encoder; } static const struct drm_connector_funcs omap_connector_funcs = { - .dpms = omap_connector_dpms, + .dpms = drm_helper_connector_dpms, .detect = omap_connector_detect, .fill_modes = drm_helper_probe_single_connector_modes, .destroy = omap_connector_destroy, @@ -302,34 +236,6 @@ static const struct drm_connector_helper_funcs omap_connector_helper_funcs = { .best_encoder = omap_connector_attached_encoder, }; -/* called from encoder when mode is set, to propagate settings to the dssdev */ -void omap_connector_mode_set(struct drm_connector *connector, - struct drm_display_mode *mode) -{ - struct drm_device *dev = connector->dev; - struct omap_connector *omap_connector = to_omap_connector(connector); - struct omap_dss_device *dssdev = omap_connector->dssdev; - struct omap_dss_driver *dssdrv = dssdev->driver; - struct omap_video_timings timings = {0}; - - copy_timings_drm_to_omap(&timings, mode); - - DBG("%s: set mode: %d:\"%s\" %d %d %d %d %d %d %d %d %d %d 0x%x 0x%x", - omap_connector->dssdev->name, - mode->base.id, mode->name, mode->vrefresh, mode->clock, - mode->hdisplay, mode->hsync_start, - mode->hsync_end, mode->htotal, - mode->vdisplay, mode->vsync_start, - mode->vsync_end, mode->vtotal, mode->type, mode->flags); - - if (dssdrv->check_timings(dssdev, &timings)) { - dev_err(dev->dev, "could not set timings\n"); - return; - } - - dssdrv->set_timings(dssdev, &timings); -} - /* flush an area of the framebuffer (in case of manual update display that * is not automatically flushed) */ @@ -344,7 +250,8 @@ void omap_connector_flush(struct drm_connector *connector, /* initialize connector */ struct drm_connector *omap_connector_init(struct drm_device *dev, - int connector_type, struct omap_dss_device *dssdev) + int connector_type, struct omap_dss_device *dssdev, + struct drm_encoder *encoder) { struct drm_connector *connector = NULL; struct omap_connector *omap_connector; @@ -360,6 +267,8 @@ struct drm_connector *omap_connector_init(struct drm_device *dev, } omap_connector->dssdev = dssdev; + omap_connector->encoder = encoder; + connector = &omap_connector->base; drm_connector_init(dev, connector, &omap_connector_funcs, diff --git a/drivers/gpu/drm/omapdrm/omap_crtc.c b/drivers/gpu/drm/omapdrm/omap_crtc.c new file mode 100644 index 000000000000..ac2258f59805 --- /dev/null +++ b/drivers/gpu/drm/omapdrm/omap_crtc.c @@ -0,0 +1,657 @@ +/* + * drivers/gpu/drm/omapdrm/omap_crtc.c + * + * Copyright (C) 2011 Texas Instruments + * Author: Rob Clark <rob@ti.com> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published by + * the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include "omap_drv.h" + +#include <drm/drm_mode.h> +#include "drm_crtc.h" +#include "drm_crtc_helper.h" + +#define to_omap_crtc(x) container_of(x, struct omap_crtc, base) + +struct omap_crtc { + struct drm_crtc base; + struct drm_plane *plane; + + const char *name; + int pipe; + enum omap_channel channel; + struct omap_overlay_manager_info info; + + /* + * Temporary: eventually this will go away, but it is needed + * for now to keep the output's happy. (They only need + * mgr->id.) Eventually this will be replaced w/ something + * more common-panel-framework-y + */ + struct omap_overlay_manager mgr; + + struct omap_video_timings timings; + bool enabled; + bool full_update; + + struct omap_drm_apply apply; + + struct omap_drm_irq apply_irq; + struct omap_drm_irq error_irq; + + /* list of in-progress apply's: */ + struct list_head pending_applies; + + /* list of queued apply's: */ + struct list_head queued_applies; + + /* for handling queued and in-progress applies: */ + struct work_struct apply_work; + + /* if there is a pending flip, these will be non-null: */ + struct drm_pending_vblank_event *event; + struct drm_framebuffer *old_fb; + + /* for handling page flips without caring about what + * the callback is called from. Possibly we should just + * make omap_gem always call the cb from the worker so + * we don't have to care about this.. + * + * XXX maybe fold into apply_work?? + */ + struct work_struct page_flip_work; +}; + +/* + * Manager-ops, callbacks from output when they need to configure + * the upstream part of the video pipe. + * + * Most of these we can ignore until we add support for command-mode + * panels.. for video-mode the crtc-helpers already do an adequate + * job of sequencing the setup of the video pipe in the proper order + */ + +/* we can probably ignore these until we support command-mode panels: */ +static void omap_crtc_start_update(struct omap_overlay_manager *mgr) +{ +} + +static int omap_crtc_enable(struct omap_overlay_manager *mgr) +{ + return 0; +} + +static void omap_crtc_disable(struct omap_overlay_manager *mgr) +{ +} + +static void omap_crtc_set_timings(struct omap_overlay_manager *mgr, + const struct omap_video_timings *timings) +{ + struct omap_crtc *omap_crtc = container_of(mgr, struct omap_crtc, mgr); + DBG("%s", omap_crtc->name); + omap_crtc->timings = *timings; + omap_crtc->full_update = true; +} + +static void omap_crtc_set_lcd_config(struct omap_overlay_manager *mgr, + const struct dss_lcd_mgr_config *config) +{ + struct omap_crtc *omap_crtc = container_of(mgr, struct omap_crtc, mgr); + DBG("%s", omap_crtc->name); + dispc_mgr_set_lcd_config(omap_crtc->channel, config); +} + +static int omap_crtc_register_framedone_handler( + struct omap_overlay_manager *mgr, + void (*handler)(void *), void *data) +{ + return 0; +} + +static void omap_crtc_unregister_framedone_handler( + struct omap_overlay_manager *mgr, + void (*handler)(void *), void *data) +{ +} + +static const struct dss_mgr_ops mgr_ops = { + .start_update = omap_crtc_start_update, + .enable = omap_crtc_enable, + .disable = omap_crtc_disable, + .set_timings = omap_crtc_set_timings, + .set_lcd_config = omap_crtc_set_lcd_config, + .register_framedone_handler = omap_crtc_register_framedone_handler, + .unregister_framedone_handler = omap_crtc_unregister_framedone_handler, +}; + +/* + * CRTC funcs: + */ + +static void omap_crtc_destroy(struct drm_crtc *crtc) +{ + struct omap_crtc *omap_crtc = to_omap_crtc(crtc); + + DBG("%s", omap_crtc->name); + + WARN_ON(omap_crtc->apply_irq.registered); + omap_irq_unregister(crtc->dev, &omap_crtc->error_irq); + + omap_crtc->plane->funcs->destroy(omap_crtc->plane); + drm_crtc_cleanup(crtc); + + kfree(omap_crtc); +} + +static void omap_crtc_dpms(struct drm_crtc *crtc, int mode) +{ + struct omap_drm_private *priv = crtc->dev->dev_private; + struct omap_crtc *omap_crtc = to_omap_crtc(crtc); + bool enabled = (mode == DRM_MODE_DPMS_ON); + int i; + + DBG("%s: %d", omap_crtc->name, mode); + + if (enabled != omap_crtc->enabled) { + omap_crtc->enabled = enabled; + omap_crtc->full_update = true; + omap_crtc_apply(crtc, &omap_crtc->apply); + + /* also enable our private plane: */ + WARN_ON(omap_plane_dpms(omap_crtc->plane, mode)); + + /* and any attached overlay planes: */ + for (i = 0; i < priv->num_planes; i++) { + struct drm_plane *plane = priv->planes[i]; + if (plane->crtc == crtc) + WARN_ON(omap_plane_dpms(plane, mode)); + } + } +} + +static bool omap_crtc_mode_fixup(struct drm_crtc *crtc, + const struct drm_display_mode *mode, + struct drm_display_mode *adjusted_mode) +{ + return true; +} + +static int omap_crtc_mode_set(struct drm_crtc *crtc, + struct drm_display_mode *mode, + struct drm_display_mode *adjusted_mode, + int x, int y, + struct drm_framebuffer *old_fb) +{ + struct omap_crtc *omap_crtc = to_omap_crtc(crtc); + + mode = adjusted_mode; + + DBG("%s: set mode: %d:\"%s\" %d %d %d %d %d %d %d %d %d %d 0x%x 0x%x", + omap_crtc->name, mode->base.id, mode->name, + mode->vrefresh, mode->clock, + mode->hdisplay, mode->hsync_start, + mode->hsync_end, mode->htotal, + mode->vdisplay, mode->vsync_start, + mode->vsync_end, mode->vtotal, + mode->type, mode->flags); + + copy_timings_drm_to_omap(&omap_crtc->timings, mode); + omap_crtc->full_update = true; + + return omap_plane_mode_set(omap_crtc->plane, crtc, crtc->fb, + 0, 0, mode->hdisplay, mode->vdisplay, + x << 16, y << 16, + mode->hdisplay << 16, mode->vdisplay << 16, + NULL, NULL); +} + +static void omap_crtc_prepare(struct drm_crtc *crtc) +{ + struct omap_crtc *omap_crtc = to_omap_crtc(crtc); + DBG("%s", omap_crtc->name); + omap_crtc_dpms(crtc, DRM_MODE_DPMS_OFF); +} + +static void omap_crtc_commit(struct drm_crtc *crtc) +{ + struct omap_crtc *omap_crtc = to_omap_crtc(crtc); + DBG("%s", omap_crtc->name); + omap_crtc_dpms(crtc, DRM_MODE_DPMS_ON); +} + +static int omap_crtc_mode_set_base(struct drm_crtc *crtc, int x, int y, + struct drm_framebuffer *old_fb) +{ + struct omap_crtc *omap_crtc = to_omap_crtc(crtc); + struct drm_plane *plane = omap_crtc->plane; + struct drm_display_mode *mode = &crtc->mode; + + return omap_plane_mode_set(plane, crtc, crtc->fb, + 0, 0, mode->hdisplay, mode->vdisplay, + x << 16, y << 16, + mode->hdisplay << 16, mode->vdisplay << 16, + NULL, NULL); +} + +static void omap_crtc_load_lut(struct drm_crtc *crtc) +{ +} + +static void vblank_cb(void *arg) +{ + struct drm_crtc *crtc = arg; + struct drm_device *dev = crtc->dev; + struct omap_crtc *omap_crtc = to_omap_crtc(crtc); + unsigned long flags; + + spin_lock_irqsave(&dev->event_lock, flags); + + /* wakeup userspace */ + if (omap_crtc->event) + drm_send_vblank_event(dev, omap_crtc->pipe, omap_crtc->event); + + omap_crtc->event = NULL; + omap_crtc->old_fb = NULL; + + spin_unlock_irqrestore(&dev->event_lock, flags); +} + +static void page_flip_worker(struct work_struct *work) +{ + struct omap_crtc *omap_crtc = + container_of(work, struct omap_crtc, page_flip_work); + struct drm_crtc *crtc = &omap_crtc->base; + struct drm_display_mode *mode = &crtc->mode; + struct drm_gem_object *bo; + + mutex_lock(&crtc->mutex); + omap_plane_mode_set(omap_crtc->plane, crtc, crtc->fb, + 0, 0, mode->hdisplay, mode->vdisplay, + crtc->x << 16, crtc->y << 16, + mode->hdisplay << 16, mode->vdisplay << 16, + vblank_cb, crtc); + mutex_unlock(&crtc->mutex); + + bo = omap_framebuffer_bo(crtc->fb, 0); + drm_gem_object_unreference_unlocked(bo); +} + +static void page_flip_cb(void *arg) +{ + struct drm_crtc *crtc = arg; + struct omap_crtc *omap_crtc = to_omap_crtc(crtc); + struct omap_drm_private *priv = crtc->dev->dev_private; + + /* avoid assumptions about what ctxt we are called from: */ + queue_work(priv->wq, &omap_crtc->page_flip_work); +} + +static int omap_crtc_page_flip_locked(struct drm_crtc *crtc, + struct drm_framebuffer *fb, + struct drm_pending_vblank_event *event) +{ + struct drm_device *dev = crtc->dev; + struct omap_crtc *omap_crtc = to_omap_crtc(crtc); + struct drm_gem_object *bo; + + DBG("%d -> %d (event=%p)", crtc->fb ? crtc->fb->base.id : -1, + fb->base.id, event); + + if (omap_crtc->old_fb) { + dev_err(dev->dev, "already a pending flip\n"); + return -EINVAL; + } + + omap_crtc->event = event; + crtc->fb = fb; + + /* + * Hold a reference temporarily until the crtc is updated + * and takes the reference to the bo. This avoids it + * getting freed from under us: + */ + bo = omap_framebuffer_bo(fb, 0); + drm_gem_object_reference(bo); + + omap_gem_op_async(bo, OMAP_GEM_READ, page_flip_cb, crtc); + + return 0; +} + +static int omap_crtc_set_property(struct drm_crtc *crtc, + struct drm_property *property, uint64_t val) +{ + struct omap_crtc *omap_crtc = to_omap_crtc(crtc); + struct omap_drm_private *priv = crtc->dev->dev_private; + + if (property == priv->rotation_prop) { + crtc->invert_dimensions = + !!(val & ((1LL << DRM_ROTATE_90) | (1LL << DRM_ROTATE_270))); + } + + return omap_plane_set_property(omap_crtc->plane, property, val); +} + +static const struct drm_crtc_funcs omap_crtc_funcs = { + .set_config = drm_crtc_helper_set_config, + .destroy = omap_crtc_destroy, + .page_flip = omap_crtc_page_flip_locked, + .set_property = omap_crtc_set_property, +}; + +static const struct drm_crtc_helper_funcs omap_crtc_helper_funcs = { + .dpms = omap_crtc_dpms, + .mode_fixup = omap_crtc_mode_fixup, + .mode_set = omap_crtc_mode_set, + .prepare = omap_crtc_prepare, + .commit = omap_crtc_commit, + .mode_set_base = omap_crtc_mode_set_base, + .load_lut = omap_crtc_load_lut, +}; + +const struct omap_video_timings *omap_crtc_timings(struct drm_crtc *crtc) +{ + struct omap_crtc *omap_crtc = to_omap_crtc(crtc); + return &omap_crtc->timings; +} + +enum omap_channel omap_crtc_channel(struct drm_crtc *crtc) +{ + struct omap_crtc *omap_crtc = to_omap_crtc(crtc); + return omap_crtc->channel; +} + +static void omap_crtc_error_irq(struct omap_drm_irq *irq, uint32_t irqstatus) +{ + struct omap_crtc *omap_crtc = + container_of(irq, struct omap_crtc, error_irq); + struct drm_crtc *crtc = &omap_crtc->base; + DRM_ERROR("%s: errors: %08x\n", omap_crtc->name, irqstatus); + /* avoid getting in a flood, unregister the irq until next vblank */ + omap_irq_unregister(crtc->dev, &omap_crtc->error_irq); +} + +static void omap_crtc_apply_irq(struct omap_drm_irq *irq, uint32_t irqstatus) +{ + struct omap_crtc *omap_crtc = + container_of(irq, struct omap_crtc, apply_irq); + struct drm_crtc *crtc = &omap_crtc->base; + + if (!omap_crtc->error_irq.registered) + omap_irq_register(crtc->dev, &omap_crtc->error_irq); + + if (!dispc_mgr_go_busy(omap_crtc->channel)) { + struct omap_drm_private *priv = + crtc->dev->dev_private; + DBG("%s: apply done", omap_crtc->name); + omap_irq_unregister(crtc->dev, &omap_crtc->apply_irq); + queue_work(priv->wq, &omap_crtc->apply_work); + } +} + +static void apply_worker(struct work_struct *work) +{ + struct omap_crtc *omap_crtc = + container_of(work, struct omap_crtc, apply_work); + struct drm_crtc *crtc = &omap_crtc->base; + struct drm_device *dev = crtc->dev; + struct omap_drm_apply *apply, *n; + bool need_apply; + + /* + * Synchronize everything on mode_config.mutex, to keep + * the callbacks and list modification all serialized + * with respect to modesetting ioctls from userspace. + */ + mutex_lock(&crtc->mutex); + dispc_runtime_get(); + + /* + * If we are still pending a previous update, wait.. when the + * pending update completes, we get kicked again. + */ + if (omap_crtc->apply_irq.registered) + goto out; + + /* finish up previous apply's: */ + list_for_each_entry_safe(apply, n, + &omap_crtc->pending_applies, pending_node) { + apply->post_apply(apply); + list_del(&apply->pending_node); + } + + need_apply = !list_empty(&omap_crtc->queued_applies); + + /* then handle the next round of of queued apply's: */ + list_for_each_entry_safe(apply, n, + &omap_crtc->queued_applies, queued_node) { + apply->pre_apply(apply); + list_del(&apply->queued_node); + apply->queued = false; + list_add_tail(&apply->pending_node, + &omap_crtc->pending_applies); + } + + if (need_apply) { + enum omap_channel channel = omap_crtc->channel; + + DBG("%s: GO", omap_crtc->name); + + if (dispc_mgr_is_enabled(channel)) { + omap_irq_register(dev, &omap_crtc->apply_irq); + dispc_mgr_go(channel); + } else { + struct omap_drm_private *priv = dev->dev_private; + queue_work(priv->wq, &omap_crtc->apply_work); + } + } + +out: + dispc_runtime_put(); + mutex_unlock(&crtc->mutex); +} + +int omap_crtc_apply(struct drm_crtc *crtc, + struct omap_drm_apply *apply) +{ + struct omap_crtc *omap_crtc = to_omap_crtc(crtc); + + WARN_ON(!mutex_is_locked(&crtc->mutex)); + + /* no need to queue it again if it is already queued: */ + if (apply->queued) + return 0; + + apply->queued = true; + list_add_tail(&apply->queued_node, &omap_crtc->queued_applies); + + /* + * If there are no currently pending updates, then go ahead and + * kick the worker immediately, otherwise it will run again when + * the current update finishes. + */ + if (list_empty(&omap_crtc->pending_applies)) { + struct omap_drm_private *priv = crtc->dev->dev_private; + queue_work(priv->wq, &omap_crtc->apply_work); + } + + return 0; +} + +/* called only from apply */ +static void set_enabled(struct drm_crtc *crtc, bool enable) +{ + struct drm_device *dev = crtc->dev; + struct omap_crtc *omap_crtc = to_omap_crtc(crtc); + enum omap_channel channel = omap_crtc->channel; + struct omap_irq_wait *wait = NULL; + + if (dispc_mgr_is_enabled(channel) == enable) + return; + + /* ignore sync-lost irqs during enable/disable */ + omap_irq_unregister(crtc->dev, &omap_crtc->error_irq); + + if (dispc_mgr_get_framedone_irq(channel)) { + if (!enable) { + wait = omap_irq_wait_init(dev, + dispc_mgr_get_framedone_irq(channel), 1); + } + } else { + /* + * When we disable digit output, we need to wait until fields + * are done. Otherwise the DSS is still working, and turning + * off the clocks prevents DSS from going to OFF mode. And when + * enabling, we need to wait for the extra sync losts + */ + wait = omap_irq_wait_init(dev, + dispc_mgr_get_vsync_irq(channel), 2); + } + + dispc_mgr_enable(channel, enable); + + if (wait) { + int ret = omap_irq_wait(dev, wait, msecs_to_jiffies(100)); + if (ret) { + dev_err(dev->dev, "%s: timeout waiting for %s\n", + omap_crtc->name, enable ? "enable" : "disable"); + } + } + + omap_irq_register(crtc->dev, &omap_crtc->error_irq); +} + +static void omap_crtc_pre_apply(struct omap_drm_apply *apply) +{ + struct omap_crtc *omap_crtc = + container_of(apply, struct omap_crtc, apply); + struct drm_crtc *crtc = &omap_crtc->base; + struct drm_encoder *encoder = NULL; + + DBG("%s: enabled=%d, full=%d", omap_crtc->name, + omap_crtc->enabled, omap_crtc->full_update); + + if (omap_crtc->full_update) { + struct omap_drm_private *priv = crtc->dev->dev_private; + int i; + for (i = 0; i < priv->num_encoders; i++) { + if (priv->encoders[i]->crtc == crtc) { + encoder = priv->encoders[i]; + break; + } + } + } + + if (!omap_crtc->enabled) { + set_enabled(&omap_crtc->base, false); + if (encoder) + omap_encoder_set_enabled(encoder, false); + } else { + if (encoder) { + omap_encoder_set_enabled(encoder, false); + omap_encoder_update(encoder, &omap_crtc->mgr, + &omap_crtc->timings); + omap_encoder_set_enabled(encoder, true); + omap_crtc->full_update = false; + } + + dispc_mgr_setup(omap_crtc->channel, &omap_crtc->info); + dispc_mgr_set_timings(omap_crtc->channel, + &omap_crtc->timings); + set_enabled(&omap_crtc->base, true); + } + + omap_crtc->full_update = false; +} + +static void omap_crtc_post_apply(struct omap_drm_apply *apply) +{ + /* nothing needed for post-apply */ +} + +static const char *channel_names[] = { + [OMAP_DSS_CHANNEL_LCD] = "lcd", + [OMAP_DSS_CHANNEL_DIGIT] = "tv", + [OMAP_DSS_CHANNEL_LCD2] = "lcd2", +}; + +/* initialize crtc */ +struct drm_crtc *omap_crtc_init(struct drm_device *dev, + struct drm_plane *plane, enum omap_channel channel, int id) +{ + struct drm_crtc *crtc = NULL; + struct omap_crtc *omap_crtc; + struct omap_overlay_manager_info *info; + + DBG("%s", channel_names[channel]); + + omap_crtc = kzalloc(sizeof(*omap_crtc), GFP_KERNEL); + + if (!omap_crtc) { + dev_err(dev->dev, "could not allocate CRTC\n"); + goto fail; + } + + crtc = &omap_crtc->base; + + INIT_WORK(&omap_crtc->page_flip_work, page_flip_worker); + INIT_WORK(&omap_crtc->apply_work, apply_worker); + + INIT_LIST_HEAD(&omap_crtc->pending_applies); + INIT_LIST_HEAD(&omap_crtc->queued_applies); + + omap_crtc->apply.pre_apply = omap_crtc_pre_apply; + omap_crtc->apply.post_apply = omap_crtc_post_apply; + + omap_crtc->apply_irq.irqmask = pipe2vbl(id); + omap_crtc->apply_irq.irq = omap_crtc_apply_irq; + + omap_crtc->error_irq.irqmask = + dispc_mgr_get_sync_lost_irq(channel); + omap_crtc->error_irq.irq = omap_crtc_error_irq; + omap_irq_register(dev, &omap_crtc->error_irq); + + omap_crtc->channel = channel; + omap_crtc->plane = plane; + omap_crtc->plane->crtc = crtc; + omap_crtc->name = channel_names[channel]; + omap_crtc->pipe = id; + + /* temporary: */ + omap_crtc->mgr.id = channel; + + dss_install_mgr_ops(&mgr_ops); + + /* TODO: fix hard-coded setup.. add properties! */ + info = &omap_crtc->info; + info->default_color = 0x00000000; + info->trans_key = 0x00000000; + info->trans_key_type = OMAP_DSS_COLOR_KEY_GFX_DST; + info->trans_enabled = false; + + drm_crtc_init(dev, crtc, &omap_crtc_funcs); + drm_crtc_helper_add(crtc, &omap_crtc_helper_funcs); + + omap_plane_install_properties(omap_crtc->plane, &crtc->base); + + return crtc; + +fail: + if (crtc) + omap_crtc_destroy(crtc); + + return NULL; +} diff --git a/drivers/staging/omapdrm/omap_debugfs.c b/drivers/gpu/drm/omapdrm/omap_debugfs.c index 2f122e00b51d..c27f59da7f29 100644 --- a/drivers/staging/omapdrm/omap_debugfs.c +++ b/drivers/gpu/drm/omapdrm/omap_debugfs.c @@ -1,5 +1,5 @@ /* - * drivers/staging/omapdrm/omap_debugfs.c + * drivers/gpu/drm/omapdrm/omap_debugfs.c * * Copyright (C) 2011 Texas Instruments * Author: Rob Clark <rob.clark@linaro.org> @@ -57,21 +57,11 @@ static int fb_show(struct seq_file *m, void *arg) struct drm_device *dev = node->minor->dev; struct omap_drm_private *priv = dev->dev_private; struct drm_framebuffer *fb; - int ret; - - ret = mutex_lock_interruptible(&dev->mode_config.mutex); - if (ret) - return ret; - - ret = mutex_lock_interruptible(&dev->struct_mutex); - if (ret) { - mutex_unlock(&dev->mode_config.mutex); - return ret; - } seq_printf(m, "fbcon "); omap_framebuffer_describe(priv->fbdev->fb, m); + mutex_lock(&dev->mode_config.fb_lock); list_for_each_entry(fb, &dev->mode_config.fb_list, head) { if (fb == priv->fbdev->fb) continue; @@ -79,9 +69,7 @@ static int fb_show(struct seq_file *m, void *arg) seq_printf(m, "user "); omap_framebuffer_describe(fb, m); } - - mutex_unlock(&dev->struct_mutex); - mutex_unlock(&dev->mode_config.mutex); + mutex_unlock(&dev->mode_config.fb_lock); return 0; } diff --git a/drivers/staging/omapdrm/omap_dmm_priv.h b/drivers/gpu/drm/omapdrm/omap_dmm_priv.h index 273ec12c028a..58bcd6ae0255 100644 --- a/drivers/staging/omapdrm/omap_dmm_priv.h +++ b/drivers/gpu/drm/omapdrm/omap_dmm_priv.h @@ -118,6 +118,11 @@ struct pat { #define DESCR_SIZE 128 #define REFILL_BUFFER_SIZE ((4 * 128 * 256) + (3 * DESCR_SIZE)) +/* For OMAP5, a fixed offset is added to all Y coordinates for 1D buffers. + * This is used in programming to address the upper portion of the LUT +*/ +#define OMAP5_LUT_OFFSET 128 + struct dmm; struct dmm_txn { diff --git a/drivers/staging/omapdrm/omap_dmm_tiler.c b/drivers/gpu/drm/omapdrm/omap_dmm_tiler.c index 59bf43899fc0..391021537105 100644 --- a/drivers/staging/omapdrm/omap_dmm_tiler.c +++ b/drivers/gpu/drm/omapdrm/omap_dmm_tiler.c @@ -213,6 +213,11 @@ static void dmm_txn_append(struct dmm_txn *txn, struct pat_area *area, txn->last_pat->next_pa = (uint32_t)pat_pa; pat->area = *area; + + /* adjust Y coordinates based off of container parameters */ + pat->area.y0 += engine->tcm->y_offset; + pat->area.y1 += engine->tcm->y_offset; + pat->ctrl = (struct pat_ctrl){ .start = 1, .lut_id = engine->tcm->lut_id, @@ -622,6 +627,11 @@ static int omap_dmm_probe(struct platform_device *dev) omap_dmm->lut_width = ((pat_geom >> 16) & 0xF) << 5; omap_dmm->lut_height = ((pat_geom >> 24) & 0xF) << 5; + /* increment LUT by one if on OMAP5 */ + /* LUT has twice the height, and is split into a separate container */ + if (omap_dmm->lut_height != omap_dmm->container_height) + omap_dmm->num_lut++; + /* initialize DMM registers */ writel(0x88888888, omap_dmm->base + DMM_PAT_VIEW__0); writel(0x88888888, omap_dmm->base + DMM_PAT_VIEW__1); @@ -701,6 +711,9 @@ static int omap_dmm_probe(struct platform_device *dev) } /* init containers */ + /* Each LUT is associated with a TCM (container manager). We use the + lut_id to denote the lut_id used to identify the correct LUT for + programming during reill operations */ for (i = 0; i < omap_dmm->num_lut; i++) { omap_dmm->tcm[i] = sita_init(omap_dmm->container_width, omap_dmm->container_height, @@ -717,13 +730,23 @@ static int omap_dmm_probe(struct platform_device *dev) /* assign access mode containers to applicable tcm container */ /* OMAP 4 has 1 container for all 4 views */ + /* OMAP 5 has 2 containers, 1 for 2D and 1 for 1D */ containers[TILFMT_8BIT] = omap_dmm->tcm[0]; containers[TILFMT_16BIT] = omap_dmm->tcm[0]; containers[TILFMT_32BIT] = omap_dmm->tcm[0]; - containers[TILFMT_PAGE] = omap_dmm->tcm[0]; + + if (omap_dmm->container_height != omap_dmm->lut_height) { + /* second LUT is used for PAGE mode. Programming must use + y offset that is added to all y coordinates. LUT id is still + 0, because it is the same LUT, just the upper 128 lines */ + containers[TILFMT_PAGE] = omap_dmm->tcm[1]; + omap_dmm->tcm[1]->y_offset = OMAP5_LUT_OFFSET; + omap_dmm->tcm[1]->lut_id = 0; + } else { + containers[TILFMT_PAGE] = omap_dmm->tcm[0]; + } area = (struct tcm_area) { - .is2d = true, .tcm = NULL, .p1.x = omap_dmm->container_width - 1, .p1.y = omap_dmm->container_height - 1, @@ -835,64 +858,81 @@ int tiler_map_show(struct seq_file *s, void *arg) int h_adj; int w_adj; unsigned long flags; + int lut_idx; + if (!omap_dmm) { /* early return if dmm/tiler device is not initialized */ return 0; } - h_adj = omap_dmm->lut_height / ydiv; - w_adj = omap_dmm->lut_width / xdiv; + h_adj = omap_dmm->container_height / ydiv; + w_adj = omap_dmm->container_width / xdiv; - map = kzalloc(h_adj * sizeof(*map), GFP_KERNEL); - global_map = kzalloc((w_adj + 1) * h_adj, GFP_KERNEL); + map = kmalloc(h_adj * sizeof(*map), GFP_KERNEL); + global_map = kmalloc((w_adj + 1) * h_adj, GFP_KERNEL); if (!map || !global_map) goto error; - memset(global_map, ' ', (w_adj + 1) * h_adj); - for (i = 0; i < omap_dmm->lut_height; i++) { - map[i] = global_map + i * (w_adj + 1); - map[i][w_adj] = 0; - } - spin_lock_irqsave(&list_lock, flags); + for (lut_idx = 0; lut_idx < omap_dmm->num_lut; lut_idx++) { + memset(map, 0, sizeof(h_adj * sizeof(*map))); + memset(global_map, ' ', (w_adj + 1) * h_adj); - list_for_each_entry(block, &omap_dmm->alloc_head, alloc_node) { - if (block->fmt != TILFMT_PAGE) { - fill_map(map, xdiv, ydiv, &block->area, *m2dp, true); - if (!*++a2dp) - a2dp = a2d; - if (!*++m2dp) - m2dp = m2d; - map_2d_info(map, xdiv, ydiv, nice, &block->area); - } else { - bool start = read_map_pt(map, xdiv, ydiv, - &block->area.p0) - == ' '; - bool end = read_map_pt(map, xdiv, ydiv, &block->area.p1) - == ' '; - tcm_for_each_slice(a, block->area, p) - fill_map(map, xdiv, ydiv, &a, '=', true); - fill_map_pt(map, xdiv, ydiv, &block->area.p0, + for (i = 0; i < omap_dmm->container_height; i++) { + map[i] = global_map + i * (w_adj + 1); + map[i][w_adj] = 0; + } + + spin_lock_irqsave(&list_lock, flags); + + list_for_each_entry(block, &omap_dmm->alloc_head, alloc_node) { + if (block->area.tcm == omap_dmm->tcm[lut_idx]) { + if (block->fmt != TILFMT_PAGE) { + fill_map(map, xdiv, ydiv, &block->area, + *m2dp, true); + if (!*++a2dp) + a2dp = a2d; + if (!*++m2dp) + m2dp = m2d; + map_2d_info(map, xdiv, ydiv, nice, + &block->area); + } else { + bool start = read_map_pt(map, xdiv, + ydiv, &block->area.p0) == ' '; + bool end = read_map_pt(map, xdiv, ydiv, + &block->area.p1) == ' '; + + tcm_for_each_slice(a, block->area, p) + fill_map(map, xdiv, ydiv, &a, + '=', true); + fill_map_pt(map, xdiv, ydiv, + &block->area.p0, start ? '<' : 'X'); - fill_map_pt(map, xdiv, ydiv, &block->area.p1, + fill_map_pt(map, xdiv, ydiv, + &block->area.p1, end ? '>' : 'X'); - map_1d_info(map, xdiv, ydiv, nice, &block->area); + map_1d_info(map, xdiv, ydiv, nice, + &block->area); + } + } } - } - spin_unlock_irqrestore(&list_lock, flags); + spin_unlock_irqrestore(&list_lock, flags); - if (s) { - seq_printf(s, "BEGIN DMM TILER MAP\n"); - for (i = 0; i < 128; i++) - seq_printf(s, "%03d:%s\n", i, map[i]); - seq_printf(s, "END TILER MAP\n"); - } else { - dev_dbg(omap_dmm->dev, "BEGIN DMM TILER MAP\n"); - for (i = 0; i < 128; i++) - dev_dbg(omap_dmm->dev, "%03d:%s\n", i, map[i]); - dev_dbg(omap_dmm->dev, "END TILER MAP\n"); + if (s) { + seq_printf(s, "CONTAINER %d DUMP BEGIN\n", lut_idx); + for (i = 0; i < 128; i++) + seq_printf(s, "%03d:%s\n", i, map[i]); + seq_printf(s, "CONTAINER %d DUMP END\n", lut_idx); + } else { + dev_dbg(omap_dmm->dev, "CONTAINER %d DUMP BEGIN\n", + lut_idx); + for (i = 0; i < 128; i++) + dev_dbg(omap_dmm->dev, "%03d:%s\n", i, map[i]); + dev_dbg(omap_dmm->dev, "CONTAINER %d DUMP END\n", + lut_idx); + } } error: @@ -903,12 +943,45 @@ error: } #endif +#ifdef CONFIG_PM +static int omap_dmm_resume(struct device *dev) +{ + struct tcm_area area; + int i; + + if (!omap_dmm) + return -ENODEV; + + area = (struct tcm_area) { + .tcm = NULL, + .p1.x = omap_dmm->container_width - 1, + .p1.y = omap_dmm->container_height - 1, + }; + + /* initialize all LUTs to dummy page entries */ + for (i = 0; i < omap_dmm->num_lut; i++) { + area.tcm = omap_dmm->tcm[i]; + if (fill(&area, NULL, 0, 0, true)) + dev_err(dev, "refill failed"); + } + + return 0; +} + +static const struct dev_pm_ops omap_dmm_pm_ops = { + .resume = omap_dmm_resume, +}; +#endif + struct platform_driver omap_dmm_driver = { .probe = omap_dmm_probe, .remove = omap_dmm_remove, .driver = { .owner = THIS_MODULE, .name = DMM_DRIVER_NAME, +#ifdef CONFIG_PM + .pm = &omap_dmm_pm_ops, +#endif }, }; diff --git a/drivers/staging/omapdrm/omap_dmm_tiler.h b/drivers/gpu/drm/omapdrm/omap_dmm_tiler.h index 4fdd61e54bd2..4fdd61e54bd2 100644 --- a/drivers/staging/omapdrm/omap_dmm_tiler.h +++ b/drivers/gpu/drm/omapdrm/omap_dmm_tiler.h diff --git a/drivers/staging/omapdrm/omap_drv.c b/drivers/gpu/drm/omapdrm/omap_drv.c index 84943e5ba1d6..9083538bd16a 100644 --- a/drivers/staging/omapdrm/omap_drv.c +++ b/drivers/gpu/drm/omapdrm/omap_drv.c @@ -1,5 +1,5 @@ /* - * drivers/staging/omapdrm/omap_drv.c + * drivers/gpu/drm/omapdrm/omap_drv.c * * Copyright (C) 2011 Texas Instruments * Author: Rob Clark <rob@ti.com> @@ -74,320 +74,99 @@ static int get_connector_type(struct omap_dss_device *dssdev) } } -#if 0 /* enable when dss2 supports hotplug */ -static int omap_drm_notifier(struct notifier_block *nb, - unsigned long evt, void *arg) -{ - switch (evt) { - case OMAP_DSS_SIZE_CHANGE: - case OMAP_DSS_HOTPLUG_CONNECT: - case OMAP_DSS_HOTPLUG_DISCONNECT: { - struct drm_device *dev = drm_device; - DBG("hotplug event: evt=%d, dev=%p", evt, dev); - if (dev) - drm_sysfs_hotplug_event(dev); - - return NOTIFY_OK; - } - default: /* don't care about other events for now */ - return NOTIFY_DONE; - } -} -#endif - -static void dump_video_chains(void) -{ - int i; - - DBG("dumping video chains: "); - for (i = 0; i < omap_dss_get_num_overlays(); i++) { - struct omap_overlay *ovl = omap_dss_get_overlay(i); - struct omap_overlay_manager *mgr = ovl->manager; - struct omap_dss_device *dssdev = mgr ? - mgr->get_device(mgr) : NULL; - if (dssdev) { - DBG("%d: %s -> %s -> %s", i, ovl->name, mgr->name, - dssdev->name); - } else if (mgr) { - DBG("%d: %s -> %s", i, ovl->name, mgr->name); - } else { - DBG("%d: %s", i, ovl->name); - } - } -} - -/* create encoders for each manager */ -static int create_encoder(struct drm_device *dev, - struct omap_overlay_manager *mgr) -{ - struct omap_drm_private *priv = dev->dev_private; - struct drm_encoder *encoder = omap_encoder_init(dev, mgr); - - if (!encoder) { - dev_err(dev->dev, "could not create encoder: %s\n", - mgr->name); - return -ENOMEM; - } - - BUG_ON(priv->num_encoders >= ARRAY_SIZE(priv->encoders)); - - priv->encoders[priv->num_encoders++] = encoder; - - return 0; -} - -/* create connectors for each display device */ -static int create_connector(struct drm_device *dev, - struct omap_dss_device *dssdev) +static int omap_modeset_init(struct drm_device *dev) { struct omap_drm_private *priv = dev->dev_private; - static struct notifier_block *notifier; - struct drm_connector *connector; - int j; - - if (!dssdev->driver) { - dev_warn(dev->dev, "%s has no driver.. skipping it\n", - dssdev->name); - return 0; - } - - if (!(dssdev->driver->get_timings || - dssdev->driver->read_edid)) { - dev_warn(dev->dev, "%s driver does not support " - "get_timings or read_edid.. skipping it!\n", - dssdev->name); - return 0; - } + struct omap_dss_device *dssdev = NULL; + int num_ovls = dss_feat_get_num_ovls(); + int id; - connector = omap_connector_init(dev, - get_connector_type(dssdev), dssdev); + drm_mode_config_init(dev); - if (!connector) { - dev_err(dev->dev, "could not create connector: %s\n", - dssdev->name); - return -ENOMEM; - } + omap_drm_irq_install(dev); - BUG_ON(priv->num_connectors >= ARRAY_SIZE(priv->connectors)); + /* + * Create private planes and CRTCs for the last NUM_CRTCs overlay + * plus manager: + */ + for (id = 0; id < min(num_crtc, num_ovls); id++) { + struct drm_plane *plane; + struct drm_crtc *crtc; - priv->connectors[priv->num_connectors++] = connector; + plane = omap_plane_init(dev, id, true); + crtc = omap_crtc_init(dev, plane, pipe2chan(id), id); -#if 0 /* enable when dss2 supports hotplug */ - notifier = kzalloc(sizeof(struct notifier_block), GFP_KERNEL); - notifier->notifier_call = omap_drm_notifier; - omap_dss_add_notify(dssdev, notifier); -#else - notifier = NULL; -#endif + BUG_ON(priv->num_crtcs >= ARRAY_SIZE(priv->crtcs)); + priv->crtcs[id] = crtc; + priv->num_crtcs++; - for (j = 0; j < priv->num_encoders; j++) { - struct omap_overlay_manager *mgr = - omap_encoder_get_manager(priv->encoders[j]); - if (mgr->get_device(mgr) == dssdev) { - drm_mode_connector_attach_encoder(connector, - priv->encoders[j]); - } + priv->planes[id] = plane; + priv->num_planes++; } - return 0; -} - -/* create up to max_overlays CRTCs mapping to overlays.. by default, - * connect the overlays to different managers/encoders, giving priority - * to encoders connected to connectors with a detected connection - */ -static int create_crtc(struct drm_device *dev, struct omap_overlay *ovl, - int *j, unsigned int connected_connectors) -{ - struct omap_drm_private *priv = dev->dev_private; - struct omap_overlay_manager *mgr = NULL; - struct drm_crtc *crtc; - - /* find next best connector, ones with detected connection first + /* + * Create normal planes for the remaining overlays: */ - while (*j < priv->num_connectors && !mgr) { - if (connected_connectors & (1 << *j)) { - struct drm_encoder *encoder = - omap_connector_attached_encoder( - priv->connectors[*j]); - if (encoder) - mgr = omap_encoder_get_manager(encoder); + for (; id < num_ovls; id++) { + struct drm_plane *plane = omap_plane_init(dev, id, false); - } - (*j)++; + BUG_ON(priv->num_planes >= ARRAY_SIZE(priv->planes)); + priv->planes[priv->num_planes++] = plane; } - /* if we couldn't find another connected connector, lets start - * looking at the unconnected connectors: - * - * note: it might not be immediately apparent, but thanks to - * the !mgr check in both this loop and the one above, the only - * way to enter this loop is with *j == priv->num_connectors, - * so idx can never go negative. - */ - while (*j < 2 * priv->num_connectors && !mgr) { - int idx = *j - priv->num_connectors; - if (!(connected_connectors & (1 << idx))) { - struct drm_encoder *encoder = - omap_connector_attached_encoder( - priv->connectors[idx]); - if (encoder) - mgr = omap_encoder_get_manager(encoder); + for_each_dss_dev(dssdev) { + struct drm_connector *connector; + struct drm_encoder *encoder; + if (!dssdev->driver) { + dev_warn(dev->dev, "%s has no driver.. skipping it\n", + dssdev->name); + return 0; } - (*j)++; - } - - crtc = omap_crtc_init(dev, ovl, priv->num_crtcs); - - if (!crtc) { - dev_err(dev->dev, "could not create CRTC: %s\n", - ovl->name); - return -ENOMEM; - } - - BUG_ON(priv->num_crtcs >= ARRAY_SIZE(priv->crtcs)); - - priv->crtcs[priv->num_crtcs++] = crtc; - - return 0; -} - -static int create_plane(struct drm_device *dev, struct omap_overlay *ovl, - unsigned int possible_crtcs) -{ - struct omap_drm_private *priv = dev->dev_private; - struct drm_plane *plane = - omap_plane_init(dev, ovl, possible_crtcs, false); - - if (!plane) { - dev_err(dev->dev, "could not create plane: %s\n", - ovl->name); - return -ENOMEM; - } - - BUG_ON(priv->num_planes >= ARRAY_SIZE(priv->planes)); - - priv->planes[priv->num_planes++] = plane; - - return 0; -} -static int match_dev_name(struct omap_dss_device *dssdev, void *data) -{ - return !strcmp(dssdev->name, data); -} - -static unsigned int detect_connectors(struct drm_device *dev) -{ - struct omap_drm_private *priv = dev->dev_private; - unsigned int connected_connectors = 0; - int i; - - for (i = 0; i < priv->num_connectors; i++) { - struct drm_connector *connector = priv->connectors[i]; - if (omap_connector_detect(connector, true) == - connector_status_connected) { - connected_connectors |= (1 << i); + if (!(dssdev->driver->get_timings || + dssdev->driver->read_edid)) { + dev_warn(dev->dev, "%s driver does not support " + "get_timings or read_edid.. skipping it!\n", + dssdev->name); + return 0; } - } - return connected_connectors; -} - -static int omap_modeset_init(struct drm_device *dev) -{ - const struct omap_drm_platform_data *pdata = dev->dev->platform_data; - struct omap_kms_platform_data *kms_pdata = NULL; - struct omap_drm_private *priv = dev->dev_private; - struct omap_dss_device *dssdev = NULL; - int i, j; - unsigned int connected_connectors = 0; - - drm_mode_config_init(dev); + encoder = omap_encoder_init(dev, dssdev); - if (pdata && pdata->kms_pdata) { - kms_pdata = pdata->kms_pdata; - - /* if platform data is provided by the board file, use it to - * control which overlays, managers, and devices we own. - */ - for (i = 0; i < kms_pdata->mgr_cnt; i++) { - struct omap_overlay_manager *mgr = - omap_dss_get_overlay_manager( - kms_pdata->mgr_ids[i]); - create_encoder(dev, mgr); + if (!encoder) { + dev_err(dev->dev, "could not create encoder: %s\n", + dssdev->name); + return -ENOMEM; } - for (i = 0; i < kms_pdata->dev_cnt; i++) { - struct omap_dss_device *dssdev = - omap_dss_find_device( - (void *)kms_pdata->dev_names[i], - match_dev_name); - if (!dssdev) { - dev_warn(dev->dev, "no such dssdev: %s\n", - kms_pdata->dev_names[i]); - continue; - } - create_connector(dev, dssdev); - } - - connected_connectors = detect_connectors(dev); + connector = omap_connector_init(dev, + get_connector_type(dssdev), dssdev, encoder); - j = 0; - for (i = 0; i < kms_pdata->ovl_cnt; i++) { - struct omap_overlay *ovl = - omap_dss_get_overlay(kms_pdata->ovl_ids[i]); - create_crtc(dev, ovl, &j, connected_connectors); + if (!connector) { + dev_err(dev->dev, "could not create connector: %s\n", + dssdev->name); + return -ENOMEM; } - for (i = 0; i < kms_pdata->pln_cnt; i++) { - struct omap_overlay *ovl = - omap_dss_get_overlay(kms_pdata->pln_ids[i]); - create_plane(dev, ovl, (1 << priv->num_crtcs) - 1); - } - } else { - /* otherwise just grab up to CONFIG_DRM_OMAP_NUM_CRTCS and try - * to make educated guesses about everything else - */ - int max_overlays = min(omap_dss_get_num_overlays(), num_crtc); + BUG_ON(priv->num_encoders >= ARRAY_SIZE(priv->encoders)); + BUG_ON(priv->num_connectors >= ARRAY_SIZE(priv->connectors)); - for (i = 0; i < omap_dss_get_num_overlay_managers(); i++) - create_encoder(dev, omap_dss_get_overlay_manager(i)); + priv->encoders[priv->num_encoders++] = encoder; + priv->connectors[priv->num_connectors++] = connector; - for_each_dss_dev(dssdev) { - create_connector(dev, dssdev); - } + drm_mode_connector_attach_encoder(connector, encoder); - connected_connectors = detect_connectors(dev); - - j = 0; - for (i = 0; i < max_overlays; i++) { - create_crtc(dev, omap_dss_get_overlay(i), - &j, connected_connectors); - } - - /* use any remaining overlays as drm planes */ - for (; i < omap_dss_get_num_overlays(); i++) { - struct omap_overlay *ovl = omap_dss_get_overlay(i); - create_plane(dev, ovl, (1 << priv->num_crtcs) - 1); + /* figure out which crtc's we can connect the encoder to: */ + encoder->possible_crtcs = 0; + for (id = 0; id < priv->num_crtcs; id++) { + enum omap_dss_output_id supported_outputs = + dss_feat_get_supported_outputs(pipe2chan(id)); + if (supported_outputs & dssdev->output->id) + encoder->possible_crtcs |= (1 << id); } } - /* for now keep the mapping of CRTCs and encoders static.. */ - for (i = 0; i < priv->num_encoders; i++) { - struct drm_encoder *encoder = priv->encoders[i]; - struct omap_overlay_manager *mgr = - omap_encoder_get_manager(encoder); - - encoder->possible_crtcs = (1 << priv->num_crtcs) - 1; - - DBG("%s: possible_crtcs=%08x", mgr->name, - encoder->possible_crtcs); - } - - dump_video_chains(); - dev->mode_config.min_width = 32; dev->mode_config.min_height = 32; @@ -450,7 +229,7 @@ static int ioctl_gem_new(struct drm_device *dev, void *data, struct drm_file *file_priv) { struct drm_omap_gem_new *args = data; - DBG("%p:%p: size=0x%08x, flags=%08x", dev, file_priv, + VERB("%p:%p: size=0x%08x, flags=%08x", dev, file_priv, args->size.bytes, args->flags); return omap_gem_new_handle(dev, file_priv, args->size, args->flags, &args->handle); @@ -510,7 +289,7 @@ static int ioctl_gem_info(struct drm_device *dev, void *data, struct drm_gem_object *obj; int ret = 0; - DBG("%p:%p: handle=%d", dev, file_priv, args->handle); + VERB("%p:%p: handle=%d", dev, file_priv, args->handle); obj = drm_gem_object_lookup(dev, file_priv, args->handle); if (!obj) @@ -565,14 +344,6 @@ static int dev_load(struct drm_device *dev, unsigned long flags) dev->dev_private = priv; - ret = omapdss_compat_init(); - if (ret) { - dev_err(dev->dev, "coult not init omapdss\n"); - dev->dev_private = NULL; - kfree(priv); - return ret; - } - priv->wq = alloc_ordered_workqueue("omapdrm", 0); INIT_LIST_HEAD(&priv->obj_list); @@ -584,21 +355,23 @@ static int dev_load(struct drm_device *dev, unsigned long flags) dev_err(dev->dev, "omap_modeset_init failed: ret=%d\n", ret); dev->dev_private = NULL; kfree(priv); - omapdss_compat_uninit(); return ret; } + ret = drm_vblank_init(dev, priv->num_crtcs); + if (ret) + dev_warn(dev->dev, "could not init vblank\n"); + priv->fbdev = omap_fbdev_init(dev); if (!priv->fbdev) { dev_warn(dev->dev, "omap_fbdev_init failed\n"); /* well, limp along without an fbdev.. maybe X11 will work? */ } - drm_kms_helper_poll_init(dev); + /* store off drm_device for use in pm ops */ + dev_set_drvdata(dev->dev, dev); - ret = drm_vblank_init(dev, priv->num_crtcs); - if (ret) - dev_warn(dev->dev, "could not init vblank\n"); + drm_kms_helper_poll_init(dev); return 0; } @@ -609,8 +382,9 @@ static int dev_unload(struct drm_device *dev) DBG("unload: dev=%p", dev); - drm_vblank_cleanup(dev); drm_kms_helper_poll_fini(dev); + drm_vblank_cleanup(dev); + omap_drm_irq_uninstall(dev); omap_fbdev_free(dev); omap_modeset_free(dev); @@ -619,11 +393,11 @@ static int dev_unload(struct drm_device *dev) flush_workqueue(priv->wq); destroy_workqueue(priv->wq); - omapdss_compat_uninit(); - kfree(dev->dev_private); dev->dev_private = NULL; + dev_set_drvdata(dev->dev, NULL); + return 0; } @@ -680,7 +454,9 @@ static void dev_lastclose(struct drm_device *dev) } } + drm_modeset_lock_all(dev); ret = drm_fb_helper_restore_fbdev_mode(priv->fbdev); + drm_modeset_unlock_all(dev); if (ret) DBG("failed to restore crtc mode"); } @@ -695,60 +471,6 @@ static void dev_postclose(struct drm_device *dev, struct drm_file *file) DBG("postclose: dev=%p, file=%p", dev, file); } -/** - * enable_vblank - enable vblank interrupt events - * @dev: DRM device - * @crtc: which irq to enable - * - * Enable vblank interrupts for @crtc. If the device doesn't have - * a hardware vblank counter, this routine should be a no-op, since - * interrupts will have to stay on to keep the count accurate. - * - * RETURNS - * Zero on success, appropriate errno if the given @crtc's vblank - * interrupt cannot be enabled. - */ -static int dev_enable_vblank(struct drm_device *dev, int crtc) -{ - DBG("enable_vblank: dev=%p, crtc=%d", dev, crtc); - return 0; -} - -/** - * disable_vblank - disable vblank interrupt events - * @dev: DRM device - * @crtc: which irq to enable - * - * Disable vblank interrupts for @crtc. If the device doesn't have - * a hardware vblank counter, this routine should be a no-op, since - * interrupts will have to stay on to keep the count accurate. - */ -static void dev_disable_vblank(struct drm_device *dev, int crtc) -{ - DBG("disable_vblank: dev=%p, crtc=%d", dev, crtc); -} - -static irqreturn_t dev_irq_handler(DRM_IRQ_ARGS) -{ - return IRQ_HANDLED; -} - -static void dev_irq_preinstall(struct drm_device *dev) -{ - DBG("irq_preinstall: dev=%p", dev); -} - -static int dev_irq_postinstall(struct drm_device *dev) -{ - DBG("irq_postinstall: dev=%p", dev); - return 0; -} - -static void dev_irq_uninstall(struct drm_device *dev) -{ - DBG("irq_uninstall: dev=%p", dev); -} - static const struct vm_operations_struct omap_gem_vm_ops = { .fault = omap_gem_fault, .open = drm_gem_vm_open, @@ -778,12 +500,12 @@ static struct drm_driver omap_drm_driver = { .preclose = dev_preclose, .postclose = dev_postclose, .get_vblank_counter = drm_vblank_count, - .enable_vblank = dev_enable_vblank, - .disable_vblank = dev_disable_vblank, - .irq_preinstall = dev_irq_preinstall, - .irq_postinstall = dev_irq_postinstall, - .irq_uninstall = dev_irq_uninstall, - .irq_handler = dev_irq_handler, + .enable_vblank = omap_irq_enable_vblank, + .disable_vblank = omap_irq_disable_vblank, + .irq_preinstall = omap_irq_preinstall, + .irq_postinstall = omap_irq_postinstall, + .irq_uninstall = omap_irq_uninstall, + .irq_handler = omap_irq_handler, #ifdef CONFIG_DEBUG_FS .debugfs_init = omap_debugfs_init, .debugfs_cleanup = omap_debugfs_cleanup, @@ -841,10 +563,19 @@ static int pdev_remove(struct platform_device *device) return 0; } +#ifdef CONFIG_PM +static const struct dev_pm_ops omapdrm_pm_ops = { + .resume = omap_gem_resume, +}; +#endif + struct platform_driver pdev = { .driver = { .name = DRIVER_NAME, .owner = THIS_MODULE, +#ifdef CONFIG_PM + .pm = &omapdrm_pm_ops, +#endif }, .probe = pdev_probe, .remove = pdev_remove, diff --git a/drivers/staging/omapdrm/omap_drv.h b/drivers/gpu/drm/omapdrm/omap_drv.h index 1d4aea53b75d..d4f997bb4ac0 100644 --- a/drivers/staging/omapdrm/omap_drv.h +++ b/drivers/gpu/drm/omapdrm/omap_drv.h @@ -1,5 +1,5 @@ /* - * drivers/staging/omapdrm/omap_drv.h + * drivers/gpu/drm/omapdrm/omap_drv.h * * Copyright (C) 2011 Texas Instruments * Author: Rob Clark <rob@ti.com> @@ -25,8 +25,9 @@ #include <linux/types.h> #include <drm/drmP.h> #include <drm/drm_crtc_helper.h> +#include <drm/omap_drm.h> #include <linux/platform_data/omap_drm.h> -#include "omap_drm.h" + #define DBG(fmt, ...) DRM_DEBUG(fmt"\n", ##__VA_ARGS__) #define VERB(fmt, ...) if (0) DRM_DEBUG(fmt, ##__VA_ARGS__) /* verbose debug */ @@ -39,6 +40,51 @@ */ #define MAX_MAPPERS 2 +/* parameters which describe (unrotated) coordinates of scanout within a fb: */ +struct omap_drm_window { + uint32_t rotation; + int32_t crtc_x, crtc_y; /* signed because can be offscreen */ + uint32_t crtc_w, crtc_h; + uint32_t src_x, src_y; + uint32_t src_w, src_h; +}; + +/* Once GO bit is set, we can't make further updates to shadowed registers + * until the GO bit is cleared. So various parts in the kms code that need + * to update shadowed registers queue up a pair of callbacks, pre_apply + * which is called before setting GO bit, and post_apply that is called + * after GO bit is cleared. The crtc manages the queuing, and everyone + * else goes thru omap_crtc_apply() using these callbacks so that the + * code which has to deal w/ GO bit state is centralized. + */ +struct omap_drm_apply { + struct list_head pending_node, queued_node; + bool queued; + void (*pre_apply)(struct omap_drm_apply *apply); + void (*post_apply)(struct omap_drm_apply *apply); +}; + +/* For transiently registering for different DSS irqs that various parts + * of the KMS code need during setup/configuration. We these are not + * necessarily the same as what drm_vblank_get/put() are requesting, and + * the hysteresis in drm_vblank_put() is not necessarily desirable for + * internal housekeeping related irq usage. + */ +struct omap_drm_irq { + struct list_head node; + uint32_t irqmask; + bool registered; + void (*irq)(struct omap_drm_irq *irq, uint32_t irqstatus); +}; + +/* For KMS code that needs to wait for a certain # of IRQs: + */ +struct omap_irq_wait; +struct omap_irq_wait * omap_irq_wait_init(struct drm_device *dev, + uint32_t irqmask, int count); +int omap_irq_wait(struct drm_device *dev, struct omap_irq_wait *wait, + unsigned long timeout); + struct omap_drm_private { uint32_t omaprev; @@ -58,6 +104,7 @@ struct omap_drm_private { struct workqueue_struct *wq; + /* list of GEM objects: */ struct list_head obj_list; bool has_dmm; @@ -65,6 +112,11 @@ struct omap_drm_private { /* properties: */ struct drm_property *rotation_prop; struct drm_property *zorder_prop; + + /* irq handling: */ + struct list_head irq_list; /* list of omap_drm_irq */ + uint32_t vblank_mask; /* irq bits set for userspace vblank */ + struct omap_drm_irq error_handler; }; /* this should probably be in drm-core to standardize amongst drivers */ @@ -75,15 +127,6 @@ struct omap_drm_private { #define DRM_REFLECT_X 4 #define DRM_REFLECT_Y 5 -/* parameters which describe (unrotated) coordinates of scanout within a fb: */ -struct omap_drm_window { - uint32_t rotation; - int32_t crtc_x, crtc_y; /* signed because can be offscreen */ - uint32_t crtc_w, crtc_h; - uint32_t src_x, src_y; - uint32_t src_w, src_h; -}; - #ifdef CONFIG_DEBUG_FS int omap_debugfs_init(struct drm_minor *minor); void omap_debugfs_cleanup(struct drm_minor *minor); @@ -92,23 +135,40 @@ void omap_gem_describe(struct drm_gem_object *obj, struct seq_file *m); void omap_gem_describe_objects(struct list_head *list, struct seq_file *m); #endif +#ifdef CONFIG_PM +int omap_gem_resume(struct device *dev); +#endif + +int omap_irq_enable_vblank(struct drm_device *dev, int crtc); +void omap_irq_disable_vblank(struct drm_device *dev, int crtc); +irqreturn_t omap_irq_handler(DRM_IRQ_ARGS); +void omap_irq_preinstall(struct drm_device *dev); +int omap_irq_postinstall(struct drm_device *dev); +void omap_irq_uninstall(struct drm_device *dev); +void omap_irq_register(struct drm_device *dev, struct omap_drm_irq *irq); +void omap_irq_unregister(struct drm_device *dev, struct omap_drm_irq *irq); +int omap_drm_irq_uninstall(struct drm_device *dev); +int omap_drm_irq_install(struct drm_device *dev); + struct drm_fb_helper *omap_fbdev_init(struct drm_device *dev); void omap_fbdev_free(struct drm_device *dev); +const struct omap_video_timings *omap_crtc_timings(struct drm_crtc *crtc); +enum omap_channel omap_crtc_channel(struct drm_crtc *crtc); +int omap_crtc_apply(struct drm_crtc *crtc, + struct omap_drm_apply *apply); struct drm_crtc *omap_crtc_init(struct drm_device *dev, - struct omap_overlay *ovl, int id); + struct drm_plane *plane, enum omap_channel channel, int id); struct drm_plane *omap_plane_init(struct drm_device *dev, - struct omap_overlay *ovl, unsigned int possible_crtcs, - bool priv); + int plane_id, bool private_plane); int omap_plane_dpms(struct drm_plane *plane, int mode); int omap_plane_mode_set(struct drm_plane *plane, struct drm_crtc *crtc, struct drm_framebuffer *fb, int crtc_x, int crtc_y, unsigned int crtc_w, unsigned int crtc_h, uint32_t src_x, uint32_t src_y, - uint32_t src_w, uint32_t src_h); -void omap_plane_on_endwin(struct drm_plane *plane, + uint32_t src_w, uint32_t src_h, void (*fxn)(void *), void *arg); void omap_plane_install_properties(struct drm_plane *plane, struct drm_mode_object *obj); @@ -116,21 +176,25 @@ int omap_plane_set_property(struct drm_plane *plane, struct drm_property *property, uint64_t val); struct drm_encoder *omap_encoder_init(struct drm_device *dev, - struct omap_overlay_manager *mgr); -struct omap_overlay_manager *omap_encoder_get_manager( + struct omap_dss_device *dssdev); +int omap_encoder_set_enabled(struct drm_encoder *encoder, bool enabled); +int omap_encoder_update(struct drm_encoder *encoder, + struct omap_overlay_manager *mgr, + struct omap_video_timings *timings); + +struct drm_connector *omap_connector_init(struct drm_device *dev, + int connector_type, struct omap_dss_device *dssdev, struct drm_encoder *encoder); struct drm_encoder *omap_connector_attached_encoder( struct drm_connector *connector); -enum drm_connector_status omap_connector_detect( - struct drm_connector *connector, bool force); - -struct drm_connector *omap_connector_init(struct drm_device *dev, - int connector_type, struct omap_dss_device *dssdev); -void omap_connector_mode_set(struct drm_connector *connector, - struct drm_display_mode *mode); void omap_connector_flush(struct drm_connector *connector, int x, int y, int w, int h); +void copy_timings_omap_to_drm(struct drm_display_mode *mode, + struct omap_video_timings *timings); +void copy_timings_drm_to_omap(struct omap_video_timings *timings, + struct drm_display_mode *mode); + uint32_t omap_framebuffer_get_formats(uint32_t *pixel_formats, uint32_t max_formats, enum omap_color_mode supported_modes); struct drm_framebuffer *omap_framebuffer_create(struct drm_device *dev, @@ -207,6 +271,40 @@ static inline int align_pitch(int pitch, int width, int bpp) return ALIGN(pitch, 8 * bytespp); } +static inline enum omap_channel pipe2chan(int pipe) +{ + int num_mgrs = dss_feat_get_num_mgrs(); + + /* + * We usually don't want to create a CRTC for each manager, + * at least not until we have a way to expose private planes + * to userspace. Otherwise there would not be enough video + * pipes left for drm planes. The higher #'d managers tend + * to have more features so start in reverse order. + */ + return num_mgrs - pipe - 1; +} + +/* map crtc to vblank mask */ +static inline uint32_t pipe2vbl(int crtc) +{ + enum omap_channel channel = pipe2chan(crtc); + return dispc_mgr_get_vsync_irq(channel); +} + +static inline int crtc2pipe(struct drm_device *dev, struct drm_crtc *crtc) +{ + struct omap_drm_private *priv = dev->dev_private; + int i; + + for (i = 0; i < ARRAY_SIZE(priv->crtcs); i++) + if (priv->crtcs[i] == crtc) + return i; + + BUG(); /* bogus CRTC ptr */ + return -1; +} + /* should these be made into common util helpers? */ diff --git a/drivers/staging/omapdrm/omap_encoder.c b/drivers/gpu/drm/omapdrm/omap_encoder.c index 5341d5e3e317..7e1f2ab65372 100644 --- a/drivers/staging/omapdrm/omap_encoder.c +++ b/drivers/gpu/drm/omapdrm/omap_encoder.c @@ -1,5 +1,5 @@ /* - * drivers/staging/omapdrm/omap_encoder.c + * drivers/gpu/drm/omapdrm/omap_encoder.c * * Copyright (C) 2011 Texas Instruments * Author: Rob Clark <rob@ti.com> @@ -22,37 +22,56 @@ #include "drm_crtc.h" #include "drm_crtc_helper.h" +#include <linux/list.h> + + /* * encoder funcs */ #define to_omap_encoder(x) container_of(x, struct omap_encoder, base) +/* The encoder and connector both map to same dssdev.. the encoder + * handles the 'active' parts, ie. anything the modifies the state + * of the hw, and the connector handles the 'read-only' parts, like + * detecting connection and reading edid. + */ struct omap_encoder { struct drm_encoder base; - struct omap_overlay_manager *mgr; + struct omap_dss_device *dssdev; }; static void omap_encoder_destroy(struct drm_encoder *encoder) { struct omap_encoder *omap_encoder = to_omap_encoder(encoder); - DBG("%s", omap_encoder->mgr->name); drm_encoder_cleanup(encoder); kfree(omap_encoder); } +static const struct drm_encoder_funcs omap_encoder_funcs = { + .destroy = omap_encoder_destroy, +}; + +/* + * The CRTC drm_crtc_helper_set_mode() doesn't really give us the right + * order.. the easiest way to work around this for now is to make all + * the encoder-helper's no-op's and have the omap_crtc code take care + * of the sequencing and call us in the right points. + * + * Eventually to handle connecting CRTCs to different encoders properly, + * either the CRTC helpers need to change or we need to replace + * drm_crtc_helper_set_mode(), but lets wait until atomic-modeset for + * that. + */ + static void omap_encoder_dpms(struct drm_encoder *encoder, int mode) { - struct omap_encoder *omap_encoder = to_omap_encoder(encoder); - DBG("%s: %d", omap_encoder->mgr->name, mode); } static bool omap_encoder_mode_fixup(struct drm_encoder *encoder, const struct drm_display_mode *mode, struct drm_display_mode *adjusted_mode) { - struct omap_encoder *omap_encoder = to_omap_encoder(encoder); - DBG("%s", omap_encoder->mgr->name); return true; } @@ -60,47 +79,16 @@ static void omap_encoder_mode_set(struct drm_encoder *encoder, struct drm_display_mode *mode, struct drm_display_mode *adjusted_mode) { - struct omap_encoder *omap_encoder = to_omap_encoder(encoder); - struct drm_device *dev = encoder->dev; - struct omap_drm_private *priv = dev->dev_private; - int i; - - mode = adjusted_mode; - - DBG("%s: set mode: %dx%d", omap_encoder->mgr->name, - mode->hdisplay, mode->vdisplay); - - for (i = 0; i < priv->num_connectors; i++) { - struct drm_connector *connector = priv->connectors[i]; - if (connector->encoder == encoder) - omap_connector_mode_set(connector, mode); - - } } static void omap_encoder_prepare(struct drm_encoder *encoder) { - struct omap_encoder *omap_encoder = to_omap_encoder(encoder); - struct drm_encoder_helper_funcs *encoder_funcs = - encoder->helper_private; - DBG("%s", omap_encoder->mgr->name); - encoder_funcs->dpms(encoder, DRM_MODE_DPMS_OFF); } static void omap_encoder_commit(struct drm_encoder *encoder) { - struct omap_encoder *omap_encoder = to_omap_encoder(encoder); - struct drm_encoder_helper_funcs *encoder_funcs = - encoder->helper_private; - DBG("%s", omap_encoder->mgr->name); - omap_encoder->mgr->apply(omap_encoder->mgr); - encoder_funcs->dpms(encoder, DRM_MODE_DPMS_ON); } -static const struct drm_encoder_funcs omap_encoder_funcs = { - .destroy = omap_encoder_destroy, -}; - static const struct drm_encoder_helper_funcs omap_encoder_helper_funcs = { .dpms = omap_encoder_dpms, .mode_fixup = omap_encoder_mode_fixup, @@ -109,23 +97,54 @@ static const struct drm_encoder_helper_funcs omap_encoder_helper_funcs = { .commit = omap_encoder_commit, }; -struct omap_overlay_manager *omap_encoder_get_manager( - struct drm_encoder *encoder) +/* + * Instead of relying on the helpers for modeset, the omap_crtc code + * calls these functions in the proper sequence. + */ + +int omap_encoder_set_enabled(struct drm_encoder *encoder, bool enabled) { struct omap_encoder *omap_encoder = to_omap_encoder(encoder); - return omap_encoder->mgr; + struct omap_dss_device *dssdev = omap_encoder->dssdev; + struct omap_dss_driver *dssdrv = dssdev->driver; + + if (enabled) { + return dssdrv->enable(dssdev); + } else { + dssdrv->disable(dssdev); + return 0; + } +} + +int omap_encoder_update(struct drm_encoder *encoder, + struct omap_overlay_manager *mgr, + struct omap_video_timings *timings) +{ + struct drm_device *dev = encoder->dev; + struct omap_encoder *omap_encoder = to_omap_encoder(encoder); + struct omap_dss_device *dssdev = omap_encoder->dssdev; + struct omap_dss_driver *dssdrv = dssdev->driver; + int ret; + + dssdev->output->manager = mgr; + + ret = dssdrv->check_timings(dssdev, timings); + if (ret) { + dev_err(dev->dev, "could not set timings: %d\n", ret); + return ret; + } + + dssdrv->set_timings(dssdev, timings); + + return 0; } /* initialize encoder */ struct drm_encoder *omap_encoder_init(struct drm_device *dev, - struct omap_overlay_manager *mgr) + struct omap_dss_device *dssdev) { struct drm_encoder *encoder = NULL; struct omap_encoder *omap_encoder; - struct omap_overlay_manager_info info; - int ret; - - DBG("%s", mgr->name); omap_encoder = kzalloc(sizeof(*omap_encoder), GFP_KERNEL); if (!omap_encoder) { @@ -133,33 +152,14 @@ struct drm_encoder *omap_encoder_init(struct drm_device *dev, goto fail; } - omap_encoder->mgr = mgr; + omap_encoder->dssdev = dssdev; + encoder = &omap_encoder->base; drm_encoder_init(dev, encoder, &omap_encoder_funcs, DRM_MODE_ENCODER_TMDS); drm_encoder_helper_add(encoder, &omap_encoder_helper_funcs); - mgr->get_manager_info(mgr, &info); - - /* TODO: fix hard-coded setup.. */ - info.default_color = 0x00000000; - info.trans_key = 0x00000000; - info.trans_key_type = OMAP_DSS_COLOR_KEY_GFX_DST; - info.trans_enabled = false; - - ret = mgr->set_manager_info(mgr, &info); - if (ret) { - dev_err(dev->dev, "could not set manager info\n"); - goto fail; - } - - ret = mgr->apply(mgr); - if (ret) { - dev_err(dev->dev, "could not apply\n"); - goto fail; - } - return encoder; fail: diff --git a/drivers/staging/omapdrm/omap_fb.c b/drivers/gpu/drm/omapdrm/omap_fb.c index 09028e9c1093..9d5f6f696c72 100644 --- a/drivers/staging/omapdrm/omap_fb.c +++ b/drivers/gpu/drm/omapdrm/omap_fb.c @@ -1,5 +1,5 @@ /* - * drivers/staging/omapdrm/omap_fb.c + * drivers/gpu/drm/omapdrm/omap_fb.c * * Copyright (C) 2011 Texas Instruments * Author: Rob Clark <rob@ti.com> @@ -424,14 +424,6 @@ struct drm_framebuffer *omap_framebuffer_init(struct drm_device *dev, } fb = &omap_fb->base; - ret = drm_framebuffer_init(dev, fb, &omap_framebuffer_funcs); - if (ret) { - dev_err(dev->dev, "framebuffer init failed: %d\n", ret); - goto fail; - } - - DBG("create: FB ID: %d (%p)", fb->base.id, fb); - omap_fb->format = format; for (i = 0; i < n; i++) { @@ -462,6 +454,14 @@ struct drm_framebuffer *omap_framebuffer_init(struct drm_device *dev, drm_helper_mode_fill_fb_struct(fb, mode_cmd); + ret = drm_framebuffer_init(dev, fb, &omap_framebuffer_funcs); + if (ret) { + dev_err(dev->dev, "framebuffer init failed: %d\n", ret); + goto fail; + } + + DBG("create: FB ID: %d (%p)", fb->base.id, fb); + return fb; fail: diff --git a/drivers/staging/omapdrm/omap_fbdev.c b/drivers/gpu/drm/omapdrm/omap_fbdev.c index 8a027bb77d97..f0033bd3e4ae 100644 --- a/drivers/staging/omapdrm/omap_fbdev.c +++ b/drivers/gpu/drm/omapdrm/omap_fbdev.c @@ -1,5 +1,5 @@ /* - * drivers/staging/omapdrm/omap_fbdev.c + * drivers/gpu/drm/omapdrm/omap_fbdev.c * * Copyright (C) 2011 Texas Instruments * Author: Rob Clark <rob@ti.com> @@ -131,9 +131,6 @@ static struct fb_ops omap_fb_ops = { .fb_pan_display = omap_fbdev_pan_display, .fb_blank = drm_fb_helper_blank, .fb_setcmap = drm_fb_helper_setcmap, - - .fb_debug_enter = drm_fb_helper_debug_enter, - .fb_debug_leave = drm_fb_helper_debug_leave, }; static int omap_fbdev_create(struct drm_fb_helper *helper, @@ -275,8 +272,10 @@ fail: if (ret) { if (fbi) framebuffer_release(fbi); - if (fb) + if (fb) { + drm_framebuffer_unregister_private(fb); drm_framebuffer_remove(fb); + } } return ret; @@ -294,25 +293,10 @@ static void omap_crtc_fb_gamma_get(struct drm_crtc *crtc, DBG("fbdev: get gamma"); } -static int omap_fbdev_probe(struct drm_fb_helper *helper, - struct drm_fb_helper_surface_size *sizes) -{ - int new_fb = 0; - int ret; - - if (!helper->fb) { - ret = omap_fbdev_create(helper, sizes); - if (ret) - return ret; - new_fb = 1; - } - return new_fb; -} - static struct drm_fb_helper_funcs omap_fb_helper_funcs = { .gamma_set = omap_crtc_fb_gamma_set, .gamma_get = omap_crtc_fb_gamma_get, - .fb_probe = omap_fbdev_probe, + .fb_probe = omap_fbdev_create, }; static struct drm_fb_helper *get_fb(struct fb_info *fbi) @@ -367,6 +351,10 @@ struct drm_fb_helper *omap_fbdev_init(struct drm_device *dev) } drm_fb_helper_single_add_all_connectors(helper); + + /* disable all the possible outputs/crtcs before entering KMS mode */ + drm_helper_disable_unused_functions(dev); + drm_fb_helper_initial_config(helper, 32); priv->fbdev = helper; @@ -400,8 +388,10 @@ void omap_fbdev_free(struct drm_device *dev) fbdev = to_omap_fbdev(priv->fbdev); /* this will free the backing object */ - if (fbdev->fb) + if (fbdev->fb) { + drm_framebuffer_unregister_private(fbdev->fb); drm_framebuffer_remove(fbdev->fb); + } kfree(fbdev); diff --git a/drivers/staging/omapdrm/omap_gem.c b/drivers/gpu/drm/omapdrm/omap_gem.c index c38992b76fc9..e8302b02691d 100644 --- a/drivers/staging/omapdrm/omap_gem.c +++ b/drivers/gpu/drm/omapdrm/omap_gem.c @@ -1,5 +1,5 @@ /* - * drivers/staging/omapdrm/omap_gem.c + * drivers/gpu/drm/omapdrm/omap_gem.c * * Copyright (C) 2011 Texas Instruments * Author: Rob Clark <rob.clark@linaro.org> @@ -964,6 +964,34 @@ void *omap_gem_vaddr(struct drm_gem_object *obj) return omap_obj->vaddr; } +#ifdef CONFIG_PM +/* re-pin objects in DMM in resume path: */ +int omap_gem_resume(struct device *dev) +{ + struct drm_device *drm_dev = dev_get_drvdata(dev); + struct omap_drm_private *priv = drm_dev->dev_private; + struct omap_gem_object *omap_obj; + int ret = 0; + + list_for_each_entry(omap_obj, &priv->obj_list, mm_list) { + if (omap_obj->block) { + struct drm_gem_object *obj = &omap_obj->base; + uint32_t npages = obj->size >> PAGE_SHIFT; + WARN_ON(!omap_obj->pages); /* this can't happen */ + ret = tiler_pin(omap_obj->block, + omap_obj->pages, npages, + omap_obj->roll, true); + if (ret) { + dev_err(dev, "could not repin: %d\n", ret); + return ret; + } + } + } + + return 0; +} +#endif + #ifdef CONFIG_DEBUG_FS void omap_gem_describe(struct drm_gem_object *obj, struct seq_file *m) { @@ -1239,12 +1267,12 @@ int omap_gem_set_sync_object(struct drm_gem_object *obj, void *syncobj) if ((omap_obj->flags & OMAP_BO_EXT_SYNC) && !syncobj) { /* clearing a previously set syncobj */ - syncobj = kzalloc(sizeof(*omap_obj->sync), GFP_ATOMIC); + syncobj = kmemdup(omap_obj->sync, sizeof(*omap_obj->sync), + GFP_ATOMIC); if (!syncobj) { ret = -ENOMEM; goto unlock; } - memcpy(syncobj, omap_obj->sync, sizeof(*omap_obj->sync)); omap_obj->flags &= ~OMAP_BO_EXT_SYNC; omap_obj->sync = syncobj; } else if (syncobj && !(omap_obj->flags & OMAP_BO_EXT_SYNC)) { diff --git a/drivers/staging/omapdrm/omap_gem_dmabuf.c b/drivers/gpu/drm/omapdrm/omap_gem_dmabuf.c index ea3840038250..ac74d1bc67bf 100644 --- a/drivers/staging/omapdrm/omap_gem_dmabuf.c +++ b/drivers/gpu/drm/omapdrm/omap_gem_dmabuf.c @@ -1,5 +1,5 @@ /* - * drivers/staging/omapdrm/omap_gem_dmabuf.c + * drivers/gpu/drm/omapdrm/omap_gem_dmabuf.c * * Copyright (C) 2011 Texas Instruments * Author: Rob Clark <rob.clark@linaro.org> @@ -53,10 +53,10 @@ static struct sg_table *omap_gem_map_dma_buf( /* this should be after _get_paddr() to ensure we have pages attached */ omap_gem_dma_sync(obj, dir); -out: - if (ret) - return ERR_PTR(ret); return sg; +out: + kfree(sg); + return ERR_PTR(ret); } static void omap_gem_unmap_dma_buf(struct dma_buf_attachment *attachment, @@ -194,7 +194,7 @@ struct dma_buf_ops omap_dmabuf_ops = { struct dma_buf *omap_gem_prime_export(struct drm_device *dev, struct drm_gem_object *obj, int flags) { - return dma_buf_export(obj, &omap_dmabuf_ops, obj->size, 0600); + return dma_buf_export(obj, &omap_dmabuf_ops, obj->size, flags); } struct drm_gem_object *omap_gem_prime_import(struct drm_device *dev, diff --git a/drivers/staging/omapdrm/omap_gem_helpers.c b/drivers/gpu/drm/omapdrm/omap_gem_helpers.c index ffb8cceaeb46..e4a66a35fc6a 100644 --- a/drivers/staging/omapdrm/omap_gem_helpers.c +++ b/drivers/gpu/drm/omapdrm/omap_gem_helpers.c @@ -1,5 +1,5 @@ /* - * drivers/staging/omapdrm/omap_gem_helpers.c + * drivers/gpu/drm/omapdrm/omap_gem_helpers.c * * Copyright (C) 2011 Texas Instruments * Author: Rob Clark <rob.clark@linaro.org> diff --git a/drivers/gpu/drm/omapdrm/omap_irq.c b/drivers/gpu/drm/omapdrm/omap_irq.c new file mode 100644 index 000000000000..e01303ee00c3 --- /dev/null +++ b/drivers/gpu/drm/omapdrm/omap_irq.c @@ -0,0 +1,322 @@ +/* + * drivers/gpu/drm/omapdrm/omap_irq.c + * + * Copyright (C) 2012 Texas Instruments + * Author: Rob Clark <rob.clark@linaro.org> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published by + * the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include "omap_drv.h" + +static DEFINE_SPINLOCK(list_lock); + +static void omap_irq_error_handler(struct omap_drm_irq *irq, + uint32_t irqstatus) +{ + DRM_ERROR("errors: %08x\n", irqstatus); +} + +/* call with list_lock and dispc runtime held */ +static void omap_irq_update(struct drm_device *dev) +{ + struct omap_drm_private *priv = dev->dev_private; + struct omap_drm_irq *irq; + uint32_t irqmask = priv->vblank_mask; + + BUG_ON(!spin_is_locked(&list_lock)); + + list_for_each_entry(irq, &priv->irq_list, node) + irqmask |= irq->irqmask; + + DBG("irqmask=%08x", irqmask); + + dispc_write_irqenable(irqmask); + dispc_read_irqenable(); /* flush posted write */ +} + +void omap_irq_register(struct drm_device *dev, struct omap_drm_irq *irq) +{ + struct omap_drm_private *priv = dev->dev_private; + unsigned long flags; + + dispc_runtime_get(); + spin_lock_irqsave(&list_lock, flags); + + if (!WARN_ON(irq->registered)) { + irq->registered = true; + list_add(&irq->node, &priv->irq_list); + omap_irq_update(dev); + } + + spin_unlock_irqrestore(&list_lock, flags); + dispc_runtime_put(); +} + +void omap_irq_unregister(struct drm_device *dev, struct omap_drm_irq *irq) +{ + unsigned long flags; + + dispc_runtime_get(); + spin_lock_irqsave(&list_lock, flags); + + if (!WARN_ON(!irq->registered)) { + irq->registered = false; + list_del(&irq->node); + omap_irq_update(dev); + } + + spin_unlock_irqrestore(&list_lock, flags); + dispc_runtime_put(); +} + +struct omap_irq_wait { + struct omap_drm_irq irq; + int count; +}; + +static DECLARE_WAIT_QUEUE_HEAD(wait_event); + +static void wait_irq(struct omap_drm_irq *irq, uint32_t irqstatus) +{ + struct omap_irq_wait *wait = + container_of(irq, struct omap_irq_wait, irq); + wait->count--; + wake_up_all(&wait_event); +} + +struct omap_irq_wait * omap_irq_wait_init(struct drm_device *dev, + uint32_t irqmask, int count) +{ + struct omap_irq_wait *wait = kzalloc(sizeof(*wait), GFP_KERNEL); + wait->irq.irq = wait_irq; + wait->irq.irqmask = irqmask; + wait->count = count; + omap_irq_register(dev, &wait->irq); + return wait; +} + +int omap_irq_wait(struct drm_device *dev, struct omap_irq_wait *wait, + unsigned long timeout) +{ + int ret = wait_event_timeout(wait_event, (wait->count <= 0), timeout); + omap_irq_unregister(dev, &wait->irq); + kfree(wait); + if (ret == 0) + return -1; + return 0; +} + +/** + * enable_vblank - enable vblank interrupt events + * @dev: DRM device + * @crtc: which irq to enable + * + * Enable vblank interrupts for @crtc. If the device doesn't have + * a hardware vblank counter, this routine should be a no-op, since + * interrupts will have to stay on to keep the count accurate. + * + * RETURNS + * Zero on success, appropriate errno if the given @crtc's vblank + * interrupt cannot be enabled. + */ +int omap_irq_enable_vblank(struct drm_device *dev, int crtc) +{ + struct omap_drm_private *priv = dev->dev_private; + unsigned long flags; + + DBG("dev=%p, crtc=%d", dev, crtc); + + dispc_runtime_get(); + spin_lock_irqsave(&list_lock, flags); + priv->vblank_mask |= pipe2vbl(crtc); + omap_irq_update(dev); + spin_unlock_irqrestore(&list_lock, flags); + dispc_runtime_put(); + + return 0; +} + +/** + * disable_vblank - disable vblank interrupt events + * @dev: DRM device + * @crtc: which irq to enable + * + * Disable vblank interrupts for @crtc. If the device doesn't have + * a hardware vblank counter, this routine should be a no-op, since + * interrupts will have to stay on to keep the count accurate. + */ +void omap_irq_disable_vblank(struct drm_device *dev, int crtc) +{ + struct omap_drm_private *priv = dev->dev_private; + unsigned long flags; + + DBG("dev=%p, crtc=%d", dev, crtc); + + dispc_runtime_get(); + spin_lock_irqsave(&list_lock, flags); + priv->vblank_mask &= ~pipe2vbl(crtc); + omap_irq_update(dev); + spin_unlock_irqrestore(&list_lock, flags); + dispc_runtime_put(); +} + +irqreturn_t omap_irq_handler(DRM_IRQ_ARGS) +{ + struct drm_device *dev = (struct drm_device *) arg; + struct omap_drm_private *priv = dev->dev_private; + struct omap_drm_irq *handler, *n; + unsigned long flags; + unsigned int id; + u32 irqstatus; + + irqstatus = dispc_read_irqstatus(); + dispc_clear_irqstatus(irqstatus); + dispc_read_irqstatus(); /* flush posted write */ + + VERB("irqs: %08x", irqstatus); + + for (id = 0; id < priv->num_crtcs; id++) + if (irqstatus & pipe2vbl(id)) + drm_handle_vblank(dev, id); + + spin_lock_irqsave(&list_lock, flags); + list_for_each_entry_safe(handler, n, &priv->irq_list, node) { + if (handler->irqmask & irqstatus) { + spin_unlock_irqrestore(&list_lock, flags); + handler->irq(handler, handler->irqmask & irqstatus); + spin_lock_irqsave(&list_lock, flags); + } + } + spin_unlock_irqrestore(&list_lock, flags); + + return IRQ_HANDLED; +} + +void omap_irq_preinstall(struct drm_device *dev) +{ + DBG("dev=%p", dev); + dispc_runtime_get(); + dispc_clear_irqstatus(0xffffffff); + dispc_runtime_put(); +} + +int omap_irq_postinstall(struct drm_device *dev) +{ + struct omap_drm_private *priv = dev->dev_private; + struct omap_drm_irq *error_handler = &priv->error_handler; + + DBG("dev=%p", dev); + + INIT_LIST_HEAD(&priv->irq_list); + + error_handler->irq = omap_irq_error_handler; + error_handler->irqmask = DISPC_IRQ_OCP_ERR; + + /* for now ignore DISPC_IRQ_SYNC_LOST_DIGIT.. really I think + * we just need to ignore it while enabling tv-out + */ + error_handler->irqmask &= ~DISPC_IRQ_SYNC_LOST_DIGIT; + + omap_irq_register(dev, error_handler); + + return 0; +} + +void omap_irq_uninstall(struct drm_device *dev) +{ + DBG("dev=%p", dev); + // TODO prolly need to call drm_irq_uninstall() somewhere too +} + +/* + * We need a special version, instead of just using drm_irq_install(), + * because we need to register the irq via omapdss. Once omapdss and + * omapdrm are merged together we can assign the dispc hwmod data to + * ourselves and drop these and just use drm_irq_{install,uninstall}() + */ + +int omap_drm_irq_install(struct drm_device *dev) +{ + int ret; + + mutex_lock(&dev->struct_mutex); + + if (dev->irq_enabled) { + mutex_unlock(&dev->struct_mutex); + return -EBUSY; + } + dev->irq_enabled = 1; + mutex_unlock(&dev->struct_mutex); + + /* Before installing handler */ + if (dev->driver->irq_preinstall) + dev->driver->irq_preinstall(dev); + + ret = dispc_request_irq(dev->driver->irq_handler, dev); + + if (ret < 0) { + mutex_lock(&dev->struct_mutex); + dev->irq_enabled = 0; + mutex_unlock(&dev->struct_mutex); + return ret; + } + + /* After installing handler */ + if (dev->driver->irq_postinstall) + ret = dev->driver->irq_postinstall(dev); + + if (ret < 0) { + mutex_lock(&dev->struct_mutex); + dev->irq_enabled = 0; + mutex_unlock(&dev->struct_mutex); + dispc_free_irq(dev); + } + + return ret; +} + +int omap_drm_irq_uninstall(struct drm_device *dev) +{ + unsigned long irqflags; + int irq_enabled, i; + + mutex_lock(&dev->struct_mutex); + irq_enabled = dev->irq_enabled; + dev->irq_enabled = 0; + mutex_unlock(&dev->struct_mutex); + + /* + * Wake up any waiters so they don't hang. + */ + if (dev->num_crtcs) { + spin_lock_irqsave(&dev->vbl_lock, irqflags); + for (i = 0; i < dev->num_crtcs; i++) { + DRM_WAKEUP(&dev->vbl_queue[i]); + dev->vblank_enabled[i] = 0; + dev->last_vblank[i] = + dev->driver->get_vblank_counter(dev, i); + } + spin_unlock_irqrestore(&dev->vbl_lock, irqflags); + } + + if (!irq_enabled) + return -EINVAL; + + if (dev->driver->irq_uninstall) + dev->driver->irq_uninstall(dev); + + dispc_free_irq(dev); + + return 0; +} diff --git a/drivers/gpu/drm/omapdrm/omap_plane.c b/drivers/gpu/drm/omapdrm/omap_plane.c new file mode 100644 index 000000000000..dd68d14ce615 --- /dev/null +++ b/drivers/gpu/drm/omapdrm/omap_plane.c @@ -0,0 +1,450 @@ +/* + * drivers/gpu/drm/omapdrm/omap_plane.c + * + * Copyright (C) 2011 Texas Instruments + * Author: Rob Clark <rob.clark@linaro.org> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published by + * the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include <linux/kfifo.h> + +#include "omap_drv.h" +#include "omap_dmm_tiler.h" + +/* some hackery because omapdss has an 'enum omap_plane' (which would be + * better named omap_plane_id).. and compiler seems unhappy about having + * both a 'struct omap_plane' and 'enum omap_plane' + */ +#define omap_plane _omap_plane + +/* + * plane funcs + */ + +struct callback { + void (*fxn)(void *); + void *arg; +}; + +#define to_omap_plane(x) container_of(x, struct omap_plane, base) + +struct omap_plane { + struct drm_plane base; + int id; /* TODO rename omap_plane -> omap_plane_id in omapdss so I can use the enum */ + const char *name; + struct omap_overlay_info info; + struct omap_drm_apply apply; + + /* position/orientation of scanout within the fb: */ + struct omap_drm_window win; + bool enabled; + + /* last fb that we pinned: */ + struct drm_framebuffer *pinned_fb; + + uint32_t nformats; + uint32_t formats[32]; + + struct omap_drm_irq error_irq; + + /* set of bo's pending unpin until next post_apply() */ + DECLARE_KFIFO_PTR(unpin_fifo, struct drm_gem_object *); + + // XXX maybe get rid of this and handle vblank in crtc too? + struct callback apply_done_cb; +}; + +static void unpin(void *arg, struct drm_gem_object *bo) +{ + struct drm_plane *plane = arg; + struct omap_plane *omap_plane = to_omap_plane(plane); + + if (kfifo_put(&omap_plane->unpin_fifo, + (const struct drm_gem_object **)&bo)) { + /* also hold a ref so it isn't free'd while pinned */ + drm_gem_object_reference(bo); + } else { + dev_err(plane->dev->dev, "unpin fifo full!\n"); + omap_gem_put_paddr(bo); + } +} + +/* update which fb (if any) is pinned for scanout */ +static int update_pin(struct drm_plane *plane, struct drm_framebuffer *fb) +{ + struct omap_plane *omap_plane = to_omap_plane(plane); + struct drm_framebuffer *pinned_fb = omap_plane->pinned_fb; + + if (pinned_fb != fb) { + int ret; + + DBG("%p -> %p", pinned_fb, fb); + + if (fb) + drm_framebuffer_reference(fb); + + ret = omap_framebuffer_replace(pinned_fb, fb, plane, unpin); + + if (pinned_fb) + drm_framebuffer_unreference(pinned_fb); + + if (ret) { + dev_err(plane->dev->dev, "could not swap %p -> %p\n", + omap_plane->pinned_fb, fb); + if (fb) + drm_framebuffer_unreference(fb); + omap_plane->pinned_fb = NULL; + return ret; + } + + omap_plane->pinned_fb = fb; + } + + return 0; +} + +static void omap_plane_pre_apply(struct omap_drm_apply *apply) +{ + struct omap_plane *omap_plane = + container_of(apply, struct omap_plane, apply); + struct omap_drm_window *win = &omap_plane->win; + struct drm_plane *plane = &omap_plane->base; + struct drm_device *dev = plane->dev; + struct omap_overlay_info *info = &omap_plane->info; + struct drm_crtc *crtc = plane->crtc; + enum omap_channel channel; + bool enabled = omap_plane->enabled && crtc; + bool ilace, replication; + int ret; + + DBG("%s, enabled=%d", omap_plane->name, enabled); + + /* if fb has changed, pin new fb: */ + update_pin(plane, enabled ? plane->fb : NULL); + + if (!enabled) { + dispc_ovl_enable(omap_plane->id, false); + return; + } + + channel = omap_crtc_channel(crtc); + + /* update scanout: */ + omap_framebuffer_update_scanout(plane->fb, win, info); + + DBG("%dx%d -> %dx%d (%d)", info->width, info->height, + info->out_width, info->out_height, + info->screen_width); + DBG("%d,%d %08x %08x", info->pos_x, info->pos_y, + info->paddr, info->p_uv_addr); + + /* TODO: */ + ilace = false; + replication = false; + + /* and finally, update omapdss: */ + ret = dispc_ovl_setup(omap_plane->id, info, + replication, omap_crtc_timings(crtc), false); + if (ret) { + dev_err(dev->dev, "dispc_ovl_setup failed: %d\n", ret); + return; + } + + dispc_ovl_enable(omap_plane->id, true); + dispc_ovl_set_channel_out(omap_plane->id, channel); +} + +static void omap_plane_post_apply(struct omap_drm_apply *apply) +{ + struct omap_plane *omap_plane = + container_of(apply, struct omap_plane, apply); + struct drm_plane *plane = &omap_plane->base; + struct omap_overlay_info *info = &omap_plane->info; + struct drm_gem_object *bo = NULL; + struct callback cb; + + cb = omap_plane->apply_done_cb; + omap_plane->apply_done_cb.fxn = NULL; + + while (kfifo_get(&omap_plane->unpin_fifo, &bo)) { + omap_gem_put_paddr(bo); + drm_gem_object_unreference_unlocked(bo); + } + + if (cb.fxn) + cb.fxn(cb.arg); + + if (omap_plane->enabled) { + omap_framebuffer_flush(plane->fb, info->pos_x, info->pos_y, + info->out_width, info->out_height); + } +} + +static int apply(struct drm_plane *plane) +{ + if (plane->crtc) { + struct omap_plane *omap_plane = to_omap_plane(plane); + return omap_crtc_apply(plane->crtc, &omap_plane->apply); + } + return 0; +} + +int omap_plane_mode_set(struct drm_plane *plane, + struct drm_crtc *crtc, struct drm_framebuffer *fb, + int crtc_x, int crtc_y, + unsigned int crtc_w, unsigned int crtc_h, + uint32_t src_x, uint32_t src_y, + uint32_t src_w, uint32_t src_h, + void (*fxn)(void *), void *arg) +{ + struct omap_plane *omap_plane = to_omap_plane(plane); + struct omap_drm_window *win = &omap_plane->win; + + win->crtc_x = crtc_x; + win->crtc_y = crtc_y; + win->crtc_w = crtc_w; + win->crtc_h = crtc_h; + + /* src values are in Q16 fixed point, convert to integer: */ + win->src_x = src_x >> 16; + win->src_y = src_y >> 16; + win->src_w = src_w >> 16; + win->src_h = src_h >> 16; + + if (fxn) { + /* omap_crtc should ensure that a new page flip + * isn't permitted while there is one pending: + */ + BUG_ON(omap_plane->apply_done_cb.fxn); + + omap_plane->apply_done_cb.fxn = fxn; + omap_plane->apply_done_cb.arg = arg; + } + + plane->fb = fb; + plane->crtc = crtc; + + return apply(plane); +} + +static int omap_plane_update(struct drm_plane *plane, + struct drm_crtc *crtc, struct drm_framebuffer *fb, + int crtc_x, int crtc_y, + unsigned int crtc_w, unsigned int crtc_h, + uint32_t src_x, uint32_t src_y, + uint32_t src_w, uint32_t src_h) +{ + struct omap_plane *omap_plane = to_omap_plane(plane); + omap_plane->enabled = true; + return omap_plane_mode_set(plane, crtc, fb, + crtc_x, crtc_y, crtc_w, crtc_h, + src_x, src_y, src_w, src_h, + NULL, NULL); +} + +static int omap_plane_disable(struct drm_plane *plane) +{ + struct omap_plane *omap_plane = to_omap_plane(plane); + omap_plane->win.rotation = BIT(DRM_ROTATE_0); + return omap_plane_dpms(plane, DRM_MODE_DPMS_OFF); +} + +static void omap_plane_destroy(struct drm_plane *plane) +{ + struct omap_plane *omap_plane = to_omap_plane(plane); + + DBG("%s", omap_plane->name); + + omap_irq_unregister(plane->dev, &omap_plane->error_irq); + + omap_plane_disable(plane); + drm_plane_cleanup(plane); + + WARN_ON(!kfifo_is_empty(&omap_plane->unpin_fifo)); + kfifo_free(&omap_plane->unpin_fifo); + + kfree(omap_plane); +} + +int omap_plane_dpms(struct drm_plane *plane, int mode) +{ + struct omap_plane *omap_plane = to_omap_plane(plane); + bool enabled = (mode == DRM_MODE_DPMS_ON); + int ret = 0; + + if (enabled != omap_plane->enabled) { + omap_plane->enabled = enabled; + ret = apply(plane); + } + + return ret; +} + +/* helper to install properties which are common to planes and crtcs */ +void omap_plane_install_properties(struct drm_plane *plane, + struct drm_mode_object *obj) +{ + struct drm_device *dev = plane->dev; + struct omap_drm_private *priv = dev->dev_private; + struct drm_property *prop; + + if (priv->has_dmm) { + prop = priv->rotation_prop; + if (!prop) { + const struct drm_prop_enum_list props[] = { + { DRM_ROTATE_0, "rotate-0" }, + { DRM_ROTATE_90, "rotate-90" }, + { DRM_ROTATE_180, "rotate-180" }, + { DRM_ROTATE_270, "rotate-270" }, + { DRM_REFLECT_X, "reflect-x" }, + { DRM_REFLECT_Y, "reflect-y" }, + }; + prop = drm_property_create_bitmask(dev, 0, "rotation", + props, ARRAY_SIZE(props)); + if (prop == NULL) + return; + priv->rotation_prop = prop; + } + drm_object_attach_property(obj, prop, 0); + } + + prop = priv->zorder_prop; + if (!prop) { + prop = drm_property_create_range(dev, 0, "zorder", 0, 3); + if (prop == NULL) + return; + priv->zorder_prop = prop; + } + drm_object_attach_property(obj, prop, 0); +} + +int omap_plane_set_property(struct drm_plane *plane, + struct drm_property *property, uint64_t val) +{ + struct omap_plane *omap_plane = to_omap_plane(plane); + struct omap_drm_private *priv = plane->dev->dev_private; + int ret = -EINVAL; + + if (property == priv->rotation_prop) { + DBG("%s: rotation: %02x", omap_plane->name, (uint32_t)val); + omap_plane->win.rotation = val; + ret = apply(plane); + } else if (property == priv->zorder_prop) { + DBG("%s: zorder: %02x", omap_plane->name, (uint32_t)val); + omap_plane->info.zorder = val; + ret = apply(plane); + } + + return ret; +} + +static const struct drm_plane_funcs omap_plane_funcs = { + .update_plane = omap_plane_update, + .disable_plane = omap_plane_disable, + .destroy = omap_plane_destroy, + .set_property = omap_plane_set_property, +}; + +static void omap_plane_error_irq(struct omap_drm_irq *irq, uint32_t irqstatus) +{ + struct omap_plane *omap_plane = + container_of(irq, struct omap_plane, error_irq); + DRM_ERROR("%s: errors: %08x\n", omap_plane->name, irqstatus); +} + +static const char *plane_names[] = { + [OMAP_DSS_GFX] = "gfx", + [OMAP_DSS_VIDEO1] = "vid1", + [OMAP_DSS_VIDEO2] = "vid2", + [OMAP_DSS_VIDEO3] = "vid3", +}; + +static const uint32_t error_irqs[] = { + [OMAP_DSS_GFX] = DISPC_IRQ_GFX_FIFO_UNDERFLOW, + [OMAP_DSS_VIDEO1] = DISPC_IRQ_VID1_FIFO_UNDERFLOW, + [OMAP_DSS_VIDEO2] = DISPC_IRQ_VID2_FIFO_UNDERFLOW, + [OMAP_DSS_VIDEO3] = DISPC_IRQ_VID3_FIFO_UNDERFLOW, +}; + +/* initialize plane */ +struct drm_plane *omap_plane_init(struct drm_device *dev, + int id, bool private_plane) +{ + struct omap_drm_private *priv = dev->dev_private; + struct drm_plane *plane = NULL; + struct omap_plane *omap_plane; + struct omap_overlay_info *info; + int ret; + + DBG("%s: priv=%d", plane_names[id], private_plane); + + omap_plane = kzalloc(sizeof(*omap_plane), GFP_KERNEL); + if (!omap_plane) { + dev_err(dev->dev, "could not allocate plane\n"); + goto fail; + } + + ret = kfifo_alloc(&omap_plane->unpin_fifo, 16, GFP_KERNEL); + if (ret) { + dev_err(dev->dev, "could not allocate unpin FIFO\n"); + goto fail; + } + + omap_plane->nformats = omap_framebuffer_get_formats( + omap_plane->formats, ARRAY_SIZE(omap_plane->formats), + dss_feat_get_supported_color_modes(id)); + omap_plane->id = id; + omap_plane->name = plane_names[id]; + + plane = &omap_plane->base; + + omap_plane->apply.pre_apply = omap_plane_pre_apply; + omap_plane->apply.post_apply = omap_plane_post_apply; + + omap_plane->error_irq.irqmask = error_irqs[id]; + omap_plane->error_irq.irq = omap_plane_error_irq; + omap_irq_register(dev, &omap_plane->error_irq); + + drm_plane_init(dev, plane, (1 << priv->num_crtcs) - 1, &omap_plane_funcs, + omap_plane->formats, omap_plane->nformats, private_plane); + + omap_plane_install_properties(plane, &plane->base); + + /* get our starting configuration, set defaults for parameters + * we don't currently use, etc: + */ + info = &omap_plane->info; + info->rotation_type = OMAP_DSS_ROT_DMA; + info->rotation = OMAP_DSS_ROT_0; + info->global_alpha = 0xff; + info->mirror = 0; + + /* Set defaults depending on whether we are a CRTC or overlay + * layer. + * TODO add ioctl to give userspace an API to change this.. this + * will come in a subsequent patch. + */ + if (private_plane) + omap_plane->info.zorder = 0; + else + omap_plane->info.zorder = id; + + return plane; + +fail: + if (plane) + omap_plane_destroy(plane); + + return NULL; +} diff --git a/drivers/staging/omapdrm/tcm-sita.c b/drivers/gpu/drm/omapdrm/tcm-sita.c index efb609510540..efb609510540 100644 --- a/drivers/staging/omapdrm/tcm-sita.c +++ b/drivers/gpu/drm/omapdrm/tcm-sita.c diff --git a/drivers/staging/omapdrm/tcm-sita.h b/drivers/gpu/drm/omapdrm/tcm-sita.h index 0444f868671c..0444f868671c 100644 --- a/drivers/staging/omapdrm/tcm-sita.h +++ b/drivers/gpu/drm/omapdrm/tcm-sita.h diff --git a/drivers/staging/omapdrm/tcm.h b/drivers/gpu/drm/omapdrm/tcm.h index d273e3ee0b4c..a8d5ce47686f 100644 --- a/drivers/staging/omapdrm/tcm.h +++ b/drivers/gpu/drm/omapdrm/tcm.h @@ -59,6 +59,8 @@ struct tcm { u16 width, height; /* container dimensions */ int lut_id; /* Lookup table identifier */ + unsigned int y_offset; /* offset to use for y coordinates */ + /* 'pvt' structure shall contain any tcm details (attr) along with linked list of allocated areas and mutex for mutually exclusive access to the list. It may also contain copies of width and height to notice diff --git a/drivers/gpu/drm/radeon/radeon.h b/drivers/gpu/drm/radeon/radeon.h index 081e55f66c40..8263af3fd832 100644 --- a/drivers/gpu/drm/radeon/radeon.h +++ b/drivers/gpu/drm/radeon/radeon.h @@ -350,7 +350,6 @@ struct radeon_bo { struct drm_gem_object gem_base; struct ttm_bo_kmap_obj dma_buf_vmap; - int vmapping_count; }; #define gem_to_radeon_bo(gobj) container_of((gobj), struct radeon_bo, gem_base) diff --git a/drivers/gpu/drm/radeon/radeon_cursor.c b/drivers/gpu/drm/radeon/radeon_cursor.c index 0d67674b64b1..b097d5b4ff39 100644 --- a/drivers/gpu/drm/radeon/radeon_cursor.c +++ b/drivers/gpu/drm/radeon/radeon_cursor.c @@ -246,8 +246,14 @@ int radeon_crtc_cursor_move(struct drm_crtc *crtc, int i = 0; struct drm_crtc *crtc_p; - /* avivo cursor image can't end on 128 pixel boundary or + /* + * avivo cursor image can't end on 128 pixel boundary or * go past the end of the frame if both crtcs are enabled + * + * NOTE: It is safe to access crtc->enabled of other crtcs + * without holding either the mode_config lock or the other + * crtc's lock as long as write access to this flag _always_ + * grabs all locks. */ list_for_each_entry(crtc_p, &crtc->dev->mode_config.crtc_list, head) { if (crtc_p->enabled) diff --git a/drivers/gpu/drm/radeon/radeon_device.c b/drivers/gpu/drm/radeon/radeon_device.c index 9f02fdd68f51..44b8034a400d 100644 --- a/drivers/gpu/drm/radeon/radeon_device.c +++ b/drivers/gpu/drm/radeon/radeon_device.c @@ -430,7 +430,8 @@ bool radeon_card_posted(struct radeon_device *rdev) { uint32_t reg; - if (efi_enabled && rdev->pdev->subsystem_vendor == PCI_VENDOR_ID_APPLE) + if (efi_enabled(EFI_BOOT) && + rdev->pdev->subsystem_vendor == PCI_VENDOR_ID_APPLE) return false; /* first check CRTCs */ diff --git a/drivers/gpu/drm/radeon/radeon_display.c b/drivers/gpu/drm/radeon/radeon_display.c index 05c96fa0b051..e38fd559f1ab 100644 --- a/drivers/gpu/drm/radeon/radeon_display.c +++ b/drivers/gpu/drm/radeon/radeon_display.c @@ -1089,12 +1089,12 @@ radeon_framebuffer_init(struct drm_device *dev, { int ret; rfb->obj = obj; + drm_helper_mode_fill_fb_struct(&rfb->base, mode_cmd); ret = drm_framebuffer_init(dev, &rfb->base, &radeon_fb_funcs); if (ret) { rfb->obj = NULL; return ret; } - drm_helper_mode_fill_fb_struct(&rfb->base, mode_cmd); return 0; } diff --git a/drivers/gpu/drm/radeon/radeon_drv.c b/drivers/gpu/drm/radeon/radeon_drv.c index 833484da12d9..167758488ed6 100644 --- a/drivers/gpu/drm/radeon/radeon_drv.c +++ b/drivers/gpu/drm/radeon/radeon_drv.c @@ -118,11 +118,13 @@ int radeon_mode_dumb_create(struct drm_file *file_priv, int radeon_mode_dumb_destroy(struct drm_file *file_priv, struct drm_device *dev, uint32_t handle); -struct dma_buf *radeon_gem_prime_export(struct drm_device *dev, - struct drm_gem_object *obj, - int flags); -struct drm_gem_object *radeon_gem_prime_import(struct drm_device *dev, - struct dma_buf *dma_buf); +struct sg_table *radeon_gem_prime_get_sg_table(struct drm_gem_object *obj); +struct drm_gem_object *radeon_gem_prime_import_sg_table(struct drm_device *dev, + size_t size, + struct sg_table *sg); +int radeon_gem_prime_pin(struct drm_gem_object *obj); +void *radeon_gem_prime_vmap(struct drm_gem_object *obj); +void radeon_gem_prime_vunmap(struct drm_gem_object *obj, void *vaddr); extern long radeon_kms_compat_ioctl(struct file *filp, unsigned int cmd, unsigned long arg); @@ -409,8 +411,13 @@ static struct drm_driver kms_driver = { .prime_handle_to_fd = drm_gem_prime_handle_to_fd, .prime_fd_to_handle = drm_gem_prime_fd_to_handle, - .gem_prime_export = radeon_gem_prime_export, - .gem_prime_import = radeon_gem_prime_import, + .gem_prime_export = drm_gem_prime_export, + .gem_prime_import = drm_gem_prime_import, + .gem_prime_pin = radeon_gem_prime_pin, + .gem_prime_get_sg_table = radeon_gem_prime_get_sg_table, + .gem_prime_import_sg_table = radeon_gem_prime_import_sg_table, + .gem_prime_vmap = radeon_gem_prime_vmap, + .gem_prime_vunmap = radeon_gem_prime_vunmap, .name = DRIVER_NAME, .desc = DRIVER_DESC, diff --git a/drivers/gpu/drm/radeon/radeon_fb.c b/drivers/gpu/drm/radeon/radeon_fb.c index cc8489d8c6d1..b1746741bc59 100644 --- a/drivers/gpu/drm/radeon/radeon_fb.c +++ b/drivers/gpu/drm/radeon/radeon_fb.c @@ -187,9 +187,10 @@ out_unref: return ret; } -static int radeonfb_create(struct radeon_fbdev *rfbdev, +static int radeonfb_create(struct drm_fb_helper *helper, struct drm_fb_helper_surface_size *sizes) { + struct radeon_fbdev *rfbdev = (struct radeon_fbdev *)helper; struct radeon_device *rdev = rfbdev->rdev; struct fb_info *info; struct drm_framebuffer *fb = NULL; @@ -293,28 +294,13 @@ out_unref: } if (fb && ret) { drm_gem_object_unreference(gobj); + drm_framebuffer_unregister_private(fb); drm_framebuffer_cleanup(fb); kfree(fb); } return ret; } -static int radeon_fb_find_or_create_single(struct drm_fb_helper *helper, - struct drm_fb_helper_surface_size *sizes) -{ - struct radeon_fbdev *rfbdev = (struct radeon_fbdev *)helper; - int new_fb = 0; - int ret; - - if (!helper->fb) { - ret = radeonfb_create(rfbdev, sizes); - if (ret) - return ret; - new_fb = 1; - } - return new_fb; -} - void radeon_fb_output_poll_changed(struct radeon_device *rdev) { drm_fb_helper_hotplug_event(&rdev->mode_info.rfbdev->helper); @@ -339,6 +325,7 @@ static int radeon_fbdev_destroy(struct drm_device *dev, struct radeon_fbdev *rfb rfb->obj = NULL; } drm_fb_helper_fini(&rfbdev->helper); + drm_framebuffer_unregister_private(&rfb->base); drm_framebuffer_cleanup(&rfb->base); return 0; @@ -347,7 +334,7 @@ static int radeon_fbdev_destroy(struct drm_device *dev, struct radeon_fbdev *rfb static struct drm_fb_helper_funcs radeon_fb_helper_funcs = { .gamma_set = radeon_crtc_fb_gamma_set, .gamma_get = radeon_crtc_fb_gamma_get, - .fb_probe = radeon_fb_find_or_create_single, + .fb_probe = radeonfb_create, }; int radeon_fbdev_init(struct radeon_device *rdev) @@ -377,6 +364,10 @@ int radeon_fbdev_init(struct radeon_device *rdev) } drm_fb_helper_single_add_all_connectors(&rfbdev->helper); + + /* disable all the possible outputs/crtcs before entering KMS mode */ + drm_helper_disable_unused_functions(rdev->ddev); + drm_fb_helper_initial_config(&rfbdev->helper, bpp_sel); return 0; } diff --git a/drivers/gpu/drm/radeon/radeon_prime.c b/drivers/gpu/drm/radeon/radeon_prime.c index 26c23bb651c6..4940af7e75e6 100644 --- a/drivers/gpu/drm/radeon/radeon_prime.c +++ b/drivers/gpu/drm/radeon/radeon_prime.c @@ -28,199 +28,71 @@ #include "radeon.h" #include <drm/radeon_drm.h> -#include <linux/dma-buf.h> - -static struct sg_table *radeon_gem_map_dma_buf(struct dma_buf_attachment *attachment, - enum dma_data_direction dir) +struct sg_table *radeon_gem_prime_get_sg_table(struct drm_gem_object *obj) { - struct radeon_bo *bo = attachment->dmabuf->priv; - struct drm_device *dev = bo->rdev->ddev; + struct radeon_bo *bo = gem_to_radeon_bo(obj); int npages = bo->tbo.num_pages; - struct sg_table *sg; - int nents; - - mutex_lock(&dev->struct_mutex); - sg = drm_prime_pages_to_sg(bo->tbo.ttm->pages, npages); - nents = dma_map_sg(attachment->dev, sg->sgl, sg->nents, dir); - mutex_unlock(&dev->struct_mutex); - return sg; -} - -static void radeon_gem_unmap_dma_buf(struct dma_buf_attachment *attachment, - struct sg_table *sg, enum dma_data_direction dir) -{ - dma_unmap_sg(attachment->dev, sg->sgl, sg->nents, dir); - sg_free_table(sg); - kfree(sg); -} - -static void radeon_gem_dmabuf_release(struct dma_buf *dma_buf) -{ - struct radeon_bo *bo = dma_buf->priv; - - if (bo->gem_base.export_dma_buf == dma_buf) { - DRM_ERROR("unreference dmabuf %p\n", &bo->gem_base); - bo->gem_base.export_dma_buf = NULL; - drm_gem_object_unreference_unlocked(&bo->gem_base); - } -} - -static void *radeon_gem_kmap_atomic(struct dma_buf *dma_buf, unsigned long page_num) -{ - return NULL; -} - -static void radeon_gem_kunmap_atomic(struct dma_buf *dma_buf, unsigned long page_num, void *addr) -{ - -} -static void *radeon_gem_kmap(struct dma_buf *dma_buf, unsigned long page_num) -{ - return NULL; -} - -static void radeon_gem_kunmap(struct dma_buf *dma_buf, unsigned long page_num, void *addr) -{ + return drm_prime_pages_to_sg(bo->tbo.ttm->pages, npages); } -static int radeon_gem_prime_mmap(struct dma_buf *dma_buf, struct vm_area_struct *vma) +void *radeon_gem_prime_vmap(struct drm_gem_object *obj) { - return -EINVAL; -} - -static void *radeon_gem_prime_vmap(struct dma_buf *dma_buf) -{ - struct radeon_bo *bo = dma_buf->priv; - struct drm_device *dev = bo->rdev->ddev; + struct radeon_bo *bo = gem_to_radeon_bo(obj); int ret; - mutex_lock(&dev->struct_mutex); - if (bo->vmapping_count) { - bo->vmapping_count++; - goto out_unlock; - } - ret = ttm_bo_kmap(&bo->tbo, 0, bo->tbo.num_pages, &bo->dma_buf_vmap); - if (ret) { - mutex_unlock(&dev->struct_mutex); + if (ret) return ERR_PTR(ret); - } - bo->vmapping_count = 1; -out_unlock: - mutex_unlock(&dev->struct_mutex); + return bo->dma_buf_vmap.virtual; } -static void radeon_gem_prime_vunmap(struct dma_buf *dma_buf, void *vaddr) +void radeon_gem_prime_vunmap(struct drm_gem_object *obj, void *vaddr) { - struct radeon_bo *bo = dma_buf->priv; - struct drm_device *dev = bo->rdev->ddev; + struct radeon_bo *bo = gem_to_radeon_bo(obj); - mutex_lock(&dev->struct_mutex); - bo->vmapping_count--; - if (bo->vmapping_count == 0) { - ttm_bo_kunmap(&bo->dma_buf_vmap); - } - mutex_unlock(&dev->struct_mutex); + ttm_bo_kunmap(&bo->dma_buf_vmap); } -const static struct dma_buf_ops radeon_dmabuf_ops = { - .map_dma_buf = radeon_gem_map_dma_buf, - .unmap_dma_buf = radeon_gem_unmap_dma_buf, - .release = radeon_gem_dmabuf_release, - .kmap = radeon_gem_kmap, - .kmap_atomic = radeon_gem_kmap_atomic, - .kunmap = radeon_gem_kunmap, - .kunmap_atomic = radeon_gem_kunmap_atomic, - .mmap = radeon_gem_prime_mmap, - .vmap = radeon_gem_prime_vmap, - .vunmap = radeon_gem_prime_vunmap, -}; - -static int radeon_prime_create(struct drm_device *dev, - size_t size, - struct sg_table *sg, - struct radeon_bo **pbo) + +struct drm_gem_object *radeon_gem_prime_import_sg_table(struct drm_device *dev, + size_t size, + struct sg_table *sg) { struct radeon_device *rdev = dev->dev_private; struct radeon_bo *bo; int ret; ret = radeon_bo_create(rdev, size, PAGE_SIZE, false, - RADEON_GEM_DOMAIN_GTT, sg, pbo); + RADEON_GEM_DOMAIN_GTT, sg, &bo); if (ret) - return ret; - bo = *pbo; + return ERR_PTR(ret); bo->gem_base.driver_private = bo; mutex_lock(&rdev->gem.mutex); list_add_tail(&bo->list, &rdev->gem.objects); mutex_unlock(&rdev->gem.mutex); - return 0; + return &bo->gem_base; } -struct dma_buf *radeon_gem_prime_export(struct drm_device *dev, - struct drm_gem_object *obj, - int flags) +int radeon_gem_prime_pin(struct drm_gem_object *obj) { struct radeon_bo *bo = gem_to_radeon_bo(obj); int ret = 0; ret = radeon_bo_reserve(bo, false); if (unlikely(ret != 0)) - return ERR_PTR(ret); + return ret; /* pin buffer into GTT */ ret = radeon_bo_pin(bo, RADEON_GEM_DOMAIN_GTT, NULL); if (ret) { radeon_bo_unreserve(bo); - return ERR_PTR(ret); + return ret; } radeon_bo_unreserve(bo); - return dma_buf_export(bo, &radeon_dmabuf_ops, obj->size, flags); -} -struct drm_gem_object *radeon_gem_prime_import(struct drm_device *dev, - struct dma_buf *dma_buf) -{ - struct dma_buf_attachment *attach; - struct sg_table *sg; - struct radeon_bo *bo; - int ret; - - if (dma_buf->ops == &radeon_dmabuf_ops) { - bo = dma_buf->priv; - if (bo->gem_base.dev == dev) { - drm_gem_object_reference(&bo->gem_base); - dma_buf_put(dma_buf); - return &bo->gem_base; - } - } - - /* need to attach */ - attach = dma_buf_attach(dma_buf, dev->dev); - if (IS_ERR(attach)) - return ERR_CAST(attach); - - sg = dma_buf_map_attachment(attach, DMA_BIDIRECTIONAL); - if (IS_ERR(sg)) { - ret = PTR_ERR(sg); - goto fail_detach; - } - - ret = radeon_prime_create(dev, dma_buf->size, sg, &bo); - if (ret) - goto fail_unmap; - - bo->gem_base.import_attach = attach; - - return &bo->gem_base; - -fail_unmap: - dma_buf_unmap_attachment(attach, sg, DMA_BIDIRECTIONAL); -fail_detach: - dma_buf_detach(dma_buf, attach); - return ERR_PTR(ret); + return 0; } diff --git a/drivers/gpu/drm/radeon/radeon_ttm.c b/drivers/gpu/drm/radeon/radeon_ttm.c index 1d8ff2f850ba..93f760e27a92 100644 --- a/drivers/gpu/drm/radeon/radeon_ttm.c +++ b/drivers/gpu/drm/radeon/radeon_ttm.c @@ -38,6 +38,7 @@ #include <drm/radeon_drm.h> #include <linux/seq_file.h> #include <linux/slab.h> +#include <linux/swiotlb.h> #include "radeon_reg.h" #include "radeon.h" diff --git a/drivers/gpu/drm/shmobile/shmob_drm_drv.c b/drivers/gpu/drm/shmobile/shmob_drm_drv.c index d1d5306ebf24..f6e0b5395051 100644 --- a/drivers/gpu/drm/shmobile/shmob_drm_drv.c +++ b/drivers/gpu/drm/shmobile/shmob_drm_drv.c @@ -313,9 +313,9 @@ static int shmob_drm_pm_resume(struct device *dev) { struct shmob_drm_device *sdev = dev_get_drvdata(dev); - mutex_lock(&sdev->ddev->mode_config.mutex); + drm_modeset_lock_all(sdev->ddev); shmob_drm_crtc_resume(&sdev->crtc); - mutex_unlock(&sdev->ddev->mode_config.mutex); + drm_modeset_unlock_all(sdev->ddev); drm_kms_helper_poll_enable(sdev->ddev); return 0; diff --git a/drivers/gpu/drm/tegra/fb.c b/drivers/gpu/drm/tegra/fb.c index 97993c6835fd..03914953cb1c 100644 --- a/drivers/gpu/drm/tegra/fb.c +++ b/drivers/gpu/drm/tegra/fb.c @@ -39,10 +39,6 @@ int tegra_drm_fb_init(struct drm_device *drm) if (IS_ERR(fbdev)) return PTR_ERR(fbdev); -#ifndef CONFIG_FRAMEBUFFER_CONSOLE - drm_fbdev_cma_restore_mode(fbdev); -#endif - host1x->fbdev = fbdev; return 0; diff --git a/drivers/gpu/drm/ttm/ttm_bo.c b/drivers/gpu/drm/ttm/ttm_bo.c index 52b20b12c83a..9b07b7d44a58 100644 --- a/drivers/gpu/drm/ttm/ttm_bo.c +++ b/drivers/gpu/drm/ttm/ttm_bo.c @@ -158,7 +158,8 @@ static void ttm_bo_release_list(struct kref *list_kref) ttm_mem_global_free(bdev->glob->mem_glob, acc_size); } -int ttm_bo_wait_unreserved(struct ttm_buffer_object *bo, bool interruptible) +static int ttm_bo_wait_unreserved(struct ttm_buffer_object *bo, + bool interruptible) { if (interruptible) { return wait_event_interruptible(bo->event_queue, @@ -168,7 +169,6 @@ int ttm_bo_wait_unreserved(struct ttm_buffer_object *bo, bool interruptible) return 0; } } -EXPORT_SYMBOL(ttm_bo_wait_unreserved); void ttm_bo_add_to_lru(struct ttm_buffer_object *bo) { @@ -213,14 +213,13 @@ int ttm_bo_del_from_lru(struct ttm_buffer_object *bo) return put_count; } -int ttm_bo_reserve_locked(struct ttm_buffer_object *bo, +int ttm_bo_reserve_nolru(struct ttm_buffer_object *bo, bool interruptible, bool no_wait, bool use_sequence, uint32_t sequence) { - struct ttm_bo_global *glob = bo->glob; int ret; - while (unlikely(atomic_read(&bo->reserved) != 0)) { + while (unlikely(atomic_xchg(&bo->reserved, 1) != 0)) { /** * Deadlock avoidance for multi-bo reserving. */ @@ -241,26 +240,36 @@ int ttm_bo_reserve_locked(struct ttm_buffer_object *bo, if (no_wait) return -EBUSY; - spin_unlock(&glob->lru_lock); ret = ttm_bo_wait_unreserved(bo, interruptible); - spin_lock(&glob->lru_lock); if (unlikely(ret)) return ret; } - atomic_set(&bo->reserved, 1); if (use_sequence) { + bool wake_up = false; /** * Wake up waiters that may need to recheck for deadlock, * if we decreased the sequence number. */ if (unlikely((bo->val_seq - sequence < (1 << 31)) || !bo->seq_valid)) - wake_up_all(&bo->event_queue); + wake_up = true; + /* + * In the worst case with memory ordering these values can be + * seen in the wrong order. However since we call wake_up_all + * in that case, this will hopefully not pose a problem, + * and the worst case would only cause someone to accidentally + * hit -EAGAIN in ttm_bo_reserve when they see old value of + * val_seq. However this would only happen if seq_valid was + * written before val_seq was, and just means some slightly + * increased cpu usage + */ bo->val_seq = sequence; bo->seq_valid = true; + if (wake_up) + wake_up_all(&bo->event_queue); } else { bo->seq_valid = false; } @@ -289,17 +298,64 @@ int ttm_bo_reserve(struct ttm_buffer_object *bo, int put_count = 0; int ret; - spin_lock(&glob->lru_lock); - ret = ttm_bo_reserve_locked(bo, interruptible, no_wait, use_sequence, - sequence); - if (likely(ret == 0)) + ret = ttm_bo_reserve_nolru(bo, interruptible, no_wait, use_sequence, + sequence); + if (likely(ret == 0)) { + spin_lock(&glob->lru_lock); put_count = ttm_bo_del_from_lru(bo); - spin_unlock(&glob->lru_lock); + spin_unlock(&glob->lru_lock); + ttm_bo_list_ref_sub(bo, put_count, true); + } - ttm_bo_list_ref_sub(bo, put_count, true); + return ret; +} + +int ttm_bo_reserve_slowpath_nolru(struct ttm_buffer_object *bo, + bool interruptible, uint32_t sequence) +{ + bool wake_up = false; + int ret; + + while (unlikely(atomic_xchg(&bo->reserved, 1) != 0)) { + WARN_ON(bo->seq_valid && sequence == bo->val_seq); + + ret = ttm_bo_wait_unreserved(bo, interruptible); + if (unlikely(ret)) + return ret; + } + + if ((bo->val_seq - sequence < (1 << 31)) || !bo->seq_valid) + wake_up = true; + + /** + * Wake up waiters that may need to recheck for deadlock, + * if we decreased the sequence number. + */ + bo->val_seq = sequence; + bo->seq_valid = true; + if (wake_up) + wake_up_all(&bo->event_queue); + + return 0; +} + +int ttm_bo_reserve_slowpath(struct ttm_buffer_object *bo, + bool interruptible, uint32_t sequence) +{ + struct ttm_bo_global *glob = bo->glob; + int put_count, ret; + + ret = ttm_bo_reserve_slowpath_nolru(bo, interruptible, sequence); + if (likely(!ret)) { + spin_lock(&glob->lru_lock); + put_count = ttm_bo_del_from_lru(bo); + spin_unlock(&glob->lru_lock); + ttm_bo_list_ref_sub(bo, put_count, true); + } return ret; } +EXPORT_SYMBOL(ttm_bo_reserve_slowpath); void ttm_bo_unreserve_locked(struct ttm_buffer_object *bo) { @@ -511,7 +567,7 @@ static void ttm_bo_cleanup_refs_or_queue(struct ttm_buffer_object *bo) int ret; spin_lock(&glob->lru_lock); - ret = ttm_bo_reserve_locked(bo, false, true, false, 0); + ret = ttm_bo_reserve_nolru(bo, false, true, false, 0); spin_lock(&bdev->fence_lock); (void) ttm_bo_wait(bo, false, false, true); @@ -604,7 +660,7 @@ static int ttm_bo_cleanup_refs_and_unlock(struct ttm_buffer_object *bo, return ret; spin_lock(&glob->lru_lock); - ret = ttm_bo_reserve_locked(bo, false, true, false, 0); + ret = ttm_bo_reserve_nolru(bo, false, true, false, 0); /* * We raced, and lost, someone else holds the reservation now, @@ -668,7 +724,14 @@ static int ttm_bo_delayed_delete(struct ttm_bo_device *bdev, bool remove_all) kref_get(&nentry->list_kref); } - ret = ttm_bo_reserve_locked(entry, false, !remove_all, false, 0); + ret = ttm_bo_reserve_nolru(entry, false, true, false, 0); + if (remove_all && ret) { + spin_unlock(&glob->lru_lock); + ret = ttm_bo_reserve_nolru(entry, false, false, + false, 0); + spin_lock(&glob->lru_lock); + } + if (!ret) ret = ttm_bo_cleanup_refs_and_unlock(entry, false, !remove_all); @@ -816,7 +879,7 @@ static int ttm_mem_evict_first(struct ttm_bo_device *bdev, spin_lock(&glob->lru_lock); list_for_each_entry(bo, &man->lru, lru) { - ret = ttm_bo_reserve_locked(bo, false, true, false, 0); + ret = ttm_bo_reserve_nolru(bo, false, true, false, 0); if (!ret) break; } @@ -1797,7 +1860,7 @@ static int ttm_bo_swapout(struct ttm_mem_shrink *shrink) spin_lock(&glob->lru_lock); list_for_each_entry(bo, &glob->swap_lru, swap) { - ret = ttm_bo_reserve_locked(bo, false, true, false, 0); + ret = ttm_bo_reserve_nolru(bo, false, true, false, 0); if (!ret) break; } diff --git a/drivers/gpu/drm/ttm/ttm_execbuf_util.c b/drivers/gpu/drm/ttm/ttm_execbuf_util.c index cd9e4523dc56..7b90def15674 100644 --- a/drivers/gpu/drm/ttm/ttm_execbuf_util.c +++ b/drivers/gpu/drm/ttm/ttm_execbuf_util.c @@ -82,22 +82,6 @@ static void ttm_eu_list_ref_sub(struct list_head *list) } } -static int ttm_eu_wait_unreserved_locked(struct list_head *list, - struct ttm_buffer_object *bo) -{ - struct ttm_bo_global *glob = bo->glob; - int ret; - - ttm_eu_del_from_lru_locked(list); - spin_unlock(&glob->lru_lock); - ret = ttm_bo_wait_unreserved(bo, true); - spin_lock(&glob->lru_lock); - if (unlikely(ret != 0)) - ttm_eu_backoff_reservation_locked(list); - return ret; -} - - void ttm_eu_backoff_reservation(struct list_head *list) { struct ttm_validate_buffer *entry; @@ -145,47 +129,65 @@ int ttm_eu_reserve_buffers(struct list_head *list) entry = list_first_entry(list, struct ttm_validate_buffer, head); glob = entry->bo->glob; -retry: spin_lock(&glob->lru_lock); val_seq = entry->bo->bdev->val_seq++; +retry: list_for_each_entry(entry, list, head) { struct ttm_buffer_object *bo = entry->bo; -retry_this_bo: - ret = ttm_bo_reserve_locked(bo, true, true, true, val_seq); + /* already slowpath reserved? */ + if (entry->reserved) + continue; + + ret = ttm_bo_reserve_nolru(bo, true, true, true, val_seq); switch (ret) { case 0: break; case -EBUSY: - ret = ttm_eu_wait_unreserved_locked(list, bo); - if (unlikely(ret != 0)) { - spin_unlock(&glob->lru_lock); - ttm_eu_list_ref_sub(list); - return ret; - } - goto retry_this_bo; + ttm_eu_del_from_lru_locked(list); + spin_unlock(&glob->lru_lock); + ret = ttm_bo_reserve_nolru(bo, true, false, + true, val_seq); + spin_lock(&glob->lru_lock); + if (!ret) + break; + + if (unlikely(ret != -EAGAIN)) + goto err; + + /* fallthrough */ case -EAGAIN: ttm_eu_backoff_reservation_locked(list); + + /* + * temporarily increase sequence number every retry, + * to prevent us from seeing our old reservation + * sequence when someone else reserved the buffer, + * but hasn't updated the seq_valid/seqno members yet. + */ + val_seq = entry->bo->bdev->val_seq++; + spin_unlock(&glob->lru_lock); ttm_eu_list_ref_sub(list); - ret = ttm_bo_wait_unreserved(bo, true); + ret = ttm_bo_reserve_slowpath_nolru(bo, true, val_seq); if (unlikely(ret != 0)) return ret; + spin_lock(&glob->lru_lock); + entry->reserved = true; + if (unlikely(atomic_read(&bo->cpu_writers) > 0)) { + ret = -EBUSY; + goto err; + } goto retry; default: - ttm_eu_backoff_reservation_locked(list); - spin_unlock(&glob->lru_lock); - ttm_eu_list_ref_sub(list); - return ret; + goto err; } entry->reserved = true; if (unlikely(atomic_read(&bo->cpu_writers) > 0)) { - ttm_eu_backoff_reservation_locked(list); - spin_unlock(&glob->lru_lock); - ttm_eu_list_ref_sub(list); - return -EBUSY; + ret = -EBUSY; + goto err; } } @@ -194,6 +196,12 @@ retry_this_bo: ttm_eu_list_ref_sub(list); return 0; + +err: + ttm_eu_backoff_reservation_locked(list); + spin_unlock(&glob->lru_lock); + ttm_eu_list_ref_sub(list); + return ret; } EXPORT_SYMBOL(ttm_eu_reserve_buffers); diff --git a/drivers/gpu/drm/udl/udl_drv.h b/drivers/gpu/drm/udl/udl_drv.h index 87aa5f5d3c88..cc6d90f28c71 100644 --- a/drivers/gpu/drm/udl/udl_drv.h +++ b/drivers/gpu/drm/udl/udl_drv.h @@ -75,6 +75,8 @@ struct udl_framebuffer { struct drm_framebuffer base; struct udl_gem_object *obj; bool active_16; /* active on the 16-bit channel */ + int x1, y1, x2, y2; /* dirty rect */ + spinlock_t dirty_lock; }; #define to_udl_fb(x) container_of(x, struct udl_framebuffer, base) diff --git a/drivers/gpu/drm/udl/udl_fb.c b/drivers/gpu/drm/udl/udl_fb.c index d4ab3beaada0..9f4be3d4a02e 100644 --- a/drivers/gpu/drm/udl/udl_fb.c +++ b/drivers/gpu/drm/udl/udl_fb.c @@ -22,9 +22,9 @@ #include <drm/drm_fb_helper.h> -#define DL_DEFIO_WRITE_DELAY 5 /* fb_deferred_io.delay in jiffies */ +#define DL_DEFIO_WRITE_DELAY (HZ/20) /* fb_deferred_io.delay in jiffies */ -static int fb_defio = 1; /* Optionally enable experimental fb_defio mmap support */ +static int fb_defio = 0; /* Optionally enable experimental fb_defio mmap support */ static int fb_bpp = 16; module_param(fb_bpp, int, S_IWUSR | S_IRUSR | S_IWGRP | S_IRGRP); @@ -153,6 +153,9 @@ int udl_handle_damage(struct udl_framebuffer *fb, int x, int y, struct urb *urb; int aligned_x; int bpp = (fb->base.bits_per_pixel / 8); + int x2, y2; + bool store_for_later = false; + unsigned long flags; if (!fb->active_16) return 0; @@ -169,8 +172,6 @@ int udl_handle_damage(struct udl_framebuffer *fb, int x, int y, } } - start_cycles = get_cycles(); - aligned_x = DL_ALIGN_DOWN(x, sizeof(unsigned long)); width = DL_ALIGN_UP(width + (x-aligned_x), sizeof(unsigned long)); x = aligned_x; @@ -180,19 +181,53 @@ int udl_handle_damage(struct udl_framebuffer *fb, int x, int y, (y + height > fb->base.height)) return -EINVAL; + /* if we are in atomic just store the info + can't test inside spin lock */ + if (in_atomic()) + store_for_later = true; + + x2 = x + width - 1; + y2 = y + height - 1; + + spin_lock_irqsave(&fb->dirty_lock, flags); + + if (fb->y1 < y) + y = fb->y1; + if (fb->y2 > y2) + y2 = fb->y2; + if (fb->x1 < x) + x = fb->x1; + if (fb->x2 > x2) + x2 = fb->x2; + + if (store_for_later) { + fb->x1 = x; + fb->x2 = x2; + fb->y1 = y; + fb->y2 = y2; + spin_unlock_irqrestore(&fb->dirty_lock, flags); + return 0; + } + + fb->x1 = fb->y1 = INT_MAX; + fb->x2 = fb->y2 = 0; + + spin_unlock_irqrestore(&fb->dirty_lock, flags); + start_cycles = get_cycles(); + urb = udl_get_urb(dev); if (!urb) return 0; cmd = urb->transfer_buffer; - for (i = y; i < y + height ; i++) { + for (i = y; i <= y2 ; i++) { const int line_offset = fb->base.pitches[0] * i; const int byte_offset = line_offset + (x * bpp); const int dev_byte_offset = (fb->base.width * bpp * i) + (x * bpp); if (udl_render_hline(dev, bpp, &urb, (char *) fb->obj->vmapping, &cmd, byte_offset, dev_byte_offset, - width * bpp, + (x2 - x + 1) * bpp, &bytes_identical, &bytes_sent)) goto error; } @@ -422,7 +457,6 @@ static void udl_user_framebuffer_destroy(struct drm_framebuffer *fb) static const struct drm_framebuffer_funcs udlfb_funcs = { .destroy = udl_user_framebuffer_destroy, .dirty = udl_user_framebuffer_dirty, - .create_handle = NULL, }; @@ -434,16 +468,18 @@ udl_framebuffer_init(struct drm_device *dev, { int ret; + spin_lock_init(&ufb->dirty_lock); ufb->obj = obj; - ret = drm_framebuffer_init(dev, &ufb->base, &udlfb_funcs); drm_helper_mode_fill_fb_struct(&ufb->base, mode_cmd); + ret = drm_framebuffer_init(dev, &ufb->base, &udlfb_funcs); return ret; } -static int udlfb_create(struct udl_fbdev *ufbdev, +static int udlfb_create(struct drm_fb_helper *helper, struct drm_fb_helper_surface_size *sizes) { + struct udl_fbdev *ufbdev = (struct udl_fbdev *)helper; struct drm_device *dev = ufbdev->helper.dev; struct fb_info *info; struct device *device = &dev->usbdev->dev; @@ -521,27 +557,10 @@ out: return ret; } -static int udl_fb_find_or_create_single(struct drm_fb_helper *helper, - struct drm_fb_helper_surface_size *sizes) -{ - struct udl_fbdev *ufbdev = (struct udl_fbdev *)helper; - int new_fb = 0; - int ret; - - if (!helper->fb) { - ret = udlfb_create(ufbdev, sizes); - if (ret) - return ret; - - new_fb = 1; - } - return new_fb; -} - static struct drm_fb_helper_funcs udl_fb_helper_funcs = { .gamma_set = udl_crtc_fb_gamma_set, .gamma_get = udl_crtc_fb_gamma_get, - .fb_probe = udl_fb_find_or_create_single, + .fb_probe = udlfb_create, }; static void udl_fbdev_destroy(struct drm_device *dev, @@ -556,6 +575,7 @@ static void udl_fbdev_destroy(struct drm_device *dev, framebuffer_release(info); } drm_fb_helper_fini(&ufbdev->helper); + drm_framebuffer_unregister_private(&ufbdev->ufb.base); drm_framebuffer_cleanup(&ufbdev->ufb.base); drm_gem_object_unreference_unlocked(&ufbdev->ufb.obj->base); } @@ -583,6 +603,10 @@ int udl_fbdev_init(struct drm_device *dev) } drm_fb_helper_single_add_all_connectors(&ufbdev->helper); + + /* disable all the possible outputs/crtcs before entering KMS mode */ + drm_helper_disable_unused_functions(dev); + drm_fb_helper_initial_config(&ufbdev->helper, bpp_sel); return 0; } diff --git a/drivers/gpu/drm/udl/udl_transfer.c b/drivers/gpu/drm/udl/udl_transfer.c index 142fee5f983f..f343db73e095 100644 --- a/drivers/gpu/drm/udl/udl_transfer.c +++ b/drivers/gpu/drm/udl/udl_transfer.c @@ -75,15 +75,19 @@ static int udl_trim_hline(const u8 *bback, const u8 **bfront, int *width_bytes) } #endif -static inline u16 pixel32_to_be16p(const uint8_t *pixel) +static inline u16 pixel32_to_be16(const uint32_t pixel) { - uint32_t pix = *(uint32_t *)pixel; - u16 retval; + return (((pixel >> 3) & 0x001f) | + ((pixel >> 5) & 0x07e0) | + ((pixel >> 8) & 0xf800)); +} - retval = (((pix >> 3) & 0x001f) | - ((pix >> 5) & 0x07e0) | - ((pix >> 8) & 0xf800)); - return retval; +static bool pixel_repeats(const void *pixel, const uint32_t repeat, int bpp) +{ + if (bpp == 2) + return *(const uint16_t *)pixel == repeat; + else + return *(const uint32_t *)pixel == repeat; } /* @@ -152,29 +156,33 @@ static void udl_compress_hline16( prefetch_range((void *) pixel, (cmd_pixel_end - pixel) * bpp); while (pixel < cmd_pixel_end) { - const u8 * const repeating_pixel = pixel; - - if (bpp == 2) - *(uint16_t *)cmd = cpu_to_be16p((uint16_t *)pixel); - else if (bpp == 4) - *(uint16_t *)cmd = cpu_to_be16(pixel32_to_be16p(pixel)); + const u8 *const start = pixel; + u32 repeating_pixel; + + if (bpp == 2) { + repeating_pixel = *(uint16_t *)pixel; + *(uint16_t *)cmd = cpu_to_be16(repeating_pixel); + } else { + repeating_pixel = *(uint32_t *)pixel; + *(uint16_t *)cmd = cpu_to_be16(pixel32_to_be16(repeating_pixel)); + } cmd += 2; pixel += bpp; if (unlikely((pixel < cmd_pixel_end) && - (!memcmp(pixel, repeating_pixel, bpp)))) { + (pixel_repeats(pixel, repeating_pixel, bpp)))) { /* go back and fill in raw pixel count */ - *raw_pixels_count_byte = (((repeating_pixel - + *raw_pixels_count_byte = (((start - raw_pixel_start) / bpp) + 1) & 0xFF; - while ((pixel < cmd_pixel_end) - && (!memcmp(pixel, repeating_pixel, bpp))) { + while ((pixel < cmd_pixel_end) && + (pixel_repeats(pixel, repeating_pixel, bpp))) { pixel += bpp; } /* immediately after raw data is repeat byte */ - *cmd++ = (((pixel - repeating_pixel) / bpp) - 1) & 0xFF; + *cmd++ = (((pixel - start) / bpp) - 1) & 0xFF; /* Then start another raw pixel span */ raw_pixel_start = pixel; @@ -223,6 +231,8 @@ int udl_render_hline(struct drm_device *dev, int bpp, struct urb **urb_ptr, u8 *cmd = *urb_buf_ptr; u8 *cmd_end = (u8 *) urb->transfer_buffer + urb->transfer_buffer_length; + BUG_ON(!(bpp == 2 || bpp == 4)); + line_start = (u8 *) (front + byte_offset); next_pixel = line_start; line_end = next_pixel + byte_width; diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_drv.c b/drivers/gpu/drm/vmwgfx/vmwgfx_drv.c index 161f8b2549aa..07dfd823cc30 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_drv.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_drv.c @@ -829,7 +829,7 @@ static void vmw_lastclose(struct drm_device *dev) list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { set.crtc = crtc; - ret = crtc->funcs->set_config(&set); + ret = drm_mode_set_config_internal(&set); WARN_ON(ret != 0); } diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_ioctl.c b/drivers/gpu/drm/vmwgfx/vmwgfx_ioctl.c index d9fbbe191071..c509d40c4897 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_ioctl.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_ioctl.c @@ -131,7 +131,7 @@ int vmw_present_ioctl(struct drm_device *dev, void *data, struct vmw_master *vmaster = vmw_master(file_priv->master); struct drm_vmw_rect __user *clips_ptr; struct drm_vmw_rect *clips = NULL; - struct drm_mode_object *obj; + struct drm_framebuffer *fb; struct vmw_framebuffer *vfb; struct vmw_resource *res; uint32_t num_clips; @@ -163,19 +163,15 @@ int vmw_present_ioctl(struct drm_device *dev, void *data, goto out_no_copy; } - ret = mutex_lock_interruptible(&dev->mode_config.mutex); - if (unlikely(ret != 0)) { - ret = -ERESTARTSYS; - goto out_no_mode_mutex; - } + drm_modeset_lock_all(dev); - obj = drm_mode_object_find(dev, arg->fb_id, DRM_MODE_OBJECT_FB); - if (!obj) { + fb = drm_framebuffer_lookup(dev, arg->fb_id); + if (!fb) { DRM_ERROR("Invalid framebuffer id.\n"); ret = -EINVAL; goto out_no_fb; } - vfb = vmw_framebuffer_to_vfb(obj_to_fb(obj)); + vfb = vmw_framebuffer_to_vfb(fb); ret = ttm_read_lock(&vmaster->lock, true); if (unlikely(ret != 0)) @@ -199,9 +195,9 @@ int vmw_present_ioctl(struct drm_device *dev, void *data, out_no_surface: ttm_read_unlock(&vmaster->lock); out_no_ttm_lock: + drm_framebuffer_unreference(fb); out_no_fb: - mutex_unlock(&dev->mode_config.mutex); -out_no_mode_mutex: + drm_modeset_unlock_all(dev); out_no_copy: kfree(clips); out_clips: @@ -220,7 +216,7 @@ int vmw_present_readback_ioctl(struct drm_device *dev, void *data, struct vmw_master *vmaster = vmw_master(file_priv->master); struct drm_vmw_rect __user *clips_ptr; struct drm_vmw_rect *clips = NULL; - struct drm_mode_object *obj; + struct drm_framebuffer *fb; struct vmw_framebuffer *vfb; uint32_t num_clips; int ret; @@ -251,24 +247,20 @@ int vmw_present_readback_ioctl(struct drm_device *dev, void *data, goto out_no_copy; } - ret = mutex_lock_interruptible(&dev->mode_config.mutex); - if (unlikely(ret != 0)) { - ret = -ERESTARTSYS; - goto out_no_mode_mutex; - } + drm_modeset_lock_all(dev); - obj = drm_mode_object_find(dev, arg->fb_id, DRM_MODE_OBJECT_FB); - if (!obj) { + fb = drm_framebuffer_lookup(dev, arg->fb_id); + if (!fb) { DRM_ERROR("Invalid framebuffer id.\n"); ret = -EINVAL; goto out_no_fb; } - vfb = vmw_framebuffer_to_vfb(obj_to_fb(obj)); + vfb = vmw_framebuffer_to_vfb(fb); if (!vfb->dmabuf) { DRM_ERROR("Framebuffer not dmabuf backed.\n"); ret = -EINVAL; - goto out_no_fb; + goto out_no_ttm_lock; } ret = ttm_read_lock(&vmaster->lock, true); @@ -281,9 +273,9 @@ int vmw_present_readback_ioctl(struct drm_device *dev, void *data, ttm_read_unlock(&vmaster->lock); out_no_ttm_lock: + drm_framebuffer_unreference(fb); out_no_fb: - mutex_unlock(&dev->mode_config.mutex); -out_no_mode_mutex: + drm_modeset_unlock_all(dev); out_no_copy: kfree(clips); out_clips: diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c b/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c index 54743943d8b3..3e3c7ab33ca2 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c @@ -180,16 +180,29 @@ int vmw_du_crtc_cursor_set(struct drm_crtc *crtc, struct drm_file *file_priv, struct vmw_dma_buffer *dmabuf = NULL; int ret; + /* + * FIXME: Unclear whether there's any global state touched by the + * cursor_set function, especially vmw_cursor_update_position looks + * suspicious. For now take the easy route and reacquire all locks. We + * can do this since the caller in the drm core doesn't check anything + * which is protected by any looks. + */ + mutex_unlock(&crtc->mutex); + drm_modeset_lock_all(dev_priv->dev); + /* A lot of the code assumes this */ - if (handle && (width != 64 || height != 64)) - return -EINVAL; + if (handle && (width != 64 || height != 64)) { + ret = -EINVAL; + goto out; + } if (handle) { ret = vmw_user_lookup_handle(dev_priv, tfile, handle, &surface, &dmabuf); if (ret) { DRM_ERROR("failed to find surface or dmabuf: %i\n", ret); - return -EINVAL; + ret = -EINVAL; + goto out; } } @@ -197,7 +210,8 @@ int vmw_du_crtc_cursor_set(struct drm_crtc *crtc, struct drm_file *file_priv, if (surface && !surface->snooper.image) { DRM_ERROR("surface not suitable for cursor\n"); vmw_surface_unreference(&surface); - return -EINVAL; + ret = -EINVAL; + goto out; } /* takedown old cursor */ @@ -225,14 +239,20 @@ int vmw_du_crtc_cursor_set(struct drm_crtc *crtc, struct drm_file *file_priv, du->hotspot_x, du->hotspot_y); } else { vmw_cursor_update_position(dev_priv, false, 0, 0); - return 0; + ret = 0; + goto out; } vmw_cursor_update_position(dev_priv, true, du->cursor_x + du->hotspot_x, du->cursor_y + du->hotspot_y); - return 0; + ret = 0; +out: + drm_modeset_unlock_all(dev_priv->dev); + mutex_lock(&crtc->mutex); + + return ret; } int vmw_du_crtc_cursor_move(struct drm_crtc *crtc, int x, int y) @@ -244,10 +264,23 @@ int vmw_du_crtc_cursor_move(struct drm_crtc *crtc, int x, int y) du->cursor_x = x + crtc->x; du->cursor_y = y + crtc->y; + /* + * FIXME: Unclear whether there's any global state touched by the + * cursor_set function, especially vmw_cursor_update_position looks + * suspicious. For now take the easy route and reacquire all locks. We + * can do this since the caller in the drm core doesn't check anything + * which is protected by any looks. + */ + mutex_unlock(&crtc->mutex); + drm_modeset_lock_all(dev_priv->dev); + vmw_cursor_update_position(dev_priv, shown, du->cursor_x + du->hotspot_x, du->cursor_y + du->hotspot_y); + drm_modeset_unlock_all(dev_priv->dev); + mutex_lock(&crtc->mutex); + return 0; } @@ -373,16 +406,6 @@ void vmw_kms_cursor_post_execbuf(struct vmw_private *dev_priv) * Generic framebuffer code */ -int vmw_framebuffer_create_handle(struct drm_framebuffer *fb, - struct drm_file *file_priv, - unsigned int *handle) -{ - if (handle) - *handle = 0; - - return 0; -} - /* * Surface framebuffer code */ @@ -610,7 +633,6 @@ int vmw_framebuffer_surface_dirty(struct drm_framebuffer *framebuffer, static struct drm_framebuffer_funcs vmw_framebuffer_surface_funcs = { .destroy = vmw_framebuffer_surface_destroy, .dirty = vmw_framebuffer_surface_dirty, - .create_handle = vmw_framebuffer_create_handle, }; static int vmw_kms_new_framebuffer_surface(struct vmw_private *dev_priv, @@ -681,14 +703,10 @@ static int vmw_kms_new_framebuffer_surface(struct vmw_private *dev_priv, goto out_err1; } - ret = drm_framebuffer_init(dev, &vfbs->base.base, - &vmw_framebuffer_surface_funcs); - if (ret) - goto out_err2; - if (!vmw_surface_reference(surface)) { DRM_ERROR("failed to reference surface %p\n", surface); - goto out_err3; + ret = -EINVAL; + goto out_err2; } /* XXX get the first 3 from the surface info */ @@ -707,10 +725,15 @@ static int vmw_kms_new_framebuffer_surface(struct vmw_private *dev_priv, *out = &vfbs->base; + ret = drm_framebuffer_init(dev, &vfbs->base.base, + &vmw_framebuffer_surface_funcs); + if (ret) + goto out_err3; + return 0; out_err3: - drm_framebuffer_cleanup(&vfbs->base.base); + vmw_surface_unreference(&surface); out_err2: kfree(vfbs); out_err1: @@ -960,7 +983,6 @@ int vmw_framebuffer_dmabuf_dirty(struct drm_framebuffer *framebuffer, static struct drm_framebuffer_funcs vmw_framebuffer_dmabuf_funcs = { .destroy = vmw_framebuffer_dmabuf_destroy, .dirty = vmw_framebuffer_dmabuf_dirty, - .create_handle = vmw_framebuffer_create_handle, }; /** @@ -1053,14 +1075,10 @@ static int vmw_kms_new_framebuffer_dmabuf(struct vmw_private *dev_priv, goto out_err1; } - ret = drm_framebuffer_init(dev, &vfbd->base.base, - &vmw_framebuffer_dmabuf_funcs); - if (ret) - goto out_err2; - if (!vmw_dmabuf_reference(dmabuf)) { DRM_ERROR("failed to reference dmabuf %p\n", dmabuf); - goto out_err3; + ret = -EINVAL; + goto out_err2; } vfbd->base.base.bits_per_pixel = mode_cmd->bpp; @@ -1077,10 +1095,15 @@ static int vmw_kms_new_framebuffer_dmabuf(struct vmw_private *dev_priv, vfbd->base.user_handle = mode_cmd->handle; *out = &vfbd->base; + ret = drm_framebuffer_init(dev, &vfbd->base.base, + &vmw_framebuffer_dmabuf_funcs); + if (ret) + goto out_err3; + return 0; out_err3: - drm_framebuffer_cleanup(&vfbd->base.base); + vmw_dmabuf_unreference(&dmabuf); out_err2: kfree(vfbd); out_err1: diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_resource.c b/drivers/gpu/drm/vmwgfx/vmwgfx_resource.c index e01a17b407b2..16556170fb32 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_resource.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_resource.c @@ -959,13 +959,13 @@ void vmw_resource_unreserve(struct vmw_resource *res, if (new_backup && new_backup != res->backup) { if (res->backup) { - BUG_ON(atomic_read(&res->backup->base.reserved) == 0); + BUG_ON(!ttm_bo_is_reserved(&res->backup->base)); list_del_init(&res->mob_head); vmw_dmabuf_unreference(&res->backup); } res->backup = vmw_dmabuf_reference(new_backup); - BUG_ON(atomic_read(&new_backup->base.reserved) == 0); + BUG_ON(!ttm_bo_is_reserved(&new_backup->base)); list_add_tail(&res->mob_head, &new_backup->res_list); } if (new_backup) diff --git a/drivers/gpu/stub/Kconfig b/drivers/gpu/stub/Kconfig deleted file mode 100644 index 419917955bf6..000000000000 --- a/drivers/gpu/stub/Kconfig +++ /dev/null @@ -1,18 +0,0 @@ -config STUB_POULSBO - tristate "Intel GMA500 Stub Driver" - depends on PCI - depends on NET # for THERMAL - # Poulsbo stub depends on ACPI_VIDEO when ACPI is enabled - # but for select to work, need to select ACPI_VIDEO's dependencies, ick - select BACKLIGHT_CLASS_DEVICE if ACPI - select VIDEO_OUTPUT_CONTROL if ACPI - select INPUT if ACPI - select ACPI_VIDEO if ACPI - select THERMAL if ACPI - help - Choose this option if you have a system that has Intel GMA500 - (Poulsbo) integrated graphics. If M is selected, the module will - be called Poulsbo. This driver is a stub driver for Poulsbo that - will call poulsbo.ko to enable the acpi backlight control sysfs - entry file because there have no poulsbo native driver can support - intel opregion. diff --git a/drivers/gpu/stub/Makefile b/drivers/gpu/stub/Makefile deleted file mode 100644 index cd940cc9d36d..000000000000 --- a/drivers/gpu/stub/Makefile +++ /dev/null @@ -1 +0,0 @@ -obj-$(CONFIG_STUB_POULSBO) += poulsbo.o diff --git a/drivers/gpu/stub/poulsbo.c b/drivers/gpu/stub/poulsbo.c deleted file mode 100644 index 7edfd27b8dee..000000000000 --- a/drivers/gpu/stub/poulsbo.c +++ /dev/null @@ -1,64 +0,0 @@ -/* - * Intel Poulsbo Stub driver - * - * Copyright (C) 2010 Novell <jlee@novell.com> - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 as published by - * the Free Software Foundation. - * - */ - -#include <linux/module.h> -#include <linux/pci.h> -#include <linux/acpi.h> -#include <acpi/video.h> - -#define DRIVER_NAME "poulsbo" - -enum { - CHIP_PSB_8108 = 0, - CHIP_PSB_8109 = 1, -}; - -static DEFINE_PCI_DEVICE_TABLE(pciidlist) = { - {0x8086, 0x8108, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_PSB_8108}, \ - {0x8086, 0x8109, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_PSB_8109}, \ - {0, 0, 0} -}; - -static int poulsbo_probe(struct pci_dev *pdev, const struct pci_device_id *id) -{ - return acpi_video_register(); -} - -static void poulsbo_remove(struct pci_dev *pdev) -{ - acpi_video_unregister(); -} - -static struct pci_driver poulsbo_driver = { - .name = DRIVER_NAME, - .id_table = pciidlist, - .probe = poulsbo_probe, - .remove = poulsbo_remove, -}; - -static int __init poulsbo_init(void) -{ - return pci_register_driver(&poulsbo_driver); -} - -static void __exit poulsbo_exit(void) -{ - pci_unregister_driver(&poulsbo_driver); -} - -module_init(poulsbo_init); -module_exit(poulsbo_exit); - -MODULE_AUTHOR("Lee, Chun-Yi <jlee@novell.com>"); -MODULE_DESCRIPTION("Poulsbo Stub Driver"); -MODULE_LICENSE("GPL"); - -MODULE_DEVICE_TABLE(pci, pciidlist); diff --git a/drivers/gpu/vga/vga_switcheroo.c b/drivers/gpu/vga/vga_switcheroo.c index fa60add0ff63..cf787e1d9322 100644 --- a/drivers/gpu/vga/vga_switcheroo.c +++ b/drivers/gpu/vga/vga_switcheroo.c @@ -25,6 +25,7 @@ #include <linux/fb.h> #include <linux/pci.h> +#include <linux/console.h> #include <linux/vga_switcheroo.h> #include <linux/vgaarb.h> @@ -337,8 +338,10 @@ static int vga_switchto_stage2(struct vga_switcheroo_client *new_client) if (new_client->fb_info) { struct fb_event event; + console_lock(); event.info = new_client->fb_info; fb_notifier_call_chain(FB_EVENT_REMAP_ALL_CONSOLE, &event); + console_unlock(); } ret = vgasr_priv.handler->switchto(new_client->id); diff --git a/drivers/hid/hid-ids.h b/drivers/hid/hid-ids.h index 4dfa605e2d14..34e25471aeaa 100644 --- a/drivers/hid/hid-ids.h +++ b/drivers/hid/hid-ids.h @@ -306,6 +306,9 @@ #define USB_VENDOR_ID_EZKEY 0x0518 #define USB_DEVICE_ID_BTC_8193 0x0002 +#define USB_VENDOR_ID_FORMOSA 0x147a +#define USB_DEVICE_ID_FORMOSA_IR_RECEIVER 0xe03e + #define USB_VENDOR_ID_FREESCALE 0x15A2 #define USB_DEVICE_ID_FREESCALE_MX28 0x004F diff --git a/drivers/hid/i2c-hid/i2c-hid.c b/drivers/hid/i2c-hid/i2c-hid.c index 12e4fdc810bf..e766b5614ef5 100644 --- a/drivers/hid/i2c-hid/i2c-hid.c +++ b/drivers/hid/i2c-hid/i2c-hid.c @@ -540,13 +540,24 @@ static int i2c_hid_output_raw_report(struct hid_device *hid, __u8 *buf, { struct i2c_client *client = hid->driver_data; int report_id = buf[0]; + int ret; if (report_type == HID_INPUT_REPORT) return -EINVAL; - return i2c_hid_set_report(client, + if (report_id) { + buf++; + count--; + } + + ret = i2c_hid_set_report(client, report_type == HID_FEATURE_REPORT ? 0x03 : 0x02, report_id, buf, count); + + if (report_id && ret >= 0) + ret++; /* add report_id to the number of transfered bytes */ + + return ret; } static int i2c_hid_parse(struct hid_device *hid) diff --git a/drivers/hid/usbhid/hid-quirks.c b/drivers/hid/usbhid/hid-quirks.c index ac9e35228254..e0e6abf1cd3b 100644 --- a/drivers/hid/usbhid/hid-quirks.c +++ b/drivers/hid/usbhid/hid-quirks.c @@ -70,6 +70,7 @@ static const struct hid_blacklist { { USB_VENDOR_ID_CH, USB_DEVICE_ID_CH_AXIS_295, HID_QUIRK_NOGET }, { USB_VENDOR_ID_DMI, USB_DEVICE_ID_DMI_ENC, HID_QUIRK_NOGET }, { USB_VENDOR_ID_ELO, USB_DEVICE_ID_ELO_TS2700, HID_QUIRK_NOGET }, + { USB_VENDOR_ID_FORMOSA, USB_DEVICE_ID_FORMOSA_IR_RECEIVER, HID_QUIRK_NO_INIT_REPORTS }, { USB_VENDOR_ID_FREESCALE, USB_DEVICE_ID_FREESCALE_MX28, HID_QUIRK_NOGET }, { USB_VENDOR_ID_MGE, USB_DEVICE_ID_MGE_UPS, HID_QUIRK_NOGET }, { USB_VENDOR_ID_NOVATEK, USB_DEVICE_ID_NOVATEK_MOUSE, HID_QUIRK_NO_INIT_REPORTS }, diff --git a/drivers/hv/hv_balloon.c b/drivers/hv/hv_balloon.c index f6c0011a0337..dd289fd179ca 100644 --- a/drivers/hv/hv_balloon.c +++ b/drivers/hv/hv_balloon.c @@ -403,7 +403,7 @@ struct dm_info_header { */ struct dm_info_msg { - struct dm_info_header header; + struct dm_header hdr; __u32 reserved; __u32 info_size; __u8 info[]; @@ -503,13 +503,17 @@ static void hot_add_req(struct hv_dynmem_device *dm, struct dm_hot_add *msg) static void process_info(struct hv_dynmem_device *dm, struct dm_info_msg *msg) { - switch (msg->header.type) { + struct dm_info_header *info_hdr; + + info_hdr = (struct dm_info_header *)msg->info; + + switch (info_hdr->type) { case INFO_TYPE_MAX_PAGE_CNT: pr_info("Received INFO_TYPE_MAX_PAGE_CNT\n"); - pr_info("Data Size is %d\n", msg->header.data_size); + pr_info("Data Size is %d\n", info_hdr->data_size); break; default: - pr_info("Received Unknown type: %d\n", msg->header.type); + pr_info("Received Unknown type: %d\n", info_hdr->type); } } @@ -879,7 +883,7 @@ static int balloon_probe(struct hv_device *dev, balloon_onchannelcallback, dev); if (ret) - return ret; + goto probe_error0; dm_device.dev = dev; dm_device.state = DM_INITIALIZING; @@ -891,7 +895,7 @@ static int balloon_probe(struct hv_device *dev, kthread_run(dm_thread_func, &dm_device, "hv_balloon"); if (IS_ERR(dm_device.thread)) { ret = PTR_ERR(dm_device.thread); - goto probe_error0; + goto probe_error1; } hv_set_drvdata(dev, &dm_device); @@ -914,12 +918,12 @@ static int balloon_probe(struct hv_device *dev, VM_PKT_DATA_INBAND, VMBUS_DATA_PACKET_FLAG_COMPLETION_REQUESTED); if (ret) - goto probe_error1; + goto probe_error2; t = wait_for_completion_timeout(&dm_device.host_event, 5*HZ); if (t == 0) { ret = -ETIMEDOUT; - goto probe_error1; + goto probe_error2; } /* @@ -928,7 +932,7 @@ static int balloon_probe(struct hv_device *dev, */ if (dm_device.state == DM_INIT_ERROR) { ret = -ETIMEDOUT; - goto probe_error1; + goto probe_error2; } /* * Now submit our capabilities to the host. @@ -961,12 +965,12 @@ static int balloon_probe(struct hv_device *dev, VM_PKT_DATA_INBAND, VMBUS_DATA_PACKET_FLAG_COMPLETION_REQUESTED); if (ret) - goto probe_error1; + goto probe_error2; t = wait_for_completion_timeout(&dm_device.host_event, 5*HZ); if (t == 0) { ret = -ETIMEDOUT; - goto probe_error1; + goto probe_error2; } /* @@ -975,18 +979,20 @@ static int balloon_probe(struct hv_device *dev, */ if (dm_device.state == DM_INIT_ERROR) { ret = -ETIMEDOUT; - goto probe_error1; + goto probe_error2; } dm_device.state = DM_INITIALIZED; return 0; -probe_error1: +probe_error2: kthread_stop(dm_device.thread); -probe_error0: +probe_error1: vmbus_close(dev->channel); +probe_error0: + kfree(send_buffer); return ret; } @@ -999,6 +1005,7 @@ static int balloon_remove(struct hv_device *dev) vmbus_close(dev->channel); kthread_stop(dm->thread); + kfree(send_buffer); return 0; } diff --git a/drivers/hwmon/vexpress.c b/drivers/hwmon/vexpress.c index 86d7f6d858b1..d867e6bb2be1 100644 --- a/drivers/hwmon/vexpress.c +++ b/drivers/hwmon/vexpress.c @@ -19,6 +19,7 @@ #include <linux/hwmon.h> #include <linux/hwmon-sysfs.h> #include <linux/module.h> +#include <linux/of.h> #include <linux/of_device.h> #include <linux/platform_device.h> #include <linux/vexpress.h> diff --git a/drivers/i2c/busses/i2c-designware-core.c b/drivers/i2c/busses/i2c-designware-core.c index cbba7db9ad59..f5258c205de5 100644 --- a/drivers/i2c/busses/i2c-designware-core.c +++ b/drivers/i2c/busses/i2c-designware-core.c @@ -34,6 +34,7 @@ #include <linux/io.h> #include <linux/pm_runtime.h> #include <linux/delay.h> +#include <linux/module.h> #include "i2c-designware-core.h" /* @@ -725,3 +726,6 @@ u32 i2c_dw_read_comp_param(struct dw_i2c_dev *dev) return dw_readl(dev, DW_IC_COMP_PARAM_1); } EXPORT_SYMBOL_GPL(i2c_dw_read_comp_param); + +MODULE_DESCRIPTION("Synopsys DesignWare I2C bus adapter core"); +MODULE_LICENSE("GPL"); diff --git a/drivers/i2c/busses/i2c-mxs.c b/drivers/i2c/busses/i2c-mxs.c index 1b1a936eccc9..d6abaf2cf2e3 100644 --- a/drivers/i2c/busses/i2c-mxs.c +++ b/drivers/i2c/busses/i2c-mxs.c @@ -127,7 +127,7 @@ struct mxs_i2c_dev { struct device *dev; void __iomem *regs; struct completion cmd_complete; - u32 cmd_err; + int cmd_err; struct i2c_adapter adapter; const struct mxs_i2c_speed_config *speed; @@ -316,7 +316,7 @@ static int mxs_i2c_xfer_msg(struct i2c_adapter *adap, struct i2c_msg *msg, if (msg->len == 0) return -EINVAL; - init_completion(&i2c->cmd_complete); + INIT_COMPLETION(i2c->cmd_complete); i2c->cmd_err = 0; ret = mxs_i2c_dma_setup_xfer(adap, msg, flags); @@ -473,6 +473,8 @@ static int mxs_i2c_probe(struct platform_device *pdev) i2c->dev = dev; i2c->speed = &mxs_i2c_95kHz_config; + init_completion(&i2c->cmd_complete); + if (dev->of_node) { err = mxs_i2c_get_ofdata(i2c); if (err) diff --git a/drivers/i2c/busses/i2c-omap.c b/drivers/i2c/busses/i2c-omap.c index 20d41bfa7c19..4cc2f0528c88 100644 --- a/drivers/i2c/busses/i2c-omap.c +++ b/drivers/i2c/busses/i2c-omap.c @@ -803,7 +803,7 @@ static int errata_omap3_i462(struct omap_i2c_dev *dev) if (stat & OMAP_I2C_STAT_AL) { dev_err(dev->dev, "Arbitration lost\n"); dev->cmd_err |= OMAP_I2C_STAT_AL; - omap_i2c_ack_stat(dev, OMAP_I2C_STAT_NACK); + omap_i2c_ack_stat(dev, OMAP_I2C_STAT_AL); } return -EIO; @@ -963,7 +963,7 @@ omap_i2c_isr_thread(int this_irq, void *dev_id) i2c_omap_errata_i207(dev, stat); omap_i2c_ack_stat(dev, OMAP_I2C_STAT_RDR); - break; + continue; } if (stat & OMAP_I2C_STAT_RRDY) { @@ -989,7 +989,7 @@ omap_i2c_isr_thread(int this_irq, void *dev_id) break; omap_i2c_ack_stat(dev, OMAP_I2C_STAT_XDR); - break; + continue; } if (stat & OMAP_I2C_STAT_XRDY) { diff --git a/drivers/i2c/busses/i2c-sirf.c b/drivers/i2c/busses/i2c-sirf.c index 3f1818b87974..e03381aee34f 100644 --- a/drivers/i2c/busses/i2c-sirf.c +++ b/drivers/i2c/busses/i2c-sirf.c @@ -12,6 +12,7 @@ #include <linux/slab.h> #include <linux/platform_device.h> #include <linux/i2c.h> +#include <linux/of_i2c.h> #include <linux/clk.h> #include <linux/err.h> #include <linux/io.h> @@ -328,6 +329,7 @@ static int i2c_sirfsoc_probe(struct platform_device *pdev) adap->algo = &i2c_sirfsoc_algo; adap->algo_data = siic; + adap->dev.of_node = pdev->dev.of_node; adap->dev.parent = &pdev->dev; adap->nr = pdev->id; @@ -371,6 +373,8 @@ static int i2c_sirfsoc_probe(struct platform_device *pdev) clk_disable(clk); + of_i2c_register_devices(adap); + dev_info(&pdev->dev, " I2C adapter ready to operate\n"); return 0; diff --git a/drivers/i2c/muxes/i2c-mux-pinctrl.c b/drivers/i2c/muxes/i2c-mux-pinctrl.c index 1e44d04d1b22..a43c0ce5e3d8 100644 --- a/drivers/i2c/muxes/i2c-mux-pinctrl.c +++ b/drivers/i2c/muxes/i2c-mux-pinctrl.c @@ -167,7 +167,7 @@ static int i2c_mux_pinctrl_probe(struct platform_device *pdev) } mux->busses = devm_kzalloc(&pdev->dev, - sizeof(mux->busses) * mux->pdata->bus_count, + sizeof(*mux->busses) * mux->pdata->bus_count, GFP_KERNEL); if (!mux->busses) { dev_err(&pdev->dev, "Cannot allocate busses\n"); diff --git a/drivers/idle/intel_idle.c b/drivers/idle/intel_idle.c index 4ba384f1ab54..2df9414a72f7 100644 --- a/drivers/idle/intel_idle.c +++ b/drivers/idle/intel_idle.c @@ -448,8 +448,6 @@ static int intel_idle_probe(void) else on_each_cpu(__setup_broadcast_timer, (void *)true, 1); - register_cpu_notifier(&cpu_hotplug_notifier); - pr_debug(PREFIX "v" INTEL_IDLE_VERSION " model 0x%X\n", boot_cpu_data.x86_model); @@ -612,6 +610,7 @@ static int __init intel_idle_init(void) return retval; } } + register_cpu_notifier(&cpu_hotplug_notifier); return 0; } diff --git a/drivers/iio/accel/Kconfig b/drivers/iio/accel/Kconfig index fe4bcd7c5b12..05e996fafc9d 100644 --- a/drivers/iio/accel/Kconfig +++ b/drivers/iio/accel/Kconfig @@ -8,6 +8,7 @@ config HID_SENSOR_ACCEL_3D select IIO_BUFFER select IIO_TRIGGERED_BUFFER select HID_SENSOR_IIO_COMMON + select HID_SENSOR_IIO_TRIGGER tristate "HID Accelerometers 3D" help Say yes here to build support for the HID SENSOR diff --git a/drivers/iio/adc/ad7266.c b/drivers/iio/adc/ad7266.c index 4a5f639bc684..bbad9b94cd75 100644 --- a/drivers/iio/adc/ad7266.c +++ b/drivers/iio/adc/ad7266.c @@ -411,7 +411,11 @@ static int ad7266_probe(struct spi_device *spi) if (ret) goto error_put_reg; - st->vref_uv = regulator_get_voltage(st->reg); + ret = regulator_get_voltage(st->reg); + if (ret < 0) + goto error_disable_reg; + + st->vref_uv = ret; } else { /* Use internal reference */ st->vref_uv = 2500000; diff --git a/drivers/iio/adc/at91_adc.c b/drivers/iio/adc/at91_adc.c index 04b013561f0f..a526c0e3aaa8 100644 --- a/drivers/iio/adc/at91_adc.c +++ b/drivers/iio/adc/at91_adc.c @@ -80,7 +80,7 @@ static irqreturn_t at91_adc_trigger_handler(int irq, void *p) *timestamp = pf->timestamp; } - iio_push_to_buffers(indio_dev, (u8 *)st->buffer); + iio_push_to_buffers(idev, (u8 *)st->buffer); iio_trigger_notify_done(idev->trig); diff --git a/drivers/iio/adc/max1363.c b/drivers/iio/adc/max1363.c index b5669be6f396..03b25b3dc71e 100644 --- a/drivers/iio/adc/max1363.c +++ b/drivers/iio/adc/max1363.c @@ -1605,19 +1605,20 @@ static int max1363_probe(struct i2c_client *client, return 0; error_free_irq: - free_irq(st->client->irq, indio_dev); + if (client->irq) + free_irq(st->client->irq, indio_dev); error_uninit_buffer: iio_buffer_unregister(indio_dev); error_cleanup_buffer: max1363_buffer_cleanup(indio_dev); error_free_available_scan_masks: kfree(indio_dev->available_scan_masks); -error_unregister_map: - iio_map_array_unregister(indio_dev, client->dev.platform_data); error_disable_reg: regulator_disable(st->reg); error_put_reg: regulator_put(st->reg); +error_unregister_map: + iio_map_array_unregister(indio_dev, client->dev.platform_data); error_free_device: iio_device_free(indio_dev); error_out: @@ -1635,10 +1636,8 @@ static int max1363_remove(struct i2c_client *client) iio_buffer_unregister(indio_dev); max1363_buffer_cleanup(indio_dev); kfree(indio_dev->available_scan_masks); - if (!IS_ERR(st->reg)) { - regulator_disable(st->reg); - regulator_put(st->reg); - } + regulator_disable(st->reg); + regulator_put(st->reg); iio_map_array_unregister(indio_dev, client->dev.platform_data); iio_device_free(indio_dev); diff --git a/drivers/iio/common/hid-sensors/Kconfig b/drivers/iio/common/hid-sensors/Kconfig index ae10778da7aa..1178121b55b0 100644 --- a/drivers/iio/common/hid-sensors/Kconfig +++ b/drivers/iio/common/hid-sensors/Kconfig @@ -6,7 +6,7 @@ menu "Hid Sensor IIO Common" config HID_SENSOR_IIO_COMMON tristate "Common modules for all HID Sensor IIO drivers" depends on HID_SENSOR_HUB - select IIO_TRIGGER if IIO_BUFFER + select HID_SENSOR_IIO_TRIGGER if IIO_BUFFER help Say yes here to build support for HID sensor to use HID sensor common processing for attributes and IIO triggers. @@ -14,6 +14,17 @@ config HID_SENSOR_IIO_COMMON HID sensor drivers, this module contains processing for those attributes. +config HID_SENSOR_IIO_TRIGGER + tristate "Common module (trigger) for all HID Sensor IIO drivers" + depends on HID_SENSOR_HUB && HID_SENSOR_IIO_COMMON + select IIO_TRIGGER + help + Say yes here to build trigger support for HID sensors. + Triggers will be send if all requested attributes were read. + + If this driver is compiled as a module, it will be named + hid-sensor-trigger. + config HID_SENSOR_ENUM_BASE_QUIRKS bool "ENUM base quirks for HID Sensor IIO drivers" depends on HID_SENSOR_IIO_COMMON diff --git a/drivers/iio/common/hid-sensors/Makefile b/drivers/iio/common/hid-sensors/Makefile index 1f463e00c242..22e7c5a82325 100644 --- a/drivers/iio/common/hid-sensors/Makefile +++ b/drivers/iio/common/hid-sensors/Makefile @@ -3,4 +3,5 @@ # obj-$(CONFIG_HID_SENSOR_IIO_COMMON) += hid-sensor-iio-common.o -hid-sensor-iio-common-y := hid-sensor-attributes.o hid-sensor-trigger.o +obj-$(CONFIG_HID_SENSOR_IIO_TRIGGER) += hid-sensor-trigger.o +hid-sensor-iio-common-y := hid-sensor-attributes.o diff --git a/drivers/iio/dac/ad5380.c b/drivers/iio/dac/ad5380.c index 6c7898c765d9..483fc379a2da 100644 --- a/drivers/iio/dac/ad5380.c +++ b/drivers/iio/dac/ad5380.c @@ -406,7 +406,11 @@ static int ad5380_probe(struct device *dev, struct regmap *regmap, goto error_free_reg; } - st->vref = regulator_get_voltage(st->vref_reg); + ret = regulator_get_voltage(st->vref_reg); + if (ret < 0) + goto error_disable_reg; + + st->vref = ret; } else { st->vref = st->chip_info->int_vref; ctrl |= AD5380_CTRL_INT_VREF_EN; diff --git a/drivers/iio/dac/ad5446.c b/drivers/iio/dac/ad5446.c index 29f653dab2f7..f5583aedfb59 100644 --- a/drivers/iio/dac/ad5446.c +++ b/drivers/iio/dac/ad5446.c @@ -226,7 +226,11 @@ static int ad5446_probe(struct device *dev, const char *name, if (ret) goto error_put_reg; - voltage_uv = regulator_get_voltage(reg); + ret = regulator_get_voltage(reg); + if (ret < 0) + goto error_disable_reg; + + voltage_uv = ret; } indio_dev = iio_device_alloc(sizeof(*st)); diff --git a/drivers/iio/dac/ad5504.c b/drivers/iio/dac/ad5504.c index b2a31a0468ed..0661829f2773 100644 --- a/drivers/iio/dac/ad5504.c +++ b/drivers/iio/dac/ad5504.c @@ -296,7 +296,11 @@ static int ad5504_probe(struct spi_device *spi) if (ret) goto error_put_reg; - voltage_uv = regulator_get_voltage(reg); + ret = regulator_get_voltage(reg); + if (ret < 0) + goto error_disable_reg; + + voltage_uv = ret; } spi_set_drvdata(spi, indio_dev); diff --git a/drivers/iio/dac/ad5624r_spi.c b/drivers/iio/dac/ad5624r_spi.c index e9947969f9fe..f6e116627b71 100644 --- a/drivers/iio/dac/ad5624r_spi.c +++ b/drivers/iio/dac/ad5624r_spi.c @@ -238,7 +238,11 @@ static int ad5624r_probe(struct spi_device *spi) if (ret) goto error_put_reg; - voltage_uv = regulator_get_voltage(st->reg); + ret = regulator_get_voltage(st->reg); + if (ret < 0) + goto error_disable_reg; + + voltage_uv = ret; } spi_set_drvdata(spi, indio_dev); diff --git a/drivers/iio/dac/ad5686.c b/drivers/iio/dac/ad5686.c index 36e51382ae52..ca9609d7a15c 100644 --- a/drivers/iio/dac/ad5686.c +++ b/drivers/iio/dac/ad5686.c @@ -332,7 +332,11 @@ static int ad5686_probe(struct spi_device *spi) if (ret) goto error_put_reg; - voltage_uv = regulator_get_voltage(st->reg); + ret = regulator_get_voltage(st->reg); + if (ret < 0) + goto error_disable_reg; + + voltage_uv = ret; } st->chip_info = diff --git a/drivers/iio/dac/ad5791.c b/drivers/iio/dac/ad5791.c index c84180f23139..6407b5407ddd 100644 --- a/drivers/iio/dac/ad5791.c +++ b/drivers/iio/dac/ad5791.c @@ -365,7 +365,11 @@ static int ad5791_probe(struct spi_device *spi) if (ret) goto error_put_reg_pos; - pos_voltage_uv = regulator_get_voltage(st->reg_vdd); + ret = regulator_get_voltage(st->reg_vdd); + if (ret < 0) + goto error_disable_reg_pos; + + pos_voltage_uv = ret; } st->reg_vss = regulator_get(&spi->dev, "vss"); @@ -374,7 +378,11 @@ static int ad5791_probe(struct spi_device *spi) if (ret) goto error_put_reg_neg; - neg_voltage_uv = regulator_get_voltage(st->reg_vss); + ret = regulator_get_voltage(st->reg_vss); + if (ret < 0) + goto error_disable_reg_neg; + + neg_voltage_uv = ret; } st->pwr_down = true; @@ -428,6 +436,7 @@ error_put_reg_neg: if (!IS_ERR(st->reg_vss)) regulator_put(st->reg_vss); +error_disable_reg_pos: if (!IS_ERR(st->reg_vdd)) regulator_disable(st->reg_vdd); error_put_reg_pos: diff --git a/drivers/iio/frequency/adf4350.c b/drivers/iio/frequency/adf4350.c index e5033b4cfba0..a884252ac66b 100644 --- a/drivers/iio/frequency/adf4350.c +++ b/drivers/iio/frequency/adf4350.c @@ -173,7 +173,7 @@ static int adf4350_set_freq(struct adf4350_state *st, unsigned long long freq) } while ((st->r1_mod > ADF4350_MAX_MODULUS) && r_cnt); } while (r_cnt == 0); - tmp = freq * (u64)st->r1_mod + (st->fpfd > 1); + tmp = freq * (u64)st->r1_mod + (st->fpfd >> 1); do_div(tmp, st->fpfd); /* Div round closest (n + d/2)/d */ st->r0_fract = do_div(tmp, st->r1_mod); st->r0_int = tmp; diff --git a/drivers/iio/gyro/Kconfig b/drivers/iio/gyro/Kconfig index 48ed1483ff27..96b68f63a902 100644 --- a/drivers/iio/gyro/Kconfig +++ b/drivers/iio/gyro/Kconfig @@ -17,6 +17,7 @@ config HID_SENSOR_GYRO_3D select IIO_BUFFER select IIO_TRIGGERED_BUFFER select HID_SENSOR_IIO_COMMON + select HID_SENSOR_IIO_TRIGGER tristate "HID Gyroscope 3D" help Say yes here to build support for the HID SENSOR diff --git a/drivers/iio/light/Kconfig b/drivers/iio/light/Kconfig index 1763c9bcb98a..dbf80abc834f 100644 --- a/drivers/iio/light/Kconfig +++ b/drivers/iio/light/Kconfig @@ -47,6 +47,7 @@ config HID_SENSOR_ALS select IIO_BUFFER select IIO_TRIGGERED_BUFFER select HID_SENSOR_IIO_COMMON + select HID_SENSOR_IIO_TRIGGER tristate "HID ALS" help Say yes here to build support for the HID SENSOR diff --git a/drivers/iio/magnetometer/Kconfig b/drivers/iio/magnetometer/Kconfig index c1f0cdd57037..ff11d68225cf 100644 --- a/drivers/iio/magnetometer/Kconfig +++ b/drivers/iio/magnetometer/Kconfig @@ -8,6 +8,7 @@ config HID_SENSOR_MAGNETOMETER_3D select IIO_BUFFER select IIO_TRIGGERED_BUFFER select HID_SENSOR_IIO_COMMON + select HID_SENSOR_IIO_TRIGGER tristate "HID Magenetometer 3D" help Say yes here to build support for the HID SENSOR diff --git a/drivers/iommu/amd_iommu_init.c b/drivers/iommu/amd_iommu_init.c index 81837b0710a9..faf10ba1ed9a 100644 --- a/drivers/iommu/amd_iommu_init.c +++ b/drivers/iommu/amd_iommu_init.c @@ -975,6 +975,38 @@ static void __init free_iommu_all(void) } /* + * Family15h Model 10h-1fh erratum 746 (IOMMU Logging May Stall Translations) + * Workaround: + * BIOS should disable L2B micellaneous clock gating by setting + * L2_L2B_CK_GATE_CONTROL[CKGateL2BMiscDisable](D0F2xF4_x90[2]) = 1b + */ +static void __init amd_iommu_erratum_746_workaround(struct amd_iommu *iommu) +{ + u32 value; + + if ((boot_cpu_data.x86 != 0x15) || + (boot_cpu_data.x86_model < 0x10) || + (boot_cpu_data.x86_model > 0x1f)) + return; + + pci_write_config_dword(iommu->dev, 0xf0, 0x90); + pci_read_config_dword(iommu->dev, 0xf4, &value); + + if (value & BIT(2)) + return; + + /* Select NB indirect register 0x90 and enable writing */ + pci_write_config_dword(iommu->dev, 0xf0, 0x90 | (1 << 8)); + + pci_write_config_dword(iommu->dev, 0xf4, value | 0x4); + pr_info("AMD-Vi: Applying erratum 746 workaround for IOMMU at %s\n", + dev_name(&iommu->dev->dev)); + + /* Clear the enable writing bit */ + pci_write_config_dword(iommu->dev, 0xf0, 0x90); +} + +/* * This function clues the initialization function for one IOMMU * together and also allocates the command buffer and programs the * hardware. It does NOT enable the IOMMU. This is done afterwards. @@ -1172,6 +1204,8 @@ static int iommu_init_pci(struct amd_iommu *iommu) iommu->stored_l2[i] = iommu_read_l2(iommu, i); } + amd_iommu_erratum_746_workaround(iommu); + return pci_enable_device(iommu->dev); } diff --git a/drivers/iommu/intel-iommu.c b/drivers/iommu/intel-iommu.c index eca28014ef3e..f1e7b86a7c37 100644 --- a/drivers/iommu/intel-iommu.c +++ b/drivers/iommu/intel-iommu.c @@ -4253,13 +4253,19 @@ static void quirk_iommu_rwbf(struct pci_dev *dev) { /* * Mobile 4 Series Chipset neglects to set RWBF capability, - * but needs it: + * but needs it. Same seems to hold for the desktop versions. */ printk(KERN_INFO "DMAR: Forcing write-buffer flush capability\n"); rwbf_quirk = 1; } DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x2a40, quirk_iommu_rwbf); +DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x2e00, quirk_iommu_rwbf); +DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x2e10, quirk_iommu_rwbf); +DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x2e20, quirk_iommu_rwbf); +DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x2e30, quirk_iommu_rwbf); +DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x2e40, quirk_iommu_rwbf); +DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x2e90, quirk_iommu_rwbf); #define GGC 0x52 #define GGC_MEMORY_SIZE_MASK (0xf << 8) diff --git a/drivers/isdn/gigaset/capi.c b/drivers/isdn/gigaset/capi.c index 68452b768da2..03a0a01a4054 100644 --- a/drivers/isdn/gigaset/capi.c +++ b/drivers/isdn/gigaset/capi.c @@ -248,6 +248,8 @@ static inline void dump_rawmsg(enum debuglevel level, const char *tag, CAPIMSG_APPID(data), CAPIMSG_MSGID(data), l, CAPIMSG_CONTROL(data)); l -= 12; + if (l <= 0) + return; dbgline = kmalloc(3 * l, GFP_ATOMIC); if (!dbgline) return; diff --git a/drivers/md/dm-raid.c b/drivers/md/dm-raid.c index 3d8984edeff7..9e58dbd8d8cb 100644 --- a/drivers/md/dm-raid.c +++ b/drivers/md/dm-raid.c @@ -340,24 +340,22 @@ static int validate_region_size(struct raid_set *rs, unsigned long region_size) } /* - * validate_rebuild_devices + * validate_raid_redundancy * @rs * - * Determine if the devices specified for rebuild can result in a valid - * usable array that is capable of rebuilding the given devices. + * Determine if there are enough devices in the array that haven't + * failed (or are being rebuilt) to form a usable array. * * Returns: 0 on success, -EINVAL on failure. */ -static int validate_rebuild_devices(struct raid_set *rs) +static int validate_raid_redundancy(struct raid_set *rs) { unsigned i, rebuild_cnt = 0; unsigned rebuilds_per_group, copies, d; - if (!(rs->print_flags & DMPF_REBUILD)) - return 0; - for (i = 0; i < rs->md.raid_disks; i++) - if (!test_bit(In_sync, &rs->dev[i].rdev.flags)) + if (!test_bit(In_sync, &rs->dev[i].rdev.flags) || + !rs->dev[i].rdev.sb_page) rebuild_cnt++; switch (rs->raid_type->level) { @@ -393,27 +391,24 @@ static int validate_rebuild_devices(struct raid_set *rs) * A A B B C * C D D E E */ - rebuilds_per_group = 0; for (i = 0; i < rs->md.raid_disks * copies; i++) { + if (!(i % copies)) + rebuilds_per_group = 0; d = i % rs->md.raid_disks; - if (!test_bit(In_sync, &rs->dev[d].rdev.flags) && + if ((!rs->dev[d].rdev.sb_page || + !test_bit(In_sync, &rs->dev[d].rdev.flags)) && (++rebuilds_per_group >= copies)) goto too_many; - if (!((i + 1) % copies)) - rebuilds_per_group = 0; } break; default: - DMERR("The rebuild parameter is not supported for %s", - rs->raid_type->name); - rs->ti->error = "Rebuild not supported for this RAID type"; - return -EINVAL; + if (rebuild_cnt) + return -EINVAL; } return 0; too_many: - rs->ti->error = "Too many rebuild devices specified"; return -EINVAL; } @@ -664,9 +659,6 @@ static int parse_raid_params(struct raid_set *rs, char **argv, } rs->md.dev_sectors = sectors_per_dev; - if (validate_rebuild_devices(rs)) - return -EINVAL; - /* Assume there are no metadata devices until the drives are parsed */ rs->md.persistent = 0; rs->md.external = 1; @@ -995,28 +987,10 @@ static int super_validate(struct mddev *mddev, struct md_rdev *rdev) static int analyse_superblocks(struct dm_target *ti, struct raid_set *rs) { int ret; - unsigned redundancy = 0; struct raid_dev *dev; struct md_rdev *rdev, *tmp, *freshest; struct mddev *mddev = &rs->md; - switch (rs->raid_type->level) { - case 1: - redundancy = rs->md.raid_disks - 1; - break; - case 4: - case 5: - case 6: - redundancy = rs->raid_type->parity_devs; - break; - case 10: - redundancy = raid10_md_layout_to_copies(mddev->layout) - 1; - break; - default: - ti->error = "Unknown RAID type"; - return -EINVAL; - } - freshest = NULL; rdev_for_each_safe(rdev, tmp, mddev) { /* @@ -1045,44 +1019,43 @@ static int analyse_superblocks(struct dm_target *ti, struct raid_set *rs) break; default: dev = container_of(rdev, struct raid_dev, rdev); - if (redundancy--) { - if (dev->meta_dev) - dm_put_device(ti, dev->meta_dev); - - dev->meta_dev = NULL; - rdev->meta_bdev = NULL; + if (dev->meta_dev) + dm_put_device(ti, dev->meta_dev); - if (rdev->sb_page) - put_page(rdev->sb_page); + dev->meta_dev = NULL; + rdev->meta_bdev = NULL; - rdev->sb_page = NULL; + if (rdev->sb_page) + put_page(rdev->sb_page); - rdev->sb_loaded = 0; + rdev->sb_page = NULL; - /* - * We might be able to salvage the data device - * even though the meta device has failed. For - * now, we behave as though '- -' had been - * set for this device in the table. - */ - if (dev->data_dev) - dm_put_device(ti, dev->data_dev); + rdev->sb_loaded = 0; - dev->data_dev = NULL; - rdev->bdev = NULL; + /* + * We might be able to salvage the data device + * even though the meta device has failed. For + * now, we behave as though '- -' had been + * set for this device in the table. + */ + if (dev->data_dev) + dm_put_device(ti, dev->data_dev); - list_del(&rdev->same_set); + dev->data_dev = NULL; + rdev->bdev = NULL; - continue; - } - ti->error = "Failed to load superblock"; - return ret; + list_del(&rdev->same_set); } } if (!freshest) return 0; + if (validate_raid_redundancy(rs)) { + rs->ti->error = "Insufficient redundancy to activate array"; + return -EINVAL; + } + /* * Validation of the freshest device provides the source of * validation for the remaining devices. @@ -1432,7 +1405,7 @@ static void raid_resume(struct dm_target *ti) static struct target_type raid_target = { .name = "raid", - .version = {1, 4, 0}, + .version = {1, 4, 1}, .module = THIS_MODULE, .ctr = raid_ctr, .dtr = raid_dtr, diff --git a/drivers/md/dm-thin.c b/drivers/md/dm-thin.c index 675ae5274016..5409607d4875 100644 --- a/drivers/md/dm-thin.c +++ b/drivers/md/dm-thin.c @@ -2746,19 +2746,9 @@ static int thin_iterate_devices(struct dm_target *ti, return 0; } -/* - * A thin device always inherits its queue limits from its pool. - */ -static void thin_io_hints(struct dm_target *ti, struct queue_limits *limits) -{ - struct thin_c *tc = ti->private; - - *limits = bdev_get_queue(tc->pool_dev->bdev)->limits; -} - static struct target_type thin_target = { .name = "thin", - .version = {1, 6, 0}, + .version = {1, 7, 0}, .module = THIS_MODULE, .ctr = thin_ctr, .dtr = thin_dtr, @@ -2767,7 +2757,6 @@ static struct target_type thin_target = { .postsuspend = thin_postsuspend, .status = thin_status, .iterate_devices = thin_iterate_devices, - .io_hints = thin_io_hints, }; /*----------------------------------------------------------------*/ diff --git a/drivers/md/dm.c b/drivers/md/dm.c index c72e4d5a9617..314a0e2faf79 100644 --- a/drivers/md/dm.c +++ b/drivers/md/dm.c @@ -1188,6 +1188,7 @@ static int __clone_and_map_changing_extent_only(struct clone_info *ci, { struct dm_target *ti; sector_t len; + unsigned num_requests; do { ti = dm_table_find_target(ci->map, ci->sector); @@ -1200,7 +1201,8 @@ static int __clone_and_map_changing_extent_only(struct clone_info *ci, * reconfiguration might also have changed that since the * check was performed. */ - if (!get_num_requests || !get_num_requests(ti)) + num_requests = get_num_requests ? get_num_requests(ti) : 0; + if (!num_requests) return -EOPNOTSUPP; if (is_split_required && !is_split_required(ti)) @@ -1208,7 +1210,7 @@ static int __clone_and_map_changing_extent_only(struct clone_info *ci, else len = min(ci->sector_count, max_io_len(ci->sector, ti)); - __issue_target_requests(ci, ti, ti->num_discard_requests, len); + __issue_target_requests(ci, ti, num_requests, len); ci->sector += len; } while (ci->sector_count -= len); diff --git a/drivers/media/i2c/m5mols/m5mols_core.c b/drivers/media/i2c/m5mols/m5mols_core.c index 8a8d42fe2633..d4e7567b367c 100644 --- a/drivers/media/i2c/m5mols/m5mols_core.c +++ b/drivers/media/i2c/m5mols/m5mols_core.c @@ -556,7 +556,7 @@ static int m5mols_get_fmt(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, mutex_lock(&info->lock); format = __find_format(info, fh, fmt->which, info->res_type); - if (!format) + if (format) fmt->format = *format; else ret = -EINVAL; diff --git a/drivers/media/platform/coda.c b/drivers/media/platform/coda.c index 1cf8293c0fb0..4a980e029ca7 100644 --- a/drivers/media/platform/coda.c +++ b/drivers/media/platform/coda.c @@ -23,8 +23,8 @@ #include <linux/slab.h> #include <linux/videodev2.h> #include <linux/of.h> +#include <linux/platform_data/imx-iram.h> -#include <mach/iram.h> #include <media/v4l2-ctrls.h> #include <media/v4l2-device.h> #include <media/v4l2-ioctl.h> diff --git a/drivers/media/platform/omap3isp/ispvideo.c b/drivers/media/platform/omap3isp/ispvideo.c index e0d73a642186..8dac17511e61 100644 --- a/drivers/media/platform/omap3isp/ispvideo.c +++ b/drivers/media/platform/omap3isp/ispvideo.c @@ -35,9 +35,6 @@ #include <linux/vmalloc.h> #include <media/v4l2-dev.h> #include <media/v4l2-ioctl.h> -#include <plat/iommu.h> -#include <plat/iovmm.h> -#include <plat/omap-pm.h> #include "ispvideo.h" #include "isp.h" diff --git a/drivers/media/platform/s5p-fimc/fimc-mdevice.c b/drivers/media/platform/s5p-fimc/fimc-mdevice.c index 4ab99f3a7b09..b4a68ecf0ca7 100644 --- a/drivers/media/platform/s5p-fimc/fimc-mdevice.c +++ b/drivers/media/platform/s5p-fimc/fimc-mdevice.c @@ -593,7 +593,7 @@ static int __fimc_md_create_flite_source_links(struct fimc_md *fmd) { struct media_entity *source, *sink; unsigned int flags = MEDIA_LNK_FL_ENABLED; - int i, ret; + int i, ret = 0; for (i = 0; i < FIMC_LITE_MAX_DEVS; i++) { struct fimc_lite *fimc = fmd->fimc_lite[i]; diff --git a/drivers/media/platform/s5p-mfc/s5p_mfc.c b/drivers/media/platform/s5p-mfc/s5p_mfc.c index 379f57433711..681bc6ba149d 100644 --- a/drivers/media/platform/s5p-mfc/s5p_mfc.c +++ b/drivers/media/platform/s5p-mfc/s5p_mfc.c @@ -412,62 +412,48 @@ leave_handle_frame: } /* Error handling for interrupt */ -static void s5p_mfc_handle_error(struct s5p_mfc_ctx *ctx, - unsigned int reason, unsigned int err) +static void s5p_mfc_handle_error(struct s5p_mfc_dev *dev, + struct s5p_mfc_ctx *ctx, unsigned int reason, unsigned int err) { - struct s5p_mfc_dev *dev; unsigned long flags; - /* If no context is available then all necessary - * processing has been done. */ - if (ctx == NULL) - return; - - dev = ctx->dev; mfc_err("Interrupt Error: %08x\n", err); - s5p_mfc_hw_call(dev->mfc_ops, clear_int_flags, dev); - wake_up_dev(dev, reason, err); - /* Error recovery is dependent on the state of context */ - switch (ctx->state) { - case MFCINST_INIT: - /* This error had to happen while acquireing instance */ - case MFCINST_GOT_INST: - /* This error had to happen while parsing the header */ - case MFCINST_HEAD_PARSED: - /* This error had to happen while setting dst buffers */ - case MFCINST_RETURN_INST: - /* This error had to happen while releasing instance */ - clear_work_bit(ctx); - wake_up_ctx(ctx, reason, err); - if (test_and_clear_bit(0, &dev->hw_lock) == 0) - BUG(); - s5p_mfc_clock_off(); - ctx->state = MFCINST_ERROR; - break; - case MFCINST_FINISHING: - case MFCINST_FINISHED: - case MFCINST_RUNNING: - /* It is higly probable that an error occured - * while decoding a frame */ - clear_work_bit(ctx); - ctx->state = MFCINST_ERROR; - /* Mark all dst buffers as having an error */ - spin_lock_irqsave(&dev->irqlock, flags); - s5p_mfc_hw_call(dev->mfc_ops, cleanup_queue, &ctx->dst_queue, - &ctx->vq_dst); - /* Mark all src buffers as having an error */ - s5p_mfc_hw_call(dev->mfc_ops, cleanup_queue, &ctx->src_queue, - &ctx->vq_src); - spin_unlock_irqrestore(&dev->irqlock, flags); - if (test_and_clear_bit(0, &dev->hw_lock) == 0) - BUG(); - s5p_mfc_clock_off(); - break; - default: - mfc_err("Encountered an error interrupt which had not been handled\n"); - break; + if (ctx != NULL) { + /* Error recovery is dependent on the state of context */ + switch (ctx->state) { + case MFCINST_RES_CHANGE_INIT: + case MFCINST_RES_CHANGE_FLUSH: + case MFCINST_RES_CHANGE_END: + case MFCINST_FINISHING: + case MFCINST_FINISHED: + case MFCINST_RUNNING: + /* It is higly probable that an error occured + * while decoding a frame */ + clear_work_bit(ctx); + ctx->state = MFCINST_ERROR; + /* Mark all dst buffers as having an error */ + spin_lock_irqsave(&dev->irqlock, flags); + s5p_mfc_hw_call(dev->mfc_ops, cleanup_queue, + &ctx->dst_queue, &ctx->vq_dst); + /* Mark all src buffers as having an error */ + s5p_mfc_hw_call(dev->mfc_ops, cleanup_queue, + &ctx->src_queue, &ctx->vq_src); + spin_unlock_irqrestore(&dev->irqlock, flags); + wake_up_ctx(ctx, reason, err); + break; + default: + clear_work_bit(ctx); + ctx->state = MFCINST_ERROR; + wake_up_ctx(ctx, reason, err); + break; + } } + if (test_and_clear_bit(0, &dev->hw_lock) == 0) + BUG(); + s5p_mfc_hw_call(dev->mfc_ops, clear_int_flags, dev); + s5p_mfc_clock_off(); + wake_up_dev(dev, reason, err); return; } @@ -632,7 +618,7 @@ static irqreturn_t s5p_mfc_irq(int irq, void *priv) dev->warn_start) s5p_mfc_handle_frame(ctx, reason, err); else - s5p_mfc_handle_error(ctx, reason, err); + s5p_mfc_handle_error(dev, ctx, reason, err); clear_bit(0, &dev->enter_suspend); break; diff --git a/drivers/media/radio/radio-keene.c b/drivers/media/radio/radio-keene.c index e10e525f33e5..296941a9ae25 100644 --- a/drivers/media/radio/radio-keene.c +++ b/drivers/media/radio/radio-keene.c @@ -374,6 +374,7 @@ static int usb_keene_probe(struct usb_interface *intf, radio->vdev.ioctl_ops = &usb_keene_ioctl_ops; radio->vdev.lock = &radio->lock; radio->vdev.release = video_device_release_empty; + radio->vdev.vfl_dir = VFL_DIR_TX; radio->usbdev = interface_to_usbdev(intf); radio->intf = intf; diff --git a/drivers/media/radio/radio-si4713.c b/drivers/media/radio/radio-si4713.c index a082e400ed0f..1507c9d508d7 100644 --- a/drivers/media/radio/radio-si4713.c +++ b/drivers/media/radio/radio-si4713.c @@ -250,6 +250,7 @@ static struct video_device radio_si4713_vdev_template = { .name = "radio-si4713", .release = video_device_release, .ioctl_ops = &radio_si4713_ioctl_ops, + .vfl_dir = VFL_DIR_TX, }; /* Platform driver interface */ diff --git a/drivers/media/radio/radio-wl1273.c b/drivers/media/radio/radio-wl1273.c index c48be195bbad..cabbe3adf435 100644 --- a/drivers/media/radio/radio-wl1273.c +++ b/drivers/media/radio/radio-wl1273.c @@ -1971,6 +1971,7 @@ static struct video_device wl1273_viddev_template = { .ioctl_ops = &wl1273_ioctl_ops, .name = WL1273_FM_DRIVER_NAME, .release = wl1273_vdev_release, + .vfl_dir = VFL_DIR_TX, }; static int wl1273_fm_radio_remove(struct platform_device *pdev) diff --git a/drivers/media/radio/wl128x/fmdrv_v4l2.c b/drivers/media/radio/wl128x/fmdrv_v4l2.c index 048de4536036..0a8ee8fab924 100644 --- a/drivers/media/radio/wl128x/fmdrv_v4l2.c +++ b/drivers/media/radio/wl128x/fmdrv_v4l2.c @@ -518,6 +518,16 @@ static struct video_device fm_viddev_template = { .ioctl_ops = &fm_drv_ioctl_ops, .name = FM_DRV_NAME, .release = video_device_release, + /* + * To ensure both the tuner and modulator ioctls are accessible we + * set the vfl_dir to M2M to indicate this. + * + * It is not really a mem2mem device of course, but it can both receive + * and transmit using the same radio device. It's the only radio driver + * that does this and it should really be split in two radio devices, + * but that would affect applications using this driver. + */ + .vfl_dir = VFL_DIR_M2M, }; int fm_v4l2_init_video_device(struct fmdev *fmdev, int radio_nr) diff --git a/drivers/media/usb/gspca/kinect.c b/drivers/media/usb/gspca/kinect.c index 40ad6687ee5d..3773a8a745df 100644 --- a/drivers/media/usb/gspca/kinect.c +++ b/drivers/media/usb/gspca/kinect.c @@ -381,6 +381,7 @@ static const struct sd_desc sd_desc = { /* -- module initialisation -- */ static const struct usb_device_id device_table[] = { {USB_DEVICE(0x045e, 0x02ae)}, + {USB_DEVICE(0x045e, 0x02bf)}, {} }; diff --git a/drivers/media/usb/gspca/sonixb.c b/drivers/media/usb/gspca/sonixb.c index 70511d5f9538..1220340e7602 100644 --- a/drivers/media/usb/gspca/sonixb.c +++ b/drivers/media/usb/gspca/sonixb.c @@ -496,7 +496,7 @@ static void reg_w(struct gspca_dev *gspca_dev, } } -static void i2c_w(struct gspca_dev *gspca_dev, const __u8 *buffer) +static void i2c_w(struct gspca_dev *gspca_dev, const u8 *buf) { int retry = 60; @@ -504,16 +504,19 @@ static void i2c_w(struct gspca_dev *gspca_dev, const __u8 *buffer) return; /* is i2c ready */ - reg_w(gspca_dev, 0x08, buffer, 8); + reg_w(gspca_dev, 0x08, buf, 8); while (retry--) { if (gspca_dev->usb_err < 0) return; - msleep(10); + msleep(1); reg_r(gspca_dev, 0x08); if (gspca_dev->usb_buf[0] & 0x04) { if (gspca_dev->usb_buf[0] & 0x08) { dev_err(gspca_dev->v4l2_dev.dev, - "i2c write error\n"); + "i2c error writing %02x %02x %02x %02x" + " %02x %02x %02x %02x\n", + buf[0], buf[1], buf[2], buf[3], + buf[4], buf[5], buf[6], buf[7]); gspca_dev->usb_err = -EIO; } return; @@ -530,7 +533,7 @@ static void i2c_w_vector(struct gspca_dev *gspca_dev, for (;;) { if (gspca_dev->usb_err < 0) return; - reg_w(gspca_dev, 0x08, *buffer, 8); + i2c_w(gspca_dev, *buffer); len -= 8; if (len <= 0) break; diff --git a/drivers/media/usb/gspca/sonixj.c b/drivers/media/usb/gspca/sonixj.c index 5a86047b846f..36307a9028a9 100644 --- a/drivers/media/usb/gspca/sonixj.c +++ b/drivers/media/usb/gspca/sonixj.c @@ -1550,6 +1550,7 @@ static void i2c_w1(struct gspca_dev *gspca_dev, u8 reg, u8 val) 0, gspca_dev->usb_buf, 8, 500); + msleep(2); if (ret < 0) { pr_err("i2c_w1 err %d\n", ret); gspca_dev->usb_err = ret; diff --git a/drivers/media/usb/uvc/uvc_ctrl.c b/drivers/media/usb/uvc/uvc_ctrl.c index 2bb7613ddebb..d5baab17a5ef 100644 --- a/drivers/media/usb/uvc/uvc_ctrl.c +++ b/drivers/media/usb/uvc/uvc_ctrl.c @@ -1431,8 +1431,10 @@ int uvc_ctrl_set(struct uvc_video_chain *chain, int ret; ctrl = uvc_find_control(chain, xctrl->id, &mapping); - if (ctrl == NULL || (ctrl->info.flags & UVC_CTRL_FLAG_SET_CUR) == 0) + if (ctrl == NULL) return -EINVAL; + if (!(ctrl->info.flags & UVC_CTRL_FLAG_SET_CUR)) + return -EACCES; /* Clamp out of range values. */ switch (mapping->v4l2_type) { diff --git a/drivers/media/usb/uvc/uvc_v4l2.c b/drivers/media/usb/uvc/uvc_v4l2.c index f2ee8c6b0d8d..68d59b527492 100644 --- a/drivers/media/usb/uvc/uvc_v4l2.c +++ b/drivers/media/usb/uvc/uvc_v4l2.c @@ -657,8 +657,7 @@ static long uvc_v4l2_do_ioctl(struct file *file, unsigned int cmd, void *arg) ret = uvc_ctrl_get(chain, ctrl); if (ret < 0) { uvc_ctrl_rollback(handle); - ctrls->error_idx = ret == -ENOENT - ? ctrls->count : i; + ctrls->error_idx = i; return ret; } } @@ -686,8 +685,7 @@ static long uvc_v4l2_do_ioctl(struct file *file, unsigned int cmd, void *arg) ret = uvc_ctrl_set(chain, ctrl); if (ret < 0) { uvc_ctrl_rollback(handle); - ctrls->error_idx = (ret == -ENOENT && - cmd == VIDIOC_S_EXT_CTRLS) + ctrls->error_idx = cmd == VIDIOC_S_EXT_CTRLS ? ctrls->count : i; return ret; } diff --git a/drivers/media/v4l2-core/videobuf2-core.c b/drivers/media/v4l2-core/videobuf2-core.c index 9f81be23a81f..e02c4797b1c6 100644 --- a/drivers/media/v4l2-core/videobuf2-core.c +++ b/drivers/media/v4l2-core/videobuf2-core.c @@ -921,8 +921,10 @@ static void __fill_vb2_buffer(struct vb2_buffer *vb, const struct v4l2_buffer *b * In videobuf we use our internal V4l2_planes struct for * single-planar buffers as well, for simplicity. */ - if (V4L2_TYPE_IS_OUTPUT(b->type)) + if (V4L2_TYPE_IS_OUTPUT(b->type)) { v4l2_planes[0].bytesused = b->bytesused; + v4l2_planes[0].data_offset = 0; + } if (b->memory == V4L2_MEMORY_USERPTR) { v4l2_planes[0].m.userptr = b->m.userptr; diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig index 1c0abd4dfc43..ff553babf455 100644 --- a/drivers/mfd/Kconfig +++ b/drivers/mfd/Kconfig @@ -237,6 +237,7 @@ config MFD_TPS65910 depends on I2C=y && GPIOLIB select MFD_CORE select REGMAP_I2C + select REGMAP_IRQ select IRQ_DOMAIN help if you say yes here you get support for the TPS65910 series of @@ -292,6 +293,7 @@ config TWL4030_CORE bool "Texas Instruments TWL4030/TWL5030/TWL6030/TPS659x0 Support" depends on I2C=y && GENERIC_HARDIRQS select IRQ_DOMAIN + select REGMAP_I2C help Say yes here if you have TWL4030 / TWL6030 family chip on your board. This core driver provides register access and IRQ handling diff --git a/drivers/mfd/ab8500-core.c b/drivers/mfd/ab8500-core.c index e1650badd106..4778bb124efe 100644 --- a/drivers/mfd/ab8500-core.c +++ b/drivers/mfd/ab8500-core.c @@ -19,6 +19,7 @@ #include <linux/mfd/core.h> #include <linux/mfd/abx500.h> #include <linux/mfd/abx500/ab8500.h> +#include <linux/mfd/abx500/ab8500-bm.h> #include <linux/mfd/dbx500-prcmu.h> #include <linux/regulator/ab8500.h> #include <linux/of.h> diff --git a/drivers/mfd/arizona-core.c b/drivers/mfd/arizona-core.c index bc8a3edb6bbf..222c03a5ddc0 100644 --- a/drivers/mfd/arizona-core.c +++ b/drivers/mfd/arizona-core.c @@ -239,7 +239,12 @@ static int arizona_runtime_resume(struct device *dev) return ret; } - regcache_sync(arizona->regmap); + ret = regcache_sync(arizona->regmap); + if (ret != 0) { + dev_err(arizona->dev, "Failed to restore register cache\n"); + regulator_disable(arizona->dcvdd); + return ret; + } return 0; } diff --git a/drivers/mfd/arizona-irq.c b/drivers/mfd/arizona-irq.c index 74713bf5371f..2bec5f0db3ee 100644 --- a/drivers/mfd/arizona-irq.c +++ b/drivers/mfd/arizona-irq.c @@ -176,14 +176,7 @@ int arizona_irq_init(struct arizona *arizona) aod = &wm5102_aod; irq = &wm5102_irq; - switch (arizona->rev) { - case 0: - case 1: - ctrlif_error = false; - break; - default: - break; - } + ctrlif_error = false; break; #endif #ifdef CONFIG_MFD_WM5110 @@ -191,14 +184,7 @@ int arizona_irq_init(struct arizona *arizona) aod = &wm5110_aod; irq = &wm5110_irq; - switch (arizona->rev) { - case 0: - case 1: - ctrlif_error = false; - break; - default: - break; - } + ctrlif_error = false; break; #endif default: diff --git a/drivers/mfd/da9052-i2c.c b/drivers/mfd/da9052-i2c.c index ac74a4d1daea..885e56780358 100644 --- a/drivers/mfd/da9052-i2c.c +++ b/drivers/mfd/da9052-i2c.c @@ -27,6 +27,66 @@ #include <linux/of_device.h> #endif +/* I2C safe register check */ +static inline bool i2c_safe_reg(unsigned char reg) +{ + switch (reg) { + case DA9052_STATUS_A_REG: + case DA9052_STATUS_B_REG: + case DA9052_STATUS_C_REG: + case DA9052_STATUS_D_REG: + case DA9052_ADC_RES_L_REG: + case DA9052_ADC_RES_H_REG: + case DA9052_VDD_RES_REG: + case DA9052_ICHG_AV_REG: + case DA9052_TBAT_RES_REG: + case DA9052_ADCIN4_RES_REG: + case DA9052_ADCIN5_RES_REG: + case DA9052_ADCIN6_RES_REG: + case DA9052_TJUNC_RES_REG: + case DA9052_TSI_X_MSB_REG: + case DA9052_TSI_Y_MSB_REG: + case DA9052_TSI_LSB_REG: + case DA9052_TSI_Z_MSB_REG: + return true; + default: + return false; + } +} + +/* + * There is an issue with DA9052 and DA9053_AA/BA/BB PMIC where the PMIC + * gets lockup up or fails to respond following a system reset. + * This fix is to follow any read or write with a dummy read to a safe + * register. + */ +int da9052_i2c_fix(struct da9052 *da9052, unsigned char reg) +{ + int val; + + switch (da9052->chip_id) { + case DA9052: + case DA9053_AA: + case DA9053_BA: + case DA9053_BB: + /* A dummy read to a safe register address. */ + if (!i2c_safe_reg(reg)) + return regmap_read(da9052->regmap, + DA9052_PARK_REGISTER, + &val); + break; + default: + /* + * For other chips parking of I2C register + * to a safe place is not required. + */ + break; + } + + return 0; +} +EXPORT_SYMBOL(da9052_i2c_fix); + static int da9052_i2c_enable_multiwrite(struct da9052 *da9052) { int reg_val, ret; @@ -83,6 +143,7 @@ static int da9052_i2c_probe(struct i2c_client *client, da9052->dev = &client->dev; da9052->chip_irq = client->irq; + da9052->fix_io = da9052_i2c_fix; i2c_set_clientdata(client, da9052); diff --git a/drivers/mfd/db8500-prcmu.c b/drivers/mfd/db8500-prcmu.c index dc8826d8d69d..268f45d42394 100644 --- a/drivers/mfd/db8500-prcmu.c +++ b/drivers/mfd/db8500-prcmu.c @@ -2524,7 +2524,7 @@ static bool read_mailbox_0(void) for (n = 0; n < NUM_PRCMU_WAKEUPS; n++) { if (ev & prcmu_irq_bit[n]) - generic_handle_irq(IRQ_PRCMU_BASE + n); + generic_handle_irq(irq_find_mapping(db8500_irq_domain, n)); } r = true; break; @@ -2737,13 +2737,14 @@ static int db8500_irq_map(struct irq_domain *d, unsigned int virq, } static struct irq_domain_ops db8500_irq_ops = { - .map = db8500_irq_map, - .xlate = irq_domain_xlate_twocell, + .map = db8500_irq_map, + .xlate = irq_domain_xlate_twocell, }; static int db8500_irq_init(struct device_node *np) { - int irq_base = -1; + int irq_base = 0; + int i; /* In the device tree case, just take some IRQs */ if (!np) @@ -2758,6 +2759,10 @@ static int db8500_irq_init(struct device_node *np) return -ENOSYS; } + /* All wakeups will be used, so create mappings for all */ + for (i = 0; i < NUM_PRCMU_WAKEUPS; i++) + irq_create_mapping(db8500_irq_domain, i); + return 0; } diff --git a/drivers/mfd/max77686.c b/drivers/mfd/max77686.c index f6878f8db57d..4d73963cd8f0 100644 --- a/drivers/mfd/max77686.c +++ b/drivers/mfd/max77686.c @@ -93,15 +93,6 @@ static int max77686_i2c_probe(struct i2c_client *i2c, if (max77686 == NULL) return -ENOMEM; - max77686->regmap = regmap_init_i2c(i2c, &max77686_regmap_config); - if (IS_ERR(max77686->regmap)) { - ret = PTR_ERR(max77686->regmap); - dev_err(max77686->dev, "Failed to allocate register map: %d\n", - ret); - kfree(max77686); - return ret; - } - i2c_set_clientdata(i2c, max77686); max77686->dev = &i2c->dev; max77686->i2c = i2c; @@ -111,6 +102,15 @@ static int max77686_i2c_probe(struct i2c_client *i2c, max77686->irq_gpio = pdata->irq_gpio; max77686->irq = i2c->irq; + max77686->regmap = regmap_init_i2c(i2c, &max77686_regmap_config); + if (IS_ERR(max77686->regmap)) { + ret = PTR_ERR(max77686->regmap); + dev_err(max77686->dev, "Failed to allocate register map: %d\n", + ret); + kfree(max77686); + return ret; + } + if (regmap_read(max77686->regmap, MAX77686_REG_DEVICE_ID, &data) < 0) { dev_err(max77686->dev, diff --git a/drivers/mfd/max77693.c b/drivers/mfd/max77693.c index cc5155e20494..9e60fed5ff82 100644 --- a/drivers/mfd/max77693.c +++ b/drivers/mfd/max77693.c @@ -114,35 +114,37 @@ static int max77693_i2c_probe(struct i2c_client *i2c, u8 reg_data; int ret = 0; + if (!pdata) { + dev_err(&i2c->dev, "No platform data found.\n"); + return -EINVAL; + } + max77693 = devm_kzalloc(&i2c->dev, sizeof(struct max77693_dev), GFP_KERNEL); if (max77693 == NULL) return -ENOMEM; - max77693->regmap = devm_regmap_init_i2c(i2c, &max77693_regmap_config); - if (IS_ERR(max77693->regmap)) { - ret = PTR_ERR(max77693->regmap); - dev_err(max77693->dev,"failed to allocate register map: %d\n", - ret); - goto err_regmap; - } - i2c_set_clientdata(i2c, max77693); max77693->dev = &i2c->dev; max77693->i2c = i2c; max77693->irq = i2c->irq; max77693->type = id->driver_data; - if (!pdata) - goto err_regmap; + max77693->regmap = devm_regmap_init_i2c(i2c, &max77693_regmap_config); + if (IS_ERR(max77693->regmap)) { + ret = PTR_ERR(max77693->regmap); + dev_err(max77693->dev, "failed to allocate register map: %d\n", + ret); + return ret; + } max77693->wakeup = pdata->wakeup; - if (max77693_read_reg(max77693->regmap, - MAX77693_PMIC_REG_PMIC_ID2, ®_data) < 0) { + ret = max77693_read_reg(max77693->regmap, MAX77693_PMIC_REG_PMIC_ID2, + ®_data); + if (ret < 0) { dev_err(max77693->dev, "device not found on this channel\n"); - ret = -ENODEV; - goto err_regmap; + return ret; } else dev_info(max77693->dev, "device ID: 0x%x\n", reg_data); @@ -163,7 +165,7 @@ static int max77693_i2c_probe(struct i2c_client *i2c, ret = PTR_ERR(max77693->regmap_muic); dev_err(max77693->dev, "failed to allocate register map: %d\n", ret); - goto err_regmap; + goto err_regmap_muic; } ret = max77693_irq_init(max77693); @@ -184,9 +186,9 @@ static int max77693_i2c_probe(struct i2c_client *i2c, err_mfd: max77693_irq_exit(max77693); err_irq: +err_regmap_muic: i2c_unregister_device(max77693->muic); i2c_unregister_device(max77693->haptic); -err_regmap: return ret; } diff --git a/drivers/mfd/pcf50633-core.c b/drivers/mfd/pcf50633-core.c index 64803f13bcec..d11567307fbe 100644 --- a/drivers/mfd/pcf50633-core.c +++ b/drivers/mfd/pcf50633-core.c @@ -208,6 +208,8 @@ static int pcf50633_probe(struct i2c_client *client, if (!pcf) return -ENOMEM; + i2c_set_clientdata(client, pcf); + pcf->dev = &client->dev; pcf->pdata = pdata; mutex_init(&pcf->lock); @@ -219,9 +221,6 @@ static int pcf50633_probe(struct i2c_client *client, return ret; } - i2c_set_clientdata(client, pcf); - pcf->dev = &client->dev; - version = pcf50633_reg_read(pcf, 0); variant = pcf50633_reg_read(pcf, 1); if (version < 0 || variant < 0) { diff --git a/drivers/mfd/rtl8411.c b/drivers/mfd/rtl8411.c index 89f046ca9e41..3d3b4addf81a 100644 --- a/drivers/mfd/rtl8411.c +++ b/drivers/mfd/rtl8411.c @@ -112,6 +112,21 @@ static int rtl8411_card_power_off(struct rtsx_pcr *pcr, int card) BPP_LDO_POWB, BPP_LDO_SUSPEND); } +static int rtl8411_switch_output_voltage(struct rtsx_pcr *pcr, u8 voltage) +{ + u8 mask, val; + + mask = (BPP_REG_TUNED18 << BPP_TUNED18_SHIFT_8411) | BPP_PAD_MASK; + if (voltage == OUTPUT_3V3) + val = (BPP_ASIC_3V3 << BPP_TUNED18_SHIFT_8411) | BPP_PAD_3V3; + else if (voltage == OUTPUT_1V8) + val = (BPP_ASIC_1V8 << BPP_TUNED18_SHIFT_8411) | BPP_PAD_1V8; + else + return -EINVAL; + + return rtsx_pci_write_register(pcr, LDO_CTL, mask, val); +} + static unsigned int rtl8411_cd_deglitch(struct rtsx_pcr *pcr) { unsigned int card_exist; @@ -163,6 +178,18 @@ static unsigned int rtl8411_cd_deglitch(struct rtsx_pcr *pcr) return card_exist; } +static int rtl8411_conv_clk_and_div_n(int input, int dir) +{ + int output; + + if (dir == CLK_TO_DIV_N) + output = input * 4 / 5 - 2; + else + output = (input + 2) * 5 / 4; + + return output; +} + static const struct pcr_ops rtl8411_pcr_ops = { .extra_init_hw = rtl8411_extra_init_hw, .optimize_phy = NULL, @@ -172,7 +199,9 @@ static const struct pcr_ops rtl8411_pcr_ops = { .disable_auto_blink = rtl8411_disable_auto_blink, .card_power_on = rtl8411_card_power_on, .card_power_off = rtl8411_card_power_off, + .switch_output_voltage = rtl8411_switch_output_voltage, .cd_deglitch = rtl8411_cd_deglitch, + .conv_clk_and_div_n = rtl8411_conv_clk_and_div_n, }; /* SD Pull Control Enable: diff --git a/drivers/mfd/rts5209.c b/drivers/mfd/rts5209.c index 283a4f148084..98fe0f39463e 100644 --- a/drivers/mfd/rts5209.c +++ b/drivers/mfd/rts5209.c @@ -144,6 +144,25 @@ static int rts5209_card_power_off(struct rtsx_pcr *pcr, int card) return rtsx_pci_send_cmd(pcr, 100); } +static int rts5209_switch_output_voltage(struct rtsx_pcr *pcr, u8 voltage) +{ + int err; + + if (voltage == OUTPUT_3V3) { + err = rtsx_pci_write_phy_register(pcr, 0x08, 0x4FC0 | 0x24); + if (err < 0) + return err; + } else if (voltage == OUTPUT_1V8) { + err = rtsx_pci_write_phy_register(pcr, 0x08, 0x4C40 | 0x24); + if (err < 0) + return err; + } else { + return -EINVAL; + } + + return 0; +} + static const struct pcr_ops rts5209_pcr_ops = { .extra_init_hw = rts5209_extra_init_hw, .optimize_phy = rts5209_optimize_phy, @@ -153,7 +172,9 @@ static const struct pcr_ops rts5209_pcr_ops = { .disable_auto_blink = rts5209_disable_auto_blink, .card_power_on = rts5209_card_power_on, .card_power_off = rts5209_card_power_off, + .switch_output_voltage = rts5209_switch_output_voltage, .cd_deglitch = NULL, + .conv_clk_and_div_n = NULL, }; /* SD Pull Control Enable: diff --git a/drivers/mfd/rts5229.c b/drivers/mfd/rts5229.c index b9dbab266fda..29d889cbb9c5 100644 --- a/drivers/mfd/rts5229.c +++ b/drivers/mfd/rts5229.c @@ -114,6 +114,25 @@ static int rts5229_card_power_off(struct rtsx_pcr *pcr, int card) return rtsx_pci_send_cmd(pcr, 100); } +static int rts5229_switch_output_voltage(struct rtsx_pcr *pcr, u8 voltage) +{ + int err; + + if (voltage == OUTPUT_3V3) { + err = rtsx_pci_write_phy_register(pcr, 0x08, 0x4FC0 | 0x24); + if (err < 0) + return err; + } else if (voltage == OUTPUT_1V8) { + err = rtsx_pci_write_phy_register(pcr, 0x08, 0x4C40 | 0x24); + if (err < 0) + return err; + } else { + return -EINVAL; + } + + return 0; +} + static const struct pcr_ops rts5229_pcr_ops = { .extra_init_hw = rts5229_extra_init_hw, .optimize_phy = rts5229_optimize_phy, @@ -123,7 +142,9 @@ static const struct pcr_ops rts5229_pcr_ops = { .disable_auto_blink = rts5229_disable_auto_blink, .card_power_on = rts5229_card_power_on, .card_power_off = rts5229_card_power_off, + .switch_output_voltage = rts5229_switch_output_voltage, .cd_deglitch = NULL, + .conv_clk_and_div_n = NULL, }; /* SD Pull Control Enable: diff --git a/drivers/mfd/rtsx_pcr.c b/drivers/mfd/rtsx_pcr.c index 7a7b0bda4618..9fc57009e228 100644 --- a/drivers/mfd/rtsx_pcr.c +++ b/drivers/mfd/rtsx_pcr.c @@ -630,7 +630,10 @@ int rtsx_pci_switch_clock(struct rtsx_pcr *pcr, unsigned int card_clock, if (clk == pcr->cur_clock) return 0; - N = (u8)(clk - 2); + if (pcr->ops->conv_clk_and_div_n) + N = (u8)pcr->ops->conv_clk_and_div_n(clk, CLK_TO_DIV_N); + else + N = (u8)(clk - 2); if ((clk <= 2) || (N > max_N)) return -EINVAL; @@ -641,7 +644,14 @@ int rtsx_pci_switch_clock(struct rtsx_pcr *pcr, unsigned int card_clock, /* Make sure that the SSC clock div_n is equal or greater than min_N */ div = CLK_DIV_1; while ((N < min_N) && (div < max_div)) { - N = (N + 2) * 2 - 2; + if (pcr->ops->conv_clk_and_div_n) { + int dbl_clk = pcr->ops->conv_clk_and_div_n(N, + DIV_N_TO_CLK) * 2; + N = (u8)pcr->ops->conv_clk_and_div_n(dbl_clk, + CLK_TO_DIV_N); + } else { + N = (N + 2) * 2 - 2; + } div++; } dev_dbg(&(pcr->pci->dev), "N = %d, div = %d\n", N, div); @@ -703,6 +713,15 @@ int rtsx_pci_card_power_off(struct rtsx_pcr *pcr, int card) } EXPORT_SYMBOL_GPL(rtsx_pci_card_power_off); +int rtsx_pci_switch_output_voltage(struct rtsx_pcr *pcr, u8 voltage) +{ + if (pcr->ops->switch_output_voltage) + return pcr->ops->switch_output_voltage(pcr, voltage); + + return 0; +} +EXPORT_SYMBOL_GPL(rtsx_pci_switch_output_voltage); + unsigned int rtsx_pci_card_exist(struct rtsx_pcr *pcr) { unsigned int val; @@ -767,10 +786,10 @@ static void rtsx_pci_card_detect(struct work_struct *work) spin_unlock_irqrestore(&pcr->lock, flags); - if (card_detect & SD_EXIST) + if ((card_detect & SD_EXIST) && pcr->slots[RTSX_SD_CARD].card_event) pcr->slots[RTSX_SD_CARD].card_event( pcr->slots[RTSX_SD_CARD].p_dev); - if (card_detect & MS_EXIST) + if ((card_detect & MS_EXIST) && pcr->slots[RTSX_MS_CARD].card_event) pcr->slots[RTSX_MS_CARD].card_event( pcr->slots[RTSX_MS_CARD].p_dev); } diff --git a/drivers/mfd/tc3589x.c b/drivers/mfd/tc3589x.c index a06d66b929b1..ecc092c7f745 100644 --- a/drivers/mfd/tc3589x.c +++ b/drivers/mfd/tc3589x.c @@ -219,25 +219,18 @@ static void tc3589x_irq_unmap(struct irq_domain *d, unsigned int virq) } static struct irq_domain_ops tc3589x_irq_ops = { - .map = tc3589x_irq_map, + .map = tc3589x_irq_map, .unmap = tc3589x_irq_unmap, - .xlate = irq_domain_xlate_twocell, + .xlate = irq_domain_xlate_twocell, }; static int tc3589x_irq_init(struct tc3589x *tc3589x, struct device_node *np) { int base = tc3589x->irq_base; - if (base) { - tc3589x->domain = irq_domain_add_legacy( - NULL, TC3589x_NR_INTERNAL_IRQS, base, - 0, &tc3589x_irq_ops, tc3589x); - } - else { - tc3589x->domain = irq_domain_add_linear( - np, TC3589x_NR_INTERNAL_IRQS, - &tc3589x_irq_ops, tc3589x); - } + tc3589x->domain = irq_domain_add_simple( + np, TC3589x_NR_INTERNAL_IRQS, base, + &tc3589x_irq_ops, tc3589x); if (!tc3589x->domain) { dev_err(tc3589x->dev, "Failed to create irqdomain\n"); diff --git a/drivers/mfd/twl4030-power.c b/drivers/mfd/twl4030-power.c index 4dae241e5017..dd362c1078e1 100644 --- a/drivers/mfd/twl4030-power.c +++ b/drivers/mfd/twl4030-power.c @@ -159,7 +159,7 @@ out: static int twl4030_write_script(u8 address, struct twl4030_ins *script, int len) { - int err; + int err = -EINVAL; for (; len; len--, address++, script++) { if (len == 1) { diff --git a/drivers/mfd/vexpress-config.c b/drivers/mfd/vexpress-config.c index fae15d880758..3c1723aa6225 100644 --- a/drivers/mfd/vexpress-config.c +++ b/drivers/mfd/vexpress-config.c @@ -67,6 +67,7 @@ struct vexpress_config_bridge *vexpress_config_bridge_register( return bridge; } +EXPORT_SYMBOL(vexpress_config_bridge_register); void vexpress_config_bridge_unregister(struct vexpress_config_bridge *bridge) { @@ -83,6 +84,7 @@ void vexpress_config_bridge_unregister(struct vexpress_config_bridge *bridge) while (!list_empty(&__bridge.transactions)) cpu_relax(); } +EXPORT_SYMBOL(vexpress_config_bridge_unregister); struct vexpress_config_func { @@ -142,6 +144,7 @@ struct vexpress_config_func *__vexpress_config_func_get(struct device *dev, return func; } +EXPORT_SYMBOL(__vexpress_config_func_get); void vexpress_config_func_put(struct vexpress_config_func *func) { @@ -149,7 +152,7 @@ void vexpress_config_func_put(struct vexpress_config_func *func) of_node_put(func->bridge->node); kfree(func); } - +EXPORT_SYMBOL(vexpress_config_func_put); struct vexpress_config_trans { struct vexpress_config_func *func; @@ -229,6 +232,7 @@ void vexpress_config_complete(struct vexpress_config_bridge *bridge, complete(&trans->completion); } +EXPORT_SYMBOL(vexpress_config_complete); int vexpress_config_wait(struct vexpress_config_trans *trans) { @@ -236,7 +240,7 @@ int vexpress_config_wait(struct vexpress_config_trans *trans) return trans->status; } - +EXPORT_SYMBOL(vexpress_config_wait); int vexpress_config_read(struct vexpress_config_func *func, int offset, u32 *data) diff --git a/drivers/mfd/vexpress-sysreg.c b/drivers/mfd/vexpress-sysreg.c index e5d8f63b252a..77048b18439e 100644 --- a/drivers/mfd/vexpress-sysreg.c +++ b/drivers/mfd/vexpress-sysreg.c @@ -313,19 +313,11 @@ static void vexpress_sysreg_config_complete(unsigned long data) } -void __init vexpress_sysreg_early_init(void __iomem *base) +void __init vexpress_sysreg_setup(struct device_node *node) { - struct device_node *node = of_find_compatible_node(NULL, NULL, - "arm,vexpress-sysreg"); - - if (node) - base = of_iomap(node, 0); - - if (WARN_ON(!base)) + if (WARN_ON(!vexpress_sysreg_base)) return; - vexpress_sysreg_base = base; - if (readl(vexpress_sysreg_base + SYS_MISC) & SYS_MISC_MASTERSITE) vexpress_master_site = VEXPRESS_SITE_DB2; else @@ -336,9 +328,23 @@ void __init vexpress_sysreg_early_init(void __iomem *base) WARN_ON(!vexpress_sysreg_config_bridge); } +void __init vexpress_sysreg_early_init(void __iomem *base) +{ + vexpress_sysreg_base = base; + vexpress_sysreg_setup(NULL); +} + void __init vexpress_sysreg_of_early_init(void) { - vexpress_sysreg_early_init(NULL); + struct device_node *node = of_find_compatible_node(NULL, NULL, + "arm,vexpress-sysreg"); + + if (node) { + vexpress_sysreg_base = of_iomap(node, 0); + vexpress_sysreg_setup(node); + } else { + pr_info("vexpress-sysreg: No Device Tree node found."); + } } @@ -426,9 +432,11 @@ static int vexpress_sysreg_probe(struct platform_device *pdev) return -EBUSY; } - if (!vexpress_sysreg_base) + if (!vexpress_sysreg_base) { vexpress_sysreg_base = devm_ioremap(&pdev->dev, res->start, resource_size(res)); + vexpress_sysreg_setup(pdev->dev.of_node); + } if (!vexpress_sysreg_base) { dev_err(&pdev->dev, "Failed to obtain base address!\n"); diff --git a/drivers/mfd/wm5102-tables.c b/drivers/mfd/wm5102-tables.c index 088872ab6338..1133a64c2dc9 100644 --- a/drivers/mfd/wm5102-tables.c +++ b/drivers/mfd/wm5102-tables.c @@ -1882,7 +1882,7 @@ static bool wm5102_volatile_register(struct device *dev, unsigned int reg) } } -#define WM5102_MAX_REGISTER 0x1a8fff +#define WM5102_MAX_REGISTER 0x1a9800 const struct regmap_config wm5102_spi_regmap = { .reg_bits = 32, diff --git a/drivers/misc/atmel-ssc.c b/drivers/misc/atmel-ssc.c index 158da5a81a66..3c09cbb70b1d 100644 --- a/drivers/misc/atmel-ssc.c +++ b/drivers/misc/atmel-ssc.c @@ -19,6 +19,7 @@ #include <linux/module.h> #include <linux/of.h> +#include <linux/pinctrl/consumer.h> /* Serialize access to ssc_list and user count */ static DEFINE_SPINLOCK(user_lock); @@ -131,6 +132,13 @@ static int ssc_probe(struct platform_device *pdev) struct resource *regs; struct ssc_device *ssc; const struct atmel_ssc_platform_data *plat_dat; + struct pinctrl *pinctrl; + + pinctrl = devm_pinctrl_get_select_default(&pdev->dev); + if (IS_ERR(pinctrl)) { + dev_err(&pdev->dev, "Failed to request pinctrl\n"); + return PTR_ERR(pinctrl); + } ssc = devm_kzalloc(&pdev->dev, sizeof(struct ssc_device), GFP_KERNEL); if (!ssc) { diff --git a/drivers/misc/mei/amthif.c b/drivers/misc/mei/amthif.c index 18794aea6062..e40ffd9502d1 100644 --- a/drivers/misc/mei/amthif.c +++ b/drivers/misc/mei/amthif.c @@ -187,13 +187,13 @@ int mei_amthif_read(struct mei_device *dev, struct file *file, wait_ret = wait_event_interruptible(dev->iamthif_cl.wait, (cb = mei_amthif_find_read_list_entry(dev, file))); + /* Locking again the Mutex */ + mutex_lock(&dev->device_lock); + if (wait_ret) return -ERESTARTSYS; dev_dbg(&dev->pdev->dev, "woke up from sleep\n"); - - /* Locking again the Mutex */ - mutex_lock(&dev->device_lock); } diff --git a/drivers/misc/ti-st/st_kim.c b/drivers/misc/ti-st/st_kim.c index 9ff942a346ed..83269f1d16e3 100644 --- a/drivers/misc/ti-st/st_kim.c +++ b/drivers/misc/ti-st/st_kim.c @@ -468,6 +468,11 @@ long st_kim_start(void *kim_data) if (pdata->chip_enable) pdata->chip_enable(kim_gdata); + /* Configure BT nShutdown to HIGH state */ + gpio_set_value(kim_gdata->nshutdown, GPIO_LOW); + mdelay(5); /* FIXME: a proper toggle */ + gpio_set_value(kim_gdata->nshutdown, GPIO_HIGH); + mdelay(100); /* re-initialize the completion */ INIT_COMPLETION(kim_gdata->ldisc_installed); /* send notification to UIM */ @@ -509,7 +514,8 @@ long st_kim_start(void *kim_data) * (b) upon failure to either install ldisc or download firmware. * The function is responsible to (a) notify UIM about un-installation, * (b) flush UART if the ldisc was installed. - * (c) invoke platform's chip disabling routine. + * (c) reset BT_EN - pull down nshutdown at the end. + * (d) invoke platform's chip disabling routine. */ long st_kim_stop(void *kim_data) { @@ -541,6 +547,13 @@ long st_kim_stop(void *kim_data) err = -ETIMEDOUT; } + /* By default configure BT nShutdown to LOW state */ + gpio_set_value(kim_gdata->nshutdown, GPIO_LOW); + mdelay(1); + gpio_set_value(kim_gdata->nshutdown, GPIO_HIGH); + mdelay(1); + gpio_set_value(kim_gdata->nshutdown, GPIO_LOW); + /* platform specific disable */ if (pdata->chip_disable) pdata->chip_disable(kim_gdata); @@ -733,6 +746,20 @@ static int kim_probe(struct platform_device *pdev) /* refer to itself */ kim_gdata->core_data->kim_data = kim_gdata; + /* Claim the chip enable nShutdown gpio from the system */ + kim_gdata->nshutdown = pdata->nshutdown_gpio; + err = gpio_request(kim_gdata->nshutdown, "kim"); + if (unlikely(err)) { + pr_err(" gpio %ld request failed ", kim_gdata->nshutdown); + return err; + } + + /* Configure nShutdown GPIO as output=0 */ + err = gpio_direction_output(kim_gdata->nshutdown, 0); + if (unlikely(err)) { + pr_err(" unable to configure gpio %ld", kim_gdata->nshutdown); + return err; + } /* get reference of pdev for request_firmware */ kim_gdata->kim_pdev = pdev; @@ -779,10 +806,18 @@ err_core_init: static int kim_remove(struct platform_device *pdev) { + /* free the GPIOs requested */ + struct ti_st_plat_data *pdata = pdev->dev.platform_data; struct kim_data_s *kim_gdata; kim_gdata = dev_get_drvdata(&pdev->dev); + /* Free the Bluetooth/FM/GPIO + * nShutdown gpio from the system + */ + gpio_free(pdata->nshutdown_gpio); + pr_info("nshutdown GPIO Freed"); + debugfs_remove_recursive(kim_debugfs_dir); sysfs_remove_group(&pdev->dev.kobj, &uim_attr_grp); pr_info("sysfs entries removed"); diff --git a/drivers/mmc/host/mvsdio.c b/drivers/mmc/host/mvsdio.c index de4c20b3936c..f8dd36102949 100644 --- a/drivers/mmc/host/mvsdio.c +++ b/drivers/mmc/host/mvsdio.c @@ -50,8 +50,6 @@ struct mvsd_host { struct timer_list timer; struct mmc_host *mmc; struct device *dev; - struct resource *res; - int irq; struct clk *clk; int gpio_card_detect; int gpio_write_protect; @@ -718,10 +716,6 @@ static int __init mvsd_probe(struct platform_device *pdev) if (!r || irq < 0 || !mvsd_data) return -ENXIO; - r = request_mem_region(r->start, SZ_1K, DRIVER_NAME); - if (!r) - return -EBUSY; - mmc = mmc_alloc_host(sizeof(struct mvsd_host), &pdev->dev); if (!mmc) { ret = -ENOMEM; @@ -731,8 +725,8 @@ static int __init mvsd_probe(struct platform_device *pdev) host = mmc_priv(mmc); host->mmc = mmc; host->dev = &pdev->dev; - host->res = r; host->base_clock = mvsd_data->clock / 2; + host->clk = ERR_PTR(-EINVAL); mmc->ops = &mvsd_ops; @@ -752,7 +746,7 @@ static int __init mvsd_probe(struct platform_device *pdev) spin_lock_init(&host->lock); - host->base = ioremap(r->start, SZ_4K); + host->base = devm_request_and_ioremap(&pdev->dev, r); if (!host->base) { ret = -ENOMEM; goto out; @@ -765,44 +759,45 @@ static int __init mvsd_probe(struct platform_device *pdev) mvsd_power_down(host); - ret = request_irq(irq, mvsd_irq, 0, DRIVER_NAME, host); + ret = devm_request_irq(&pdev->dev, irq, mvsd_irq, 0, DRIVER_NAME, host); if (ret) { pr_err("%s: cannot assign irq %d\n", DRIVER_NAME, irq); goto out; - } else - host->irq = irq; + } /* Not all platforms can gate the clock, so it is not an error if the clock does not exists. */ - host->clk = clk_get(&pdev->dev, NULL); - if (!IS_ERR(host->clk)) { + host->clk = devm_clk_get(&pdev->dev, NULL); + if (!IS_ERR(host->clk)) clk_prepare_enable(host->clk); - } if (mvsd_data->gpio_card_detect) { - ret = gpio_request(mvsd_data->gpio_card_detect, - DRIVER_NAME " cd"); + ret = devm_gpio_request_one(&pdev->dev, + mvsd_data->gpio_card_detect, + GPIOF_IN, DRIVER_NAME " cd"); if (ret == 0) { - gpio_direction_input(mvsd_data->gpio_card_detect); irq = gpio_to_irq(mvsd_data->gpio_card_detect); - ret = request_irq(irq, mvsd_card_detect_irq, - IRQ_TYPE_EDGE_RISING | IRQ_TYPE_EDGE_FALLING, - DRIVER_NAME " cd", host); + ret = devm_request_irq(&pdev->dev, irq, + mvsd_card_detect_irq, + IRQ_TYPE_EDGE_RISING | + IRQ_TYPE_EDGE_FALLING, + DRIVER_NAME " cd", host); if (ret == 0) host->gpio_card_detect = mvsd_data->gpio_card_detect; else - gpio_free(mvsd_data->gpio_card_detect); + devm_gpio_free(&pdev->dev, + mvsd_data->gpio_card_detect); } } if (!host->gpio_card_detect) mmc->caps |= MMC_CAP_NEEDS_POLL; if (mvsd_data->gpio_write_protect) { - ret = gpio_request(mvsd_data->gpio_write_protect, - DRIVER_NAME " wp"); + ret = devm_gpio_request_one(&pdev->dev, + mvsd_data->gpio_write_protect, + GPIOF_IN, DRIVER_NAME " wp"); if (ret == 0) { - gpio_direction_input(mvsd_data->gpio_write_protect); host->gpio_write_protect = mvsd_data->gpio_write_protect; } @@ -824,26 +819,11 @@ static int __init mvsd_probe(struct platform_device *pdev) return 0; out: - if (host) { - if (host->irq) - free_irq(host->irq, host); - if (host->gpio_card_detect) { - free_irq(gpio_to_irq(host->gpio_card_detect), host); - gpio_free(host->gpio_card_detect); - } - if (host->gpio_write_protect) - gpio_free(host->gpio_write_protect); - if (host->base) - iounmap(host->base); - } - if (r) - release_resource(r); - if (mmc) - if (!IS_ERR_OR_NULL(host->clk)) { + if (mmc) { + if (!IS_ERR(host->clk)) clk_disable_unprepare(host->clk); - clk_put(host->clk); - } mmc_free_host(mmc); + } return ret; } @@ -852,28 +832,16 @@ static int __exit mvsd_remove(struct platform_device *pdev) { struct mmc_host *mmc = platform_get_drvdata(pdev); - if (mmc) { - struct mvsd_host *host = mmc_priv(mmc); + struct mvsd_host *host = mmc_priv(mmc); - if (host->gpio_card_detect) { - free_irq(gpio_to_irq(host->gpio_card_detect), host); - gpio_free(host->gpio_card_detect); - } - mmc_remove_host(mmc); - free_irq(host->irq, host); - if (host->gpio_write_protect) - gpio_free(host->gpio_write_protect); - del_timer_sync(&host->timer); - mvsd_power_down(host); - iounmap(host->base); - release_resource(host->res); + mmc_remove_host(mmc); + del_timer_sync(&host->timer); + mvsd_power_down(host); + + if (!IS_ERR(host->clk)) + clk_disable_unprepare(host->clk); + mmc_free_host(mmc); - if (!IS_ERR(host->clk)) { - clk_disable_unprepare(host->clk); - clk_put(host->clk); - } - mmc_free_host(mmc); - } platform_set_drvdata(pdev, NULL); return 0; } diff --git a/drivers/mmc/host/rtsx_pci_sdmmc.c b/drivers/mmc/host/rtsx_pci_sdmmc.c index 571915dfb218..f74b5adca642 100644 --- a/drivers/mmc/host/rtsx_pci_sdmmc.c +++ b/drivers/mmc/host/rtsx_pci_sdmmc.c @@ -1060,26 +1060,6 @@ static int sd_wait_voltage_stable_2(struct realtek_pci_sdmmc *host) return 0; } -static int sd_change_bank_voltage(struct realtek_pci_sdmmc *host, u8 voltage) -{ - struct rtsx_pcr *pcr = host->pcr; - int err; - - if (voltage == SD_IO_3V3) { - err = rtsx_pci_write_phy_register(pcr, 0x08, 0x4FC0 | 0x24); - if (err < 0) - return err; - } else if (voltage == SD_IO_1V8) { - err = rtsx_pci_write_phy_register(pcr, 0x08, 0x4C40 | 0x24); - if (err < 0) - return err; - } else { - return -EINVAL; - } - - return 0; -} - static int sdmmc_switch_voltage(struct mmc_host *mmc, struct mmc_ios *ios) { struct realtek_pci_sdmmc *host = mmc_priv(mmc); @@ -1098,11 +1078,11 @@ static int sdmmc_switch_voltage(struct mmc_host *mmc, struct mmc_ios *ios) rtsx_pci_start_run(pcr); if (ios->signal_voltage == MMC_SIGNAL_VOLTAGE_330) - voltage = SD_IO_3V3; + voltage = OUTPUT_3V3; else - voltage = SD_IO_1V8; + voltage = OUTPUT_1V8; - if (voltage == SD_IO_1V8) { + if (voltage == OUTPUT_1V8) { err = rtsx_pci_write_register(pcr, SD30_DRIVE_SEL, 0x07, DRIVER_TYPE_B); if (err < 0) @@ -1113,11 +1093,11 @@ static int sdmmc_switch_voltage(struct mmc_host *mmc, struct mmc_ios *ios) goto out; } - err = sd_change_bank_voltage(host, voltage); + err = rtsx_pci_switch_output_voltage(pcr, voltage); if (err < 0) goto out; - if (voltage == SD_IO_1V8) { + if (voltage == OUTPUT_1V8) { err = sd_wait_voltage_stable_2(host); if (err < 0) goto out; diff --git a/drivers/mtd/devices/Kconfig b/drivers/mtd/devices/Kconfig index 27f80cd8aef3..46dcb54c32ec 100644 --- a/drivers/mtd/devices/Kconfig +++ b/drivers/mtd/devices/Kconfig @@ -272,6 +272,7 @@ config MTD_DOCG3 tristate "M-Systems Disk-On-Chip G3" select BCH select BCH_CONST_PARAMS + select BITREVERSE ---help--- This provides an MTD device driver for the M-Systems DiskOnChip G3 devices. diff --git a/drivers/mtd/maps/physmap_of.c b/drivers/mtd/maps/physmap_of.c index 67cc73c18ddd..7901d72c9242 100644 --- a/drivers/mtd/maps/physmap_of.c +++ b/drivers/mtd/maps/physmap_of.c @@ -170,7 +170,7 @@ static int of_flash_probe(struct platform_device *dev) resource_size_t res_size; struct mtd_part_parser_data ppdata; bool map_indirect; - const char *mtd_name; + const char *mtd_name = NULL; match = of_match_device(of_flash_match, &dev->dev); if (!match) diff --git a/drivers/mtd/nand/bcm47xxnflash/ops_bcm4706.c b/drivers/mtd/nand/bcm47xxnflash/ops_bcm4706.c index 86c9a79b89b3..595de4012e71 100644 --- a/drivers/mtd/nand/bcm47xxnflash/ops_bcm4706.c +++ b/drivers/mtd/nand/bcm47xxnflash/ops_bcm4706.c @@ -17,8 +17,8 @@ #include "bcm47xxnflash.h" /* Broadcom uses 1'000'000 but it seems to be too many. Tests on WNDR4500 has - * shown 164 retries as maxiumum. */ -#define NFLASH_READY_RETRIES 1000 + * shown ~1000 retries as maxiumum. */ +#define NFLASH_READY_RETRIES 10000 #define NFLASH_SECTOR_SIZE 512 diff --git a/drivers/mtd/nand/davinci_nand.c b/drivers/mtd/nand/davinci_nand.c index 3502606f6480..feae55c7b880 100644 --- a/drivers/mtd/nand/davinci_nand.c +++ b/drivers/mtd/nand/davinci_nand.c @@ -523,7 +523,7 @@ static struct nand_ecclayout hwecc4_2048 __initconst = { static const struct of_device_id davinci_nand_of_match[] = { {.compatible = "ti,davinci-nand", }, {}, -} +}; MODULE_DEVICE_TABLE(of, davinci_nand_of_match); static struct davinci_nand_pdata diff --git a/drivers/mtd/nand/nand_base.c b/drivers/mtd/nand/nand_base.c index 8323ac991ad1..3766682a0289 100644 --- a/drivers/mtd/nand/nand_base.c +++ b/drivers/mtd/nand/nand_base.c @@ -2857,8 +2857,11 @@ static int nand_flash_detect_onfi(struct mtd_info *mtd, struct nand_chip *chip, int i; int val; - /* ONFI need to be probed in 8 bits mode */ - WARN_ON(chip->options & NAND_BUSWIDTH_16); + /* ONFI need to be probed in 8 bits mode, and 16 bits should be selected with NAND_BUSWIDTH_AUTO */ + if (chip->options & NAND_BUSWIDTH_16) { + pr_err("Trying ONFI probe in 16 bits mode, aborting !\n"); + return 0; + } /* Try ONFI for unknown chip or LP */ chip->cmdfunc(mtd, NAND_CMD_READID, 0x20, -1); if (chip->read_byte(mtd) != 'O' || chip->read_byte(mtd) != 'N' || diff --git a/drivers/net/can/c_can/c_can.c b/drivers/net/can/c_can/c_can.c index 5233b8f58d77..58607f196c9e 100644 --- a/drivers/net/can/c_can/c_can.c +++ b/drivers/net/can/c_can/c_can.c @@ -960,7 +960,7 @@ static int c_can_handle_bus_err(struct net_device *dev, break; case LEC_ACK_ERROR: netdev_dbg(dev, "ack error\n"); - cf->data[2] |= (CAN_ERR_PROT_LOC_ACK | + cf->data[3] |= (CAN_ERR_PROT_LOC_ACK | CAN_ERR_PROT_LOC_ACK_DEL); break; case LEC_BIT1_ERROR: @@ -973,7 +973,7 @@ static int c_can_handle_bus_err(struct net_device *dev, break; case LEC_CRC_ERROR: netdev_dbg(dev, "CRC error\n"); - cf->data[2] |= (CAN_ERR_PROT_LOC_CRC_SEQ | + cf->data[3] |= (CAN_ERR_PROT_LOC_CRC_SEQ | CAN_ERR_PROT_LOC_CRC_DEL); break; default: diff --git a/drivers/net/can/pch_can.c b/drivers/net/can/pch_can.c index 7d1748575b1f..5c314a961970 100644 --- a/drivers/net/can/pch_can.c +++ b/drivers/net/can/pch_can.c @@ -560,7 +560,7 @@ static void pch_can_error(struct net_device *ndev, u32 status) stats->rx_errors++; break; case PCH_CRC_ERR: - cf->data[2] |= CAN_ERR_PROT_LOC_CRC_SEQ | + cf->data[3] |= CAN_ERR_PROT_LOC_CRC_SEQ | CAN_ERR_PROT_LOC_CRC_DEL; priv->can.can_stats.bus_error++; stats->rx_errors++; diff --git a/drivers/net/can/ti_hecc.c b/drivers/net/can/ti_hecc.c index f898c6363729..300581b24ff3 100644 --- a/drivers/net/can/ti_hecc.c +++ b/drivers/net/can/ti_hecc.c @@ -746,12 +746,12 @@ static int ti_hecc_error(struct net_device *ndev, int int_status, } if (err_status & HECC_CANES_CRCE) { hecc_set_bit(priv, HECC_CANES, HECC_CANES_CRCE); - cf->data[2] |= CAN_ERR_PROT_LOC_CRC_SEQ | + cf->data[3] |= CAN_ERR_PROT_LOC_CRC_SEQ | CAN_ERR_PROT_LOC_CRC_DEL; } if (err_status & HECC_CANES_ACKE) { hecc_set_bit(priv, HECC_CANES, HECC_CANES_ACKE); - cf->data[2] |= CAN_ERR_PROT_LOC_ACK | + cf->data[3] |= CAN_ERR_PROT_LOC_ACK | CAN_ERR_PROT_LOC_ACK_DEL; } } diff --git a/drivers/net/ethernet/3com/3c574_cs.c b/drivers/net/ethernet/3com/3c574_cs.c index 66df93638085..ffd8de28a76a 100644 --- a/drivers/net/ethernet/3com/3c574_cs.c +++ b/drivers/net/ethernet/3com/3c574_cs.c @@ -432,7 +432,7 @@ static int tc574_config(struct pcmcia_device *link) netdev_info(dev, "%s at io %#3lx, irq %d, hw_addr %pM\n", cardname, dev->base_addr, dev->irq, dev->dev_addr); netdev_info(dev, " %dK FIFO split %s Rx:Tx, %sMII interface.\n", - 8 << config & Ram_size, + 8 << (config & Ram_size), ram_split[(config & Ram_split) >> Ram_split_shift], config & Autoselect ? "autoselect " : ""); diff --git a/drivers/net/ethernet/adi/Kconfig b/drivers/net/ethernet/adi/Kconfig index e49c0eff040b..a9481606bbcd 100644 --- a/drivers/net/ethernet/adi/Kconfig +++ b/drivers/net/ethernet/adi/Kconfig @@ -61,6 +61,7 @@ config BFIN_RX_DESC_NUM config BFIN_MAC_USE_HWSTAMP bool "Use IEEE 1588 hwstamp" + depends on BFIN_MAC && BF518 select PTP_1588_CLOCK default y ---help--- diff --git a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.c b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.c index 01588b66a38c..f771ddfba646 100644 --- a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.c +++ b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.c @@ -80,12 +80,37 @@ static inline void bnx2x_move_fp(struct bnx2x *bp, int from, int to) new_txdata_index = new_max_eth_txqs + FCOE_TXQ_IDX_OFFSET; } - memcpy(&bp->bnx2x_txq[old_txdata_index], - &bp->bnx2x_txq[new_txdata_index], + memcpy(&bp->bnx2x_txq[new_txdata_index], + &bp->bnx2x_txq[old_txdata_index], sizeof(struct bnx2x_fp_txdata)); to_fp->txdata_ptr[0] = &bp->bnx2x_txq[new_txdata_index]; } +/** + * bnx2x_shrink_eth_fp - guarantees fastpath structures stay intact + * + * @bp: driver handle + * @delta: number of eth queues which were not allocated + */ +static void bnx2x_shrink_eth_fp(struct bnx2x *bp, int delta) +{ + int i, cos, old_eth_num = BNX2X_NUM_ETH_QUEUES(bp); + + /* Queue pointer cannot be re-set on an fp-basis, as moving pointer + * backward along the array could cause memory to be overriden + */ + for (cos = 1; cos < bp->max_cos; cos++) { + for (i = 0; i < old_eth_num - delta; i++) { + struct bnx2x_fastpath *fp = &bp->fp[i]; + int new_idx = cos * (old_eth_num - delta) + i; + + memcpy(&bp->bnx2x_txq[new_idx], fp->txdata_ptr[cos], + sizeof(struct bnx2x_fp_txdata)); + fp->txdata_ptr[cos] = &bp->bnx2x_txq[new_idx]; + } + } +} + int load_count[2][3] = { {0} }; /* per-path: 0-common, 1-port0, 2-port1 */ /* free skb in the packet ring at pos idx @@ -3863,6 +3888,7 @@ int bnx2x_alloc_fp_mem(struct bnx2x *bp) int delta = BNX2X_NUM_ETH_QUEUES(bp) - i; WARN_ON(delta < 0); + bnx2x_shrink_eth_fp(bp, delta); if (CNIC_SUPPORT(bp)) /* move non eth FPs next to last eth FP * must be done in that order diff --git a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_ethtool.c b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_ethtool.c index 277f17e3c8f8..a427b49a886c 100644 --- a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_ethtool.c +++ b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_ethtool.c @@ -2777,10 +2777,10 @@ static int bnx2x_set_rss_flags(struct bnx2x *bp, struct ethtool_rxnfc *info) } else if ((info->flow_type == UDP_V6_FLOW) && (bp->rss_conf_obj.udp_rss_v6 != udp_rss_requested)) { bp->rss_conf_obj.udp_rss_v6 = udp_rss_requested; - return bnx2x_config_rss_pf(bp, &bp->rss_conf_obj, 0); DP(BNX2X_MSG_ETHTOOL, "rss re-configured, UDP 4-tupple %s\n", udp_rss_requested ? "enabled" : "disabled"); + return bnx2x_config_rss_pf(bp, &bp->rss_conf_obj, 0); } else { return 0; } diff --git a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_main.c b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_main.c index 940ef859dc60..5523da3afcdc 100644 --- a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_main.c +++ b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_main.c @@ -127,6 +127,17 @@ MODULE_PARM_DESC(debug, " Default debug msglevel"); struct workqueue_struct *bnx2x_wq; +struct bnx2x_mac_vals { + u32 xmac_addr; + u32 xmac_val; + u32 emac_addr; + u32 emac_val; + u32 umac_addr; + u32 umac_val; + u32 bmac_addr; + u32 bmac_val[2]; +}; + enum bnx2x_board_type { BCM57710 = 0, BCM57711, @@ -9420,12 +9431,19 @@ static inline void bnx2x_undi_int_disable(struct bnx2x *bp) bnx2x_undi_int_disable_e1h(bp); } -static void bnx2x_prev_unload_close_mac(struct bnx2x *bp) +static void bnx2x_prev_unload_close_mac(struct bnx2x *bp, + struct bnx2x_mac_vals *vals) { u32 val, base_addr, offset, mask, reset_reg; bool mac_stopped = false; u8 port = BP_PORT(bp); + /* reset addresses as they also mark which values were changed */ + vals->bmac_addr = 0; + vals->umac_addr = 0; + vals->xmac_addr = 0; + vals->emac_addr = 0; + reset_reg = REG_RD(bp, MISC_REG_RESET_REG_2); if (!CHIP_IS_E3(bp)) { @@ -9447,14 +9465,18 @@ static void bnx2x_prev_unload_close_mac(struct bnx2x *bp) */ wb_data[0] = REG_RD(bp, base_addr + offset); wb_data[1] = REG_RD(bp, base_addr + offset + 0x4); + vals->bmac_addr = base_addr + offset; + vals->bmac_val[0] = wb_data[0]; + vals->bmac_val[1] = wb_data[1]; wb_data[0] &= ~BMAC_CONTROL_RX_ENABLE; - REG_WR(bp, base_addr + offset, wb_data[0]); - REG_WR(bp, base_addr + offset + 0x4, wb_data[1]); + REG_WR(bp, vals->bmac_addr, wb_data[0]); + REG_WR(bp, vals->bmac_addr + 0x4, wb_data[1]); } BNX2X_DEV_INFO("Disable emac Rx\n"); - REG_WR(bp, NIG_REG_NIG_EMAC0_EN + BP_PORT(bp)*4, 0); - + vals->emac_addr = NIG_REG_NIG_EMAC0_EN + BP_PORT(bp)*4; + vals->emac_val = REG_RD(bp, vals->emac_addr); + REG_WR(bp, vals->emac_addr, 0); mac_stopped = true; } else { if (reset_reg & MISC_REGISTERS_RESET_REG_2_XMAC) { @@ -9465,14 +9487,18 @@ static void bnx2x_prev_unload_close_mac(struct bnx2x *bp) val & ~(1 << 1)); REG_WR(bp, base_addr + XMAC_REG_PFC_CTRL_HI, val | (1 << 1)); - REG_WR(bp, base_addr + XMAC_REG_CTRL, 0); + vals->xmac_addr = base_addr + XMAC_REG_CTRL; + vals->xmac_val = REG_RD(bp, vals->xmac_addr); + REG_WR(bp, vals->xmac_addr, 0); mac_stopped = true; } mask = MISC_REGISTERS_RESET_REG_2_UMAC0 << port; if (mask & reset_reg) { BNX2X_DEV_INFO("Disable umac Rx\n"); base_addr = BP_PORT(bp) ? GRCBASE_UMAC1 : GRCBASE_UMAC0; - REG_WR(bp, base_addr + UMAC_REG_COMMAND_CONFIG, 0); + vals->umac_addr = base_addr + UMAC_REG_COMMAND_CONFIG; + vals->umac_val = REG_RD(bp, vals->umac_addr); + REG_WR(bp, vals->umac_addr, 0); mac_stopped = true; } } @@ -9664,12 +9690,16 @@ static int bnx2x_prev_unload_common(struct bnx2x *bp) { u32 reset_reg, tmp_reg = 0, rc; bool prev_undi = false; + struct bnx2x_mac_vals mac_vals; + /* It is possible a previous function received 'common' answer, * but hasn't loaded yet, therefore creating a scenario of * multiple functions receiving 'common' on the same path. */ BNX2X_DEV_INFO("Common unload Flow\n"); + memset(&mac_vals, 0, sizeof(mac_vals)); + if (bnx2x_prev_is_path_marked(bp)) return bnx2x_prev_mcp_done(bp); @@ -9680,7 +9710,10 @@ static int bnx2x_prev_unload_common(struct bnx2x *bp) u32 timer_count = 1000; /* Close the MAC Rx to prevent BRB from filling up */ - bnx2x_prev_unload_close_mac(bp); + bnx2x_prev_unload_close_mac(bp, &mac_vals); + + /* close LLH filters towards the BRB */ + bnx2x_set_rx_filter(&bp->link_params, 0); /* Check if the UNDI driver was previously loaded * UNDI driver initializes CID offset for normal bell to 0x7 @@ -9727,6 +9760,17 @@ static int bnx2x_prev_unload_common(struct bnx2x *bp) /* No packets are in the pipeline, path is ready for reset */ bnx2x_reset_common(bp); + if (mac_vals.xmac_addr) + REG_WR(bp, mac_vals.xmac_addr, mac_vals.xmac_val); + if (mac_vals.umac_addr) + REG_WR(bp, mac_vals.umac_addr, mac_vals.umac_val); + if (mac_vals.emac_addr) + REG_WR(bp, mac_vals.emac_addr, mac_vals.emac_val); + if (mac_vals.bmac_addr) { + REG_WR(bp, mac_vals.bmac_addr, mac_vals.bmac_val[0]); + REG_WR(bp, mac_vals.bmac_addr + 4, mac_vals.bmac_val[1]); + } + rc = bnx2x_prev_mark_path(bp, prev_undi); if (rc) { bnx2x_prev_mcp_done(bp); diff --git a/drivers/net/ethernet/broadcom/tg3.c b/drivers/net/ethernet/broadcom/tg3.c index 78ea90c40e19..bdb086934cd9 100644 --- a/drivers/net/ethernet/broadcom/tg3.c +++ b/drivers/net/ethernet/broadcom/tg3.c @@ -1283,14 +1283,26 @@ static int tg3_phy_auxctl_write(struct tg3 *tp, int reg, u32 set) return tg3_writephy(tp, MII_TG3_AUX_CTRL, set | reg); } -#define TG3_PHY_AUXCTL_SMDSP_ENABLE(tp) \ - tg3_phy_auxctl_write((tp), MII_TG3_AUXCTL_SHDWSEL_AUXCTL, \ - MII_TG3_AUXCTL_ACTL_SMDSP_ENA | \ - MII_TG3_AUXCTL_ACTL_TX_6DB) +static int tg3_phy_toggle_auxctl_smdsp(struct tg3 *tp, bool enable) +{ + u32 val; + int err; -#define TG3_PHY_AUXCTL_SMDSP_DISABLE(tp) \ - tg3_phy_auxctl_write((tp), MII_TG3_AUXCTL_SHDWSEL_AUXCTL, \ - MII_TG3_AUXCTL_ACTL_TX_6DB); + err = tg3_phy_auxctl_read(tp, MII_TG3_AUXCTL_SHDWSEL_AUXCTL, &val); + + if (err) + return err; + if (enable) + + val |= MII_TG3_AUXCTL_ACTL_SMDSP_ENA; + else + val &= ~MII_TG3_AUXCTL_ACTL_SMDSP_ENA; + + err = tg3_phy_auxctl_write((tp), MII_TG3_AUXCTL_SHDWSEL_AUXCTL, + val | MII_TG3_AUXCTL_ACTL_TX_6DB); + + return err; +} static int tg3_bmcr_reset(struct tg3 *tp) { @@ -2223,7 +2235,7 @@ static void tg3_phy_apply_otp(struct tg3 *tp) otp = tp->phy_otp; - if (TG3_PHY_AUXCTL_SMDSP_ENABLE(tp)) + if (tg3_phy_toggle_auxctl_smdsp(tp, true)) return; phy = ((otp & TG3_OTP_AGCTGT_MASK) >> TG3_OTP_AGCTGT_SHIFT); @@ -2248,7 +2260,7 @@ static void tg3_phy_apply_otp(struct tg3 *tp) ((otp & TG3_OTP_RCOFF_MASK) >> TG3_OTP_RCOFF_SHIFT); tg3_phydsp_write(tp, MII_TG3_DSP_EXP97, phy); - TG3_PHY_AUXCTL_SMDSP_DISABLE(tp); + tg3_phy_toggle_auxctl_smdsp(tp, false); } static void tg3_phy_eee_adjust(struct tg3 *tp, u32 current_link_up) @@ -2284,9 +2296,9 @@ static void tg3_phy_eee_adjust(struct tg3 *tp, u32 current_link_up) if (!tp->setlpicnt) { if (current_link_up == 1 && - !TG3_PHY_AUXCTL_SMDSP_ENABLE(tp)) { + !tg3_phy_toggle_auxctl_smdsp(tp, true)) { tg3_phydsp_write(tp, MII_TG3_DSP_TAP26, 0x0000); - TG3_PHY_AUXCTL_SMDSP_DISABLE(tp); + tg3_phy_toggle_auxctl_smdsp(tp, false); } val = tr32(TG3_CPMU_EEE_MODE); @@ -2302,11 +2314,11 @@ static void tg3_phy_eee_enable(struct tg3 *tp) (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5717 || GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5719 || tg3_flag(tp, 57765_CLASS)) && - !TG3_PHY_AUXCTL_SMDSP_ENABLE(tp)) { + !tg3_phy_toggle_auxctl_smdsp(tp, true)) { val = MII_TG3_DSP_TAP26_ALNOKO | MII_TG3_DSP_TAP26_RMRXSTO; tg3_phydsp_write(tp, MII_TG3_DSP_TAP26, val); - TG3_PHY_AUXCTL_SMDSP_DISABLE(tp); + tg3_phy_toggle_auxctl_smdsp(tp, false); } val = tr32(TG3_CPMU_EEE_MODE); @@ -2450,7 +2462,7 @@ static int tg3_phy_reset_5703_4_5(struct tg3 *tp) tg3_writephy(tp, MII_CTRL1000, CTL1000_AS_MASTER | CTL1000_ENABLE_MASTER); - err = TG3_PHY_AUXCTL_SMDSP_ENABLE(tp); + err = tg3_phy_toggle_auxctl_smdsp(tp, true); if (err) return err; @@ -2471,7 +2483,7 @@ static int tg3_phy_reset_5703_4_5(struct tg3 *tp) tg3_writephy(tp, MII_TG3_DSP_ADDRESS, 0x8200); tg3_writephy(tp, MII_TG3_DSP_CONTROL, 0x0000); - TG3_PHY_AUXCTL_SMDSP_DISABLE(tp); + tg3_phy_toggle_auxctl_smdsp(tp, false); tg3_writephy(tp, MII_CTRL1000, phy9_orig); @@ -2572,10 +2584,10 @@ static int tg3_phy_reset(struct tg3 *tp) out: if ((tp->phy_flags & TG3_PHYFLG_ADC_BUG) && - !TG3_PHY_AUXCTL_SMDSP_ENABLE(tp)) { + !tg3_phy_toggle_auxctl_smdsp(tp, true)) { tg3_phydsp_write(tp, 0x201f, 0x2aaa); tg3_phydsp_write(tp, 0x000a, 0x0323); - TG3_PHY_AUXCTL_SMDSP_DISABLE(tp); + tg3_phy_toggle_auxctl_smdsp(tp, false); } if (tp->phy_flags & TG3_PHYFLG_5704_A0_BUG) { @@ -2584,14 +2596,14 @@ out: } if (tp->phy_flags & TG3_PHYFLG_BER_BUG) { - if (!TG3_PHY_AUXCTL_SMDSP_ENABLE(tp)) { + if (!tg3_phy_toggle_auxctl_smdsp(tp, true)) { tg3_phydsp_write(tp, 0x000a, 0x310b); tg3_phydsp_write(tp, 0x201f, 0x9506); tg3_phydsp_write(tp, 0x401f, 0x14e2); - TG3_PHY_AUXCTL_SMDSP_DISABLE(tp); + tg3_phy_toggle_auxctl_smdsp(tp, false); } } else if (tp->phy_flags & TG3_PHYFLG_JITTER_BUG) { - if (!TG3_PHY_AUXCTL_SMDSP_ENABLE(tp)) { + if (!tg3_phy_toggle_auxctl_smdsp(tp, true)) { tg3_writephy(tp, MII_TG3_DSP_ADDRESS, 0x000a); if (tp->phy_flags & TG3_PHYFLG_ADJUST_TRIM) { tg3_writephy(tp, MII_TG3_DSP_RW_PORT, 0x110b); @@ -2600,7 +2612,7 @@ out: } else tg3_writephy(tp, MII_TG3_DSP_RW_PORT, 0x010b); - TG3_PHY_AUXCTL_SMDSP_DISABLE(tp); + tg3_phy_toggle_auxctl_smdsp(tp, false); } } @@ -4009,7 +4021,7 @@ static int tg3_phy_autoneg_cfg(struct tg3 *tp, u32 advertise, u32 flowctrl) tw32(TG3_CPMU_EEE_MODE, tr32(TG3_CPMU_EEE_MODE) & ~TG3_CPMU_EEEMD_LPI_ENABLE); - err = TG3_PHY_AUXCTL_SMDSP_ENABLE(tp); + err = tg3_phy_toggle_auxctl_smdsp(tp, true); if (!err) { u32 err2; @@ -4042,7 +4054,7 @@ static int tg3_phy_autoneg_cfg(struct tg3 *tp, u32 advertise, u32 flowctrl) MII_TG3_DSP_CH34TP2_HIBW01); } - err2 = TG3_PHY_AUXCTL_SMDSP_DISABLE(tp); + err2 = tg3_phy_toggle_auxctl_smdsp(tp, false); if (!err) err = err2; } @@ -6950,6 +6962,9 @@ static void tg3_poll_controller(struct net_device *dev) int i; struct tg3 *tp = netdev_priv(dev); + if (tg3_irq_sync(tp)) + return; + for (i = 0; i < tp->irq_cnt; i++) tg3_interrupt(tp->napi[i].irq_vec, &tp->napi[i]); } @@ -16367,6 +16382,7 @@ static int tg3_init_one(struct pci_dev *pdev, tp->pm_cap = pm_cap; tp->rx_mode = TG3_DEF_RX_MODE; tp->tx_mode = TG3_DEF_TX_MODE; + tp->irq_sync = 1; if (tg3_debug > 0) tp->msg_enable = tg3_debug; diff --git a/drivers/net/ethernet/calxeda/xgmac.c b/drivers/net/ethernet/calxeda/xgmac.c index b407043ce9b0..f7f02900f650 100644 --- a/drivers/net/ethernet/calxeda/xgmac.c +++ b/drivers/net/ethernet/calxeda/xgmac.c @@ -548,6 +548,10 @@ static int desc_get_rx_status(struct xgmac_priv *priv, struct xgmac_dma_desc *p) return -1; } + /* All frames should fit into a single buffer */ + if (!(status & RXDESC_FIRST_SEG) || !(status & RXDESC_LAST_SEG)) + return -1; + /* Check if packet has checksum already */ if ((status & RXDESC_FRAME_TYPE) && (status & RXDESC_EXT_STATUS) && !(ext_status & RXDESC_IP_PAYLOAD_MASK)) diff --git a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c index f0718e1a8369..c306df7d4568 100644 --- a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c +++ b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c @@ -1994,9 +1994,20 @@ static int set_coalesce(struct net_device *dev, struct ethtool_coalesce *c) { const struct port_info *pi = netdev_priv(dev); struct adapter *adap = pi->adapter; - - return set_rxq_intr_params(adap, &adap->sge.ethrxq[pi->first_qset].rspq, - c->rx_coalesce_usecs, c->rx_max_coalesced_frames); + struct sge_rspq *q; + int i; + int r = 0; + + for (i = pi->first_qset; i < pi->first_qset + pi->nqsets; i++) { + q = &adap->sge.ethrxq[i].rspq; + r = set_rxq_intr_params(adap, q, c->rx_coalesce_usecs, + c->rx_max_coalesced_frames); + if (r) { + dev_err(&dev->dev, "failed to set coalesce %d\n", r); + break; + } + } + return r; } static int get_coalesce(struct net_device *dev, struct ethtool_coalesce *c) diff --git a/drivers/net/ethernet/emulex/benet/be.h b/drivers/net/ethernet/emulex/benet/be.h index 3bc1912afba9..4eba17b83ba8 100644 --- a/drivers/net/ethernet/emulex/benet/be.h +++ b/drivers/net/ethernet/emulex/benet/be.h @@ -190,6 +190,7 @@ struct be_eq_obj { u8 idx; /* array index */ u16 tx_budget; + u16 spurious_intr; struct napi_struct napi; struct be_adapter *adapter; } ____cacheline_aligned_in_smp; diff --git a/drivers/net/ethernet/emulex/benet/be_main.c b/drivers/net/ethernet/emulex/benet/be_main.c index 9dca22be8125..5c995700e534 100644 --- a/drivers/net/ethernet/emulex/benet/be_main.c +++ b/drivers/net/ethernet/emulex/benet/be_main.c @@ -2026,19 +2026,30 @@ static irqreturn_t be_intx(int irq, void *dev) struct be_adapter *adapter = eqo->adapter; int num_evts = 0; - /* On Lancer, clear-intr bit of the EQ DB does not work. - * INTx is de-asserted only on notifying num evts. + /* IRQ is not expected when NAPI is scheduled as the EQ + * will not be armed. + * But, this can happen on Lancer INTx where it takes + * a while to de-assert INTx or in BE2 where occasionaly + * an interrupt may be raised even when EQ is unarmed. + * If NAPI is already scheduled, then counting & notifying + * events will orphan them. */ - if (lancer_chip(adapter)) + if (napi_schedule_prep(&eqo->napi)) { num_evts = events_get(eqo); + __napi_schedule(&eqo->napi); + if (num_evts) + eqo->spurious_intr = 0; + } + be_eq_notify(adapter, eqo->q.id, false, true, num_evts); - /* The EQ-notify may not de-assert INTx rightaway, causing - * the ISR to be invoked again. So, return HANDLED even when - * num_evts is zero. + /* Return IRQ_HANDLED only for the the first spurious intr + * after a valid intr to stop the kernel from branding + * this irq as a bad one! */ - be_eq_notify(adapter, eqo->q.id, false, true, num_evts); - napi_schedule(&eqo->napi); - return IRQ_HANDLED; + if (num_evts || eqo->spurious_intr++ == 0) + return IRQ_HANDLED; + else + return IRQ_NONE; } static irqreturn_t be_msix(int irq, void *dev) diff --git a/drivers/net/ethernet/intel/ixgbe/Makefile b/drivers/net/ethernet/intel/ixgbe/Makefile index f3a632bf8d96..687c83d1bdab 100644 --- a/drivers/net/ethernet/intel/ixgbe/Makefile +++ b/drivers/net/ethernet/intel/ixgbe/Makefile @@ -32,7 +32,7 @@ obj-$(CONFIG_IXGBE) += ixgbe.o -ixgbe-objs := ixgbe_main.o ixgbe_common.o ixgbe_ethtool.o ixgbe_debugfs.o\ +ixgbe-objs := ixgbe_main.o ixgbe_common.o ixgbe_ethtool.o \ ixgbe_82599.o ixgbe_82598.o ixgbe_phy.o ixgbe_sriov.o \ ixgbe_mbx.o ixgbe_x540.o ixgbe_lib.o ixgbe_ptp.o @@ -40,4 +40,5 @@ ixgbe-$(CONFIG_IXGBE_DCB) += ixgbe_dcb.o ixgbe_dcb_82598.o \ ixgbe_dcb_82599.o ixgbe_dcb_nl.o ixgbe-$(CONFIG_IXGBE_HWMON) += ixgbe_sysfs.o +ixgbe-$(CONFIG_DEBUG_FS) += ixgbe_debugfs.o ixgbe-$(CONFIG_FCOE:m=y) += ixgbe_fcoe.o diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_debugfs.c b/drivers/net/ethernet/intel/ixgbe/ixgbe_debugfs.c index 50aa546b8c7a..3504686d3af5 100644 --- a/drivers/net/ethernet/intel/ixgbe/ixgbe_debugfs.c +++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_debugfs.c @@ -24,9 +24,6 @@ Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 *******************************************************************************/ - -#ifdef CONFIG_DEBUG_FS - #include <linux/debugfs.h> #include <linux/module.h> @@ -277,5 +274,3 @@ void ixgbe_dbg_exit(void) { debugfs_remove_recursive(ixgbe_dbg_root); } - -#endif /* CONFIG_DEBUG_FS */ diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_ptp.c b/drivers/net/ethernet/intel/ixgbe/ixgbe_ptp.c index 1a751c9d09c4..bb9256a1b0a9 100644 --- a/drivers/net/ethernet/intel/ixgbe/ixgbe_ptp.c +++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_ptp.c @@ -660,11 +660,11 @@ int ixgbe_ptp_hwtstamp_ioctl(struct ixgbe_adapter *adapter, break; case HWTSTAMP_FILTER_PTP_V1_L4_SYNC: tsync_rx_ctl |= IXGBE_TSYNCRXCTL_TYPE_L4_V1; - tsync_rx_mtrl = IXGBE_RXMTRL_V1_SYNC_MSG; + tsync_rx_mtrl |= IXGBE_RXMTRL_V1_SYNC_MSG; break; case HWTSTAMP_FILTER_PTP_V1_L4_DELAY_REQ: tsync_rx_ctl |= IXGBE_TSYNCRXCTL_TYPE_L4_V1; - tsync_rx_mtrl = IXGBE_RXMTRL_V1_DELAY_REQ_MSG; + tsync_rx_mtrl |= IXGBE_RXMTRL_V1_DELAY_REQ_MSG; break; case HWTSTAMP_FILTER_PTP_V2_EVENT: case HWTSTAMP_FILTER_PTP_V2_L2_EVENT: diff --git a/drivers/net/ethernet/mellanox/mlx4/en_tx.c b/drivers/net/ethernet/mellanox/mlx4/en_tx.c index 2b799f4f1c37..6771b69f40d5 100644 --- a/drivers/net/ethernet/mellanox/mlx4/en_tx.c +++ b/drivers/net/ethernet/mellanox/mlx4/en_tx.c @@ -630,10 +630,15 @@ netdev_tx_t mlx4_en_xmit(struct sk_buff *skb, struct net_device *dev) ring->tx_csum++; } - /* Copy dst mac address to wqe */ - ethh = (struct ethhdr *)skb->data; - tx_desc->ctrl.srcrb_flags16[0] = get_unaligned((__be16 *)ethh->h_dest); - tx_desc->ctrl.imm = get_unaligned((__be32 *)(ethh->h_dest + 2)); + if (mlx4_is_mfunc(mdev->dev) || priv->validate_loopback) { + /* Copy dst mac address to wqe. This allows loopback in eSwitch, + * so that VFs and PF can communicate with each other + */ + ethh = (struct ethhdr *)skb->data; + tx_desc->ctrl.srcrb_flags16[0] = get_unaligned((__be16 *)ethh->h_dest); + tx_desc->ctrl.imm = get_unaligned((__be32 *)(ethh->h_dest + 2)); + } + /* Handle LSO (TSO) packets */ if (lso_header_size) { /* Mark opcode as LSO */ diff --git a/drivers/net/ethernet/mellanox/mlx4/main.c b/drivers/net/ethernet/mellanox/mlx4/main.c index e1bafffbc3b1..a6542d75374c 100644 --- a/drivers/net/ethernet/mellanox/mlx4/main.c +++ b/drivers/net/ethernet/mellanox/mlx4/main.c @@ -1790,15 +1790,8 @@ static void mlx4_enable_msi_x(struct mlx4_dev *dev) int i; if (msi_x) { - /* In multifunction mode each function gets 2 msi-X vectors - * one for data path completions anf the other for asynch events - * or command completions */ - if (mlx4_is_mfunc(dev)) { - nreq = 2; - } else { - nreq = min_t(int, dev->caps.num_eqs - - dev->caps.reserved_eqs, nreq); - } + nreq = min_t(int, dev->caps.num_eqs - dev->caps.reserved_eqs, + nreq); entries = kcalloc(nreq, sizeof *entries, GFP_KERNEL); if (!entries) diff --git a/drivers/net/ethernet/qlogic/netxen/netxen_nic_init.c b/drivers/net/ethernet/qlogic/netxen/netxen_nic_init.c index bc165f4d0f65..695667d471a1 100644 --- a/drivers/net/ethernet/qlogic/netxen/netxen_nic_init.c +++ b/drivers/net/ethernet/qlogic/netxen/netxen_nic_init.c @@ -144,7 +144,7 @@ void netxen_release_tx_buffers(struct netxen_adapter *adapter) buffrag->length, PCI_DMA_TODEVICE); buffrag->dma = 0ULL; } - for (j = 0; j < cmd_buf->frag_count; j++) { + for (j = 1; j < cmd_buf->frag_count; j++) { buffrag++; if (buffrag->dma) { pci_unmap_page(adapter->pdev, buffrag->dma, diff --git a/drivers/net/ethernet/qlogic/netxen/netxen_nic_main.c b/drivers/net/ethernet/qlogic/netxen/netxen_nic_main.c index 6098fd4adfeb..69e321a65077 100644 --- a/drivers/net/ethernet/qlogic/netxen/netxen_nic_main.c +++ b/drivers/net/ethernet/qlogic/netxen/netxen_nic_main.c @@ -1963,10 +1963,12 @@ unwind: while (--i >= 0) { nf = &pbuf->frag_array[i+1]; pci_unmap_page(pdev, nf->dma, nf->length, PCI_DMA_TODEVICE); + nf->dma = 0ULL; } nf = &pbuf->frag_array[0]; pci_unmap_single(pdev, nf->dma, skb_headlen(skb), PCI_DMA_TODEVICE); + nf->dma = 0ULL; out_err: return -ENOMEM; diff --git a/drivers/net/ethernet/qlogic/qlge/qlge_main.c b/drivers/net/ethernet/qlogic/qlge/qlge_main.c index f80cd975daed..3e73742024b0 100644 --- a/drivers/net/ethernet/qlogic/qlge/qlge_main.c +++ b/drivers/net/ethernet/qlogic/qlge/qlge_main.c @@ -4678,7 +4678,7 @@ static int qlge_probe(struct pci_dev *pdev, qdev = netdev_priv(ndev); SET_NETDEV_DEV(ndev, &pdev->dev); ndev->hw_features = NETIF_F_SG | NETIF_F_IP_CSUM | - NETIF_F_TSO | NETIF_F_TSO6 | NETIF_F_TSO_ECN | + NETIF_F_TSO | NETIF_F_TSO_ECN | NETIF_F_HW_VLAN_TX | NETIF_F_RXCSUM; ndev->features = ndev->hw_features | NETIF_F_HW_VLAN_RX | NETIF_F_HW_VLAN_FILTER; diff --git a/drivers/net/ethernet/realtek/r8169.c b/drivers/net/ethernet/realtek/r8169.c index ed96f309bca8..11702324a071 100644 --- a/drivers/net/ethernet/realtek/r8169.c +++ b/drivers/net/ethernet/realtek/r8169.c @@ -1826,8 +1826,6 @@ static void rtl8169_rx_vlan_tag(struct RxDesc *desc, struct sk_buff *skb) if (opts2 & RxVlanTag) __vlan_hwaccel_put_tag(skb, swab16(opts2 & 0xffff)); - - desc->opts2 = 0; } static int rtl8169_gset_tbi(struct net_device *dev, struct ethtool_cmd *cmd) @@ -6064,8 +6062,6 @@ static int rtl_rx(struct net_device *dev, struct rtl8169_private *tp, u32 budget !(status & (RxRWT | RxFOVF)) && (dev->features & NETIF_F_RXALL)) goto process_pkt; - - rtl8169_mark_to_asic(desc, rx_buf_sz); } else { struct sk_buff *skb; dma_addr_t addr; @@ -6086,16 +6082,14 @@ process_pkt: if (unlikely(rtl8169_fragmented_frame(status))) { dev->stats.rx_dropped++; dev->stats.rx_length_errors++; - rtl8169_mark_to_asic(desc, rx_buf_sz); - continue; + goto release_descriptor; } skb = rtl8169_try_rx_copy(tp->Rx_databuff[entry], tp, pkt_size, addr); - rtl8169_mark_to_asic(desc, rx_buf_sz); if (!skb) { dev->stats.rx_dropped++; - continue; + goto release_descriptor; } rtl8169_rx_csum(skb, status); @@ -6111,13 +6105,10 @@ process_pkt: tp->rx_stats.bytes += pkt_size; u64_stats_update_end(&tp->rx_stats.syncp); } - - /* Work around for AMD plateform. */ - if ((desc->opts2 & cpu_to_le32(0xfffe000)) && - (tp->mac_version == RTL_GIGA_MAC_VER_05)) { - desc->opts2 = 0; - cur_rx++; - } +release_descriptor: + desc->opts2 = 0; + wmb(); + rtl8169_mark_to_asic(desc, rx_buf_sz); } count = cur_rx - tp->cur_rx; diff --git a/drivers/net/ethernet/xilinx/Kconfig b/drivers/net/ethernet/xilinx/Kconfig index 5778a4ae1164..122d60c0481b 100644 --- a/drivers/net/ethernet/xilinx/Kconfig +++ b/drivers/net/ethernet/xilinx/Kconfig @@ -27,7 +27,7 @@ config XILINX_EMACLITE config XILINX_AXI_EMAC tristate "Xilinx 10/100/1000 AXI Ethernet support" - depends on (PPC32 || MICROBLAZE) + depends on MICROBLAZE select PHYLIB ---help--- This driver supports the 10/100/1000 Ethernet from Xilinx for the diff --git a/drivers/net/ethernet/xilinx/xilinx_axienet_main.c b/drivers/net/ethernet/xilinx/xilinx_axienet_main.c index d9f69b82cc4f..6f47100e58d7 100644 --- a/drivers/net/ethernet/xilinx/xilinx_axienet_main.c +++ b/drivers/net/ethernet/xilinx/xilinx_axienet_main.c @@ -1590,7 +1590,7 @@ static int axienet_of_probe(struct platform_device *op) lp->rx_irq = irq_of_parse_and_map(np, 1); lp->tx_irq = irq_of_parse_and_map(np, 0); of_node_put(np); - if ((lp->rx_irq == NO_IRQ) || (lp->tx_irq == NO_IRQ)) { + if ((lp->rx_irq <= 0) || (lp->tx_irq <= 0)) { dev_err(&op->dev, "could not determine irqs\n"); ret = -ENOMEM; goto err_iounmap_2; diff --git a/drivers/net/hyperv/hyperv_net.h b/drivers/net/hyperv/hyperv_net.h index 5fd6f4674326..e6fe0d80d612 100644 --- a/drivers/net/hyperv/hyperv_net.h +++ b/drivers/net/hyperv/hyperv_net.h @@ -84,7 +84,7 @@ struct hv_netvsc_packet { }; struct netvsc_device_info { - unsigned char mac_adr[6]; + unsigned char mac_adr[ETH_ALEN]; bool link_state; /* 0 - link up, 1 - link down */ int ring_size; }; diff --git a/drivers/net/hyperv/netvsc_drv.c b/drivers/net/hyperv/netvsc_drv.c index f825a629a699..8264f0ef7692 100644 --- a/drivers/net/hyperv/netvsc_drv.c +++ b/drivers/net/hyperv/netvsc_drv.c @@ -349,7 +349,7 @@ static int netvsc_set_mac_addr(struct net_device *ndev, void *p) struct net_device_context *ndevctx = netdev_priv(ndev); struct hv_device *hdev = ndevctx->device_ctx; struct sockaddr *addr = p; - char save_adr[14]; + char save_adr[ETH_ALEN]; unsigned char save_aatype; int err; diff --git a/drivers/net/loopback.c b/drivers/net/loopback.c index 81f8f9e31db5..fcbf680c3e62 100644 --- a/drivers/net/loopback.c +++ b/drivers/net/loopback.c @@ -77,6 +77,11 @@ static netdev_tx_t loopback_xmit(struct sk_buff *skb, skb_orphan(skb); + /* Before queueing this packet to netif_rx(), + * make sure dst is refcounted. + */ + skb_dst_force(skb); + skb->protocol = eth_type_trans(skb, dev); /* it's OK to use per_cpu_ptr() because BHs are off */ diff --git a/drivers/net/macvlan.c b/drivers/net/macvlan.c index 68a43fe602e7..d3fb97d97cbc 100644 --- a/drivers/net/macvlan.c +++ b/drivers/net/macvlan.c @@ -822,7 +822,10 @@ static int macvlan_changelink(struct net_device *dev, static size_t macvlan_get_size(const struct net_device *dev) { - return nla_total_size(4); + return (0 + + nla_total_size(4) /* IFLA_MACVLAN_MODE */ + + nla_total_size(2) /* IFLA_MACVLAN_FLAGS */ + ); } static int macvlan_fill_info(struct sk_buff *skb, diff --git a/drivers/net/phy/icplus.c b/drivers/net/phy/icplus.c index d5199cb4caec..b5ddd5077a80 100644 --- a/drivers/net/phy/icplus.c +++ b/drivers/net/phy/icplus.c @@ -36,8 +36,9 @@ MODULE_LICENSE("GPL"); /* IP101A/G - IP1001 */ #define IP10XX_SPEC_CTRL_STATUS 16 /* Spec. Control Register */ +#define IP1001_RXPHASE_SEL (1<<0) /* Add delay on RX_CLK */ +#define IP1001_TXPHASE_SEL (1<<1) /* Add delay on TX_CLK */ #define IP1001_SPEC_CTRL_STATUS_2 20 /* IP1001 Spec. Control Reg 2 */ -#define IP1001_PHASE_SEL_MASK 3 /* IP1001 RX/TXPHASE_SEL */ #define IP1001_APS_ON 11 /* IP1001 APS Mode bit */ #define IP101A_G_APS_ON 2 /* IP101A/G APS Mode bit */ #define IP101A_G_IRQ_CONF_STATUS 0x11 /* Conf Info IRQ & Status Reg */ @@ -138,19 +139,24 @@ static int ip1001_config_init(struct phy_device *phydev) if (c < 0) return c; - /* INTR pin used: speed/link/duplex will cause an interrupt */ - c = phy_write(phydev, IP101A_G_IRQ_CONF_STATUS, IP101A_G_IRQ_DEFAULT); - if (c < 0) - return c; + if ((phydev->interface == PHY_INTERFACE_MODE_RGMII) || + (phydev->interface == PHY_INTERFACE_MODE_RGMII_ID) || + (phydev->interface == PHY_INTERFACE_MODE_RGMII_RXID) || + (phydev->interface == PHY_INTERFACE_MODE_RGMII_TXID)) { - if (phydev->interface == PHY_INTERFACE_MODE_RGMII) { - /* Additional delay (2ns) used to adjust RX clock phase - * at RGMII interface */ c = phy_read(phydev, IP10XX_SPEC_CTRL_STATUS); if (c < 0) return c; - c |= IP1001_PHASE_SEL_MASK; + c &= ~(IP1001_RXPHASE_SEL | IP1001_TXPHASE_SEL); + + if (phydev->interface == PHY_INTERFACE_MODE_RGMII_ID) + c |= (IP1001_RXPHASE_SEL | IP1001_TXPHASE_SEL); + else if (phydev->interface == PHY_INTERFACE_MODE_RGMII_RXID) + c |= IP1001_RXPHASE_SEL; + else if (phydev->interface == PHY_INTERFACE_MODE_RGMII_TXID) + c |= IP1001_TXPHASE_SEL; + c = phy_write(phydev, IP10XX_SPEC_CTRL_STATUS, c); if (c < 0) return c; @@ -167,6 +173,11 @@ static int ip101a_g_config_init(struct phy_device *phydev) if (c < 0) return c; + /* INTR pin used: speed/link/duplex will cause an interrupt */ + c = phy_write(phydev, IP101A_G_IRQ_CONF_STATUS, IP101A_G_IRQ_DEFAULT); + if (c < 0) + return c; + /* Enable Auto Power Saving mode */ c = phy_read(phydev, IP10XX_SPEC_CTRL_STATUS); c |= IP101A_G_APS_ON; diff --git a/drivers/net/phy/marvell.c b/drivers/net/phy/marvell.c index 5d2a3f215887..22dec9c7ef05 100644 --- a/drivers/net/phy/marvell.c +++ b/drivers/net/phy/marvell.c @@ -353,15 +353,6 @@ static int m88e1111_config_init(struct phy_device *phydev) int err; int temp; - /* Enable Fiber/Copper auto selection */ - temp = phy_read(phydev, MII_M1111_PHY_EXT_SR); - temp &= ~MII_M1111_HWCFG_FIBER_COPPER_AUTO; - phy_write(phydev, MII_M1111_PHY_EXT_SR, temp); - - temp = phy_read(phydev, MII_BMCR); - temp |= BMCR_RESET; - phy_write(phydev, MII_BMCR, temp); - if ((phydev->interface == PHY_INTERFACE_MODE_RGMII) || (phydev->interface == PHY_INTERFACE_MODE_RGMII_ID) || (phydev->interface == PHY_INTERFACE_MODE_RGMII_RXID) || diff --git a/drivers/net/tun.c b/drivers/net/tun.c index fbd106edbe59..cc09b67c23bc 100644 --- a/drivers/net/tun.c +++ b/drivers/net/tun.c @@ -109,11 +109,11 @@ struct tap_filter { unsigned char addr[FLT_EXACT_COUNT][ETH_ALEN]; }; -/* 1024 is probably a high enough limit: modern hypervisors seem to support on - * the order of 100-200 CPUs so this leaves us some breathing space if we want - * to match a queue per guest CPU. - */ -#define MAX_TAP_QUEUES 1024 +/* DEFAULT_MAX_NUM_RSS_QUEUES were choosed to let the rx/tx queues allocated for + * the netdevice to be fit in one page. So we can make sure the success of + * memory allocation. TODO: increase the limit. */ +#define MAX_TAP_QUEUES DEFAULT_MAX_NUM_RSS_QUEUES +#define MAX_TAP_FLOWS 4096 #define TUN_FLOW_EXPIRE (3 * HZ) @@ -185,6 +185,8 @@ struct tun_struct { unsigned long ageing_time; unsigned int numdisabled; struct list_head disabled; + void *security; + u32 flow_count; }; static inline u32 tun_hashfn(u32 rxhash) @@ -218,6 +220,7 @@ static struct tun_flow_entry *tun_flow_create(struct tun_struct *tun, e->queue_index = queue_index; e->tun = tun; hlist_add_head_rcu(&e->hash_link, head); + ++tun->flow_count; } return e; } @@ -228,6 +231,7 @@ static void tun_flow_delete(struct tun_struct *tun, struct tun_flow_entry *e) e->rxhash, e->queue_index); hlist_del_rcu(&e->hash_link); kfree_rcu(e, rcu); + --tun->flow_count; } static void tun_flow_flush(struct tun_struct *tun) @@ -317,7 +321,8 @@ static void tun_flow_update(struct tun_struct *tun, u32 rxhash, e->updated = jiffies; } else { spin_lock_bh(&tun->lock); - if (!tun_flow_find(head, rxhash)) + if (!tun_flow_find(head, rxhash) && + tun->flow_count < MAX_TAP_FLOWS) tun_flow_create(tun, head, rxhash, queue_index); if (!timer_pending(&tun->flow_gc_timer)) @@ -404,8 +409,8 @@ static void __tun_detach(struct tun_file *tfile, bool clean) struct tun_struct *tun; struct net_device *dev; - tun = rcu_dereference_protected(tfile->tun, - lockdep_rtnl_is_held()); + tun = rtnl_dereference(tfile->tun); + if (tun) { u16 index = tfile->queue_index; BUG_ON(index >= tun->numqueues); @@ -414,8 +419,7 @@ static void __tun_detach(struct tun_file *tfile, bool clean) rcu_assign_pointer(tun->tfiles[index], tun->tfiles[tun->numqueues - 1]); rcu_assign_pointer(tfile->tun, NULL); - ntfile = rcu_dereference_protected(tun->tfiles[index], - lockdep_rtnl_is_held()); + ntfile = rtnl_dereference(tun->tfiles[index]); ntfile->queue_index = index; --tun->numqueues; @@ -429,8 +433,10 @@ static void __tun_detach(struct tun_file *tfile, bool clean) /* Drop read queue */ skb_queue_purge(&tfile->sk.sk_receive_queue); tun_set_real_num_queues(tun); - } else if (tfile->detached && clean) + } else if (tfile->detached && clean) { tun = tun_enable_queue(tfile); + sock_put(&tfile->sk); + } if (clean) { if (tun && tun->numqueues == 0 && tun->numdisabled == 0 && @@ -458,8 +464,7 @@ static void tun_detach_all(struct net_device *dev) int i, n = tun->numqueues; for (i = 0; i < n; i++) { - tfile = rcu_dereference_protected(tun->tfiles[i], - lockdep_rtnl_is_held()); + tfile = rtnl_dereference(tun->tfiles[i]); BUG_ON(!tfile); wake_up_all(&tfile->wq.wait); rcu_assign_pointer(tfile->tun, NULL); @@ -469,8 +474,7 @@ static void tun_detach_all(struct net_device *dev) synchronize_net(); for (i = 0; i < n; i++) { - tfile = rcu_dereference_protected(tun->tfiles[i], - lockdep_rtnl_is_held()); + tfile = rtnl_dereference(tun->tfiles[i]); /* Drop read queue */ skb_queue_purge(&tfile->sk.sk_receive_queue); sock_put(&tfile->sk); @@ -481,6 +485,9 @@ static void tun_detach_all(struct net_device *dev) sock_put(&tfile->sk); } BUG_ON(tun->numdisabled != 0); + + if (tun->flags & TUN_PERSIST) + module_put(THIS_MODULE); } static int tun_attach(struct tun_struct *tun, struct file *file) @@ -488,8 +495,12 @@ static int tun_attach(struct tun_struct *tun, struct file *file) struct tun_file *tfile = file->private_data; int err; + err = security_tun_dev_attach(tfile->socket.sk, tun->security); + if (err < 0) + goto out; + err = -EINVAL; - if (rcu_dereference_protected(tfile->tun, lockdep_rtnl_is_held())) + if (rtnl_dereference(tfile->tun)) goto out; err = -EBUSY; @@ -1371,6 +1382,7 @@ static void tun_free_netdev(struct net_device *dev) BUG_ON(!(list_empty(&tun->disabled))); tun_flow_uninit(tun); + security_tun_dev_free_security(tun->security); free_netdev(dev); } @@ -1544,6 +1556,9 @@ static int tun_set_iff(struct net *net, struct file *file, struct ifreq *ifr) struct net_device *dev; int err; + if (tfile->detached) + return -EINVAL; + dev = __dev_get_by_name(net, ifr->ifr_name); if (dev) { if (ifr->ifr_flags & IFF_TUN_EXCL) @@ -1557,7 +1572,7 @@ static int tun_set_iff(struct net *net, struct file *file, struct ifreq *ifr) if (tun_not_capable(tun)) return -EPERM; - err = security_tun_dev_attach(tfile->socket.sk); + err = security_tun_dev_open(tun->security); if (err < 0) return err; @@ -1572,6 +1587,8 @@ static int tun_set_iff(struct net *net, struct file *file, struct ifreq *ifr) else { char *name; unsigned long flags = 0; + int queues = ifr->ifr_flags & IFF_MULTI_QUEUE ? + MAX_TAP_QUEUES : 1; if (!ns_capable(net->user_ns, CAP_NET_ADMIN)) return -EPERM; @@ -1595,8 +1612,8 @@ static int tun_set_iff(struct net *net, struct file *file, struct ifreq *ifr) name = ifr->ifr_name; dev = alloc_netdev_mqs(sizeof(struct tun_struct), name, - tun_setup, - MAX_TAP_QUEUES, MAX_TAP_QUEUES); + tun_setup, queues, queues); + if (!dev) return -ENOMEM; @@ -1614,7 +1631,9 @@ static int tun_set_iff(struct net *net, struct file *file, struct ifreq *ifr) spin_lock_init(&tun->lock); - security_tun_dev_post_create(&tfile->sk); + err = security_tun_dev_alloc_security(&tun->security); + if (err < 0) + goto err_free_dev; tun_net_init(dev); @@ -1738,8 +1757,7 @@ static void tun_detach_filter(struct tun_struct *tun, int n) struct tun_file *tfile; for (i = 0; i < n; i++) { - tfile = rcu_dereference_protected(tun->tfiles[i], - lockdep_rtnl_is_held()); + tfile = rtnl_dereference(tun->tfiles[i]); sk_detach_filter(tfile->socket.sk); } @@ -1752,8 +1770,7 @@ static int tun_attach_filter(struct tun_struct *tun) struct tun_file *tfile; for (i = 0; i < tun->numqueues; i++) { - tfile = rcu_dereference_protected(tun->tfiles[i], - lockdep_rtnl_is_held()); + tfile = rtnl_dereference(tun->tfiles[i]); ret = sk_attach_filter(&tun->fprog, tfile->socket.sk); if (ret) { tun_detach_filter(tun, i); @@ -1771,8 +1788,7 @@ static void tun_set_sndbuf(struct tun_struct *tun) int i; for (i = 0; i < tun->numqueues; i++) { - tfile = rcu_dereference_protected(tun->tfiles[i], - lockdep_rtnl_is_held()); + tfile = rtnl_dereference(tun->tfiles[i]); tfile->socket.sk->sk_sndbuf = tun->sndbuf; } } @@ -1787,15 +1803,16 @@ static int tun_set_queue(struct file *file, struct ifreq *ifr) if (ifr->ifr_flags & IFF_ATTACH_QUEUE) { tun = tfile->detached; - if (!tun) + if (!tun) { ret = -EINVAL; - else if (tun_not_capable(tun)) - ret = -EPERM; - else - ret = tun_attach(tun, file); + goto unlock; + } + ret = security_tun_dev_attach_queue(tun->security); + if (ret < 0) + goto unlock; + ret = tun_attach(tun, file); } else if (ifr->ifr_flags & IFF_DETACH_QUEUE) { - tun = rcu_dereference_protected(tfile->tun, - lockdep_rtnl_is_held()); + tun = rtnl_dereference(tfile->tun); if (!tun || !(tun->flags & TUN_TAP_MQ)) ret = -EINVAL; else @@ -1803,6 +1820,7 @@ static int tun_set_queue(struct file *file, struct ifreq *ifr) } else ret = -EINVAL; +unlock: rtnl_unlock(); return ret; } @@ -1880,10 +1898,11 @@ static long __tun_chr_ioctl(struct file *file, unsigned int cmd, /* Disable/Enable persist mode. Keep an extra reference to the * module to prevent the module being unprobed. */ - if (arg) { + if (arg && !(tun->flags & TUN_PERSIST)) { tun->flags |= TUN_PERSIST; __module_get(THIS_MODULE); - } else { + } + if (!arg && (tun->flags & TUN_PERSIST)) { tun->flags &= ~TUN_PERSIST; module_put(THIS_MODULE); } diff --git a/drivers/net/usb/cdc_mbim.c b/drivers/net/usb/cdc_mbim.c index 42f51c71ec1f..248d2dc765a5 100644 --- a/drivers/net/usb/cdc_mbim.c +++ b/drivers/net/usb/cdc_mbim.c @@ -374,6 +374,21 @@ static const struct driver_info cdc_mbim_info = { .tx_fixup = cdc_mbim_tx_fixup, }; +/* MBIM and NCM devices should not need a ZLP after NTBs with + * dwNtbOutMaxSize length. This driver_info is for the exceptional + * devices requiring it anyway, allowing them to be supported without + * forcing the performance penalty on all the sane devices. + */ +static const struct driver_info cdc_mbim_info_zlp = { + .description = "CDC MBIM", + .flags = FLAG_NO_SETINT | FLAG_MULTI_PACKET | FLAG_WWAN | FLAG_SEND_ZLP, + .bind = cdc_mbim_bind, + .unbind = cdc_mbim_unbind, + .manage_power = cdc_mbim_manage_power, + .rx_fixup = cdc_mbim_rx_fixup, + .tx_fixup = cdc_mbim_tx_fixup, +}; + static const struct usb_device_id mbim_devs[] = { /* This duplicate NCM entry is intentional. MBIM devices can * be disguised as NCM by default, and this is necessary to @@ -385,6 +400,10 @@ static const struct usb_device_id mbim_devs[] = { { USB_INTERFACE_INFO(USB_CLASS_COMM, USB_CDC_SUBCLASS_NCM, USB_CDC_PROTO_NONE), .driver_info = (unsigned long)&cdc_mbim_info, }, + /* Sierra Wireless MC7710 need ZLPs */ + { USB_DEVICE_AND_INTERFACE_INFO(0x1199, 0x68a2, USB_CLASS_COMM, USB_CDC_SUBCLASS_MBIM, USB_CDC_PROTO_NONE), + .driver_info = (unsigned long)&cdc_mbim_info_zlp, + }, { USB_INTERFACE_INFO(USB_CLASS_COMM, USB_CDC_SUBCLASS_MBIM, USB_CDC_PROTO_NONE), .driver_info = (unsigned long)&cdc_mbim_info, }, diff --git a/drivers/net/usb/cdc_ncm.c b/drivers/net/usb/cdc_ncm.c index 71b6e92b8e9b..9197b2c72ca3 100644 --- a/drivers/net/usb/cdc_ncm.c +++ b/drivers/net/usb/cdc_ncm.c @@ -435,6 +435,13 @@ advance: len -= temp; } + /* some buggy devices have an IAD but no CDC Union */ + if (!ctx->union_desc && intf->intf_assoc && intf->intf_assoc->bInterfaceCount == 2) { + ctx->control = intf; + ctx->data = usb_ifnum_to_if(dev->udev, intf->cur_altsetting->desc.bInterfaceNumber + 1); + dev_dbg(&intf->dev, "CDC Union missing - got slave from IAD\n"); + } + /* check if we got everything */ if ((ctx->control == NULL) || (ctx->data == NULL) || ((!ctx->mbim_desc) && ((ctx->ether_desc == NULL) || (ctx->control != intf)))) @@ -497,7 +504,8 @@ advance: error2: usb_set_intfdata(ctx->control, NULL); usb_set_intfdata(ctx->data, NULL); - usb_driver_release_interface(driver, ctx->data); + if (ctx->data != ctx->control) + usb_driver_release_interface(driver, ctx->data); error: cdc_ncm_free((struct cdc_ncm_ctx *)dev->data[0]); dev->data[0] = 0; @@ -1155,6 +1163,20 @@ static const struct driver_info wwan_info = { .tx_fixup = cdc_ncm_tx_fixup, }; +/* Same as wwan_info, but with FLAG_NOARP */ +static const struct driver_info wwan_noarp_info = { + .description = "Mobile Broadband Network Device (NO ARP)", + .flags = FLAG_POINTTOPOINT | FLAG_NO_SETINT | FLAG_MULTI_PACKET + | FLAG_WWAN | FLAG_NOARP, + .bind = cdc_ncm_bind, + .unbind = cdc_ncm_unbind, + .check_connect = cdc_ncm_check_connect, + .manage_power = usbnet_manage_power, + .status = cdc_ncm_status, + .rx_fixup = cdc_ncm_rx_fixup, + .tx_fixup = cdc_ncm_tx_fixup, +}; + static const struct usb_device_id cdc_devs[] = { /* Ericsson MBM devices like F5521gw */ { .match_flags = USB_DEVICE_ID_MATCH_INT_INFO @@ -1194,6 +1216,13 @@ static const struct usb_device_id cdc_devs[] = { .driver_info = (unsigned long)&wwan_info, }, + /* Infineon(now Intel) HSPA Modem platform */ + { USB_DEVICE_AND_INTERFACE_INFO(0x1519, 0x0443, + USB_CLASS_COMM, + USB_CDC_SUBCLASS_NCM, USB_CDC_PROTO_NONE), + .driver_info = (unsigned long)&wwan_noarp_info, + }, + /* Generic CDC-NCM devices */ { USB_INTERFACE_INFO(USB_CLASS_COMM, USB_CDC_SUBCLASS_NCM, USB_CDC_PROTO_NONE), diff --git a/drivers/net/usb/dm9601.c b/drivers/net/usb/dm9601.c index 3f554c1149f3..d7e99445518e 100644 --- a/drivers/net/usb/dm9601.c +++ b/drivers/net/usb/dm9601.c @@ -45,6 +45,12 @@ #define DM_MCAST_ADDR 0x16 /* 8 bytes */ #define DM_GPR_CTRL 0x1e #define DM_GPR_DATA 0x1f +#define DM_CHIP_ID 0x2c +#define DM_MODE_CTRL 0x91 /* only on dm9620 */ + +/* chip id values */ +#define ID_DM9601 0 +#define ID_DM9620 1 #define DM_MAX_MCAST 64 #define DM_MCAST_SIZE 8 @@ -53,7 +59,6 @@ #define DM_RX_OVERHEAD 7 /* 3 byte header + 4 byte crc tail */ #define DM_TIMEOUT 1000 - static int dm_read(struct usbnet *dev, u8 reg, u16 length, void *data) { int err; @@ -84,32 +89,23 @@ static int dm_write(struct usbnet *dev, u8 reg, u16 length, void *data) static int dm_write_reg(struct usbnet *dev, u8 reg, u8 value) { - return usbnet_write_cmd(dev, DM_WRITE_REGS, + return usbnet_write_cmd(dev, DM_WRITE_REG, USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, value, reg, NULL, 0); } -static void dm_write_async_helper(struct usbnet *dev, u8 reg, u8 value, - u16 length, void *data) +static void dm_write_async(struct usbnet *dev, u8 reg, u16 length, void *data) { usbnet_write_cmd_async(dev, DM_WRITE_REGS, USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, - value, reg, data, length); -} - -static void dm_write_async(struct usbnet *dev, u8 reg, u16 length, void *data) -{ - netdev_dbg(dev->net, "dm_write_async() reg=0x%02x length=%d\n", reg, length); - - dm_write_async_helper(dev, reg, 0, length, data); + 0, reg, data, length); } static void dm_write_reg_async(struct usbnet *dev, u8 reg, u8 value) { - netdev_dbg(dev->net, "dm_write_reg_async() reg=0x%02x value=0x%02x\n", - reg, value); - - dm_write_async_helper(dev, reg, value, 0, NULL); + usbnet_write_cmd_async(dev, DM_WRITE_REG, + USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, + value, reg, NULL, 0); } static int dm_read_shared_word(struct usbnet *dev, int phy, u8 reg, __le16 *value) @@ -358,7 +354,7 @@ static const struct net_device_ops dm9601_netdev_ops = { static int dm9601_bind(struct usbnet *dev, struct usb_interface *intf) { int ret; - u8 mac[ETH_ALEN]; + u8 mac[ETH_ALEN], id; ret = usbnet_get_endpoints(dev, intf); if (ret) @@ -399,6 +395,24 @@ static int dm9601_bind(struct usbnet *dev, struct usb_interface *intf) __dm9601_set_mac_address(dev); } + if (dm_read_reg(dev, DM_CHIP_ID, &id) < 0) { + netdev_err(dev->net, "Error reading chip ID\n"); + ret = -ENODEV; + goto out; + } + + /* put dm9620 devices in dm9601 mode */ + if (id == ID_DM9620) { + u8 mode; + + if (dm_read_reg(dev, DM_MODE_CTRL, &mode) < 0) { + netdev_err(dev->net, "Error reading MODE_CTRL\n"); + ret = -ENODEV; + goto out; + } + dm_write_reg(dev, DM_MODE_CTRL, mode & 0x7f); + } + /* power up phy */ dm_write_reg(dev, DM_GPR_CTRL, 1); dm_write_reg(dev, DM_GPR_DATA, 0); @@ -581,6 +595,10 @@ static const struct usb_device_id products[] = { USB_DEVICE(0x0a46, 0x9000), /* DM9000E */ .driver_info = (unsigned long)&dm9601_info, }, + { + USB_DEVICE(0x0a46, 0x9620), /* DM9620 USB to Fast Ethernet Adapter */ + .driver_info = (unsigned long)&dm9601_info, + }, {}, // END }; diff --git a/drivers/net/usb/qmi_wwan.c b/drivers/net/usb/qmi_wwan.c index 6a1ca500e612..575a5839ee34 100644 --- a/drivers/net/usb/qmi_wwan.c +++ b/drivers/net/usb/qmi_wwan.c @@ -433,6 +433,7 @@ static const struct usb_device_id products[] = { {QMI_FIXED_INTF(0x19d2, 0x0199, 1)}, /* ZTE MF820S */ {QMI_FIXED_INTF(0x19d2, 0x0200, 1)}, {QMI_FIXED_INTF(0x19d2, 0x0257, 3)}, /* ZTE MF821 */ + {QMI_FIXED_INTF(0x19d2, 0x0265, 4)}, /* ONDA MT8205 4G LTE */ {QMI_FIXED_INTF(0x19d2, 0x0284, 4)}, /* ZTE MF880 */ {QMI_FIXED_INTF(0x19d2, 0x0326, 4)}, /* ZTE MF821D */ {QMI_FIXED_INTF(0x19d2, 0x1008, 4)}, /* ZTE (Vodafone) K3570-Z */ @@ -459,6 +460,7 @@ static const struct usb_device_id products[] = { {QMI_FIXED_INTF(0x1199, 0x68a2, 19)}, /* Sierra Wireless MC7710 in QMI mode */ {QMI_FIXED_INTF(0x1199, 0x901c, 8)}, /* Sierra Wireless EM7700 */ {QMI_FIXED_INTF(0x1bbb, 0x011e, 4)}, /* Telekom Speedstick LTE II (Alcatel One Touch L100V LTE) */ + {QMI_FIXED_INTF(0x2357, 0x0201, 4)}, /* TP-LINK HSUPA Modem MA180 */ /* 4. Gobi 1000 devices */ {QMI_GOBI1K_DEVICE(0x05c6, 0x9212)}, /* Acer Gobi Modem Device */ diff --git a/drivers/net/usb/usbnet.c b/drivers/net/usb/usbnet.c index 3d4bf01641b4..f34b2ebee815 100644 --- a/drivers/net/usb/usbnet.c +++ b/drivers/net/usb/usbnet.c @@ -1448,6 +1448,10 @@ usbnet_probe (struct usb_interface *udev, const struct usb_device_id *prod) if ((dev->driver_info->flags & FLAG_WWAN) != 0) strcpy(net->name, "wwan%d"); + /* devices that cannot do ARP */ + if ((dev->driver_info->flags & FLAG_NOARP) != 0) + net->flags |= IFF_NOARP; + /* maybe the remote can't receive an Ethernet MTU */ if (net->mtu > (dev->hard_mtu - net->hard_header_len)) net->mtu = dev->hard_mtu - net->hard_header_len; diff --git a/drivers/net/virtio_net.c b/drivers/net/virtio_net.c index a6fcf15adc4f..35c00c5ea02a 100644 --- a/drivers/net/virtio_net.c +++ b/drivers/net/virtio_net.c @@ -26,6 +26,7 @@ #include <linux/scatterlist.h> #include <linux/if_vlan.h> #include <linux/slab.h> +#include <linux/cpu.h> static int napi_weight = 128; module_param(napi_weight, int, 0444); @@ -123,6 +124,12 @@ struct virtnet_info { /* Does the affinity hint is set for virtqueues? */ bool affinity_hint_set; + + /* Per-cpu variable to show the mapping from CPU to virtqueue */ + int __percpu *vq_index; + + /* CPU hot plug notifier */ + struct notifier_block nb; }; struct skb_vnet_hdr { @@ -1013,32 +1020,75 @@ static int virtnet_vlan_rx_kill_vid(struct net_device *dev, u16 vid) return 0; } -static void virtnet_set_affinity(struct virtnet_info *vi, bool set) +static void virtnet_clean_affinity(struct virtnet_info *vi, long hcpu) { int i; + int cpu; + + if (vi->affinity_hint_set) { + for (i = 0; i < vi->max_queue_pairs; i++) { + virtqueue_set_affinity(vi->rq[i].vq, -1); + virtqueue_set_affinity(vi->sq[i].vq, -1); + } + + vi->affinity_hint_set = false; + } + + i = 0; + for_each_online_cpu(cpu) { + if (cpu == hcpu) { + *per_cpu_ptr(vi->vq_index, cpu) = -1; + } else { + *per_cpu_ptr(vi->vq_index, cpu) = + ++i % vi->curr_queue_pairs; + } + } +} + +static void virtnet_set_affinity(struct virtnet_info *vi) +{ + int i; + int cpu; /* In multiqueue mode, when the number of cpu is equal to the number of * queue pairs, we let the queue pairs to be private to one cpu by * setting the affinity hint to eliminate the contention. */ - if ((vi->curr_queue_pairs == 1 || - vi->max_queue_pairs != num_online_cpus()) && set) { - if (vi->affinity_hint_set) - set = false; - else - return; + if (vi->curr_queue_pairs == 1 || + vi->max_queue_pairs != num_online_cpus()) { + virtnet_clean_affinity(vi, -1); + return; } - for (i = 0; i < vi->max_queue_pairs; i++) { - int cpu = set ? i : -1; + i = 0; + for_each_online_cpu(cpu) { virtqueue_set_affinity(vi->rq[i].vq, cpu); virtqueue_set_affinity(vi->sq[i].vq, cpu); + *per_cpu_ptr(vi->vq_index, cpu) = i; + i++; } - if (set) - vi->affinity_hint_set = true; - else - vi->affinity_hint_set = false; + vi->affinity_hint_set = true; +} + +static int virtnet_cpu_callback(struct notifier_block *nfb, + unsigned long action, void *hcpu) +{ + struct virtnet_info *vi = container_of(nfb, struct virtnet_info, nb); + + switch(action & ~CPU_TASKS_FROZEN) { + case CPU_ONLINE: + case CPU_DOWN_FAILED: + case CPU_DEAD: + virtnet_set_affinity(vi); + break; + case CPU_DOWN_PREPARE: + virtnet_clean_affinity(vi, (long)hcpu); + break; + default: + break; + } + return NOTIFY_OK; } static void virtnet_get_ringparam(struct net_device *dev, @@ -1082,13 +1132,15 @@ static int virtnet_set_channels(struct net_device *dev, if (queue_pairs > vi->max_queue_pairs) return -EINVAL; + get_online_cpus(); err = virtnet_set_queues(vi, queue_pairs); if (!err) { netif_set_real_num_tx_queues(dev, queue_pairs); netif_set_real_num_rx_queues(dev, queue_pairs); - virtnet_set_affinity(vi, true); + virtnet_set_affinity(vi); } + put_online_cpus(); return err; } @@ -1127,12 +1179,19 @@ static int virtnet_change_mtu(struct net_device *dev, int new_mtu) /* To avoid contending a lock hold by a vcpu who would exit to host, select the * txq based on the processor id. - * TODO: handle cpu hotplug. */ static u16 virtnet_select_queue(struct net_device *dev, struct sk_buff *skb) { - int txq = skb_rx_queue_recorded(skb) ? skb_get_rx_queue(skb) : - smp_processor_id(); + int txq; + struct virtnet_info *vi = netdev_priv(dev); + + if (skb_rx_queue_recorded(skb)) { + txq = skb_get_rx_queue(skb); + } else { + txq = *__this_cpu_ptr(vi->vq_index); + if (txq == -1) + txq = 0; + } while (unlikely(txq >= dev->real_num_tx_queues)) txq -= dev->real_num_tx_queues; @@ -1248,7 +1307,7 @@ static void virtnet_del_vqs(struct virtnet_info *vi) { struct virtio_device *vdev = vi->vdev; - virtnet_set_affinity(vi, false); + virtnet_clean_affinity(vi, -1); vdev->config->del_vqs(vdev); @@ -1371,7 +1430,10 @@ static int init_vqs(struct virtnet_info *vi) if (ret) goto err_free; - virtnet_set_affinity(vi, true); + get_online_cpus(); + virtnet_set_affinity(vi); + put_online_cpus(); + return 0; err_free: @@ -1453,6 +1515,10 @@ static int virtnet_probe(struct virtio_device *vdev) if (vi->stats == NULL) goto free; + vi->vq_index = alloc_percpu(int); + if (vi->vq_index == NULL) + goto free_stats; + mutex_init(&vi->config_lock); vi->config_enable = true; INIT_WORK(&vi->config_work, virtnet_config_changed_work); @@ -1476,7 +1542,7 @@ static int virtnet_probe(struct virtio_device *vdev) /* Allocate/initialize the rx/tx queues, and invoke find_vqs */ err = init_vqs(vi); if (err) - goto free_stats; + goto free_index; netif_set_real_num_tx_queues(dev, 1); netif_set_real_num_rx_queues(dev, 1); @@ -1499,6 +1565,13 @@ static int virtnet_probe(struct virtio_device *vdev) } } + vi->nb.notifier_call = &virtnet_cpu_callback; + err = register_hotcpu_notifier(&vi->nb); + if (err) { + pr_debug("virtio_net: registering cpu notifier failed\n"); + goto free_recv_bufs; + } + /* Assume link up if device can't report link status, otherwise get link status from config. */ if (virtio_has_feature(vi->vdev, VIRTIO_NET_F_STATUS)) { @@ -1520,6 +1593,8 @@ free_recv_bufs: free_vqs: cancel_delayed_work_sync(&vi->refill); virtnet_del_vqs(vi); +free_index: + free_percpu(vi->vq_index); free_stats: free_percpu(vi->stats); free: @@ -1543,6 +1618,8 @@ static void virtnet_remove(struct virtio_device *vdev) { struct virtnet_info *vi = vdev->priv; + unregister_hotcpu_notifier(&vi->nb); + /* Prevent config work handler from accessing the device. */ mutex_lock(&vi->config_lock); vi->config_enable = false; @@ -1554,6 +1631,7 @@ static void virtnet_remove(struct virtio_device *vdev) flush_work(&vi->config_work); + free_percpu(vi->vq_index); free_percpu(vi->stats); free_netdev(vi->dev); } diff --git a/drivers/net/wireless/ath/Kconfig b/drivers/net/wireless/ath/Kconfig index 1a67a4f829fe..2c02b4e84094 100644 --- a/drivers/net/wireless/ath/Kconfig +++ b/drivers/net/wireless/ath/Kconfig @@ -30,5 +30,6 @@ source "drivers/net/wireless/ath/ath9k/Kconfig" source "drivers/net/wireless/ath/carl9170/Kconfig" source "drivers/net/wireless/ath/ath6kl/Kconfig" source "drivers/net/wireless/ath/ar5523/Kconfig" +source "drivers/net/wireless/ath/wil6210/Kconfig" endif diff --git a/drivers/net/wireless/ath/Makefile b/drivers/net/wireless/ath/Makefile index 1e18621326dc..97b964ded2be 100644 --- a/drivers/net/wireless/ath/Makefile +++ b/drivers/net/wireless/ath/Makefile @@ -3,6 +3,7 @@ obj-$(CONFIG_ATH9K_HW) += ath9k/ obj-$(CONFIG_CARL9170) += carl9170/ obj-$(CONFIG_ATH6KL) += ath6kl/ obj-$(CONFIG_AR5523) += ar5523/ +obj-$(CONFIG_WIL6210) += wil6210/ obj-$(CONFIG_ATH_COMMON) += ath.o diff --git a/drivers/net/wireless/ath/ath9k/ar9003_calib.c b/drivers/net/wireless/ath/ath9k/ar9003_calib.c index 8b0d8dcd7625..56317b0fb6b6 100644 --- a/drivers/net/wireless/ath/ath9k/ar9003_calib.c +++ b/drivers/net/wireless/ath/ath9k/ar9003_calib.c @@ -976,6 +976,8 @@ static bool ar9003_hw_init_cal(struct ath_hw *ah, AR_PHY_CL_TAB_1, AR_PHY_CL_TAB_2 }; + ar9003_hw_set_chain_masks(ah, ah->caps.rx_chainmask, ah->caps.tx_chainmask); + if (rtt) { if (!ar9003_hw_rtt_restore(ah, chan)) run_rtt_cal = true; diff --git a/drivers/net/wireless/ath/ath9k/ar9003_phy.c b/drivers/net/wireless/ath/ath9k/ar9003_phy.c index ce19c09fa8e8..3afc24bde6d6 100644 --- a/drivers/net/wireless/ath/ath9k/ar9003_phy.c +++ b/drivers/net/wireless/ath/ath9k/ar9003_phy.c @@ -586,32 +586,19 @@ static void ar9003_hw_init_bb(struct ath_hw *ah, ath9k_hw_synth_delay(ah, chan, synthDelay); } -static void ar9003_hw_set_chain_masks(struct ath_hw *ah, u8 rx, u8 tx) +void ar9003_hw_set_chain_masks(struct ath_hw *ah, u8 rx, u8 tx) { - switch (rx) { - case 0x5: + if (ah->caps.tx_chainmask == 5 || ah->caps.rx_chainmask == 5) REG_SET_BIT(ah, AR_PHY_ANALOG_SWAP, AR_PHY_SWAP_ALT_CHAIN); - case 0x3: - case 0x1: - case 0x2: - case 0x7: - REG_WRITE(ah, AR_PHY_RX_CHAINMASK, rx); - REG_WRITE(ah, AR_PHY_CAL_CHAINMASK, rx); - break; - default: - break; - } + + REG_WRITE(ah, AR_PHY_RX_CHAINMASK, rx); + REG_WRITE(ah, AR_PHY_CAL_CHAINMASK, rx); if ((ah->caps.hw_caps & ATH9K_HW_CAP_APM) && (tx == 0x7)) - REG_WRITE(ah, AR_SELFGEN_MASK, 0x3); - else - REG_WRITE(ah, AR_SELFGEN_MASK, tx); + tx = 3; - if (tx == 0x5) { - REG_SET_BIT(ah, AR_PHY_ANALOG_SWAP, - AR_PHY_SWAP_ALT_CHAIN); - } + REG_WRITE(ah, AR_SELFGEN_MASK, tx); } /* diff --git a/drivers/net/wireless/ath/ath9k/ath9k.h b/drivers/net/wireless/ath/ath9k/ath9k.h index 86e26a19efda..42794c546a40 100644 --- a/drivers/net/wireless/ath/ath9k/ath9k.h +++ b/drivers/net/wireless/ath/ath9k/ath9k.h @@ -317,7 +317,6 @@ struct ath_rx { u32 *rxlink; u32 num_pkts; unsigned int rxfilter; - spinlock_t rxbuflock; struct list_head rxbuf; struct ath_descdma rxdma; struct ath_buf *rx_bufptr; @@ -328,7 +327,6 @@ struct ath_rx { int ath_startrecv(struct ath_softc *sc); bool ath_stoprecv(struct ath_softc *sc); -void ath_flushrecv(struct ath_softc *sc); u32 ath_calcrxfilter(struct ath_softc *sc); int ath_rx_init(struct ath_softc *sc, int nbufs); void ath_rx_cleanup(struct ath_softc *sc); @@ -646,7 +644,6 @@ void ath_ant_comb_update(struct ath_softc *sc); enum sc_op_flags { SC_OP_INVALID, SC_OP_BEACONS, - SC_OP_RXFLUSH, SC_OP_ANI_RUN, SC_OP_PRIM_STA_VIF, SC_OP_HW_RESET, diff --git a/drivers/net/wireless/ath/ath9k/beacon.c b/drivers/net/wireless/ath/ath9k/beacon.c index 531fffd801a3..2ca355e94da6 100644 --- a/drivers/net/wireless/ath/ath9k/beacon.c +++ b/drivers/net/wireless/ath/ath9k/beacon.c @@ -147,6 +147,7 @@ static struct ath_buf *ath9k_beacon_generate(struct ieee80211_hw *hw, skb->len, DMA_TO_DEVICE); dev_kfree_skb_any(skb); bf->bf_buf_addr = 0; + bf->bf_mpdu = NULL; } skb = ieee80211_beacon_get(hw, vif); @@ -359,7 +360,6 @@ void ath9k_beacon_tasklet(unsigned long data) return; bf = ath9k_beacon_generate(sc->hw, vif); - WARN_ON(!bf); if (sc->beacon.bmisscnt != 0) { ath_dbg(common, BSTUCK, "resume beacon xmit after %u misses\n", diff --git a/drivers/net/wireless/ath/ath9k/debug.c b/drivers/net/wireless/ath/ath9k/debug.c index 13ff9edc2401..e585fc827c50 100644 --- a/drivers/net/wireless/ath/ath9k/debug.c +++ b/drivers/net/wireless/ath/ath9k/debug.c @@ -861,7 +861,6 @@ static ssize_t read_file_recv(struct file *file, char __user *user_buf, RXS_ERR("RX-LENGTH-ERR", rx_len_err); RXS_ERR("RX-OOM-ERR", rx_oom_err); RXS_ERR("RX-RATE-ERR", rx_rate_err); - RXS_ERR("RX-DROP-RXFLUSH", rx_drop_rxflush); RXS_ERR("RX-TOO-MANY-FRAGS", rx_too_many_frags_err); PHY_ERR("UNDERRUN ERR", ATH9K_PHYERR_UNDERRUN); diff --git a/drivers/net/wireless/ath/ath9k/debug.h b/drivers/net/wireless/ath/ath9k/debug.h index 375c3b46411e..6df2ab62dcb7 100644 --- a/drivers/net/wireless/ath/ath9k/debug.h +++ b/drivers/net/wireless/ath/ath9k/debug.h @@ -216,7 +216,6 @@ struct ath_tx_stats { * @rx_oom_err: No. of frames dropped due to OOM issues. * @rx_rate_err: No. of frames dropped due to rate errors. * @rx_too_many_frags_err: Frames dropped due to too-many-frags received. - * @rx_drop_rxflush: No. of frames dropped due to RX-FLUSH. * @rx_beacons: No. of beacons received. * @rx_frags: No. of rx-fragements received. */ @@ -235,7 +234,6 @@ struct ath_rx_stats { u32 rx_oom_err; u32 rx_rate_err; u32 rx_too_many_frags_err; - u32 rx_drop_rxflush; u32 rx_beacons; u32 rx_frags; }; diff --git a/drivers/net/wireless/ath/ath9k/htc_hst.c b/drivers/net/wireless/ath/ath9k/htc_hst.c index 4a9570dfba72..aac4a406a513 100644 --- a/drivers/net/wireless/ath/ath9k/htc_hst.c +++ b/drivers/net/wireless/ath/ath9k/htc_hst.c @@ -344,6 +344,8 @@ void ath9k_htc_txcompletion_cb(struct htc_target *htc_handle, endpoint->ep_callbacks.tx(endpoint->ep_callbacks.priv, skb, htc_hdr->endpoint_id, txok); + } else { + kfree_skb(skb); } } diff --git a/drivers/net/wireless/ath/ath9k/hw.h b/drivers/net/wireless/ath/ath9k/hw.h index 7f1a8e91c908..9d26fc56ca56 100644 --- a/drivers/net/wireless/ath/ath9k/hw.h +++ b/drivers/net/wireless/ath/ath9k/hw.h @@ -1066,6 +1066,7 @@ void ar9003_paprd_setup_gain_table(struct ath_hw *ah, int chain); int ar9003_paprd_init_table(struct ath_hw *ah); bool ar9003_paprd_is_done(struct ath_hw *ah); bool ar9003_is_paprd_enabled(struct ath_hw *ah); +void ar9003_hw_set_chain_masks(struct ath_hw *ah, u8 rx, u8 tx); /* Hardware family op attach helpers */ void ar5008_hw_attach_phy_ops(struct ath_hw *ah); diff --git a/drivers/net/wireless/ath/ath9k/main.c b/drivers/net/wireless/ath/ath9k/main.c index be30a9af1528..dd91f8fdc01c 100644 --- a/drivers/net/wireless/ath/ath9k/main.c +++ b/drivers/net/wireless/ath/ath9k/main.c @@ -182,7 +182,7 @@ static void ath_restart_work(struct ath_softc *sc) ath_start_ani(sc); } -static bool ath_prepare_reset(struct ath_softc *sc, bool retry_tx, bool flush) +static bool ath_prepare_reset(struct ath_softc *sc, bool retry_tx) { struct ath_hw *ah = sc->sc_ah; bool ret = true; @@ -202,14 +202,6 @@ static bool ath_prepare_reset(struct ath_softc *sc, bool retry_tx, bool flush) if (!ath_drain_all_txq(sc, retry_tx)) ret = false; - if (!flush) { - if (ah->caps.hw_caps & ATH9K_HW_CAP_EDMA) - ath_rx_tasklet(sc, 1, true); - ath_rx_tasklet(sc, 1, false); - } else { - ath_flushrecv(sc); - } - return ret; } @@ -262,11 +254,11 @@ static int ath_reset_internal(struct ath_softc *sc, struct ath9k_channel *hchan, struct ath_common *common = ath9k_hw_common(ah); struct ath9k_hw_cal_data *caldata = NULL; bool fastcc = true; - bool flush = false; int r; __ath_cancel_work(sc); + tasklet_disable(&sc->intr_tq); spin_lock_bh(&sc->sc_pcu_lock); if (!(sc->hw->conf.flags & IEEE80211_CONF_OFFCHANNEL)) { @@ -276,11 +268,10 @@ static int ath_reset_internal(struct ath_softc *sc, struct ath9k_channel *hchan, if (!hchan) { fastcc = false; - flush = true; hchan = ah->curchan; } - if (!ath_prepare_reset(sc, retry_tx, flush)) + if (!ath_prepare_reset(sc, retry_tx)) fastcc = false; ath_dbg(common, CONFIG, "Reset to %u MHz, HT40: %d fastcc: %d\n", @@ -302,6 +293,8 @@ static int ath_reset_internal(struct ath_softc *sc, struct ath9k_channel *hchan, out: spin_unlock_bh(&sc->sc_pcu_lock); + tasklet_enable(&sc->intr_tq); + return r; } @@ -804,7 +797,7 @@ static void ath9k_stop(struct ieee80211_hw *hw) ath9k_hw_cfg_gpio_input(ah, ah->led_pin); } - ath_prepare_reset(sc, false, true); + ath_prepare_reset(sc, false); if (sc->rx.frag) { dev_kfree_skb_any(sc->rx.frag); @@ -1833,6 +1826,9 @@ static u32 fill_chainmask(u32 cap, u32 new) static bool validate_antenna_mask(struct ath_hw *ah, u32 val) { + if (AR_SREV_9300_20_OR_LATER(ah)) + return true; + switch (val & 0x7) { case 0x1: case 0x3: diff --git a/drivers/net/wireless/ath/ath9k/recv.c b/drivers/net/wireless/ath/ath9k/recv.c index d4df98a938bf..90752f246970 100644 --- a/drivers/net/wireless/ath/ath9k/recv.c +++ b/drivers/net/wireless/ath/ath9k/recv.c @@ -254,8 +254,6 @@ rx_init_fail: static void ath_edma_start_recv(struct ath_softc *sc) { - spin_lock_bh(&sc->rx.rxbuflock); - ath9k_hw_rxena(sc->sc_ah); ath_rx_addbuffer_edma(sc, ATH9K_RX_QUEUE_HP, @@ -267,8 +265,6 @@ static void ath_edma_start_recv(struct ath_softc *sc) ath_opmode_init(sc); ath9k_hw_startpcureceive(sc->sc_ah, !!(sc->hw->conf.flags & IEEE80211_CONF_OFFCHANNEL)); - - spin_unlock_bh(&sc->rx.rxbuflock); } static void ath_edma_stop_recv(struct ath_softc *sc) @@ -285,8 +281,6 @@ int ath_rx_init(struct ath_softc *sc, int nbufs) int error = 0; spin_lock_init(&sc->sc_pcu_lock); - spin_lock_init(&sc->rx.rxbuflock); - clear_bit(SC_OP_RXFLUSH, &sc->sc_flags); common->rx_bufsize = IEEE80211_MAX_MPDU_LEN / 2 + sc->sc_ah->caps.rx_status_len; @@ -447,7 +441,6 @@ int ath_startrecv(struct ath_softc *sc) return 0; } - spin_lock_bh(&sc->rx.rxbuflock); if (list_empty(&sc->rx.rxbuf)) goto start_recv; @@ -468,26 +461,31 @@ start_recv: ath_opmode_init(sc); ath9k_hw_startpcureceive(ah, !!(sc->hw->conf.flags & IEEE80211_CONF_OFFCHANNEL)); - spin_unlock_bh(&sc->rx.rxbuflock); - return 0; } +static void ath_flushrecv(struct ath_softc *sc) +{ + if (sc->sc_ah->caps.hw_caps & ATH9K_HW_CAP_EDMA) + ath_rx_tasklet(sc, 1, true); + ath_rx_tasklet(sc, 1, false); +} + bool ath_stoprecv(struct ath_softc *sc) { struct ath_hw *ah = sc->sc_ah; bool stopped, reset = false; - spin_lock_bh(&sc->rx.rxbuflock); ath9k_hw_abortpcurecv(ah); ath9k_hw_setrxfilter(ah, 0); stopped = ath9k_hw_stopdmarecv(ah, &reset); + ath_flushrecv(sc); + if (sc->sc_ah->caps.hw_caps & ATH9K_HW_CAP_EDMA) ath_edma_stop_recv(sc); else sc->rx.rxlink = NULL; - spin_unlock_bh(&sc->rx.rxbuflock); if (!(ah->ah_flags & AH_UNPLUGGED) && unlikely(!stopped)) { @@ -499,15 +497,6 @@ bool ath_stoprecv(struct ath_softc *sc) return stopped && !reset; } -void ath_flushrecv(struct ath_softc *sc) -{ - set_bit(SC_OP_RXFLUSH, &sc->sc_flags); - if (sc->sc_ah->caps.hw_caps & ATH9K_HW_CAP_EDMA) - ath_rx_tasklet(sc, 1, true); - ath_rx_tasklet(sc, 1, false); - clear_bit(SC_OP_RXFLUSH, &sc->sc_flags); -} - static bool ath_beacon_dtim_pending_cab(struct sk_buff *skb) { /* Check whether the Beacon frame has DTIM indicating buffered bc/mc */ @@ -744,6 +733,7 @@ static struct ath_buf *ath_get_next_rx_buf(struct ath_softc *sc, return NULL; } + list_del(&bf->list); if (!bf->bf_mpdu) return bf; @@ -1059,16 +1049,12 @@ int ath_rx_tasklet(struct ath_softc *sc, int flush, bool hp) dma_type = DMA_FROM_DEVICE; qtype = hp ? ATH9K_RX_QUEUE_HP : ATH9K_RX_QUEUE_LP; - spin_lock_bh(&sc->rx.rxbuflock); tsf = ath9k_hw_gettsf64(ah); tsf_lower = tsf & 0xffffffff; do { bool decrypt_error = false; - /* If handling rx interrupt and flush is in progress => exit */ - if (test_bit(SC_OP_RXFLUSH, &sc->sc_flags) && (flush == 0)) - break; memset(&rs, 0, sizeof(rs)); if (edma) @@ -1111,15 +1097,6 @@ int ath_rx_tasklet(struct ath_softc *sc, int flush, bool hp) ath_debug_stat_rx(sc, &rs); - /* - * If we're asked to flush receive queue, directly - * chain it back at the queue without processing it. - */ - if (test_bit(SC_OP_RXFLUSH, &sc->sc_flags)) { - RX_STAT_INC(rx_drop_rxflush); - goto requeue_drop_frag; - } - memset(rxs, 0, sizeof(struct ieee80211_rx_status)); rxs->mactime = (tsf & ~0xffffffffULL) | rs.rs_tstamp; @@ -1254,19 +1231,18 @@ requeue_drop_frag: sc->rx.frag = NULL; } requeue: + list_add_tail(&bf->list, &sc->rx.rxbuf); + if (flush) + continue; + if (edma) { - list_add_tail(&bf->list, &sc->rx.rxbuf); ath_rx_edma_buf_link(sc, qtype); } else { - list_move_tail(&bf->list, &sc->rx.rxbuf); ath_rx_buf_link(sc, bf); - if (!flush) - ath9k_hw_rxena(ah); + ath9k_hw_rxena(ah); } } while (1); - spin_unlock_bh(&sc->rx.rxbuflock); - if (!(ah->imask & ATH9K_INT_RXEOL)) { ah->imask |= (ATH9K_INT_RXEOL | ATH9K_INT_RXORN); ath9k_hw_set_interrupts(ah); diff --git a/drivers/net/wireless/ath/wil6210/Kconfig b/drivers/net/wireless/ath/wil6210/Kconfig new file mode 100644 index 000000000000..bac3d98a0cfb --- /dev/null +++ b/drivers/net/wireless/ath/wil6210/Kconfig @@ -0,0 +1,29 @@ +config WIL6210 + tristate "Wilocity 60g WiFi card wil6210 support" + depends on CFG80211 + depends on PCI + default n + ---help--- + This module adds support for wireless adapter based on + wil6210 chip by Wilocity. It supports operation on the + 60 GHz band, covered by the IEEE802.11ad standard. + + http://wireless.kernel.org/en/users/Drivers/wil6210 + + If you choose to build it as a module, it will be called + wil6210 + +config WIL6210_ISR_COR + bool "Use Clear-On-Read mode for ISR registers for wil6210" + depends on WIL6210 + default y + ---help--- + ISR registers on wil6210 chip may operate in either + COR (Clear-On-Read) or W1C (Write-1-to-Clear) mode. + For production code, use COR (say y); is default since + it saves extra target transaction; + For ISR debug, use W1C (say n); is allows to monitor ISR + registers with debugfs. If COR were used, ISR would + self-clear when accessed for debug purposes, it makes + such monitoring impossible. + Say y unless you debug interrupts diff --git a/drivers/net/wireless/ath/wil6210/Makefile b/drivers/net/wireless/ath/wil6210/Makefile new file mode 100644 index 000000000000..9396dc9fe3c5 --- /dev/null +++ b/drivers/net/wireless/ath/wil6210/Makefile @@ -0,0 +1,13 @@ +obj-$(CONFIG_WIL6210) += wil6210.o + +wil6210-objs := main.o +wil6210-objs += netdev.o +wil6210-objs += cfg80211.o +wil6210-objs += pcie_bus.o +wil6210-objs += debugfs.o +wil6210-objs += wmi.o +wil6210-objs += interrupt.o +wil6210-objs += txrx.o + +subdir-ccflags-y += -Werror +subdir-ccflags-y += -D__CHECK_ENDIAN__ diff --git a/drivers/net/wireless/ath/wil6210/cfg80211.c b/drivers/net/wireless/ath/wil6210/cfg80211.c new file mode 100644 index 000000000000..116f4e807ae1 --- /dev/null +++ b/drivers/net/wireless/ath/wil6210/cfg80211.c @@ -0,0 +1,573 @@ +/* + * Copyright (c) 2012 Qualcomm Atheros, Inc. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include <linux/kernel.h> +#include <linux/netdevice.h> +#include <linux/sched.h> +#include <linux/etherdevice.h> +#include <linux/wireless.h> +#include <linux/ieee80211.h> +#include <linux/slab.h> +#include <linux/version.h> +#include <net/cfg80211.h> + +#include "wil6210.h" +#include "wmi.h" + +#define CHAN60G(_channel, _flags) { \ + .band = IEEE80211_BAND_60GHZ, \ + .center_freq = 56160 + (2160 * (_channel)), \ + .hw_value = (_channel), \ + .flags = (_flags), \ + .max_antenna_gain = 0, \ + .max_power = 40, \ +} + +static struct ieee80211_channel wil_60ghz_channels[] = { + CHAN60G(1, 0), + CHAN60G(2, 0), + CHAN60G(3, 0), +/* channel 4 not supported yet */ +}; + +static struct ieee80211_supported_band wil_band_60ghz = { + .channels = wil_60ghz_channels, + .n_channels = ARRAY_SIZE(wil_60ghz_channels), + .ht_cap = { + .ht_supported = true, + .cap = 0, /* TODO */ + .ampdu_factor = IEEE80211_HT_MAX_AMPDU_64K, /* TODO */ + .ampdu_density = IEEE80211_HT_MPDU_DENSITY_8, /* TODO */ + .mcs = { + /* MCS 1..12 - SC PHY */ + .rx_mask = {0xfe, 0x1f}, /* 1..12 */ + .tx_params = IEEE80211_HT_MCS_TX_DEFINED, /* TODO */ + }, + }, +}; + +static const struct ieee80211_txrx_stypes +wil_mgmt_stypes[NUM_NL80211_IFTYPES] = { + [NL80211_IFTYPE_STATION] = { + .tx = BIT(IEEE80211_STYPE_ACTION >> 4) | + BIT(IEEE80211_STYPE_PROBE_RESP >> 4), + .rx = BIT(IEEE80211_STYPE_ACTION >> 4) | + BIT(IEEE80211_STYPE_PROBE_REQ >> 4) + }, + [NL80211_IFTYPE_AP] = { + .tx = BIT(IEEE80211_STYPE_ACTION >> 4) | + BIT(IEEE80211_STYPE_PROBE_RESP >> 4), + .rx = BIT(IEEE80211_STYPE_ACTION >> 4) | + BIT(IEEE80211_STYPE_PROBE_REQ >> 4) + }, + [NL80211_IFTYPE_P2P_CLIENT] = { + .tx = BIT(IEEE80211_STYPE_ACTION >> 4) | + BIT(IEEE80211_STYPE_PROBE_RESP >> 4), + .rx = BIT(IEEE80211_STYPE_ACTION >> 4) | + BIT(IEEE80211_STYPE_PROBE_REQ >> 4) + }, + [NL80211_IFTYPE_P2P_GO] = { + .tx = BIT(IEEE80211_STYPE_ACTION >> 4) | + BIT(IEEE80211_STYPE_PROBE_RESP >> 4), + .rx = BIT(IEEE80211_STYPE_ACTION >> 4) | + BIT(IEEE80211_STYPE_PROBE_REQ >> 4) + }, +}; + +static const u32 wil_cipher_suites[] = { + WLAN_CIPHER_SUITE_GCMP, +}; + +int wil_iftype_nl2wmi(enum nl80211_iftype type) +{ + static const struct { + enum nl80211_iftype nl; + enum wmi_network_type wmi; + } __nl2wmi[] = { + {NL80211_IFTYPE_ADHOC, WMI_NETTYPE_ADHOC}, + {NL80211_IFTYPE_STATION, WMI_NETTYPE_INFRA}, + {NL80211_IFTYPE_AP, WMI_NETTYPE_AP}, + {NL80211_IFTYPE_P2P_CLIENT, WMI_NETTYPE_P2P}, + {NL80211_IFTYPE_P2P_GO, WMI_NETTYPE_P2P}, + {NL80211_IFTYPE_MONITOR, WMI_NETTYPE_ADHOC}, /* FIXME */ + }; + uint i; + + for (i = 0; i < ARRAY_SIZE(__nl2wmi); i++) { + if (__nl2wmi[i].nl == type) + return __nl2wmi[i].wmi; + } + + return -EOPNOTSUPP; +} + +static int wil_cfg80211_get_station(struct wiphy *wiphy, + struct net_device *ndev, + u8 *mac, struct station_info *sinfo) +{ + struct wil6210_priv *wil = wiphy_to_wil(wiphy); + int rc; + struct wmi_notify_req_cmd cmd = { + .cid = 0, + .interval_usec = 0, + }; + + if (memcmp(mac, wil->dst_addr[0], ETH_ALEN)) + return -ENOENT; + + /* WMI_NOTIFY_REQ_DONE_EVENTID handler fills wil->stats.bf_mcs */ + rc = wmi_call(wil, WMI_NOTIFY_REQ_CMDID, &cmd, sizeof(cmd), + WMI_NOTIFY_REQ_DONE_EVENTID, NULL, 0, 20); + if (rc) + return rc; + + sinfo->generation = wil->sinfo_gen; + + sinfo->filled |= STATION_INFO_TX_BITRATE; + sinfo->txrate.flags = RATE_INFO_FLAGS_MCS | RATE_INFO_FLAGS_60G; + sinfo->txrate.mcs = wil->stats.bf_mcs; + sinfo->filled |= STATION_INFO_RX_BITRATE; + sinfo->rxrate.flags = RATE_INFO_FLAGS_MCS | RATE_INFO_FLAGS_60G; + sinfo->rxrate.mcs = wil->stats.last_mcs_rx; + + if (test_bit(wil_status_fwconnected, &wil->status)) { + sinfo->filled |= STATION_INFO_SIGNAL; + sinfo->signal = 12; /* TODO: provide real value */ + } + + return 0; +} + +static int wil_cfg80211_change_iface(struct wiphy *wiphy, + struct net_device *ndev, + enum nl80211_iftype type, u32 *flags, + struct vif_params *params) +{ + struct wil6210_priv *wil = wiphy_to_wil(wiphy); + struct wireless_dev *wdev = wil->wdev; + + switch (type) { + case NL80211_IFTYPE_STATION: + case NL80211_IFTYPE_AP: + case NL80211_IFTYPE_P2P_CLIENT: + case NL80211_IFTYPE_P2P_GO: + break; + case NL80211_IFTYPE_MONITOR: + if (flags) + wil->monitor_flags = *flags; + else + wil->monitor_flags = 0; + + break; + default: + return -EOPNOTSUPP; + } + + wdev->iftype = type; + + return 0; +} + +static int wil_cfg80211_scan(struct wiphy *wiphy, + struct cfg80211_scan_request *request) +{ + struct wil6210_priv *wil = wiphy_to_wil(wiphy); + struct wireless_dev *wdev = wil->wdev; + struct { + struct wmi_start_scan_cmd cmd; + u16 chnl[4]; + } __packed cmd; + uint i, n; + + if (wil->scan_request) { + wil_err(wil, "Already scanning\n"); + return -EAGAIN; + } + + /* check we are client side */ + switch (wdev->iftype) { + case NL80211_IFTYPE_STATION: + case NL80211_IFTYPE_P2P_CLIENT: + break; + default: + return -EOPNOTSUPP; + + } + + /* FW don't support scan after connection attempt */ + if (test_bit(wil_status_dontscan, &wil->status)) { + wil_err(wil, "Scan after connect attempt not supported\n"); + return -EBUSY; + } + + wil->scan_request = request; + + memset(&cmd, 0, sizeof(cmd)); + cmd.cmd.num_channels = 0; + n = min(request->n_channels, 4U); + for (i = 0; i < n; i++) { + int ch = request->channels[i]->hw_value; + if (ch == 0) { + wil_err(wil, + "Scan requested for unknown frequency %dMhz\n", + request->channels[i]->center_freq); + continue; + } + /* 0-based channel indexes */ + cmd.cmd.channel_list[cmd.cmd.num_channels++].channel = ch - 1; + wil_dbg(wil, "Scan for ch %d : %d MHz\n", ch, + request->channels[i]->center_freq); + } + + return wmi_send(wil, WMI_START_SCAN_CMDID, &cmd, sizeof(cmd.cmd) + + cmd.cmd.num_channels * sizeof(cmd.cmd.channel_list[0])); +} + +static int wil_cfg80211_connect(struct wiphy *wiphy, + struct net_device *ndev, + struct cfg80211_connect_params *sme) +{ + struct wil6210_priv *wil = wiphy_to_wil(wiphy); + struct cfg80211_bss *bss; + struct wmi_connect_cmd conn; + const u8 *ssid_eid; + const u8 *rsn_eid; + int ch; + int rc = 0; + + bss = cfg80211_get_bss(wiphy, sme->channel, sme->bssid, + sme->ssid, sme->ssid_len, + WLAN_CAPABILITY_ESS, WLAN_CAPABILITY_ESS); + if (!bss) { + wil_err(wil, "Unable to find BSS\n"); + return -ENOENT; + } + + ssid_eid = ieee80211_bss_get_ie(bss, WLAN_EID_SSID); + if (!ssid_eid) { + wil_err(wil, "No SSID\n"); + rc = -ENOENT; + goto out; + } + + rsn_eid = sme->ie ? + cfg80211_find_ie(WLAN_EID_RSN, sme->ie, sme->ie_len) : + NULL; + if (rsn_eid) { + if (sme->ie_len > WMI_MAX_IE_LEN) { + rc = -ERANGE; + wil_err(wil, "IE too large (%td bytes)\n", + sme->ie_len); + goto out; + } + /* + * For secure assoc, send: + * (1) WMI_DELETE_CIPHER_KEY_CMD + * (2) WMI_SET_APPIE_CMD + */ + rc = wmi_del_cipher_key(wil, 0, bss->bssid); + if (rc) { + wil_err(wil, "WMI_DELETE_CIPHER_KEY_CMD failed\n"); + goto out; + } + /* WMI_SET_APPIE_CMD */ + rc = wmi_set_ie(wil, WMI_FRAME_ASSOC_REQ, sme->ie_len, sme->ie); + if (rc) { + wil_err(wil, "WMI_SET_APPIE_CMD failed\n"); + goto out; + } + } + + /* WMI_CONNECT_CMD */ + memset(&conn, 0, sizeof(conn)); + switch (bss->capability & 0x03) { + case WLAN_CAPABILITY_DMG_TYPE_AP: + conn.network_type = WMI_NETTYPE_INFRA; + break; + case WLAN_CAPABILITY_DMG_TYPE_PBSS: + conn.network_type = WMI_NETTYPE_P2P; + break; + default: + wil_err(wil, "Unsupported BSS type, capability= 0x%04x\n", + bss->capability); + goto out; + } + if (rsn_eid) { + conn.dot11_auth_mode = WMI_AUTH11_SHARED; + conn.auth_mode = WMI_AUTH_WPA2_PSK; + conn.pairwise_crypto_type = WMI_CRYPT_AES_GCMP; + conn.pairwise_crypto_len = 16; + } else { + conn.dot11_auth_mode = WMI_AUTH11_OPEN; + conn.auth_mode = WMI_AUTH_NONE; + } + + conn.ssid_len = min_t(u8, ssid_eid[1], 32); + memcpy(conn.ssid, ssid_eid+2, conn.ssid_len); + + ch = bss->channel->hw_value; + if (ch == 0) { + wil_err(wil, "BSS at unknown frequency %dMhz\n", + bss->channel->center_freq); + rc = -EOPNOTSUPP; + goto out; + } + conn.channel = ch - 1; + + memcpy(conn.bssid, bss->bssid, 6); + memcpy(conn.dst_mac, bss->bssid, 6); + /* + * FW don't support scan after connection attempt + */ + set_bit(wil_status_dontscan, &wil->status); + + rc = wmi_send(wil, WMI_CONNECT_CMDID, &conn, sizeof(conn)); + if (rc == 0) { + /* Connect can take lots of time */ + mod_timer(&wil->connect_timer, + jiffies + msecs_to_jiffies(2000)); + } + + out: + cfg80211_put_bss(bss); + + return rc; +} + +static int wil_cfg80211_disconnect(struct wiphy *wiphy, + struct net_device *ndev, + u16 reason_code) +{ + int rc; + struct wil6210_priv *wil = wiphy_to_wil(wiphy); + + rc = wmi_send(wil, WMI_DISCONNECT_CMDID, NULL, 0); + + return rc; +} + +static int wil_cfg80211_set_channel(struct wiphy *wiphy, + struct cfg80211_chan_def *chandef) +{ + struct wil6210_priv *wil = wiphy_to_wil(wiphy); + struct wireless_dev *wdev = wil->wdev; + + wdev->preset_chandef = *chandef; + + return 0; +} + +static int wil_cfg80211_add_key(struct wiphy *wiphy, + struct net_device *ndev, + u8 key_index, bool pairwise, + const u8 *mac_addr, + struct key_params *params) +{ + struct wil6210_priv *wil = wiphy_to_wil(wiphy); + + /* group key is not used */ + if (!pairwise) + return 0; + + return wmi_add_cipher_key(wil, key_index, mac_addr, + params->key_len, params->key); +} + +static int wil_cfg80211_del_key(struct wiphy *wiphy, + struct net_device *ndev, + u8 key_index, bool pairwise, + const u8 *mac_addr) +{ + struct wil6210_priv *wil = wiphy_to_wil(wiphy); + + /* group key is not used */ + if (!pairwise) + return 0; + + return wmi_del_cipher_key(wil, key_index, mac_addr); +} + +/* Need to be present or wiphy_new() will WARN */ +static int wil_cfg80211_set_default_key(struct wiphy *wiphy, + struct net_device *ndev, + u8 key_index, bool unicast, + bool multicast) +{ + return 0; +} + +static int wil_cfg80211_start_ap(struct wiphy *wiphy, + struct net_device *ndev, + struct cfg80211_ap_settings *info) +{ + int rc = 0; + struct wil6210_priv *wil = wiphy_to_wil(wiphy); + struct wireless_dev *wdev = ndev->ieee80211_ptr; + struct ieee80211_channel *channel = info->chandef.chan; + struct cfg80211_beacon_data *bcon = &info->beacon; + u8 wmi_nettype = wil_iftype_nl2wmi(wdev->iftype); + + if (!channel) { + wil_err(wil, "AP: No channel???\n"); + return -EINVAL; + } + + wil_dbg(wil, "AP on Channel %d %d MHz, %s\n", channel->hw_value, + channel->center_freq, info->privacy ? "secure" : "open"); + print_hex_dump_bytes("SSID ", DUMP_PREFIX_OFFSET, + info->ssid, info->ssid_len); + + rc = wil_reset(wil); + if (rc) + return rc; + + rc = wmi_set_ssid(wil, info->ssid_len, info->ssid); + if (rc) + return rc; + + rc = wmi_set_channel(wil, channel->hw_value); + if (rc) + return rc; + + /* MAC address - pre-requisite for other commands */ + wmi_set_mac_address(wil, ndev->dev_addr); + + /* IE's */ + /* bcon 'head IE's are not relevant for 60g band */ + wmi_set_ie(wil, WMI_FRAME_BEACON, bcon->beacon_ies_len, + bcon->beacon_ies); + wmi_set_ie(wil, WMI_FRAME_PROBE_RESP, bcon->proberesp_ies_len, + bcon->proberesp_ies); + wmi_set_ie(wil, WMI_FRAME_ASSOC_RESP, bcon->assocresp_ies_len, + bcon->assocresp_ies); + + wil->secure_pcp = info->privacy; + + rc = wmi_set_bcon(wil, info->beacon_interval, wmi_nettype); + if (rc) + return rc; + + /* Rx VRING. After MAC and beacon */ + rc = wil_rx_init(wil); + + netif_carrier_on(ndev); + + return rc; +} + +static int wil_cfg80211_stop_ap(struct wiphy *wiphy, + struct net_device *ndev) +{ + int rc = 0; + struct wil6210_priv *wil = wiphy_to_wil(wiphy); + struct wireless_dev *wdev = ndev->ieee80211_ptr; + u8 wmi_nettype = wil_iftype_nl2wmi(wdev->iftype); + + /* To stop beaconing, set BI to 0 */ + rc = wmi_set_bcon(wil, 0, wmi_nettype); + + return rc; +} + +static struct cfg80211_ops wil_cfg80211_ops = { + .scan = wil_cfg80211_scan, + .connect = wil_cfg80211_connect, + .disconnect = wil_cfg80211_disconnect, + .change_virtual_intf = wil_cfg80211_change_iface, + .get_station = wil_cfg80211_get_station, + .set_monitor_channel = wil_cfg80211_set_channel, + .add_key = wil_cfg80211_add_key, + .del_key = wil_cfg80211_del_key, + .set_default_key = wil_cfg80211_set_default_key, + /* AP mode */ + .start_ap = wil_cfg80211_start_ap, + .stop_ap = wil_cfg80211_stop_ap, +}; + +static void wil_wiphy_init(struct wiphy *wiphy) +{ + /* TODO: set real value */ + wiphy->max_scan_ssids = 10; + wiphy->max_num_pmkids = 0 /* TODO: */; + wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION) | + BIT(NL80211_IFTYPE_AP) | + BIT(NL80211_IFTYPE_MONITOR); + /* TODO: enable P2P when integrated with supplicant: + * BIT(NL80211_IFTYPE_P2P_CLIENT) | BIT(NL80211_IFTYPE_P2P_GO) + */ + wiphy->flags |= WIPHY_FLAG_HAVE_AP_SME | + WIPHY_FLAG_AP_PROBE_RESP_OFFLOAD; + dev_warn(wiphy_dev(wiphy), "%s : flags = 0x%08x\n", + __func__, wiphy->flags); + wiphy->probe_resp_offload = + NL80211_PROBE_RESP_OFFLOAD_SUPPORT_WPS | + NL80211_PROBE_RESP_OFFLOAD_SUPPORT_WPS2 | + NL80211_PROBE_RESP_OFFLOAD_SUPPORT_P2P; + + wiphy->bands[IEEE80211_BAND_60GHZ] = &wil_band_60ghz; + + /* TODO: figure this out */ + wiphy->signal_type = CFG80211_SIGNAL_TYPE_MBM; + + wiphy->cipher_suites = wil_cipher_suites; + wiphy->n_cipher_suites = ARRAY_SIZE(wil_cipher_suites); + wiphy->mgmt_stypes = wil_mgmt_stypes; +} + +struct wireless_dev *wil_cfg80211_init(struct device *dev) +{ + int rc = 0; + struct wireless_dev *wdev; + + wdev = kzalloc(sizeof(struct wireless_dev), GFP_KERNEL); + if (!wdev) + return ERR_PTR(-ENOMEM); + + wdev->wiphy = wiphy_new(&wil_cfg80211_ops, + sizeof(struct wil6210_priv)); + if (!wdev->wiphy) { + rc = -ENOMEM; + goto out; + } + + set_wiphy_dev(wdev->wiphy, dev); + wil_wiphy_init(wdev->wiphy); + + rc = wiphy_register(wdev->wiphy); + if (rc < 0) + goto out_failed_reg; + + return wdev; + +out_failed_reg: + wiphy_free(wdev->wiphy); +out: + kfree(wdev); + + return ERR_PTR(rc); +} + +void wil_wdev_free(struct wil6210_priv *wil) +{ + struct wireless_dev *wdev = wil_to_wdev(wil); + + if (!wdev) + return; + + wiphy_unregister(wdev->wiphy); + wiphy_free(wdev->wiphy); + kfree(wdev); +} diff --git a/drivers/net/wireless/ath/wil6210/dbg_hexdump.h b/drivers/net/wireless/ath/wil6210/dbg_hexdump.h new file mode 100644 index 000000000000..6a315ba5aa7d --- /dev/null +++ b/drivers/net/wireless/ath/wil6210/dbg_hexdump.h @@ -0,0 +1,30 @@ +#ifndef WIL_DBG_HEXDUMP_H_ +#define WIL_DBG_HEXDUMP_H_ + +#if defined(CONFIG_DYNAMIC_DEBUG) +#define wil_dynamic_hex_dump(prefix_str, prefix_type, rowsize, \ + groupsize, buf, len, ascii) \ +do { \ + DEFINE_DYNAMIC_DEBUG_METADATA(descriptor, \ + __builtin_constant_p(prefix_str) ? prefix_str : "hexdump");\ + if (unlikely(descriptor.flags & _DPRINTK_FLAGS_PRINT)) \ + print_hex_dump(KERN_DEBUG, prefix_str, \ + prefix_type, rowsize, groupsize, \ + buf, len, ascii); \ +} while (0) + +#define wil_print_hex_dump_debug(prefix_str, prefix_type, rowsize, \ + groupsize, buf, len, ascii) \ + wil_dynamic_hex_dump(prefix_str, prefix_type, rowsize, \ + groupsize, buf, len, ascii) + +#define print_hex_dump_bytes(prefix_str, prefix_type, buf, len) \ + wil_dynamic_hex_dump(prefix_str, prefix_type, 16, 1, buf, len, true) +#else /* defined(CONFIG_DYNAMIC_DEBUG) */ +#define wil_print_hex_dump_debug(prefix_str, prefix_type, rowsize, \ + groupsize, buf, len, ascii) \ + print_hex_dump(KERN_DEBUG, prefix_str, prefix_type, rowsize, \ + groupsize, buf, len, ascii) +#endif /* defined(CONFIG_DYNAMIC_DEBUG) */ + +#endif /* WIL_DBG_HEXDUMP_H_ */ diff --git a/drivers/net/wireless/ath/wil6210/debugfs.c b/drivers/net/wireless/ath/wil6210/debugfs.c new file mode 100644 index 000000000000..65fc9683bfd8 --- /dev/null +++ b/drivers/net/wireless/ath/wil6210/debugfs.c @@ -0,0 +1,603 @@ +/* + * Copyright (c) 2012 Qualcomm Atheros, Inc. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include <linux/module.h> +#include <linux/debugfs.h> +#include <linux/seq_file.h> +#include <linux/pci.h> +#include <linux/rtnetlink.h> + +#include "wil6210.h" +#include "txrx.h" + +/* Nasty hack. Better have per device instances */ +static u32 mem_addr; +static u32 dbg_txdesc_index; + +static void wil_print_vring(struct seq_file *s, struct wil6210_priv *wil, + const char *name, struct vring *vring) +{ + void __iomem *x = wmi_addr(wil, vring->hwtail); + + seq_printf(s, "VRING %s = {\n", name); + seq_printf(s, " pa = 0x%016llx\n", (unsigned long long)vring->pa); + seq_printf(s, " va = 0x%p\n", vring->va); + seq_printf(s, " size = %d\n", vring->size); + seq_printf(s, " swtail = %d\n", vring->swtail); + seq_printf(s, " swhead = %d\n", vring->swhead); + seq_printf(s, " hwtail = [0x%08x] -> ", vring->hwtail); + if (x) + seq_printf(s, "0x%08x\n", ioread32(x)); + else + seq_printf(s, "???\n"); + + if (vring->va && (vring->size < 1025)) { + uint i; + for (i = 0; i < vring->size; i++) { + volatile struct vring_tx_desc *d = &vring->va[i].tx; + if ((i % 64) == 0 && (i != 0)) + seq_printf(s, "\n"); + seq_printf(s, "%s", (d->dma.status & BIT(0)) ? + "S" : (vring->ctx[i] ? "H" : "h")); + } + seq_printf(s, "\n"); + } + seq_printf(s, "}\n"); +} + +static int wil_vring_debugfs_show(struct seq_file *s, void *data) +{ + uint i; + struct wil6210_priv *wil = s->private; + + wil_print_vring(s, wil, "rx", &wil->vring_rx); + + for (i = 0; i < ARRAY_SIZE(wil->vring_tx); i++) { + struct vring *vring = &(wil->vring_tx[i]); + if (vring->va) { + char name[10]; + snprintf(name, sizeof(name), "tx_%2d", i); + wil_print_vring(s, wil, name, vring); + } + } + + return 0; +} + +static int wil_vring_seq_open(struct inode *inode, struct file *file) +{ + return single_open(file, wil_vring_debugfs_show, inode->i_private); +} + +static const struct file_operations fops_vring = { + .open = wil_vring_seq_open, + .release = single_release, + .read = seq_read, + .llseek = seq_lseek, +}; + +static void wil_print_ring(struct seq_file *s, const char *prefix, + void __iomem *off) +{ + struct wil6210_priv *wil = s->private; + struct wil6210_mbox_ring r; + int rsize; + uint i; + + wil_memcpy_fromio_32(&r, off, sizeof(r)); + wil_mbox_ring_le2cpus(&r); + /* + * we just read memory block from NIC. This memory may be + * garbage. Check validity before using it. + */ + rsize = r.size / sizeof(struct wil6210_mbox_ring_desc); + + seq_printf(s, "ring %s = {\n", prefix); + seq_printf(s, " base = 0x%08x\n", r.base); + seq_printf(s, " size = 0x%04x bytes -> %d entries\n", r.size, rsize); + seq_printf(s, " tail = 0x%08x\n", r.tail); + seq_printf(s, " head = 0x%08x\n", r.head); + seq_printf(s, " entry size = %d\n", r.entry_size); + + if (r.size % sizeof(struct wil6210_mbox_ring_desc)) { + seq_printf(s, " ??? size is not multiple of %zd, garbage?\n", + sizeof(struct wil6210_mbox_ring_desc)); + goto out; + } + + if (!wmi_addr(wil, r.base) || + !wmi_addr(wil, r.tail) || + !wmi_addr(wil, r.head)) { + seq_printf(s, " ??? pointers are garbage?\n"); + goto out; + } + + for (i = 0; i < rsize; i++) { + struct wil6210_mbox_ring_desc d; + struct wil6210_mbox_hdr hdr; + size_t delta = i * sizeof(d); + void __iomem *x = wil->csr + HOSTADDR(r.base) + delta; + + wil_memcpy_fromio_32(&d, x, sizeof(d)); + + seq_printf(s, " [%2x] %s %s%s 0x%08x", i, + d.sync ? "F" : "E", + (r.tail - r.base == delta) ? "t" : " ", + (r.head - r.base == delta) ? "h" : " ", + le32_to_cpu(d.addr)); + if (0 == wmi_read_hdr(wil, d.addr, &hdr)) { + u16 len = le16_to_cpu(hdr.len); + seq_printf(s, " -> %04x %04x %04x %02x\n", + le16_to_cpu(hdr.seq), len, + le16_to_cpu(hdr.type), hdr.flags); + if (len <= MAX_MBOXITEM_SIZE) { + int n = 0; + unsigned char printbuf[16 * 3 + 2]; + unsigned char databuf[MAX_MBOXITEM_SIZE]; + void __iomem *src = wmi_buffer(wil, d.addr) + + sizeof(struct wil6210_mbox_hdr); + /* + * No need to check @src for validity - + * we already validated @d.addr while + * reading header + */ + wil_memcpy_fromio_32(databuf, src, len); + while (n < len) { + int l = min(len - n, 16); + hex_dump_to_buffer(databuf + n, l, + 16, 1, printbuf, + sizeof(printbuf), + false); + seq_printf(s, " : %s\n", printbuf); + n += l; + } + } + } else { + seq_printf(s, "\n"); + } + } + out: + seq_printf(s, "}\n"); +} + +static int wil_mbox_debugfs_show(struct seq_file *s, void *data) +{ + struct wil6210_priv *wil = s->private; + + wil_print_ring(s, "tx", wil->csr + HOST_MBOX + + offsetof(struct wil6210_mbox_ctl, tx)); + wil_print_ring(s, "rx", wil->csr + HOST_MBOX + + offsetof(struct wil6210_mbox_ctl, rx)); + + return 0; +} + +static int wil_mbox_seq_open(struct inode *inode, struct file *file) +{ + return single_open(file, wil_mbox_debugfs_show, inode->i_private); +} + +static const struct file_operations fops_mbox = { + .open = wil_mbox_seq_open, + .release = single_release, + .read = seq_read, + .llseek = seq_lseek, +}; + +static int wil_debugfs_iomem_x32_set(void *data, u64 val) +{ + iowrite32(val, (void __iomem *)data); + wmb(); /* make sure write propagated to HW */ + + return 0; +} + +static int wil_debugfs_iomem_x32_get(void *data, u64 *val) +{ + *val = ioread32((void __iomem *)data); + + return 0; +} + +DEFINE_SIMPLE_ATTRIBUTE(fops_iomem_x32, wil_debugfs_iomem_x32_get, + wil_debugfs_iomem_x32_set, "0x%08llx\n"); + +static struct dentry *wil_debugfs_create_iomem_x32(const char *name, + mode_t mode, + struct dentry *parent, + void __iomem *value) +{ + return debugfs_create_file(name, mode, parent, (void * __force)value, + &fops_iomem_x32); +} + +static int wil6210_debugfs_create_ISR(struct wil6210_priv *wil, + const char *name, + struct dentry *parent, u32 off) +{ + struct dentry *d = debugfs_create_dir(name, parent); + + if (IS_ERR_OR_NULL(d)) + return -ENODEV; + + wil_debugfs_create_iomem_x32("ICC", S_IRUGO | S_IWUSR, d, + wil->csr + off); + wil_debugfs_create_iomem_x32("ICR", S_IRUGO | S_IWUSR, d, + wil->csr + off + 4); + wil_debugfs_create_iomem_x32("ICM", S_IRUGO | S_IWUSR, d, + wil->csr + off + 8); + wil_debugfs_create_iomem_x32("ICS", S_IWUSR, d, + wil->csr + off + 12); + wil_debugfs_create_iomem_x32("IMV", S_IRUGO | S_IWUSR, d, + wil->csr + off + 16); + wil_debugfs_create_iomem_x32("IMS", S_IWUSR, d, + wil->csr + off + 20); + wil_debugfs_create_iomem_x32("IMC", S_IWUSR, d, + wil->csr + off + 24); + + return 0; +} + +static int wil6210_debugfs_create_pseudo_ISR(struct wil6210_priv *wil, + struct dentry *parent) +{ + struct dentry *d = debugfs_create_dir("PSEUDO_ISR", parent); + + if (IS_ERR_OR_NULL(d)) + return -ENODEV; + + wil_debugfs_create_iomem_x32("CAUSE", S_IRUGO, d, wil->csr + + HOSTADDR(RGF_DMA_PSEUDO_CAUSE)); + wil_debugfs_create_iomem_x32("MASK_SW", S_IRUGO, d, wil->csr + + HOSTADDR(RGF_DMA_PSEUDO_CAUSE_MASK_SW)); + wil_debugfs_create_iomem_x32("MASK_FW", S_IRUGO, d, wil->csr + + HOSTADDR(RGF_DMA_PSEUDO_CAUSE_MASK_FW)); + + return 0; +} + +static int wil6210_debugfs_create_ITR_CNT(struct wil6210_priv *wil, + struct dentry *parent) +{ + struct dentry *d = debugfs_create_dir("ITR_CNT", parent); + + if (IS_ERR_OR_NULL(d)) + return -ENODEV; + + wil_debugfs_create_iomem_x32("TRSH", S_IRUGO, d, wil->csr + + HOSTADDR(RGF_DMA_ITR_CNT_TRSH)); + wil_debugfs_create_iomem_x32("DATA", S_IRUGO, d, wil->csr + + HOSTADDR(RGF_DMA_ITR_CNT_DATA)); + wil_debugfs_create_iomem_x32("CTL", S_IRUGO, d, wil->csr + + HOSTADDR(RGF_DMA_ITR_CNT_CRL)); + + return 0; +} + +static int wil_memread_debugfs_show(struct seq_file *s, void *data) +{ + struct wil6210_priv *wil = s->private; + void __iomem *a = wmi_buffer(wil, cpu_to_le32(mem_addr)); + + if (a) + seq_printf(s, "[0x%08x] = 0x%08x\n", mem_addr, ioread32(a)); + else + seq_printf(s, "[0x%08x] = INVALID\n", mem_addr); + + return 0; +} + +static int wil_memread_seq_open(struct inode *inode, struct file *file) +{ + return single_open(file, wil_memread_debugfs_show, inode->i_private); +} + +static const struct file_operations fops_memread = { + .open = wil_memread_seq_open, + .release = single_release, + .read = seq_read, + .llseek = seq_lseek, +}; + +static int wil_default_open(struct inode *inode, struct file *file) +{ + if (inode->i_private) + file->private_data = inode->i_private; + + return 0; +} + +static ssize_t wil_read_file_ioblob(struct file *file, char __user *user_buf, + size_t count, loff_t *ppos) +{ + enum { max_count = 4096 }; + struct debugfs_blob_wrapper *blob = file->private_data; + loff_t pos = *ppos; + size_t available = blob->size; + void *buf; + size_t ret; + + if (pos < 0) + return -EINVAL; + + if (pos >= available || !count) + return 0; + + if (count > available - pos) + count = available - pos; + if (count > max_count) + count = max_count; + + buf = kmalloc(count, GFP_KERNEL); + if (!buf) + return -ENOMEM; + + wil_memcpy_fromio_32(buf, (const volatile void __iomem *)blob->data + + pos, count); + + ret = copy_to_user(user_buf, buf, count); + kfree(buf); + if (ret == count) + return -EFAULT; + + count -= ret; + *ppos = pos + count; + + return count; +} + +static const struct file_operations fops_ioblob = { + .read = wil_read_file_ioblob, + .open = wil_default_open, + .llseek = default_llseek, +}; + +static +struct dentry *wil_debugfs_create_ioblob(const char *name, + mode_t mode, + struct dentry *parent, + struct debugfs_blob_wrapper *blob) +{ + return debugfs_create_file(name, mode, parent, blob, &fops_ioblob); +} +/*---reset---*/ +static ssize_t wil_write_file_reset(struct file *file, const char __user *buf, + size_t len, loff_t *ppos) +{ + struct wil6210_priv *wil = file->private_data; + struct net_device *ndev = wil_to_ndev(wil); + + /** + * BUG: + * this code does NOT sync device state with the rest of system + * use with care, debug only!!! + */ + rtnl_lock(); + dev_close(ndev); + ndev->flags &= ~IFF_UP; + rtnl_unlock(); + wil_reset(wil); + + return len; +} + +static const struct file_operations fops_reset = { + .write = wil_write_file_reset, + .open = wil_default_open, +}; +/*---------Tx descriptor------------*/ + +static int wil_txdesc_debugfs_show(struct seq_file *s, void *data) +{ + struct wil6210_priv *wil = s->private; + struct vring *vring = &(wil->vring_tx[0]); + + if (!vring->va) { + seq_printf(s, "No Tx VRING\n"); + return 0; + } + + if (dbg_txdesc_index < vring->size) { + volatile struct vring_tx_desc *d = + &(vring->va[dbg_txdesc_index].tx); + volatile u32 *u = (volatile u32 *)d; + struct sk_buff *skb = vring->ctx[dbg_txdesc_index]; + + seq_printf(s, "Tx[%3d] = {\n", dbg_txdesc_index); + seq_printf(s, " MAC = 0x%08x 0x%08x 0x%08x 0x%08x\n", + u[0], u[1], u[2], u[3]); + seq_printf(s, " DMA = 0x%08x 0x%08x 0x%08x 0x%08x\n", + u[4], u[5], u[6], u[7]); + seq_printf(s, " SKB = %p\n", skb); + + if (skb) { + unsigned char printbuf[16 * 3 + 2]; + int i = 0; + int len = skb_headlen(skb); + void *p = skb->data; + + seq_printf(s, " len = %d\n", len); + + while (i < len) { + int l = min(len - i, 16); + hex_dump_to_buffer(p + i, l, 16, 1, printbuf, + sizeof(printbuf), false); + seq_printf(s, " : %s\n", printbuf); + i += l; + } + } + seq_printf(s, "}\n"); + } else { + seq_printf(s, "TxDesc index (%d) >= size (%d)\n", + dbg_txdesc_index, vring->size); + } + + return 0; +} + +static int wil_txdesc_seq_open(struct inode *inode, struct file *file) +{ + return single_open(file, wil_txdesc_debugfs_show, inode->i_private); +} + +static const struct file_operations fops_txdesc = { + .open = wil_txdesc_seq_open, + .release = single_release, + .read = seq_read, + .llseek = seq_lseek, +}; + +/*---------beamforming------------*/ +static int wil_bf_debugfs_show(struct seq_file *s, void *data) +{ + struct wil6210_priv *wil = s->private; + seq_printf(s, + "TSF : 0x%016llx\n" + "TxMCS : %d\n" + "Sectors(rx:tx) my %2d:%2d peer %2d:%2d\n", + wil->stats.tsf, wil->stats.bf_mcs, + wil->stats.my_rx_sector, wil->stats.my_tx_sector, + wil->stats.peer_rx_sector, wil->stats.peer_tx_sector); + return 0; +} + +static int wil_bf_seq_open(struct inode *inode, struct file *file) +{ + return single_open(file, wil_bf_debugfs_show, inode->i_private); +} + +static const struct file_operations fops_bf = { + .open = wil_bf_seq_open, + .release = single_release, + .read = seq_read, + .llseek = seq_lseek, +}; +/*---------SSID------------*/ +static ssize_t wil_read_file_ssid(struct file *file, char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct wil6210_priv *wil = file->private_data; + struct wireless_dev *wdev = wil_to_wdev(wil); + + return simple_read_from_buffer(user_buf, count, ppos, + wdev->ssid, wdev->ssid_len); +} + +static ssize_t wil_write_file_ssid(struct file *file, const char __user *buf, + size_t count, loff_t *ppos) +{ + struct wil6210_priv *wil = file->private_data; + struct wireless_dev *wdev = wil_to_wdev(wil); + struct net_device *ndev = wil_to_ndev(wil); + + if (*ppos != 0) { + wil_err(wil, "Unable to set SSID substring from [%d]\n", + (int)*ppos); + return -EINVAL; + } + + if (count > sizeof(wdev->ssid)) { + wil_err(wil, "SSID too long, len = %d\n", (int)count); + return -EINVAL; + } + if (netif_running(ndev)) { + wil_err(wil, "Unable to change SSID on running interface\n"); + return -EINVAL; + } + + wdev->ssid_len = count; + return simple_write_to_buffer(wdev->ssid, wdev->ssid_len, ppos, + buf, count); +} + +static const struct file_operations fops_ssid = { + .read = wil_read_file_ssid, + .write = wil_write_file_ssid, + .open = wil_default_open, +}; + +/*----------------*/ +int wil6210_debugfs_init(struct wil6210_priv *wil) +{ + struct dentry *dbg = wil->debug = debugfs_create_dir(WIL_NAME, + wil_to_wiphy(wil)->debugfsdir); + + if (IS_ERR_OR_NULL(dbg)) + return -ENODEV; + + debugfs_create_file("mbox", S_IRUGO, dbg, wil, &fops_mbox); + debugfs_create_file("vrings", S_IRUGO, dbg, wil, &fops_vring); + debugfs_create_file("txdesc", S_IRUGO, dbg, wil, &fops_txdesc); + debugfs_create_u32("txdesc_index", S_IRUGO | S_IWUSR, dbg, + &dbg_txdesc_index); + debugfs_create_file("bf", S_IRUGO, dbg, wil, &fops_bf); + debugfs_create_file("ssid", S_IRUGO | S_IWUSR, dbg, wil, &fops_ssid); + debugfs_create_u32("secure_pcp", S_IRUGO | S_IWUSR, dbg, + &wil->secure_pcp); + + wil6210_debugfs_create_ISR(wil, "USER_ICR", dbg, + HOSTADDR(RGF_USER_USER_ICR)); + wil6210_debugfs_create_ISR(wil, "DMA_EP_TX_ICR", dbg, + HOSTADDR(RGF_DMA_EP_TX_ICR)); + wil6210_debugfs_create_ISR(wil, "DMA_EP_RX_ICR", dbg, + HOSTADDR(RGF_DMA_EP_RX_ICR)); + wil6210_debugfs_create_ISR(wil, "DMA_EP_MISC_ICR", dbg, + HOSTADDR(RGF_DMA_EP_MISC_ICR)); + wil6210_debugfs_create_pseudo_ISR(wil, dbg); + wil6210_debugfs_create_ITR_CNT(wil, dbg); + + debugfs_create_u32("mem_addr", S_IRUGO | S_IWUSR, dbg, &mem_addr); + debugfs_create_file("mem_val", S_IRUGO, dbg, wil, &fops_memread); + + debugfs_create_file("reset", S_IWUSR, dbg, wil, &fops_reset); + + wil->rgf_blob.data = (void * __force)wil->csr + 0; + wil->rgf_blob.size = 0xa000; + wil_debugfs_create_ioblob("blob_rgf", S_IRUGO, dbg, &wil->rgf_blob); + + wil->fw_code_blob.data = (void * __force)wil->csr + 0x40000; + wil->fw_code_blob.size = 0x40000; + wil_debugfs_create_ioblob("blob_fw_code", S_IRUGO, dbg, + &wil->fw_code_blob); + + wil->fw_data_blob.data = (void * __force)wil->csr + 0x80000; + wil->fw_data_blob.size = 0x8000; + wil_debugfs_create_ioblob("blob_fw_data", S_IRUGO, dbg, + &wil->fw_data_blob); + + wil->fw_peri_blob.data = (void * __force)wil->csr + 0x88000; + wil->fw_peri_blob.size = 0x18000; + wil_debugfs_create_ioblob("blob_fw_peri", S_IRUGO, dbg, + &wil->fw_peri_blob); + + wil->uc_code_blob.data = (void * __force)wil->csr + 0xa0000; + wil->uc_code_blob.size = 0x10000; + wil_debugfs_create_ioblob("blob_uc_code", S_IRUGO, dbg, + &wil->uc_code_blob); + + wil->uc_data_blob.data = (void * __force)wil->csr + 0xb0000; + wil->uc_data_blob.size = 0x4000; + wil_debugfs_create_ioblob("blob_uc_data", S_IRUGO, dbg, + &wil->uc_data_blob); + + return 0; +} + +void wil6210_debugfs_remove(struct wil6210_priv *wil) +{ + debugfs_remove_recursive(wil->debug); + wil->debug = NULL; +} diff --git a/drivers/net/wireless/ath/wil6210/interrupt.c b/drivers/net/wireless/ath/wil6210/interrupt.c new file mode 100644 index 000000000000..38049da71049 --- /dev/null +++ b/drivers/net/wireless/ath/wil6210/interrupt.c @@ -0,0 +1,471 @@ +/* + * Copyright (c) 2012 Qualcomm Atheros, Inc. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include <linux/interrupt.h> + +#include "wil6210.h" + +/** + * Theory of operation: + * + * There is ISR pseudo-cause register, + * dma_rgf->DMA_RGF.PSEUDO_CAUSE.PSEUDO_CAUSE + * Its bits represents OR'ed bits from 3 real ISR registers: + * TX, RX, and MISC. + * + * Registers may be configured to either "write 1 to clear" or + * "clear on read" mode + * + * When handling interrupt, one have to mask/unmask interrupts for the + * real ISR registers, or hardware may malfunction. + * + */ + +#define WIL6210_IRQ_DISABLE (0xFFFFFFFFUL) +#define WIL6210_IMC_RX BIT_DMA_EP_RX_ICR_RX_DONE +#define WIL6210_IMC_TX (BIT_DMA_EP_TX_ICR_TX_DONE | \ + BIT_DMA_EP_TX_ICR_TX_DONE_N(0)) +#define WIL6210_IMC_MISC (ISR_MISC_FW_READY | ISR_MISC_MBOX_EVT) + +#define WIL6210_IRQ_PSEUDO_MASK (u32)(~(BIT_DMA_PSEUDO_CAUSE_RX | \ + BIT_DMA_PSEUDO_CAUSE_TX | \ + BIT_DMA_PSEUDO_CAUSE_MISC)) + +#if defined(CONFIG_WIL6210_ISR_COR) +/* configure to Clear-On-Read mode */ +#define WIL_ICR_ICC_VALUE (0xFFFFFFFFUL) + +static inline void wil_icr_clear(u32 x, void __iomem *addr) +{ + +} +#else /* defined(CONFIG_WIL6210_ISR_COR) */ +/* configure to Write-1-to-Clear mode */ +#define WIL_ICR_ICC_VALUE (0UL) + +static inline void wil_icr_clear(u32 x, void __iomem *addr) +{ + iowrite32(x, addr); +} +#endif /* defined(CONFIG_WIL6210_ISR_COR) */ + +static inline u32 wil_ioread32_and_clear(void __iomem *addr) +{ + u32 x = ioread32(addr); + + wil_icr_clear(x, addr); + + return x; +} + +static void wil6210_mask_irq_tx(struct wil6210_priv *wil) +{ + iowrite32(WIL6210_IRQ_DISABLE, wil->csr + + HOSTADDR(RGF_DMA_EP_TX_ICR) + + offsetof(struct RGF_ICR, IMS)); +} + +static void wil6210_mask_irq_rx(struct wil6210_priv *wil) +{ + iowrite32(WIL6210_IRQ_DISABLE, wil->csr + + HOSTADDR(RGF_DMA_EP_RX_ICR) + + offsetof(struct RGF_ICR, IMS)); +} + +static void wil6210_mask_irq_misc(struct wil6210_priv *wil) +{ + iowrite32(WIL6210_IRQ_DISABLE, wil->csr + + HOSTADDR(RGF_DMA_EP_MISC_ICR) + + offsetof(struct RGF_ICR, IMS)); +} + +static void wil6210_mask_irq_pseudo(struct wil6210_priv *wil) +{ + wil_dbg_IRQ(wil, "%s()\n", __func__); + + iowrite32(WIL6210_IRQ_DISABLE, wil->csr + + HOSTADDR(RGF_DMA_PSEUDO_CAUSE_MASK_SW)); + + clear_bit(wil_status_irqen, &wil->status); +} + +static void wil6210_unmask_irq_tx(struct wil6210_priv *wil) +{ + iowrite32(WIL6210_IMC_TX, wil->csr + + HOSTADDR(RGF_DMA_EP_TX_ICR) + + offsetof(struct RGF_ICR, IMC)); +} + +static void wil6210_unmask_irq_rx(struct wil6210_priv *wil) +{ + iowrite32(WIL6210_IMC_RX, wil->csr + + HOSTADDR(RGF_DMA_EP_RX_ICR) + + offsetof(struct RGF_ICR, IMC)); +} + +static void wil6210_unmask_irq_misc(struct wil6210_priv *wil) +{ + iowrite32(WIL6210_IMC_MISC, wil->csr + + HOSTADDR(RGF_DMA_EP_MISC_ICR) + + offsetof(struct RGF_ICR, IMC)); +} + +static void wil6210_unmask_irq_pseudo(struct wil6210_priv *wil) +{ + wil_dbg_IRQ(wil, "%s()\n", __func__); + + set_bit(wil_status_irqen, &wil->status); + + iowrite32(WIL6210_IRQ_PSEUDO_MASK, wil->csr + + HOSTADDR(RGF_DMA_PSEUDO_CAUSE_MASK_SW)); +} + +void wil6210_disable_irq(struct wil6210_priv *wil) +{ + wil_dbg_IRQ(wil, "%s()\n", __func__); + + wil6210_mask_irq_tx(wil); + wil6210_mask_irq_rx(wil); + wil6210_mask_irq_misc(wil); + wil6210_mask_irq_pseudo(wil); +} + +void wil6210_enable_irq(struct wil6210_priv *wil) +{ + wil_dbg_IRQ(wil, "%s()\n", __func__); + + iowrite32(WIL_ICR_ICC_VALUE, wil->csr + HOSTADDR(RGF_DMA_EP_RX_ICR) + + offsetof(struct RGF_ICR, ICC)); + iowrite32(WIL_ICR_ICC_VALUE, wil->csr + HOSTADDR(RGF_DMA_EP_TX_ICR) + + offsetof(struct RGF_ICR, ICC)); + iowrite32(WIL_ICR_ICC_VALUE, wil->csr + HOSTADDR(RGF_DMA_EP_MISC_ICR) + + offsetof(struct RGF_ICR, ICC)); + + wil6210_unmask_irq_pseudo(wil); + wil6210_unmask_irq_tx(wil); + wil6210_unmask_irq_rx(wil); + wil6210_unmask_irq_misc(wil); +} + +static irqreturn_t wil6210_irq_rx(int irq, void *cookie) +{ + struct wil6210_priv *wil = cookie; + u32 isr = wil_ioread32_and_clear(wil->csr + + HOSTADDR(RGF_DMA_EP_RX_ICR) + + offsetof(struct RGF_ICR, ICR)); + + wil_dbg_IRQ(wil, "ISR RX 0x%08x\n", isr); + + if (!isr) { + wil_err(wil, "spurious IRQ: RX\n"); + return IRQ_NONE; + } + + wil6210_mask_irq_rx(wil); + + if (isr & BIT_DMA_EP_RX_ICR_RX_DONE) { + wil_dbg_IRQ(wil, "RX done\n"); + isr &= ~BIT_DMA_EP_RX_ICR_RX_DONE; + wil_rx_handle(wil); + } + + if (isr) + wil_err(wil, "un-handled RX ISR bits 0x%08x\n", isr); + + wil6210_unmask_irq_rx(wil); + + return IRQ_HANDLED; +} + +static irqreturn_t wil6210_irq_tx(int irq, void *cookie) +{ + struct wil6210_priv *wil = cookie; + u32 isr = wil_ioread32_and_clear(wil->csr + + HOSTADDR(RGF_DMA_EP_TX_ICR) + + offsetof(struct RGF_ICR, ICR)); + + wil_dbg_IRQ(wil, "ISR TX 0x%08x\n", isr); + + if (!isr) { + wil_err(wil, "spurious IRQ: TX\n"); + return IRQ_NONE; + } + + wil6210_mask_irq_tx(wil); + + if (isr & BIT_DMA_EP_TX_ICR_TX_DONE) { + uint i; + wil_dbg_IRQ(wil, "TX done\n"); + isr &= ~BIT_DMA_EP_TX_ICR_TX_DONE; + for (i = 0; i < 24; i++) { + u32 mask = BIT_DMA_EP_TX_ICR_TX_DONE_N(i); + if (isr & mask) { + isr &= ~mask; + wil_dbg_IRQ(wil, "TX done(%i)\n", i); + wil_tx_complete(wil, i); + } + } + } + + if (isr) + wil_err(wil, "un-handled TX ISR bits 0x%08x\n", isr); + + wil6210_unmask_irq_tx(wil); + + return IRQ_HANDLED; +} + +static irqreturn_t wil6210_irq_misc(int irq, void *cookie) +{ + struct wil6210_priv *wil = cookie; + u32 isr = wil_ioread32_and_clear(wil->csr + + HOSTADDR(RGF_DMA_EP_MISC_ICR) + + offsetof(struct RGF_ICR, ICR)); + + wil_dbg_IRQ(wil, "ISR MISC 0x%08x\n", isr); + + if (!isr) { + wil_err(wil, "spurious IRQ: MISC\n"); + return IRQ_NONE; + } + + wil6210_mask_irq_misc(wil); + + if (isr & ISR_MISC_FW_READY) { + wil_dbg_IRQ(wil, "IRQ: FW ready\n"); + /** + * Actual FW ready indicated by the + * WMI_FW_READY_EVENTID + */ + isr &= ~ISR_MISC_FW_READY; + } + + wil->isr_misc = isr; + + if (isr) { + return IRQ_WAKE_THREAD; + } else { + wil6210_unmask_irq_misc(wil); + return IRQ_HANDLED; + } +} + +static irqreturn_t wil6210_irq_misc_thread(int irq, void *cookie) +{ + struct wil6210_priv *wil = cookie; + u32 isr = wil->isr_misc; + + wil_dbg_IRQ(wil, "Thread ISR MISC 0x%08x\n", isr); + + if (isr & ISR_MISC_MBOX_EVT) { + wil_dbg_IRQ(wil, "MBOX event\n"); + wmi_recv_cmd(wil); + isr &= ~ISR_MISC_MBOX_EVT; + } + + if (isr) + wil_err(wil, "un-handled MISC ISR bits 0x%08x\n", isr); + + wil->isr_misc = 0; + + wil6210_unmask_irq_misc(wil); + + return IRQ_HANDLED; +} + +/** + * thread IRQ handler + */ +static irqreturn_t wil6210_thread_irq(int irq, void *cookie) +{ + struct wil6210_priv *wil = cookie; + + wil_dbg_IRQ(wil, "Thread IRQ\n"); + /* Discover real IRQ cause */ + if (wil->isr_misc) + wil6210_irq_misc_thread(irq, cookie); + + wil6210_unmask_irq_pseudo(wil); + + return IRQ_HANDLED; +} + +/* DEBUG + * There is subtle bug in hardware that causes IRQ to raise when it should be + * masked. It is quite rare and hard to debug. + * + * Catch irq issue if it happens and print all I can. + */ +static int wil6210_debug_irq_mask(struct wil6210_priv *wil, u32 pseudo_cause) +{ + if (!test_bit(wil_status_irqen, &wil->status)) { + u32 icm_rx = wil_ioread32_and_clear(wil->csr + + HOSTADDR(RGF_DMA_EP_RX_ICR) + + offsetof(struct RGF_ICR, ICM)); + u32 icr_rx = wil_ioread32_and_clear(wil->csr + + HOSTADDR(RGF_DMA_EP_RX_ICR) + + offsetof(struct RGF_ICR, ICR)); + u32 imv_rx = ioread32(wil->csr + + HOSTADDR(RGF_DMA_EP_RX_ICR) + + offsetof(struct RGF_ICR, IMV)); + u32 icm_tx = wil_ioread32_and_clear(wil->csr + + HOSTADDR(RGF_DMA_EP_TX_ICR) + + offsetof(struct RGF_ICR, ICM)); + u32 icr_tx = wil_ioread32_and_clear(wil->csr + + HOSTADDR(RGF_DMA_EP_TX_ICR) + + offsetof(struct RGF_ICR, ICR)); + u32 imv_tx = ioread32(wil->csr + + HOSTADDR(RGF_DMA_EP_TX_ICR) + + offsetof(struct RGF_ICR, IMV)); + u32 icm_misc = wil_ioread32_and_clear(wil->csr + + HOSTADDR(RGF_DMA_EP_MISC_ICR) + + offsetof(struct RGF_ICR, ICM)); + u32 icr_misc = wil_ioread32_and_clear(wil->csr + + HOSTADDR(RGF_DMA_EP_MISC_ICR) + + offsetof(struct RGF_ICR, ICR)); + u32 imv_misc = ioread32(wil->csr + + HOSTADDR(RGF_DMA_EP_MISC_ICR) + + offsetof(struct RGF_ICR, IMV)); + wil_err(wil, "IRQ when it should be masked: pseudo 0x%08x\n" + "Rx icm:icr:imv 0x%08x 0x%08x 0x%08x\n" + "Tx icm:icr:imv 0x%08x 0x%08x 0x%08x\n" + "Misc icm:icr:imv 0x%08x 0x%08x 0x%08x\n", + pseudo_cause, + icm_rx, icr_rx, imv_rx, + icm_tx, icr_tx, imv_tx, + icm_misc, icr_misc, imv_misc); + + return -EINVAL; + } + + return 0; +} + +static irqreturn_t wil6210_hardirq(int irq, void *cookie) +{ + irqreturn_t rc = IRQ_HANDLED; + struct wil6210_priv *wil = cookie; + u32 pseudo_cause = ioread32(wil->csr + HOSTADDR(RGF_DMA_PSEUDO_CAUSE)); + + /** + * pseudo_cause is Clear-On-Read, no need to ACK + */ + if ((pseudo_cause == 0) || ((pseudo_cause & 0xff) == 0xff)) + return IRQ_NONE; + + /* FIXME: IRQ mask debug */ + if (wil6210_debug_irq_mask(wil, pseudo_cause)) + return IRQ_NONE; + + wil6210_mask_irq_pseudo(wil); + + /* Discover real IRQ cause + * There are 2 possible phases for every IRQ: + * - hard IRQ handler called right here + * - threaded handler called later + * + * Hard IRQ handler reads and clears ISR. + * + * If threaded handler requested, hard IRQ handler + * returns IRQ_WAKE_THREAD and saves ISR register value + * for the threaded handler use. + * + * voting for wake thread - need at least 1 vote + */ + if ((pseudo_cause & BIT_DMA_PSEUDO_CAUSE_RX) && + (wil6210_irq_rx(irq, cookie) == IRQ_WAKE_THREAD)) + rc = IRQ_WAKE_THREAD; + + if ((pseudo_cause & BIT_DMA_PSEUDO_CAUSE_TX) && + (wil6210_irq_tx(irq, cookie) == IRQ_WAKE_THREAD)) + rc = IRQ_WAKE_THREAD; + + if ((pseudo_cause & BIT_DMA_PSEUDO_CAUSE_MISC) && + (wil6210_irq_misc(irq, cookie) == IRQ_WAKE_THREAD)) + rc = IRQ_WAKE_THREAD; + + /* if thread is requested, it will unmask IRQ */ + if (rc != IRQ_WAKE_THREAD) + wil6210_unmask_irq_pseudo(wil); + + wil_dbg_IRQ(wil, "Hard IRQ 0x%08x\n", pseudo_cause); + + return rc; +} + +static int wil6210_request_3msi(struct wil6210_priv *wil, int irq) +{ + int rc; + /* + * IRQ's are in the following order: + * - Tx + * - Rx + * - Misc + */ + + rc = request_irq(irq, wil6210_irq_tx, IRQF_SHARED, + WIL_NAME"_tx", wil); + if (rc) + return rc; + + rc = request_irq(irq + 1, wil6210_irq_rx, IRQF_SHARED, + WIL_NAME"_rx", wil); + if (rc) + goto free0; + + rc = request_threaded_irq(irq + 2, wil6210_irq_misc, + wil6210_irq_misc_thread, + IRQF_SHARED, WIL_NAME"_misc", wil); + if (rc) + goto free1; + + return 0; + /* error branch */ +free1: + free_irq(irq + 1, wil); +free0: + free_irq(irq, wil); + + return rc; +} + +int wil6210_init_irq(struct wil6210_priv *wil, int irq) +{ + int rc; + if (wil->n_msi == 3) + rc = wil6210_request_3msi(wil, irq); + else + rc = request_threaded_irq(irq, wil6210_hardirq, + wil6210_thread_irq, + wil->n_msi ? 0 : IRQF_SHARED, + WIL_NAME, wil); + if (rc) + return rc; + + wil6210_enable_irq(wil); + + return 0; +} + +void wil6210_fini_irq(struct wil6210_priv *wil, int irq) +{ + wil6210_disable_irq(wil); + free_irq(irq, wil); + if (wil->n_msi == 3) { + free_irq(irq + 1, wil); + free_irq(irq + 2, wil); + } +} diff --git a/drivers/net/wireless/ath/wil6210/main.c b/drivers/net/wireless/ath/wil6210/main.c new file mode 100644 index 000000000000..95fcd361322b --- /dev/null +++ b/drivers/net/wireless/ath/wil6210/main.c @@ -0,0 +1,407 @@ +/* + * Copyright (c) 2012 Qualcomm Atheros, Inc. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include <linux/kernel.h> +#include <linux/netdevice.h> +#include <linux/sched.h> +#include <linux/ieee80211.h> +#include <linux/wireless.h> +#include <linux/slab.h> +#include <linux/moduleparam.h> +#include <linux/if_arp.h> + +#include "wil6210.h" + +/* + * Due to a hardware issue, + * one has to read/write to/from NIC in 32-bit chunks; + * regular memcpy_fromio and siblings will + * not work on 64-bit platform - it uses 64-bit transactions + * + * Force 32-bit transactions to enable NIC on 64-bit platforms + * + * To avoid byte swap on big endian host, __raw_{read|write}l + * should be used - {read|write}l would swap bytes to provide + * little endian on PCI value in host endianness. + */ +void wil_memcpy_fromio_32(void *dst, const volatile void __iomem *src, + size_t count) +{ + u32 *d = dst; + const volatile u32 __iomem *s = src; + + /* size_t is unsigned, if (count%4 != 0) it will wrap */ + for (count += 4; count > 4; count -= 4) + *d++ = __raw_readl(s++); +} + +void wil_memcpy_toio_32(volatile void __iomem *dst, const void *src, + size_t count) +{ + volatile u32 __iomem *d = dst; + const u32 *s = src; + + for (count += 4; count > 4; count -= 4) + __raw_writel(*s++, d++); +} + +static void _wil6210_disconnect(struct wil6210_priv *wil, void *bssid) +{ + uint i; + struct net_device *ndev = wil_to_ndev(wil); + struct wireless_dev *wdev = wil->wdev; + + wil_dbg(wil, "%s()\n", __func__); + + wil_link_off(wil); + clear_bit(wil_status_fwconnected, &wil->status); + + switch (wdev->sme_state) { + case CFG80211_SME_CONNECTED: + cfg80211_disconnected(ndev, WLAN_STATUS_UNSPECIFIED_FAILURE, + NULL, 0, GFP_KERNEL); + break; + case CFG80211_SME_CONNECTING: + cfg80211_connect_result(ndev, bssid, NULL, 0, NULL, 0, + WLAN_STATUS_UNSPECIFIED_FAILURE, + GFP_KERNEL); + break; + default: + ; + } + + for (i = 0; i < ARRAY_SIZE(wil->vring_tx); i++) + wil_vring_fini_tx(wil, i); +} + +static void wil_disconnect_worker(struct work_struct *work) +{ + struct wil6210_priv *wil = container_of(work, + struct wil6210_priv, disconnect_worker); + + _wil6210_disconnect(wil, NULL); +} + +static void wil_connect_timer_fn(ulong x) +{ + struct wil6210_priv *wil = (void *)x; + + wil_dbg(wil, "Connect timeout\n"); + + /* reschedule to thread context - disconnect won't + * run from atomic context + */ + schedule_work(&wil->disconnect_worker); +} + +int wil_priv_init(struct wil6210_priv *wil) +{ + wil_dbg(wil, "%s()\n", __func__); + + mutex_init(&wil->mutex); + mutex_init(&wil->wmi_mutex); + + init_completion(&wil->wmi_ready); + + wil->pending_connect_cid = -1; + setup_timer(&wil->connect_timer, wil_connect_timer_fn, (ulong)wil); + + INIT_WORK(&wil->wmi_connect_worker, wmi_connect_worker); + INIT_WORK(&wil->disconnect_worker, wil_disconnect_worker); + INIT_WORK(&wil->wmi_event_worker, wmi_event_worker); + + INIT_LIST_HEAD(&wil->pending_wmi_ev); + spin_lock_init(&wil->wmi_ev_lock); + + wil->wmi_wq = create_singlethread_workqueue(WIL_NAME"_wmi"); + if (!wil->wmi_wq) + return -EAGAIN; + + wil->wmi_wq_conn = create_singlethread_workqueue(WIL_NAME"_connect"); + if (!wil->wmi_wq_conn) { + destroy_workqueue(wil->wmi_wq); + return -EAGAIN; + } + + /* make shadow copy of registers that should not change on run time */ + wil_memcpy_fromio_32(&wil->mbox_ctl, wil->csr + HOST_MBOX, + sizeof(struct wil6210_mbox_ctl)); + wil_mbox_ring_le2cpus(&wil->mbox_ctl.rx); + wil_mbox_ring_le2cpus(&wil->mbox_ctl.tx); + + return 0; +} + +void wil6210_disconnect(struct wil6210_priv *wil, void *bssid) +{ + del_timer_sync(&wil->connect_timer); + _wil6210_disconnect(wil, bssid); +} + +void wil_priv_deinit(struct wil6210_priv *wil) +{ + cancel_work_sync(&wil->disconnect_worker); + wil6210_disconnect(wil, NULL); + wmi_event_flush(wil); + destroy_workqueue(wil->wmi_wq_conn); + destroy_workqueue(wil->wmi_wq); +} + +static void wil_target_reset(struct wil6210_priv *wil) +{ + wil_dbg(wil, "Resetting...\n"); + + /* register write */ +#define W(a, v) iowrite32(v, wil->csr + HOSTADDR(a)) + /* register set = read, OR, write */ +#define S(a, v) iowrite32(ioread32(wil->csr + HOSTADDR(a)) | v, \ + wil->csr + HOSTADDR(a)) + + /* hpal_perst_from_pad_src_n_mask */ + S(RGF_USER_CLKS_CTL_SW_RST_MASK_0, BIT(6)); + /* car_perst_rst_src_n_mask */ + S(RGF_USER_CLKS_CTL_SW_RST_MASK_0, BIT(7)); + + W(RGF_USER_MAC_CPU_0, BIT(1)); /* mac_cpu_man_rst */ + W(RGF_USER_USER_CPU_0, BIT(1)); /* user_cpu_man_rst */ + + msleep(100); + + W(RGF_USER_CLKS_CTL_SW_RST_VEC_2, 0xFE000000); + W(RGF_USER_CLKS_CTL_SW_RST_VEC_1, 0x0000003F); + W(RGF_USER_CLKS_CTL_SW_RST_VEC_3, 0x00000170); + W(RGF_USER_CLKS_CTL_SW_RST_VEC_0, 0xFFE7FC00); + + msleep(100); + + W(RGF_USER_CLKS_CTL_SW_RST_VEC_3, 0); + W(RGF_USER_CLKS_CTL_SW_RST_VEC_2, 0); + W(RGF_USER_CLKS_CTL_SW_RST_VEC_1, 0); + W(RGF_USER_CLKS_CTL_SW_RST_VEC_0, 0); + + W(RGF_USER_CLKS_CTL_SW_RST_VEC_3, 0x00000001); + W(RGF_USER_CLKS_CTL_SW_RST_VEC_2, 0x00000080); + W(RGF_USER_CLKS_CTL_SW_RST_VEC_0, 0); + + msleep(2000); + + W(RGF_USER_USER_CPU_0, BIT(0)); /* user_cpu_man_de_rst */ + + msleep(2000); + + wil_dbg(wil, "Reset completed\n"); + +#undef W +#undef S +} + +void wil_mbox_ring_le2cpus(struct wil6210_mbox_ring *r) +{ + le32_to_cpus(&r->base); + le16_to_cpus(&r->entry_size); + le16_to_cpus(&r->size); + le32_to_cpus(&r->tail); + le32_to_cpus(&r->head); +} + +static int wil_wait_for_fw_ready(struct wil6210_priv *wil) +{ + ulong to = msecs_to_jiffies(1000); + ulong left = wait_for_completion_timeout(&wil->wmi_ready, to); + if (0 == left) { + wil_err(wil, "Firmware not ready\n"); + return -ETIME; + } else { + wil_dbg(wil, "FW ready after %d ms\n", + jiffies_to_msecs(to-left)); + } + return 0; +} + +/* + * We reset all the structures, and we reset the UMAC. + * After calling this routine, you're expected to reload + * the firmware. + */ +int wil_reset(struct wil6210_priv *wil) +{ + int rc; + + cancel_work_sync(&wil->disconnect_worker); + wil6210_disconnect(wil, NULL); + + wmi_event_flush(wil); + + flush_workqueue(wil->wmi_wq); + flush_workqueue(wil->wmi_wq_conn); + + wil6210_disable_irq(wil); + wil->status = 0; + + /* TODO: put MAC in reset */ + wil_target_reset(wil); + + /* init after reset */ + wil->pending_connect_cid = -1; + INIT_COMPLETION(wil->wmi_ready); + + /* make shadow copy of registers that should not change on run time */ + wil_memcpy_fromio_32(&wil->mbox_ctl, wil->csr + HOST_MBOX, + sizeof(struct wil6210_mbox_ctl)); + wil_mbox_ring_le2cpus(&wil->mbox_ctl.rx); + wil_mbox_ring_le2cpus(&wil->mbox_ctl.tx); + + /* TODO: release MAC reset */ + wil6210_enable_irq(wil); + + /* we just started MAC, wait for FW ready */ + rc = wil_wait_for_fw_ready(wil); + + return rc; +} + + +void wil_link_on(struct wil6210_priv *wil) +{ + struct net_device *ndev = wil_to_ndev(wil); + + wil_dbg(wil, "%s()\n", __func__); + + netif_carrier_on(ndev); + netif_tx_wake_all_queues(ndev); +} + +void wil_link_off(struct wil6210_priv *wil) +{ + struct net_device *ndev = wil_to_ndev(wil); + + wil_dbg(wil, "%s()\n", __func__); + + netif_tx_stop_all_queues(ndev); + netif_carrier_off(ndev); +} + +static int __wil_up(struct wil6210_priv *wil) +{ + struct net_device *ndev = wil_to_ndev(wil); + struct wireless_dev *wdev = wil->wdev; + struct ieee80211_channel *channel = wdev->preset_chandef.chan; + int rc; + int bi; + u16 wmi_nettype = wil_iftype_nl2wmi(wdev->iftype); + + rc = wil_reset(wil); + if (rc) + return rc; + + /* FIXME Firmware works now in PBSS mode(ToDS=0, FromDS=0) */ + wmi_nettype = wil_iftype_nl2wmi(NL80211_IFTYPE_ADHOC); + switch (wdev->iftype) { + case NL80211_IFTYPE_STATION: + wil_dbg(wil, "type: STATION\n"); + bi = 0; + ndev->type = ARPHRD_ETHER; + break; + case NL80211_IFTYPE_AP: + wil_dbg(wil, "type: AP\n"); + bi = 100; + ndev->type = ARPHRD_ETHER; + break; + case NL80211_IFTYPE_P2P_CLIENT: + wil_dbg(wil, "type: P2P_CLIENT\n"); + bi = 0; + ndev->type = ARPHRD_ETHER; + break; + case NL80211_IFTYPE_P2P_GO: + wil_dbg(wil, "type: P2P_GO\n"); + bi = 100; + ndev->type = ARPHRD_ETHER; + break; + case NL80211_IFTYPE_MONITOR: + wil_dbg(wil, "type: Monitor\n"); + bi = 0; + ndev->type = ARPHRD_IEEE80211_RADIOTAP; + /* ARPHRD_IEEE80211 or ARPHRD_IEEE80211_RADIOTAP ? */ + break; + default: + return -EOPNOTSUPP; + } + + /* Apply profile in the following order: */ + /* SSID and channel for the AP */ + switch (wdev->iftype) { + case NL80211_IFTYPE_AP: + case NL80211_IFTYPE_P2P_GO: + if (wdev->ssid_len == 0) { + wil_err(wil, "SSID not set\n"); + return -EINVAL; + } + wmi_set_ssid(wil, wdev->ssid_len, wdev->ssid); + if (channel) + wmi_set_channel(wil, channel->hw_value); + break; + default: + ; + } + + /* MAC address - pre-requisite for other commands */ + wmi_set_mac_address(wil, ndev->dev_addr); + + /* Set up beaconing if required. */ + rc = wmi_set_bcon(wil, bi, wmi_nettype); + if (rc) + return rc; + + /* Rx VRING. After MAC and beacon */ + wil_rx_init(wil); + + return 0; +} + +int wil_up(struct wil6210_priv *wil) +{ + int rc; + + mutex_lock(&wil->mutex); + rc = __wil_up(wil); + mutex_unlock(&wil->mutex); + + return rc; +} + +static int __wil_down(struct wil6210_priv *wil) +{ + if (wil->scan_request) { + cfg80211_scan_done(wil->scan_request, true); + wil->scan_request = NULL; + } + + wil6210_disconnect(wil, NULL); + wil_rx_fini(wil); + + return 0; +} + +int wil_down(struct wil6210_priv *wil) +{ + int rc; + + mutex_lock(&wil->mutex); + rc = __wil_down(wil); + mutex_unlock(&wil->mutex); + + return rc; +} diff --git a/drivers/net/wireless/ath/wil6210/netdev.c b/drivers/net/wireless/ath/wil6210/netdev.c new file mode 100644 index 000000000000..3068b5cb53a7 --- /dev/null +++ b/drivers/net/wireless/ath/wil6210/netdev.c @@ -0,0 +1,157 @@ +/* + * Copyright (c) 2012 Qualcomm Atheros, Inc. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include <linux/module.h> +#include <linux/netdevice.h> +#include <linux/etherdevice.h> +#include <linux/slab.h> + +#include "wil6210.h" + +static int wil_open(struct net_device *ndev) +{ + struct wil6210_priv *wil = ndev_to_wil(ndev); + + return wil_up(wil); +} + +static int wil_stop(struct net_device *ndev) +{ + struct wil6210_priv *wil = ndev_to_wil(ndev); + + return wil_down(wil); +} + +/* + * AC to queue mapping + * + * AC_VO -> queue 3 + * AC_VI -> queue 2 + * AC_BE -> queue 1 + * AC_BK -> queue 0 + */ +static u16 wil_select_queue(struct net_device *ndev, struct sk_buff *skb) +{ + static const u16 wil_1d_to_queue[8] = { 1, 0, 0, 1, 2, 2, 3, 3 }; + struct wil6210_priv *wil = ndev_to_wil(ndev); + u16 rc; + + skb->priority = cfg80211_classify8021d(skb); + + rc = wil_1d_to_queue[skb->priority]; + + wil_dbg_TXRX(wil, "%s() %d -> %d\n", __func__, (int)skb->priority, + (int)rc); + + return rc; +} + +static const struct net_device_ops wil_netdev_ops = { + .ndo_open = wil_open, + .ndo_stop = wil_stop, + .ndo_start_xmit = wil_start_xmit, + .ndo_select_queue = wil_select_queue, + .ndo_set_mac_address = eth_mac_addr, + .ndo_validate_addr = eth_validate_addr, +}; + +void *wil_if_alloc(struct device *dev, void __iomem *csr) +{ + struct net_device *ndev; + struct wireless_dev *wdev; + struct wil6210_priv *wil; + struct ieee80211_channel *ch; + int rc = 0; + + wdev = wil_cfg80211_init(dev); + if (IS_ERR(wdev)) { + dev_err(dev, "wil_cfg80211_init failed\n"); + return wdev; + } + + wil = wdev_to_wil(wdev); + wil->csr = csr; + wil->wdev = wdev; + + rc = wil_priv_init(wil); + if (rc) { + dev_err(dev, "wil_priv_init failed\n"); + goto out_wdev; + } + + wdev->iftype = NL80211_IFTYPE_STATION; /* TODO */ + /* default monitor channel */ + ch = wdev->wiphy->bands[IEEE80211_BAND_60GHZ]->channels; + cfg80211_chandef_create(&wdev->preset_chandef, ch, NL80211_CHAN_NO_HT); + + ndev = alloc_netdev_mqs(0, "wlan%d", ether_setup, WIL6210_TX_QUEUES, 1); + if (!ndev) { + dev_err(dev, "alloc_netdev_mqs failed\n"); + rc = -ENOMEM; + goto out_priv; + } + + ndev->netdev_ops = &wil_netdev_ops; + ndev->ieee80211_ptr = wdev; + SET_NETDEV_DEV(ndev, wiphy_dev(wdev->wiphy)); + wdev->netdev = ndev; + + wil_link_off(wil); + + return wil; + + out_priv: + wil_priv_deinit(wil); + + out_wdev: + wil_wdev_free(wil); + + return ERR_PTR(rc); +} + +void wil_if_free(struct wil6210_priv *wil) +{ + struct net_device *ndev = wil_to_ndev(wil); + if (!ndev) + return; + + free_netdev(ndev); + wil_priv_deinit(wil); + wil_wdev_free(wil); +} + +int wil_if_add(struct wil6210_priv *wil) +{ + struct net_device *ndev = wil_to_ndev(wil); + int rc; + + rc = register_netdev(ndev); + if (rc < 0) { + dev_err(&ndev->dev, "Failed to register netdev: %d\n", rc); + return rc; + } + + wil_link_off(wil); + + return 0; +} + +void wil_if_remove(struct wil6210_priv *wil) +{ + struct net_device *ndev = wil_to_ndev(wil); + + unregister_netdev(ndev); +} diff --git a/drivers/net/wireless/ath/wil6210/pcie_bus.c b/drivers/net/wireless/ath/wil6210/pcie_bus.c new file mode 100644 index 000000000000..0fc83edd6bad --- /dev/null +++ b/drivers/net/wireless/ath/wil6210/pcie_bus.c @@ -0,0 +1,223 @@ +/* + * Copyright (c) 2012 Qualcomm Atheros, Inc. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/slab.h> +#include <linux/netdevice.h> +#include <linux/debugfs.h> +#include <linux/pci.h> +#include <linux/moduleparam.h> + +#include "wil6210.h" + +static int use_msi = 1; +module_param(use_msi, int, S_IRUGO); +MODULE_PARM_DESC(use_msi, + " Use MSI interrupt: " + "0 - don't, 1 - (default) - single, or 3"); + +/* Bus ops */ +static int wil_if_pcie_enable(struct wil6210_priv *wil) +{ + struct pci_dev *pdev = wil->pdev; + int rc; + + pci_set_master(pdev); + + /* + * how many MSI interrupts to request? + */ + switch (use_msi) { + case 3: + case 1: + case 0: + break; + default: + wil_err(wil, "Invalid use_msi=%d, default to 1\n", + use_msi); + use_msi = 1; + } + wil->n_msi = use_msi; + if (wil->n_msi) { + wil_dbg(wil, "Setup %d MSI interrupts\n", use_msi); + rc = pci_enable_msi_block(pdev, wil->n_msi); + if (rc && (wil->n_msi == 3)) { + wil_err(wil, "3 MSI mode failed, try 1 MSI\n"); + wil->n_msi = 1; + rc = pci_enable_msi_block(pdev, wil->n_msi); + } + if (rc) { + wil_err(wil, "pci_enable_msi failed, use INTx\n"); + wil->n_msi = 0; + } + } else { + wil_dbg(wil, "MSI interrupts disabled, use INTx\n"); + } + + rc = wil6210_init_irq(wil, pdev->irq); + if (rc) + goto stop_master; + + /* need reset here to obtain MAC */ + rc = wil_reset(wil); + if (rc) + goto release_irq; + + return 0; + + release_irq: + wil6210_fini_irq(wil, pdev->irq); + /* safe to call if no MSI */ + pci_disable_msi(pdev); + stop_master: + pci_clear_master(pdev); + return rc; +} + +static int wil_if_pcie_disable(struct wil6210_priv *wil) +{ + struct pci_dev *pdev = wil->pdev; + + pci_clear_master(pdev); + /* disable and release IRQ */ + wil6210_fini_irq(wil, pdev->irq); + /* safe to call if no MSI */ + pci_disable_msi(pdev); + /* TODO: disable HW */ + + return 0; +} + +static int wil_pcie_probe(struct pci_dev *pdev, const struct pci_device_id *id) +{ + struct wil6210_priv *wil; + struct device *dev = &pdev->dev; + void __iomem *csr; + int rc; + + /* check HW */ + dev_info(&pdev->dev, WIL_NAME " device found [%04x:%04x] (rev %x)\n", + (int)pdev->vendor, (int)pdev->device, (int)pdev->revision); + + if (pci_resource_len(pdev, 0) != WIL6210_MEM_SIZE) { + dev_err(&pdev->dev, "Not " WIL_NAME "? " + "BAR0 size is %lu while expecting %lu\n", + (ulong)pci_resource_len(pdev, 0), WIL6210_MEM_SIZE); + return -ENODEV; + } + + rc = pci_enable_device(pdev); + if (rc) { + dev_err(&pdev->dev, "pci_enable_device failed\n"); + return -ENODEV; + } + /* rollback to err_disable_pdev */ + + rc = pci_request_region(pdev, 0, WIL_NAME); + if (rc) { + dev_err(&pdev->dev, "pci_request_region failed\n"); + goto err_disable_pdev; + } + /* rollback to err_release_reg */ + + csr = pci_ioremap_bar(pdev, 0); + if (!csr) { + dev_err(&pdev->dev, "pci_ioremap_bar failed\n"); + rc = -ENODEV; + goto err_release_reg; + } + /* rollback to err_iounmap */ + dev_info(&pdev->dev, "CSR at %pR -> %p\n", &pdev->resource[0], csr); + + wil = wil_if_alloc(dev, csr); + if (IS_ERR(wil)) { + rc = (int)PTR_ERR(wil); + dev_err(dev, "wil_if_alloc failed: %d\n", rc); + goto err_iounmap; + } + /* rollback to if_free */ + + pci_set_drvdata(pdev, wil); + wil->pdev = pdev; + + /* FW should raise IRQ when ready */ + rc = wil_if_pcie_enable(wil); + if (rc) { + wil_err(wil, "Enable device failed\n"); + goto if_free; + } + /* rollback to bus_disable */ + + rc = wil_if_add(wil); + if (rc) { + wil_err(wil, "wil_if_add failed: %d\n", rc); + goto bus_disable; + } + + wil6210_debugfs_init(wil); + + /* check FW is alive */ + wmi_echo(wil); + + return 0; + + bus_disable: + wil_if_pcie_disable(wil); + if_free: + wil_if_free(wil); + err_iounmap: + pci_iounmap(pdev, csr); + err_release_reg: + pci_release_region(pdev, 0); + err_disable_pdev: + pci_disable_device(pdev); + + return rc; +} + +static void wil_pcie_remove(struct pci_dev *pdev) +{ + struct wil6210_priv *wil = pci_get_drvdata(pdev); + + wil6210_debugfs_remove(wil); + wil_if_pcie_disable(wil); + wil_if_remove(wil); + wil_if_free(wil); + pci_iounmap(pdev, wil->csr); + pci_release_region(pdev, 0); + pci_disable_device(pdev); + pci_set_drvdata(pdev, NULL); +} + +static DEFINE_PCI_DEVICE_TABLE(wil6210_pcie_ids) = { + { PCI_DEVICE(0x1ae9, 0x0301) }, + { /* end: all zeroes */ }, +}; +MODULE_DEVICE_TABLE(pci, wil6210_pcie_ids); + +static struct pci_driver wil6210_driver = { + .probe = wil_pcie_probe, + .remove = wil_pcie_remove, + .id_table = wil6210_pcie_ids, + .name = WIL_NAME, +}; + +module_pci_driver(wil6210_driver); + +MODULE_LICENSE("Dual BSD/GPL"); +MODULE_AUTHOR("Qualcomm Atheros <wil6210@qca.qualcomm.com>"); +MODULE_DESCRIPTION("Driver for 60g WiFi WIL6210 card"); diff --git a/drivers/net/wireless/ath/wil6210/txrx.c b/drivers/net/wireless/ath/wil6210/txrx.c new file mode 100644 index 000000000000..f29c294413cf --- /dev/null +++ b/drivers/net/wireless/ath/wil6210/txrx.c @@ -0,0 +1,871 @@ +/* + * Copyright (c) 2012 Qualcomm Atheros, Inc. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include <linux/kernel.h> +#include <linux/netdevice.h> +#include <linux/etherdevice.h> +#include <linux/hardirq.h> +#include <net/ieee80211_radiotap.h> +#include <linux/if_arp.h> +#include <linux/moduleparam.h> + +#include "wil6210.h" +#include "wmi.h" +#include "txrx.h" + +static bool rtap_include_phy_info; +module_param(rtap_include_phy_info, bool, S_IRUGO); +MODULE_PARM_DESC(rtap_include_phy_info, + " Include PHY info in the radiotap header, default - no"); + +static inline int wil_vring_is_empty(struct vring *vring) +{ + return vring->swhead == vring->swtail; +} + +static inline u32 wil_vring_next_tail(struct vring *vring) +{ + return (vring->swtail + 1) % vring->size; +} + +static inline void wil_vring_advance_head(struct vring *vring, int n) +{ + vring->swhead = (vring->swhead + n) % vring->size; +} + +static inline int wil_vring_is_full(struct vring *vring) +{ + return wil_vring_next_tail(vring) == vring->swhead; +} +/* + * Available space in Tx Vring + */ +static inline int wil_vring_avail_tx(struct vring *vring) +{ + u32 swhead = vring->swhead; + u32 swtail = vring->swtail; + int used = (vring->size + swhead - swtail) % vring->size; + + return vring->size - used - 1; +} + +static int wil_vring_alloc(struct wil6210_priv *wil, struct vring *vring) +{ + struct device *dev = wil_to_dev(wil); + size_t sz = vring->size * sizeof(vring->va[0]); + uint i; + + BUILD_BUG_ON(sizeof(vring->va[0]) != 32); + + vring->swhead = 0; + vring->swtail = 0; + vring->ctx = kzalloc(vring->size * sizeof(vring->ctx[0]), GFP_KERNEL); + if (!vring->ctx) { + wil_err(wil, "vring_alloc [%d] failed to alloc ctx mem\n", + vring->size); + vring->va = NULL; + return -ENOMEM; + } + /* + * vring->va should be aligned on its size rounded up to power of 2 + * This is granted by the dma_alloc_coherent + */ + vring->va = dma_alloc_coherent(dev, sz, &vring->pa, GFP_KERNEL); + if (!vring->va) { + wil_err(wil, "vring_alloc [%d] failed to alloc DMA mem\n", + vring->size); + kfree(vring->ctx); + vring->ctx = NULL; + return -ENOMEM; + } + /* initially, all descriptors are SW owned + * For Tx and Rx, ownership bit is at the same location, thus + * we can use any + */ + for (i = 0; i < vring->size; i++) { + volatile struct vring_tx_desc *d = &(vring->va[i].tx); + d->dma.status = TX_DMA_STATUS_DU; + } + + wil_dbg(wil, "vring[%d] 0x%p:0x%016llx 0x%p\n", vring->size, + vring->va, (unsigned long long)vring->pa, vring->ctx); + + return 0; +} + +static void wil_vring_free(struct wil6210_priv *wil, struct vring *vring, + int tx) +{ + struct device *dev = wil_to_dev(wil); + size_t sz = vring->size * sizeof(vring->va[0]); + + while (!wil_vring_is_empty(vring)) { + if (tx) { + volatile struct vring_tx_desc *d = + &vring->va[vring->swtail].tx; + dma_addr_t pa = d->dma.addr_low | + ((u64)d->dma.addr_high << 32); + struct sk_buff *skb = vring->ctx[vring->swtail]; + if (skb) { + dma_unmap_single(dev, pa, d->dma.length, + DMA_TO_DEVICE); + dev_kfree_skb_any(skb); + vring->ctx[vring->swtail] = NULL; + } else { + dma_unmap_page(dev, pa, d->dma.length, + DMA_TO_DEVICE); + } + vring->swtail = wil_vring_next_tail(vring); + } else { /* rx */ + volatile struct vring_rx_desc *d = + &vring->va[vring->swtail].rx; + dma_addr_t pa = d->dma.addr_low | + ((u64)d->dma.addr_high << 32); + struct sk_buff *skb = vring->ctx[vring->swhead]; + dma_unmap_single(dev, pa, d->dma.length, + DMA_FROM_DEVICE); + kfree_skb(skb); + wil_vring_advance_head(vring, 1); + } + } + dma_free_coherent(dev, sz, (void *)vring->va, vring->pa); + kfree(vring->ctx); + vring->pa = 0; + vring->va = NULL; + vring->ctx = NULL; +} + +/** + * Allocate one skb for Rx VRING + * + * Safe to call from IRQ + */ +static int wil_vring_alloc_skb(struct wil6210_priv *wil, struct vring *vring, + u32 i, int headroom) +{ + struct device *dev = wil_to_dev(wil); + unsigned int sz = RX_BUF_LEN; + volatile struct vring_rx_desc *d = &(vring->va[i].rx); + dma_addr_t pa; + + /* TODO align */ + struct sk_buff *skb = dev_alloc_skb(sz + headroom); + if (unlikely(!skb)) + return -ENOMEM; + + skb_reserve(skb, headroom); + skb_put(skb, sz); + + pa = dma_map_single(dev, skb->data, skb->len, DMA_FROM_DEVICE); + if (unlikely(dma_mapping_error(dev, pa))) { + kfree_skb(skb); + return -ENOMEM; + } + + d->dma.d0 = BIT(9) | RX_DMA_D0_CMD_DMA_IT; + d->dma.addr_low = lower_32_bits(pa); + d->dma.addr_high = (u16)upper_32_bits(pa); + /* ip_length don't care */ + /* b11 don't care */ + /* error don't care */ + d->dma.status = 0; /* BIT(0) should be 0 for HW_OWNED */ + d->dma.length = sz; + vring->ctx[i] = skb; + + return 0; +} + +/** + * Adds radiotap header + * + * Any error indicated as "Bad FCS" + * + * Vendor data for 04:ce:14-1 (Wilocity-1) consists of: + * - Rx descriptor: 32 bytes + * - Phy info + */ +static void wil_rx_add_radiotap_header(struct wil6210_priv *wil, + struct sk_buff *skb, + volatile struct vring_rx_desc *d) +{ + struct wireless_dev *wdev = wil->wdev; + struct wil6210_rtap { + struct ieee80211_radiotap_header rthdr; + /* fields should be in the order of bits in rthdr.it_present */ + /* flags */ + u8 flags; + /* channel */ + __le16 chnl_freq __aligned(2); + __le16 chnl_flags; + /* MCS */ + u8 mcs_present; + u8 mcs_flags; + u8 mcs_index; + } __packed; + struct wil6210_rtap_vendor { + struct wil6210_rtap rtap; + /* vendor */ + u8 vendor_oui[3] __aligned(2); + u8 vendor_ns; + __le16 vendor_skip; + u8 vendor_data[0]; + } __packed; + struct wil6210_rtap_vendor *rtap_vendor; + int rtap_len = sizeof(struct wil6210_rtap); + int phy_length = 0; /* phy info header size, bytes */ + static char phy_data[128]; + struct ieee80211_channel *ch = wdev->preset_chandef.chan; + + if (rtap_include_phy_info) { + rtap_len = sizeof(*rtap_vendor) + sizeof(*d); + /* calculate additional length */ + if (d->dma.status & RX_DMA_STATUS_PHY_INFO) { + /** + * PHY info starts from 8-byte boundary + * there are 8-byte lines, last line may be partially + * written (HW bug), thus FW configures for last line + * to be excessive. Driver skips this last line. + */ + int len = min_t(int, 8 + sizeof(phy_data), + wil_rxdesc_phy_length(d)); + if (len > 8) { + void *p = skb_tail_pointer(skb); + void *pa = PTR_ALIGN(p, 8); + if (skb_tailroom(skb) >= len + (pa - p)) { + phy_length = len - 8; + memcpy(phy_data, pa, phy_length); + } + } + } + rtap_len += phy_length; + } + + if (skb_headroom(skb) < rtap_len && + pskb_expand_head(skb, rtap_len, 0, GFP_ATOMIC)) { + wil_err(wil, "Unable to expand headrom to %d\n", rtap_len); + return; + } + + rtap_vendor = (void *)skb_push(skb, rtap_len); + memset(rtap_vendor, 0, rtap_len); + + rtap_vendor->rtap.rthdr.it_version = PKTHDR_RADIOTAP_VERSION; + rtap_vendor->rtap.rthdr.it_len = cpu_to_le16(rtap_len); + rtap_vendor->rtap.rthdr.it_present = cpu_to_le32( + (1 << IEEE80211_RADIOTAP_FLAGS) | + (1 << IEEE80211_RADIOTAP_CHANNEL) | + (1 << IEEE80211_RADIOTAP_MCS)); + if (d->dma.status & RX_DMA_STATUS_ERROR) + rtap_vendor->rtap.flags |= IEEE80211_RADIOTAP_F_BADFCS; + + rtap_vendor->rtap.chnl_freq = cpu_to_le16(ch ? ch->center_freq : 58320); + rtap_vendor->rtap.chnl_flags = cpu_to_le16(0); + + rtap_vendor->rtap.mcs_present = IEEE80211_RADIOTAP_MCS_HAVE_MCS; + rtap_vendor->rtap.mcs_flags = 0; + rtap_vendor->rtap.mcs_index = wil_rxdesc_mcs(d); + + if (rtap_include_phy_info) { + rtap_vendor->rtap.rthdr.it_present |= cpu_to_le32(1 << + IEEE80211_RADIOTAP_VENDOR_NAMESPACE); + /* OUI for Wilocity 04:ce:14 */ + rtap_vendor->vendor_oui[0] = 0x04; + rtap_vendor->vendor_oui[1] = 0xce; + rtap_vendor->vendor_oui[2] = 0x14; + rtap_vendor->vendor_ns = 1; + /* Rx descriptor + PHY data */ + rtap_vendor->vendor_skip = cpu_to_le16(sizeof(*d) + + phy_length); + memcpy(rtap_vendor->vendor_data, (void *)d, sizeof(*d)); + memcpy(rtap_vendor->vendor_data + sizeof(*d), phy_data, + phy_length); + } +} + +/* + * Fast swap in place between 2 registers + */ +static void wil_swap_u16(u16 *a, u16 *b) +{ + *a ^= *b; + *b ^= *a; + *a ^= *b; +} + +static void wil_swap_ethaddr(void *data) +{ + struct ethhdr *eth = data; + u16 *s = (u16 *)eth->h_source; + u16 *d = (u16 *)eth->h_dest; + + wil_swap_u16(s++, d++); + wil_swap_u16(s++, d++); + wil_swap_u16(s, d); +} + +/** + * reap 1 frame from @swhead + * + * Safe to call from IRQ + */ +static struct sk_buff *wil_vring_reap_rx(struct wil6210_priv *wil, + struct vring *vring) +{ + struct device *dev = wil_to_dev(wil); + struct net_device *ndev = wil_to_ndev(wil); + volatile struct vring_rx_desc *d; + struct sk_buff *skb; + dma_addr_t pa; + unsigned int sz = RX_BUF_LEN; + u8 ftype; + u8 ds_bits; + + if (wil_vring_is_empty(vring)) + return NULL; + + d = &(vring->va[vring->swhead].rx); + if (!(d->dma.status & RX_DMA_STATUS_DU)) { + /* it is not error, we just reached end of Rx done area */ + return NULL; + } + + pa = d->dma.addr_low | ((u64)d->dma.addr_high << 32); + skb = vring->ctx[vring->swhead]; + dma_unmap_single(dev, pa, sz, DMA_FROM_DEVICE); + skb_trim(skb, d->dma.length); + + wil->stats.last_mcs_rx = wil_rxdesc_mcs(d); + + /* use radiotap header only if required */ + if (ndev->type == ARPHRD_IEEE80211_RADIOTAP) + wil_rx_add_radiotap_header(wil, skb, d); + + wil_dbg_TXRX(wil, "Rx[%3d] : %d bytes\n", vring->swhead, d->dma.length); + wil_hex_dump_TXRX("Rx ", DUMP_PREFIX_NONE, 32, 4, + (const void *)d, sizeof(*d), false); + + wil_vring_advance_head(vring, 1); + + /* no extra checks if in sniffer mode */ + if (ndev->type != ARPHRD_ETHER) + return skb; + /* + * Non-data frames may be delivered through Rx DMA channel (ex: BAR) + * Driver should recognize it by frame type, that is found + * in Rx descriptor. If type is not data, it is 802.11 frame as is + */ + ftype = wil_rxdesc_ftype(d) << 2; + if (ftype != IEEE80211_FTYPE_DATA) { + wil_dbg_TXRX(wil, "Non-data frame ftype 0x%08x\n", ftype); + /* TODO: process it */ + kfree_skb(skb); + return NULL; + } + + if (skb->len < ETH_HLEN) { + wil_err(wil, "Short frame, len = %d\n", skb->len); + /* TODO: process it (i.e. BAR) */ + kfree_skb(skb); + return NULL; + } + + ds_bits = wil_rxdesc_ds_bits(d); + if (ds_bits == 1) { + /* + * HW bug - in ToDS mode, i.e. Rx on AP side, + * addresses get swapped + */ + wil_swap_ethaddr(skb->data); + } + + return skb; +} + +/** + * allocate and fill up to @count buffers in rx ring + * buffers posted at @swtail + */ +static int wil_rx_refill(struct wil6210_priv *wil, int count) +{ + struct net_device *ndev = wil_to_ndev(wil); + struct vring *v = &wil->vring_rx; + u32 next_tail; + int rc = 0; + int headroom = ndev->type == ARPHRD_IEEE80211_RADIOTAP ? + WIL6210_RTAP_SIZE : 0; + + for (; next_tail = wil_vring_next_tail(v), + (next_tail != v->swhead) && (count-- > 0); + v->swtail = next_tail) { + rc = wil_vring_alloc_skb(wil, v, v->swtail, headroom); + if (rc) { + wil_err(wil, "Error %d in wil_rx_refill[%d]\n", + rc, v->swtail); + break; + } + } + iowrite32(v->swtail, wil->csr + HOSTADDR(v->hwtail)); + + return rc; +} + +/* + * Pass Rx packet to the netif. Update statistics. + */ +static void wil_netif_rx_any(struct sk_buff *skb, struct net_device *ndev) +{ + int rc; + unsigned int len = skb->len; + + if (in_interrupt()) + rc = netif_rx(skb); + else + rc = netif_rx_ni(skb); + + if (likely(rc == NET_RX_SUCCESS)) { + ndev->stats.rx_packets++; + ndev->stats.rx_bytes += len; + + } else { + ndev->stats.rx_dropped++; + } +} + +/** + * Proceed all completed skb's from Rx VRING + * + * Safe to call from IRQ + */ +void wil_rx_handle(struct wil6210_priv *wil) +{ + struct net_device *ndev = wil_to_ndev(wil); + struct vring *v = &wil->vring_rx; + struct sk_buff *skb; + + if (!v->va) { + wil_err(wil, "Rx IRQ while Rx not yet initialized\n"); + return; + } + wil_dbg_TXRX(wil, "%s()\n", __func__); + while (NULL != (skb = wil_vring_reap_rx(wil, v))) { + wil_hex_dump_TXRX("Rx ", DUMP_PREFIX_OFFSET, 16, 1, + skb->data, skb_headlen(skb), false); + + skb_orphan(skb); + + if (wil->wdev->iftype == NL80211_IFTYPE_MONITOR) { + skb->dev = ndev; + skb_reset_mac_header(skb); + skb->ip_summed = CHECKSUM_UNNECESSARY; + skb->pkt_type = PACKET_OTHERHOST; + skb->protocol = htons(ETH_P_802_2); + + } else { + skb->protocol = eth_type_trans(skb, ndev); + } + + wil_netif_rx_any(skb, ndev); + } + wil_rx_refill(wil, v->size); +} + +int wil_rx_init(struct wil6210_priv *wil) +{ + struct net_device *ndev = wil_to_ndev(wil); + struct wireless_dev *wdev = wil->wdev; + struct vring *vring = &wil->vring_rx; + int rc; + struct wmi_cfg_rx_chain_cmd cmd = { + .action = WMI_RX_CHAIN_ADD, + .rx_sw_ring = { + .max_mpdu_size = cpu_to_le16(RX_BUF_LEN), + }, + .mid = 0, /* TODO - what is it? */ + .decap_trans_type = WMI_DECAP_TYPE_802_3, + }; + struct { + struct wil6210_mbox_hdr_wmi wmi; + struct wmi_cfg_rx_chain_done_event evt; + } __packed evt; + + vring->size = WIL6210_RX_RING_SIZE; + rc = wil_vring_alloc(wil, vring); + if (rc) + return rc; + + cmd.rx_sw_ring.ring_mem_base = cpu_to_le64(vring->pa); + cmd.rx_sw_ring.ring_size = cpu_to_le16(vring->size); + if (wdev->iftype == NL80211_IFTYPE_MONITOR) { + struct ieee80211_channel *ch = wdev->preset_chandef.chan; + + cmd.sniffer_cfg.mode = cpu_to_le32(WMI_SNIFFER_ON); + if (ch) + cmd.sniffer_cfg.channel = ch->hw_value - 1; + cmd.sniffer_cfg.phy_info_mode = + cpu_to_le32(ndev->type == ARPHRD_IEEE80211_RADIOTAP); + cmd.sniffer_cfg.phy_support = + cpu_to_le32((wil->monitor_flags & MONITOR_FLAG_CONTROL) + ? WMI_SNIFFER_CP : WMI_SNIFFER_DP); + } + /* typical time for secure PCP is 840ms */ + rc = wmi_call(wil, WMI_CFG_RX_CHAIN_CMDID, &cmd, sizeof(cmd), + WMI_CFG_RX_CHAIN_DONE_EVENTID, &evt, sizeof(evt), 2000); + if (rc) + goto err_free; + + vring->hwtail = le32_to_cpu(evt.evt.rx_ring_tail_ptr); + + wil_dbg(wil, "Rx init: status %d tail 0x%08x\n", + le32_to_cpu(evt.evt.status), vring->hwtail); + + rc = wil_rx_refill(wil, vring->size); + if (rc) + goto err_free; + + return 0; + err_free: + wil_vring_free(wil, vring, 0); + + return rc; +} + +void wil_rx_fini(struct wil6210_priv *wil) +{ + struct vring *vring = &wil->vring_rx; + + if (vring->va) { + int rc; + struct wmi_cfg_rx_chain_cmd cmd = { + .action = cpu_to_le32(WMI_RX_CHAIN_DEL), + .rx_sw_ring = { + .max_mpdu_size = cpu_to_le16(RX_BUF_LEN), + }, + }; + struct { + struct wil6210_mbox_hdr_wmi wmi; + struct wmi_cfg_rx_chain_done_event cfg; + } __packed wmi_rx_cfg_reply; + + rc = wmi_call(wil, WMI_CFG_RX_CHAIN_CMDID, &cmd, sizeof(cmd), + WMI_CFG_RX_CHAIN_DONE_EVENTID, + &wmi_rx_cfg_reply, sizeof(wmi_rx_cfg_reply), + 100); + wil_vring_free(wil, vring, 0); + } +} + +int wil_vring_init_tx(struct wil6210_priv *wil, int id, int size, + int cid, int tid) +{ + int rc; + struct wmi_vring_cfg_cmd cmd = { + .action = cpu_to_le32(WMI_VRING_CMD_ADD), + .vring_cfg = { + .tx_sw_ring = { + .max_mpdu_size = cpu_to_le16(TX_BUF_LEN), + }, + .ringid = id, + .cidxtid = (cid & 0xf) | ((tid & 0xf) << 4), + .encap_trans_type = WMI_VRING_ENC_TYPE_802_3, + .mac_ctrl = 0, + .to_resolution = 0, + .agg_max_wsize = 16, + .schd_params = { + .priority = cpu_to_le16(0), + .timeslot_us = cpu_to_le16(0xfff), + }, + }, + }; + struct { + struct wil6210_mbox_hdr_wmi wmi; + struct wmi_vring_cfg_done_event cmd; + } __packed reply; + struct vring *vring = &wil->vring_tx[id]; + + if (vring->va) { + wil_err(wil, "Tx ring [%d] already allocated\n", id); + rc = -EINVAL; + goto out; + } + + vring->size = size; + rc = wil_vring_alloc(wil, vring); + if (rc) + goto out; + + cmd.vring_cfg.tx_sw_ring.ring_mem_base = cpu_to_le64(vring->pa); + cmd.vring_cfg.tx_sw_ring.ring_size = cpu_to_le16(vring->size); + + rc = wmi_call(wil, WMI_VRING_CFG_CMDID, &cmd, sizeof(cmd), + WMI_VRING_CFG_DONE_EVENTID, &reply, sizeof(reply), 100); + if (rc) + goto out_free; + + if (reply.cmd.status != WMI_VRING_CFG_SUCCESS) { + wil_err(wil, "Tx config failed, status 0x%02x\n", + reply.cmd.status); + goto out_free; + } + vring->hwtail = le32_to_cpu(reply.cmd.tx_vring_tail_ptr); + + return 0; + out_free: + wil_vring_free(wil, vring, 1); + out: + + return rc; +} + +void wil_vring_fini_tx(struct wil6210_priv *wil, int id) +{ + struct vring *vring = &wil->vring_tx[id]; + + if (!vring->va) + return; + + wil_vring_free(wil, vring, 1); +} + +static struct vring *wil_find_tx_vring(struct wil6210_priv *wil, + struct sk_buff *skb) +{ + struct vring *v = &wil->vring_tx[0]; + + if (v->va) + return v; + + return NULL; +} + +static int wil_tx_desc_map(volatile struct vring_tx_desc *d, + dma_addr_t pa, u32 len) +{ + d->dma.addr_low = lower_32_bits(pa); + d->dma.addr_high = (u16)upper_32_bits(pa); + d->dma.ip_length = 0; + /* 0..6: mac_length; 7:ip_version 0-IP6 1-IP4*/ + d->dma.b11 = 0/*14 | BIT(7)*/; + d->dma.error = 0; + d->dma.status = 0; /* BIT(0) should be 0 for HW_OWNED */ + d->dma.length = len; + d->dma.d0 = 0; + d->mac.d[0] = 0; + d->mac.d[1] = 0; + d->mac.d[2] = 0; + d->mac.ucode_cmd = 0; + /* use dst index 0 */ + d->mac.d[1] |= BIT(MAC_CFG_DESC_TX_1_DST_INDEX_EN_POS) | + (0 << MAC_CFG_DESC_TX_1_DST_INDEX_POS); + /* translation type: 0 - bypass; 1 - 802.3; 2 - native wifi */ + d->mac.d[2] = BIT(MAC_CFG_DESC_TX_2_SNAP_HDR_INSERTION_EN_POS) | + (1 << MAC_CFG_DESC_TX_2_L2_TRANSLATION_TYPE_POS); + + return 0; +} + +static int wil_tx_vring(struct wil6210_priv *wil, struct vring *vring, + struct sk_buff *skb) +{ + struct device *dev = wil_to_dev(wil); + volatile struct vring_tx_desc *d; + u32 swhead = vring->swhead; + int avail = wil_vring_avail_tx(vring); + int nr_frags = skb_shinfo(skb)->nr_frags; + uint f; + int vring_index = vring - wil->vring_tx; + uint i = swhead; + dma_addr_t pa; + + wil_dbg_TXRX(wil, "%s()\n", __func__); + + if (avail < vring->size/8) + netif_tx_stop_all_queues(wil_to_ndev(wil)); + if (avail < 1 + nr_frags) { + wil_err(wil, "Tx ring full. No space for %d fragments\n", + 1 + nr_frags); + return -ENOMEM; + } + d = &(vring->va[i].tx); + + /* FIXME FW can accept only unicast frames for the peer */ + memcpy(skb->data, wil->dst_addr[vring_index], ETH_ALEN); + + pa = dma_map_single(dev, skb->data, + skb_headlen(skb), DMA_TO_DEVICE); + + wil_dbg_TXRX(wil, "Tx skb %d bytes %p -> %#08llx\n", skb_headlen(skb), + skb->data, (unsigned long long)pa); + wil_hex_dump_TXRX("Tx ", DUMP_PREFIX_OFFSET, 16, 1, + skb->data, skb_headlen(skb), false); + + if (unlikely(dma_mapping_error(dev, pa))) + return -EINVAL; + /* 1-st segment */ + wil_tx_desc_map(d, pa, skb_headlen(skb)); + d->mac.d[2] |= ((nr_frags + 1) << + MAC_CFG_DESC_TX_2_NUM_OF_DESCRIPTORS_POS); + /* middle segments */ + for (f = 0; f < nr_frags; f++) { + const struct skb_frag_struct *frag = + &skb_shinfo(skb)->frags[f]; + int len = skb_frag_size(frag); + i = (swhead + f + 1) % vring->size; + d = &(vring->va[i].tx); + pa = skb_frag_dma_map(dev, frag, 0, skb_frag_size(frag), + DMA_TO_DEVICE); + if (unlikely(dma_mapping_error(dev, pa))) + goto dma_error; + wil_tx_desc_map(d, pa, len); + vring->ctx[i] = NULL; + } + /* for the last seg only */ + d->dma.d0 |= BIT(DMA_CFG_DESC_TX_0_CMD_EOP_POS); + d->dma.d0 |= BIT(9); /* BUG: undocumented bit */ + d->dma.d0 |= BIT(DMA_CFG_DESC_TX_0_CMD_DMA_IT_POS); + d->dma.d0 |= (vring_index << DMA_CFG_DESC_TX_0_QID_POS); + + wil_hex_dump_TXRX("Tx ", DUMP_PREFIX_NONE, 32, 4, + (const void *)d, sizeof(*d), false); + + /* advance swhead */ + wil_vring_advance_head(vring, nr_frags + 1); + wil_dbg_TXRX(wil, "Tx swhead %d -> %d\n", swhead, vring->swhead); + iowrite32(vring->swhead, wil->csr + HOSTADDR(vring->hwtail)); + /* hold reference to skb + * to prevent skb release before accounting + * in case of immediate "tx done" + */ + vring->ctx[i] = skb_get(skb); + + return 0; + dma_error: + /* unmap what we have mapped */ + /* Note: increment @f to operate with positive index */ + for (f++; f > 0; f--) { + i = (swhead + f) % vring->size; + d = &(vring->va[i].tx); + d->dma.status = TX_DMA_STATUS_DU; + pa = d->dma.addr_low | ((u64)d->dma.addr_high << 32); + if (vring->ctx[i]) + dma_unmap_single(dev, pa, d->dma.length, DMA_TO_DEVICE); + else + dma_unmap_page(dev, pa, d->dma.length, DMA_TO_DEVICE); + } + + return -EINVAL; +} + + +netdev_tx_t wil_start_xmit(struct sk_buff *skb, struct net_device *ndev) +{ + struct wil6210_priv *wil = ndev_to_wil(ndev); + struct vring *vring; + int rc; + + wil_dbg_TXRX(wil, "%s()\n", __func__); + if (!test_bit(wil_status_fwready, &wil->status)) { + wil_err(wil, "FW not ready\n"); + goto drop; + } + if (!test_bit(wil_status_fwconnected, &wil->status)) { + wil_err(wil, "FW not connected\n"); + goto drop; + } + if (wil->wdev->iftype == NL80211_IFTYPE_MONITOR) { + wil_err(wil, "Xmit in monitor mode not supported\n"); + goto drop; + } + if (skb->protocol == cpu_to_be16(ETH_P_PAE)) { + rc = wmi_tx_eapol(wil, skb); + } else { + /* find vring */ + vring = wil_find_tx_vring(wil, skb); + if (!vring) { + wil_err(wil, "No Tx VRING available\n"); + goto drop; + } + /* set up vring entry */ + rc = wil_tx_vring(wil, vring, skb); + } + switch (rc) { + case 0: + ndev->stats.tx_packets++; + ndev->stats.tx_bytes += skb->len; + dev_kfree_skb_any(skb); + return NETDEV_TX_OK; + case -ENOMEM: + return NETDEV_TX_BUSY; + default: + ; /* goto drop; */ + break; + } + drop: + netif_tx_stop_all_queues(ndev); + ndev->stats.tx_dropped++; + dev_kfree_skb_any(skb); + + return NET_XMIT_DROP; +} + +/** + * Clean up transmitted skb's from the Tx VRING + * + * Safe to call from IRQ + */ +void wil_tx_complete(struct wil6210_priv *wil, int ringid) +{ + struct device *dev = wil_to_dev(wil); + struct vring *vring = &wil->vring_tx[ringid]; + + if (!vring->va) { + wil_err(wil, "Tx irq[%d]: vring not initialized\n", ringid); + return; + } + + wil_dbg_TXRX(wil, "%s(%d)\n", __func__, ringid); + + while (!wil_vring_is_empty(vring)) { + volatile struct vring_tx_desc *d = &vring->va[vring->swtail].tx; + dma_addr_t pa; + struct sk_buff *skb; + if (!(d->dma.status & TX_DMA_STATUS_DU)) + break; + + wil_dbg_TXRX(wil, + "Tx[%3d] : %d bytes, status 0x%02x err 0x%02x\n", + vring->swtail, d->dma.length, d->dma.status, + d->dma.error); + wil_hex_dump_TXRX("TxC ", DUMP_PREFIX_NONE, 32, 4, + (const void *)d, sizeof(*d), false); + + pa = d->dma.addr_low | ((u64)d->dma.addr_high << 32); + skb = vring->ctx[vring->swtail]; + if (skb) { + dma_unmap_single(dev, pa, d->dma.length, DMA_TO_DEVICE); + dev_kfree_skb_any(skb); + vring->ctx[vring->swtail] = NULL; + } else { + dma_unmap_page(dev, pa, d->dma.length, DMA_TO_DEVICE); + } + d->dma.addr_low = 0; + d->dma.addr_high = 0; + d->dma.length = 0; + d->dma.status = TX_DMA_STATUS_DU; + vring->swtail = wil_vring_next_tail(vring); + } + if (wil_vring_avail_tx(vring) > vring->size/4) + netif_tx_wake_all_queues(wil_to_ndev(wil)); +} diff --git a/drivers/net/wireless/ath/wil6210/txrx.h b/drivers/net/wireless/ath/wil6210/txrx.h new file mode 100644 index 000000000000..45a61f597c5c --- /dev/null +++ b/drivers/net/wireless/ath/wil6210/txrx.h @@ -0,0 +1,362 @@ +/* + * Copyright (c) 2012 Qualcomm Atheros, Inc. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef WIL6210_TXRX_H +#define WIL6210_TXRX_H + +#define BUF_SW_OWNED (1) +#define BUF_HW_OWNED (0) + +/* size of max. Rx packet */ +#define RX_BUF_LEN (2048) +#define TX_BUF_LEN (2048) +/* how many bytes to reserve for rtap header? */ +#define WIL6210_RTAP_SIZE (128) + +/* Tx/Rx path */ +/* + * Tx descriptor - MAC part + * [dword 0] + * bit 0.. 9 : lifetime_expiry_value:10 + * bit 10 : interrup_en:1 + * bit 11 : status_en:1 + * bit 12..13 : txss_override:2 + * bit 14 : timestamp_insertion:1 + * bit 15 : duration_preserve:1 + * bit 16..21 : reserved0:6 + * bit 22..26 : mcs_index:5 + * bit 27 : mcs_en:1 + * bit 28..29 : reserved1:2 + * bit 30 : reserved2:1 + * bit 31 : sn_preserved:1 + * [dword 1] + * bit 0.. 3 : pkt_mode:4 + * bit 4 : pkt_mode_en:1 + * bit 5.. 7 : reserved0:3 + * bit 8..13 : reserved1:6 + * bit 14 : reserved2:1 + * bit 15 : ack_policy_en:1 + * bit 16..19 : dst_index:4 + * bit 20 : dst_index_en:1 + * bit 21..22 : ack_policy:2 + * bit 23 : lifetime_en:1 + * bit 24..30 : max_retry:7 + * bit 31 : max_retry_en:1 + * [dword 2] + * bit 0.. 7 : num_of_descriptors:8 + * bit 8..17 : reserved:10 + * bit 18..19 : l2_translation_type:2 + * bit 20 : snap_hdr_insertion_en:1 + * bit 21 : vlan_removal_en:1 + * bit 22..31 : reserved0:10 + * [dword 3] + * bit 0.. 31: ucode_cmd:32 + */ +struct vring_tx_mac { + u32 d[3]; + u32 ucode_cmd; +} __packed; + +/* TX MAC Dword 0 */ +#define MAC_CFG_DESC_TX_0_LIFETIME_EXPIRY_VALUE_POS 0 +#define MAC_CFG_DESC_TX_0_LIFETIME_EXPIRY_VALUE_LEN 10 +#define MAC_CFG_DESC_TX_0_LIFETIME_EXPIRY_VALUE_MSK 0x3FF + +#define MAC_CFG_DESC_TX_0_INTERRUP_EN_POS 10 +#define MAC_CFG_DESC_TX_0_INTERRUP_EN_LEN 1 +#define MAC_CFG_DESC_TX_0_INTERRUP_EN_MSK 0x400 + +#define MAC_CFG_DESC_TX_0_STATUS_EN_POS 11 +#define MAC_CFG_DESC_TX_0_STATUS_EN_LEN 1 +#define MAC_CFG_DESC_TX_0_STATUS_EN_MSK 0x800 + +#define MAC_CFG_DESC_TX_0_TXSS_OVERRIDE_POS 12 +#define MAC_CFG_DESC_TX_0_TXSS_OVERRIDE_LEN 2 +#define MAC_CFG_DESC_TX_0_TXSS_OVERRIDE_MSK 0x3000 + +#define MAC_CFG_DESC_TX_0_TIMESTAMP_INSERTION_POS 14 +#define MAC_CFG_DESC_TX_0_TIMESTAMP_INSERTION_LEN 1 +#define MAC_CFG_DESC_TX_0_TIMESTAMP_INSERTION_MSK 0x4000 + +#define MAC_CFG_DESC_TX_0_DURATION_PRESERVE_POS 15 +#define MAC_CFG_DESC_TX_0_DURATION_PRESERVE_LEN 1 +#define MAC_CFG_DESC_TX_0_DURATION_PRESERVE_MSK 0x8000 + +#define MAC_CFG_DESC_TX_0_MCS_INDEX_POS 22 +#define MAC_CFG_DESC_TX_0_MCS_INDEX_LEN 5 +#define MAC_CFG_DESC_TX_0_MCS_INDEX_MSK 0x7C00000 + +#define MAC_CFG_DESC_TX_0_MCS_EN_POS 27 +#define MAC_CFG_DESC_TX_0_MCS_EN_LEN 1 +#define MAC_CFG_DESC_TX_0_MCS_EN_MSK 0x8000000 + +#define MAC_CFG_DESC_TX_0_SN_PRESERVED_POS 31 +#define MAC_CFG_DESC_TX_0_SN_PRESERVED_LEN 1 +#define MAC_CFG_DESC_TX_0_SN_PRESERVED_MSK 0x80000000 + +/* TX MAC Dword 1 */ +#define MAC_CFG_DESC_TX_1_PKT_MODE_POS 0 +#define MAC_CFG_DESC_TX_1_PKT_MODE_LEN 4 +#define MAC_CFG_DESC_TX_1_PKT_MODE_MSK 0xF + +#define MAC_CFG_DESC_TX_1_PKT_MODE_EN_POS 4 +#define MAC_CFG_DESC_TX_1_PKT_MODE_EN_LEN 1 +#define MAC_CFG_DESC_TX_1_PKT_MODE_EN_MSK 0x10 + +#define MAC_CFG_DESC_TX_1_ACK_POLICY_EN_POS 15 +#define MAC_CFG_DESC_TX_1_ACK_POLICY_EN_LEN 1 +#define MAC_CFG_DESC_TX_1_ACK_POLICY_EN_MSK 0x8000 + +#define MAC_CFG_DESC_TX_1_DST_INDEX_POS 16 +#define MAC_CFG_DESC_TX_1_DST_INDEX_LEN 4 +#define MAC_CFG_DESC_TX_1_DST_INDEX_MSK 0xF0000 + +#define MAC_CFG_DESC_TX_1_DST_INDEX_EN_POS 20 +#define MAC_CFG_DESC_TX_1_DST_INDEX_EN_LEN 1 +#define MAC_CFG_DESC_TX_1_DST_INDEX_EN_MSK 0x100000 + +#define MAC_CFG_DESC_TX_1_ACK_POLICY_POS 21 +#define MAC_CFG_DESC_TX_1_ACK_POLICY_LEN 2 +#define MAC_CFG_DESC_TX_1_ACK_POLICY_MSK 0x600000 + +#define MAC_CFG_DESC_TX_1_LIFETIME_EN_POS 23 +#define MAC_CFG_DESC_TX_1_LIFETIME_EN_LEN 1 +#define MAC_CFG_DESC_TX_1_LIFETIME_EN_MSK 0x800000 + +#define MAC_CFG_DESC_TX_1_MAX_RETRY_POS 24 +#define MAC_CFG_DESC_TX_1_MAX_RETRY_LEN 7 +#define MAC_CFG_DESC_TX_1_MAX_RETRY_MSK 0x7F000000 + +#define MAC_CFG_DESC_TX_1_MAX_RETRY_EN_POS 31 +#define MAC_CFG_DESC_TX_1_MAX_RETRY_EN_LEN 1 +#define MAC_CFG_DESC_TX_1_MAX_RETRY_EN_MSK 0x80000000 + +/* TX MAC Dword 2 */ +#define MAC_CFG_DESC_TX_2_NUM_OF_DESCRIPTORS_POS 0 +#define MAC_CFG_DESC_TX_2_NUM_OF_DESCRIPTORS_LEN 8 +#define MAC_CFG_DESC_TX_2_NUM_OF_DESCRIPTORS_MSK 0xFF + +#define MAC_CFG_DESC_TX_2_RESERVED_POS 8 +#define MAC_CFG_DESC_TX_2_RESERVED_LEN 10 +#define MAC_CFG_DESC_TX_2_RESERVED_MSK 0x3FF00 + +#define MAC_CFG_DESC_TX_2_L2_TRANSLATION_TYPE_POS 18 +#define MAC_CFG_DESC_TX_2_L2_TRANSLATION_TYPE_LEN 2 +#define MAC_CFG_DESC_TX_2_L2_TRANSLATION_TYPE_MSK 0xC0000 + +#define MAC_CFG_DESC_TX_2_SNAP_HDR_INSERTION_EN_POS 20 +#define MAC_CFG_DESC_TX_2_SNAP_HDR_INSERTION_EN_LEN 1 +#define MAC_CFG_DESC_TX_2_SNAP_HDR_INSERTION_EN_MSK 0x100000 + +#define MAC_CFG_DESC_TX_2_VLAN_REMOVAL_EN_POS 21 +#define MAC_CFG_DESC_TX_2_VLAN_REMOVAL_EN_LEN 1 +#define MAC_CFG_DESC_TX_2_VLAN_REMOVAL_EN_MSK 0x200000 + +/* TX MAC Dword 3 */ +#define MAC_CFG_DESC_TX_3_UCODE_CMD_POS 0 +#define MAC_CFG_DESC_TX_3_UCODE_CMD_LEN 32 +#define MAC_CFG_DESC_TX_3_UCODE_CMD_MSK 0xFFFFFFFF + +/* TX DMA Dword 0 */ +#define DMA_CFG_DESC_TX_0_L4_LENGTH_POS 0 +#define DMA_CFG_DESC_TX_0_L4_LENGTH_LEN 8 +#define DMA_CFG_DESC_TX_0_L4_LENGTH_MSK 0xFF + +#define DMA_CFG_DESC_TX_0_CMD_EOP_POS 8 +#define DMA_CFG_DESC_TX_0_CMD_EOP_LEN 1 +#define DMA_CFG_DESC_TX_0_CMD_EOP_MSK 0x100 + +#define DMA_CFG_DESC_TX_0_CMD_DMA_IT_POS 10 +#define DMA_CFG_DESC_TX_0_CMD_DMA_IT_LEN 1 +#define DMA_CFG_DESC_TX_0_CMD_DMA_IT_MSK 0x400 + +#define DMA_CFG_DESC_TX_0_SEGMENT_BUF_DETAILS_POS 11 +#define DMA_CFG_DESC_TX_0_SEGMENT_BUF_DETAILS_LEN 2 +#define DMA_CFG_DESC_TX_0_SEGMENT_BUF_DETAILS_MSK 0x1800 + +#define DMA_CFG_DESC_TX_0_TCP_SEG_EN_POS 13 +#define DMA_CFG_DESC_TX_0_TCP_SEG_EN_LEN 1 +#define DMA_CFG_DESC_TX_0_TCP_SEG_EN_MSK 0x2000 + +#define DMA_CFG_DESC_TX_0_IPV4_CHECKSUM_EN_POS 14 +#define DMA_CFG_DESC_TX_0_IPV4_CHECKSUM_EN_LEN 1 +#define DMA_CFG_DESC_TX_0_IPV4_CHECKSUM_EN_MSK 0x4000 + +#define DMA_CFG_DESC_TX_0_TCP_UDP_CHECKSUM_EN_POS 15 +#define DMA_CFG_DESC_TX_0_TCP_UDP_CHECKSUM_EN_LEN 1 +#define DMA_CFG_DESC_TX_0_TCP_UDP_CHECKSUM_EN_MSK 0x8000 + +#define DMA_CFG_DESC_TX_0_QID_POS 16 +#define DMA_CFG_DESC_TX_0_QID_LEN 5 +#define DMA_CFG_DESC_TX_0_QID_MSK 0x1F0000 + +#define DMA_CFG_DESC_TX_0_PSEUDO_HEADER_CALC_EN_POS 21 +#define DMA_CFG_DESC_TX_0_PSEUDO_HEADER_CALC_EN_LEN 1 +#define DMA_CFG_DESC_TX_0_PSEUDO_HEADER_CALC_EN_MSK 0x200000 + +#define DMA_CFG_DESC_TX_0_L4_TYPE_POS 30 +#define DMA_CFG_DESC_TX_0_L4_TYPE_LEN 2 +#define DMA_CFG_DESC_TX_0_L4_TYPE_MSK 0xC0000000 + + +#define TX_DMA_STATUS_DU BIT(0) + +struct vring_tx_dma { + u32 d0; + u32 addr_low; + u16 addr_high; + u8 ip_length; + u8 b11; /* 0..6: mac_length; 7:ip_version */ + u8 error; /* 0..2: err; 3..7: reserved; */ + u8 status; /* 0: used; 1..7; reserved */ + u16 length; +} __packed; + +/* + * Rx descriptor - MAC part + * [dword 0] + * bit 0.. 3 : tid:4 The QoS (b3-0) TID Field + * bit 4.. 6 : connection_id:3 :The Source index that was found during + * Parsing the TA. This field is used to define the source of the packet + * bit 7 : reserved:1 + * bit 8.. 9 : mac_id:2 : The MAC virtual Ring number (always zero) + * bit 10..11 : frame_type:2 : The FC Control (b3-2) - MPDU Type + * (management, data, control and extension) + * bit 12..15 : frame_subtype:4 : The FC Control (b7-4) - Frame Subtype + * bit 16..27 : seq_number:12 The received Sequence number field + * bit 28..31 : extended:4 extended subtype + * [dword 1] + * bit 0.. 3 : reserved + * bit 4.. 5 : key_id:2 + * bit 6 : decrypt_bypass:1 + * bit 7 : security:1 + * bit 8.. 9 : ds_bits:2 + * bit 10 : a_msdu_present:1 from qos header + * bit 11 : a_msdu_type:1 from qos header + * bit 12 : a_mpdu:1 part of AMPDU aggregation + * bit 13 : broadcast:1 + * bit 14 : mutlicast:1 + * bit 15 : reserved:1 + * bit 16..20 : rx_mac_qid:5 The Queue Identifier that the packet + * is received from + * bit 21..24 : mcs:4 + * bit 25..28 : mic_icr:4 + * bit 29..31 : reserved:3 + * [dword 2] + * bit 0.. 2 : time_slot:3 The timeslot that the MPDU is received + * bit 3 : fc_protocol_ver:1 The FC Control (b0) - Protocol Version + * bit 4 : fc_order:1 The FC Control (b15) -Order + * bit 5.. 7 : qos_ack_policy:3 The QoS (b6-5) ack policy Field + * bit 8 : esop:1 The QoS (b4) ESOP field + * bit 9 : qos_rdg_more_ppdu:1 The QoS (b9) RDG field + * bit 10..14 : qos_reserved:5 The QoS (b14-10) Reserved field + * bit 15 : qos_ac_constraint:1 + * bit 16..31 : pn_15_0:16 low 2 bytes of PN + * [dword 3] + * bit 0..31 : pn_47_16:32 high 4 bytes of PN + */ +struct vring_rx_mac { + u32 d0; + u32 d1; + u16 w4; + u16 pn_15_0; + u32 pn_47_16; +} __packed; + +/* + * Rx descriptor - DMA part + * [dword 0] + * bit 0.. 7 : l4_length:8 layer 4 length + * bit 8.. 9 : reserved:2 + * bit 10 : cmd_dma_it:1 + * bit 11..15 : reserved:5 + * bit 16..29 : phy_info_length:14 + * bit 30..31 : l4_type:2 valid if the L4I bit is set in the status field + * [dword 1] + * bit 0..31 : addr_low:32 The payload buffer low address + * [dword 2] + * bit 0..15 : addr_high:16 The payload buffer high address + * bit 16..23 : ip_length:8 + * bit 24..30 : mac_length:7 + * bit 31 : ip_version:1 + * [dword 3] + * [byte 12] error + * [byte 13] status + * bit 0 : du:1 + * bit 1 : eop:1 + * bit 2 : error:1 + * bit 3 : mi:1 + * bit 4 : l3_identified:1 + * bit 5 : l4_identified:1 + * bit 6 : phy_info_included:1 + * bit 7 : reserved:1 + * [word 7] length + * + */ + +#define RX_DMA_D0_CMD_DMA_IT BIT(10) + +#define RX_DMA_STATUS_DU BIT(0) +#define RX_DMA_STATUS_ERROR BIT(2) +#define RX_DMA_STATUS_PHY_INFO BIT(6) + +struct vring_rx_dma { + u32 d0; + u32 addr_low; + u16 addr_high; + u8 ip_length; + u8 b11; + u8 error; + u8 status; + u16 length; +} __packed; + +struct vring_tx_desc { + struct vring_tx_mac mac; + struct vring_tx_dma dma; +} __packed; + +struct vring_rx_desc { + struct vring_rx_mac mac; + struct vring_rx_dma dma; +} __packed; + +union vring_desc { + struct vring_tx_desc tx; + struct vring_rx_desc rx; +} __packed; + +static inline int wil_rxdesc_phy_length(volatile struct vring_rx_desc *d) +{ + return WIL_GET_BITS(d->dma.d0, 16, 29); +} + +static inline int wil_rxdesc_mcs(volatile struct vring_rx_desc *d) +{ + return WIL_GET_BITS(d->mac.d1, 21, 24); +} + +static inline int wil_rxdesc_ds_bits(volatile struct vring_rx_desc *d) +{ + return WIL_GET_BITS(d->mac.d1, 8, 9); +} + +static inline int wil_rxdesc_ftype(volatile struct vring_rx_desc *d) +{ + return WIL_GET_BITS(d->mac.d0, 10, 11); +} + +#endif /* WIL6210_TXRX_H */ diff --git a/drivers/net/wireless/ath/wil6210/wil6210.h b/drivers/net/wireless/ath/wil6210/wil6210.h new file mode 100644 index 000000000000..9bcfffa4006c --- /dev/null +++ b/drivers/net/wireless/ath/wil6210/wil6210.h @@ -0,0 +1,363 @@ +/* + * Copyright (c) 2012 Qualcomm Atheros, Inc. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef __WIL6210_H__ +#define __WIL6210_H__ + +#include <linux/netdevice.h> +#include <linux/wireless.h> +#include <net/cfg80211.h> + +#include "dbg_hexdump.h" + +#define WIL_NAME "wil6210" + +/** + * extract bits [@b0:@b1] (inclusive) from the value @x + * it should be @b0 <= @b1, or result is incorrect + */ +static inline u32 WIL_GET_BITS(u32 x, int b0, int b1) +{ + return (x >> b0) & ((1 << (b1 - b0 + 1)) - 1); +} + +#define WIL6210_MEM_SIZE (2*1024*1024UL) + +#define WIL6210_TX_QUEUES (4) + +#define WIL6210_RX_RING_SIZE (128) +#define WIL6210_TX_RING_SIZE (128) +#define WIL6210_MAX_TX_RINGS (24) + +/* Hardware definitions begin */ + +/* + * Mapping + * RGF File | Host addr | FW addr + * | | + * user_rgf | 0x000000 | 0x880000 + * dma_rgf | 0x001000 | 0x881000 + * pcie_rgf | 0x002000 | 0x882000 + * | | + */ + +/* Where various structures placed in host address space */ +#define WIL6210_FW_HOST_OFF (0x880000UL) + +#define HOSTADDR(fwaddr) (fwaddr - WIL6210_FW_HOST_OFF) + +/* + * Interrupt control registers block + * + * each interrupt controlled by the same bit in all registers + */ +struct RGF_ICR { + u32 ICC; /* Cause Control, RW: 0 - W1C, 1 - COR */ + u32 ICR; /* Cause, W1C/COR depending on ICC */ + u32 ICM; /* Cause masked (ICR & ~IMV), W1C/COR depending on ICC */ + u32 ICS; /* Cause Set, WO */ + u32 IMV; /* Mask, RW+S/C */ + u32 IMS; /* Mask Set, write 1 to set */ + u32 IMC; /* Mask Clear, write 1 to clear */ +} __packed; + +/* registers - FW addresses */ +#define RGF_USER_USER_SCRATCH_PAD (0x8802bc) +#define RGF_USER_USER_ICR (0x880b4c) /* struct RGF_ICR */ + #define BIT_USER_USER_ICR_SW_INT_2 BIT(18) +#define RGF_USER_CLKS_CTL_SW_RST_MASK_0 (0x880b14) +#define RGF_USER_MAC_CPU_0 (0x8801fc) +#define RGF_USER_USER_CPU_0 (0x8801e0) +#define RGF_USER_CLKS_CTL_SW_RST_VEC_0 (0x880b04) +#define RGF_USER_CLKS_CTL_SW_RST_VEC_1 (0x880b08) +#define RGF_USER_CLKS_CTL_SW_RST_VEC_2 (0x880b0c) +#define RGF_USER_CLKS_CTL_SW_RST_VEC_3 (0x880b10) + +#define RGF_DMA_PSEUDO_CAUSE (0x881c68) +#define RGF_DMA_PSEUDO_CAUSE_MASK_SW (0x881c6c) +#define RGF_DMA_PSEUDO_CAUSE_MASK_FW (0x881c70) + #define BIT_DMA_PSEUDO_CAUSE_RX BIT(0) + #define BIT_DMA_PSEUDO_CAUSE_TX BIT(1) + #define BIT_DMA_PSEUDO_CAUSE_MISC BIT(2) + +#define RGF_DMA_EP_TX_ICR (0x881bb4) /* struct RGF_ICR */ + #define BIT_DMA_EP_TX_ICR_TX_DONE BIT(0) + #define BIT_DMA_EP_TX_ICR_TX_DONE_N(n) BIT(n+1) /* n = [0..23] */ +#define RGF_DMA_EP_RX_ICR (0x881bd0) /* struct RGF_ICR */ + #define BIT_DMA_EP_RX_ICR_RX_DONE BIT(0) +#define RGF_DMA_EP_MISC_ICR (0x881bec) /* struct RGF_ICR */ + #define BIT_DMA_EP_MISC_ICR_RX_HTRSH BIT(0) + #define BIT_DMA_EP_MISC_ICR_TX_NO_ACT BIT(1) + #define BIT_DMA_EP_MISC_ICR_FW_INT0 BIT(28) + #define BIT_DMA_EP_MISC_ICR_FW_INT1 BIT(29) + +/* Interrupt moderation control */ +#define RGF_DMA_ITR_CNT_TRSH (0x881c5c) +#define RGF_DMA_ITR_CNT_DATA (0x881c60) +#define RGF_DMA_ITR_CNT_CRL (0x881C64) + #define BIT_DMA_ITR_CNT_CRL_EN BIT(0) + #define BIT_DMA_ITR_CNT_CRL_EXT_TICK BIT(1) + #define BIT_DMA_ITR_CNT_CRL_FOREVER BIT(2) + #define BIT_DMA_ITR_CNT_CRL_CLR BIT(3) + #define BIT_DMA_ITR_CNT_CRL_REACH_TRSH BIT(4) + +/* popular locations */ +#define HOST_MBOX HOSTADDR(RGF_USER_USER_SCRATCH_PAD) +#define HOST_SW_INT (HOSTADDR(RGF_USER_USER_ICR) + \ + offsetof(struct RGF_ICR, ICS)) +#define SW_INT_MBOX BIT_USER_USER_ICR_SW_INT_2 + +/* ISR register bits */ +#define ISR_MISC_FW_READY BIT_DMA_EP_MISC_ICR_FW_INT0 +#define ISR_MISC_MBOX_EVT BIT_DMA_EP_MISC_ICR_FW_INT1 + +/* Hardware definitions end */ + +struct wil6210_mbox_ring { + u32 base; + u16 entry_size; /* max. size of mbox entry, incl. all headers */ + u16 size; + u32 tail; + u32 head; +} __packed; + +struct wil6210_mbox_ring_desc { + __le32 sync; + __le32 addr; +} __packed; + +/* at HOST_OFF_WIL6210_MBOX_CTL */ +struct wil6210_mbox_ctl { + struct wil6210_mbox_ring tx; + struct wil6210_mbox_ring rx; +} __packed; + +struct wil6210_mbox_hdr { + __le16 seq; + __le16 len; /* payload, bytes after this header */ + __le16 type; + u8 flags; + u8 reserved; +} __packed; + +#define WIL_MBOX_HDR_TYPE_WMI (0) + +/* max. value for wil6210_mbox_hdr.len */ +#define MAX_MBOXITEM_SIZE (240) + +struct wil6210_mbox_hdr_wmi { + u8 reserved0[2]; + __le16 id; + __le16 info1; /* bits [0..3] - device_id, rest - unused */ + u8 reserved1[2]; +} __packed; + +struct pending_wmi_event { + struct list_head list; + struct { + struct wil6210_mbox_hdr hdr; + struct wil6210_mbox_hdr_wmi wmi; + u8 data[0]; + } __packed event; +}; + +union vring_desc; + +struct vring { + dma_addr_t pa; + volatile union vring_desc *va; /* vring_desc[size], WriteBack by DMA */ + u16 size; /* number of vring_desc elements */ + u32 swtail; + u32 swhead; + u32 hwtail; /* write here to inform hw */ + void **ctx; /* void *ctx[size] - software context */ +}; + +enum { /* for wil6210_priv.status */ + wil_status_fwready = 0, + wil_status_fwconnected, + wil_status_dontscan, + wil_status_irqen, /* FIXME: interrupts enabled - for debug */ +}; + +struct pci_dev; + +struct wil6210_stats { + u64 tsf; + u32 snr; + u16 last_mcs_rx; + u16 bf_mcs; /* last BF, used for Tx */ + u16 my_rx_sector; + u16 my_tx_sector; + u16 peer_rx_sector; + u16 peer_tx_sector; +}; + +struct wil6210_priv { + struct pci_dev *pdev; + int n_msi; + struct wireless_dev *wdev; + void __iomem *csr; + ulong status; + /* profile */ + u32 monitor_flags; + u32 secure_pcp; /* create secure PCP? */ + int sinfo_gen; + /* cached ISR registers */ + u32 isr_misc; + /* mailbox related */ + struct mutex wmi_mutex; + struct wil6210_mbox_ctl mbox_ctl; + struct completion wmi_ready; + u16 wmi_seq; + u16 reply_id; /**< wait for this WMI event */ + void *reply_buf; + u16 reply_size; + struct workqueue_struct *wmi_wq; /* for deferred calls */ + struct work_struct wmi_event_worker; + struct workqueue_struct *wmi_wq_conn; /* for connect worker */ + struct work_struct wmi_connect_worker; + struct work_struct disconnect_worker; + struct timer_list connect_timer; + int pending_connect_cid; + struct list_head pending_wmi_ev; + /* + * protect pending_wmi_ev + * - fill in IRQ from wil6210_irq_misc, + * - consumed in thread by wmi_event_worker + */ + spinlock_t wmi_ev_lock; + /* DMA related */ + struct vring vring_rx; + struct vring vring_tx[WIL6210_MAX_TX_RINGS]; + u8 dst_addr[WIL6210_MAX_TX_RINGS][ETH_ALEN]; + /* scan */ + struct cfg80211_scan_request *scan_request; + + struct mutex mutex; /* for wil6210_priv access in wil_{up|down} */ + /* statistics */ + struct wil6210_stats stats; + /* debugfs */ + struct dentry *debug; + struct debugfs_blob_wrapper fw_code_blob; + struct debugfs_blob_wrapper fw_data_blob; + struct debugfs_blob_wrapper fw_peri_blob; + struct debugfs_blob_wrapper uc_code_blob; + struct debugfs_blob_wrapper uc_data_blob; + struct debugfs_blob_wrapper rgf_blob; +}; + +#define wil_to_wiphy(i) (i->wdev->wiphy) +#define wil_to_dev(i) (wiphy_dev(wil_to_wiphy(i))) +#define wiphy_to_wil(w) (struct wil6210_priv *)(wiphy_priv(w)) +#define wil_to_wdev(i) (i->wdev) +#define wdev_to_wil(w) (struct wil6210_priv *)(wdev_priv(w)) +#define wil_to_ndev(i) (wil_to_wdev(i)->netdev) +#define ndev_to_wil(n) (wdev_to_wil(n->ieee80211_ptr)) + +#define wil_dbg(wil, fmt, arg...) netdev_dbg(wil_to_ndev(wil), fmt, ##arg) +#define wil_info(wil, fmt, arg...) netdev_info(wil_to_ndev(wil), fmt, ##arg) +#define wil_err(wil, fmt, arg...) netdev_err(wil_to_ndev(wil), fmt, ##arg) + +#define wil_dbg_IRQ(wil, fmt, arg...) wil_dbg(wil, "DBG[ IRQ]" fmt, ##arg) +#define wil_dbg_TXRX(wil, fmt, arg...) wil_dbg(wil, "DBG[TXRX]" fmt, ##arg) +#define wil_dbg_WMI(wil, fmt, arg...) wil_dbg(wil, "DBG[ WMI]" fmt, ##arg) + +#define wil_hex_dump_TXRX(prefix_str, prefix_type, rowsize, \ + groupsize, buf, len, ascii) \ + wil_print_hex_dump_debug("DBG[TXRX]" prefix_str,\ + prefix_type, rowsize, \ + groupsize, buf, len, ascii) + +#define wil_hex_dump_WMI(prefix_str, prefix_type, rowsize, \ + groupsize, buf, len, ascii) \ + wil_print_hex_dump_debug("DBG[ WMI]" prefix_str,\ + prefix_type, rowsize, \ + groupsize, buf, len, ascii) + +void wil_memcpy_fromio_32(void *dst, const volatile void __iomem *src, + size_t count); +void wil_memcpy_toio_32(volatile void __iomem *dst, const void *src, + size_t count); + +void *wil_if_alloc(struct device *dev, void __iomem *csr); +void wil_if_free(struct wil6210_priv *wil); +int wil_if_add(struct wil6210_priv *wil); +void wil_if_remove(struct wil6210_priv *wil); +int wil_priv_init(struct wil6210_priv *wil); +void wil_priv_deinit(struct wil6210_priv *wil); +int wil_reset(struct wil6210_priv *wil); +void wil_link_on(struct wil6210_priv *wil); +void wil_link_off(struct wil6210_priv *wil); +int wil_up(struct wil6210_priv *wil); +int wil_down(struct wil6210_priv *wil); +void wil_mbox_ring_le2cpus(struct wil6210_mbox_ring *r); + +void __iomem *wmi_buffer(struct wil6210_priv *wil, __le32 ptr); +void __iomem *wmi_addr(struct wil6210_priv *wil, u32 ptr); +int wmi_read_hdr(struct wil6210_priv *wil, __le32 ptr, + struct wil6210_mbox_hdr *hdr); +int wmi_send(struct wil6210_priv *wil, u16 cmdid, void *buf, u16 len); +void wmi_recv_cmd(struct wil6210_priv *wil); +int wmi_call(struct wil6210_priv *wil, u16 cmdid, void *buf, u16 len, + u16 reply_id, void *reply, u8 reply_size, int to_msec); +void wmi_connect_worker(struct work_struct *work); +void wmi_event_worker(struct work_struct *work); +void wmi_event_flush(struct wil6210_priv *wil); +int wmi_set_ssid(struct wil6210_priv *wil, u8 ssid_len, const void *ssid); +int wmi_get_ssid(struct wil6210_priv *wil, u8 *ssid_len, void *ssid); +int wmi_set_channel(struct wil6210_priv *wil, int channel); +int wmi_get_channel(struct wil6210_priv *wil, int *channel); +int wmi_tx_eapol(struct wil6210_priv *wil, struct sk_buff *skb); +int wmi_del_cipher_key(struct wil6210_priv *wil, u8 key_index, + const void *mac_addr); +int wmi_add_cipher_key(struct wil6210_priv *wil, u8 key_index, + const void *mac_addr, int key_len, const void *key); +int wmi_echo(struct wil6210_priv *wil); +int wmi_set_ie(struct wil6210_priv *wil, u8 type, u16 ie_len, const void *ie); + +int wil6210_init_irq(struct wil6210_priv *wil, int irq); +void wil6210_fini_irq(struct wil6210_priv *wil, int irq); +void wil6210_disable_irq(struct wil6210_priv *wil); +void wil6210_enable_irq(struct wil6210_priv *wil); + +int wil6210_debugfs_init(struct wil6210_priv *wil); +void wil6210_debugfs_remove(struct wil6210_priv *wil); + +struct wireless_dev *wil_cfg80211_init(struct device *dev); +void wil_wdev_free(struct wil6210_priv *wil); + +int wmi_set_mac_address(struct wil6210_priv *wil, void *addr); +int wmi_set_bcon(struct wil6210_priv *wil, int bi, u8 wmi_nettype); +void wil6210_disconnect(struct wil6210_priv *wil, void *bssid); + +int wil_rx_init(struct wil6210_priv *wil); +void wil_rx_fini(struct wil6210_priv *wil); + +/* TX API */ +int wil_vring_init_tx(struct wil6210_priv *wil, int id, int size, + int cid, int tid); +void wil_vring_fini_tx(struct wil6210_priv *wil, int id); + +netdev_tx_t wil_start_xmit(struct sk_buff *skb, struct net_device *ndev); +void wil_tx_complete(struct wil6210_priv *wil, int ringid); + +/* RX API */ +void wil_rx_handle(struct wil6210_priv *wil); + +int wil_iftype_nl2wmi(enum nl80211_iftype type); + +#endif /* __WIL6210_H__ */ diff --git a/drivers/net/wireless/ath/wil6210/wmi.c b/drivers/net/wireless/ath/wil6210/wmi.c new file mode 100644 index 000000000000..12915f6e7617 --- /dev/null +++ b/drivers/net/wireless/ath/wil6210/wmi.c @@ -0,0 +1,975 @@ +/* + * Copyright (c) 2012 Qualcomm Atheros, Inc. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include <linux/pci.h> +#include <linux/io.h> +#include <linux/list.h> +#include <linux/etherdevice.h> + +#include "wil6210.h" +#include "wmi.h" + +/** + * WMI event receiving - theory of operations + * + * When firmware about to report WMI event, it fills memory area + * in the mailbox and raises misc. IRQ. Thread interrupt handler invoked for + * the misc IRQ, function @wmi_recv_cmd called by thread IRQ handler. + * + * @wmi_recv_cmd reads event, allocates memory chunk and attaches it to the + * event list @wil->pending_wmi_ev. Then, work queue @wil->wmi_wq wakes up + * and handles events within the @wmi_event_worker. Every event get detached + * from list, processed and deleted. + * + * Purpose for this mechanism is to release IRQ thread; otherwise, + * if WMI event handling involves another WMI command flow, this 2-nd flow + * won't be completed because of blocked IRQ thread. + */ + +/** + * Addressing - theory of operations + * + * There are several buses present on the WIL6210 card. + * Same memory areas are visible at different address on + * the different busses. There are 3 main bus masters: + * - MAC CPU (ucode) + * - User CPU (firmware) + * - AHB (host) + * + * On the PCI bus, there is one BAR (BAR0) of 2Mb size, exposing + * AHB addresses starting from 0x880000 + * + * Internally, firmware uses addresses that allows faster access but + * are invisible from the host. To read from these addresses, alternative + * AHB address must be used. + * + * Memory mapping + * Linker address PCI/Host address + * 0x880000 .. 0xa80000 2Mb BAR0 + * 0x800000 .. 0x807000 0x900000 .. 0x907000 28k DCCM + * 0x840000 .. 0x857000 0x908000 .. 0x91f000 92k PERIPH + */ + +/** + * @fw_mapping provides memory remapping table + */ +static const struct { + u32 from; /* linker address - from, inclusive */ + u32 to; /* linker address - to, exclusive */ + u32 host; /* PCI/Host address - BAR0 + 0x880000 */ +} fw_mapping[] = { + {0x000000, 0x040000, 0x8c0000}, /* FW code RAM 256k */ + {0x800000, 0x808000, 0x900000}, /* FW data RAM 32k */ + {0x840000, 0x860000, 0x908000}, /* peripheral data RAM 128k/96k used */ + {0x880000, 0x88a000, 0x880000}, /* various RGF */ + {0x8c0000, 0x932000, 0x8c0000}, /* trivial mapping for upper area */ + /* + * 920000..930000 ucode code RAM + * 930000..932000 ucode data RAM + */ +}; + +/** + * return AHB address for given firmware/ucode internal (linker) address + * @x - internal address + * If address have no valid AHB mapping, return 0 + */ +static u32 wmi_addr_remap(u32 x) +{ + uint i; + + for (i = 0; i < ARRAY_SIZE(fw_mapping); i++) { + if ((x >= fw_mapping[i].from) && (x < fw_mapping[i].to)) + return x + fw_mapping[i].host - fw_mapping[i].from; + } + + return 0; +} + +/** + * Check address validity for WMI buffer; remap if needed + * @ptr - internal (linker) fw/ucode address + * + * Valid buffer should be DWORD aligned + * + * return address for accessing buffer from the host; + * if buffer is not valid, return NULL. + */ +void __iomem *wmi_buffer(struct wil6210_priv *wil, __le32 ptr_) +{ + u32 off; + u32 ptr = le32_to_cpu(ptr_); + + if (ptr % 4) + return NULL; + + ptr = wmi_addr_remap(ptr); + if (ptr < WIL6210_FW_HOST_OFF) + return NULL; + + off = HOSTADDR(ptr); + if (off > WIL6210_MEM_SIZE - 4) + return NULL; + + return wil->csr + off; +} + +/** + * Check address validity + */ +void __iomem *wmi_addr(struct wil6210_priv *wil, u32 ptr) +{ + u32 off; + + if (ptr % 4) + return NULL; + + if (ptr < WIL6210_FW_HOST_OFF) + return NULL; + + off = HOSTADDR(ptr); + if (off > WIL6210_MEM_SIZE - 4) + return NULL; + + return wil->csr + off; +} + +int wmi_read_hdr(struct wil6210_priv *wil, __le32 ptr, + struct wil6210_mbox_hdr *hdr) +{ + void __iomem *src = wmi_buffer(wil, ptr); + if (!src) + return -EINVAL; + + wil_memcpy_fromio_32(hdr, src, sizeof(*hdr)); + + return 0; +} + +static int __wmi_send(struct wil6210_priv *wil, u16 cmdid, void *buf, u16 len) +{ + struct { + struct wil6210_mbox_hdr hdr; + struct wil6210_mbox_hdr_wmi wmi; + } __packed cmd = { + .hdr = { + .type = WIL_MBOX_HDR_TYPE_WMI, + .flags = 0, + .len = cpu_to_le16(sizeof(cmd.wmi) + len), + }, + .wmi = { + .id = cpu_to_le16(cmdid), + .info1 = 0, + }, + }; + struct wil6210_mbox_ring *r = &wil->mbox_ctl.tx; + struct wil6210_mbox_ring_desc d_head; + u32 next_head; + void __iomem *dst; + void __iomem *head = wmi_addr(wil, r->head); + uint retry; + + if (sizeof(cmd) + len > r->entry_size) { + wil_err(wil, "WMI size too large: %d bytes, max is %d\n", + (int)(sizeof(cmd) + len), r->entry_size); + return -ERANGE; + + } + + might_sleep(); + + if (!test_bit(wil_status_fwready, &wil->status)) { + wil_err(wil, "FW not ready\n"); + return -EAGAIN; + } + + if (!head) { + wil_err(wil, "WMI head is garbage: 0x%08x\n", r->head); + return -EINVAL; + } + /* read Tx head till it is not busy */ + for (retry = 5; retry > 0; retry--) { + wil_memcpy_fromio_32(&d_head, head, sizeof(d_head)); + if (d_head.sync == 0) + break; + msleep(20); + } + if (d_head.sync != 0) { + wil_err(wil, "WMI head busy\n"); + return -EBUSY; + } + /* next head */ + next_head = r->base + ((r->head - r->base + sizeof(d_head)) % r->size); + wil_dbg_WMI(wil, "Head 0x%08x -> 0x%08x\n", r->head, next_head); + /* wait till FW finish with previous command */ + for (retry = 5; retry > 0; retry--) { + r->tail = ioread32(wil->csr + HOST_MBOX + + offsetof(struct wil6210_mbox_ctl, tx.tail)); + if (next_head != r->tail) + break; + msleep(20); + } + if (next_head == r->tail) { + wil_err(wil, "WMI ring full\n"); + return -EBUSY; + } + dst = wmi_buffer(wil, d_head.addr); + if (!dst) { + wil_err(wil, "invalid WMI buffer: 0x%08x\n", + le32_to_cpu(d_head.addr)); + return -EINVAL; + } + cmd.hdr.seq = cpu_to_le16(++wil->wmi_seq); + /* set command */ + wil_dbg_WMI(wil, "WMI command 0x%04x [%d]\n", cmdid, len); + wil_hex_dump_WMI("Cmd ", DUMP_PREFIX_OFFSET, 16, 1, &cmd, + sizeof(cmd), true); + wil_hex_dump_WMI("cmd ", DUMP_PREFIX_OFFSET, 16, 1, buf, + len, true); + wil_memcpy_toio_32(dst, &cmd, sizeof(cmd)); + wil_memcpy_toio_32(dst + sizeof(cmd), buf, len); + /* mark entry as full */ + iowrite32(1, wil->csr + HOSTADDR(r->head) + + offsetof(struct wil6210_mbox_ring_desc, sync)); + /* advance next ptr */ + iowrite32(r->head = next_head, wil->csr + HOST_MBOX + + offsetof(struct wil6210_mbox_ctl, tx.head)); + + /* interrupt to FW */ + iowrite32(SW_INT_MBOX, wil->csr + HOST_SW_INT); + + return 0; +} + +int wmi_send(struct wil6210_priv *wil, u16 cmdid, void *buf, u16 len) +{ + int rc; + + mutex_lock(&wil->wmi_mutex); + rc = __wmi_send(wil, cmdid, buf, len); + mutex_unlock(&wil->wmi_mutex); + + return rc; +} + +/*=== Event handlers ===*/ +static void wmi_evt_ready(struct wil6210_priv *wil, int id, void *d, int len) +{ + struct net_device *ndev = wil_to_ndev(wil); + struct wireless_dev *wdev = wil->wdev; + struct wmi_ready_event *evt = d; + u32 ver = le32_to_cpu(evt->sw_version); + + wil_dbg_WMI(wil, "FW ver. %d; MAC %pM\n", ver, evt->mac); + + if (!is_valid_ether_addr(ndev->dev_addr)) { + memcpy(ndev->dev_addr, evt->mac, ETH_ALEN); + memcpy(ndev->perm_addr, evt->mac, ETH_ALEN); + } + snprintf(wdev->wiphy->fw_version, sizeof(wdev->wiphy->fw_version), + "%d", ver); +} + +static void wmi_evt_fw_ready(struct wil6210_priv *wil, int id, void *d, + int len) +{ + wil_dbg_WMI(wil, "WMI: FW ready\n"); + + set_bit(wil_status_fwready, &wil->status); + /* reuse wmi_ready for the firmware ready indication */ + complete(&wil->wmi_ready); +} + +static void wmi_evt_rx_mgmt(struct wil6210_priv *wil, int id, void *d, int len) +{ + struct wmi_rx_mgmt_packet_event *data = d; + struct wiphy *wiphy = wil_to_wiphy(wil); + struct ieee80211_mgmt *rx_mgmt_frame = + (struct ieee80211_mgmt *)data->payload; + int ch_no = data->info.channel+1; + u32 freq = ieee80211_channel_to_frequency(ch_no, + IEEE80211_BAND_60GHZ); + struct ieee80211_channel *channel = ieee80211_get_channel(wiphy, freq); + /* TODO convert LE to CPU */ + s32 signal = 0; /* TODO */ + __le16 fc = rx_mgmt_frame->frame_control; + u32 d_len = le32_to_cpu(data->info.len); + u16 d_status = le16_to_cpu(data->info.status); + + wil_dbg_WMI(wil, "MGMT: channel %d MCS %d SNR %d\n", + data->info.channel, data->info.mcs, data->info.snr); + wil_dbg_WMI(wil, "status 0x%04x len %d stype %04x\n", d_status, d_len, + le16_to_cpu(data->info.stype)); + wil_dbg_WMI(wil, "qid %d mid %d cid %d\n", + data->info.qid, data->info.mid, data->info.cid); + + if (!channel) { + wil_err(wil, "Frame on unsupported channel\n"); + return; + } + + if (ieee80211_is_beacon(fc) || ieee80211_is_probe_resp(fc)) { + struct cfg80211_bss *bss; + u64 tsf = le64_to_cpu(rx_mgmt_frame->u.beacon.timestamp); + u16 cap = le16_to_cpu(rx_mgmt_frame->u.beacon.capab_info); + u16 bi = le16_to_cpu(rx_mgmt_frame->u.beacon.beacon_int); + const u8 *ie_buf = rx_mgmt_frame->u.beacon.variable; + size_t ie_len = d_len - offsetof(struct ieee80211_mgmt, + u.beacon.variable); + wil_dbg_WMI(wil, "Capability info : 0x%04x\n", cap); + + bss = cfg80211_inform_bss(wiphy, channel, rx_mgmt_frame->bssid, + tsf, cap, bi, ie_buf, ie_len, + signal, GFP_KERNEL); + if (bss) { + wil_dbg_WMI(wil, "Added BSS %pM\n", + rx_mgmt_frame->bssid); + cfg80211_put_bss(bss); + } else { + wil_err(wil, "cfg80211_inform_bss() failed\n"); + } + } +} + +static void wmi_evt_scan_complete(struct wil6210_priv *wil, int id, + void *d, int len) +{ + if (wil->scan_request) { + struct wmi_scan_complete_event *data = d; + bool aborted = (data->status != 0); + + wil_dbg_WMI(wil, "SCAN_COMPLETE(0x%08x)\n", data->status); + cfg80211_scan_done(wil->scan_request, aborted); + wil->scan_request = NULL; + } else { + wil_err(wil, "SCAN_COMPLETE while not scanning\n"); + } +} + +static void wmi_evt_connect(struct wil6210_priv *wil, int id, void *d, int len) +{ + struct net_device *ndev = wil_to_ndev(wil); + struct wireless_dev *wdev = wil->wdev; + struct wmi_connect_event *evt = d; + int ch; /* channel number */ + struct station_info sinfo; + u8 *assoc_req_ie, *assoc_resp_ie; + size_t assoc_req_ielen, assoc_resp_ielen; + /* capinfo(u16) + listen_interval(u16) + IEs */ + const size_t assoc_req_ie_offset = sizeof(u16) * 2; + /* capinfo(u16) + status_code(u16) + associd(u16) + IEs */ + const size_t assoc_resp_ie_offset = sizeof(u16) * 3; + + if (len < sizeof(*evt)) { + wil_err(wil, "Connect event too short : %d bytes\n", len); + return; + } + if (len != sizeof(*evt) + evt->beacon_ie_len + evt->assoc_req_len + + evt->assoc_resp_len) { + wil_err(wil, + "Connect event corrupted : %d != %d + %d + %d + %d\n", + len, (int)sizeof(*evt), evt->beacon_ie_len, + evt->assoc_req_len, evt->assoc_resp_len); + return; + } + ch = evt->channel + 1; + wil_dbg_WMI(wil, "Connect %pM channel [%d] cid %d\n", + evt->bssid, ch, evt->cid); + wil_hex_dump_WMI("connect AI : ", DUMP_PREFIX_OFFSET, 16, 1, + evt->assoc_info, len - sizeof(*evt), true); + + /* figure out IE's */ + assoc_req_ie = &evt->assoc_info[evt->beacon_ie_len + + assoc_req_ie_offset]; + assoc_req_ielen = evt->assoc_req_len - assoc_req_ie_offset; + if (evt->assoc_req_len <= assoc_req_ie_offset) { + assoc_req_ie = NULL; + assoc_req_ielen = 0; + } + + assoc_resp_ie = &evt->assoc_info[evt->beacon_ie_len + + evt->assoc_req_len + + assoc_resp_ie_offset]; + assoc_resp_ielen = evt->assoc_resp_len - assoc_resp_ie_offset; + if (evt->assoc_resp_len <= assoc_resp_ie_offset) { + assoc_resp_ie = NULL; + assoc_resp_ielen = 0; + } + + if ((wdev->iftype == NL80211_IFTYPE_STATION) || + (wdev->iftype == NL80211_IFTYPE_P2P_CLIENT)) { + if (wdev->sme_state != CFG80211_SME_CONNECTING) { + wil_err(wil, "Not in connecting state\n"); + return; + } + del_timer_sync(&wil->connect_timer); + cfg80211_connect_result(ndev, evt->bssid, + assoc_req_ie, assoc_req_ielen, + assoc_resp_ie, assoc_resp_ielen, + WLAN_STATUS_SUCCESS, GFP_KERNEL); + + } else if ((wdev->iftype == NL80211_IFTYPE_AP) || + (wdev->iftype == NL80211_IFTYPE_P2P_GO)) { + memset(&sinfo, 0, sizeof(sinfo)); + + sinfo.generation = wil->sinfo_gen++; + + if (assoc_req_ie) { + sinfo.assoc_req_ies = assoc_req_ie; + sinfo.assoc_req_ies_len = assoc_req_ielen; + sinfo.filled |= STATION_INFO_ASSOC_REQ_IES; + } + + cfg80211_new_sta(ndev, evt->bssid, &sinfo, GFP_KERNEL); + } + set_bit(wil_status_fwconnected, &wil->status); + + /* FIXME FW can transmit only ucast frames to peer */ + /* FIXME real ring_id instead of hard coded 0 */ + memcpy(wil->dst_addr[0], evt->bssid, ETH_ALEN); + + wil->pending_connect_cid = evt->cid; + queue_work(wil->wmi_wq_conn, &wil->wmi_connect_worker); +} + +static void wmi_evt_disconnect(struct wil6210_priv *wil, int id, + void *d, int len) +{ + struct wmi_disconnect_event *evt = d; + + wil_dbg_WMI(wil, "Disconnect %pM reason %d proto %d wmi\n", + evt->bssid, + evt->protocol_reason_status, evt->disconnect_reason); + + wil->sinfo_gen++; + + wil6210_disconnect(wil, evt->bssid); + clear_bit(wil_status_dontscan, &wil->status); +} + +static void wmi_evt_notify(struct wil6210_priv *wil, int id, void *d, int len) +{ + struct wmi_notify_req_done_event *evt = d; + + if (len < sizeof(*evt)) { + wil_err(wil, "Short NOTIFY event\n"); + return; + } + + wil->stats.tsf = le64_to_cpu(evt->tsf); + wil->stats.snr = le32_to_cpu(evt->snr_val); + wil->stats.bf_mcs = le16_to_cpu(evt->bf_mcs); + wil->stats.my_rx_sector = le16_to_cpu(evt->my_rx_sector); + wil->stats.my_tx_sector = le16_to_cpu(evt->my_tx_sector); + wil->stats.peer_rx_sector = le16_to_cpu(evt->other_rx_sector); + wil->stats.peer_tx_sector = le16_to_cpu(evt->other_tx_sector); + wil_dbg_WMI(wil, "Link status, MCS %d TSF 0x%016llx\n" + "BF status 0x%08x SNR 0x%08x\n" + "Tx Tpt %d goodput %d Rx goodput %d\n" + "Sectors(rx:tx) my %d:%d peer %d:%d\n", + wil->stats.bf_mcs, wil->stats.tsf, evt->status, + wil->stats.snr, le32_to_cpu(evt->tx_tpt), + le32_to_cpu(evt->tx_goodput), le32_to_cpu(evt->rx_goodput), + wil->stats.my_rx_sector, wil->stats.my_tx_sector, + wil->stats.peer_rx_sector, wil->stats.peer_tx_sector); +} + +/* + * Firmware reports EAPOL frame using WME event. + * Reconstruct Ethernet frame and deliver it via normal Rx + */ +static void wmi_evt_eapol_rx(struct wil6210_priv *wil, int id, + void *d, int len) +{ + struct net_device *ndev = wil_to_ndev(wil); + struct wmi_eapol_rx_event *evt = d; + u16 eapol_len = le16_to_cpu(evt->eapol_len); + int sz = eapol_len + ETH_HLEN; + struct sk_buff *skb; + struct ethhdr *eth; + + wil_dbg_WMI(wil, "EAPOL len %d from %pM\n", eapol_len, + evt->src_mac); + + if (eapol_len > 196) { /* TODO: revisit size limit */ + wil_err(wil, "EAPOL too large\n"); + return; + } + + skb = alloc_skb(sz, GFP_KERNEL); + if (!skb) { + wil_err(wil, "Failed to allocate skb\n"); + return; + } + eth = (struct ethhdr *)skb_put(skb, ETH_HLEN); + memcpy(eth->h_dest, ndev->dev_addr, ETH_ALEN); + memcpy(eth->h_source, evt->src_mac, ETH_ALEN); + eth->h_proto = cpu_to_be16(ETH_P_PAE); + memcpy(skb_put(skb, eapol_len), evt->eapol, eapol_len); + skb->protocol = eth_type_trans(skb, ndev); + if (likely(netif_rx_ni(skb) == NET_RX_SUCCESS)) { + ndev->stats.rx_packets++; + ndev->stats.rx_bytes += skb->len; + } else { + ndev->stats.rx_dropped++; + } +} + +static const struct { + int eventid; + void (*handler)(struct wil6210_priv *wil, int eventid, + void *data, int data_len); +} wmi_evt_handlers[] = { + {WMI_READY_EVENTID, wmi_evt_ready}, + {WMI_FW_READY_EVENTID, wmi_evt_fw_ready}, + {WMI_RX_MGMT_PACKET_EVENTID, wmi_evt_rx_mgmt}, + {WMI_SCAN_COMPLETE_EVENTID, wmi_evt_scan_complete}, + {WMI_CONNECT_EVENTID, wmi_evt_connect}, + {WMI_DISCONNECT_EVENTID, wmi_evt_disconnect}, + {WMI_NOTIFY_REQ_DONE_EVENTID, wmi_evt_notify}, + {WMI_EAPOL_RX_EVENTID, wmi_evt_eapol_rx}, +}; + +/* + * Run in IRQ context + * Extract WMI command from mailbox. Queue it to the @wil->pending_wmi_ev + * that will be eventually handled by the @wmi_event_worker in the thread + * context of thread "wil6210_wmi" + */ +void wmi_recv_cmd(struct wil6210_priv *wil) +{ + struct wil6210_mbox_ring_desc d_tail; + struct wil6210_mbox_hdr hdr; + struct wil6210_mbox_ring *r = &wil->mbox_ctl.rx; + struct pending_wmi_event *evt; + u8 *cmd; + void __iomem *src; + ulong flags; + + for (;;) { + u16 len; + + r->head = ioread32(wil->csr + HOST_MBOX + + offsetof(struct wil6210_mbox_ctl, rx.head)); + if (r->tail == r->head) + return; + + /* read cmd from tail */ + wil_memcpy_fromio_32(&d_tail, wil->csr + HOSTADDR(r->tail), + sizeof(struct wil6210_mbox_ring_desc)); + if (d_tail.sync == 0) { + wil_err(wil, "Mbox evt not owned by FW?\n"); + return; + } + + if (0 != wmi_read_hdr(wil, d_tail.addr, &hdr)) { + wil_err(wil, "Mbox evt at 0x%08x?\n", + le32_to_cpu(d_tail.addr)); + return; + } + + len = le16_to_cpu(hdr.len); + src = wmi_buffer(wil, d_tail.addr) + + sizeof(struct wil6210_mbox_hdr); + evt = kmalloc(ALIGN(offsetof(struct pending_wmi_event, + event.wmi) + len, 4), + GFP_KERNEL); + if (!evt) { + wil_err(wil, "kmalloc for WMI event (%d) failed\n", + len); + return; + } + evt->event.hdr = hdr; + cmd = (void *)&evt->event.wmi; + wil_memcpy_fromio_32(cmd, src, len); + /* mark entry as empty */ + iowrite32(0, wil->csr + HOSTADDR(r->tail) + + offsetof(struct wil6210_mbox_ring_desc, sync)); + /* indicate */ + wil_dbg_WMI(wil, "Mbox evt %04x %04x %04x %02x\n", + le16_to_cpu(hdr.seq), len, le16_to_cpu(hdr.type), + hdr.flags); + if ((hdr.type == WIL_MBOX_HDR_TYPE_WMI) && + (len >= sizeof(struct wil6210_mbox_hdr_wmi))) { + wil_dbg_WMI(wil, "WMI event 0x%04x\n", + evt->event.wmi.id); + } + wil_hex_dump_WMI("evt ", DUMP_PREFIX_OFFSET, 16, 1, + &evt->event.hdr, sizeof(hdr) + len, true); + + /* advance tail */ + r->tail = r->base + ((r->tail - r->base + + sizeof(struct wil6210_mbox_ring_desc)) % r->size); + iowrite32(r->tail, wil->csr + HOST_MBOX + + offsetof(struct wil6210_mbox_ctl, rx.tail)); + + /* add to the pending list */ + spin_lock_irqsave(&wil->wmi_ev_lock, flags); + list_add_tail(&evt->list, &wil->pending_wmi_ev); + spin_unlock_irqrestore(&wil->wmi_ev_lock, flags); + { + int q = queue_work(wil->wmi_wq, + &wil->wmi_event_worker); + wil_dbg_WMI(wil, "queue_work -> %d\n", q); + } + } +} + +int wmi_call(struct wil6210_priv *wil, u16 cmdid, void *buf, u16 len, + u16 reply_id, void *reply, u8 reply_size, int to_msec) +{ + int rc; + int remain; + + mutex_lock(&wil->wmi_mutex); + + rc = __wmi_send(wil, cmdid, buf, len); + if (rc) + goto out; + + wil->reply_id = reply_id; + wil->reply_buf = reply; + wil->reply_size = reply_size; + remain = wait_for_completion_timeout(&wil->wmi_ready, + msecs_to_jiffies(to_msec)); + if (0 == remain) { + wil_err(wil, "wmi_call(0x%04x->0x%04x) timeout %d msec\n", + cmdid, reply_id, to_msec); + rc = -ETIME; + } else { + wil_dbg_WMI(wil, + "wmi_call(0x%04x->0x%04x) completed in %d msec\n", + cmdid, reply_id, + to_msec - jiffies_to_msecs(remain)); + } + wil->reply_id = 0; + wil->reply_buf = NULL; + wil->reply_size = 0; + out: + mutex_unlock(&wil->wmi_mutex); + + return rc; +} + +int wmi_echo(struct wil6210_priv *wil) +{ + struct wmi_echo_cmd cmd = { + .value = cpu_to_le32(0x12345678), + }; + + return wmi_call(wil, WMI_ECHO_CMDID, &cmd, sizeof(cmd), + WMI_ECHO_RSP_EVENTID, NULL, 0, 20); +} + +int wmi_set_mac_address(struct wil6210_priv *wil, void *addr) +{ + struct wmi_set_mac_address_cmd cmd; + + memcpy(cmd.mac, addr, ETH_ALEN); + + wil_dbg_WMI(wil, "Set MAC %pM\n", addr); + + return wmi_send(wil, WMI_SET_MAC_ADDRESS_CMDID, &cmd, sizeof(cmd)); +} + +int wmi_set_bcon(struct wil6210_priv *wil, int bi, u8 wmi_nettype) +{ + struct wmi_bcon_ctrl_cmd cmd = { + .bcon_interval = cpu_to_le16(bi), + .network_type = wmi_nettype, + .disable_sec_offload = 1, + }; + + if (!wil->secure_pcp) + cmd.disable_sec = 1; + + return wmi_send(wil, WMI_BCON_CTRL_CMDID, &cmd, sizeof(cmd)); +} + +int wmi_set_ssid(struct wil6210_priv *wil, u8 ssid_len, const void *ssid) +{ + struct wmi_set_ssid_cmd cmd = { + .ssid_len = cpu_to_le32(ssid_len), + }; + + if (ssid_len > sizeof(cmd.ssid)) + return -EINVAL; + + memcpy(cmd.ssid, ssid, ssid_len); + + return wmi_send(wil, WMI_SET_SSID_CMDID, &cmd, sizeof(cmd)); +} + +int wmi_get_ssid(struct wil6210_priv *wil, u8 *ssid_len, void *ssid) +{ + int rc; + struct { + struct wil6210_mbox_hdr_wmi wmi; + struct wmi_set_ssid_cmd cmd; + } __packed reply; + int len; /* reply.cmd.ssid_len in CPU order */ + + rc = wmi_call(wil, WMI_GET_SSID_CMDID, NULL, 0, WMI_GET_SSID_EVENTID, + &reply, sizeof(reply), 20); + if (rc) + return rc; + + len = le32_to_cpu(reply.cmd.ssid_len); + if (len > sizeof(reply.cmd.ssid)) + return -EINVAL; + + *ssid_len = len; + memcpy(ssid, reply.cmd.ssid, len); + + return 0; +} + +int wmi_set_channel(struct wil6210_priv *wil, int channel) +{ + struct wmi_set_pcp_channel_cmd cmd = { + .channel = channel - 1, + }; + + return wmi_send(wil, WMI_SET_PCP_CHANNEL_CMDID, &cmd, sizeof(cmd)); +} + +int wmi_get_channel(struct wil6210_priv *wil, int *channel) +{ + int rc; + struct { + struct wil6210_mbox_hdr_wmi wmi; + struct wmi_set_pcp_channel_cmd cmd; + } __packed reply; + + rc = wmi_call(wil, WMI_GET_PCP_CHANNEL_CMDID, NULL, 0, + WMI_GET_PCP_CHANNEL_EVENTID, &reply, sizeof(reply), 20); + if (rc) + return rc; + + if (reply.cmd.channel > 3) + return -EINVAL; + + *channel = reply.cmd.channel + 1; + + return 0; +} + +int wmi_tx_eapol(struct wil6210_priv *wil, struct sk_buff *skb) +{ + struct wmi_eapol_tx_cmd *cmd; + struct ethhdr *eth; + u16 eapol_len = skb->len - ETH_HLEN; + void *eapol = skb->data + ETH_HLEN; + uint i; + int rc; + + skb_set_mac_header(skb, 0); + eth = eth_hdr(skb); + wil_dbg_WMI(wil, "EAPOL %d bytes to %pM\n", eapol_len, eth->h_dest); + for (i = 0; i < ARRAY_SIZE(wil->vring_tx); i++) { + if (memcmp(wil->dst_addr[i], eth->h_dest, ETH_ALEN) == 0) + goto found_dest; + } + + return -EINVAL; + + found_dest: + /* find out eapol data & len */ + cmd = kzalloc(sizeof(*cmd) + eapol_len, GFP_KERNEL); + if (!cmd) + return -EINVAL; + + memcpy(cmd->dst_mac, eth->h_dest, ETH_ALEN); + cmd->eapol_len = cpu_to_le16(eapol_len); + memcpy(cmd->eapol, eapol, eapol_len); + rc = wmi_send(wil, WMI_EAPOL_TX_CMDID, cmd, sizeof(*cmd) + eapol_len); + kfree(cmd); + + return rc; +} + +int wmi_del_cipher_key(struct wil6210_priv *wil, u8 key_index, + const void *mac_addr) +{ + struct wmi_delete_cipher_key_cmd cmd = { + .key_index = key_index, + }; + + if (mac_addr) + memcpy(cmd.mac, mac_addr, WMI_MAC_LEN); + + return wmi_send(wil, WMI_DELETE_CIPHER_KEY_CMDID, &cmd, sizeof(cmd)); +} + +int wmi_add_cipher_key(struct wil6210_priv *wil, u8 key_index, + const void *mac_addr, int key_len, const void *key) +{ + struct wmi_add_cipher_key_cmd cmd = { + .key_index = key_index, + .key_usage = WMI_KEY_USE_PAIRWISE, + .key_len = key_len, + }; + + if (!key || (key_len > sizeof(cmd.key))) + return -EINVAL; + + memcpy(cmd.key, key, key_len); + if (mac_addr) + memcpy(cmd.mac, mac_addr, WMI_MAC_LEN); + + return wmi_send(wil, WMI_ADD_CIPHER_KEY_CMDID, &cmd, sizeof(cmd)); +} + +int wmi_set_ie(struct wil6210_priv *wil, u8 type, u16 ie_len, const void *ie) +{ + int rc; + u16 len = sizeof(struct wmi_set_appie_cmd) + ie_len; + struct wmi_set_appie_cmd *cmd = kzalloc(len, GFP_KERNEL); + if (!cmd) { + wil_err(wil, "kmalloc(%d) failed\n", len); + return -ENOMEM; + } + + cmd->mgmt_frm_type = type; + /* BUG: FW API define ieLen as u8. Will fix FW */ + cmd->ie_len = cpu_to_le16(ie_len); + memcpy(cmd->ie_info, ie, ie_len); + rc = wmi_send(wil, WMI_SET_APPIE_CMDID, &cmd, len); + kfree(cmd); + + return rc; +} + +void wmi_event_flush(struct wil6210_priv *wil) +{ + struct pending_wmi_event *evt, *t; + + wil_dbg_WMI(wil, "%s()\n", __func__); + + list_for_each_entry_safe(evt, t, &wil->pending_wmi_ev, list) { + list_del(&evt->list); + kfree(evt); + } +} + +static bool wmi_evt_call_handler(struct wil6210_priv *wil, int id, + void *d, int len) +{ + uint i; + + for (i = 0; i < ARRAY_SIZE(wmi_evt_handlers); i++) { + if (wmi_evt_handlers[i].eventid == id) { + wmi_evt_handlers[i].handler(wil, id, d, len); + return true; + } + } + + return false; +} + +static void wmi_event_handle(struct wil6210_priv *wil, + struct wil6210_mbox_hdr *hdr) +{ + u16 len = le16_to_cpu(hdr->len); + + if ((hdr->type == WIL_MBOX_HDR_TYPE_WMI) && + (len >= sizeof(struct wil6210_mbox_hdr_wmi))) { + struct wil6210_mbox_hdr_wmi *wmi = (void *)(&hdr[1]); + void *evt_data = (void *)(&wmi[1]); + u16 id = le16_to_cpu(wmi->id); + /* check if someone waits for this event */ + if (wil->reply_id && wil->reply_id == id) { + if (wil->reply_buf) { + memcpy(wil->reply_buf, wmi, + min(len, wil->reply_size)); + } else { + wmi_evt_call_handler(wil, id, evt_data, + len - sizeof(*wmi)); + } + wil_dbg_WMI(wil, "Complete WMI 0x%04x\n", id); + complete(&wil->wmi_ready); + return; + } + /* unsolicited event */ + /* search for handler */ + if (!wmi_evt_call_handler(wil, id, evt_data, + len - sizeof(*wmi))) { + wil_err(wil, "Unhandled event 0x%04x\n", id); + } + } else { + wil_err(wil, "Unknown event type\n"); + print_hex_dump(KERN_ERR, "evt?? ", DUMP_PREFIX_OFFSET, 16, 1, + hdr, sizeof(*hdr) + len, true); + } +} + +/* + * Retrieve next WMI event from the pending list + */ +static struct list_head *next_wmi_ev(struct wil6210_priv *wil) +{ + ulong flags; + struct list_head *ret = NULL; + + spin_lock_irqsave(&wil->wmi_ev_lock, flags); + + if (!list_empty(&wil->pending_wmi_ev)) { + ret = wil->pending_wmi_ev.next; + list_del(ret); + } + + spin_unlock_irqrestore(&wil->wmi_ev_lock, flags); + + return ret; +} + +/* + * Handler for the WMI events + */ +void wmi_event_worker(struct work_struct *work) +{ + struct wil6210_priv *wil = container_of(work, struct wil6210_priv, + wmi_event_worker); + struct pending_wmi_event *evt; + struct list_head *lh; + + while ((lh = next_wmi_ev(wil)) != NULL) { + evt = list_entry(lh, struct pending_wmi_event, list); + wmi_event_handle(wil, &evt->event.hdr); + kfree(evt); + } +} + +void wmi_connect_worker(struct work_struct *work) +{ + int rc; + struct wil6210_priv *wil = container_of(work, struct wil6210_priv, + wmi_connect_worker); + + if (wil->pending_connect_cid < 0) { + wil_err(wil, "No connection pending\n"); + return; + } + + wil_dbg_WMI(wil, "Configure for connection CID %d\n", + wil->pending_connect_cid); + + rc = wil_vring_init_tx(wil, 0, WIL6210_TX_RING_SIZE, + wil->pending_connect_cid, 0); + wil->pending_connect_cid = -1; + if (rc == 0) + wil_link_on(wil); +} diff --git a/drivers/net/wireless/ath/wil6210/wmi.h b/drivers/net/wireless/ath/wil6210/wmi.h new file mode 100644 index 000000000000..3bbf87572b07 --- /dev/null +++ b/drivers/net/wireless/ath/wil6210/wmi.h @@ -0,0 +1,1116 @@ +/* + * Copyright (c) 2012 Qualcomm Atheros, Inc. + * Copyright (c) 2006-2012 Wilocity . + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +/* + * This file contains the definitions of the WMI protocol specified in the + * Wireless Module Interface (WMI) for the Wilocity + * MARLON 60 Gigabit wireless solution. + * It includes definitions of all the commands and events. + * Commands are messages from the host to the WM. + * Events are messages from the WM to the host. + */ + +#ifndef __WILOCITY_WMI_H__ +#define __WILOCITY_WMI_H__ + +/* General */ + +#define WMI_MAC_LEN (6) +#define WMI_PROX_RANGE_NUM (3) + +/* List of Commands */ +enum wmi_command_id { + WMI_CONNECT_CMDID = 0x0001, + WMI_DISCONNECT_CMDID = 0x0003, + WMI_START_SCAN_CMDID = 0x0007, + WMI_SET_BSS_FILTER_CMDID = 0x0009, + WMI_SET_PROBED_SSID_CMDID = 0x000a, + WMI_SET_LISTEN_INT_CMDID = 0x000b, + WMI_BCON_CTRL_CMDID = 0x000f, + WMI_ADD_CIPHER_KEY_CMDID = 0x0016, + WMI_DELETE_CIPHER_KEY_CMDID = 0x0017, + WMI_SET_APPIE_CMDID = 0x003f, + WMI_GET_APPIE_CMDID = 0x0040, + WMI_SET_WSC_STATUS_CMDID = 0x0041, + WMI_PXMT_RANGE_CFG_CMDID = 0x0042, + WMI_PXMT_SNR2_RANGE_CFG_CMDID = 0x0043, + WMI_FAST_MEM_ACC_MODE_CMDID = 0x0300, + WMI_MEM_READ_CMDID = 0x0800, + WMI_MEM_WR_CMDID = 0x0801, + WMI_ECHO_CMDID = 0x0803, + WMI_DEEP_ECHO_CMDID = 0x0804, + WMI_CONFIG_MAC_CMDID = 0x0805, + WMI_CONFIG_PHY_DEBUG_CMDID = 0x0806, + WMI_ADD_STATION_CMDID = 0x0807, + WMI_ADD_DEBUG_TX_PCKT_CMDID = 0x0808, + WMI_PHY_GET_STATISTICS_CMDID = 0x0809, + WMI_FS_TUNE_CMDID = 0x080a, + WMI_CORR_MEASURE_CMDID = 0x080b, + WMI_TEMP_SENSE_CMDID = 0x080e, + WMI_DC_CALIB_CMDID = 0x080f, + WMI_SEND_TONE_CMDID = 0x0810, + WMI_IQ_TX_CALIB_CMDID = 0x0811, + WMI_IQ_RX_CALIB_CMDID = 0x0812, + WMI_SET_UCODE_IDLE_CMDID = 0x0813, + WMI_SET_WORK_MODE_CMDID = 0x0815, + WMI_LO_LEAKAGE_CALIB_CMDID = 0x0816, + WMI_MARLON_R_ACTIVATE_CMDID = 0x0817, + WMI_MARLON_R_READ_CMDID = 0x0818, + WMI_MARLON_R_WRITE_CMDID = 0x0819, + WMI_MARLON_R_TXRX_SEL_CMDID = 0x081a, + MAC_IO_STATIC_PARAMS_CMDID = 0x081b, + MAC_IO_DYNAMIC_PARAMS_CMDID = 0x081c, + WMI_SILENT_RSSI_CALIB_CMDID = 0x081d, + WMI_CFG_RX_CHAIN_CMDID = 0x0820, + WMI_VRING_CFG_CMDID = 0x0821, + WMI_RX_ON_CMDID = 0x0822, + WMI_VRING_BA_EN_CMDID = 0x0823, + WMI_VRING_BA_DIS_CMDID = 0x0824, + WMI_RCP_ADDBA_RESP_CMDID = 0x0825, + WMI_RCP_DELBA_CMDID = 0x0826, + WMI_SET_SSID_CMDID = 0x0827, + WMI_GET_SSID_CMDID = 0x0828, + WMI_SET_PCP_CHANNEL_CMDID = 0x0829, + WMI_GET_PCP_CHANNEL_CMDID = 0x082a, + WMI_SW_TX_REQ_CMDID = 0x082b, + WMI_RX_OFF_CMDID = 0x082c, + WMI_READ_MAC_RXQ_CMDID = 0x0830, + WMI_READ_MAC_TXQ_CMDID = 0x0831, + WMI_WRITE_MAC_RXQ_CMDID = 0x0832, + WMI_WRITE_MAC_TXQ_CMDID = 0x0833, + WMI_WRITE_MAC_XQ_FIELD_CMDID = 0x0834, + WMI_MLME_PUSH_CMDID = 0x0835, + WMI_BEAMFORMING_MGMT_CMDID = 0x0836, + WMI_BF_TXSS_MGMT_CMDID = 0x0837, + WMI_BF_SM_MGMT_CMDID = 0x0838, + WMI_BF_RXSS_MGMT_CMDID = 0x0839, + WMI_SET_SECTORS_CMDID = 0x0849, + WMI_MAINTAIN_PAUSE_CMDID = 0x0850, + WMI_MAINTAIN_RESUME_CMDID = 0x0851, + WMI_RS_MGMT_CMDID = 0x0852, + WMI_RF_MGMT_CMDID = 0x0853, + /* Performance monitoring commands */ + WMI_BF_CTRL_CMDID = 0x0862, + WMI_NOTIFY_REQ_CMDID = 0x0863, + WMI_GET_STATUS_CMDID = 0x0864, + WMI_UNIT_TEST_CMDID = 0x0900, + WMI_HICCUP_CMDID = 0x0901, + WMI_FLASH_READ_CMDID = 0x0902, + WMI_FLASH_WRITE_CMDID = 0x0903, + WMI_SECURITY_UNIT_TEST_CMDID = 0x0904, + + WMI_SET_MAC_ADDRESS_CMDID = 0xf003, + WMI_ABORT_SCAN_CMDID = 0xf007, + WMI_SET_PMK_CMDID = 0xf028, + + WMI_SET_PROMISCUOUS_MODE_CMDID = 0xf041, + WMI_GET_PMK_CMDID = 0xf048, + WMI_SET_PASSPHRASE_CMDID = 0xf049, + WMI_SEND_ASSOC_RES_CMDID = 0xf04a, + WMI_SET_ASSOC_REQ_RELAY_CMDID = 0xf04b, + WMI_EAPOL_TX_CMDID = 0xf04c, + WMI_MAC_ADDR_REQ_CMDID = 0xf04d, + WMI_FW_VER_CMDID = 0xf04e, +}; + +/* + * Commands data structures + */ + +/* + * Frame Types + */ +enum wmi_mgmt_frame_type { + WMI_FRAME_BEACON = 0, + WMI_FRAME_PROBE_REQ = 1, + WMI_FRAME_PROBE_RESP = 2, + WMI_FRAME_ASSOC_REQ = 3, + WMI_FRAME_ASSOC_RESP = 4, + WMI_NUM_MGMT_FRAME, +}; + +/* + * WMI_CONNECT_CMDID + */ +enum wmi_network_type { + WMI_NETTYPE_INFRA = 0x01, + WMI_NETTYPE_ADHOC = 0x02, + WMI_NETTYPE_ADHOC_CREATOR = 0x04, + WMI_NETTYPE_AP = 0x10, + WMI_NETTYPE_P2P = 0x20, + WMI_NETTYPE_WBE = 0x40, /* PCIE over 60g */ +}; + +enum wmi_dot11_auth_mode { + WMI_AUTH11_OPEN = 0x01, + WMI_AUTH11_SHARED = 0x02, + WMI_AUTH11_LEAP = 0x04, + WMI_AUTH11_WSC = 0x08, +}; + +enum wmi_auth_mode { + WMI_AUTH_NONE = 0x01, + WMI_AUTH_WPA = 0x02, + WMI_AUTH_WPA2 = 0x04, + WMI_AUTH_WPA_PSK = 0x08, + WMI_AUTH_WPA2_PSK = 0x10, + WMI_AUTH_WPA_CCKM = 0x20, + WMI_AUTH_WPA2_CCKM = 0x40, +}; + +enum wmi_crypto_type { + WMI_CRYPT_NONE = 0x01, + WMI_CRYPT_WEP = 0x02, + WMI_CRYPT_TKIP = 0x04, + WMI_CRYPT_AES = 0x08, + WMI_CRYPT_AES_GCMP = 0x20, +}; + + +enum wmi_connect_ctrl_flag_bits { + WMI_CONNECT_ASSOC_POLICY_USER = 0x0001, + WMI_CONNECT_SEND_REASSOC = 0x0002, + WMI_CONNECT_IGNORE_WPAx_GROUP_CIPHER = 0x0004, + WMI_CONNECT_PROFILE_MATCH_DONE = 0x0008, + WMI_CONNECT_IGNORE_AAC_BEACON = 0x0010, + WMI_CONNECT_CSA_FOLLOW_BSS = 0x0020, + WMI_CONNECT_DO_WPA_OFFLOAD = 0x0040, + WMI_CONNECT_DO_NOT_DEAUTH = 0x0080, +}; + +#define WMI_MAX_SSID_LEN (32) + +struct wmi_connect_cmd { + u8 network_type; + u8 dot11_auth_mode; + u8 auth_mode; + u8 pairwise_crypto_type; + u8 pairwise_crypto_len; + u8 group_crypto_type; + u8 group_crypto_len; + u8 ssid_len; + u8 ssid[WMI_MAX_SSID_LEN]; + u8 channel; + u8 reserved0; + u8 bssid[WMI_MAC_LEN]; + __le32 ctrl_flags; + u8 dst_mac[WMI_MAC_LEN]; + u8 reserved1[2]; +} __packed; + + +/* + * WMI_RECONNECT_CMDID + */ +struct wmi_reconnect_cmd { + u8 channel; /* hint */ + u8 reserved; + u8 bssid[WMI_MAC_LEN]; /* mandatory if set */ +} __packed; + + +/* + * WMI_SET_PMK_CMDID + */ + +#define WMI_MIN_KEY_INDEX (0) +#define WMI_MAX_KEY_INDEX (3) +#define WMI_MAX_KEY_LEN (32) +#define WMI_PASSPHRASE_LEN (64) +#define WMI_PMK_LEN (32) + +struct wmi_set_pmk_cmd { + u8 pmk[WMI_PMK_LEN]; +} __packed; + + +/* + * WMI_SET_PASSPHRASE_CMDID + */ +struct wmi_set_passphrase_cmd { + u8 ssid[WMI_MAX_SSID_LEN]; + u8 passphrase[WMI_PASSPHRASE_LEN]; + u8 ssid_len; + u8 passphrase_len; +} __packed; + +/* + * WMI_ADD_CIPHER_KEY_CMDID + */ +enum wmi_key_usage { + WMI_KEY_USE_PAIRWISE = 0, + WMI_KEY_USE_GROUP = 1, + WMI_KEY_USE_TX = 2, /* default Tx Key - Static WEP only */ +}; + +struct wmi_add_cipher_key_cmd { + u8 key_index; + u8 key_type; + u8 key_usage; /* enum wmi_key_usage */ + u8 key_len; + u8 key_rsc[8]; /* key replay sequence counter */ + u8 key[WMI_MAX_KEY_LEN]; + u8 key_op_ctrl; /* Additional Key Control information */ + u8 mac[WMI_MAC_LEN]; +} __packed; + +/* + * WMI_DELETE_CIPHER_KEY_CMDID + */ +struct wmi_delete_cipher_key_cmd { + u8 key_index; + u8 mac[WMI_MAC_LEN]; +} __packed; + + +/* + * WMI_START_SCAN_CMDID + * + * Start L1 scan operation + * + * Returned events: + * - WMI_RX_MGMT_PACKET_EVENTID - for every probe resp. + * - WMI_SCAN_COMPLETE_EVENTID + */ +enum wmi_scan_type { + WMI_LONG_SCAN = 0, + WMI_SHORT_SCAN = 1, +}; + +struct wmi_start_scan_cmd { + u8 reserved[8]; + __le32 home_dwell_time; /* Max duration in the home channel(ms) */ + __le32 force_scan_interval; /* Time interval between scans (ms)*/ + u8 scan_type; /* wmi_scan_type */ + u8 num_channels; /* how many channels follow */ + struct { + u8 channel; + u8 reserved; + } channel_list[0]; /* channels ID's */ + /* 0 - 58320 MHz */ + /* 1 - 60480 MHz */ + /* 2 - 62640 MHz */ +} __packed; + +/* + * WMI_SET_PROBED_SSID_CMDID + */ +#define MAX_PROBED_SSID_INDEX (15) + +enum wmi_ssid_flag { + WMI_SSID_FLAG_DISABLE = 0, /* disables entry */ + WMI_SSID_FLAG_SPECIFIC = 1, /* probes specified ssid */ + WMI_SSID_FLAG_ANY = 2, /* probes for any ssid */ +}; + +struct wmi_probed_ssid_cmd { + u8 entry_index; /* 0 to MAX_PROBED_SSID_INDEX */ + u8 flag; /* enum wmi_ssid_flag */ + u8 ssid_len; + u8 ssid[WMI_MAX_SSID_LEN]; +} __packed; + +/* + * WMI_SET_APPIE_CMDID + * Add Application specified IE to a management frame + */ +struct wmi_set_appie_cmd { + u8 mgmt_frm_type; /* enum wmi_mgmt_frame_type */ + u8 reserved; + __le16 ie_len; /* Length of the IE to be added to MGMT frame */ + u8 ie_info[0]; +} __packed; + +#define WMI_MAX_IE_LEN (1024) + +struct wmi_pxmt_range_cfg_cmd { + u8 dst_mac[WMI_MAC_LEN]; + __le16 range; +} __packed; + +struct wmi_pxmt_snr2_range_cfg_cmd { + s8 snr2range_arr[WMI_PROX_RANGE_NUM-1]; +} __packed; + +/* + * WMI_RF_MGMT_CMDID + */ +enum wmi_rf_mgmt_type { + WMI_RF_MGMT_W_DISABLE = 0, + WMI_RF_MGMT_W_ENABLE = 1, + WMI_RF_MGMT_GET_STATUS = 2, +}; + +struct wmi_rf_mgmt_cmd { + __le32 rf_mgmt_type; +} __packed; + +/* + * WMI_SET_SSID_CMDID + */ +struct wmi_set_ssid_cmd { + __le32 ssid_len; + u8 ssid[WMI_MAX_SSID_LEN]; +} __packed; + +/* + * WMI_SET_PCP_CHANNEL_CMDID + */ +struct wmi_set_pcp_channel_cmd { + u8 channel; + u8 reserved[3]; +} __packed; + +/* + * WMI_BCON_CTRL_CMDID + */ +struct wmi_bcon_ctrl_cmd { + __le16 bcon_interval; + __le16 frag_num; + __le64 ss_mask; + u8 network_type; + u8 reserved; + u8 disable_sec_offload; + u8 disable_sec; +} __packed; + +/* + * WMI_SW_TX_REQ_CMDID + */ +struct wmi_sw_tx_req_cmd { + u8 dst_mac[WMI_MAC_LEN]; + __le16 len; + u8 payload[0]; +} __packed; + +/* + * WMI_VRING_CFG_CMDID + */ + +struct wmi_sw_ring_cfg { + __le64 ring_mem_base; + __le16 ring_size; + __le16 max_mpdu_size; +} __packed; + +struct wmi_vring_cfg_schd { + __le16 priority; + __le16 timeslot_us; +} __packed; + +enum wmi_vring_cfg_encap_trans_type { + WMI_VRING_ENC_TYPE_802_3 = 0, + WMI_VRING_ENC_TYPE_NATIVE_WIFI = 1, +}; + +enum wmi_vring_cfg_ds_cfg { + WMI_VRING_DS_PBSS = 0, + WMI_VRING_DS_STATION = 1, + WMI_VRING_DS_AP = 2, + WMI_VRING_DS_ADDR4 = 3, +}; + +enum wmi_vring_cfg_nwifi_ds_trans_type { + WMI_NWIFI_TX_TRANS_MODE_NO = 0, + WMI_NWIFI_TX_TRANS_MODE_AP2PBSS = 1, + WMI_NWIFI_TX_TRANS_MODE_STA2PBSS = 2, +}; + +enum wmi_vring_cfg_schd_params_priority { + WMI_SCH_PRIO_REGULAR = 0, + WMI_SCH_PRIO_HIGH = 1, +}; + +struct wmi_vring_cfg { + struct wmi_sw_ring_cfg tx_sw_ring; + u8 ringid; /* 0-23 vrings */ + + #define CIDXTID_CID_POS (0) + #define CIDXTID_CID_LEN (4) + #define CIDXTID_CID_MSK (0xF) + #define CIDXTID_TID_POS (4) + #define CIDXTID_TID_LEN (4) + #define CIDXTID_TID_MSK (0xF0) + u8 cidxtid; + + u8 encap_trans_type; + u8 ds_cfg; /* 802.3 DS cfg */ + u8 nwifi_ds_trans_type; + + #define VRING_CFG_MAC_CTRL_LIFETIME_EN_POS (0) + #define VRING_CFG_MAC_CTRL_LIFETIME_EN_LEN (1) + #define VRING_CFG_MAC_CTRL_LIFETIME_EN_MSK (0x1) + #define VRING_CFG_MAC_CTRL_AGGR_EN_POS (1) + #define VRING_CFG_MAC_CTRL_AGGR_EN_LEN (1) + #define VRING_CFG_MAC_CTRL_AGGR_EN_MSK (0x2) + u8 mac_ctrl; + + #define VRING_CFG_TO_RESOLUTION_VALUE_POS (0) + #define VRING_CFG_TO_RESOLUTION_VALUE_LEN (6) + #define VRING_CFG_TO_RESOLUTION_VALUE_MSK (0x3F) + u8 to_resolution; + u8 agg_max_wsize; + struct wmi_vring_cfg_schd schd_params; +} __packed; + +enum wmi_vring_cfg_cmd_action { + WMI_VRING_CMD_ADD = 0, + WMI_VRING_CMD_MODIFY = 1, + WMI_VRING_CMD_DELETE = 2, +}; + +struct wmi_vring_cfg_cmd { + __le32 action; + struct wmi_vring_cfg vring_cfg; +} __packed; + +/* + * WMI_VRING_BA_EN_CMDID + */ +struct wmi_vring_ba_en_cmd { + u8 ringid; + u8 agg_max_wsize; + __le16 ba_timeout; +} __packed; + +/* + * WMI_VRING_BA_DIS_CMDID + */ +struct wmi_vring_ba_dis_cmd { + u8 ringid; + u8 reserved; + __le16 reason; +} __packed; + +/* + * WMI_NOTIFY_REQ_CMDID + */ +struct wmi_notify_req_cmd { + u8 cid; + u8 reserved[3]; + __le32 interval_usec; +} __packed; + +/* + * WMI_CFG_RX_CHAIN_CMDID + */ +enum wmi_sniffer_cfg_mode { + WMI_SNIFFER_OFF = 0, + WMI_SNIFFER_ON = 1, +}; + +enum wmi_sniffer_cfg_phy_info_mode { + WMI_SNIFFER_PHY_INFO_DISABLED = 0, + WMI_SNIFFER_PHY_INFO_ENABLED = 1, +}; + +enum wmi_sniffer_cfg_phy_support { + WMI_SNIFFER_CP = 0, + WMI_SNIFFER_DP = 1, + WMI_SNIFFER_BOTH_PHYS = 2, +}; + +struct wmi_sniffer_cfg { + __le32 mode; /* enum wmi_sniffer_cfg_mode */ + __le32 phy_info_mode; /* enum wmi_sniffer_cfg_phy_info_mode */ + __le32 phy_support; /* enum wmi_sniffer_cfg_phy_support */ + u8 channel; + u8 reserved[3]; +} __packed; + +enum wmi_cfg_rx_chain_cmd_action { + WMI_RX_CHAIN_ADD = 0, + WMI_RX_CHAIN_DEL = 1, +}; + +enum wmi_cfg_rx_chain_cmd_decap_trans_type { + WMI_DECAP_TYPE_802_3 = 0, + WMI_DECAP_TYPE_NATIVE_WIFI = 1, +}; + +enum wmi_cfg_rx_chain_cmd_nwifi_ds_trans_type { + WMI_NWIFI_RX_TRANS_MODE_NO = 0, + WMI_NWIFI_RX_TRANS_MODE_PBSS2AP = 1, + WMI_NWIFI_RX_TRANS_MODE_PBSS2STA = 2, +}; + +struct wmi_cfg_rx_chain_cmd { + __le32 action; + struct wmi_sw_ring_cfg rx_sw_ring; + u8 mid; + u8 decap_trans_type; + + #define L2_802_3_OFFLOAD_CTRL_VLAN_TAG_INSERTION_POS (0) + #define L2_802_3_OFFLOAD_CTRL_VLAN_TAG_INSERTION_LEN (1) + #define L2_802_3_OFFLOAD_CTRL_VLAN_TAG_INSERTION_MSK (0x1) + u8 l2_802_3_offload_ctrl; + + #define L2_NWIFI_OFFLOAD_CTRL_REMOVE_QOS_POS (0) + #define L2_NWIFI_OFFLOAD_CTRL_REMOVE_QOS_LEN (1) + #define L2_NWIFI_OFFLOAD_CTRL_REMOVE_QOS_MSK (0x1) + #define L2_NWIFI_OFFLOAD_CTRL_REMOVE_PN_POS (1) + #define L2_NWIFI_OFFLOAD_CTRL_REMOVE_PN_LEN (1) + #define L2_NWIFI_OFFLOAD_CTRL_REMOVE_PN_MSK (0x2) + u8 l2_nwifi_offload_ctrl; + + u8 vlan_id; + u8 nwifi_ds_trans_type; + + #define L3_L4_CTRL_IPV4_CHECKSUM_EN_POS (0) + #define L3_L4_CTRL_IPV4_CHECKSUM_EN_LEN (1) + #define L3_L4_CTRL_IPV4_CHECKSUM_EN_MSK (0x1) + #define L3_L4_CTRL_TCPIP_CHECKSUM_EN_POS (1) + #define L3_L4_CTRL_TCPIP_CHECKSUM_EN_LEN (1) + #define L3_L4_CTRL_TCPIP_CHECKSUM_EN_MSK (0x2) + u8 l3_l4_ctrl; + + #define RING_CTRL_OVERRIDE_PREFETCH_THRSH_POS (0) + #define RING_CTRL_OVERRIDE_PREFETCH_THRSH_LEN (1) + #define RING_CTRL_OVERRIDE_PREFETCH_THRSH_MSK (0x1) + #define RING_CTRL_OVERRIDE_WB_THRSH_POS (1) + #define RING_CTRL_OVERRIDE_WB_THRSH_LEN (1) + #define RING_CTRL_OVERRIDE_WB_THRSH_MSK (0x2) + #define RING_CTRL_OVERRIDE_ITR_THRSH_POS (2) + #define RING_CTRL_OVERRIDE_ITR_THRSH_LEN (1) + #define RING_CTRL_OVERRIDE_ITR_THRSH_MSK (0x4) + #define RING_CTRL_OVERRIDE_HOST_THRSH_POS (3) + #define RING_CTRL_OVERRIDE_HOST_THRSH_LEN (1) + #define RING_CTRL_OVERRIDE_HOST_THRSH_MSK (0x8) + u8 ring_ctrl; + + __le16 prefetch_thrsh; + __le16 wb_thrsh; + __le32 itr_value; + __le16 host_thrsh; + u8 reserved[2]; + struct wmi_sniffer_cfg sniffer_cfg; +} __packed; + +/* + * WMI_RCP_ADDBA_RESP_CMDID + */ +struct wmi_rcp_addba_resp_cmd { + + #define CIDXTID_CID_POS (0) + #define CIDXTID_CID_LEN (4) + #define CIDXTID_CID_MSK (0xF) + #define CIDXTID_TID_POS (4) + #define CIDXTID_TID_LEN (4) + #define CIDXTID_TID_MSK (0xF0) + u8 cidxtid; + + u8 dialog_token; + __le16 status_code; + __le16 ba_param_set; /* ieee80211_ba_parameterset field to send */ + __le16 ba_timeout; +} __packed; + +/* + * WMI_RCP_DELBA_CMDID + */ +struct wmi_rcp_delba_cmd { + + #define CIDXTID_CID_POS (0) + #define CIDXTID_CID_LEN (4) + #define CIDXTID_CID_MSK (0xF) + #define CIDXTID_TID_POS (4) + #define CIDXTID_TID_LEN (4) + #define CIDXTID_TID_MSK (0xF0) + u8 cidxtid; + + u8 reserved; + __le16 reason; +} __packed; + +/* + * WMI_RCP_ADDBA_REQ_CMDID + */ +struct wmi_rcp_addba_req_cmd { + + #define CIDXTID_CID_POS (0) + #define CIDXTID_CID_LEN (4) + #define CIDXTID_CID_MSK (0xF) + #define CIDXTID_TID_POS (4) + #define CIDXTID_TID_LEN (4) + #define CIDXTID_TID_MSK (0xF0) + u8 cidxtid; + + u8 dialog_token; + /* ieee80211_ba_parameterset field as it received */ + __le16 ba_param_set; + __le16 ba_timeout; + /* ieee80211_ba_seqstrl field as it received */ + __le16 ba_seq_ctrl; +} __packed; + +/* + * WMI_SET_MAC_ADDRESS_CMDID + */ +struct wmi_set_mac_address_cmd { + u8 mac[WMI_MAC_LEN]; + u8 reserved[2]; +} __packed; + + +/* +* WMI_EAPOL_TX_CMDID +*/ +struct wmi_eapol_tx_cmd { + u8 dst_mac[WMI_MAC_LEN]; + __le16 eapol_len; + u8 eapol[0]; +} __packed; + +/* + * WMI_ECHO_CMDID + * + * Check FW is alive + * + * WMI_DEEP_ECHO_CMDID + * + * Check FW and ucode are alive + * + * Returned event: WMI_ECHO_RSP_EVENTID + * same event for both commands + */ +struct wmi_echo_cmd { + __le32 value; +} __packed; + +/* + * WMI Events + */ + +/* + * List of Events (target to host) + */ +enum wmi_event_id { + WMI_IMM_RSP_EVENTID = 0x0000, + WMI_READY_EVENTID = 0x1001, + WMI_CONNECT_EVENTID = 0x1002, + WMI_DISCONNECT_EVENTID = 0x1003, + WMI_SCAN_COMPLETE_EVENTID = 0x100a, + WMI_REPORT_STATISTICS_EVENTID = 0x100b, + WMI_RD_MEM_RSP_EVENTID = 0x1800, + WMI_FW_READY_EVENTID = 0x1801, + WMI_EXIT_FAST_MEM_ACC_MODE_EVENTID = 0x0200, + WMI_ECHO_RSP_EVENTID = 0x1803, + WMI_CONFIG_MAC_DONE_EVENTID = 0x1805, + WMI_CONFIG_PHY_DEBUG_DONE_EVENTID = 0x1806, + WMI_ADD_STATION_DONE_EVENTID = 0x1807, + WMI_ADD_DEBUG_TX_PCKT_DONE_EVENTID = 0x1808, + WMI_PHY_GET_STATISTICS_EVENTID = 0x1809, + WMI_FS_TUNE_DONE_EVENTID = 0x180a, + WMI_CORR_MEASURE_DONE_EVENTID = 0x180b, + WMI_TEMP_SENSE_DONE_EVENTID = 0x180e, + WMI_DC_CALIB_DONE_EVENTID = 0x180f, + WMI_IQ_TX_CALIB_DONE_EVENTID = 0x1811, + WMI_IQ_RX_CALIB_DONE_EVENTID = 0x1812, + WMI_SET_WORK_MODE_DONE_EVENTID = 0x1815, + WMI_LO_LEAKAGE_CALIB_DONE_EVENTID = 0x1816, + WMI_MARLON_R_ACTIVATE_DONE_EVENTID = 0x1817, + WMI_MARLON_R_READ_DONE_EVENTID = 0x1818, + WMI_MARLON_R_WRITE_DONE_EVENTID = 0x1819, + WMI_MARLON_R_TXRX_SEL_DONE_EVENTID = 0x181a, + WMI_SILENT_RSSI_CALIB_DONE_EVENTID = 0x181d, + + WMI_CFG_RX_CHAIN_DONE_EVENTID = 0x1820, + WMI_VRING_CFG_DONE_EVENTID = 0x1821, + WMI_RX_ON_DONE_EVENTID = 0x1822, + WMI_BA_STATUS_EVENTID = 0x1823, + WMI_RCP_ADDBA_REQ_EVENTID = 0x1824, + WMI_ADDBA_RESP_SENT_EVENTID = 0x1825, + WMI_DELBA_EVENTID = 0x1826, + WMI_GET_SSID_EVENTID = 0x1828, + WMI_GET_PCP_CHANNEL_EVENTID = 0x182a, + WMI_SW_TX_COMPLETE_EVENTID = 0x182b, + WMI_RX_OFF_DONE_EVENTID = 0x182c, + + WMI_READ_MAC_RXQ_EVENTID = 0x1830, + WMI_READ_MAC_TXQ_EVENTID = 0x1831, + WMI_WRITE_MAC_RXQ_EVENTID = 0x1832, + WMI_WRITE_MAC_TXQ_EVENTID = 0x1833, + WMI_WRITE_MAC_XQ_FIELD_EVENTID = 0x1834, + + WMI_BEAFORMING_MGMT_DONE_EVENTID = 0x1836, + WMI_BF_TXSS_MGMT_DONE_EVENTID = 0x1837, + WMI_BF_RXSS_MGMT_DONE_EVENTID = 0x1839, + WMI_RS_MGMT_DONE_EVENTID = 0x1852, + WMI_RF_MGMT_STATUS_EVENTID = 0x1853, + WMI_BF_SM_MGMT_DONE_EVENTID = 0x1838, + WMI_RX_MGMT_PACKET_EVENTID = 0x1840, + + /* Performance monitoring events */ + WMI_DATA_PORT_OPEN_EVENTID = 0x1860, + WMI_WBE_LINKDOWN_EVENTID = 0x1861, + + WMI_BF_CTRL_DONE_EVENTID = 0x1862, + WMI_NOTIFY_REQ_DONE_EVENTID = 0x1863, + WMI_GET_STATUS_DONE_EVENTID = 0x1864, + + WMI_UNIT_TEST_EVENTID = 0x1900, + WMI_FLASH_READ_DONE_EVENTID = 0x1902, + WMI_FLASH_WRITE_DONE_EVENTID = 0x1903, + + WMI_SET_CHANNEL_EVENTID = 0x9000, + WMI_ASSOC_REQ_EVENTID = 0x9001, + WMI_EAPOL_RX_EVENTID = 0x9002, + WMI_MAC_ADDR_RESP_EVENTID = 0x9003, + WMI_FW_VER_EVENTID = 0x9004, +}; + +/* + * Events data structures + */ + +/* + * WMI_RF_MGMT_STATUS_EVENTID + */ +enum wmi_rf_status { + WMI_RF_ENABLED = 0, + WMI_RF_DISABLED_HW = 1, + WMI_RF_DISABLED_SW = 2, + WMI_RF_DISABLED_HW_SW = 3, +}; + +struct wmi_rf_mgmt_status_event { + __le32 rf_status; +} __packed; + +/* + * WMI_GET_STATUS_DONE_EVENTID + */ +struct wmi_get_status_done_event { + __le32 is_associated; + u8 cid; + u8 reserved0[3]; + u8 bssid[WMI_MAC_LEN]; + u8 channel; + u8 reserved1; + u8 network_type; + u8 reserved2[3]; + __le32 ssid_len; + u8 ssid[WMI_MAX_SSID_LEN]; + __le32 rf_status; + __le32 is_secured; +} __packed; + +/* + * WMI_FW_VER_EVENTID + */ +struct wmi_fw_ver_event { + u8 major; + u8 minor; + __le16 subminor; + __le16 build; +} __packed; + +/* +* WMI_MAC_ADDR_RESP_EVENTID +*/ +struct wmi_mac_addr_resp_event { + u8 mac[WMI_MAC_LEN]; + u8 auth_mode; + u8 crypt_mode; + __le32 offload_mode; +} __packed; + +/* +* WMI_EAPOL_RX_EVENTID +*/ +struct wmi_eapol_rx_event { + u8 src_mac[WMI_MAC_LEN]; + __le16 eapol_len; + u8 eapol[0]; +} __packed; + +/* +* WMI_READY_EVENTID +*/ +enum wmi_phy_capability { + WMI_11A_CAPABILITY = 1, + WMI_11G_CAPABILITY = 2, + WMI_11AG_CAPABILITY = 3, + WMI_11NA_CAPABILITY = 4, + WMI_11NG_CAPABILITY = 5, + WMI_11NAG_CAPABILITY = 6, + WMI_11AD_CAPABILITY = 7, + WMI_11N_CAPABILITY_OFFSET = WMI_11NA_CAPABILITY - WMI_11A_CAPABILITY, +}; + +struct wmi_ready_event { + __le32 sw_version; + __le32 abi_version; + u8 mac[WMI_MAC_LEN]; + u8 phy_capability; /* enum wmi_phy_capability */ + u8 reserved; +} __packed; + +/* + * WMI_NOTIFY_REQ_DONE_EVENTID + */ +struct wmi_notify_req_done_event { + __le32 status; + __le64 tsf; + __le32 snr_val; + __le32 tx_tpt; + __le32 tx_goodput; + __le32 rx_goodput; + __le16 bf_mcs; + __le16 my_rx_sector; + __le16 my_tx_sector; + __le16 other_rx_sector; + __le16 other_tx_sector; + __le16 range; +} __packed; + +/* + * WMI_CONNECT_EVENTID + */ +struct wmi_connect_event { + u8 channel; + u8 reserved0; + u8 bssid[WMI_MAC_LEN]; + __le16 listen_interval; + __le16 beacon_interval; + u8 network_type; + u8 reserved1[3]; + u8 beacon_ie_len; + u8 assoc_req_len; + u8 assoc_resp_len; + u8 cid; + u8 reserved2[3]; + u8 assoc_info[0]; +} __packed; + +/* + * WMI_DISCONNECT_EVENTID + */ +enum wmi_disconnect_reason { + WMI_DIS_REASON_NO_NETWORK_AVAIL = 1, + WMI_DIS_REASON_LOST_LINK = 2, /* bmiss */ + WMI_DIS_REASON_DISCONNECT_CMD = 3, + WMI_DIS_REASON_BSS_DISCONNECTED = 4, + WMI_DIS_REASON_AUTH_FAILED = 5, + WMI_DIS_REASON_ASSOC_FAILED = 6, + WMI_DIS_REASON_NO_RESOURCES_AVAIL = 7, + WMI_DIS_REASON_CSERV_DISCONNECT = 8, + WMI_DIS_REASON_INVALID_PROFILE = 10, + WMI_DIS_REASON_DOT11H_CHANNEL_SWITCH = 11, + WMI_DIS_REASON_PROFILE_MISMATCH = 12, + WMI_DIS_REASON_CONNECTION_EVICTED = 13, + WMI_DIS_REASON_IBSS_MERGE = 14, +}; + +struct wmi_disconnect_event { + __le16 protocol_reason_status; /* reason code, see 802.11 spec. */ + u8 bssid[WMI_MAC_LEN]; /* set if known */ + u8 disconnect_reason; /* see wmi_disconnect_reason_e */ + u8 assoc_resp_len; + u8 assoc_info[0]; +} __packed; + +/* + * WMI_SCAN_COMPLETE_EVENTID + */ +struct wmi_scan_complete_event { + __le32 status; +} __packed; + +/* + * WMI_BA_STATUS_EVENTID + */ +enum wmi_vring_ba_status { + WMI_BA_AGREED = 0, + WMI_BA_NON_AGREED = 1, +}; + +struct wmi_vring_ba_status_event { + __le16 status; + u8 reserved[2]; + u8 ringid; + u8 agg_wsize; + __le16 ba_timeout; +} __packed; + +/* + * WMI_DELBA_EVENTID + */ +struct wmi_delba_event { + + #define CIDXTID_CID_POS (0) + #define CIDXTID_CID_LEN (4) + #define CIDXTID_CID_MSK (0xF) + #define CIDXTID_TID_POS (4) + #define CIDXTID_TID_LEN (4) + #define CIDXTID_TID_MSK (0xF0) + u8 cidxtid; + + u8 from_initiator; + __le16 reason; +} __packed; + +/* + * WMI_VRING_CFG_DONE_EVENTID + */ +enum wmi_vring_cfg_done_event_status { + WMI_VRING_CFG_SUCCESS = 0, + WMI_VRING_CFG_FAILURE = 1, +}; + +struct wmi_vring_cfg_done_event { + u8 ringid; + u8 status; + u8 reserved[2]; + __le32 tx_vring_tail_ptr; +} __packed; + +/* + * WMI_ADDBA_RESP_SENT_EVENTID + */ +enum wmi_rcp_addba_resp_sent_event_status { + WMI_ADDBA_SUCCESS = 0, + WMI_ADDBA_FAIL = 1, +}; + +struct wmi_rcp_addba_resp_sent_event { + + #define CIDXTID_CID_POS (0) + #define CIDXTID_CID_LEN (4) + #define CIDXTID_CID_MSK (0xF) + #define CIDXTID_TID_POS (4) + #define CIDXTID_TID_LEN (4) + #define CIDXTID_TID_MSK (0xF0) + u8 cidxtid; + + u8 reserved; + __le16 status; +} __packed; + +/* + * WMI_RCP_ADDBA_REQ_EVENTID + */ +struct wmi_rcp_addba_req_event { + + #define CIDXTID_CID_POS (0) + #define CIDXTID_CID_LEN (4) + #define CIDXTID_CID_MSK (0xF) + #define CIDXTID_TID_POS (4) + #define CIDXTID_TID_LEN (4) + #define CIDXTID_TID_MSK (0xF0) + u8 cidxtid; + + u8 dialog_token; + __le16 ba_param_set; /* ieee80211_ba_parameterset as it received */ + __le16 ba_timeout; + __le16 ba_seq_ctrl; /* ieee80211_ba_seqstrl field as it received */ +} __packed; + +/* + * WMI_CFG_RX_CHAIN_DONE_EVENTID + */ +enum wmi_cfg_rx_chain_done_event_status { + WMI_CFG_RX_CHAIN_SUCCESS = 1, +}; + +struct wmi_cfg_rx_chain_done_event { + __le32 rx_ring_tail_ptr; /* Rx V-Ring Tail pointer */ + __le32 status; +} __packed; + +/* + * WMI_WBE_LINKDOWN_EVENTID + */ +enum wmi_wbe_link_down_event_reason { + WMI_WBE_REASON_USER_REQUEST = 0, + WMI_WBE_REASON_RX_DISASSOC = 1, + WMI_WBE_REASON_BAD_PHY_LINK = 2, +}; + +struct wmi_wbe_link_down_event { + u8 cid; + u8 reserved[3]; + __le32 reason; +} __packed; + +/* + * WMI_DATA_PORT_OPEN_EVENTID + */ +struct wmi_data_port_open_event { + u8 cid; + u8 reserved[3]; +} __packed; + +/* + * WMI_GET_PCP_CHANNEL_EVENTID + */ +struct wmi_get_pcp_channel_event { + u8 channel; + u8 reserved[3]; +} __packed; + +/* + * WMI_SW_TX_COMPLETE_EVENTID + */ +enum wmi_sw_tx_status { + WMI_TX_SW_STATUS_SUCCESS = 0, + WMI_TX_SW_STATUS_FAILED_NO_RESOURCES = 1, + WMI_TX_SW_STATUS_FAILED_TX = 2, +}; + +struct wmi_sw_tx_complete_event { + u8 status; /* enum wmi_sw_tx_status */ + u8 reserved[3]; +} __packed; + +/* + * WMI_GET_SSID_EVENTID + */ +struct wmi_get_ssid_event { + __le32 ssid_len; + u8 ssid[WMI_MAX_SSID_LEN]; +} __packed; + +/* + * WMI_RX_MGMT_PACKET_EVENTID + */ +struct wmi_rx_mgmt_info { + u8 mcs; + s8 snr; + __le16 range; + __le16 stype; + __le16 status; + __le32 len; + u8 qid; + u8 mid; + u8 cid; + u8 channel; /* From Radio MNGR */ +} __packed; + +struct wmi_rx_mgmt_packet_event { + struct wmi_rx_mgmt_info info; + u8 payload[0]; +} __packed; + +/* + * WMI_ECHO_RSP_EVENTID + */ +struct wmi_echo_event { + __le32 echoed_value; +} __packed; + +#endif /* __WILOCITY_WMI_H__ */ diff --git a/drivers/net/wireless/b43/b43.h b/drivers/net/wireless/b43/b43.h index b298e5d68be2..10e288d470e7 100644 --- a/drivers/net/wireless/b43/b43.h +++ b/drivers/net/wireless/b43/b43.h @@ -7,6 +7,7 @@ #include <linux/hw_random.h> #include <linux/bcma/bcma.h> #include <linux/ssb/ssb.h> +#include <linux/completion.h> #include <net/mac80211.h> #include "debugfs.h" @@ -722,6 +723,10 @@ enum b43_firmware_file_type { struct b43_request_fw_context { /* The device we are requesting the fw for. */ struct b43_wldev *dev; + /* a completion event structure needed if this call is asynchronous */ + struct completion fw_load_complete; + /* a pointer to the firmware object */ + const struct firmware *blob; /* The type of firmware to request. */ enum b43_firmware_file_type req_type; /* Error messages for each firmware type. */ diff --git a/drivers/net/wireless/b43/main.c b/drivers/net/wireless/b43/main.c index 16ab280359bd..806e34c19281 100644 --- a/drivers/net/wireless/b43/main.c +++ b/drivers/net/wireless/b43/main.c @@ -2088,11 +2088,18 @@ static void b43_print_fw_helptext(struct b43_wl *wl, bool error) b43warn(wl, text); } +static void b43_fw_cb(const struct firmware *firmware, void *context) +{ + struct b43_request_fw_context *ctx = context; + + ctx->blob = firmware; + complete(&ctx->fw_load_complete); +} + int b43_do_request_fw(struct b43_request_fw_context *ctx, const char *name, - struct b43_firmware_file *fw) + struct b43_firmware_file *fw, bool async) { - const struct firmware *blob; struct b43_fw_header *hdr; u32 size; int err; @@ -2131,11 +2138,31 @@ int b43_do_request_fw(struct b43_request_fw_context *ctx, B43_WARN_ON(1); return -ENOSYS; } - err = request_firmware(&blob, ctx->fwname, ctx->dev->dev->dev); + if (async) { + /* do this part asynchronously */ + init_completion(&ctx->fw_load_complete); + err = request_firmware_nowait(THIS_MODULE, 1, ctx->fwname, + ctx->dev->dev->dev, GFP_KERNEL, + ctx, b43_fw_cb); + if (err < 0) { + pr_err("Unable to load firmware\n"); + return err; + } + /* stall here until fw ready */ + wait_for_completion(&ctx->fw_load_complete); + if (ctx->blob) + goto fw_ready; + /* On some ARM systems, the async request will fail, but the next sync + * request works. For this reason, we dall through here + */ + } + err = request_firmware(&ctx->blob, ctx->fwname, + ctx->dev->dev->dev); if (err == -ENOENT) { snprintf(ctx->errors[ctx->req_type], sizeof(ctx->errors[ctx->req_type]), - "Firmware file \"%s\" not found\n", ctx->fwname); + "Firmware file \"%s\" not found\n", + ctx->fwname); return err; } else if (err) { snprintf(ctx->errors[ctx->req_type], @@ -2144,14 +2171,15 @@ int b43_do_request_fw(struct b43_request_fw_context *ctx, ctx->fwname, err); return err; } - if (blob->size < sizeof(struct b43_fw_header)) +fw_ready: + if (ctx->blob->size < sizeof(struct b43_fw_header)) goto err_format; - hdr = (struct b43_fw_header *)(blob->data); + hdr = (struct b43_fw_header *)(ctx->blob->data); switch (hdr->type) { case B43_FW_TYPE_UCODE: case B43_FW_TYPE_PCM: size = be32_to_cpu(hdr->size); - if (size != blob->size - sizeof(struct b43_fw_header)) + if (size != ctx->blob->size - sizeof(struct b43_fw_header)) goto err_format; /* fallthrough */ case B43_FW_TYPE_IV: @@ -2162,7 +2190,7 @@ int b43_do_request_fw(struct b43_request_fw_context *ctx, goto err_format; } - fw->data = blob; + fw->data = ctx->blob; fw->filename = name; fw->type = ctx->req_type; @@ -2172,7 +2200,7 @@ err_format: snprintf(ctx->errors[ctx->req_type], sizeof(ctx->errors[ctx->req_type]), "Firmware file \"%s\" format error.\n", ctx->fwname); - release_firmware(blob); + release_firmware(ctx->blob); return -EPROTO; } @@ -2223,7 +2251,7 @@ static int b43_try_request_fw(struct b43_request_fw_context *ctx) goto err_no_ucode; } } - err = b43_do_request_fw(ctx, filename, &fw->ucode); + err = b43_do_request_fw(ctx, filename, &fw->ucode, true); if (err) goto err_load; @@ -2235,7 +2263,7 @@ static int b43_try_request_fw(struct b43_request_fw_context *ctx) else goto err_no_pcm; fw->pcm_request_failed = false; - err = b43_do_request_fw(ctx, filename, &fw->pcm); + err = b43_do_request_fw(ctx, filename, &fw->pcm, false); if (err == -ENOENT) { /* We did not find a PCM file? Not fatal, but * core rev <= 10 must do without hwcrypto then. */ @@ -2296,7 +2324,7 @@ static int b43_try_request_fw(struct b43_request_fw_context *ctx) default: goto err_no_initvals; } - err = b43_do_request_fw(ctx, filename, &fw->initvals); + err = b43_do_request_fw(ctx, filename, &fw->initvals, false); if (err) goto err_load; @@ -2355,7 +2383,7 @@ static int b43_try_request_fw(struct b43_request_fw_context *ctx) default: goto err_no_initvals; } - err = b43_do_request_fw(ctx, filename, &fw->initvals_band); + err = b43_do_request_fw(ctx, filename, &fw->initvals_band, false); if (err) goto err_load; diff --git a/drivers/net/wireless/b43/main.h b/drivers/net/wireless/b43/main.h index 8c684cd33529..abac25ee958d 100644 --- a/drivers/net/wireless/b43/main.h +++ b/drivers/net/wireless/b43/main.h @@ -137,9 +137,8 @@ void b43_mac_phy_clock_set(struct b43_wldev *dev, bool on); struct b43_request_fw_context; -int b43_do_request_fw(struct b43_request_fw_context *ctx, - const char *name, - struct b43_firmware_file *fw); +int b43_do_request_fw(struct b43_request_fw_context *ctx, const char *name, + struct b43_firmware_file *fw, bool async); void b43_do_release_fw(struct b43_firmware_file *fw); #endif /* B43_MAIN_H_ */ diff --git a/drivers/net/wireless/brcm80211/brcmsmac/mac80211_if.c b/drivers/net/wireless/brcm80211/brcmsmac/mac80211_if.c index 1fbd8ecbe2ea..0f71d1d4339d 100644 --- a/drivers/net/wireless/brcm80211/brcmsmac/mac80211_if.c +++ b/drivers/net/wireless/brcm80211/brcmsmac/mac80211_if.c @@ -1407,9 +1407,10 @@ void brcms_add_timer(struct brcms_timer *t, uint ms, int periodic) #endif t->ms = ms; t->periodic = (bool) periodic; - t->set = true; - - atomic_inc(&t->wl->callbacks); + if (!t->set) { + t->set = true; + atomic_inc(&t->wl->callbacks); + } ieee80211_queue_delayed_work(hw, &t->dly_wrk, msecs_to_jiffies(ms)); } diff --git a/drivers/net/wireless/iwlegacy/3945-mac.c b/drivers/net/wireless/iwlegacy/3945-mac.c index d604b4036a76..3726cd6fcd75 100644 --- a/drivers/net/wireless/iwlegacy/3945-mac.c +++ b/drivers/net/wireless/iwlegacy/3945-mac.c @@ -3273,7 +3273,7 @@ il3945_store_measurement(struct device *d, struct device_attribute *attr, if (count) { char *p = buffer; - strncpy(buffer, buf, min(sizeof(buffer), count)); + strlcpy(buffer, buf, sizeof(buffer)); channel = simple_strtoul(p, NULL, 0); if (channel) params.channel = channel; diff --git a/drivers/net/wireless/iwlegacy/common.c b/drivers/net/wireless/iwlegacy/common.c index 7e16d10a7f14..90b8970eadf0 100644 --- a/drivers/net/wireless/iwlegacy/common.c +++ b/drivers/net/wireless/iwlegacy/common.c @@ -3958,17 +3958,21 @@ il_connection_init_rx_config(struct il_priv *il) memset(&il->staging, 0, sizeof(il->staging)); - if (!il->vif) { + switch (il->iw_mode) { + case NL80211_IFTYPE_UNSPECIFIED: il->staging.dev_type = RXON_DEV_TYPE_ESS; - } else if (il->vif->type == NL80211_IFTYPE_STATION) { + break; + case NL80211_IFTYPE_STATION: il->staging.dev_type = RXON_DEV_TYPE_ESS; il->staging.filter_flags = RXON_FILTER_ACCEPT_GRP_MSK; - } else if (il->vif->type == NL80211_IFTYPE_ADHOC) { + break; + case NL80211_IFTYPE_ADHOC: il->staging.dev_type = RXON_DEV_TYPE_IBSS; il->staging.flags = RXON_FLG_SHORT_PREAMBLE_MSK; il->staging.filter_flags = RXON_FILTER_BCON_AWARE_MSK | RXON_FILTER_ACCEPT_GRP_MSK; - } else { + break; + default: IL_ERR("Unsupported interface type %d\n", il->vif->type); return; } @@ -4550,8 +4554,7 @@ out: EXPORT_SYMBOL(il_mac_add_interface); static void -il_teardown_interface(struct il_priv *il, struct ieee80211_vif *vif, - bool mode_change) +il_teardown_interface(struct il_priv *il, struct ieee80211_vif *vif) { lockdep_assert_held(&il->mutex); @@ -4560,9 +4563,7 @@ il_teardown_interface(struct il_priv *il, struct ieee80211_vif *vif, il_force_scan_end(il); } - if (!mode_change) - il_set_mode(il); - + il_set_mode(il); } void @@ -4575,8 +4576,8 @@ il_mac_remove_interface(struct ieee80211_hw *hw, struct ieee80211_vif *vif) WARN_ON(il->vif != vif); il->vif = NULL; - - il_teardown_interface(il, vif, false); + il->iw_mode = NL80211_IFTYPE_UNSPECIFIED; + il_teardown_interface(il, vif); memset(il->bssid, 0, ETH_ALEN); D_MAC80211("leave\n"); @@ -4685,18 +4686,10 @@ il_mac_change_interface(struct ieee80211_hw *hw, struct ieee80211_vif *vif, } /* success */ - il_teardown_interface(il, vif, true); vif->type = newtype; vif->p2p = false; - err = il_set_mode(il); - WARN_ON(err); - /* - * We've switched internally, but submitting to the - * device may have failed for some reason. Mask this - * error, because otherwise mac80211 will not switch - * (and set the interface type back) and we'll be - * out of sync with it. - */ + il->iw_mode = newtype; + il_teardown_interface(il, vif); err = 0; out: diff --git a/drivers/net/wireless/iwlwifi/dvm/tx.c b/drivers/net/wireless/iwlwifi/dvm/tx.c index da21328ca8ed..31534f7c0548 100644 --- a/drivers/net/wireless/iwlwifi/dvm/tx.c +++ b/drivers/net/wireless/iwlwifi/dvm/tx.c @@ -1079,6 +1079,8 @@ static void iwlagn_set_tx_status(struct iwl_priv *priv, { u16 status = le16_to_cpu(tx_resp->status.status); + info->flags &= ~IEEE80211_TX_CTL_AMPDU; + info->status.rates[0].count = tx_resp->failure_frame + 1; info->flags |= iwl_tx_status_to_mac80211(status); iwlagn_hwrate_to_tx_control(priv, le32_to_cpu(tx_resp->rate_n_flags), @@ -1151,13 +1153,6 @@ int iwlagn_rx_reply_tx(struct iwl_priv *priv, struct iwl_rx_cmd_buffer *rxb, next_reclaimed = ssn; } - if (tid != IWL_TID_NON_QOS) { - priv->tid_data[sta_id][tid].next_reclaimed = - next_reclaimed; - IWL_DEBUG_TX_REPLY(priv, "Next reclaimed packet:%d\n", - next_reclaimed); - } - iwl_trans_reclaim(priv->trans, txq_id, ssn, &skbs); iwlagn_check_ratid_empty(priv, sta_id, tid); @@ -1208,11 +1203,28 @@ int iwlagn_rx_reply_tx(struct iwl_priv *priv, struct iwl_rx_cmd_buffer *rxb, if (!is_agg) iwlagn_non_agg_tx_status(priv, ctx, hdr->addr1); + /* + * W/A for FW bug - the seq_ctl isn't updated when the + * queues are flushed. Fetch it from the packet itself + */ + if (!is_agg && status == TX_STATUS_FAIL_FIFO_FLUSHED) { + next_reclaimed = le16_to_cpu(hdr->seq_ctrl); + next_reclaimed = + SEQ_TO_SN(next_reclaimed + 0x10); + } + is_offchannel_skb = (info->flags & IEEE80211_TX_CTL_TX_OFFCHAN); freed++; } + if (tid != IWL_TID_NON_QOS) { + priv->tid_data[sta_id][tid].next_reclaimed = + next_reclaimed; + IWL_DEBUG_TX_REPLY(priv, "Next reclaimed packet:%d\n", + next_reclaimed); + } + WARN_ON(!is_agg && freed != 1); /* diff --git a/drivers/net/wireless/iwlwifi/pcie/rx.c b/drivers/net/wireless/iwlwifi/pcie/rx.c index dad4c4aad91f..8389cd38338b 100644 --- a/drivers/net/wireless/iwlwifi/pcie/rx.c +++ b/drivers/net/wireless/iwlwifi/pcie/rx.c @@ -1166,6 +1166,7 @@ static irqreturn_t iwl_pcie_isr(int irq, void *data) else if (test_bit(STATUS_INT_ENABLED, &trans_pcie->status) && !trans_pcie->inta) iwl_enable_interrupts(trans); + return IRQ_HANDLED; none: /* re-enable interrupts here since we don't have anything to service. */ diff --git a/drivers/net/wireless/mwifiex/cfg80211.c b/drivers/net/wireless/mwifiex/cfg80211.c index a875499f8945..cdb11b3964e2 100644 --- a/drivers/net/wireless/mwifiex/cfg80211.c +++ b/drivers/net/wireless/mwifiex/cfg80211.c @@ -1459,7 +1459,7 @@ mwifiex_cfg80211_assoc(struct mwifiex_private *priv, size_t ssid_len, u8 *ssid, struct cfg80211_ssid req_ssid; int ret, auth_type = 0; struct cfg80211_bss *bss = NULL; - u8 is_scanning_required = 0, config_bands = 0; + u8 is_scanning_required = 0; memset(&req_ssid, 0, sizeof(struct cfg80211_ssid)); @@ -1478,19 +1478,6 @@ mwifiex_cfg80211_assoc(struct mwifiex_private *priv, size_t ssid_len, u8 *ssid, /* disconnect before try to associate */ mwifiex_deauthenticate(priv, NULL); - if (channel) { - if (mode == NL80211_IFTYPE_STATION) { - if (channel->band == IEEE80211_BAND_2GHZ) - config_bands = BAND_B | BAND_G | BAND_GN; - else - config_bands = BAND_A | BAND_AN; - - if (!((config_bands | priv->adapter->fw_bands) & - ~priv->adapter->fw_bands)) - priv->adapter->config_bands = config_bands; - } - } - /* As this is new association, clear locally stored * keys and security related flags */ priv->sec_info.wpa_enabled = false; @@ -1707,9 +1694,9 @@ static int mwifiex_set_ibss_params(struct mwifiex_private *priv, if (cfg80211_get_chandef_type(¶ms->chandef) != NL80211_CHAN_NO_HT) - config_bands |= BAND_GN; + config_bands |= BAND_G | BAND_GN; } else { - if (cfg80211_get_chandef_type(¶ms->chandef) != + if (cfg80211_get_chandef_type(¶ms->chandef) == NL80211_CHAN_NO_HT) config_bands = BAND_A; else diff --git a/drivers/net/wireless/mwifiex/pcie.c b/drivers/net/wireless/mwifiex/pcie.c index 13fbc4eb1595..b879e1338a54 100644 --- a/drivers/net/wireless/mwifiex/pcie.c +++ b/drivers/net/wireless/mwifiex/pcie.c @@ -161,7 +161,7 @@ static int mwifiex_pcie_suspend(struct pci_dev *pdev, pm_message_t state) if (pdev) { card = (struct pcie_service_card *) pci_get_drvdata(pdev); - if (!card || card->adapter) { + if (!card || !card->adapter) { pr_err("Card or adapter structure is not valid\n"); return 0; } diff --git a/drivers/net/wireless/mwifiex/sta_ioctl.c b/drivers/net/wireless/mwifiex/sta_ioctl.c index cb682561c438..f542bb8ccbc8 100644 --- a/drivers/net/wireless/mwifiex/sta_ioctl.c +++ b/drivers/net/wireless/mwifiex/sta_ioctl.c @@ -56,7 +56,6 @@ int mwifiex_copy_mcast_addr(struct mwifiex_multicast_list *mlist, */ int mwifiex_wait_queue_complete(struct mwifiex_adapter *adapter) { - bool cancel_flag = false; int status; struct cmd_ctrl_node *cmd_queued; @@ -70,14 +69,11 @@ int mwifiex_wait_queue_complete(struct mwifiex_adapter *adapter) atomic_inc(&adapter->cmd_pending); /* Wait for completion */ - wait_event_interruptible(adapter->cmd_wait_q.wait, - *(cmd_queued->condition)); - if (!*(cmd_queued->condition)) - cancel_flag = true; - - if (cancel_flag) { - mwifiex_cancel_pending_ioctl(adapter); - dev_dbg(adapter->dev, "cmd cancel\n"); + status = wait_event_interruptible(adapter->cmd_wait_q.wait, + *(cmd_queued->condition)); + if (status) { + dev_err(adapter->dev, "cmd_wait_q terminated: %d\n", status); + return status; } status = adapter->cmd_wait_q.status; @@ -287,6 +283,20 @@ int mwifiex_bss_start(struct mwifiex_private *priv, struct cfg80211_bss *bss, if (ret) goto done; + if (bss_desc) { + u8 config_bands = 0; + + if (mwifiex_band_to_radio_type((u8) bss_desc->bss_band) + == HostCmd_SCAN_RADIO_TYPE_BG) + config_bands = BAND_B | BAND_G | BAND_GN; + else + config_bands = BAND_A | BAND_AN; + + if (!((config_bands | adapter->fw_bands) & + ~adapter->fw_bands)) + adapter->config_bands = config_bands; + } + ret = mwifiex_check_network_compatibility(priv, bss_desc); if (ret) goto done; @@ -496,8 +506,11 @@ int mwifiex_enable_hs(struct mwifiex_adapter *adapter) return false; } - wait_event_interruptible(adapter->hs_activate_wait_q, - adapter->hs_activate_wait_q_woken); + if (wait_event_interruptible(adapter->hs_activate_wait_q, + adapter->hs_activate_wait_q_woken)) { + dev_err(adapter->dev, "hs_activate_wait_q terminated\n"); + return false; + } return true; } diff --git a/drivers/net/wireless/mwl8k.c b/drivers/net/wireless/mwl8k.c index f221b95b90b3..83564d36e801 100644 --- a/drivers/net/wireless/mwl8k.c +++ b/drivers/net/wireless/mwl8k.c @@ -4250,9 +4250,11 @@ static int mwl8k_cmd_update_stadb_add(struct ieee80211_hw *hw, p->amsdu_enabled = 0; rc = mwl8k_post_cmd(hw, &cmd->header); + if (!rc) + rc = p->station_id; kfree(cmd); - return rc ? rc : p->station_id; + return rc; } static int mwl8k_cmd_update_stadb_del(struct ieee80211_hw *hw, diff --git a/drivers/net/wireless/rtlwifi/Kconfig b/drivers/net/wireless/rtlwifi/Kconfig index 21b1bbb93a7e..b80bc4612581 100644 --- a/drivers/net/wireless/rtlwifi/Kconfig +++ b/drivers/net/wireless/rtlwifi/Kconfig @@ -57,12 +57,12 @@ config RTL8192CU config RTLWIFI tristate - depends on RTL8192CE || RTL8192CU || RTL8192SE || RTL8192DE + depends on RTL8192CE || RTL8192CU || RTL8192SE || RTL8192DE || RTL8723AE default m config RTLWIFI_DEBUG bool "Additional debugging output" - depends on RTL8192CE || RTL8192CU || RTL8192SE || RTL8192DE + depends on RTL8192CE || RTL8192CU || RTL8192SE || RTL8192DE || RTL8723AE default y config RTL8192C_COMMON diff --git a/drivers/net/wireless/rtlwifi/rtl8192c/phy_common.c b/drivers/net/wireless/rtlwifi/rtl8192c/phy_common.c index 1d5d3604e3e0..246e5352f2e1 100644 --- a/drivers/net/wireless/rtlwifi/rtl8192c/phy_common.c +++ b/drivers/net/wireless/rtlwifi/rtl8192c/phy_common.c @@ -692,7 +692,7 @@ u8 rtl92c_phy_sw_chnl(struct ieee80211_hw *hw) if (!(is_hal_stop(rtlhal)) && !(RT_CANNOT_IO(hw))) { rtl92c_phy_sw_chnl_callback(hw); RT_TRACE(rtlpriv, COMP_CHAN, DBG_LOUD, - "sw_chnl_inprogress false schdule workitem\n"); + "sw_chnl_inprogress false schedule workitem\n"); rtlphy->sw_chnl_inprogress = false; } else { RT_TRACE(rtlpriv, COMP_CHAN, DBG_LOUD, diff --git a/drivers/net/wireless/rtlwifi/rtl8723ae/phy.c b/drivers/net/wireless/rtlwifi/rtl8723ae/phy.c index 39cc7938eedf..3d8536bb0d2b 100644 --- a/drivers/net/wireless/rtlwifi/rtl8723ae/phy.c +++ b/drivers/net/wireless/rtlwifi/rtl8723ae/phy.c @@ -1106,7 +1106,7 @@ u8 rtl8723ae_phy_sw_chnl(struct ieee80211_hw *hw) if (!(is_hal_stop(rtlhal)) && !(RT_CANNOT_IO(hw))) { rtl8723ae_phy_sw_chnl_callback(hw); RT_TRACE(rtlpriv, COMP_CHAN, DBG_LOUD, - "sw_chnl_inprogress false schdule workitem\n"); + "sw_chnl_inprogress false schedule workitem\n"); rtlphy->sw_chnl_inprogress = false; } else { RT_TRACE(rtlpriv, COMP_CHAN, DBG_LOUD, diff --git a/drivers/pci/hotplug/pciehp.h b/drivers/pci/hotplug/pciehp.h index 26ffd3e3fb74..2c113de94323 100644 --- a/drivers/pci/hotplug/pciehp.h +++ b/drivers/pci/hotplug/pciehp.h @@ -44,7 +44,6 @@ extern bool pciehp_poll_mode; extern int pciehp_poll_time; extern bool pciehp_debug; extern bool pciehp_force; -extern struct workqueue_struct *pciehp_wq; #define dbg(format, arg...) \ do { \ @@ -78,6 +77,7 @@ struct slot { struct hotplug_slot *hotplug_slot; struct delayed_work work; /* work for button event */ struct mutex lock; + struct workqueue_struct *wq; }; struct event_info { diff --git a/drivers/pci/hotplug/pciehp_core.c b/drivers/pci/hotplug/pciehp_core.c index 916bf4f53aba..939bd1d4b5b1 100644 --- a/drivers/pci/hotplug/pciehp_core.c +++ b/drivers/pci/hotplug/pciehp_core.c @@ -42,7 +42,6 @@ bool pciehp_debug; bool pciehp_poll_mode; int pciehp_poll_time; bool pciehp_force; -struct workqueue_struct *pciehp_wq; #define DRIVER_VERSION "0.4" #define DRIVER_AUTHOR "Dan Zink <dan.zink@compaq.com>, Greg Kroah-Hartman <greg@kroah.com>, Dely Sy <dely.l.sy@intel.com>" @@ -340,18 +339,13 @@ static int __init pcied_init(void) { int retval = 0; - pciehp_wq = alloc_workqueue("pciehp", 0, 0); - if (!pciehp_wq) - return -ENOMEM; - pciehp_firmware_init(); retval = pcie_port_service_register(&hpdriver_portdrv); dbg("pcie_port_service_register = %d\n", retval); info(DRIVER_DESC " version: " DRIVER_VERSION "\n"); - if (retval) { - destroy_workqueue(pciehp_wq); + if (retval) dbg("Failure to register service\n"); - } + return retval; } @@ -359,7 +353,6 @@ static void __exit pcied_cleanup(void) { dbg("unload_pciehpd()\n"); pcie_port_service_unregister(&hpdriver_portdrv); - destroy_workqueue(pciehp_wq); info(DRIVER_DESC " version: " DRIVER_VERSION " unloaded\n"); } diff --git a/drivers/pci/hotplug/pciehp_ctrl.c b/drivers/pci/hotplug/pciehp_ctrl.c index 27f44295a657..38f018679175 100644 --- a/drivers/pci/hotplug/pciehp_ctrl.c +++ b/drivers/pci/hotplug/pciehp_ctrl.c @@ -49,7 +49,7 @@ static int queue_interrupt_event(struct slot *p_slot, u32 event_type) info->p_slot = p_slot; INIT_WORK(&info->work, interrupt_event_handler); - queue_work(pciehp_wq, &info->work); + queue_work(p_slot->wq, &info->work); return 0; } @@ -344,7 +344,7 @@ void pciehp_queue_pushbutton_work(struct work_struct *work) kfree(info); goto out; } - queue_work(pciehp_wq, &info->work); + queue_work(p_slot->wq, &info->work); out: mutex_unlock(&p_slot->lock); } @@ -377,7 +377,7 @@ static void handle_button_press_event(struct slot *p_slot) if (ATTN_LED(ctrl)) pciehp_set_attention_status(p_slot, 0); - queue_delayed_work(pciehp_wq, &p_slot->work, 5*HZ); + queue_delayed_work(p_slot->wq, &p_slot->work, 5*HZ); break; case BLINKINGOFF_STATE: case BLINKINGON_STATE: @@ -439,7 +439,7 @@ static void handle_surprise_event(struct slot *p_slot) else p_slot->state = POWERON_STATE; - queue_work(pciehp_wq, &info->work); + queue_work(p_slot->wq, &info->work); } static void interrupt_event_handler(struct work_struct *work) diff --git a/drivers/pci/hotplug/pciehp_hpc.c b/drivers/pci/hotplug/pciehp_hpc.c index 13b2eaf7ba43..5127f3f41821 100644 --- a/drivers/pci/hotplug/pciehp_hpc.c +++ b/drivers/pci/hotplug/pciehp_hpc.c @@ -773,23 +773,32 @@ static void pcie_shutdown_notification(struct controller *ctrl) static int pcie_init_slot(struct controller *ctrl) { struct slot *slot; + char name[32]; slot = kzalloc(sizeof(*slot), GFP_KERNEL); if (!slot) return -ENOMEM; + snprintf(name, sizeof(name), "pciehp-%u", PSN(ctrl)); + slot->wq = alloc_workqueue(name, 0, 0); + if (!slot->wq) + goto abort; + slot->ctrl = ctrl; mutex_init(&slot->lock); INIT_DELAYED_WORK(&slot->work, pciehp_queue_pushbutton_work); ctrl->slot = slot; return 0; +abort: + kfree(slot); + return -ENOMEM; } static void pcie_cleanup_slot(struct controller *ctrl) { struct slot *slot = ctrl->slot; cancel_delayed_work(&slot->work); - flush_workqueue(pciehp_wq); + destroy_workqueue(slot->wq); kfree(slot); } diff --git a/drivers/pci/hotplug/shpchp.h b/drivers/pci/hotplug/shpchp.h index ca64932e658b..b849f995075a 100644 --- a/drivers/pci/hotplug/shpchp.h +++ b/drivers/pci/hotplug/shpchp.h @@ -46,8 +46,6 @@ extern bool shpchp_poll_mode; extern int shpchp_poll_time; extern bool shpchp_debug; -extern struct workqueue_struct *shpchp_wq; -extern struct workqueue_struct *shpchp_ordered_wq; #define dbg(format, arg...) \ do { \ @@ -91,6 +89,7 @@ struct slot { struct list_head slot_list; struct delayed_work work; /* work for button event */ struct mutex lock; + struct workqueue_struct *wq; u8 hp_slot; }; diff --git a/drivers/pci/hotplug/shpchp_core.c b/drivers/pci/hotplug/shpchp_core.c index b6de307248e4..3100c52c837c 100644 --- a/drivers/pci/hotplug/shpchp_core.c +++ b/drivers/pci/hotplug/shpchp_core.c @@ -39,8 +39,6 @@ bool shpchp_debug; bool shpchp_poll_mode; int shpchp_poll_time; -struct workqueue_struct *shpchp_wq; -struct workqueue_struct *shpchp_ordered_wq; #define DRIVER_VERSION "0.4" #define DRIVER_AUTHOR "Dan Zink <dan.zink@compaq.com>, Greg Kroah-Hartman <greg@kroah.com>, Dely Sy <dely.l.sy@intel.com>" @@ -129,6 +127,14 @@ static int init_slots(struct controller *ctrl) slot->device = ctrl->slot_device_offset + i; slot->hpc_ops = ctrl->hpc_ops; slot->number = ctrl->first_slot + (ctrl->slot_num_inc * i); + + snprintf(name, sizeof(name), "shpchp-%d", slot->number); + slot->wq = alloc_workqueue(name, 0, 0); + if (!slot->wq) { + retval = -ENOMEM; + goto error_info; + } + mutex_init(&slot->lock); INIT_DELAYED_WORK(&slot->work, shpchp_queue_pushbutton_work); @@ -148,7 +154,7 @@ static int init_slots(struct controller *ctrl) if (retval) { ctrl_err(ctrl, "pci_hp_register failed with error %d\n", retval); - goto error_info; + goto error_slotwq; } get_power_status(hotplug_slot, &info->power_status); @@ -160,6 +166,8 @@ static int init_slots(struct controller *ctrl) } return 0; +error_slotwq: + destroy_workqueue(slot->wq); error_info: kfree(info); error_hpslot: @@ -180,8 +188,7 @@ void cleanup_slots(struct controller *ctrl) slot = list_entry(tmp, struct slot, slot_list); list_del(&slot->slot_list); cancel_delayed_work(&slot->work); - flush_workqueue(shpchp_wq); - flush_workqueue(shpchp_ordered_wq); + destroy_workqueue(slot->wq); pci_hp_deregister(slot->hotplug_slot); } } @@ -364,25 +371,12 @@ static struct pci_driver shpc_driver = { static int __init shpcd_init(void) { - int retval = 0; - - shpchp_wq = alloc_ordered_workqueue("shpchp", 0); - if (!shpchp_wq) - return -ENOMEM; - - shpchp_ordered_wq = alloc_ordered_workqueue("shpchp_ordered", 0); - if (!shpchp_ordered_wq) { - destroy_workqueue(shpchp_wq); - return -ENOMEM; - } + int retval; retval = pci_register_driver(&shpc_driver); dbg("%s: pci_register_driver = %d\n", __func__, retval); info(DRIVER_DESC " version: " DRIVER_VERSION "\n"); - if (retval) { - destroy_workqueue(shpchp_ordered_wq); - destroy_workqueue(shpchp_wq); - } + return retval; } @@ -390,8 +384,6 @@ static void __exit shpcd_cleanup(void) { dbg("unload_shpchpd()\n"); pci_unregister_driver(&shpc_driver); - destroy_workqueue(shpchp_ordered_wq); - destroy_workqueue(shpchp_wq); info(DRIVER_DESC " version: " DRIVER_VERSION " unloaded\n"); } diff --git a/drivers/pci/hotplug/shpchp_ctrl.c b/drivers/pci/hotplug/shpchp_ctrl.c index f9b5a52e4115..58499277903a 100644 --- a/drivers/pci/hotplug/shpchp_ctrl.c +++ b/drivers/pci/hotplug/shpchp_ctrl.c @@ -51,7 +51,7 @@ static int queue_interrupt_event(struct slot *p_slot, u32 event_type) info->p_slot = p_slot; INIT_WORK(&info->work, interrupt_event_handler); - queue_work(shpchp_wq, &info->work); + queue_work(p_slot->wq, &info->work); return 0; } @@ -453,7 +453,7 @@ void shpchp_queue_pushbutton_work(struct work_struct *work) kfree(info); goto out; } - queue_work(shpchp_ordered_wq, &info->work); + queue_work(p_slot->wq, &info->work); out: mutex_unlock(&p_slot->lock); } @@ -501,7 +501,7 @@ static void handle_button_press_event(struct slot *p_slot) p_slot->hpc_ops->green_led_blink(p_slot); p_slot->hpc_ops->set_attention_status(p_slot, 0); - queue_delayed_work(shpchp_wq, &p_slot->work, 5*HZ); + queue_delayed_work(p_slot->wq, &p_slot->work, 5*HZ); break; case BLINKINGOFF_STATE: case BLINKINGON_STATE: diff --git a/drivers/pci/iov.c b/drivers/pci/iov.c index bafd2bbcaf65..c18e5bf444fa 100644 --- a/drivers/pci/iov.c +++ b/drivers/pci/iov.c @@ -739,7 +739,7 @@ EXPORT_SYMBOL_GPL(pci_num_vf); /** * pci_sriov_set_totalvfs -- reduce the TotalVFs available * @dev: the PCI PF device - * numvfs: number that should be used for TotalVFs supported + * @numvfs: number that should be used for TotalVFs supported * * Should be called from PF driver's probe routine with * device's mutex held. diff --git a/drivers/pci/pcie/Kconfig b/drivers/pci/pcie/Kconfig index 6c8bc5809787..fde4a32a0295 100644 --- a/drivers/pci/pcie/Kconfig +++ b/drivers/pci/pcie/Kconfig @@ -82,4 +82,4 @@ endchoice config PCIE_PME def_bool y - depends on PCIEPORTBUS && PM_RUNTIME && EXPERIMENTAL && ACPI + depends on PCIEPORTBUS && PM_RUNTIME && ACPI diff --git a/drivers/pci/pcie/aer/aerdrv_core.c b/drivers/pci/pcie/aer/aerdrv_core.c index 421bbc5fee32..564d97f94b6c 100644 --- a/drivers/pci/pcie/aer/aerdrv_core.c +++ b/drivers/pci/pcie/aer/aerdrv_core.c @@ -630,6 +630,7 @@ static void aer_recover_work_func(struct work_struct *work) continue; } do_recovery(pdev, entry.severity); + pci_dev_put(pdev); } } #endif diff --git a/drivers/pci/pcie/aspm.c b/drivers/pci/pcie/aspm.c index b52630b8eada..8474b6a4fc9b 100644 --- a/drivers/pci/pcie/aspm.c +++ b/drivers/pci/pcie/aspm.c @@ -771,6 +771,9 @@ void pcie_clear_aspm(struct pci_bus *bus) { struct pci_dev *child; + if (aspm_force) + return; + /* * Clear any ASPM setup that the firmware has carried out on this bus */ diff --git a/drivers/pinctrl/Kconfig b/drivers/pinctrl/Kconfig index c31aeb01bb00..efaecefe3f8c 100644 --- a/drivers/pinctrl/Kconfig +++ b/drivers/pinctrl/Kconfig @@ -181,7 +181,6 @@ config PINCTRL_COH901 config PINCTRL_SAMSUNG bool - depends on OF && GPIOLIB select PINMUX select PINCONF diff --git a/drivers/pinctrl/mvebu/pinctrl-dove.c b/drivers/pinctrl/mvebu/pinctrl-dove.c index 69aba3697287..428ea96a94d3 100644 --- a/drivers/pinctrl/mvebu/pinctrl-dove.c +++ b/drivers/pinctrl/mvebu/pinctrl-dove.c @@ -588,7 +588,7 @@ static int dove_pinctrl_probe(struct platform_device *pdev) { const struct of_device_id *match = of_match_device(dove_pinctrl_of_match, &pdev->dev); - pdev->dev.platform_data = match->data; + pdev->dev.platform_data = (void *)match->data; /* * General MPP Configuration Register is part of pdma registers. diff --git a/drivers/pinctrl/mvebu/pinctrl-kirkwood.c b/drivers/pinctrl/mvebu/pinctrl-kirkwood.c index f12084e18057..cdd483df673e 100644 --- a/drivers/pinctrl/mvebu/pinctrl-kirkwood.c +++ b/drivers/pinctrl/mvebu/pinctrl-kirkwood.c @@ -66,9 +66,9 @@ static struct mvebu_mpp_mode mv88f6xxx_mpp_modes[] = { MPP_VAR_FUNCTION(0x5, "sata0", "act", V(0, 1, 1, 1, 1, 0)), MPP_VAR_FUNCTION(0xb, "lcd", "vsync", V(0, 0, 0, 0, 1, 0))), MPP_MODE(6, - MPP_VAR_FUNCTION(0x0, "sysrst", "out", V(1, 1, 1, 1, 1, 1)), - MPP_VAR_FUNCTION(0x1, "spi", "mosi", V(1, 1, 1, 1, 1, 1)), - MPP_VAR_FUNCTION(0x2, "ptp", "trig", V(1, 1, 1, 1, 0, 0))), + MPP_VAR_FUNCTION(0x1, "sysrst", "out", V(1, 1, 1, 1, 1, 1)), + MPP_VAR_FUNCTION(0x2, "spi", "mosi", V(1, 1, 1, 1, 1, 1)), + MPP_VAR_FUNCTION(0x3, "ptp", "trig", V(1, 1, 1, 1, 0, 0))), MPP_MODE(7, MPP_VAR_FUNCTION(0x0, "gpo", NULL, V(1, 1, 1, 1, 1, 1)), MPP_VAR_FUNCTION(0x1, "pex", "rsto", V(1, 1, 1, 1, 0, 1)), @@ -458,7 +458,7 @@ static int kirkwood_pinctrl_probe(struct platform_device *pdev) { const struct of_device_id *match = of_match_device(kirkwood_pinctrl_of_match, &pdev->dev); - pdev->dev.platform_data = match->data; + pdev->dev.platform_data = (void *)match->data; return mvebu_pinctrl_probe(pdev); } diff --git a/drivers/pinctrl/pinctrl-exynos5440.c b/drivers/pinctrl/pinctrl-exynos5440.c index de05b64f0da6..142729914c34 100644 --- a/drivers/pinctrl/pinctrl-exynos5440.c +++ b/drivers/pinctrl/pinctrl-exynos5440.c @@ -599,7 +599,7 @@ static int exynos5440_gpio_direction_output(struct gpio_chip *gc, unsigned offse } /* parse the pin numbers listed in the 'samsung,exynos5440-pins' property */ -static int __init exynos5440_pinctrl_parse_dt_pins(struct platform_device *pdev, +static int exynos5440_pinctrl_parse_dt_pins(struct platform_device *pdev, struct device_node *cfg_np, unsigned int **pin_list, unsigned int *npins) { @@ -630,7 +630,7 @@ static int __init exynos5440_pinctrl_parse_dt_pins(struct platform_device *pdev, * Parse the information about all the available pin groups and pin functions * from device node of the pin-controller. */ -static int __init exynos5440_pinctrl_parse_dt(struct platform_device *pdev, +static int exynos5440_pinctrl_parse_dt(struct platform_device *pdev, struct exynos5440_pinctrl_priv_data *priv) { struct device *dev = &pdev->dev; @@ -723,7 +723,7 @@ static int __init exynos5440_pinctrl_parse_dt(struct platform_device *pdev, } /* register the pinctrl interface with the pinctrl subsystem */ -static int __init exynos5440_pinctrl_register(struct platform_device *pdev, +static int exynos5440_pinctrl_register(struct platform_device *pdev, struct exynos5440_pinctrl_priv_data *priv) { struct device *dev = &pdev->dev; @@ -798,7 +798,7 @@ static int __init exynos5440_pinctrl_register(struct platform_device *pdev, } /* register the gpiolib interface with the gpiolib subsystem */ -static int __init exynos5440_gpiolib_register(struct platform_device *pdev, +static int exynos5440_gpiolib_register(struct platform_device *pdev, struct exynos5440_pinctrl_priv_data *priv) { struct gpio_chip *gc; @@ -831,7 +831,7 @@ static int __init exynos5440_gpiolib_register(struct platform_device *pdev, } /* unregister the gpiolib interface with the gpiolib subsystem */ -static int __init exynos5440_gpiolib_unregister(struct platform_device *pdev, +static int exynos5440_gpiolib_unregister(struct platform_device *pdev, struct exynos5440_pinctrl_priv_data *priv) { int ret = gpiochip_remove(priv->gc); diff --git a/drivers/pinctrl/pinctrl-mxs.c b/drivers/pinctrl/pinctrl-mxs.c index dd227d21dcf2..23af9f1f9c35 100644 --- a/drivers/pinctrl/pinctrl-mxs.c +++ b/drivers/pinctrl/pinctrl-mxs.c @@ -146,7 +146,7 @@ free: static void mxs_dt_free_map(struct pinctrl_dev *pctldev, struct pinctrl_map *map, unsigned num_maps) { - int i; + u32 i; for (i = 0; i < num_maps; i++) { if (map[i].type == PIN_MAP_TYPE_MUX_GROUP) @@ -203,7 +203,7 @@ static int mxs_pinctrl_enable(struct pinctrl_dev *pctldev, unsigned selector, void __iomem *reg; u8 bank, shift; u16 pin; - int i; + u32 i; for (i = 0; i < g->npins; i++) { bank = PINID_TO_BANK(g->pins[i]); @@ -256,7 +256,7 @@ static int mxs_pinconf_group_set(struct pinctrl_dev *pctldev, void __iomem *reg; u8 ma, vol, pull, bank, shift; u16 pin; - int i; + u32 i; ma = CONFIG_TO_MA(config); vol = CONFIG_TO_VOL(config); @@ -345,8 +345,7 @@ static int mxs_pinctrl_parse_group(struct platform_device *pdev, const char *propname = "fsl,pinmux-ids"; char *group; int length = strlen(np->name) + SUFFIX_LEN; - int i; - u32 val; + u32 val, i; group = devm_kzalloc(&pdev->dev, length, GFP_KERNEL); if (!group) diff --git a/drivers/pinctrl/pinctrl-nomadik.c b/drivers/pinctrl/pinctrl-nomadik.c index 1bb16ffb4e41..5767b18ebdff 100644 --- a/drivers/pinctrl/pinctrl-nomadik.c +++ b/drivers/pinctrl/pinctrl-nomadik.c @@ -676,7 +676,7 @@ int nmk_gpio_set_mode(int gpio, int gpio_mode) } EXPORT_SYMBOL(nmk_gpio_set_mode); -static int nmk_prcm_gpiocr_get_mode(struct pinctrl_dev *pctldev, int gpio) +static int __maybe_unused nmk_prcm_gpiocr_get_mode(struct pinctrl_dev *pctldev, int gpio) { int i; u16 reg; diff --git a/drivers/pinctrl/pinctrl-single.c b/drivers/pinctrl/pinctrl-single.c index f6a360b86eb6..5c32e880bcb2 100644 --- a/drivers/pinctrl/pinctrl-single.c +++ b/drivers/pinctrl/pinctrl-single.c @@ -30,7 +30,6 @@ #define PCS_MUX_BITS_NAME "pinctrl-single,bits" #define PCS_REG_NAME_LEN ((sizeof(unsigned long) * 2) + 1) #define PCS_OFF_DISABLED ~0U -#define PCS_MAX_GPIO_VALUES 2 /** * struct pcs_pingroup - pingroups for a function @@ -78,16 +77,6 @@ struct pcs_function { }; /** - * struct pcs_gpio_range - pinctrl gpio range - * @range: subrange of the GPIO number space - * @gpio_func: gpio function value in the pinmux register - */ -struct pcs_gpio_range { - struct pinctrl_gpio_range range; - int gpio_func; -}; - -/** * struct pcs_data - wrapper for data needed by pinctrl framework * @pa: pindesc array * @cur: index to current element @@ -414,26 +403,9 @@ static void pcs_disable(struct pinctrl_dev *pctldev, unsigned fselector, } static int pcs_request_gpio(struct pinctrl_dev *pctldev, - struct pinctrl_gpio_range *range, unsigned pin) + struct pinctrl_gpio_range *range, unsigned offset) { - struct pcs_device *pcs = pinctrl_dev_get_drvdata(pctldev); - struct pcs_gpio_range *gpio = NULL; - int end, mux_bytes; - unsigned data; - - gpio = container_of(range, struct pcs_gpio_range, range); - end = range->pin_base + range->npins - 1; - if (pin < range->pin_base || pin > end) { - dev_err(pctldev->dev, - "pin %d isn't in the range of %d to %d\n", - pin, range->pin_base, end); - return -EINVAL; - } - mux_bytes = pcs->width / BITS_PER_BYTE; - data = pcs->read(pcs->base + pin * mux_bytes) & ~pcs->fmask; - data |= gpio->gpio_func; - pcs->write(data, pcs->base + pin * mux_bytes); - return 0; + return -ENOTSUPP; } static struct pinmux_ops pcs_pinmux_ops = { @@ -907,49 +879,6 @@ static void pcs_free_resources(struct pcs_device *pcs) static struct of_device_id pcs_of_match[]; -static int pcs_add_gpio_range(struct device_node *node, struct pcs_device *pcs) -{ - struct pcs_gpio_range *gpio; - struct device_node *child; - struct resource r; - const char name[] = "pinctrl-single"; - u32 gpiores[PCS_MAX_GPIO_VALUES]; - int ret, i = 0, mux_bytes = 0; - - for_each_child_of_node(node, child) { - ret = of_address_to_resource(child, 0, &r); - if (ret < 0) - continue; - memset(gpiores, 0, sizeof(u32) * PCS_MAX_GPIO_VALUES); - ret = of_property_read_u32_array(child, "pinctrl-single,gpio", - gpiores, PCS_MAX_GPIO_VALUES); - if (ret < 0) - continue; - gpio = devm_kzalloc(pcs->dev, sizeof(*gpio), GFP_KERNEL); - if (!gpio) { - dev_err(pcs->dev, "failed to allocate pcs gpio\n"); - return -ENOMEM; - } - gpio->range.name = devm_kzalloc(pcs->dev, sizeof(name), - GFP_KERNEL); - if (!gpio->range.name) { - dev_err(pcs->dev, "failed to allocate range name\n"); - return -ENOMEM; - } - memcpy((char *)gpio->range.name, name, sizeof(name)); - - gpio->range.id = i++; - gpio->range.base = gpiores[0]; - gpio->gpio_func = gpiores[1]; - mux_bytes = pcs->width / BITS_PER_BYTE; - gpio->range.pin_base = (r.start - pcs->res->start) / mux_bytes; - gpio->range.npins = (r.end - r.start) / mux_bytes + 1; - - pinctrl_add_gpio_range(pcs->pctl, &gpio->range); - } - return 0; -} - static int pcs_probe(struct platform_device *pdev) { struct device_node *np = pdev->dev.of_node; @@ -1046,10 +975,6 @@ static int pcs_probe(struct platform_device *pdev) goto free; } - ret = pcs_add_gpio_range(np, pcs); - if (ret < 0) - goto free; - dev_info(pcs->dev, "%i pins at pa %p size %u\n", pcs->desc.npins, pcs->base, pcs->size); diff --git a/drivers/platform/x86/ibm_rtl.c b/drivers/platform/x86/ibm_rtl.c index 7481146a5b47..97c2be195efc 100644 --- a/drivers/platform/x86/ibm_rtl.c +++ b/drivers/platform/x86/ibm_rtl.c @@ -244,7 +244,7 @@ static int __init ibm_rtl_init(void) { if (force) pr_warn("module loaded by force\n"); /* first ensure that we are running on IBM HW */ - else if (efi_enabled || !dmi_check_system(ibm_rtl_dmi_table)) + else if (efi_enabled(EFI_BOOT) || !dmi_check_system(ibm_rtl_dmi_table)) return -ENODEV; /* Get the address for the Extended BIOS Data Area */ diff --git a/drivers/platform/x86/samsung-laptop.c b/drivers/platform/x86/samsung-laptop.c index 71623a2ff3e8..d1f030053176 100644 --- a/drivers/platform/x86/samsung-laptop.c +++ b/drivers/platform/x86/samsung-laptop.c @@ -26,6 +26,7 @@ #include <linux/seq_file.h> #include <linux/debugfs.h> #include <linux/ctype.h> +#include <linux/efi.h> #include <acpi/video.h> /* @@ -1544,6 +1545,9 @@ static int __init samsung_init(void) struct samsung_laptop *samsung; int ret; + if (efi_enabled(EFI_BOOT)) + return -ENODEV; + quirks = &samsung_unknown; if (!force && !dmi_check_system(samsung_dmi_table)) return -ENODEV; diff --git a/drivers/regulator/core.c b/drivers/regulator/core.c index 0f65b246cc0c..278584302f2d 100644 --- a/drivers/regulator/core.c +++ b/drivers/regulator/core.c @@ -1885,9 +1885,15 @@ int regulator_can_change_voltage(struct regulator *regulator) struct regulator_dev *rdev = regulator->rdev; if (rdev->constraints && - rdev->constraints->valid_ops_mask & REGULATOR_CHANGE_VOLTAGE && - (rdev->desc->n_voltages - rdev->desc->linear_min_sel) > 1) - return 1; + (rdev->constraints->valid_ops_mask & REGULATOR_CHANGE_VOLTAGE)) { + if (rdev->desc->n_voltages - rdev->desc->linear_min_sel > 1) + return 1; + + if (rdev->desc->continuous_voltage_range && + rdev->constraints->min_uV && rdev->constraints->max_uV && + rdev->constraints->min_uV != rdev->constraints->max_uV) + return 1; + } return 0; } @@ -3315,7 +3321,8 @@ static void rdev_init_debugfs(struct regulator_dev *rdev) * @config: runtime configuration for regulator * * Called by regulator drivers to register a regulator. - * Returns 0 on success. + * Returns a valid pointer to struct regulator_dev on success + * or an ERR_PTR() on error. */ struct regulator_dev * regulator_register(const struct regulator_desc *regulator_desc, diff --git a/drivers/regulator/dbx500-prcmu.c b/drivers/regulator/dbx500-prcmu.c index 261f3d2299bc..89bd2faaef8c 100644 --- a/drivers/regulator/dbx500-prcmu.c +++ b/drivers/regulator/dbx500-prcmu.c @@ -14,6 +14,7 @@ #include <linux/debugfs.h> #include <linux/seq_file.h> #include <linux/slab.h> +#include <linux/module.h> #include "dbx500-prcmu.h" diff --git a/drivers/regulator/max8997.c b/drivers/regulator/max8997.c index df0eafb0dc7e..02be7fcae32f 100644 --- a/drivers/regulator/max8997.c +++ b/drivers/regulator/max8997.c @@ -71,26 +71,26 @@ struct voltage_map_desc { int step; }; -/* Voltage maps in mV */ +/* Voltage maps in uV */ static const struct voltage_map_desc ldo_voltage_map_desc = { - .min = 800, .max = 3950, .step = 50, + .min = 800000, .max = 3950000, .step = 50000, }; /* LDO1 ~ 18, 21 all */ static const struct voltage_map_desc buck1245_voltage_map_desc = { - .min = 650, .max = 2225, .step = 25, + .min = 650000, .max = 2225000, .step = 25000, }; /* Buck1, 2, 4, 5 */ static const struct voltage_map_desc buck37_voltage_map_desc = { - .min = 750, .max = 3900, .step = 50, + .min = 750000, .max = 3900000, .step = 50000, }; /* Buck3, 7 */ -/* current map in mA */ +/* current map in uA */ static const struct voltage_map_desc charger_current_map_desc = { - .min = 200, .max = 950, .step = 50, + .min = 200000, .max = 950000, .step = 50000, }; static const struct voltage_map_desc topoff_current_map_desc = { - .min = 50, .max = 200, .step = 10, + .min = 50000, .max = 200000, .step = 10000, }; static const struct voltage_map_desc *reg_voltage_map[] = { @@ -194,7 +194,7 @@ static int max8997_list_voltage(struct regulator_dev *rdev, if (val > desc->max) return -EINVAL; - return val * 1000; + return val; } static int max8997_get_enable_register(struct regulator_dev *rdev, @@ -485,7 +485,6 @@ static int max8997_set_voltage_ldobuck(struct regulator_dev *rdev, { struct max8997_data *max8997 = rdev_get_drvdata(rdev); struct i2c_client *i2c = max8997->iodev->i2c; - int min_vol = min_uV / 1000, max_vol = max_uV / 1000; const struct voltage_map_desc *desc; int rid = rdev_get_id(rdev); int i, reg, shift, mask, ret; @@ -509,7 +508,7 @@ static int max8997_set_voltage_ldobuck(struct regulator_dev *rdev, desc = reg_voltage_map[rid]; - i = max8997_get_voltage_proper_val(desc, min_vol, max_vol); + i = max8997_get_voltage_proper_val(desc, min_uV, max_uV); if (i < 0) return i; @@ -557,7 +556,7 @@ static int max8997_set_voltage_ldobuck_time_sel(struct regulator_dev *rdev, case MAX8997_BUCK4: case MAX8997_BUCK5: return DIV_ROUND_UP(desc->step * (new_selector - old_selector), - max8997->ramp_delay); + max8997->ramp_delay * 1000); } return 0; @@ -656,7 +655,6 @@ static int max8997_set_voltage_buck(struct regulator_dev *rdev, const struct voltage_map_desc *desc; int new_val, new_idx, damage, tmp_val, tmp_idx, tmp_dmg; bool gpio_dvs_mode = false; - int min_vol = min_uV / 1000, max_vol = max_uV / 1000; if (rid < MAX8997_BUCK1 || rid > MAX8997_BUCK7) return -EINVAL; @@ -681,7 +679,7 @@ static int max8997_set_voltage_buck(struct regulator_dev *rdev, selector); desc = reg_voltage_map[rid]; - new_val = max8997_get_voltage_proper_val(desc, min_vol, max_vol); + new_val = max8997_get_voltage_proper_val(desc, min_uV, max_uV); if (new_val < 0) return new_val; @@ -1123,8 +1121,8 @@ static int max8997_pmic_probe(struct platform_device *pdev) max8997->buck1_vol[i] = ret = max8997_get_voltage_proper_val( &buck1245_voltage_map_desc, - pdata->buck1_voltage[i] / 1000, - pdata->buck1_voltage[i] / 1000 + + pdata->buck1_voltage[i], + pdata->buck1_voltage[i] + buck1245_voltage_map_desc.step); if (ret < 0) goto err_out; @@ -1132,8 +1130,8 @@ static int max8997_pmic_probe(struct platform_device *pdev) max8997->buck2_vol[i] = ret = max8997_get_voltage_proper_val( &buck1245_voltage_map_desc, - pdata->buck2_voltage[i] / 1000, - pdata->buck2_voltage[i] / 1000 + + pdata->buck2_voltage[i], + pdata->buck2_voltage[i] + buck1245_voltage_map_desc.step); if (ret < 0) goto err_out; @@ -1141,8 +1139,8 @@ static int max8997_pmic_probe(struct platform_device *pdev) max8997->buck5_vol[i] = ret = max8997_get_voltage_proper_val( &buck1245_voltage_map_desc, - pdata->buck5_voltage[i] / 1000, - pdata->buck5_voltage[i] / 1000 + + pdata->buck5_voltage[i], + pdata->buck5_voltage[i] + buck1245_voltage_map_desc.step); if (ret < 0) goto err_out; diff --git a/drivers/regulator/max8998.c b/drivers/regulator/max8998.c index b821d08eb64a..1f0df4046b86 100644 --- a/drivers/regulator/max8998.c +++ b/drivers/regulator/max8998.c @@ -51,39 +51,39 @@ struct voltage_map_desc { int step; }; -/* Voltage maps */ +/* Voltage maps in uV*/ static const struct voltage_map_desc ldo23_voltage_map_desc = { - .min = 800, .step = 50, .max = 1300, + .min = 800000, .step = 50000, .max = 1300000, }; static const struct voltage_map_desc ldo456711_voltage_map_desc = { - .min = 1600, .step = 100, .max = 3600, + .min = 1600000, .step = 100000, .max = 3600000, }; static const struct voltage_map_desc ldo8_voltage_map_desc = { - .min = 3000, .step = 100, .max = 3600, + .min = 3000000, .step = 100000, .max = 3600000, }; static const struct voltage_map_desc ldo9_voltage_map_desc = { - .min = 2800, .step = 100, .max = 3100, + .min = 2800000, .step = 100000, .max = 3100000, }; static const struct voltage_map_desc ldo10_voltage_map_desc = { - .min = 950, .step = 50, .max = 1300, + .min = 95000, .step = 50000, .max = 1300000, }; static const struct voltage_map_desc ldo1213_voltage_map_desc = { - .min = 800, .step = 100, .max = 3300, + .min = 800000, .step = 100000, .max = 3300000, }; static const struct voltage_map_desc ldo1415_voltage_map_desc = { - .min = 1200, .step = 100, .max = 3300, + .min = 1200000, .step = 100000, .max = 3300000, }; static const struct voltage_map_desc ldo1617_voltage_map_desc = { - .min = 1600, .step = 100, .max = 3600, + .min = 1600000, .step = 100000, .max = 3600000, }; static const struct voltage_map_desc buck12_voltage_map_desc = { - .min = 750, .step = 25, .max = 1525, + .min = 750000, .step = 25000, .max = 1525000, }; static const struct voltage_map_desc buck3_voltage_map_desc = { - .min = 1600, .step = 100, .max = 3600, + .min = 1600000, .step = 100000, .max = 3600000, }; static const struct voltage_map_desc buck4_voltage_map_desc = { - .min = 800, .step = 100, .max = 2300, + .min = 800000, .step = 100000, .max = 2300000, }; static const struct voltage_map_desc *ldo_voltage_map[] = { @@ -445,9 +445,9 @@ static int max8998_set_voltage_buck_time_sel(struct regulator_dev *rdev, if (max8998->iodev->type == TYPE_MAX8998 && !(val & MAX8998_ENRAMP)) return 0; - difference = (new_selector - old_selector) * desc->step; + difference = (new_selector - old_selector) * desc->step / 1000; if (difference > 0) - return difference / ((val & 0x0f) + 1); + return DIV_ROUND_UP(difference, (val & 0x0f) + 1); return 0; } @@ -702,7 +702,7 @@ static int max8998_pmic_probe(struct platform_device *pdev) i = 0; while (buck12_voltage_map_desc.min + buck12_voltage_map_desc.step*i - < (pdata->buck1_voltage1 / 1000)) + < pdata->buck1_voltage1) i++; max8998->buck1_vol[0] = i; ret = max8998_write_reg(i2c, MAX8998_REG_BUCK1_VOLTAGE1, i); @@ -713,7 +713,7 @@ static int max8998_pmic_probe(struct platform_device *pdev) i = 0; while (buck12_voltage_map_desc.min + buck12_voltage_map_desc.step*i - < (pdata->buck1_voltage2 / 1000)) + < pdata->buck1_voltage2) i++; max8998->buck1_vol[1] = i; @@ -725,7 +725,7 @@ static int max8998_pmic_probe(struct platform_device *pdev) i = 0; while (buck12_voltage_map_desc.min + buck12_voltage_map_desc.step*i - < (pdata->buck1_voltage3 / 1000)) + < pdata->buck1_voltage3) i++; max8998->buck1_vol[2] = i; @@ -737,7 +737,7 @@ static int max8998_pmic_probe(struct platform_device *pdev) i = 0; while (buck12_voltage_map_desc.min + buck12_voltage_map_desc.step*i - < (pdata->buck1_voltage4 / 1000)) + < pdata->buck1_voltage4) i++; max8998->buck1_vol[3] = i; @@ -763,7 +763,7 @@ static int max8998_pmic_probe(struct platform_device *pdev) i = 0; while (buck12_voltage_map_desc.min + buck12_voltage_map_desc.step*i - < (pdata->buck2_voltage1 / 1000)) + < pdata->buck2_voltage1) i++; max8998->buck2_vol[0] = i; ret = max8998_write_reg(i2c, MAX8998_REG_BUCK2_VOLTAGE1, i); @@ -774,7 +774,7 @@ static int max8998_pmic_probe(struct platform_device *pdev) i = 0; while (buck12_voltage_map_desc.min + buck12_voltage_map_desc.step*i - < (pdata->buck2_voltage2 / 1000)) + < pdata->buck2_voltage2) i++; max8998->buck2_vol[1] = i; ret = max8998_write_reg(i2c, MAX8998_REG_BUCK2_VOLTAGE2, i); @@ -792,8 +792,8 @@ static int max8998_pmic_probe(struct platform_device *pdev) int count = (desc->max - desc->min) / desc->step + 1; regulators[index].n_voltages = count; - regulators[index].min_uV = desc->min * 1000; - regulators[index].uV_step = desc->step * 1000; + regulators[index].min_uV = desc->min; + regulators[index].uV_step = desc->step; } config.dev = max8998->dev; diff --git a/drivers/regulator/s5m8767.c b/drivers/regulator/s5m8767.c index 9f991f2c525a..33b65c9ad5d5 100644 --- a/drivers/regulator/s5m8767.c +++ b/drivers/regulator/s5m8767.c @@ -214,7 +214,7 @@ static int s5m8767_reg_is_enabled(struct regulator_dev *rdev) struct s5m8767_info *s5m8767 = rdev_get_drvdata(rdev); int ret, reg; int mask = 0xc0, enable_ctrl; - u8 val; + unsigned int val; ret = s5m8767_get_register(rdev, ®, &enable_ctrl); if (ret == -EINVAL) @@ -306,7 +306,7 @@ static int s5m8767_get_voltage_sel(struct regulator_dev *rdev) struct s5m8767_info *s5m8767 = rdev_get_drvdata(rdev); int reg, mask, ret; int reg_id = rdev_get_id(rdev); - u8 val; + unsigned int val; ret = s5m8767_get_voltage_register(rdev, ®); if (ret) diff --git a/drivers/regulator/tps80031-regulator.c b/drivers/regulator/tps80031-regulator.c index b15d711bc8c6..9019d0e7ecb6 100644 --- a/drivers/regulator/tps80031-regulator.c +++ b/drivers/regulator/tps80031-regulator.c @@ -728,7 +728,7 @@ static int tps80031_regulator_probe(struct platform_device *pdev) } } rdev = regulator_register(&ri->rinfo->desc, &config); - if (IS_ERR_OR_NULL(rdev)) { + if (IS_ERR(rdev)) { dev_err(&pdev->dev, "register regulator failed %s\n", ri->rinfo->desc.name); diff --git a/drivers/rtc/rtc-da9055.c b/drivers/rtc/rtc-da9055.c index 96bafc5c3bf8..8f0dcfedb83c 100644 --- a/drivers/rtc/rtc-da9055.c +++ b/drivers/rtc/rtc-da9055.c @@ -227,7 +227,7 @@ static const struct rtc_class_ops da9055_rtc_ops = { .alarm_irq_enable = da9055_rtc_alarm_irq_enable, }; -static int __init da9055_rtc_device_init(struct da9055 *da9055, +static int da9055_rtc_device_init(struct da9055 *da9055, struct da9055_pdata *pdata) { int ret; diff --git a/drivers/rtc/rtc-isl1208.c b/drivers/rtc/rtc-isl1208.c index afb7cfa85ccc..c016ad81767a 100644 --- a/drivers/rtc/rtc-isl1208.c +++ b/drivers/rtc/rtc-isl1208.c @@ -506,6 +506,7 @@ isl1208_rtc_interrupt(int irq, void *data) { unsigned long timeout = jiffies + msecs_to_jiffies(1000); struct i2c_client *client = data; + struct rtc_device *rtc = i2c_get_clientdata(client); int handled = 0, sr, err; /* @@ -528,6 +529,8 @@ isl1208_rtc_interrupt(int irq, void *data) if (sr & ISL1208_REG_SR_ALM) { dev_dbg(&client->dev, "alarm!\n"); + rtc_update_irq(rtc, 1, RTC_IRQF | RTC_AF); + /* Clear the alarm */ sr &= ~ISL1208_REG_SR_ALM; sr = i2c_smbus_write_byte_data(client, ISL1208_REG_SR, sr); diff --git a/drivers/rtc/rtc-pl031.c b/drivers/rtc/rtc-pl031.c index 08378e3cc21c..10c1a3454e48 100644 --- a/drivers/rtc/rtc-pl031.c +++ b/drivers/rtc/rtc-pl031.c @@ -44,6 +44,7 @@ #define RTC_YMR 0x34 /* Year match register */ #define RTC_YLR 0x38 /* Year data load register */ +#define RTC_CR_EN (1 << 0) /* counter enable bit */ #define RTC_CR_CWEN (1 << 26) /* Clockwatch enable bit */ #define RTC_TCR_EN (1 << 1) /* Periodic timer enable bit */ @@ -320,7 +321,7 @@ static int pl031_probe(struct amba_device *adev, const struct amba_id *id) struct pl031_local *ldata; struct pl031_vendor_data *vendor = id->data; struct rtc_class_ops *ops = &vendor->ops; - unsigned long time; + unsigned long time, data; ret = amba_request_regions(adev, NULL); if (ret) @@ -345,10 +346,11 @@ static int pl031_probe(struct amba_device *adev, const struct amba_id *id) dev_dbg(&adev->dev, "designer ID = 0x%02x\n", amba_manf(adev)); dev_dbg(&adev->dev, "revision = 0x%01x\n", amba_rev(adev)); + data = readl(ldata->base + RTC_CR); /* Enable the clockwatch on ST Variants */ if (vendor->clockwatch) - writel(readl(ldata->base + RTC_CR) | RTC_CR_CWEN, - ldata->base + RTC_CR); + data |= RTC_CR_CWEN; + writel(data | RTC_CR_EN, ldata->base + RTC_CR); /* * On ST PL031 variants, the RTC reset value does not provide correct diff --git a/drivers/rtc/rtc-vt8500.c b/drivers/rtc/rtc-vt8500.c index 00c930f4b6f3..2730533e2d2d 100644 --- a/drivers/rtc/rtc-vt8500.c +++ b/drivers/rtc/rtc-vt8500.c @@ -137,7 +137,7 @@ static int vt8500_rtc_set_time(struct device *dev, struct rtc_time *tm) return -EINVAL; } - writel((bin2bcd(tm->tm_year - 100) << DATE_YEAR_S) + writel((bin2bcd(tm->tm_year % 100) << DATE_YEAR_S) | (bin2bcd(tm->tm_mon + 1) << DATE_MONTH_S) | (bin2bcd(tm->tm_mday)) | ((tm->tm_year >= 200) << DATE_CENTURY_S), diff --git a/drivers/s390/cio/chsc.c b/drivers/s390/cio/chsc.c index 68e80e2734a4..10729bbceced 100644 --- a/drivers/s390/cio/chsc.c +++ b/drivers/s390/cio/chsc.c @@ -283,7 +283,7 @@ struct chsc_sei_nt2_area { u8 ccdf[PAGE_SIZE - 24 - 56]; /* content-code dependent field */ } __packed; -#define CHSC_SEI_NT0 0ULL +#define CHSC_SEI_NT0 (1ULL << 63) #define CHSC_SEI_NT2 (1ULL << 61) struct chsc_sei { @@ -291,7 +291,8 @@ struct chsc_sei { u32 reserved1; u64 ntsm; /* notification type mask */ struct chsc_header response; - u32 reserved2; + u32 :24; + u8 nt; union { struct chsc_sei_nt0_area nt0_area; struct chsc_sei_nt2_area nt2_area; @@ -496,17 +497,17 @@ static int __chsc_process_crw(struct chsc_sei *sei, u64 ntsm) css_schedule_eval_all(); } - switch (sei->ntsm) { - case CHSC_SEI_NT0: + switch (sei->nt) { + case 0: chsc_process_sei_nt0(&sei->u.nt0_area); - return 1; - case CHSC_SEI_NT2: + break; + case 2: chsc_process_sei_nt2(&sei->u.nt2_area); - return 1; + break; default: - CIO_CRW_EVENT(2, "chsc: unhandled nt (nt=%08Lx)\n", - sei->ntsm); - return 0; + CIO_CRW_EVENT(2, "chsc: unhandled nt=%d\n", + sei->nt); + break; } } else { CIO_CRW_EVENT(2, "chsc: sei failed (rc=%04x)\n", @@ -537,15 +538,7 @@ static void chsc_process_crw(struct crw *crw0, struct crw *crw1, int overflow) sei = sei_page; CIO_TRACE_EVENT(2, "prcss"); - - /* - * The ntsm does not allow to select NT0 and NT2 together. We need to - * first check for NT2, than additionally for NT0... - */ -#ifdef CONFIG_PCI - if (!__chsc_process_crw(sei, CHSC_SEI_NT2)) -#endif - __chsc_process_crw(sei, CHSC_SEI_NT0); + __chsc_process_crw(sei, CHSC_SEI_NT0 | CHSC_SEI_NT2); } void chsc_chp_online(struct chp_id chpid) diff --git a/drivers/scsi/isci/init.c b/drivers/scsi/isci/init.c index d73fdcfeb45a..2839baa82a5a 100644 --- a/drivers/scsi/isci/init.c +++ b/drivers/scsi/isci/init.c @@ -633,7 +633,7 @@ static int isci_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id) return -ENOMEM; pci_set_drvdata(pdev, pci_info); - if (efi_enabled) + if (efi_enabled(EFI_RUNTIME_SERVICES)) orom = isci_get_efi_var(pdev); if (!orom) diff --git a/drivers/sh/clk/cpg.c b/drivers/sh/clk/cpg.c index 5aedcdf4ac5c..1ebe67cd1833 100644 --- a/drivers/sh/clk/cpg.c +++ b/drivers/sh/clk/cpg.c @@ -126,6 +126,12 @@ static int sh_clk_div_set_rate(struct clk *clk, unsigned long rate) static int sh_clk_div_enable(struct clk *clk) { + if (clk->div_mask == SH_CLK_DIV6_MSK) { + int ret = sh_clk_div_set_rate(clk, clk->rate); + if (ret < 0) + return ret; + } + sh_clk_write(sh_clk_read(clk) & ~CPG_CKSTP_BIT, clk); return 0; } diff --git a/drivers/staging/Kconfig b/drivers/staging/Kconfig index 329bdb42109f..eca907bf8b6d 100644 --- a/drivers/staging/Kconfig +++ b/drivers/staging/Kconfig @@ -114,8 +114,6 @@ source "drivers/staging/media/Kconfig" source "drivers/staging/net/Kconfig" -source "drivers/staging/omapdrm/Kconfig" - source "drivers/staging/android/Kconfig" source "drivers/staging/ozwpan/Kconfig" diff --git a/drivers/staging/Makefile b/drivers/staging/Makefile index c7ec486680f7..d810ed729add 100644 --- a/drivers/staging/Makefile +++ b/drivers/staging/Makefile @@ -49,7 +49,6 @@ obj-$(CONFIG_SPEAKUP) += speakup/ obj-$(CONFIG_TOUCHSCREEN_CLEARPAD_TM1217) += cptm1217/ obj-$(CONFIG_TOUCHSCREEN_SYNAPTICS_I2C_RMI4) += ste_rmi4/ obj-$(CONFIG_MFD_NVEC) += nvec/ -obj-$(CONFIG_DRM_OMAP) += omapdrm/ obj-$(CONFIG_ANDROID) += android/ obj-$(CONFIG_USB_WPAN_HCD) += ozwpan/ obj-$(CONFIG_USB_G_CCG) += ccg/ diff --git a/drivers/staging/comedi/Kconfig b/drivers/staging/comedi/Kconfig index 7de2a10213bd..36eec320569c 100644 --- a/drivers/staging/comedi/Kconfig +++ b/drivers/staging/comedi/Kconfig @@ -444,6 +444,7 @@ config COMEDI_ADQ12B config COMEDI_NI_AT_A2150 tristate "NI AT-A2150 ISA card support" + select COMEDI_FC depends on VIRT_TO_BUS ---help--- Enable support for National Instruments AT-A2150 cards diff --git a/drivers/staging/comedi/comedi_fops.c b/drivers/staging/comedi/comedi_fops.c index b7bba1790a20..9b038e4a7e71 100644 --- a/drivers/staging/comedi/comedi_fops.c +++ b/drivers/staging/comedi/comedi_fops.c @@ -1549,6 +1549,9 @@ static long comedi_unlocked_ioctl(struct file *file, unsigned int cmd, if (cmd == COMEDI_DEVCONFIG) { rc = do_devconfig_ioctl(dev, (struct comedi_devconfig __user *)arg); + if (rc == 0) + /* Evade comedi_auto_unconfig(). */ + dev_file_info->hardware_device = NULL; goto done; } diff --git a/drivers/staging/comedi/drivers/comedi_test.c b/drivers/staging/comedi/drivers/comedi_test.c index fb3d09323ba1..01de996239f1 100644 --- a/drivers/staging/comedi/drivers/comedi_test.c +++ b/drivers/staging/comedi/drivers/comedi_test.c @@ -345,7 +345,7 @@ static int waveform_ai_cancel(struct comedi_device *dev, struct waveform_private *devpriv = dev->private; devpriv->timer_running = 0; - del_timer(&devpriv->timer); + del_timer_sync(&devpriv->timer); return 0; } diff --git a/drivers/staging/comedi/drivers/ni_pcimio.c b/drivers/staging/comedi/drivers/ni_pcimio.c index aaac0b2cc9eb..fd1662b4175d 100644 --- a/drivers/staging/comedi/drivers/ni_pcimio.c +++ b/drivers/staging/comedi/drivers/ni_pcimio.c @@ -963,7 +963,7 @@ static const struct ni_board_struct ni_boards[] = { .ao_range_table = &range_ni_M_625x_ao, .reg_type = ni_reg_625x, .ao_unipolar = 0, - .ao_speed = 357, + .ao_speed = 350, .num_p0_dio_channels = 8, .caldac = {caldac_none}, .has_8255 = 0, @@ -982,7 +982,7 @@ static const struct ni_board_struct ni_boards[] = { .ao_range_table = &range_ni_M_625x_ao, .reg_type = ni_reg_625x, .ao_unipolar = 0, - .ao_speed = 357, + .ao_speed = 350, .num_p0_dio_channels = 8, .caldac = {caldac_none}, .has_8255 = 0, @@ -1001,7 +1001,7 @@ static const struct ni_board_struct ni_boards[] = { .ao_range_table = &range_ni_M_625x_ao, .reg_type = ni_reg_625x, .ao_unipolar = 0, - .ao_speed = 357, + .ao_speed = 350, .num_p0_dio_channels = 8, .caldac = {caldac_none}, .has_8255 = 0, @@ -1037,7 +1037,7 @@ static const struct ni_board_struct ni_boards[] = { .ao_range_table = &range_ni_M_625x_ao, .reg_type = ni_reg_625x, .ao_unipolar = 0, - .ao_speed = 357, + .ao_speed = 350, .num_p0_dio_channels = 32, .caldac = {caldac_none}, .has_8255 = 0, @@ -1056,7 +1056,7 @@ static const struct ni_board_struct ni_boards[] = { .ao_range_table = &range_ni_M_625x_ao, .reg_type = ni_reg_625x, .ao_unipolar = 0, - .ao_speed = 357, + .ao_speed = 350, .num_p0_dio_channels = 32, .caldac = {caldac_none}, .has_8255 = 0, @@ -1092,7 +1092,7 @@ static const struct ni_board_struct ni_boards[] = { .ao_range_table = &range_ni_M_628x_ao, .reg_type = ni_reg_628x, .ao_unipolar = 1, - .ao_speed = 357, + .ao_speed = 350, .num_p0_dio_channels = 8, .caldac = {caldac_none}, .has_8255 = 0, @@ -1111,7 +1111,7 @@ static const struct ni_board_struct ni_boards[] = { .ao_range_table = &range_ni_M_628x_ao, .reg_type = ni_reg_628x, .ao_unipolar = 1, - .ao_speed = 357, + .ao_speed = 350, .num_p0_dio_channels = 8, .caldac = {caldac_none}, .has_8255 = 0, @@ -1147,7 +1147,7 @@ static const struct ni_board_struct ni_boards[] = { .ao_range_table = &range_ni_M_628x_ao, .reg_type = ni_reg_628x, .ao_unipolar = 1, - .ao_speed = 357, + .ao_speed = 350, .num_p0_dio_channels = 32, .caldac = {caldac_none}, .has_8255 = 0, diff --git a/drivers/staging/fwserial/Kconfig b/drivers/staging/fwserial/Kconfig index 580406cb1808..b2f8331e4acf 100644 --- a/drivers/staging/fwserial/Kconfig +++ b/drivers/staging/fwserial/Kconfig @@ -3,7 +3,9 @@ config FIREWIRE_SERIAL depends on FIREWIRE help This enables TTY over IEEE 1394, providing high-speed serial - connectivity to cabled peers. + connectivity to cabled peers. This driver implements a + ad-hoc transport protocol and is currently limited to + Linux-to-Linux communication. To compile this driver as a module, say M here: the module will be called firewire-serial. diff --git a/drivers/staging/fwserial/TODO b/drivers/staging/fwserial/TODO index 726900548eae..8dae8fb25223 100644 --- a/drivers/staging/fwserial/TODO +++ b/drivers/staging/fwserial/TODO @@ -1,5 +1,5 @@ -TODOs ------ +TODOs prior to this driver moving out of staging +------------------------------------------------ 1. Implement retries for RCODE_BUSY, RCODE_NO_ACK and RCODE_SEND_ERROR - I/O is handled asynchronously which presents some issues when error conditions occur. @@ -11,17 +11,9 @@ TODOs -- Issues with firewire stack -- 1. This driver uses the same unregistered vendor id that the firewire core does (0xd00d1e). Perhaps this could be exposed as a define in - firewire-constants.h? -2. MAX_ASYNC_PAYLOAD needs to be publicly exposed by core/ohci - - otherwise how will this driver know the max size of address window to - open for one packet write? + firewire.h? 3. Maybe device_max_receive() and link_speed_to_max_payload() should be taken up by the firewire core? -4. To avoid dropping rx data while still limiting the maximum buffering, - the size of the AR context must be known. How to expose this to drivers? -5. Explore if bigger AR context will reduce RCODE_BUSY responses - (or auto-grow to certain max size -- but this would require major surgery - as the current AR is contiguously mapped) -- Issues with TTY core -- 1. Hack for alternate device name scheme diff --git a/drivers/staging/fwserial/fwserial.c b/drivers/staging/fwserial/fwserial.c index 61ee29083b26..d03a7f57e8d4 100644 --- a/drivers/staging/fwserial/fwserial.c +++ b/drivers/staging/fwserial/fwserial.c @@ -179,7 +179,7 @@ static void dump_profile(struct seq_file *m, struct stats *stats) /* Returns the max receive packet size for the given card */ static inline int device_max_receive(struct fw_device *fw_device) { - return 1 << (clamp_t(int, fw_device->max_rec, 8U, 13U) + 1); + return 1 << (clamp_t(int, fw_device->max_rec, 8U, 11U) + 1); } static void fwtty_log_tx_error(struct fwtty_port *port, int rcode) diff --git a/drivers/staging/fwserial/fwserial.h b/drivers/staging/fwserial/fwserial.h index 8b572edf9563..caa1c1ea82d5 100644 --- a/drivers/staging/fwserial/fwserial.h +++ b/drivers/staging/fwserial/fwserial.h @@ -374,10 +374,10 @@ static inline void fwtty_bind_console(struct fwtty_port *port, */ static inline int link_speed_to_max_payload(unsigned speed) { - static const int max_async[] = { 307, 614, 1229, 2458, 4916, 9832, }; - BUILD_BUG_ON(ARRAY_SIZE(max_async) - 1 != SCODE_3200); + static const int max_async[] = { 307, 614, 1229, 2458, }; + BUILD_BUG_ON(ARRAY_SIZE(max_async) - 1 != SCODE_800); - speed = clamp(speed, (unsigned) SCODE_100, (unsigned) SCODE_3200); + speed = clamp(speed, (unsigned) SCODE_100, (unsigned) SCODE_800); if (limit_bw) return max_async[speed]; else diff --git a/drivers/staging/iio/adc/mxs-lradc.c b/drivers/staging/iio/adc/mxs-lradc.c index fb31b457a56a..c5ceb9d90ea8 100644 --- a/drivers/staging/iio/adc/mxs-lradc.c +++ b/drivers/staging/iio/adc/mxs-lradc.c @@ -239,7 +239,7 @@ static irqreturn_t mxs_lradc_trigger_handler(int irq, void *p) struct mxs_lradc *lradc = iio_priv(iio); const uint32_t chan_value = LRADC_CH_ACCUMULATE | ((LRADC_DELAY_TIMER_LOOP - 1) << LRADC_CH_NUM_SAMPLES_OFFSET); - int i, j = 0; + unsigned int i, j = 0; for_each_set_bit(i, iio->active_scan_mask, iio->masklength) { lradc->buffer[j] = readl(lradc->base + LRADC_CH(j)); diff --git a/drivers/staging/iio/gyro/Kconfig b/drivers/staging/iio/gyro/Kconfig index ea295b25308c..87979a0d03a9 100644 --- a/drivers/staging/iio/gyro/Kconfig +++ b/drivers/staging/iio/gyro/Kconfig @@ -27,8 +27,8 @@ config ADIS16130 config ADIS16260 tristate "Analog Devices ADIS16260 Digital Gyroscope Sensor SPI driver" depends on SPI - select IIO_TRIGGER if IIO_BUFFER - select IIO_SW_RING if IIO_BUFFER + select IIO_ADIS_LIB + select IIO_ADIS_LIB_BUFFER if IIO_BUFFER help Say yes here to build support for Analog Devices ADIS16260 ADIS16265 ADIS16250 ADIS16255 and ADIS16251 programmable digital gyroscope sensors. diff --git a/drivers/staging/iio/gyro/adis16080_core.c b/drivers/staging/iio/gyro/adis16080_core.c index 3525a68d6a75..41d7350d030f 100644 --- a/drivers/staging/iio/gyro/adis16080_core.c +++ b/drivers/staging/iio/gyro/adis16080_core.c @@ -69,7 +69,7 @@ static int adis16080_spi_read(struct iio_dev *indio_dev, ret = spi_read(st->us, st->buf, 2); if (ret == 0) - *val = ((st->buf[0] & 0xF) << 8) | st->buf[1]; + *val = sign_extend32(((st->buf[0] & 0xF) << 8) | st->buf[1], 11); mutex_unlock(&st->buf_lock); return ret; diff --git a/drivers/staging/imx-drm/imx-drm-core.c b/drivers/staging/imx-drm/imx-drm-core.c index ecf0f44bc70e..cec19f1cf56c 100644 --- a/drivers/staging/imx-drm/imx-drm-core.c +++ b/drivers/staging/imx-drm/imx-drm-core.c @@ -584,7 +584,6 @@ int imx_drm_add_encoder(struct drm_encoder *encoder, ret = imx_drm_encoder_register(imx_drm_encoder); if (ret) { - kfree(imx_drm_encoder); ret = -ENOMEM; goto err_register; } diff --git a/drivers/staging/imx-drm/ipu-v3/ipu-common.c b/drivers/staging/imx-drm/ipu-v3/ipu-common.c index 677e665ca86d..f7059cddd7fd 100644 --- a/drivers/staging/imx-drm/ipu-v3/ipu-common.c +++ b/drivers/staging/imx-drm/ipu-v3/ipu-common.c @@ -1104,7 +1104,9 @@ static int ipu_probe(struct platform_device *pdev) if (ret) goto out_failed_irq; - ipu_reset(ipu); + ret = ipu_reset(ipu); + if (ret) + goto out_failed_reset; /* Set MCU_T to divide MCU access window into 2 */ ipu_cm_write(ipu, 0x00400000L | (IPU_MCU_T_DEFAULT << 18), @@ -1129,6 +1131,7 @@ failed_add_clients: ipu_submodules_exit(ipu); failed_submodules_init: ipu_irq_exit(ipu); +out_failed_reset: out_failed_irq: clk_disable_unprepare(ipu->clk); failed_clk_get: diff --git a/drivers/staging/imx-drm/ipuv3-crtc.c b/drivers/staging/imx-drm/ipuv3-crtc.c index 1892006526b5..4b3a019409b5 100644 --- a/drivers/staging/imx-drm/ipuv3-crtc.c +++ b/drivers/staging/imx-drm/ipuv3-crtc.c @@ -452,7 +452,7 @@ static int ipu_get_resources(struct ipu_crtc *ipu_crtc, int ret; ipu_crtc->ipu_ch = ipu_idmac_get(ipu, pdata->dma[0]); - if (IS_ERR_OR_NULL(ipu_crtc->ipu_ch)) { + if (IS_ERR(ipu_crtc->ipu_ch)) { ret = PTR_ERR(ipu_crtc->ipu_ch); goto err_out; } @@ -472,7 +472,7 @@ static int ipu_get_resources(struct ipu_crtc *ipu_crtc, if (pdata->dp >= 0) { ipu_crtc->dp = ipu_dp_get(ipu, pdata->dp); if (IS_ERR(ipu_crtc->dp)) { - ret = PTR_ERR(ipu_crtc->ipu_ch); + ret = PTR_ERR(ipu_crtc->dp); goto err_out; } } @@ -548,6 +548,8 @@ static int ipu_drm_probe(struct platform_device *pdev) ipu_crtc->dev = &pdev->dev; ret = ipu_crtc_init(ipu_crtc, pdata); + if (ret) + return ret; platform_set_drvdata(pdev, ipu_crtc); diff --git a/drivers/staging/omapdrm/TODO b/drivers/staging/omapdrm/TODO deleted file mode 100644 index 938c7888ca31..000000000000 --- a/drivers/staging/omapdrm/TODO +++ /dev/null @@ -1,35 +0,0 @@ -TODO -. add video decode/encode support (via syslink3 + codec-engine) - . NOTE: with dmabuf this probably could be split into different driver - so perhaps this TODO doesn't belong here -. where should we do eviction (detatch_pages())? We aren't necessarily - accessing the pages via a GART, so maybe we need some other threshold - to put a cap on the # of pages that can be pin'd. (It is mostly only - of interest in case you have a swap partition/file.. which a lot of - these devices do not.. but it doesn't hurt for the driver to do the - right thing anyways.) - . Use mm_shrinker to trigger unpinning pages. Need to figure out how - to handle next issue first (I think?) - . Note TTM already has some mm_shrinker stuff.. maybe an argument to - move to TTM? Or maybe something that could be factored out in common? -. GEM/shmem backed pages can have existing mappings (kernel linear map, - etc..), which isn't really ideal. -. Revisit GEM sync object infrastructure.. TTM has some framework for this - already. Possibly this could be refactored out and made more common? - There should be some way to do this with less wheel-reinvention. -. Review DSS vs KMS mismatches. The omap_dss_device is sort of part encoder, - part connector. Which results in a bit of duct tape to fwd calls from - encoder to connector. Possibly this could be done a bit better. -. Solve PM sequencing on resume. DMM/TILER must be reloaded before any - access is made from any component in the system. Which means on suspend - CRTC's should be disabled, and on resume the LUT should be reprogrammed - before CRTC's are re-enabled, to prevent DSS from trying to DMA from a - buffer mapped in DMM/TILER before LUT is reloaded. - -Userspace: -. git://github.com/robclark/xf86-video-omap.git - -Currently tested on -. OMAP3530 beagleboard -. OMAP4430 pandaboard -. OMAP4460 pandaboard diff --git a/drivers/staging/omapdrm/omap_crtc.c b/drivers/staging/omapdrm/omap_crtc.c deleted file mode 100644 index d87bd84257bd..000000000000 --- a/drivers/staging/omapdrm/omap_crtc.c +++ /dev/null @@ -1,270 +0,0 @@ -/* - * drivers/staging/omapdrm/omap_crtc.c - * - * Copyright (C) 2011 Texas Instruments - * Author: Rob Clark <rob@ti.com> - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 as published by - * the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License along with - * this program. If not, see <http://www.gnu.org/licenses/>. - */ - -#include "omap_drv.h" - -#include <drm/drm_mode.h> -#include "drm_crtc.h" -#include "drm_crtc_helper.h" - -#define to_omap_crtc(x) container_of(x, struct omap_crtc, base) - -struct omap_crtc { - struct drm_crtc base; - struct drm_plane *plane; - const char *name; - int id; - - /* if there is a pending flip, these will be non-null: */ - struct drm_pending_vblank_event *event; - struct drm_framebuffer *old_fb; -}; - -static void omap_crtc_destroy(struct drm_crtc *crtc) -{ - struct omap_crtc *omap_crtc = to_omap_crtc(crtc); - omap_crtc->plane->funcs->destroy(omap_crtc->plane); - drm_crtc_cleanup(crtc); - kfree(omap_crtc); -} - -static void omap_crtc_dpms(struct drm_crtc *crtc, int mode) -{ - struct omap_drm_private *priv = crtc->dev->dev_private; - struct omap_crtc *omap_crtc = to_omap_crtc(crtc); - int i; - - WARN_ON(omap_plane_dpms(omap_crtc->plane, mode)); - - for (i = 0; i < priv->num_planes; i++) { - struct drm_plane *plane = priv->planes[i]; - if (plane->crtc == crtc) - WARN_ON(omap_plane_dpms(plane, mode)); - } -} - -static bool omap_crtc_mode_fixup(struct drm_crtc *crtc, - const struct drm_display_mode *mode, - struct drm_display_mode *adjusted_mode) -{ - return true; -} - -static int omap_crtc_mode_set(struct drm_crtc *crtc, - struct drm_display_mode *mode, - struct drm_display_mode *adjusted_mode, - int x, int y, - struct drm_framebuffer *old_fb) -{ - struct omap_crtc *omap_crtc = to_omap_crtc(crtc); - struct drm_plane *plane = omap_crtc->plane; - - return omap_plane_mode_set(plane, crtc, crtc->fb, - 0, 0, mode->hdisplay, mode->vdisplay, - x << 16, y << 16, - mode->hdisplay << 16, mode->vdisplay << 16); -} - -static void omap_crtc_prepare(struct drm_crtc *crtc) -{ - struct omap_crtc *omap_crtc = to_omap_crtc(crtc); - DBG("%s", omap_crtc->name); - omap_crtc_dpms(crtc, DRM_MODE_DPMS_OFF); -} - -static void omap_crtc_commit(struct drm_crtc *crtc) -{ - struct omap_crtc *omap_crtc = to_omap_crtc(crtc); - DBG("%s", omap_crtc->name); - omap_crtc_dpms(crtc, DRM_MODE_DPMS_ON); -} - -static int omap_crtc_mode_set_base(struct drm_crtc *crtc, int x, int y, - struct drm_framebuffer *old_fb) -{ - struct omap_crtc *omap_crtc = to_omap_crtc(crtc); - struct drm_plane *plane = omap_crtc->plane; - struct drm_display_mode *mode = &crtc->mode; - - return plane->funcs->update_plane(plane, crtc, crtc->fb, - 0, 0, mode->hdisplay, mode->vdisplay, - x << 16, y << 16, - mode->hdisplay << 16, mode->vdisplay << 16); -} - -static void omap_crtc_load_lut(struct drm_crtc *crtc) -{ -} - -static void vblank_cb(void *arg) -{ - static uint32_t sequence; - struct drm_crtc *crtc = arg; - struct drm_device *dev = crtc->dev; - struct omap_crtc *omap_crtc = to_omap_crtc(crtc); - struct drm_pending_vblank_event *event = omap_crtc->event; - unsigned long flags; - struct timeval now; - - WARN_ON(!event); - - omap_crtc->event = NULL; - - /* wakeup userspace */ - if (event) { - do_gettimeofday(&now); - - spin_lock_irqsave(&dev->event_lock, flags); - /* TODO: we can't yet use the vblank time accounting, - * because omapdss lower layer is the one that knows - * the irq # and registers the handler, which more or - * less defeats how drm_irq works.. for now just fake - * the sequence number and use gettimeofday.. - * - event->event.sequence = drm_vblank_count_and_time( - dev, omap_crtc->id, &now); - */ - event->event.sequence = sequence++; - event->event.tv_sec = now.tv_sec; - event->event.tv_usec = now.tv_usec; - list_add_tail(&event->base.link, - &event->base.file_priv->event_list); - wake_up_interruptible(&event->base.file_priv->event_wait); - spin_unlock_irqrestore(&dev->event_lock, flags); - } -} - -static void page_flip_cb(void *arg) -{ - struct drm_crtc *crtc = arg; - struct omap_crtc *omap_crtc = to_omap_crtc(crtc); - struct drm_framebuffer *old_fb = omap_crtc->old_fb; - struct drm_gem_object *bo; - - omap_crtc->old_fb = NULL; - - omap_crtc_mode_set_base(crtc, crtc->x, crtc->y, old_fb); - - /* really we'd like to setup the callback atomically w/ setting the - * new scanout buffer to avoid getting stuck waiting an extra vblank - * cycle.. for now go for correctness and later figure out speed.. - */ - omap_plane_on_endwin(omap_crtc->plane, vblank_cb, crtc); - - bo = omap_framebuffer_bo(crtc->fb, 0); - drm_gem_object_unreference_unlocked(bo); -} - -static int omap_crtc_page_flip_locked(struct drm_crtc *crtc, - struct drm_framebuffer *fb, - struct drm_pending_vblank_event *event) -{ - struct drm_device *dev = crtc->dev; - struct omap_crtc *omap_crtc = to_omap_crtc(crtc); - struct drm_gem_object *bo; - - DBG("%d -> %d", crtc->fb ? crtc->fb->base.id : -1, fb->base.id); - - if (omap_crtc->event) { - dev_err(dev->dev, "already a pending flip\n"); - return -EINVAL; - } - - omap_crtc->old_fb = crtc->fb; - omap_crtc->event = event; - crtc->fb = fb; - - /* - * Hold a reference temporarily until the crtc is updated - * and takes the reference to the bo. This avoids it - * getting freed from under us: - */ - bo = omap_framebuffer_bo(fb, 0); - drm_gem_object_reference(bo); - - omap_gem_op_async(bo, OMAP_GEM_READ, page_flip_cb, crtc); - - return 0; -} - -static int omap_crtc_set_property(struct drm_crtc *crtc, - struct drm_property *property, uint64_t val) -{ - struct omap_crtc *omap_crtc = to_omap_crtc(crtc); - struct omap_drm_private *priv = crtc->dev->dev_private; - - if (property == priv->rotation_prop) { - crtc->invert_dimensions = - !!(val & ((1LL << DRM_ROTATE_90) | (1LL << DRM_ROTATE_270))); - } - - return omap_plane_set_property(omap_crtc->plane, property, val); -} - -static const struct drm_crtc_funcs omap_crtc_funcs = { - .set_config = drm_crtc_helper_set_config, - .destroy = omap_crtc_destroy, - .page_flip = omap_crtc_page_flip_locked, - .set_property = omap_crtc_set_property, -}; - -static const struct drm_crtc_helper_funcs omap_crtc_helper_funcs = { - .dpms = omap_crtc_dpms, - .mode_fixup = omap_crtc_mode_fixup, - .mode_set = omap_crtc_mode_set, - .prepare = omap_crtc_prepare, - .commit = omap_crtc_commit, - .mode_set_base = omap_crtc_mode_set_base, - .load_lut = omap_crtc_load_lut, -}; - -/* initialize crtc */ -struct drm_crtc *omap_crtc_init(struct drm_device *dev, - struct omap_overlay *ovl, int id) -{ - struct drm_crtc *crtc = NULL; - struct omap_crtc *omap_crtc = kzalloc(sizeof(*omap_crtc), GFP_KERNEL); - - DBG("%s", ovl->name); - - if (!omap_crtc) { - dev_err(dev->dev, "could not allocate CRTC\n"); - goto fail; - } - - crtc = &omap_crtc->base; - - omap_crtc->plane = omap_plane_init(dev, ovl, (1 << id), true); - omap_crtc->plane->crtc = crtc; - omap_crtc->name = ovl->name; - omap_crtc->id = id; - - drm_crtc_init(dev, crtc, &omap_crtc_funcs); - drm_crtc_helper_add(crtc, &omap_crtc_helper_funcs); - - omap_plane_install_properties(omap_crtc->plane, &crtc->base); - - return crtc; - -fail: - if (crtc) - omap_crtc_destroy(crtc); - - return NULL; -} diff --git a/drivers/staging/omapdrm/omap_drm.h b/drivers/staging/omapdrm/omap_drm.h deleted file mode 100644 index f0ac34a8973e..000000000000 --- a/drivers/staging/omapdrm/omap_drm.h +++ /dev/null @@ -1,123 +0,0 @@ -/* - * include/drm/omap_drm.h - * - * Copyright (C) 2011 Texas Instruments - * Author: Rob Clark <rob@ti.com> - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 as published by - * the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License along with - * this program. If not, see <http://www.gnu.org/licenses/>. - */ - -#ifndef __OMAP_DRM_H__ -#define __OMAP_DRM_H__ - -#include <drm/drm.h> - -/* Please note that modifications to all structs defined here are - * subject to backwards-compatibility constraints. - */ - -#define OMAP_PARAM_CHIPSET_ID 1 /* ie. 0x3430, 0x4430, etc */ - -struct drm_omap_param { - uint64_t param; /* in */ - uint64_t value; /* in (set_param), out (get_param) */ -}; - -#define OMAP_BO_SCANOUT 0x00000001 /* scanout capable (phys contiguous) */ -#define OMAP_BO_CACHE_MASK 0x00000006 /* cache type mask, see cache modes */ -#define OMAP_BO_TILED_MASK 0x00000f00 /* tiled mapping mask, see tiled modes */ - -/* cache modes */ -#define OMAP_BO_CACHED 0x00000000 /* default */ -#define OMAP_BO_WC 0x00000002 /* write-combine */ -#define OMAP_BO_UNCACHED 0x00000004 /* strongly-ordered (uncached) */ - -/* tiled modes */ -#define OMAP_BO_TILED_8 0x00000100 -#define OMAP_BO_TILED_16 0x00000200 -#define OMAP_BO_TILED_32 0x00000300 -#define OMAP_BO_TILED (OMAP_BO_TILED_8 | OMAP_BO_TILED_16 | OMAP_BO_TILED_32) - -union omap_gem_size { - uint32_t bytes; /* (for non-tiled formats) */ - struct { - uint16_t width; - uint16_t height; - } tiled; /* (for tiled formats) */ -}; - -struct drm_omap_gem_new { - union omap_gem_size size; /* in */ - uint32_t flags; /* in */ - uint32_t handle; /* out */ - uint32_t __pad; -}; - -/* mask of operations: */ -enum omap_gem_op { - OMAP_GEM_READ = 0x01, - OMAP_GEM_WRITE = 0x02, -}; - -struct drm_omap_gem_cpu_prep { - uint32_t handle; /* buffer handle (in) */ - uint32_t op; /* mask of omap_gem_op (in) */ -}; - -struct drm_omap_gem_cpu_fini { - uint32_t handle; /* buffer handle (in) */ - uint32_t op; /* mask of omap_gem_op (in) */ - /* TODO maybe here we pass down info about what regions are touched - * by sw so we can be clever about cache ops? For now a placeholder, - * set to zero and we just do full buffer flush.. - */ - uint32_t nregions; - uint32_t __pad; -}; - -struct drm_omap_gem_info { - uint32_t handle; /* buffer handle (in) */ - uint32_t pad; - uint64_t offset; /* mmap offset (out) */ - /* note: in case of tiled buffers, the user virtual size can be - * different from the physical size (ie. how many pages are needed - * to back the object) which is returned in DRM_IOCTL_GEM_OPEN.. - * This size here is the one that should be used if you want to - * mmap() the buffer: - */ - uint32_t size; /* virtual size for mmap'ing (out) */ - uint32_t __pad; -}; - -#define DRM_OMAP_GET_PARAM 0x00 -#define DRM_OMAP_SET_PARAM 0x01 -/* placeholder for plugin-api -#define DRM_OMAP_GET_BASE 0x02 -*/ -#define DRM_OMAP_GEM_NEW 0x03 -#define DRM_OMAP_GEM_CPU_PREP 0x04 -#define DRM_OMAP_GEM_CPU_FINI 0x05 -#define DRM_OMAP_GEM_INFO 0x06 -#define DRM_OMAP_NUM_IOCTLS 0x07 - -#define DRM_IOCTL_OMAP_GET_PARAM DRM_IOWR(DRM_COMMAND_BASE + DRM_OMAP_GET_PARAM, struct drm_omap_param) -#define DRM_IOCTL_OMAP_SET_PARAM DRM_IOW (DRM_COMMAND_BASE + DRM_OMAP_SET_PARAM, struct drm_omap_param) -/* placeholder for plugin-api -#define DRM_IOCTL_OMAP_GET_BASE DRM_IOWR(DRM_COMMAND_BASE + DRM_OMAP_GET_BASE, struct drm_omap_get_base) -*/ -#define DRM_IOCTL_OMAP_GEM_NEW DRM_IOWR(DRM_COMMAND_BASE + DRM_OMAP_GEM_NEW, struct drm_omap_gem_new) -#define DRM_IOCTL_OMAP_GEM_CPU_PREP DRM_IOW (DRM_COMMAND_BASE + DRM_OMAP_GEM_CPU_PREP, struct drm_omap_gem_cpu_prep) -#define DRM_IOCTL_OMAP_GEM_CPU_FINI DRM_IOW (DRM_COMMAND_BASE + DRM_OMAP_GEM_CPU_FINI, struct drm_omap_gem_cpu_fini) -#define DRM_IOCTL_OMAP_GEM_INFO DRM_IOWR(DRM_COMMAND_BASE + DRM_OMAP_GEM_INFO, struct drm_omap_gem_info) - -#endif /* __OMAP_DRM_H__ */ diff --git a/drivers/staging/omapdrm/omap_plane.c b/drivers/staging/omapdrm/omap_plane.c deleted file mode 100644 index 2a8e5bab49c9..000000000000 --- a/drivers/staging/omapdrm/omap_plane.c +++ /dev/null @@ -1,558 +0,0 @@ -/* - * drivers/staging/omapdrm/omap_plane.c - * - * Copyright (C) 2011 Texas Instruments - * Author: Rob Clark <rob.clark@linaro.org> - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 as published by - * the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License along with - * this program. If not, see <http://www.gnu.org/licenses/>. - */ - -#include <linux/kfifo.h> - -#include "omap_drv.h" -#include "omap_dmm_tiler.h" - -/* some hackery because omapdss has an 'enum omap_plane' (which would be - * better named omap_plane_id).. and compiler seems unhappy about having - * both a 'struct omap_plane' and 'enum omap_plane' - */ -#define omap_plane _omap_plane - -/* - * plane funcs - */ - -struct callback { - void (*fxn)(void *); - void *arg; -}; - -#define to_omap_plane(x) container_of(x, struct omap_plane, base) - -struct omap_plane { - struct drm_plane base; - struct omap_overlay *ovl; - struct omap_overlay_info info; - - /* position/orientation of scanout within the fb: */ - struct omap_drm_window win; - - - /* last fb that we pinned: */ - struct drm_framebuffer *pinned_fb; - - uint32_t nformats; - uint32_t formats[32]; - - /* for synchronizing access to unpins fifo */ - struct mutex unpin_mutex; - - /* set of bo's pending unpin until next END_WIN irq */ - DECLARE_KFIFO_PTR(unpin_fifo, struct drm_gem_object *); - int num_unpins, pending_num_unpins; - - /* for deferred unpin when we need to wait for scanout complete irq */ - struct work_struct work; - - /* callback on next endwin irq */ - struct callback endwin; -}; - -/* map from ovl->id to the irq we are interested in for scanout-done */ -static const uint32_t id2irq[] = { - [OMAP_DSS_GFX] = DISPC_IRQ_GFX_END_WIN, - [OMAP_DSS_VIDEO1] = DISPC_IRQ_VID1_END_WIN, - [OMAP_DSS_VIDEO2] = DISPC_IRQ_VID2_END_WIN, - [OMAP_DSS_VIDEO3] = DISPC_IRQ_VID3_END_WIN, -}; - -static void dispc_isr(void *arg, uint32_t mask) -{ - struct drm_plane *plane = arg; - struct omap_plane *omap_plane = to_omap_plane(plane); - struct omap_drm_private *priv = plane->dev->dev_private; - - omap_dispc_unregister_isr(dispc_isr, plane, - id2irq[omap_plane->ovl->id]); - - queue_work(priv->wq, &omap_plane->work); -} - -static void unpin_worker(struct work_struct *work) -{ - struct omap_plane *omap_plane = - container_of(work, struct omap_plane, work); - struct callback endwin; - - mutex_lock(&omap_plane->unpin_mutex); - DBG("unpinning %d of %d", omap_plane->num_unpins, - omap_plane->num_unpins + omap_plane->pending_num_unpins); - while (omap_plane->num_unpins > 0) { - struct drm_gem_object *bo = NULL; - int ret = kfifo_get(&omap_plane->unpin_fifo, &bo); - WARN_ON(!ret); - omap_gem_put_paddr(bo); - drm_gem_object_unreference_unlocked(bo); - omap_plane->num_unpins--; - } - endwin = omap_plane->endwin; - omap_plane->endwin.fxn = NULL; - mutex_unlock(&omap_plane->unpin_mutex); - - if (endwin.fxn) - endwin.fxn(endwin.arg); -} - -static void install_irq(struct drm_plane *plane) -{ - struct omap_plane *omap_plane = to_omap_plane(plane); - struct omap_overlay *ovl = omap_plane->ovl; - int ret; - - ret = omap_dispc_register_isr(dispc_isr, plane, id2irq[ovl->id]); - - /* - * omapdss has upper limit on # of registered irq handlers, - * which we shouldn't hit.. but if we do the limit should - * be raised or bad things happen: - */ - WARN_ON(ret == -EBUSY); -} - -/* push changes down to dss2 */ -static int commit(struct drm_plane *plane) -{ - struct drm_device *dev = plane->dev; - struct omap_plane *omap_plane = to_omap_plane(plane); - struct omap_overlay *ovl = omap_plane->ovl; - struct omap_overlay_info *info = &omap_plane->info; - int ret; - - DBG("%s", ovl->name); - DBG("%dx%d -> %dx%d (%d)", info->width, info->height, info->out_width, - info->out_height, info->screen_width); - DBG("%d,%d %08x %08x", info->pos_x, info->pos_y, - info->paddr, info->p_uv_addr); - - /* NOTE: do we want to do this at all here, or just wait - * for dpms(ON) since other CRTC's may not have their mode - * set yet, so fb dimensions may still change.. - */ - ret = ovl->set_overlay_info(ovl, info); - if (ret) { - dev_err(dev->dev, "could not set overlay info\n"); - return ret; - } - - mutex_lock(&omap_plane->unpin_mutex); - omap_plane->num_unpins += omap_plane->pending_num_unpins; - omap_plane->pending_num_unpins = 0; - mutex_unlock(&omap_plane->unpin_mutex); - - /* our encoder doesn't necessarily get a commit() after this, in - * particular in the dpms() and mode_set_base() cases, so force the - * manager to update: - * - * could this be in the encoder somehow? - */ - if (ovl->manager) { - ret = ovl->manager->apply(ovl->manager); - if (ret) { - dev_err(dev->dev, "could not apply settings\n"); - return ret; - } - - /* - * NOTE: really this should be atomic w/ mgr->apply() but - * omapdss does not expose such an API - */ - if (omap_plane->num_unpins > 0) - install_irq(plane); - - } else { - struct omap_drm_private *priv = dev->dev_private; - queue_work(priv->wq, &omap_plane->work); - } - - - if (ovl->is_enabled(ovl)) { - omap_framebuffer_flush(plane->fb, info->pos_x, info->pos_y, - info->out_width, info->out_height); - } - - return 0; -} - -/* when CRTC that we are attached to has potentially changed, this checks - * if we are attached to proper manager, and if necessary updates. - */ -static void update_manager(struct drm_plane *plane) -{ - struct omap_drm_private *priv = plane->dev->dev_private; - struct omap_plane *omap_plane = to_omap_plane(plane); - struct omap_overlay *ovl = omap_plane->ovl; - struct omap_overlay_manager *mgr = NULL; - int i; - - if (plane->crtc) { - for (i = 0; i < priv->num_encoders; i++) { - struct drm_encoder *encoder = priv->encoders[i]; - if (encoder->crtc == plane->crtc) { - mgr = omap_encoder_get_manager(encoder); - break; - } - } - } - - if (ovl->manager != mgr) { - bool enabled = ovl->is_enabled(ovl); - - /* don't switch things around with enabled overlays: */ - if (enabled) - omap_plane_dpms(plane, DRM_MODE_DPMS_OFF); - - if (ovl->manager) { - DBG("disconnecting %s from %s", ovl->name, - ovl->manager->name); - ovl->unset_manager(ovl); - } - - if (mgr) { - DBG("connecting %s to %s", ovl->name, mgr->name); - ovl->set_manager(ovl, mgr); - } - - if (enabled && mgr) - omap_plane_dpms(plane, DRM_MODE_DPMS_ON); - } -} - -static void unpin(void *arg, struct drm_gem_object *bo) -{ - struct drm_plane *plane = arg; - struct omap_plane *omap_plane = to_omap_plane(plane); - - if (kfifo_put(&omap_plane->unpin_fifo, - (const struct drm_gem_object **)&bo)) { - omap_plane->pending_num_unpins++; - /* also hold a ref so it isn't free'd while pinned */ - drm_gem_object_reference(bo); - } else { - dev_err(plane->dev->dev, "unpin fifo full!\n"); - omap_gem_put_paddr(bo); - } -} - -/* update which fb (if any) is pinned for scanout */ -static int update_pin(struct drm_plane *plane, struct drm_framebuffer *fb) -{ - struct omap_plane *omap_plane = to_omap_plane(plane); - struct drm_framebuffer *pinned_fb = omap_plane->pinned_fb; - - if (pinned_fb != fb) { - int ret; - - DBG("%p -> %p", pinned_fb, fb); - - mutex_lock(&omap_plane->unpin_mutex); - ret = omap_framebuffer_replace(pinned_fb, fb, plane, unpin); - mutex_unlock(&omap_plane->unpin_mutex); - - if (ret) { - dev_err(plane->dev->dev, "could not swap %p -> %p\n", - omap_plane->pinned_fb, fb); - omap_plane->pinned_fb = NULL; - return ret; - } - - omap_plane->pinned_fb = fb; - } - - return 0; -} - -/* update parameters that are dependent on the framebuffer dimensions and - * position within the fb that this plane scans out from. This is called - * when framebuffer or x,y base may have changed. - */ -static void update_scanout(struct drm_plane *plane) -{ - struct omap_plane *omap_plane = to_omap_plane(plane); - struct omap_overlay_info *info = &omap_plane->info; - struct omap_drm_window *win = &omap_plane->win; - int ret; - - ret = update_pin(plane, plane->fb); - if (ret) { - dev_err(plane->dev->dev, - "could not pin fb: %d\n", ret); - omap_plane_dpms(plane, DRM_MODE_DPMS_OFF); - return; - } - - omap_framebuffer_update_scanout(plane->fb, win, info); - - DBG("%s: %d,%d: %08x %08x (%d)", omap_plane->ovl->name, - win->src_x, win->src_y, - (u32)info->paddr, (u32)info->p_uv_addr, - info->screen_width); -} - -int omap_plane_mode_set(struct drm_plane *plane, - struct drm_crtc *crtc, struct drm_framebuffer *fb, - int crtc_x, int crtc_y, - unsigned int crtc_w, unsigned int crtc_h, - uint32_t src_x, uint32_t src_y, - uint32_t src_w, uint32_t src_h) -{ - struct omap_plane *omap_plane = to_omap_plane(plane); - struct omap_drm_window *win = &omap_plane->win; - - win->crtc_x = crtc_x; - win->crtc_y = crtc_y; - win->crtc_w = crtc_w; - win->crtc_h = crtc_h; - - /* src values are in Q16 fixed point, convert to integer: */ - win->src_x = src_x >> 16; - win->src_y = src_y >> 16; - win->src_w = src_w >> 16; - win->src_h = src_h >> 16; - - /* note: this is done after this fxn returns.. but if we need - * to do a commit/update_scanout, etc before this returns we - * need the current value. - */ - plane->fb = fb; - plane->crtc = crtc; - - update_scanout(plane); - update_manager(plane); - - return 0; -} - -static int omap_plane_update(struct drm_plane *plane, - struct drm_crtc *crtc, struct drm_framebuffer *fb, - int crtc_x, int crtc_y, - unsigned int crtc_w, unsigned int crtc_h, - uint32_t src_x, uint32_t src_y, - uint32_t src_w, uint32_t src_h) -{ - omap_plane_mode_set(plane, crtc, fb, crtc_x, crtc_y, crtc_w, crtc_h, - src_x, src_y, src_w, src_h); - return omap_plane_dpms(plane, DRM_MODE_DPMS_ON); -} - -static int omap_plane_disable(struct drm_plane *plane) -{ - struct omap_plane *omap_plane = to_omap_plane(plane); - omap_plane->win.rotation = BIT(DRM_ROTATE_0); - return omap_plane_dpms(plane, DRM_MODE_DPMS_OFF); -} - -static void omap_plane_destroy(struct drm_plane *plane) -{ - struct omap_plane *omap_plane = to_omap_plane(plane); - DBG("%s", omap_plane->ovl->name); - omap_plane_disable(plane); - drm_plane_cleanup(plane); - WARN_ON(omap_plane->pending_num_unpins + omap_plane->num_unpins > 0); - kfifo_free(&omap_plane->unpin_fifo); - kfree(omap_plane); -} - -int omap_plane_dpms(struct drm_plane *plane, int mode) -{ - struct omap_plane *omap_plane = to_omap_plane(plane); - struct omap_overlay *ovl = omap_plane->ovl; - int r; - - DBG("%s: %d", omap_plane->ovl->name, mode); - - if (mode == DRM_MODE_DPMS_ON) { - update_scanout(plane); - r = commit(plane); - if (!r) - r = ovl->enable(ovl); - } else { - struct omap_drm_private *priv = plane->dev->dev_private; - r = ovl->disable(ovl); - update_pin(plane, NULL); - queue_work(priv->wq, &omap_plane->work); - } - - return r; -} - -void omap_plane_on_endwin(struct drm_plane *plane, - void (*fxn)(void *), void *arg) -{ - struct omap_plane *omap_plane = to_omap_plane(plane); - - mutex_lock(&omap_plane->unpin_mutex); - omap_plane->endwin.fxn = fxn; - omap_plane->endwin.arg = arg; - mutex_unlock(&omap_plane->unpin_mutex); - - install_irq(plane); -} - -/* helper to install properties which are common to planes and crtcs */ -void omap_plane_install_properties(struct drm_plane *plane, - struct drm_mode_object *obj) -{ - struct drm_device *dev = plane->dev; - struct omap_drm_private *priv = dev->dev_private; - struct drm_property *prop; - - if (priv->has_dmm) { - prop = priv->rotation_prop; - if (!prop) { - const struct drm_prop_enum_list props[] = { - { DRM_ROTATE_0, "rotate-0" }, - { DRM_ROTATE_90, "rotate-90" }, - { DRM_ROTATE_180, "rotate-180" }, - { DRM_ROTATE_270, "rotate-270" }, - { DRM_REFLECT_X, "reflect-x" }, - { DRM_REFLECT_Y, "reflect-y" }, - }; - prop = drm_property_create_bitmask(dev, 0, "rotation", - props, ARRAY_SIZE(props)); - if (prop == NULL) - return; - priv->rotation_prop = prop; - } - drm_object_attach_property(obj, prop, 0); - } - - prop = priv->zorder_prop; - if (!prop) { - prop = drm_property_create_range(dev, 0, "zorder", 0, 3); - if (prop == NULL) - return; - priv->zorder_prop = prop; - } - drm_object_attach_property(obj, prop, 0); -} - -int omap_plane_set_property(struct drm_plane *plane, - struct drm_property *property, uint64_t val) -{ - struct omap_plane *omap_plane = to_omap_plane(plane); - struct omap_drm_private *priv = plane->dev->dev_private; - int ret = -EINVAL; - - if (property == priv->rotation_prop) { - struct omap_overlay *ovl = omap_plane->ovl; - - DBG("%s: rotation: %02x", ovl->name, (uint32_t)val); - omap_plane->win.rotation = val; - - if (ovl->is_enabled(ovl)) - ret = omap_plane_dpms(plane, DRM_MODE_DPMS_ON); - else - ret = 0; - } else if (property == priv->zorder_prop) { - struct omap_overlay *ovl = omap_plane->ovl; - - DBG("%s: zorder: %d", ovl->name, (uint32_t)val); - omap_plane->info.zorder = val; - - if (ovl->is_enabled(ovl)) - ret = omap_plane_dpms(plane, DRM_MODE_DPMS_ON); - else - ret = 0; - } - - return ret; -} - -static const struct drm_plane_funcs omap_plane_funcs = { - .update_plane = omap_plane_update, - .disable_plane = omap_plane_disable, - .destroy = omap_plane_destroy, - .set_property = omap_plane_set_property, -}; - -/* initialize plane */ -struct drm_plane *omap_plane_init(struct drm_device *dev, - struct omap_overlay *ovl, unsigned int possible_crtcs, - bool priv) -{ - struct drm_plane *plane = NULL; - struct omap_plane *omap_plane; - int ret; - - DBG("%s: possible_crtcs=%08x, priv=%d", ovl->name, - possible_crtcs, priv); - - /* friendly reminder to update table for future hw: */ - WARN_ON(ovl->id >= ARRAY_SIZE(id2irq)); - - omap_plane = kzalloc(sizeof(*omap_plane), GFP_KERNEL); - if (!omap_plane) { - dev_err(dev->dev, "could not allocate plane\n"); - goto fail; - } - - mutex_init(&omap_plane->unpin_mutex); - - ret = kfifo_alloc(&omap_plane->unpin_fifo, 16, GFP_KERNEL); - if (ret) { - dev_err(dev->dev, "could not allocate unpin FIFO\n"); - goto fail; - } - - INIT_WORK(&omap_plane->work, unpin_worker); - - omap_plane->nformats = omap_framebuffer_get_formats( - omap_plane->formats, ARRAY_SIZE(omap_plane->formats), - ovl->supported_modes); - omap_plane->ovl = ovl; - plane = &omap_plane->base; - - drm_plane_init(dev, plane, possible_crtcs, &omap_plane_funcs, - omap_plane->formats, omap_plane->nformats, priv); - - omap_plane_install_properties(plane, &plane->base); - - /* get our starting configuration, set defaults for parameters - * we don't currently use, etc: - */ - ovl->get_overlay_info(ovl, &omap_plane->info); - omap_plane->info.rotation_type = OMAP_DSS_ROT_DMA; - omap_plane->info.rotation = OMAP_DSS_ROT_0; - omap_plane->info.global_alpha = 0xff; - omap_plane->info.mirror = 0; - - /* Set defaults depending on whether we are a CRTC or overlay - * layer. - * TODO add ioctl to give userspace an API to change this.. this - * will come in a subsequent patch. - */ - if (priv) - omap_plane->info.zorder = 0; - else - omap_plane->info.zorder = ovl->id; - - update_manager(plane); - - return plane; - -fail: - if (plane) - omap_plane_destroy(plane); - - return NULL; -} diff --git a/drivers/staging/rtl8187se/r8180_core.c b/drivers/staging/rtl8187se/r8180_core.c index ae38475854b5..d10d75e8a33f 100644 --- a/drivers/staging/rtl8187se/r8180_core.c +++ b/drivers/staging/rtl8187se/r8180_core.c @@ -937,7 +937,8 @@ short alloc_rx_desc_ring(struct net_device *dev, u16 bufsize, int count) dma_tmp = pci_map_single(pdev, buf, bufsize * sizeof(u8), PCI_DMA_FROMDEVICE); - + if (pci_dma_mapping_error(pdev, dma_tmp)) + return -1; if (-1 == buffer_add(&(priv->rxbuffer), buf, dma_tmp, &(priv->rxbufferhead))) { DMESGE("Unable to allocate mem RX buf"); diff --git a/drivers/staging/rtl8192e/rtl8192e/r8192E_dev.c b/drivers/staging/rtl8192e/rtl8192e/r8192E_dev.c index 808aab6fa5ef..a9d78e9651c6 100644 --- a/drivers/staging/rtl8192e/rtl8192e/r8192E_dev.c +++ b/drivers/staging/rtl8192e/rtl8192e/r8192E_dev.c @@ -1183,6 +1183,8 @@ void rtl8192_tx_fill_desc(struct net_device *dev, struct tx_desc *pdesc, pTxFwInfo->TxRate, cb_desc); + if (pci_dma_mapping_error(priv->pdev, mapping)) + RT_TRACE(COMP_ERR, "DMA Mapping error\n");; if (cb_desc->bAMPDUEnable) { pTxFwInfo->AllowAggregation = 1; pTxFwInfo->RxMF = cb_desc->ampdu_factor; @@ -1280,6 +1282,8 @@ void rtl8192_tx_fill_cmd_desc(struct net_device *dev, dma_addr_t mapping = pci_map_single(priv->pdev, skb->data, skb->len, PCI_DMA_TODEVICE); + if (pci_dma_mapping_error(priv->pdev, mapping)) + RT_TRACE(COMP_ERR, "DMA Mapping error\n");; memset(entry, 0, 12); entry->LINIP = cb_desc->bLastIniPkt; entry->FirstSeg = 1; diff --git a/drivers/staging/rtl8192e/rtl8192e/rtl_core.c b/drivers/staging/rtl8192e/rtl8192e/rtl_core.c index 1a70f324552f..4ebf99b30975 100644 --- a/drivers/staging/rtl8192e/rtl8192e/rtl_core.c +++ b/drivers/staging/rtl8192e/rtl8192e/rtl_core.c @@ -2104,7 +2104,10 @@ static short rtl8192_alloc_rx_desc_ring(struct net_device *dev) skb_tail_pointer_rsl(skb), priv->rxbuffersize, PCI_DMA_FROMDEVICE); - + if (pci_dma_mapping_error(priv->pdev, *mapping)) { + dev_kfree_skb_any(skb); + return -1; + } entry->BufferAddress = cpu_to_le32(*mapping); entry->Length = priv->rxbuffersize; @@ -2397,7 +2400,11 @@ static void rtl8192_rx_normal(struct net_device *dev) skb_tail_pointer_rsl(skb), priv->rxbuffersize, PCI_DMA_FROMDEVICE); - + if (pci_dma_mapping_error(priv->pdev, + *((dma_addr_t *)skb->cb))) { + dev_kfree_skb_any(skb); + return; + } } done: pdesc->BufferAddress = cpu_to_le32(*((dma_addr_t *)skb->cb)); diff --git a/drivers/staging/rtl8712/usb_intf.c b/drivers/staging/rtl8712/usb_intf.c index 6b73843e580a..a96cd06d69dd 100644 --- a/drivers/staging/rtl8712/usb_intf.c +++ b/drivers/staging/rtl8712/usb_intf.c @@ -63,6 +63,8 @@ static struct usb_device_id rtl871x_usb_id_tbl[] = { {USB_DEVICE(0x0B05, 0x1791)}, /* 11n mode disable */ /* Belkin */ {USB_DEVICE(0x050D, 0x945A)}, + /* ISY IWL - Belkin clone */ + {USB_DEVICE(0x050D, 0x11F1)}, /* Corega */ {USB_DEVICE(0x07AA, 0x0047)}, /* D-Link */ diff --git a/drivers/staging/sb105x/Kconfig b/drivers/staging/sb105x/Kconfig index ac87c5e38dee..1facad625554 100644 --- a/drivers/staging/sb105x/Kconfig +++ b/drivers/staging/sb105x/Kconfig @@ -2,6 +2,7 @@ config SB105X tristate "SystemBase PCI Multiport UART" select SERIAL_CORE depends on PCI + depends on X86 help A driver for the SystemBase Multi-2/PCI serial card diff --git a/drivers/staging/sb105x/sb_pci_mp.c b/drivers/staging/sb105x/sb_pci_mp.c index edb2a85b9d52..9464f3874346 100644 --- a/drivers/staging/sb105x/sb_pci_mp.c +++ b/drivers/staging/sb105x/sb_pci_mp.c @@ -3054,6 +3054,7 @@ static int init_mp_dev(struct pci_dev *pcidev, mppcibrd_t brd) sbdev->nr_ports = ((portnum_hex/16)*10) + (portnum_hex % 16); } break; +#ifdef CONFIG_PARPORT_PC case PCI_DEVICE_ID_MP2S1P : sbdev->nr_ports = 2; @@ -3073,6 +3074,7 @@ static int init_mp_dev(struct pci_dev *pcidev, mppcibrd_t brd) /* add PC compatible parallel port */ parport_pc_probe_port(pcidev->resource[2].start, pcidev->resource[3].start, PARPORT_IRQ_NONE, PARPORT_DMA_NONE, &pcidev->dev, 0); break; +#endif } ret = request_region(sbdev->uart_access_addr, (8*sbdev->nr_ports), sbdev->name); diff --git a/drivers/staging/speakup/synth.c b/drivers/staging/speakup/synth.c index df9533798095..7616f058a00b 100644 --- a/drivers/staging/speakup/synth.c +++ b/drivers/staging/speakup/synth.c @@ -342,7 +342,7 @@ int synth_init(char *synth_name) mutex_lock(&spk_mutex); /* First, check if we already have it loaded. */ - for (i = 0; synths[i] != NULL && i < MAXSYNTHS; i++) + for (i = 0; i < MAXSYNTHS && synths[i] != NULL; i++) if (strcmp(synths[i]->name, synth_name) == 0) synth = synths[i]; @@ -423,7 +423,7 @@ int synth_add(struct spk_synth *in_synth) int i; int status = 0; mutex_lock(&spk_mutex); - for (i = 0; synths[i] != NULL && i < MAXSYNTHS; i++) + for (i = 0; i < MAXSYNTHS && synths[i] != NULL; i++) /* synth_remove() is responsible for rotating the array down */ if (in_synth == synths[i]) { mutex_unlock(&spk_mutex); diff --git a/drivers/staging/tidspbridge/core/_tiomap.h b/drivers/staging/tidspbridge/core/_tiomap.h index 543a127c7d4d..b783bfa59b1c 100644 --- a/drivers/staging/tidspbridge/core/_tiomap.h +++ b/drivers/staging/tidspbridge/core/_tiomap.h @@ -31,7 +31,7 @@ * driver should read or write to PRM/CM registers directly; they * should rely on OMAP core code to do this. */ -#include <mach-omap2/cm2xxx_3xxx.h> +#include <mach-omap2/cm3xxx.h> #include <mach-omap2/prm-regbits-34xx.h> #include <mach-omap2/cm-regbits-34xx.h> #include <dspbridge/devdefs.h> diff --git a/drivers/staging/tidspbridge/core/dsp-clock.c b/drivers/staging/tidspbridge/core/dsp-clock.c index b647207928b1..2f084e181d39 100644 --- a/drivers/staging/tidspbridge/core/dsp-clock.c +++ b/drivers/staging/tidspbridge/core/dsp-clock.c @@ -121,9 +121,13 @@ void dsp_clk_exit(void) for (i = 0; i < DM_TIMER_CLOCKS; i++) omap_dm_timer_free(timer[i]); + clk_unprepare(iva2_clk); clk_put(iva2_clk); + clk_unprepare(ssi.sst_fck); clk_put(ssi.sst_fck); + clk_unprepare(ssi.ssr_fck); clk_put(ssi.ssr_fck); + clk_unprepare(ssi.ick); clk_put(ssi.ick); } @@ -145,14 +149,21 @@ void dsp_clk_init(void) iva2_clk = clk_get(&dspbridge_device.dev, "iva2_ck"); if (IS_ERR(iva2_clk)) dev_err(bridge, "failed to get iva2 clock %p\n", iva2_clk); + else + clk_prepare(iva2_clk); ssi.sst_fck = clk_get(&dspbridge_device.dev, "ssi_sst_fck"); ssi.ssr_fck = clk_get(&dspbridge_device.dev, "ssi_ssr_fck"); ssi.ick = clk_get(&dspbridge_device.dev, "ssi_ick"); - if (IS_ERR(ssi.sst_fck) || IS_ERR(ssi.ssr_fck) || IS_ERR(ssi.ick)) + if (IS_ERR(ssi.sst_fck) || IS_ERR(ssi.ssr_fck) || IS_ERR(ssi.ick)) { dev_err(bridge, "failed to get ssi: sst %p, ssr %p, ick %p\n", ssi.sst_fck, ssi.ssr_fck, ssi.ick); + } else { + clk_prepare(ssi.sst_fck); + clk_prepare(ssi.ssr_fck); + clk_prepare(ssi.ick); + } } /** diff --git a/drivers/staging/tidspbridge/core/wdt.c b/drivers/staging/tidspbridge/core/wdt.c index 1dce36fb828f..7ff0e6c98039 100644 --- a/drivers/staging/tidspbridge/core/wdt.c +++ b/drivers/staging/tidspbridge/core/wdt.c @@ -63,11 +63,15 @@ int dsp_wdt_init(void) dsp_wdt.fclk = clk_get(NULL, "wdt3_fck"); if (!IS_ERR(dsp_wdt.fclk)) { + clk_prepare(dsp_wdt.fclk); + dsp_wdt.iclk = clk_get(NULL, "wdt3_ick"); if (IS_ERR(dsp_wdt.iclk)) { clk_put(dsp_wdt.fclk); dsp_wdt.fclk = NULL; ret = -EFAULT; + } else { + clk_prepare(dsp_wdt.iclk); } } else ret = -EFAULT; @@ -95,10 +99,14 @@ void dsp_wdt_exit(void) free_irq(INT_34XX_WDT3_IRQ, &dsp_wdt); tasklet_kill(&dsp_wdt.wdt3_tasklet); - if (dsp_wdt.fclk) + if (dsp_wdt.fclk) { + clk_unprepare(dsp_wdt.fclk); clk_put(dsp_wdt.fclk); - if (dsp_wdt.iclk) + } + if (dsp_wdt.iclk) { + clk_unprepare(dsp_wdt.iclk); clk_put(dsp_wdt.iclk); + } dsp_wdt.fclk = NULL; dsp_wdt.iclk = NULL; diff --git a/drivers/staging/vme/devices/vme_pio2_core.c b/drivers/staging/vme/devices/vme_pio2_core.c index 0331178ca3b3..bf73ba26e88a 100644 --- a/drivers/staging/vme/devices/vme_pio2_core.c +++ b/drivers/staging/vme/devices/vme_pio2_core.c @@ -162,11 +162,9 @@ static struct vme_driver pio2_driver = { static int __init pio2_init(void) { - int retval = 0; - if (bus_num == 0) { pr_err("No cards, skipping registration\n"); - goto err_nocard; + return -ENODEV; } if (bus_num > PIO2_CARDS_MAX) { @@ -176,15 +174,7 @@ static int __init pio2_init(void) } /* Register the PIO2 driver */ - retval = vme_register_driver(&pio2_driver, bus_num); - if (retval != 0) - goto err_reg; - - return retval; - -err_reg: -err_nocard: - return retval; + return vme_register_driver(&pio2_driver, bus_num); } static int pio2_match(struct vme_dev *vdev) diff --git a/drivers/staging/vt6656/bssdb.h b/drivers/staging/vt6656/bssdb.h index 6b2ec390e775..806cbf72fb59 100644 --- a/drivers/staging/vt6656/bssdb.h +++ b/drivers/staging/vt6656/bssdb.h @@ -90,7 +90,6 @@ typedef struct tagSRSNCapObject { } SRSNCapObject, *PSRSNCapObject; // BSS info(AP) -#pragma pack(1) typedef struct tagKnownBSS { // BSS info BOOL bActive; diff --git a/drivers/staging/vt6656/int.h b/drivers/staging/vt6656/int.h index 5d8faf9f96ec..e0d2b07ba608 100644 --- a/drivers/staging/vt6656/int.h +++ b/drivers/staging/vt6656/int.h @@ -34,7 +34,6 @@ #include "device.h" /*--------------------- Export Definitions -------------------------*/ -#pragma pack(1) typedef struct tagSINTData { BYTE byTSR0; BYTE byPkt0; diff --git a/drivers/staging/vt6656/iocmd.h b/drivers/staging/vt6656/iocmd.h index 22710cef751d..ae6e2d237b20 100644 --- a/drivers/staging/vt6656/iocmd.h +++ b/drivers/staging/vt6656/iocmd.h @@ -95,13 +95,12 @@ typedef enum tagWZONETYPE { // Ioctl interface structure // Command structure // -#pragma pack(1) typedef struct tagSCmdRequest { u8 name[16]; void *data; u16 wResult; u16 wCmdCode; -} SCmdRequest, *PSCmdRequest; +} __packed SCmdRequest, *PSCmdRequest; // // Scan @@ -111,7 +110,7 @@ typedef struct tagSCmdScan { u8 ssid[SSID_MAXLEN + 2]; -} SCmdScan, *PSCmdScan; +} __packed SCmdScan, *PSCmdScan; // // BSS Join @@ -126,7 +125,7 @@ typedef struct tagSCmdBSSJoin { BOOL bPSEnable; BOOL bShareKeyAuth; -} SCmdBSSJoin, *PSCmdBSSJoin; +} __packed SCmdBSSJoin, *PSCmdBSSJoin; // // Zonetype Setting @@ -137,7 +136,7 @@ typedef struct tagSCmdZoneTypeSet { BOOL bWrite; WZONETYPE ZoneType; -} SCmdZoneTypeSet, *PSCmdZoneTypeSet; +} __packed SCmdZoneTypeSet, *PSCmdZoneTypeSet; typedef struct tagSWPAResult { char ifname[100]; @@ -145,7 +144,7 @@ typedef struct tagSWPAResult { u8 key_mgmt; u8 eap_type; BOOL authenticated; -} SWPAResult, *PSWPAResult; +} __packed SWPAResult, *PSWPAResult; typedef struct tagSCmdStartAP { @@ -157,7 +156,7 @@ typedef struct tagSCmdStartAP { BOOL bShareKeyAuth; u8 byBasicRate; -} SCmdStartAP, *PSCmdStartAP; +} __packed SCmdStartAP, *PSCmdStartAP; typedef struct tagSCmdSetWEP { @@ -167,7 +166,7 @@ typedef struct tagSCmdSetWEP { BOOL bWepKeyAvailable[WEP_NKEYS]; u32 auWepKeyLength[WEP_NKEYS]; -} SCmdSetWEP, *PSCmdSetWEP; +} __packed SCmdSetWEP, *PSCmdSetWEP; typedef struct tagSBSSIDItem { @@ -180,14 +179,14 @@ typedef struct tagSBSSIDItem { BOOL bWEPOn; u32 uRSSI; -} SBSSIDItem; +} __packed SBSSIDItem; typedef struct tagSBSSIDList { u32 uItem; SBSSIDItem sBSSIDList[0]; -} SBSSIDList, *PSBSSIDList; +} __packed SBSSIDList, *PSBSSIDList; typedef struct tagSNodeItem { @@ -208,7 +207,7 @@ typedef struct tagSNodeItem { u32 uTxAttempts; u16 wFailureRatio; -} SNodeItem; +} __packed SNodeItem; typedef struct tagSNodeList { @@ -216,7 +215,7 @@ typedef struct tagSNodeList { u32 uItem; SNodeItem sNodeList[0]; -} SNodeList, *PSNodeList; +} __packed SNodeList, *PSNodeList; typedef struct tagSCmdLinkStatus { @@ -229,7 +228,7 @@ typedef struct tagSCmdLinkStatus { u32 uChannel; u32 uLinkRate; -} SCmdLinkStatus, *PSCmdLinkStatus; +} __packed SCmdLinkStatus, *PSCmdLinkStatus; // // 802.11 counter @@ -247,7 +246,7 @@ typedef struct tagSDot11MIBCount { u32 ReceivedFragmentCount; u32 MulticastReceivedFrameCount; u32 FCSErrorCount; -} SDot11MIBCount, *PSDot11MIBCount; +} __packed SDot11MIBCount, *PSDot11MIBCount; @@ -355,13 +354,13 @@ typedef struct tagSStatMIBCount { u32 ullTxBroadcastBytes[2]; u32 ullTxMulticastBytes[2]; u32 ullTxDirectedBytes[2]; -} SStatMIBCount, *PSStatMIBCount; +} __packed SStatMIBCount, *PSStatMIBCount; typedef struct tagSCmdValue { u32 dwValue; -} SCmdValue, *PSCmdValue; +} __packed SCmdValue, *PSCmdValue; // // hostapd & viawget ioctl related @@ -431,7 +430,7 @@ struct viawget_hostapd_param { u8 ssid[32]; } scan_req; } u; -}; +} __packed; /*--------------------- Export Classes ----------------------------*/ diff --git a/drivers/staging/vt6656/iowpa.h b/drivers/staging/vt6656/iowpa.h index 959c8868f6e2..2522ddec718d 100644 --- a/drivers/staging/vt6656/iowpa.h +++ b/drivers/staging/vt6656/iowpa.h @@ -67,12 +67,11 @@ enum { -#pragma pack(1) typedef struct viawget_wpa_header { u8 type; u16 req_ie_len; u16 resp_ie_len; -} viawget_wpa_header; +} __packed viawget_wpa_header; struct viawget_wpa_param { u32 cmd; @@ -113,9 +112,8 @@ struct viawget_wpa_param { u8 *buf; } scan_results; } u; -}; +} __packed; -#pragma pack(1) struct viawget_scan_result { u8 bssid[6]; u8 ssid[32]; @@ -130,7 +128,7 @@ struct viawget_scan_result { int noise; int level; int maxrate; -}; +} __packed; /*--------------------- Export Classes ----------------------------*/ diff --git a/drivers/staging/wlan-ng/cfg80211.c b/drivers/staging/wlan-ng/cfg80211.c index 18c06a59c091..1d31eab19d16 100644 --- a/drivers/staging/wlan-ng/cfg80211.c +++ b/drivers/staging/wlan-ng/cfg80211.c @@ -638,8 +638,8 @@ int prism2_leave_ibss(struct wiphy *wiphy, struct net_device *dev) } -int prism2_set_tx_power(struct wiphy *wiphy, enum nl80211_tx_power_setting type, - int mbm) +int prism2_set_tx_power(struct wiphy *wiphy, struct wireless_dev *wdev, + enum nl80211_tx_power_setting type, int mbm) { struct prism2_wiphy_private *priv = wiphy_priv(wiphy); wlandevice_t *wlandev = priv->wlandev; @@ -665,7 +665,8 @@ exit: return err; } -int prism2_get_tx_power(struct wiphy *wiphy, int *dbm) +int prism2_get_tx_power(struct wiphy *wiphy, struct wireless_dev *wdev, + int *dbm) { struct prism2_wiphy_private *priv = wiphy_priv(wiphy); wlandevice_t *wlandev = priv->wlandev; diff --git a/drivers/staging/wlan-ng/prism2mgmt.c b/drivers/staging/wlan-ng/prism2mgmt.c index 4efa9bc0fcf0..89bfd858bb28 100644 --- a/drivers/staging/wlan-ng/prism2mgmt.c +++ b/drivers/staging/wlan-ng/prism2mgmt.c @@ -406,7 +406,7 @@ int prism2mgmt_scan_results(wlandevice_t *wlandev, void *msgp) /* SSID */ req->ssid.status = P80211ENUM_msgitem_status_data_ok; req->ssid.data.len = le16_to_cpu(item->ssid.len); - req->ssid.data.len = min_t(u16, req->ssid.data.len, WLAN_BSSID_LEN); + req->ssid.data.len = min_t(u16, req->ssid.data.len, WLAN_SSID_MAXLEN); memcpy(req->ssid.data.data, item->ssid.data, req->ssid.data.len); /* supported rates */ diff --git a/drivers/staging/zram/zram_drv.c b/drivers/staging/zram/zram_drv.c index fb4a7c94aed3..f2a73bd739fb 100644 --- a/drivers/staging/zram/zram_drv.c +++ b/drivers/staging/zram/zram_drv.c @@ -265,7 +265,7 @@ out_cleanup: static int zram_bvec_write(struct zram *zram, struct bio_vec *bvec, u32 index, int offset) { - int ret; + int ret = 0; size_t clen; unsigned long handle; struct page *page; @@ -286,10 +286,8 @@ static int zram_bvec_write(struct zram *zram, struct bio_vec *bvec, u32 index, goto out; } ret = zram_decompress_page(zram, uncmem, index); - if (ret) { - kfree(uncmem); + if (ret) goto out; - } } /* @@ -302,16 +300,18 @@ static int zram_bvec_write(struct zram *zram, struct bio_vec *bvec, u32 index, user_mem = kmap_atomic(page); - if (is_partial_io(bvec)) + if (is_partial_io(bvec)) { memcpy(uncmem + offset, user_mem + bvec->bv_offset, bvec->bv_len); - else + kunmap_atomic(user_mem); + user_mem = NULL; + } else { uncmem = user_mem; + } if (page_zero_filled(uncmem)) { - kunmap_atomic(user_mem); - if (is_partial_io(bvec)) - kfree(uncmem); + if (!is_partial_io(bvec)) + kunmap_atomic(user_mem); zram_stat_inc(&zram->stats.pages_zero); zram_set_flag(zram, index, ZRAM_ZERO); ret = 0; @@ -321,9 +321,11 @@ static int zram_bvec_write(struct zram *zram, struct bio_vec *bvec, u32 index, ret = lzo1x_1_compress(uncmem, PAGE_SIZE, src, &clen, zram->compress_workmem); - kunmap_atomic(user_mem); - if (is_partial_io(bvec)) - kfree(uncmem); + if (!is_partial_io(bvec)) { + kunmap_atomic(user_mem); + user_mem = NULL; + uncmem = NULL; + } if (unlikely(ret != LZO_E_OK)) { pr_err("Compression failed! err=%d\n", ret); @@ -332,8 +334,10 @@ static int zram_bvec_write(struct zram *zram, struct bio_vec *bvec, u32 index, if (unlikely(clen > max_zpage_size)) { zram_stat_inc(&zram->stats.bad_compress); - src = uncmem; clen = PAGE_SIZE; + src = NULL; + if (is_partial_io(bvec)) + src = uncmem; } handle = zs_malloc(zram->mem_pool, clen); @@ -345,7 +349,11 @@ static int zram_bvec_write(struct zram *zram, struct bio_vec *bvec, u32 index, } cmem = zs_map_object(zram->mem_pool, handle, ZS_MM_WO); + if ((clen == PAGE_SIZE) && !is_partial_io(bvec)) + src = kmap_atomic(page); memcpy(cmem, src, clen); + if ((clen == PAGE_SIZE) && !is_partial_io(bvec)) + kunmap_atomic(src); zs_unmap_object(zram->mem_pool, handle); @@ -358,9 +366,10 @@ static int zram_bvec_write(struct zram *zram, struct bio_vec *bvec, u32 index, if (clen <= PAGE_SIZE / 2) zram_stat_inc(&zram->stats.good_compress); - return 0; - out: + if (is_partial_io(bvec)) + kfree(uncmem); + if (ret) zram_stat64_inc(zram, &zram->stats.failed_writes); return ret; diff --git a/drivers/target/iscsi/iscsi_target_erl2.c b/drivers/target/iscsi/iscsi_target_erl2.c index 9ac4c151eae4..ba6091bf93fc 100644 --- a/drivers/target/iscsi/iscsi_target_erl2.c +++ b/drivers/target/iscsi/iscsi_target_erl2.c @@ -372,7 +372,7 @@ int iscsit_prepare_cmds_for_realligance(struct iscsi_conn *conn) * made generic here. */ if (!(cmd->cmd_flags & ICF_OOO_CMDSN) && !cmd->immediate_cmd && - iscsi_sna_gte(cmd->stat_sn, conn->sess->exp_cmd_sn)) { + iscsi_sna_gte(cmd->cmd_sn, conn->sess->exp_cmd_sn)) { list_del(&cmd->i_conn_node); spin_unlock_bh(&conn->cmd_lock); iscsit_free_cmd(cmd); diff --git a/drivers/target/target_core_alua.c b/drivers/target/target_core_alua.c index 85140f7dde1e..7d4ec02e29a9 100644 --- a/drivers/target/target_core_alua.c +++ b/drivers/target/target_core_alua.c @@ -212,7 +212,7 @@ target_emulate_set_target_port_groups(struct se_cmd *cmd) struct t10_alua_tg_pt_gp_member *tg_pt_gp_mem, *l_tg_pt_gp_mem; unsigned char *buf; unsigned char *ptr; - sense_reason_t rc; + sense_reason_t rc = TCM_NO_SENSE; u32 len = 4; /* Skip over RESERVED area in header */ int alua_access_state, primary = 0; u16 tg_pt_id, rtpi; diff --git a/drivers/target/target_core_device.c b/drivers/target/target_core_device.c index e2695101bb99..f2aa7543d20a 100644 --- a/drivers/target/target_core_device.c +++ b/drivers/target/target_core_device.c @@ -941,6 +941,8 @@ int se_dev_set_queue_depth(struct se_device *dev, u32 queue_depth) int se_dev_set_fabric_max_sectors(struct se_device *dev, u32 fabric_max_sectors) { + int block_size = dev->dev_attrib.block_size; + if (dev->export_count) { pr_err("dev[%p]: Unable to change SE Device" " fabric_max_sectors while export_count is %d\n", @@ -978,8 +980,12 @@ int se_dev_set_fabric_max_sectors(struct se_device *dev, u32 fabric_max_sectors) /* * Align max_sectors down to PAGE_SIZE to follow transport_allocate_data_tasks() */ + if (!block_size) { + block_size = 512; + pr_warn("Defaulting to 512 for zero block_size\n"); + } fabric_max_sectors = se_dev_align_max_sectors(fabric_max_sectors, - dev->dev_attrib.block_size); + block_size); dev->dev_attrib.fabric_max_sectors = fabric_max_sectors; pr_debug("dev[%p]: SE Device max_sectors changed to %u\n", diff --git a/drivers/target/target_core_fabric_configfs.c b/drivers/target/target_core_fabric_configfs.c index 810263dfa4a1..c57bbbc7a7d1 100644 --- a/drivers/target/target_core_fabric_configfs.c +++ b/drivers/target/target_core_fabric_configfs.c @@ -754,6 +754,11 @@ static int target_fabric_port_link( return -EFAULT; } + if (!(dev->dev_flags & DF_CONFIGURED)) { + pr_err("se_device not configured yet, cannot port link\n"); + return -ENODEV; + } + tpg_ci = &lun_ci->ci_parent->ci_group->cg_item; se_tpg = container_of(to_config_group(tpg_ci), struct se_portal_group, tpg_group); diff --git a/drivers/target/target_core_pr.c b/drivers/target/target_core_pr.c index e35dbf85841f..8e0290b38e43 100644 --- a/drivers/target/target_core_pr.c +++ b/drivers/target/target_core_pr.c @@ -2053,7 +2053,7 @@ core_scsi3_emulate_pro_register(struct se_cmd *cmd, u64 res_key, u64 sa_res_key, /* Used for APTPL metadata w/ UNREGISTER */ unsigned char *pr_aptpl_buf = NULL; unsigned char isid_buf[PR_REG_ISID_LEN], *isid_ptr = NULL; - sense_reason_t ret; + sense_reason_t ret = TCM_NO_SENSE; int pr_holder = 0, type; if (!se_sess || !se_lun) { diff --git a/drivers/target/target_core_sbc.c b/drivers/target/target_core_sbc.c index 26a6d183ccb1..a664c664a31a 100644 --- a/drivers/target/target_core_sbc.c +++ b/drivers/target/target_core_sbc.c @@ -58,11 +58,10 @@ sbc_emulate_readcapacity(struct se_cmd *cmd) buf[7] = dev->dev_attrib.block_size & 0xff; rbuf = transport_kmap_data_sg(cmd); - if (!rbuf) - return TCM_LOGICAL_UNIT_COMMUNICATION_FAILURE; - - memcpy(rbuf, buf, min_t(u32, sizeof(buf), cmd->data_length)); - transport_kunmap_data_sg(cmd); + if (rbuf) { + memcpy(rbuf, buf, min_t(u32, sizeof(buf), cmd->data_length)); + transport_kunmap_data_sg(cmd); + } target_complete_cmd(cmd, GOOD); return 0; @@ -97,11 +96,10 @@ sbc_emulate_readcapacity_16(struct se_cmd *cmd) buf[14] = 0x80; rbuf = transport_kmap_data_sg(cmd); - if (!rbuf) - return TCM_LOGICAL_UNIT_COMMUNICATION_FAILURE; - - memcpy(rbuf, buf, min_t(u32, sizeof(buf), cmd->data_length)); - transport_kunmap_data_sg(cmd); + if (rbuf) { + memcpy(rbuf, buf, min_t(u32, sizeof(buf), cmd->data_length)); + transport_kunmap_data_sg(cmd); + } target_complete_cmd(cmd, GOOD); return 0; diff --git a/drivers/target/target_core_spc.c b/drivers/target/target_core_spc.c index 84f9e96e8ace..2d88f087d961 100644 --- a/drivers/target/target_core_spc.c +++ b/drivers/target/target_core_spc.c @@ -641,11 +641,10 @@ spc_emulate_inquiry(struct se_cmd *cmd) out: rbuf = transport_kmap_data_sg(cmd); - if (!rbuf) - return TCM_LOGICAL_UNIT_COMMUNICATION_FAILURE; - - memcpy(rbuf, buf, min_t(u32, sizeof(buf), cmd->data_length)); - transport_kunmap_data_sg(cmd); + if (rbuf) { + memcpy(rbuf, buf, min_t(u32, sizeof(buf), cmd->data_length)); + transport_kunmap_data_sg(cmd); + } if (!ret) target_complete_cmd(cmd, GOOD); @@ -851,7 +850,7 @@ static sense_reason_t spc_emulate_modesense(struct se_cmd *cmd) { struct se_device *dev = cmd->se_dev; char *cdb = cmd->t_task_cdb; - unsigned char *buf, *map_buf; + unsigned char buf[SE_MODE_PAGE_BUF], *rbuf; int type = dev->transport->get_device_type(dev); int ten = (cmd->t_task_cdb[0] == MODE_SENSE_10); bool dbd = !!(cdb[1] & 0x08); @@ -863,26 +862,8 @@ static sense_reason_t spc_emulate_modesense(struct se_cmd *cmd) int ret; int i; - map_buf = transport_kmap_data_sg(cmd); - if (!map_buf) - return TCM_LOGICAL_UNIT_COMMUNICATION_FAILURE; - /* - * If SCF_PASSTHROUGH_SG_TO_MEM_NOALLOC is not set, then we - * know we actually allocated a full page. Otherwise, if the - * data buffer is too small, allocate a temporary buffer so we - * don't have to worry about overruns in all our INQUIRY - * emulation handling. - */ - if (cmd->data_length < SE_MODE_PAGE_BUF && - (cmd->se_cmd_flags & SCF_PASSTHROUGH_SG_TO_MEM_NOALLOC)) { - buf = kzalloc(SE_MODE_PAGE_BUF, GFP_KERNEL); - if (!buf) { - transport_kunmap_data_sg(cmd); - return TCM_LOGICAL_UNIT_COMMUNICATION_FAILURE; - } - } else { - buf = map_buf; - } + memset(buf, 0, SE_MODE_PAGE_BUF); + /* * Skip over MODE DATA LENGTH + MEDIUM TYPE fields to byte 3 for * MODE_SENSE_10 and byte 2 for MODE_SENSE (6). @@ -934,8 +915,6 @@ static sense_reason_t spc_emulate_modesense(struct se_cmd *cmd) if (page == 0x3f) { if (subpage != 0x00 && subpage != 0xff) { pr_warn("MODE_SENSE: Invalid subpage code: 0x%02x\n", subpage); - kfree(buf); - transport_kunmap_data_sg(cmd); return TCM_INVALID_CDB_FIELD; } @@ -972,7 +951,6 @@ static sense_reason_t spc_emulate_modesense(struct se_cmd *cmd) pr_err("MODE SENSE: unimplemented page/subpage: 0x%02x/0x%02x\n", page, subpage); - transport_kunmap_data_sg(cmd); return TCM_UNKNOWN_MODE_PAGE; set_length: @@ -981,12 +959,12 @@ set_length: else buf[0] = length - 1; - if (buf != map_buf) { - memcpy(map_buf, buf, cmd->data_length); - kfree(buf); + rbuf = transport_kmap_data_sg(cmd); + if (rbuf) { + memcpy(rbuf, buf, min_t(u32, SE_MODE_PAGE_BUF, cmd->data_length)); + transport_kunmap_data_sg(cmd); } - transport_kunmap_data_sg(cmd); target_complete_cmd(cmd, GOOD); return 0; } diff --git a/drivers/target/target_core_transport.c b/drivers/target/target_core_transport.c index c23c76ccef65..bd587b70661a 100644 --- a/drivers/target/target_core_transport.c +++ b/drivers/target/target_core_transport.c @@ -541,9 +541,6 @@ static void transport_lun_remove_cmd(struct se_cmd *cmd) void transport_cmd_finish_abort(struct se_cmd *cmd, int remove) { - if (!(cmd->se_cmd_flags & SCF_SCSI_TMR_CDB)) - transport_lun_remove_cmd(cmd); - if (transport_cmd_check_stop_to_fabric(cmd)) return; if (remove) @@ -1396,6 +1393,8 @@ static void target_complete_tmr_failure(struct work_struct *work) se_cmd->se_tmr_req->response = TMR_LUN_DOES_NOT_EXIST; se_cmd->se_tfo->queue_tm_rsp(se_cmd); + + transport_cmd_check_stop_to_fabric(se_cmd); } /** @@ -1688,6 +1687,7 @@ void target_execute_cmd(struct se_cmd *cmd) } cmd->t_state = TRANSPORT_PROCESSING; + cmd->transport_state |= CMD_T_ACTIVE; spin_unlock_irq(&cmd->t_state_lock); if (!target_handle_task_attr(cmd)) @@ -2597,6 +2597,16 @@ transport_send_check_condition_and_sense(struct se_cmd *cmd, * SENSE KEY values from include/scsi/scsi.h */ switch (reason) { + case TCM_NO_SENSE: + /* CURRENT ERROR */ + buffer[0] = 0x70; + buffer[SPC_ADD_SENSE_LEN_OFFSET] = 10; + /* Not Ready */ + buffer[SPC_SENSE_KEY_OFFSET] = NOT_READY; + /* NO ADDITIONAL SENSE INFORMATION */ + buffer[SPC_ASC_KEY_OFFSET] = 0; + buffer[SPC_ASCQ_KEY_OFFSET] = 0; + break; case TCM_NON_EXISTENT_LUN: /* CURRENT ERROR */ buffer[0] = 0x70; @@ -2743,7 +2753,7 @@ transport_send_check_condition_and_sense(struct se_cmd *cmd, /* ILLEGAL REQUEST */ buffer[SPC_SENSE_KEY_OFFSET] = ILLEGAL_REQUEST; /* LOGICAL UNIT COMMUNICATION FAILURE */ - buffer[SPC_ASC_KEY_OFFSET] = 0x80; + buffer[SPC_ASC_KEY_OFFSET] = 0x08; break; } /* @@ -2804,6 +2814,8 @@ void transport_send_task_abort(struct se_cmd *cmd) } cmd->scsi_status = SAM_STAT_TASK_ABORTED; + transport_lun_remove_cmd(cmd); + pr_debug("Setting SAM_STAT_TASK_ABORTED status for CDB: 0x%02x," " ITT: 0x%08x\n", cmd->t_task_cdb[0], cmd->se_tfo->get_task_tag(cmd)); diff --git a/drivers/target/tcm_fc/tfc_sess.c b/drivers/target/tcm_fc/tfc_sess.c index 12d6fa21e5e1..6659dd36e806 100644 --- a/drivers/target/tcm_fc/tfc_sess.c +++ b/drivers/target/tcm_fc/tfc_sess.c @@ -355,11 +355,11 @@ static int ft_prli_locked(struct fc_rport_priv *rdata, u32 spp_len, tport = ft_tport_create(rdata->local_port); if (!tport) - return 0; /* not a target for this local port */ + goto not_target; /* not a target for this local port */ acl = ft_acl_get(tport->tpg, rdata); if (!acl) - return 0; + goto not_target; /* no target for this remote */ if (!rspp) goto fill; @@ -396,12 +396,18 @@ static int ft_prli_locked(struct fc_rport_priv *rdata, u32 spp_len, /* * OR in our service parameters with other provider (initiator), if any. - * TBD XXX - indicate RETRY capability? */ fill: fcp_parm = ntohl(spp->spp_params); + fcp_parm &= ~FCP_SPPF_RETRY; spp->spp_params = htonl(fcp_parm | FCP_SPPF_TARG_FCN); return FC_SPP_RESP_ACK; + +not_target: + fcp_parm = ntohl(spp->spp_params); + fcp_parm &= ~FCP_SPPF_TARG_FCN; + spp->spp_params = htonl(fcp_parm); + return 0; } /** diff --git a/drivers/tty/pty.c b/drivers/tty/pty.c index be6a373601b7..79ff3a5e925d 100644 --- a/drivers/tty/pty.c +++ b/drivers/tty/pty.c @@ -441,6 +441,8 @@ static int pty_bsd_ioctl(struct tty_struct *tty, return pty_get_pktmode(tty, (int __user *)arg); case TIOCSIG: /* Send signal to other side of pty */ return pty_signal(tty, (int) arg); + case TIOCGPTN: /* TTY returns ENOTTY, but glibc expects EINVAL here */ + return -EINVAL; } return -ENOIOCTLCMD; } diff --git a/drivers/tty/serial/8250/8250.c b/drivers/tty/serial/8250/8250.c index d085e3a8ec06..f9320437a649 100644 --- a/drivers/tty/serial/8250/8250.c +++ b/drivers/tty/serial/8250/8250.c @@ -300,6 +300,12 @@ static const struct serial8250_config uart_config[] = { UART_FCR_R_TRIG_00 | UART_FCR_T_TRIG_00, .flags = UART_CAP_FIFO, }, + [PORT_BRCM_TRUMANAGE] = { + .name = "TruManage", + .fifo_size = 1, + .tx_loadsz = 1024, + .flags = UART_CAP_HFIFO, + }, [PORT_8250_CIR] = { .name = "CIR port" } @@ -1490,6 +1496,11 @@ void serial8250_tx_chars(struct uart_8250_port *up) port->icount.tx++; if (uart_circ_empty(xmit)) break; + if (up->capabilities & UART_CAP_HFIFO) { + if ((serial_port_in(port, UART_LSR) & BOTH_EMPTY) != + BOTH_EMPTY) + break; + } } while (--count > 0); if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) diff --git a/drivers/tty/serial/8250/8250.h b/drivers/tty/serial/8250/8250.h index 3b4ea84898c2..12caa1292b75 100644 --- a/drivers/tty/serial/8250/8250.h +++ b/drivers/tty/serial/8250/8250.h @@ -40,6 +40,7 @@ struct serial8250_config { #define UART_CAP_AFE (1 << 11) /* MCR-based hw flow control */ #define UART_CAP_UUE (1 << 12) /* UART needs IER bit 6 set (Xscale) */ #define UART_CAP_RTOIE (1 << 13) /* UART needs IER bit 4 set (Xscale, Tegra) */ +#define UART_CAP_HFIFO (1 << 14) /* UART has a "hidden" FIFO */ #define UART_BUG_QUOT (1 << 0) /* UART has buggy quot LSB */ #define UART_BUG_TXEN (1 << 1) /* UART has buggy TX IIR status */ diff --git a/drivers/tty/serial/8250/8250_dw.c b/drivers/tty/serial/8250/8250_dw.c index 1d0dba2d562d..096d2ef48b32 100644 --- a/drivers/tty/serial/8250/8250_dw.c +++ b/drivers/tty/serial/8250/8250_dw.c @@ -79,7 +79,7 @@ static int dw8250_handle_irq(struct uart_port *p) } else if ((iir & UART_IIR_BUSY) == UART_IIR_BUSY) { /* Clear the USR and write the LCR again. */ (void)p->serial_in(p, UART_USR); - p->serial_out(p, d->last_lcr, UART_LCR); + p->serial_out(p, UART_LCR, d->last_lcr); return 1; } diff --git a/drivers/tty/serial/8250/8250_pci.c b/drivers/tty/serial/8250/8250_pci.c index 26b9dc012ed0..a27a98e1b066 100644 --- a/drivers/tty/serial/8250/8250_pci.c +++ b/drivers/tty/serial/8250/8250_pci.c @@ -1085,6 +1085,18 @@ pci_omegapci_setup(struct serial_private *priv, return setup_port(priv, port, 2, idx * 8, 0); } +static int +pci_brcm_trumanage_setup(struct serial_private *priv, + const struct pciserial_board *board, + struct uart_8250_port *port, int idx) +{ + int ret = pci_default_setup(priv, board, port, idx); + + port->port.type = PORT_BRCM_TRUMANAGE; + port->port.flags = (port->port.flags | UPF_FIXED_PORT | UPF_FIXED_TYPE); + return ret; +} + static int skip_tx_en_setup(struct serial_private *priv, const struct pciserial_board *board, struct uart_8250_port *port, int idx) @@ -1301,9 +1313,10 @@ pci_wch_ch353_setup(struct serial_private *priv, #define PCI_VENDOR_ID_AGESTAR 0x5372 #define PCI_DEVICE_ID_AGESTAR_9375 0x6872 #define PCI_VENDOR_ID_ASIX 0x9710 -#define PCI_DEVICE_ID_COMMTECH_4222PCIE 0x0019 #define PCI_DEVICE_ID_COMMTECH_4224PCIE 0x0020 #define PCI_DEVICE_ID_COMMTECH_4228PCIE 0x0021 +#define PCI_DEVICE_ID_COMMTECH_4222PCIE 0x0022 +#define PCI_DEVICE_ID_BROADCOM_TRUMANAGE 0x160a /* Unknown vendors/cards - this should not be in linux/pci_ids.h */ @@ -1954,6 +1967,17 @@ static struct pci_serial_quirk pci_serial_quirks[] __refdata = { .setup = pci_xr17v35x_setup, }, /* + * Broadcom TruManage (NetXtreme) + */ + { + .vendor = PCI_VENDOR_ID_BROADCOM, + .device = PCI_DEVICE_ID_BROADCOM_TRUMANAGE, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .setup = pci_brcm_trumanage_setup, + }, + + /* * Default "match everything" terminator entry */ { @@ -2148,6 +2172,7 @@ enum pci_board_num_t { pbn_ce4100_1_115200, pbn_omegapci, pbn_NETMOS9900_2s_115200, + pbn_brcm_trumanage, }; /* @@ -2246,7 +2271,7 @@ static struct pciserial_board pci_boards[] = { [pbn_b0_8_1152000_200] = { .flags = FL_BASE0, - .num_ports = 2, + .num_ports = 8, .base_baud = 1152000, .uart_offset = 0x200, }, @@ -2892,6 +2917,12 @@ static struct pciserial_board pci_boards[] = { .num_ports = 2, .base_baud = 115200, }, + [pbn_brcm_trumanage] = { + .flags = FL_BASE0, + .num_ports = 1, + .reg_shift = 2, + .base_baud = 115200, + }, }; static const struct pci_device_id blacklist[] = { @@ -4471,6 +4502,13 @@ static struct pci_device_id serial_pci_tbl[] = { pbn_omegapci }, /* + * Broadcom TruManage + */ + { PCI_VENDOR_ID_BROADCOM, PCI_DEVICE_ID_BROADCOM_TRUMANAGE, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_brcm_trumanage }, + + /* * AgeStar as-prs2-009 */ { PCI_VENDOR_ID_AGESTAR, PCI_DEVICE_ID_AGESTAR_9375, diff --git a/drivers/tty/serial/ifx6x60.c b/drivers/tty/serial/ifx6x60.c index 675d94ab0aff..8cb6d8d66a13 100644 --- a/drivers/tty/serial/ifx6x60.c +++ b/drivers/tty/serial/ifx6x60.c @@ -637,6 +637,7 @@ static void ifx_port_shutdown(struct tty_port *port) clear_bit(IFX_SPI_STATE_IO_AVAILABLE, &ifx_dev->flags); mrdy_set_low(ifx_dev); + del_timer(&ifx_dev->spi_timer); clear_bit(IFX_SPI_STATE_TIMER_PENDING, &ifx_dev->flags); tasklet_kill(&ifx_dev->io_work_tasklet); } @@ -810,7 +811,8 @@ static void ifx_spi_io(unsigned long data) ifx_dev->spi_xfer.cs_change = 0; ifx_dev->spi_xfer.speed_hz = ifx_dev->spi_dev->max_speed_hz; /* ifx_dev->spi_xfer.speed_hz = 390625; */ - ifx_dev->spi_xfer.bits_per_word = spi_bpw; + ifx_dev->spi_xfer.bits_per_word = + ifx_dev->spi_dev->bits_per_word; ifx_dev->spi_xfer.tx_buf = ifx_dev->tx_buffer; ifx_dev->spi_xfer.rx_buf = ifx_dev->rx_buffer; diff --git a/drivers/tty/serial/mxs-auart.c b/drivers/tty/serial/mxs-auart.c index 6db23b035efe..e55615eb34ad 100644 --- a/drivers/tty/serial/mxs-auart.c +++ b/drivers/tty/serial/mxs-auart.c @@ -253,7 +253,7 @@ static void mxs_auart_tx_chars(struct mxs_auart_port *s) struct circ_buf *xmit = &s->port.state->xmit; if (auart_dma_enabled(s)) { - int i = 0; + u32 i = 0; int size; void *buffer = s->tx_dma_buf; @@ -412,10 +412,12 @@ static void mxs_auart_set_mctrl(struct uart_port *u, unsigned mctrl) u32 ctrl = readl(u->membase + AUART_CTRL2); - ctrl &= ~AUART_CTRL2_RTSEN; + ctrl &= ~(AUART_CTRL2_RTSEN | AUART_CTRL2_RTS); if (mctrl & TIOCM_RTS) { if (tty_port_cts_enabled(&u->state->port)) ctrl |= AUART_CTRL2_RTSEN; + else + ctrl |= AUART_CTRL2_RTS; } s->ctrl = mctrl; diff --git a/drivers/tty/serial/samsung.c b/drivers/tty/serial/samsung.c index 12e5249d053e..e514b3a4dc57 100644 --- a/drivers/tty/serial/samsung.c +++ b/drivers/tty/serial/samsung.c @@ -1006,7 +1006,6 @@ static void s3c24xx_serial_resetport(struct uart_port *port, ucon &= ucon_mask; wr_regl(port, S3C2410_UCON, ucon | cfg->ucon); - wr_regl(port, S3C2410_ULCON, cfg->ulcon); /* reset both fifos */ wr_regl(port, S3C2410_UFCON, cfg->ufcon | S3C2410_UFCON_RESETBOTH); diff --git a/drivers/tty/serial/vt8500_serial.c b/drivers/tty/serial/vt8500_serial.c index 8fd181436a6b..d5ed9f613005 100644 --- a/drivers/tty/serial/vt8500_serial.c +++ b/drivers/tty/serial/vt8500_serial.c @@ -604,7 +604,7 @@ static int vt8500_serial_probe(struct platform_device *pdev) vt8500_port->uart.flags = UPF_IOREMAP | UPF_BOOT_AUTOCONF; vt8500_port->clk = of_clk_get(pdev->dev.of_node, 0); - if (vt8500_port->clk) { + if (!IS_ERR(vt8500_port->clk)) { vt8500_port->uart.uartclk = clk_get_rate(vt8500_port->clk); } else { /* use the default of 24Mhz if not specified and warn */ diff --git a/drivers/tty/vt/vt.c b/drivers/tty/vt/vt.c index 8fd89687d068..ed43a7f88b4f 100644 --- a/drivers/tty/vt/vt.c +++ b/drivers/tty/vt/vt.c @@ -638,7 +638,7 @@ static inline void save_screen(struct vc_data *vc) * Redrawing of screen */ -static void clear_buffer_attributes(struct vc_data *vc) +void clear_buffer_attributes(struct vc_data *vc) { unsigned short *p = (unsigned short *)vc->vc_origin; int count = vc->vc_screenbuf_size / 2; @@ -2987,7 +2987,7 @@ int __init vty_init(const struct file_operations *console_fops) static struct class *vtconsole_class; -static int bind_con_driver(const struct consw *csw, int first, int last, +static int do_bind_con_driver(const struct consw *csw, int first, int last, int deflt) { struct module *owner = csw->owner; @@ -2998,7 +2998,7 @@ static int bind_con_driver(const struct consw *csw, int first, int last, if (!try_module_get(owner)) return -ENODEV; - console_lock(); + WARN_CONSOLE_UNLOCKED(); /* check if driver is registered */ for (i = 0; i < MAX_NR_CON_DRIVER; i++) { @@ -3083,11 +3083,22 @@ static int bind_con_driver(const struct consw *csw, int first, int last, retval = 0; err: - console_unlock(); module_put(owner); return retval; }; + +static int bind_con_driver(const struct consw *csw, int first, int last, + int deflt) +{ + int ret; + + console_lock(); + ret = do_bind_con_driver(csw, first, last, deflt); + console_unlock(); + return ret; +} + #ifdef CONFIG_VT_HW_CONSOLE_BINDING static int con_is_graphics(const struct consw *csw, int first, int last) { @@ -3124,6 +3135,18 @@ static int con_is_graphics(const struct consw *csw, int first, int last) */ int unbind_con_driver(const struct consw *csw, int first, int last, int deflt) { + int retval; + + console_lock(); + retval = do_unbind_con_driver(csw, first, last, deflt); + console_unlock(); + return retval; +} +EXPORT_SYMBOL(unbind_con_driver); + +/* unlocked version of unbind_con_driver() */ +int do_unbind_con_driver(const struct consw *csw, int first, int last, int deflt) +{ struct module *owner = csw->owner; const struct consw *defcsw = NULL; struct con_driver *con_driver = NULL, *con_back = NULL; @@ -3132,7 +3155,7 @@ int unbind_con_driver(const struct consw *csw, int first, int last, int deflt) if (!try_module_get(owner)) return -ENODEV; - console_lock(); + WARN_CONSOLE_UNLOCKED(); /* check if driver is registered and if it is unbindable */ for (i = 0; i < MAX_NR_CON_DRIVER; i++) { @@ -3145,10 +3168,8 @@ int unbind_con_driver(const struct consw *csw, int first, int last, int deflt) } } - if (retval) { - console_unlock(); + if (retval) goto err; - } retval = -ENODEV; @@ -3164,15 +3185,11 @@ int unbind_con_driver(const struct consw *csw, int first, int last, int deflt) } } - if (retval) { - console_unlock(); + if (retval) goto err; - } - if (!con_is_bound(csw)) { - console_unlock(); + if (!con_is_bound(csw)) goto err; - } first = max(first, con_driver->first); last = min(last, con_driver->last); @@ -3199,15 +3216,14 @@ int unbind_con_driver(const struct consw *csw, int first, int last, int deflt) if (!con_is_bound(csw)) con_driver->flag &= ~CON_DRIVER_FLAG_INIT; - console_unlock(); /* ignore return value, binding should not fail */ - bind_con_driver(defcsw, first, last, deflt); + do_bind_con_driver(defcsw, first, last, deflt); err: module_put(owner); return retval; } -EXPORT_SYMBOL(unbind_con_driver); +EXPORT_SYMBOL_GPL(do_unbind_con_driver); static int vt_bind(struct con_driver *con) { @@ -3492,28 +3508,18 @@ int con_debug_leave(void) } EXPORT_SYMBOL_GPL(con_debug_leave); -/** - * register_con_driver - register console driver to console layer - * @csw: console driver - * @first: the first console to take over, minimum value is 0 - * @last: the last console to take over, maximum value is MAX_NR_CONSOLES -1 - * - * DESCRIPTION: This function registers a console driver which can later - * bind to a range of consoles specified by @first and @last. It will - * also initialize the console driver by calling con_startup(). - */ -int register_con_driver(const struct consw *csw, int first, int last) +static int do_register_con_driver(const struct consw *csw, int first, int last) { struct module *owner = csw->owner; struct con_driver *con_driver; const char *desc; int i, retval = 0; + WARN_CONSOLE_UNLOCKED(); + if (!try_module_get(owner)) return -ENODEV; - console_lock(); - for (i = 0; i < MAX_NR_CON_DRIVER; i++) { con_driver = ®istered_con_driver[i]; @@ -3566,10 +3572,29 @@ int register_con_driver(const struct consw *csw, int first, int last) } err: - console_unlock(); module_put(owner); return retval; } + +/** + * register_con_driver - register console driver to console layer + * @csw: console driver + * @first: the first console to take over, minimum value is 0 + * @last: the last console to take over, maximum value is MAX_NR_CONSOLES -1 + * + * DESCRIPTION: This function registers a console driver which can later + * bind to a range of consoles specified by @first and @last. It will + * also initialize the console driver by calling con_startup(). + */ +int register_con_driver(const struct consw *csw, int first, int last) +{ + int retval; + + console_lock(); + retval = do_register_con_driver(csw, first, last); + console_unlock(); + return retval; +} EXPORT_SYMBOL(register_con_driver); /** @@ -3585,9 +3610,18 @@ EXPORT_SYMBOL(register_con_driver); */ int unregister_con_driver(const struct consw *csw) { - int i, retval = -ENODEV; + int retval; console_lock(); + retval = do_unregister_con_driver(csw); + console_unlock(); + return retval; +} +EXPORT_SYMBOL(unregister_con_driver); + +int do_unregister_con_driver(const struct consw *csw) +{ + int i, retval = -ENODEV; /* cannot unregister a bound driver */ if (con_is_bound(csw)) @@ -3613,27 +3647,53 @@ int unregister_con_driver(const struct consw *csw) } } err: - console_unlock(); return retval; } -EXPORT_SYMBOL(unregister_con_driver); +EXPORT_SYMBOL_GPL(do_unregister_con_driver); /* * If we support more console drivers, this function is used * when a driver wants to take over some existing consoles * and become default driver for newly opened ones. * - * take_over_console is basically a register followed by unbind + * take_over_console is basically a register followed by unbind + */ +int do_take_over_console(const struct consw *csw, int first, int last, int deflt) +{ + int err; + + err = do_register_con_driver(csw, first, last); + /* + * If we get an busy error we still want to bind the console driver + * and return success, as we may have unbound the console driver + * but not unregistered it. + */ + if (err == -EBUSY) + err = 0; + if (!err) + do_bind_con_driver(csw, first, last, deflt); + + return err; +} +EXPORT_SYMBOL_GPL(do_take_over_console); + +/* + * If we support more console drivers, this function is used + * when a driver wants to take over some existing consoles + * and become default driver for newly opened ones. + * + * take_over_console is basically a register followed by unbind */ int take_over_console(const struct consw *csw, int first, int last, int deflt) { int err; err = register_con_driver(csw, first, last); - /* if we get an busy error we still want to bind the console driver + /* + * If we get an busy error we still want to bind the console driver * and return success, as we may have unbound the console driver - Â * but not unregistered it. - */ + * but not unregistered it. + */ if (err == -EBUSY) err = 0; if (!err) diff --git a/drivers/usb/Kconfig b/drivers/usb/Kconfig index 4c90b510d016..640ae6c6d2d2 100644 --- a/drivers/usb/Kconfig +++ b/drivers/usb/Kconfig @@ -37,6 +37,7 @@ config USB_ARCH_HAS_EHCI default y if ARCH_W90X900 default y if ARCH_AT91 default y if ARCH_MXC + default y if ARCH_MXS default y if ARCH_OMAP3 default y if ARCH_CNS3XXX default y if ARCH_VT8500 diff --git a/drivers/usb/chipidea/host.c b/drivers/usb/chipidea/host.c index caecad9213f5..8e9d31277c43 100644 --- a/drivers/usb/chipidea/host.c +++ b/drivers/usb/chipidea/host.c @@ -70,6 +70,9 @@ static int host_start(struct ci13xxx *ci) else ci->hcd = hcd; + if (ci->platdata->flags & CI13XXX_DISABLE_STREAMING) + hw_write(ci, OP_USBMODE, USBMODE_CI_SDIS, USBMODE_CI_SDIS); + return ret; } diff --git a/drivers/usb/class/cdc-acm.c b/drivers/usb/class/cdc-acm.c index 8d809a811e16..2d92cce260d7 100644 --- a/drivers/usb/class/cdc-acm.c +++ b/drivers/usb/class/cdc-acm.c @@ -1602,6 +1602,9 @@ static const struct usb_device_id acm_ids[] = { { USB_DEVICE(0x0572, 0x1340), /* Conexant CX93010-2x UCMxx */ .driver_info = NO_UNION_NORMAL, }, + { USB_DEVICE(0x05f9, 0x4002), /* PSC Scanning, Magellan 800i */ + .driver_info = NO_UNION_NORMAL, + }, { USB_DEVICE(0x1bbb, 0x0003), /* Alcatel OT-I650 */ .driver_info = NO_UNION_NORMAL, /* reports zero length descriptor */ }, diff --git a/drivers/usb/core/hcd.c b/drivers/usb/core/hcd.c index 4225d5e72131..8e64adf8e4d5 100644 --- a/drivers/usb/core/hcd.c +++ b/drivers/usb/core/hcd.c @@ -39,6 +39,7 @@ #include <asm/unaligned.h> #include <linux/platform_device.h> #include <linux/workqueue.h> +#include <linux/pm_runtime.h> #include <linux/usb.h> #include <linux/usb/hcd.h> @@ -1025,6 +1026,49 @@ static int register_root_hub(struct usb_hcd *hcd) return retval; } +/* + * usb_hcd_start_port_resume - a root-hub port is sending a resume signal + * @bus: the bus which the root hub belongs to + * @portnum: the port which is being resumed + * + * HCDs should call this function when they know that a resume signal is + * being sent to a root-hub port. The root hub will be prevented from + * going into autosuspend until usb_hcd_end_port_resume() is called. + * + * The bus's private lock must be held by the caller. + */ +void usb_hcd_start_port_resume(struct usb_bus *bus, int portnum) +{ + unsigned bit = 1 << portnum; + + if (!(bus->resuming_ports & bit)) { + bus->resuming_ports |= bit; + pm_runtime_get_noresume(&bus->root_hub->dev); + } +} +EXPORT_SYMBOL_GPL(usb_hcd_start_port_resume); + +/* + * usb_hcd_end_port_resume - a root-hub port has stopped sending a resume signal + * @bus: the bus which the root hub belongs to + * @portnum: the port which is being resumed + * + * HCDs should call this function when they know that a resume signal has + * stopped being sent to a root-hub port. The root hub will be allowed to + * autosuspend again. + * + * The bus's private lock must be held by the caller. + */ +void usb_hcd_end_port_resume(struct usb_bus *bus, int portnum) +{ + unsigned bit = 1 << portnum; + + if (bus->resuming_ports & bit) { + bus->resuming_ports &= ~bit; + pm_runtime_put_noidle(&bus->root_hub->dev); + } +} +EXPORT_SYMBOL_GPL(usb_hcd_end_port_resume); /*-------------------------------------------------------------------------*/ diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c index a815fd2cc5e7..cbf7168e3ce7 100644 --- a/drivers/usb/core/hub.c +++ b/drivers/usb/core/hub.c @@ -877,6 +877,60 @@ static int hub_hub_status(struct usb_hub *hub, return ret; } +static int hub_set_port_link_state(struct usb_hub *hub, int port1, + unsigned int link_status) +{ + return set_port_feature(hub->hdev, + port1 | (link_status << 3), + USB_PORT_FEAT_LINK_STATE); +} + +/* + * If USB 3.0 ports are placed into the Disabled state, they will no longer + * detect any device connects or disconnects. This is generally not what the + * USB core wants, since it expects a disabled port to produce a port status + * change event when a new device connects. + * + * Instead, set the link state to Disabled, wait for the link to settle into + * that state, clear any change bits, and then put the port into the RxDetect + * state. + */ +static int hub_usb3_port_disable(struct usb_hub *hub, int port1) +{ + int ret; + int total_time; + u16 portchange, portstatus; + + if (!hub_is_superspeed(hub->hdev)) + return -EINVAL; + + ret = hub_set_port_link_state(hub, port1, USB_SS_PORT_LS_SS_DISABLED); + if (ret) { + dev_err(hub->intfdev, "cannot disable port %d (err = %d)\n", + port1, ret); + return ret; + } + + /* Wait for the link to enter the disabled state. */ + for (total_time = 0; ; total_time += HUB_DEBOUNCE_STEP) { + ret = hub_port_status(hub, port1, &portstatus, &portchange); + if (ret < 0) + return ret; + + if ((portstatus & USB_PORT_STAT_LINK_STATE) == + USB_SS_PORT_LS_SS_DISABLED) + break; + if (total_time >= HUB_DEBOUNCE_TIMEOUT) + break; + msleep(HUB_DEBOUNCE_STEP); + } + if (total_time >= HUB_DEBOUNCE_TIMEOUT) + dev_warn(hub->intfdev, "Could not disable port %d after %d ms\n", + port1, total_time); + + return hub_set_port_link_state(hub, port1, USB_SS_PORT_LS_RX_DETECT); +} + static int hub_port_disable(struct usb_hub *hub, int port1, int set_state) { struct usb_device *hdev = hub->hdev; @@ -885,8 +939,13 @@ static int hub_port_disable(struct usb_hub *hub, int port1, int set_state) if (hub->ports[port1 - 1]->child && set_state) usb_set_device_state(hub->ports[port1 - 1]->child, USB_STATE_NOTATTACHED); - if (!hub->error && !hub_is_superspeed(hub->hdev)) - ret = clear_port_feature(hdev, port1, USB_PORT_FEAT_ENABLE); + if (!hub->error) { + if (hub_is_superspeed(hub->hdev)) + ret = hub_usb3_port_disable(hub, port1); + else + ret = clear_port_feature(hdev, port1, + USB_PORT_FEAT_ENABLE); + } if (ret) dev_err(hub->intfdev, "cannot disable port %d (err = %d)\n", port1, ret); @@ -2440,7 +2499,7 @@ static unsigned hub_is_wusb(struct usb_hub *hub) #define HUB_SHORT_RESET_TIME 10 #define HUB_BH_RESET_TIME 50 #define HUB_LONG_RESET_TIME 200 -#define HUB_RESET_TIMEOUT 500 +#define HUB_RESET_TIMEOUT 800 static int hub_port_reset(struct usb_hub *hub, int port1, struct usb_device *udev, unsigned int delay, bool warm); @@ -2475,6 +2534,10 @@ static int hub_port_wait_reset(struct usb_hub *hub, int port1, if (ret < 0) return ret; + /* The port state is unknown until the reset completes. */ + if ((portstatus & USB_PORT_STAT_RESET)) + goto delay; + /* * Some buggy devices require a warm reset to be issued even * when the port appears not to be connected. @@ -2520,11 +2583,7 @@ static int hub_port_wait_reset(struct usb_hub *hub, int port1, if ((portchange & USB_PORT_STAT_C_CONNECTION)) return -ENOTCONN; - /* if we`ve finished resetting, then break out of - * the loop - */ - if (!(portstatus & USB_PORT_STAT_RESET) && - (portstatus & USB_PORT_STAT_ENABLE)) { + if ((portstatus & USB_PORT_STAT_ENABLE)) { if (hub_is_wusb(hub)) udev->speed = USB_SPEED_WIRELESS; else if (hub_is_superspeed(hub->hdev)) @@ -2538,10 +2597,15 @@ static int hub_port_wait_reset(struct usb_hub *hub, int port1, return 0; } } else { - if (portchange & USB_PORT_STAT_C_BH_RESET) - return 0; + if (!(portstatus & USB_PORT_STAT_CONNECTION) || + hub_port_warm_reset_required(hub, + portstatus)) + return -ENOTCONN; + + return 0; } +delay: /* switch to the long delay after two short delay failures */ if (delay_time >= 2 * HUB_SHORT_RESET_TIME) delay = HUB_LONG_RESET_TIME; @@ -2565,14 +2629,11 @@ static void hub_port_finish_reset(struct usb_hub *hub, int port1, msleep(10 + 40); update_devnum(udev, 0); hcd = bus_to_hcd(udev->bus); - if (hcd->driver->reset_device) { - *status = hcd->driver->reset_device(hcd, udev); - if (*status < 0) { - dev_err(&udev->dev, "Cannot reset " - "HCD device state\n"); - break; - } - } + /* The xHC may think the device is already reset, + * so ignore the status. + */ + if (hcd->driver->reset_device) + hcd->driver->reset_device(hcd, udev); } /* FALL THROUGH */ case -ENOTCONN: @@ -2580,16 +2641,16 @@ static void hub_port_finish_reset(struct usb_hub *hub, int port1, clear_port_feature(hub->hdev, port1, USB_PORT_FEAT_C_RESET); /* FIXME need disconnect() for NOTATTACHED device */ - if (warm) { + if (hub_is_superspeed(hub->hdev)) { clear_port_feature(hub->hdev, port1, USB_PORT_FEAT_C_BH_PORT_RESET); clear_port_feature(hub->hdev, port1, USB_PORT_FEAT_C_PORT_LINK_STATE); - } else { + } + if (!warm) usb_set_device_state(udev, *status ? USB_STATE_NOTATTACHED : USB_STATE_DEFAULT); - } break; } } @@ -2777,6 +2838,23 @@ void usb_enable_ltm(struct usb_device *udev) EXPORT_SYMBOL_GPL(usb_enable_ltm); #ifdef CONFIG_USB_SUSPEND +/* + * usb_disable_function_remotewakeup - disable usb3.0 + * device's function remote wakeup + * @udev: target device + * + * Assume there's only one function on the USB 3.0 + * device and disable remote wake for the first + * interface. FIXME if the interface association + * descriptor shows there's more than one function. + */ +static int usb_disable_function_remotewakeup(struct usb_device *udev) +{ + return usb_control_msg(udev, usb_sndctrlpipe(udev, 0), + USB_REQ_CLEAR_FEATURE, USB_RECIP_INTERFACE, + USB_INTRF_FUNC_SUSPEND, 0, NULL, 0, + USB_CTRL_SET_TIMEOUT); +} /* * usb_port_suspend - suspend a usb device's upstream port @@ -2894,12 +2972,19 @@ int usb_port_suspend(struct usb_device *udev, pm_message_t msg) dev_dbg(hub->intfdev, "can't suspend port %d, status %d\n", port1, status); /* paranoia: "should not happen" */ - if (udev->do_remote_wakeup) - (void) usb_control_msg(udev, usb_sndctrlpipe(udev, 0), - USB_REQ_CLEAR_FEATURE, USB_RECIP_DEVICE, - USB_DEVICE_REMOTE_WAKEUP, 0, - NULL, 0, - USB_CTRL_SET_TIMEOUT); + if (udev->do_remote_wakeup) { + if (!hub_is_superspeed(hub->hdev)) { + (void) usb_control_msg(udev, + usb_sndctrlpipe(udev, 0), + USB_REQ_CLEAR_FEATURE, + USB_RECIP_DEVICE, + USB_DEVICE_REMOTE_WAKEUP, 0, + NULL, 0, + USB_CTRL_SET_TIMEOUT); + } else + (void) usb_disable_function_remotewakeup(udev); + + } /* Try to enable USB2 hardware LPM again */ if (udev->usb2_hw_lpm_capable == 1) @@ -2939,7 +3024,7 @@ int usb_port_suspend(struct usb_device *udev, pm_message_t msg) static int finish_port_resume(struct usb_device *udev) { int status = 0; - u16 devstatus; + u16 devstatus = 0; /* caller owns the udev device lock */ dev_dbg(&udev->dev, "%s\n", @@ -2984,21 +3069,37 @@ static int finish_port_resume(struct usb_device *udev) if (status) { dev_dbg(&udev->dev, "gone after usb resume? status %d\n", status); - } else if (udev->actconfig) { - le16_to_cpus(&devstatus); - if (devstatus & (1 << USB_DEVICE_REMOTE_WAKEUP)) { - status = usb_control_msg(udev, - usb_sndctrlpipe(udev, 0), - USB_REQ_CLEAR_FEATURE, + /* + * There are a few quirky devices which violate the standard + * by claiming to have remote wakeup enabled after a reset, + * which crash if the feature is cleared, hence check for + * udev->reset_resume + */ + } else if (udev->actconfig && !udev->reset_resume) { + if (!hub_is_superspeed(udev->parent)) { + le16_to_cpus(&devstatus); + if (devstatus & (1 << USB_DEVICE_REMOTE_WAKEUP)) + status = usb_control_msg(udev, + usb_sndctrlpipe(udev, 0), + USB_REQ_CLEAR_FEATURE, USB_RECIP_DEVICE, - USB_DEVICE_REMOTE_WAKEUP, 0, - NULL, 0, - USB_CTRL_SET_TIMEOUT); - if (status) - dev_dbg(&udev->dev, - "disable remote wakeup, status %d\n", - status); + USB_DEVICE_REMOTE_WAKEUP, 0, + NULL, 0, + USB_CTRL_SET_TIMEOUT); + } else { + status = usb_get_status(udev, USB_RECIP_INTERFACE, 0, + &devstatus); + le16_to_cpus(&devstatus); + if (!status && devstatus & (USB_INTRF_STAT_FUNC_RW_CAP + | USB_INTRF_STAT_FUNC_RW)) + status = + usb_disable_function_remotewakeup(udev); } + + if (status) + dev_dbg(&udev->dev, + "disable remote wakeup, status %d\n", + status); status = 0; } return status; @@ -4638,9 +4739,14 @@ static void hub_events(void) * SS.Inactive state. */ if (hub_port_warm_reset_required(hub, portstatus)) { + int status; + dev_dbg(hub_dev, "warm reset port %d\n", i); - hub_port_reset(hub, i, NULL, + status = hub_port_reset(hub, i, NULL, HUB_BH_RESET_TIME, true); + if (status < 0) + hub_port_disable(hub, i, 1); + connect_change = 0; } if (connect_change) diff --git a/drivers/usb/core/quirks.c b/drivers/usb/core/quirks.c index fdefd9c7f7af..3113c1d71442 100644 --- a/drivers/usb/core/quirks.c +++ b/drivers/usb/core/quirks.c @@ -43,6 +43,9 @@ static const struct usb_device_id usb_quirk_list[] = { /* Creative SB Audigy 2 NX */ { USB_DEVICE(0x041e, 0x3020), .driver_info = USB_QUIRK_RESET_RESUME }, + /* Microsoft LifeCam-VX700 v2.0 */ + { USB_DEVICE(0x045e, 0x0770), .driver_info = USB_QUIRK_RESET_RESUME }, + /* Logitech Quickcam Fusion */ { USB_DEVICE(0x046d, 0x08c1), .driver_info = USB_QUIRK_RESET_RESUME }, diff --git a/drivers/usb/dwc3/debugfs.c b/drivers/usb/dwc3/debugfs.c index 92604b4f9712..5945aadaa1c9 100644 --- a/drivers/usb/dwc3/debugfs.c +++ b/drivers/usb/dwc3/debugfs.c @@ -56,7 +56,7 @@ #define dump_register(nm) \ { \ .name = __stringify(nm), \ - .offset = DWC3_ ##nm, \ + .offset = DWC3_ ##nm - DWC3_GLOBALS_REGS_START, \ } static const struct debugfs_reg32 dwc3_regs[] = { diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c index 2e43b332aae8..2fdd767f8fe8 100644 --- a/drivers/usb/dwc3/gadget.c +++ b/drivers/usb/dwc3/gadget.c @@ -1605,6 +1605,7 @@ static int dwc3_gadget_init_endpoints(struct dwc3 *dwc) if (epnum == 0 || epnum == 1) { dep->endpoint.maxpacket = 512; + dep->endpoint.maxburst = 1; dep->endpoint.ops = &dwc3_gadget_ep0_ops; if (!epnum) dwc->gadget.ep0 = &dep->endpoint; diff --git a/drivers/usb/gadget/amd5536udc.c b/drivers/usb/gadget/amd5536udc.c index fc0ec5e0d58e..d9f6b9372491 100644 --- a/drivers/usb/gadget/amd5536udc.c +++ b/drivers/usb/gadget/amd5536udc.c @@ -3231,7 +3231,7 @@ static int udc_pci_probe( } if (!pdev->irq) { - dev_err(&dev->pdev->dev, "irq not set\n"); + dev_err(&pdev->dev, "irq not set\n"); kfree(dev); dev = NULL; retval = -ENODEV; @@ -3250,7 +3250,7 @@ static int udc_pci_probe( dev->txfifo = (u32 __iomem *)(dev->virt_addr + UDC_TXFIFO_ADDR); if (request_irq(pdev->irq, udc_irq, IRQF_SHARED, name, dev) != 0) { - dev_dbg(&dev->pdev->dev, "request_irq(%d) fail\n", pdev->irq); + dev_dbg(&pdev->dev, "request_irq(%d) fail\n", pdev->irq); kfree(dev); dev = NULL; retval = -EBUSY; diff --git a/drivers/usb/gadget/dummy_hcd.c b/drivers/usb/gadget/dummy_hcd.c index 95d584dbed13..8cf0c0f6fa1f 100644 --- a/drivers/usb/gadget/dummy_hcd.c +++ b/drivers/usb/gadget/dummy_hcd.c @@ -130,10 +130,7 @@ static const char ep0name[] = "ep0"; static const char *const ep_name[] = { ep0name, /* everyone has ep0 */ - /* act like a net2280: high speed, six configurable endpoints */ - "ep-a", "ep-b", "ep-c", "ep-d", "ep-e", "ep-f", - - /* or like pxa250: fifteen fixed function endpoints */ + /* act like a pxa250: fifteen fixed function endpoints */ "ep1in-bulk", "ep2out-bulk", "ep3in-iso", "ep4out-iso", "ep5in-int", "ep6in-bulk", "ep7out-bulk", "ep8in-iso", "ep9out-iso", "ep10in-int", "ep11in-bulk", "ep12out-bulk", "ep13in-iso", "ep14out-iso", @@ -141,6 +138,10 @@ static const char *const ep_name[] = { /* or like sa1100: two fixed function endpoints */ "ep1out-bulk", "ep2in-bulk", + + /* and now some generic EPs so we have enough in multi config */ + "ep3out", "ep4in", "ep5out", "ep6out", "ep7in", "ep8out", "ep9in", + "ep10out", "ep11out", "ep12in", "ep13out", "ep14in", "ep15out", }; #define DUMMY_ENDPOINTS ARRAY_SIZE(ep_name) diff --git a/drivers/usb/gadget/f_fs.c b/drivers/usb/gadget/f_fs.c index 4a6961c517f2..8c2f25121149 100644 --- a/drivers/usb/gadget/f_fs.c +++ b/drivers/usb/gadget/f_fs.c @@ -1153,15 +1153,15 @@ static int ffs_fs_parse_opts(struct ffs_sb_fill_data *data, char *opts) pr_err("%s: unmapped value: %lu\n", opts, value); return -EINVAL; } - } - else if (!memcmp(opts, "gid", 3)) + } else if (!memcmp(opts, "gid", 3)) { data->perms.gid = make_kgid(current_user_ns(), value); if (!gid_valid(data->perms.gid)) { pr_err("%s: unmapped value: %lu\n", opts, value); return -EINVAL; } - else + } else { goto invalid; + } break; default: diff --git a/drivers/usb/gadget/fsl_mxc_udc.c b/drivers/usb/gadget/fsl_mxc_udc.c index 1b0f086426bd..d3bd7b095ba3 100644 --- a/drivers/usb/gadget/fsl_mxc_udc.c +++ b/drivers/usb/gadget/fsl_mxc_udc.c @@ -18,14 +18,13 @@ #include <linux/platform_device.h> #include <linux/io.h> -#include <mach/hardware.h> - static struct clk *mxc_ahb_clk; static struct clk *mxc_per_clk; static struct clk *mxc_ipg_clk; /* workaround ENGcm09152 for i.MX35 */ -#define USBPHYCTRL_OTGBASE_OFFSET 0x608 +#define MX35_USBPHYCTRL_OFFSET 0x600 +#define USBPHYCTRL_OTGBASE_OFFSET 0x8 #define USBPHYCTRL_EVDO (1 << 23) int fsl_udc_clk_init(struct platform_device *pdev) @@ -59,7 +58,7 @@ int fsl_udc_clk_init(struct platform_device *pdev) clk_prepare_enable(mxc_per_clk); /* make sure USB_CLK is running at 60 MHz +/- 1000 Hz */ - if (!cpu_is_mx51()) { + if (!strcmp(pdev->id_entry->name, "imx-udc-mx27")) { freq = clk_get_rate(mxc_per_clk); if (pdata->phy_mode != FSL_USB2_PHY_ULPI && (freq < 59999000 || freq > 60001000)) { @@ -79,27 +78,40 @@ eclkrate: return ret; } -void fsl_udc_clk_finalize(struct platform_device *pdev) +int fsl_udc_clk_finalize(struct platform_device *pdev) { struct fsl_usb2_platform_data *pdata = pdev->dev.platform_data; - if (cpu_is_mx35()) { - unsigned int v; + int ret = 0; - /* workaround ENGcm09152 for i.MX35 */ - if (pdata->workaround & FLS_USB2_WORKAROUND_ENGCM09152) { - v = readl(MX35_IO_ADDRESS(MX35_USB_BASE_ADDR + - USBPHYCTRL_OTGBASE_OFFSET)); - writel(v | USBPHYCTRL_EVDO, - MX35_IO_ADDRESS(MX35_USB_BASE_ADDR + - USBPHYCTRL_OTGBASE_OFFSET)); + /* workaround ENGcm09152 for i.MX35 */ + if (pdata->workaround & FLS_USB2_WORKAROUND_ENGCM09152) { + unsigned int v; + struct resource *res = platform_get_resource + (pdev, IORESOURCE_MEM, 0); + void __iomem *phy_regs = ioremap(res->start + + MX35_USBPHYCTRL_OFFSET, 512); + if (!phy_regs) { + dev_err(&pdev->dev, "ioremap for phy address fails\n"); + ret = -EINVAL; + goto ioremap_err; } + + v = readl(phy_regs + USBPHYCTRL_OTGBASE_OFFSET); + writel(v | USBPHYCTRL_EVDO, + phy_regs + USBPHYCTRL_OTGBASE_OFFSET); + + iounmap(phy_regs); } + +ioremap_err: /* ULPI transceivers don't need usbpll */ if (pdata->phy_mode == FSL_USB2_PHY_ULPI) { clk_disable_unprepare(mxc_per_clk); mxc_per_clk = NULL; } + + return ret; } void fsl_udc_clk_release(void) diff --git a/drivers/usb/gadget/fsl_udc_core.c b/drivers/usb/gadget/fsl_udc_core.c index c19f7f13790b..667275cb7bad 100644 --- a/drivers/usb/gadget/fsl_udc_core.c +++ b/drivers/usb/gadget/fsl_udc_core.c @@ -41,6 +41,7 @@ #include <linux/fsl_devices.h> #include <linux/dmapool.h> #include <linux/delay.h> +#include <linux/of_device.h> #include <asm/byteorder.h> #include <asm/io.h> @@ -2438,11 +2439,6 @@ static int __init fsl_udc_probe(struct platform_device *pdev) unsigned int i; u32 dccparams; - if (strcmp(pdev->name, driver_name)) { - VDBG("Wrong device"); - return -ENODEV; - } - udc_controller = kzalloc(sizeof(struct fsl_udc), GFP_KERNEL); if (udc_controller == NULL) { ERR("malloc udc failed\n"); @@ -2547,7 +2543,9 @@ static int __init fsl_udc_probe(struct platform_device *pdev) dr_controller_setup(udc_controller); } - fsl_udc_clk_finalize(pdev); + ret = fsl_udc_clk_finalize(pdev); + if (ret) + goto err_free_irq; /* Setup gadget structure */ udc_controller->gadget.ops = &fsl_gadget_ops; @@ -2756,22 +2754,32 @@ static int fsl_udc_otg_resume(struct device *dev) return fsl_udc_resume(NULL); } - /*------------------------------------------------------------------------- Register entry point for the peripheral controller driver --------------------------------------------------------------------------*/ - +static const struct platform_device_id fsl_udc_devtype[] = { + { + .name = "imx-udc-mx27", + }, { + .name = "imx-udc-mx51", + }, { + /* sentinel */ + } +}; +MODULE_DEVICE_TABLE(platform, fsl_udc_devtype); static struct platform_driver udc_driver = { - .remove = __exit_p(fsl_udc_remove), + .remove = __exit_p(fsl_udc_remove), + /* Just for FSL i.mx SoC currently */ + .id_table = fsl_udc_devtype, /* these suspend and resume are not usb suspend and resume */ - .suspend = fsl_udc_suspend, - .resume = fsl_udc_resume, - .driver = { - .name = (char *)driver_name, - .owner = THIS_MODULE, - /* udc suspend/resume called from OTG driver */ - .suspend = fsl_udc_otg_suspend, - .resume = fsl_udc_otg_resume, + .suspend = fsl_udc_suspend, + .resume = fsl_udc_resume, + .driver = { + .name = (char *)driver_name, + .owner = THIS_MODULE, + /* udc suspend/resume called from OTG driver */ + .suspend = fsl_udc_otg_suspend, + .resume = fsl_udc_otg_resume, }, }; diff --git a/drivers/usb/gadget/fsl_usb2_udc.h b/drivers/usb/gadget/fsl_usb2_udc.h index f61a967f7082..c6703bb07b23 100644 --- a/drivers/usb/gadget/fsl_usb2_udc.h +++ b/drivers/usb/gadget/fsl_usb2_udc.h @@ -592,15 +592,16 @@ static inline struct ep_queue_head *get_qh_by_ep(struct fsl_ep *ep) struct platform_device; #ifdef CONFIG_ARCH_MXC int fsl_udc_clk_init(struct platform_device *pdev); -void fsl_udc_clk_finalize(struct platform_device *pdev); +int fsl_udc_clk_finalize(struct platform_device *pdev); void fsl_udc_clk_release(void); #else static inline int fsl_udc_clk_init(struct platform_device *pdev) { return 0; } -static inline void fsl_udc_clk_finalize(struct platform_device *pdev) +static inline int fsl_udc_clk_finalize(struct platform_device *pdev) { + return 0; } static inline void fsl_udc_clk_release(void) { diff --git a/drivers/usb/gadget/mv_udc_core.c b/drivers/usb/gadget/mv_udc_core.c index 379aac7b82fc..6e8b1272ebce 100644 --- a/drivers/usb/gadget/mv_udc_core.c +++ b/drivers/usb/gadget/mv_udc_core.c @@ -1012,7 +1012,7 @@ static void udc_clock_enable(struct mv_udc *udc) unsigned int i; for (i = 0; i < udc->clknum; i++) - clk_enable(udc->clk[i]); + clk_prepare_enable(udc->clk[i]); } static void udc_clock_disable(struct mv_udc *udc) @@ -1020,7 +1020,7 @@ static void udc_clock_disable(struct mv_udc *udc) unsigned int i; for (i = 0; i < udc->clknum; i++) - clk_disable(udc->clk[i]); + clk_disable_unprepare(udc->clk[i]); } static void udc_stop(struct mv_udc *udc) diff --git a/drivers/usb/gadget/s3c-hsotg.c b/drivers/usb/gadget/s3c-hsotg.c index 141971d9051e..439c3f972f8c 100644 --- a/drivers/usb/gadget/s3c-hsotg.c +++ b/drivers/usb/gadget/s3c-hsotg.c @@ -3477,12 +3477,11 @@ static void s3c_hsotg_delete_debug(struct s3c_hsotg *hsotg) /** * s3c_hsotg_release - release callback for hsotg device * @dev: Device to for which release is called + * + * Nothing to do as the resource is allocated using devm_ API. */ static void s3c_hsotg_release(struct device *dev) { - struct s3c_hsotg *hsotg = dev_get_drvdata(dev); - - kfree(hsotg); } /** diff --git a/drivers/usb/gadget/tcm_usb_gadget.c b/drivers/usb/gadget/tcm_usb_gadget.c index 4f7f76f00c74..7cacd6ae818e 100644 --- a/drivers/usb/gadget/tcm_usb_gadget.c +++ b/drivers/usb/gadget/tcm_usb_gadget.c @@ -1794,9 +1794,10 @@ static int tcm_usbg_drop_nexus(struct usbg_tpg *tpg) tpg->tpg_nexus = NULL; kfree(tv_nexus); + ret = 0; out: mutex_unlock(&tpg->tpg_mutex); - return 0; + return ret; } static ssize_t tcm_usbg_tpg_store_nexus( diff --git a/drivers/usb/gadget/u_serial.c b/drivers/usb/gadget/u_serial.c index d0f95482f40e..598dcc1212f0 100644 --- a/drivers/usb/gadget/u_serial.c +++ b/drivers/usb/gadget/u_serial.c @@ -887,7 +887,7 @@ static void gs_close(struct tty_struct *tty, struct file *file) pr_debug("gs_close: ttyGS%d (%p,%p) done!\n", port->port_num, tty, file); - wake_up_interruptible(&port->port.close_wait); + wake_up(&port->port.close_wait); exit: spin_unlock_irq(&port->port_lock); } diff --git a/drivers/usb/host/Kconfig b/drivers/usb/host/Kconfig index d6bb128ce21e..3a21c5d683c0 100644 --- a/drivers/usb/host/Kconfig +++ b/drivers/usb/host/Kconfig @@ -148,7 +148,7 @@ config USB_EHCI_FSL Variation of ARC USB block used in some Freescale chips. config USB_EHCI_MXC - bool "Support for Freescale i.MX on-chip EHCI USB controller" + tristate "Support for Freescale i.MX on-chip EHCI USB controller" depends on USB_EHCI_HCD && ARCH_MXC select USB_EHCI_ROOT_HUB_TT ---help--- diff --git a/drivers/usb/host/Makefile b/drivers/usb/host/Makefile index 1eb4c3006e9e..001fbff2fdef 100644 --- a/drivers/usb/host/Makefile +++ b/drivers/usb/host/Makefile @@ -26,6 +26,7 @@ obj-$(CONFIG_PCI) += pci-quirks.o obj-$(CONFIG_USB_EHCI_HCD) += ehci-hcd.o obj-$(CONFIG_USB_EHCI_PCI) += ehci-pci.o obj-$(CONFIG_USB_EHCI_HCD_PLATFORM) += ehci-platform.o +obj-$(CONFIG_USB_EHCI_MXC) += ehci-mxc.o obj-$(CONFIG_USB_OXU210HP_HCD) += oxu210hp-hcd.o obj-$(CONFIG_USB_ISP116X_HCD) += isp116x-hcd.o diff --git a/drivers/usb/host/ehci-fsl.c b/drivers/usb/host/ehci-fsl.c index fd9b5424b860..d81d2fcbff18 100644 --- a/drivers/usb/host/ehci-fsl.c +++ b/drivers/usb/host/ehci-fsl.c @@ -230,7 +230,7 @@ static int ehci_fsl_setup_phy(struct usb_hcd *hcd, switch (phy_mode) { case FSL_USB2_PHY_ULPI: - if (pdata->controller_ver) { + if (pdata->have_sysif_regs && pdata->controller_ver) { /* controller version 1.6 or above */ setbits32(non_ehci + FSL_SOC_USB_CTRL, ULPI_PHY_CLK_SEL); @@ -251,7 +251,7 @@ static int ehci_fsl_setup_phy(struct usb_hcd *hcd, portsc |= PORT_PTS_PTW; /* fall through */ case FSL_USB2_PHY_UTMI: - if (pdata->controller_ver) { + if (pdata->have_sysif_regs && pdata->controller_ver) { /* controller version 1.6 or above */ setbits32(non_ehci + FSL_SOC_USB_CTRL, UTMI_PHY_EN); mdelay(FSL_UTMI_PHY_DLY); /* Delay for UTMI PHY CLK to @@ -267,7 +267,8 @@ static int ehci_fsl_setup_phy(struct usb_hcd *hcd, break; } - if (pdata->controller_ver && (phy_mode == FSL_USB2_PHY_ULPI)) { + if (pdata->have_sysif_regs && pdata->controller_ver && + (phy_mode == FSL_USB2_PHY_ULPI)) { /* check PHY_CLK_VALID to get phy clk valid */ if (!spin_event_timeout(in_be32(non_ehci + FSL_SOC_USB_CTRL) & PHY_CLK_VALID, FSL_USB_PHY_CLK_TIMEOUT, 0)) { @@ -278,7 +279,7 @@ static int ehci_fsl_setup_phy(struct usb_hcd *hcd, ehci_writel(ehci, portsc, &ehci->regs->port_status[port_offset]); - if (phy_mode != FSL_USB2_PHY_ULPI) + if (phy_mode != FSL_USB2_PHY_ULPI && pdata->have_sysif_regs) setbits32(non_ehci + FSL_SOC_USB_CTRL, USB_CTRL_USB_EN); return 0; diff --git a/drivers/usb/host/ehci-hcd.c b/drivers/usb/host/ehci-hcd.c index c97503bb0b0e..b416a3fc9959 100644 --- a/drivers/usb/host/ehci-hcd.c +++ b/drivers/usb/host/ehci-hcd.c @@ -74,10 +74,6 @@ static const char hcd_name [] = "ehci_hcd"; #undef VERBOSE_DEBUG #undef EHCI_URB_TRACE -#ifdef DEBUG -#define EHCI_STATS -#endif - /* magic numbers that can affect system performance */ #define EHCI_TUNE_CERR 3 /* 0-3 qtd retries; 0 == don't stop */ #define EHCI_TUNE_RL_HS 4 /* nak throttle; see 4.9 */ @@ -801,6 +797,7 @@ static irqreturn_t ehci_irq (struct usb_hcd *hcd) ehci->reset_done[i] = jiffies + msecs_to_jiffies(25); set_bit(i, &ehci->resuming_ports); ehci_dbg (ehci, "port %d remote wakeup\n", i + 1); + usb_hcd_start_port_resume(&hcd->self, i); mod_timer(&hcd->rh_timer, ehci->reset_done[i]); } } @@ -1250,11 +1247,6 @@ MODULE_LICENSE ("GPL"); #define PLATFORM_DRIVER ehci_fsl_driver #endif -#ifdef CONFIG_USB_EHCI_MXC -#include "ehci-mxc.c" -#define PLATFORM_DRIVER ehci_mxc_driver -#endif - #ifdef CONFIG_USB_EHCI_SH #include "ehci-sh.c" #define PLATFORM_DRIVER ehci_hcd_sh_driver @@ -1352,7 +1344,8 @@ MODULE_LICENSE ("GPL"); #if !IS_ENABLED(CONFIG_USB_EHCI_PCI) && \ !IS_ENABLED(CONFIG_USB_EHCI_HCD_PLATFORM) && \ - !defined(CONFIG_USB_CHIPIDEA_HOST) && \ + !IS_ENABLED(CONFIG_USB_CHIPIDEA_HOST) && \ + !IS_ENABLED(CONFIG_USB_EHCI_MXC) && \ !defined(PLATFORM_DRIVER) && \ !defined(PS3_SYSTEM_BUS_DRIVER) && \ !defined(OF_PLATFORM_DRIVER) && \ diff --git a/drivers/usb/host/ehci-hub.c b/drivers/usb/host/ehci-hub.c index 4ccb97c0678f..4d3b294f203e 100644 --- a/drivers/usb/host/ehci-hub.c +++ b/drivers/usb/host/ehci-hub.c @@ -649,7 +649,11 @@ ehci_hub_status_data (struct usb_hcd *hcd, char *buf) status = STS_PCD; } } - /* FIXME autosuspend idle root hubs */ + + /* If a resume is in progress, make sure it can finish */ + if (ehci->resuming_ports) + mod_timer(&hcd->rh_timer, jiffies + msecs_to_jiffies(25)); + spin_unlock_irqrestore (&ehci->lock, flags); return status ? retval : 0; } @@ -851,6 +855,7 @@ static int ehci_hub_control ( /* resume signaling for 20 msec */ ehci->reset_done[wIndex] = jiffies + msecs_to_jiffies(20); + usb_hcd_start_port_resume(&hcd->self, wIndex); /* check the port again */ mod_timer(&ehci_to_hcd(ehci)->rh_timer, ehci->reset_done[wIndex]); @@ -862,6 +867,7 @@ static int ehci_hub_control ( clear_bit(wIndex, &ehci->suspended_ports); set_bit(wIndex, &ehci->port_c_suspend); ehci->reset_done[wIndex] = 0; + usb_hcd_end_port_resume(&hcd->self, wIndex); /* stop resume signaling */ temp = ehci_readl(ehci, status_reg); @@ -950,6 +956,7 @@ static int ehci_hub_control ( ehci->reset_done[wIndex] = 0; if (temp & PORT_PE) set_bit(wIndex, &ehci->port_c_suspend); + usb_hcd_end_port_resume(&hcd->self, wIndex); } if (temp & PORT_OC) diff --git a/drivers/usb/host/ehci-mv.c b/drivers/usb/host/ehci-mv.c index f7bfc0b898b9..6c56297ea16b 100644 --- a/drivers/usb/host/ehci-mv.c +++ b/drivers/usb/host/ehci-mv.c @@ -43,7 +43,7 @@ static void ehci_clock_enable(struct ehci_hcd_mv *ehci_mv) unsigned int i; for (i = 0; i < ehci_mv->clknum; i++) - clk_enable(ehci_mv->clk[i]); + clk_prepare_enable(ehci_mv->clk[i]); } static void ehci_clock_disable(struct ehci_hcd_mv *ehci_mv) @@ -51,7 +51,7 @@ static void ehci_clock_disable(struct ehci_hcd_mv *ehci_mv) unsigned int i; for (i = 0; i < ehci_mv->clknum; i++) - clk_disable(ehci_mv->clk[i]); + clk_disable_unprepare(ehci_mv->clk[i]); } static int mv_ehci_enable(struct ehci_hcd_mv *ehci_mv) diff --git a/drivers/usb/host/ehci-mxc.c b/drivers/usb/host/ehci-mxc.c index ec7f5d2c90de..dedb80bb8d40 100644 --- a/drivers/usb/host/ehci-mxc.c +++ b/drivers/usb/host/ehci-mxc.c @@ -17,75 +17,38 @@ * Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/io.h> #include <linux/platform_device.h> #include <linux/clk.h> #include <linux/delay.h> #include <linux/usb/otg.h> #include <linux/usb/ulpi.h> #include <linux/slab.h> +#include <linux/usb.h> +#include <linux/usb/hcd.h> #include <linux/platform_data/usb-ehci-mxc.h> #include <asm/mach-types.h> +#include "ehci.h" + +#define DRIVER_DESC "Freescale On-Chip EHCI Host driver" + +static const char hcd_name[] = "ehci-mxc"; + #define ULPI_VIEWPORT_OFFSET 0x170 struct ehci_mxc_priv { struct clk *usbclk, *ahbclk, *phyclk; - struct usb_hcd *hcd; }; -/* called during probe() after chip reset completes */ -static int ehci_mxc_setup(struct usb_hcd *hcd) -{ - hcd->has_tt = 1; - - return ehci_setup(hcd); -} +static struct hc_driver __read_mostly ehci_mxc_hc_driver; -static const struct hc_driver ehci_mxc_hc_driver = { - .description = hcd_name, - .product_desc = "Freescale On-Chip EHCI Host Controller", - .hcd_priv_size = sizeof(struct ehci_hcd), - - /* - * generic hardware linkage - */ - .irq = ehci_irq, - .flags = HCD_USB2 | HCD_MEMORY, - - /* - * basic lifecycle operations - */ - .reset = ehci_mxc_setup, - .start = ehci_run, - .stop = ehci_stop, - .shutdown = ehci_shutdown, - - /* - * managing i/o requests and associated device resources - */ - .urb_enqueue = ehci_urb_enqueue, - .urb_dequeue = ehci_urb_dequeue, - .endpoint_disable = ehci_endpoint_disable, - .endpoint_reset = ehci_endpoint_reset, - - /* - * scheduling support - */ - .get_frame_number = ehci_get_frame, - - /* - * root hub support - */ - .hub_status_data = ehci_hub_status_data, - .hub_control = ehci_hub_control, - .bus_suspend = ehci_bus_suspend, - .bus_resume = ehci_bus_resume, - .relinquish_port = ehci_relinquish_port, - .port_handed_over = ehci_port_handed_over, - - .clear_tt_buffer_complete = ehci_clear_tt_buffer_complete, +static const struct ehci_driver_overrides ehci_mxc_overrides __initdata = { + .extra_priv_size = sizeof(struct ehci_mxc_priv), }; static int ehci_mxc_drv_probe(struct platform_device *pdev) @@ -112,12 +75,6 @@ static int ehci_mxc_drv_probe(struct platform_device *pdev) if (!hcd) return -ENOMEM; - priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL); - if (!priv) { - ret = -ENOMEM; - goto err_alloc; - } - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); if (!res) { dev_err(dev, "Found HC with no register addr. Check setup!\n"); @@ -135,6 +92,10 @@ static int ehci_mxc_drv_probe(struct platform_device *pdev) goto err_alloc; } + hcd->has_tt = 1; + ehci = hcd_to_ehci(hcd); + priv = (struct ehci_mxc_priv *) ehci->priv; + /* enable clocks */ priv->usbclk = devm_clk_get(&pdev->dev, "ipg"); if (IS_ERR(priv->usbclk)) { @@ -169,8 +130,6 @@ static int ehci_mxc_drv_probe(struct platform_device *pdev) mdelay(10); } - ehci = hcd_to_ehci(hcd); - /* EHCI registers start at offset 0x100 */ ehci->caps = hcd->regs + 0x100; ehci->regs = hcd->regs + 0x100 + @@ -198,8 +157,7 @@ static int ehci_mxc_drv_probe(struct platform_device *pdev) } } - priv->hcd = hcd; - platform_set_drvdata(pdev, priv); + platform_set_drvdata(pdev, hcd); ret = usb_add_hcd(hcd, irq, IRQF_SHARED); if (ret) @@ -244,8 +202,11 @@ err_alloc: static int __exit ehci_mxc_drv_remove(struct platform_device *pdev) { struct mxc_usbh_platform_data *pdata = pdev->dev.platform_data; - struct ehci_mxc_priv *priv = platform_get_drvdata(pdev); - struct usb_hcd *hcd = priv->hcd; + struct usb_hcd *hcd = platform_get_drvdata(pdev); + struct ehci_hcd *ehci = hcd_to_ehci(hcd); + struct ehci_mxc_priv *priv = (struct ehci_mxc_priv *) ehci->priv; + + usb_remove_hcd(hcd); if (pdata && pdata->exit) pdata->exit(pdev); @@ -253,23 +214,20 @@ static int __exit ehci_mxc_drv_remove(struct platform_device *pdev) if (pdata->otg) usb_phy_shutdown(pdata->otg); - usb_remove_hcd(hcd); - usb_put_hcd(hcd); - platform_set_drvdata(pdev, NULL); - clk_disable_unprepare(priv->usbclk); clk_disable_unprepare(priv->ahbclk); if (priv->phyclk) clk_disable_unprepare(priv->phyclk); + usb_put_hcd(hcd); + platform_set_drvdata(pdev, NULL); return 0; } static void ehci_mxc_drv_shutdown(struct platform_device *pdev) { - struct ehci_mxc_priv *priv = platform_get_drvdata(pdev); - struct usb_hcd *hcd = priv->hcd; + struct usb_hcd *hcd = platform_get_drvdata(pdev); if (hcd->driver->shutdown) hcd->driver->shutdown(hcd); @@ -279,9 +237,31 @@ MODULE_ALIAS("platform:mxc-ehci"); static struct platform_driver ehci_mxc_driver = { .probe = ehci_mxc_drv_probe, - .remove = __exit_p(ehci_mxc_drv_remove), + .remove = ehci_mxc_drv_remove, .shutdown = ehci_mxc_drv_shutdown, .driver = { .name = "mxc-ehci", }, }; + +static int __init ehci_mxc_init(void) +{ + if (usb_disabled()) + return -ENODEV; + + pr_info("%s: " DRIVER_DESC "\n", hcd_name); + + ehci_init_driver(&ehci_mxc_hc_driver, &ehci_mxc_overrides); + return platform_driver_register(&ehci_mxc_driver); +} +module_init(ehci_mxc_init); + +static void __exit ehci_mxc_cleanup(void) +{ + platform_driver_unregister(&ehci_mxc_driver); +} +module_exit(ehci_mxc_cleanup); + +MODULE_DESCRIPTION(DRIVER_DESC); +MODULE_AUTHOR("Sascha Hauer"); +MODULE_LICENSE("GPL"); diff --git a/drivers/usb/host/ehci-pci.c b/drivers/usb/host/ehci-pci.c index dabb20494826..170b9399e09f 100644 --- a/drivers/usb/host/ehci-pci.c +++ b/drivers/usb/host/ehci-pci.c @@ -200,6 +200,26 @@ static int ehci_pci_setup(struct usb_hcd *hcd) break; } + /* optional debug port, normally in the first BAR */ + temp = pci_find_capability(pdev, PCI_CAP_ID_DBG); + if (temp) { + pci_read_config_dword(pdev, temp, &temp); + temp >>= 16; + if (((temp >> 13) & 7) == 1) { + u32 hcs_params = ehci_readl(ehci, + &ehci->caps->hcs_params); + + temp &= 0x1fff; + ehci->debug = hcd->regs + temp; + temp = ehci_readl(ehci, &ehci->debug->control); + ehci_info(ehci, "debug port %d%s\n", + HCS_DEBUG_PORT(hcs_params), + (temp & DBGP_ENABLED) ? " IN USE" : ""); + if (!(temp & DBGP_ENABLED)) + ehci->debug = NULL; + } + } + retval = ehci_setup(hcd); if (retval) return retval; @@ -228,25 +248,6 @@ static int ehci_pci_setup(struct usb_hcd *hcd) break; } - /* optional debug port, normally in the first BAR */ - temp = pci_find_capability(pdev, 0x0a); - if (temp) { - pci_read_config_dword(pdev, temp, &temp); - temp >>= 16; - if ((temp & (3 << 13)) == (1 << 13)) { - temp &= 0x1fff; - ehci->debug = hcd->regs + temp; - temp = ehci_readl(ehci, &ehci->debug->control); - ehci_info(ehci, "debug port %d%s\n", - HCS_DEBUG_PORT(ehci->hcs_params), - (temp & DBGP_ENABLED) - ? " IN USE" - : ""); - if (!(temp & DBGP_ENABLED)) - ehci->debug = NULL; - } - } - /* at least the Genesys GL880S needs fixup here */ temp = HCS_N_CC(ehci->hcs_params) * HCS_N_PCC(ehci->hcs_params); temp &= 0x0f; diff --git a/drivers/usb/host/ehci-q.c b/drivers/usb/host/ehci-q.c index 3d989028c836..fd252f0cfb3a 100644 --- a/drivers/usb/host/ehci-q.c +++ b/drivers/usb/host/ehci-q.c @@ -1197,17 +1197,26 @@ static void start_iaa_cycle(struct ehci_hcd *ehci, bool nested) if (ehci->async_iaa || ehci->async_unlinking) return; - /* Do all the waiting QHs at once */ - ehci->async_iaa = ehci->async_unlink; - ehci->async_unlink = NULL; - /* If the controller isn't running, we don't have to wait for it */ if (unlikely(ehci->rh_state < EHCI_RH_RUNNING)) { + + /* Do all the waiting QHs */ + ehci->async_iaa = ehci->async_unlink; + ehci->async_unlink = NULL; + if (!nested) /* Avoid recursion */ end_unlink_async(ehci); /* Otherwise start a new IAA cycle */ } else if (likely(ehci->rh_state == EHCI_RH_RUNNING)) { + struct ehci_qh *qh; + + /* Do only the first waiting QH (nVidia bug?) */ + qh = ehci->async_unlink; + ehci->async_iaa = qh; + ehci->async_unlink = qh->unlink_next; + qh->unlink_next = NULL; + /* Make sure the unlinks are all visible to the hardware */ wmb(); @@ -1255,34 +1264,35 @@ static void end_unlink_async(struct ehci_hcd *ehci) } } +static void start_unlink_async(struct ehci_hcd *ehci, struct ehci_qh *qh); + static void unlink_empty_async(struct ehci_hcd *ehci) { - struct ehci_qh *qh, *next; - bool stopped = (ehci->rh_state < EHCI_RH_RUNNING); + struct ehci_qh *qh; + struct ehci_qh *qh_to_unlink = NULL; bool check_unlinks_later = false; + int count = 0; - /* Unlink all the async QHs that have been empty for a timer cycle */ - next = ehci->async->qh_next.qh; - while (next) { - qh = next; - next = qh->qh_next.qh; - + /* Find the last async QH which has been empty for a timer cycle */ + for (qh = ehci->async->qh_next.qh; qh; qh = qh->qh_next.qh) { if (list_empty(&qh->qtd_list) && qh->qh_state == QH_STATE_LINKED) { - if (!stopped && qh->unlink_cycle == - ehci->async_unlink_cycle) + ++count; + if (qh->unlink_cycle == ehci->async_unlink_cycle) check_unlinks_later = true; else - single_unlink_async(ehci, qh); + qh_to_unlink = qh; } } - /* Start a new IAA cycle if any QHs are waiting for it */ - if (ehci->async_unlink) - start_iaa_cycle(ehci, false); + /* If nothing else is being unlinked, unlink the last empty QH */ + if (!ehci->async_iaa && !ehci->async_unlink && qh_to_unlink) { + start_unlink_async(ehci, qh_to_unlink); + --count; + } - /* QHs that haven't been empty for long enough will be handled later */ - if (check_unlinks_later) { + /* Other QHs will be handled later */ + if (count > 0) { ehci_enable_event(ehci, EHCI_HRTIMER_ASYNC_UNLINKS, true); ++ehci->async_unlink_cycle; } diff --git a/drivers/usb/host/ehci-sched.c b/drivers/usb/host/ehci-sched.c index 69ebee73c0c1..b476daf49f6f 100644 --- a/drivers/usb/host/ehci-sched.c +++ b/drivers/usb/host/ehci-sched.c @@ -213,7 +213,7 @@ static inline unsigned char tt_start_uframe(struct ehci_hcd *ehci, __hc32 mask) } static const unsigned char -max_tt_usecs[] = { 125, 125, 125, 125, 125, 125, 30, 0 }; +max_tt_usecs[] = { 125, 125, 125, 125, 125, 125, 125, 25 }; /* carryover low/fullspeed bandwidth that crosses uframe boundries */ static inline void carryover_tt_bandwidth(unsigned short tt_usecs[8]) @@ -2212,11 +2212,11 @@ static void scan_isoc(struct ehci_hcd *ehci) } ehci->now_frame = now_frame; + frame = ehci->last_iso_frame; for (;;) { union ehci_shadow q, *q_p; __hc32 type, *hw_p; - frame = ehci->last_iso_frame; restart: /* scan each element in frame's queue for completions */ q_p = &ehci->pshadow [frame]; @@ -2321,6 +2321,9 @@ restart: /* Stop when we have reached the current frame */ if (frame == now_frame) break; - ehci->last_iso_frame = (frame + 1) & fmask; + + /* The last frame may still have active siTDs */ + ehci->last_iso_frame = frame; + frame = (frame + 1) & fmask; } } diff --git a/drivers/usb/host/ehci-timer.c b/drivers/usb/host/ehci-timer.c index 20dbdcbe9b0f..f904071d70df 100644 --- a/drivers/usb/host/ehci-timer.c +++ b/drivers/usb/host/ehci-timer.c @@ -113,14 +113,15 @@ static void ehci_poll_ASS(struct ehci_hcd *ehci) if (want != actual) { - /* Poll again later, but give up after about 20 ms */ - if (ehci->ASS_poll_count++ < 20) { - ehci_enable_event(ehci, EHCI_HRTIMER_POLL_ASS, true); - return; - } - ehci_dbg(ehci, "Waited too long for the async schedule status (%x/%x), giving up\n", - want, actual); + /* Poll again later */ + ehci_enable_event(ehci, EHCI_HRTIMER_POLL_ASS, true); + ++ehci->ASS_poll_count; + return; } + + if (ehci->ASS_poll_count > 20) + ehci_dbg(ehci, "ASS poll count reached %d\n", + ehci->ASS_poll_count); ehci->ASS_poll_count = 0; /* The status is up-to-date; restart or stop the schedule as needed */ @@ -159,14 +160,14 @@ static void ehci_poll_PSS(struct ehci_hcd *ehci) if (want != actual) { - /* Poll again later, but give up after about 20 ms */ - if (ehci->PSS_poll_count++ < 20) { - ehci_enable_event(ehci, EHCI_HRTIMER_POLL_PSS, true); - return; - } - ehci_dbg(ehci, "Waited too long for the periodic schedule status (%x/%x), giving up\n", - want, actual); + /* Poll again later */ + ehci_enable_event(ehci, EHCI_HRTIMER_POLL_PSS, true); + return; } + + if (ehci->PSS_poll_count > 20) + ehci_dbg(ehci, "PSS poll count reached %d\n", + ehci->PSS_poll_count); ehci->PSS_poll_count = 0; /* The status is up-to-date; restart or stop the schedule as needed */ diff --git a/drivers/usb/host/ehci.h b/drivers/usb/host/ehci.h index 9dadc7118d68..36c3a8210595 100644 --- a/drivers/usb/host/ehci.h +++ b/drivers/usb/host/ehci.h @@ -38,6 +38,10 @@ typedef __u16 __bitwise __hc16; #endif /* statistics can be kept for tuning/monitoring */ +#ifdef DEBUG +#define EHCI_STATS +#endif + struct ehci_stats { /* irq usage */ unsigned long normal; @@ -221,6 +225,9 @@ struct ehci_hcd { /* one per controller */ #ifdef DEBUG struct dentry *debug_dir; #endif + + /* platform-specific data -- must come last */ + unsigned long priv[0] __aligned(sizeof(s64)); }; /* convert between an HCD pointer and the corresponding EHCI_HCD */ diff --git a/drivers/usb/host/fsl-mph-dr-of.c b/drivers/usb/host/fsl-mph-dr-of.c index 5105127c1d4b..11e0b79ff9d5 100644 --- a/drivers/usb/host/fsl-mph-dr-of.c +++ b/drivers/usb/host/fsl-mph-dr-of.c @@ -142,6 +142,9 @@ static int usb_get_ver_info(struct device_node *np) return ver; } + if (of_device_is_compatible(np, "fsl,mpc5121-usb2-dr")) + return FSL_USB_VER_OLD; + if (of_device_is_compatible(np, "fsl-usb2-mph")) { if (of_device_is_compatible(np, "fsl-usb2-mph-v1.6")) ver = FSL_USB_VER_1_6; diff --git a/drivers/usb/host/imx21-hcd.c b/drivers/usb/host/imx21-hcd.c index bd6a7447ccc9..f0ebe8e7c58b 100644 --- a/drivers/usb/host/imx21-hcd.c +++ b/drivers/usb/host/imx21-hcd.c @@ -58,6 +58,7 @@ #include <linux/usb.h> #include <linux/usb/hcd.h> #include <linux/dma-mapping.h> +#include <linux/module.h> #include "imx21-hcd.h" diff --git a/drivers/usb/host/ohci-tmio.c b/drivers/usb/host/ohci-tmio.c index d370245a4ee2..5e3a6deb62b1 100644 --- a/drivers/usb/host/ohci-tmio.c +++ b/drivers/usb/host/ohci-tmio.c @@ -128,7 +128,8 @@ static void tmio_start_hc(struct platform_device *dev) tmio_iowrite8(2, tmio->ccr + CCR_INTC); dev_info(&dev->dev, "revision %d @ 0x%08llx, irq %d\n", - tmio_ioread8(tmio->ccr + CCR_REVID), hcd->rsrc_start, hcd->irq); + tmio_ioread8(tmio->ccr + CCR_REVID), + (u64) hcd->rsrc_start, hcd->irq); } static int ohci_tmio_start(struct usb_hcd *hcd) diff --git a/drivers/usb/host/pci-quirks.c b/drivers/usb/host/pci-quirks.c index a3b6d7104ae2..4c338ec03a07 100644 --- a/drivers/usb/host/pci-quirks.c +++ b/drivers/usb/host/pci-quirks.c @@ -780,6 +780,7 @@ void usb_enable_xhci_ports(struct pci_dev *xhci_pdev) "defaulting to EHCI.\n"); dev_warn(&xhci_pdev->dev, "USB 3.0 devices will work at USB 2.0 speeds.\n"); + usb_disable_xhci_ports(xhci_pdev); return; } diff --git a/drivers/usb/host/uhci-hcd.c b/drivers/usb/host/uhci-hcd.c index 4b9e9aba2665..4f64d24eebc8 100644 --- a/drivers/usb/host/uhci-hcd.c +++ b/drivers/usb/host/uhci-hcd.c @@ -447,6 +447,10 @@ static irqreturn_t uhci_irq(struct usb_hcd *hcd) return IRQ_NONE; uhci_writew(uhci, status, USBSTS); /* Clear it */ + spin_lock(&uhci->lock); + if (unlikely(!uhci->is_initialized)) /* not yet configured */ + goto done; + if (status & ~(USBSTS_USBINT | USBSTS_ERROR | USBSTS_RD)) { if (status & USBSTS_HSE) dev_err(uhci_dev(uhci), "host system error, " @@ -455,7 +459,6 @@ static irqreturn_t uhci_irq(struct usb_hcd *hcd) dev_err(uhci_dev(uhci), "host controller process " "error, something bad happened!\n"); if (status & USBSTS_HCH) { - spin_lock(&uhci->lock); if (uhci->rh_state >= UHCI_RH_RUNNING) { dev_err(uhci_dev(uhci), "host controller halted, " @@ -473,15 +476,15 @@ static irqreturn_t uhci_irq(struct usb_hcd *hcd) * pending unlinks */ mod_timer(&hcd->rh_timer, jiffies); } - spin_unlock(&uhci->lock); } } - if (status & USBSTS_RD) + if (status & USBSTS_RD) { + spin_unlock(&uhci->lock); usb_hcd_poll_rh_status(hcd); - else { - spin_lock(&uhci->lock); + } else { uhci_scan_schedule(uhci); + done: spin_unlock(&uhci->lock); } @@ -662,9 +665,9 @@ static int uhci_start(struct usb_hcd *hcd) */ mb(); + spin_lock_irq(&uhci->lock); configure_hc(uhci); uhci->is_initialized = 1; - spin_lock_irq(&uhci->lock); start_rh(uhci); spin_unlock_irq(&uhci->lock); return 0; diff --git a/drivers/usb/host/uhci-hub.c b/drivers/usb/host/uhci-hub.c index 768d54295a20..15d13229ddbb 100644 --- a/drivers/usb/host/uhci-hub.c +++ b/drivers/usb/host/uhci-hub.c @@ -116,6 +116,7 @@ static void uhci_finish_suspend(struct uhci_hcd *uhci, int port, } } clear_bit(port, &uhci->resuming_ports); + usb_hcd_end_port_resume(&uhci_to_hcd(uhci)->self, port); } /* Wait for the UHCI controller in HP's iLO2 server management chip. @@ -167,6 +168,8 @@ static void uhci_check_ports(struct uhci_hcd *uhci) set_bit(port, &uhci->resuming_ports); uhci->ports_timeout = jiffies + msecs_to_jiffies(25); + usb_hcd_start_port_resume( + &uhci_to_hcd(uhci)->self, port); /* Make sure we see the port again * after the resuming period is over. */ diff --git a/drivers/usb/host/xhci-hub.c b/drivers/usb/host/xhci-hub.c index a686cf4905bb..68914429482f 100644 --- a/drivers/usb/host/xhci-hub.c +++ b/drivers/usb/host/xhci-hub.c @@ -761,12 +761,39 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, break; case USB_PORT_FEAT_LINK_STATE: temp = xhci_readl(xhci, port_array[wIndex]); + + /* Disable port */ + if (link_state == USB_SS_PORT_LS_SS_DISABLED) { + xhci_dbg(xhci, "Disable port %d\n", wIndex); + temp = xhci_port_state_to_neutral(temp); + /* + * Clear all change bits, so that we get a new + * connection event. + */ + temp |= PORT_CSC | PORT_PEC | PORT_WRC | + PORT_OCC | PORT_RC | PORT_PLC | + PORT_CEC; + xhci_writel(xhci, temp | PORT_PE, + port_array[wIndex]); + temp = xhci_readl(xhci, port_array[wIndex]); + break; + } + + /* Put link in RxDetect (enable port) */ + if (link_state == USB_SS_PORT_LS_RX_DETECT) { + xhci_dbg(xhci, "Enable port %d\n", wIndex); + xhci_set_link_state(xhci, port_array, wIndex, + link_state); + temp = xhci_readl(xhci, port_array[wIndex]); + break; + } + /* Software should not attempt to set - * port link state above '5' (Rx.Detect) and the port + * port link state above '3' (U3) and the port * must be enabled. */ if ((temp & PORT_PE) == 0 || - (link_state > USB_SS_PORT_LS_RX_DETECT)) { + (link_state > USB_SS_PORT_LS_U3)) { xhci_warn(xhci, "Cannot set link state.\n"); goto error; } @@ -957,6 +984,7 @@ int xhci_hub_status_data(struct usb_hcd *hcd, char *buf) int max_ports; __le32 __iomem **port_array; struct xhci_bus_state *bus_state; + bool reset_change = false; max_ports = xhci_get_ports(hcd, &port_array); bus_state = &xhci->bus_state[hcd_index(hcd)]; @@ -988,6 +1016,12 @@ int xhci_hub_status_data(struct usb_hcd *hcd, char *buf) buf[(i + 1) / 8] |= 1 << (i + 1) % 8; status = 1; } + if ((temp & PORT_RC)) + reset_change = true; + } + if (!status && !reset_change) { + xhci_dbg(xhci, "%s: stopping port polling.\n", __func__); + clear_bit(HCD_FLAG_POLL_RH, &hcd->flags); } spin_unlock_irqrestore(&xhci->lock, flags); return status ? retval : 0; diff --git a/drivers/usb/host/xhci-mem.c b/drivers/usb/host/xhci-mem.c index fb51c7085ad0..35616ffbe3ae 100644 --- a/drivers/usb/host/xhci-mem.c +++ b/drivers/usb/host/xhci-mem.c @@ -1250,6 +1250,8 @@ static unsigned int xhci_microframes_to_exponent(struct usb_device *udev, static unsigned int xhci_parse_microframe_interval(struct usb_device *udev, struct usb_host_endpoint *ep) { + if (ep->desc.bInterval == 0) + return 0; return xhci_microframes_to_exponent(udev, ep, ep->desc.bInterval, 0, 15); } diff --git a/drivers/usb/host/xhci-ring.c b/drivers/usb/host/xhci-ring.c index cbb44b7b9d65..7f76a49e90d3 100644 --- a/drivers/usb/host/xhci-ring.c +++ b/drivers/usb/host/xhci-ring.c @@ -1698,7 +1698,7 @@ static void handle_port_status(struct xhci_hcd *xhci, faked_port_index + 1); if (slot_id && xhci->devs[slot_id]) xhci_ring_device(xhci, slot_id); - if (bus_state->port_remote_wakeup && (1 << faked_port_index)) { + if (bus_state->port_remote_wakeup & (1 << faked_port_index)) { bus_state->port_remote_wakeup &= ~(1 << faked_port_index); xhci_test_and_clear_bit(xhci, port_array, @@ -1725,6 +1725,15 @@ cleanup: if (bogus_port_status) return; + /* + * xHCI port-status-change events occur when the "or" of all the + * status-change bits in the portsc register changes from 0 to 1. + * New status changes won't cause an event if any other change + * bits are still set. When an event occurs, switch over to + * polling to avoid losing status changes. + */ + xhci_dbg(xhci, "%s: starting port polling.\n", __func__); + set_bit(HCD_FLAG_POLL_RH, &hcd->flags); spin_unlock(&xhci->lock); /* Pass this up to the core */ usb_hcd_poll_rh_status(hcd); @@ -2580,6 +2589,8 @@ cleanup: (trb_comp_code != COMP_STALL && trb_comp_code != COMP_BABBLE)) xhci_urb_free_priv(xhci, urb_priv); + else + kfree(urb_priv); usb_hcd_unlink_urb_from_ep(bus_to_hcd(urb->dev->bus), urb); if ((urb->actual_length != urb->transfer_buffer_length && @@ -3099,7 +3110,7 @@ static u32 xhci_v1_0_td_remainder(int running_total, int trb_buff_len, * running_total. */ packets_transferred = (running_total + trb_buff_len) / - usb_endpoint_maxp(&urb->ep->desc); + GET_MAX_PACKET(usb_endpoint_maxp(&urb->ep->desc)); if ((total_packet_count - packets_transferred) > 31) return 31 << 17; @@ -3633,7 +3644,8 @@ static int xhci_queue_isoc_tx(struct xhci_hcd *xhci, gfp_t mem_flags, td_len = urb->iso_frame_desc[i].length; td_remain_len = td_len; total_packet_count = DIV_ROUND_UP(td_len, - usb_endpoint_maxp(&urb->ep->desc)); + GET_MAX_PACKET( + usb_endpoint_maxp(&urb->ep->desc))); /* A zero-length transfer still involves at least one packet. */ if (total_packet_count == 0) total_packet_count++; @@ -3655,9 +3667,11 @@ static int xhci_queue_isoc_tx(struct xhci_hcd *xhci, gfp_t mem_flags, td = urb_priv->td[i]; for (j = 0; j < trbs_per_td; j++) { u32 remainder = 0; - field = TRB_TBC(burst_count) | TRB_TLBPC(residue); + field = 0; if (first_trb) { + field = TRB_TBC(burst_count) | + TRB_TLBPC(residue); /* Queue the isoc TRB */ field |= TRB_TYPE(TRB_ISOC); /* Assume URB_ISO_ASAP is set */ diff --git a/drivers/usb/host/xhci.c b/drivers/usb/host/xhci.c index 5c72c431bab1..f1f01a834ba7 100644 --- a/drivers/usb/host/xhci.c +++ b/drivers/usb/host/xhci.c @@ -884,6 +884,11 @@ int xhci_suspend(struct xhci_hcd *xhci) xhci->shared_hcd->state != HC_STATE_SUSPENDED) return -EINVAL; + /* Don't poll the roothubs on bus suspend. */ + xhci_dbg(xhci, "%s: stopping port polling.\n", __func__); + clear_bit(HCD_FLAG_POLL_RH, &hcd->flags); + del_timer_sync(&hcd->rh_timer); + spin_lock_irq(&xhci->lock); clear_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags); clear_bit(HCD_FLAG_HW_ACCESSIBLE, &xhci->shared_hcd->flags); @@ -1069,6 +1074,11 @@ int xhci_resume(struct xhci_hcd *xhci, bool hibernated) if (xhci->quirks & XHCI_COMP_MODE_QUIRK) compliance_mode_recovery_timer_init(xhci); + /* Re-enable port polling. */ + xhci_dbg(xhci, "%s: starting port polling.\n", __func__); + set_bit(HCD_FLAG_POLL_RH, &hcd->flags); + usb_hcd_poll_rh_status(hcd); + return retval; } #endif /* CONFIG_PM */ diff --git a/drivers/usb/misc/usbtest.c b/drivers/usb/misc/usbtest.c index 7667b12f2ff5..268148de9714 100644 --- a/drivers/usb/misc/usbtest.c +++ b/drivers/usb/misc/usbtest.c @@ -2179,7 +2179,7 @@ usbtest_ioctl(struct usb_interface *intf, unsigned int code, void *buf) if (dev->out_pipe == 0 || !param->length || param->sglen < 4) break; retval = 0; - dev_info(&intf->dev, "TEST 17: unlink from %d queues of " + dev_info(&intf->dev, "TEST 24: unlink from %d queues of " "%d %d-byte writes\n", param->iterations, param->sglen, param->length); for (i = param->iterations; retval == 0 && i > 0; --i) { diff --git a/drivers/usb/musb/cppi_dma.c b/drivers/usb/musb/cppi_dma.c index 0968dd7a859d..f522000e8f06 100644 --- a/drivers/usb/musb/cppi_dma.c +++ b/drivers/usb/musb/cppi_dma.c @@ -105,7 +105,7 @@ static void cppi_reset_tx(struct cppi_tx_stateram __iomem *tx, u32 ptr) musb_writel(&tx->tx_complete, 0, ptr); } -static void __init cppi_pool_init(struct cppi *cppi, struct cppi_channel *c) +static void cppi_pool_init(struct cppi *cppi, struct cppi_channel *c) { int j; @@ -150,7 +150,7 @@ static void cppi_pool_free(struct cppi_channel *c) c->last_processed = NULL; } -static int __init cppi_controller_start(struct dma_controller *c) +static int cppi_controller_start(struct dma_controller *c) { struct cppi *controller; void __iomem *tibase; diff --git a/drivers/usb/musb/musb_core.c b/drivers/usb/musb/musb_core.c index f1c6c5470b92..fd3486745e64 100644 --- a/drivers/usb/musb/musb_core.c +++ b/drivers/usb/musb/musb_core.c @@ -2298,10 +2298,7 @@ static int __init musb_init(void) if (usb_disabled()) return 0; - pr_info("%s: version " MUSB_VERSION ", " - "?dma?" - ", " - "otg (peripheral+host)", + pr_info("%s: version " MUSB_VERSION ", ?dma?, otg (peripheral+host)\n", musb_driver_name); return platform_driver_register(&musb_driver); } diff --git a/drivers/usb/musb/musb_dsps.c b/drivers/usb/musb/musb_dsps.c index e6f2ae8368bb..f7d764de6fda 100644 --- a/drivers/usb/musb/musb_dsps.c +++ b/drivers/usb/musb/musb_dsps.c @@ -134,6 +134,11 @@ static const resource_size_t dsps_control_module_phys[] = { DSPS_AM33XX_CONTROL_MODULE_PHYS_1, }; +#define USBPHY_CM_PWRDN (1 << 0) +#define USBPHY_OTG_PWRDN (1 << 1) +#define USBPHY_OTGVDET_EN (1 << 19) +#define USBPHY_OTGSESSEND_EN (1 << 20) + /** * musb_dsps_phy_control - phy on/off * @glue: struct dsps_glue * diff --git a/drivers/usb/otg/Kconfig b/drivers/usb/otg/Kconfig index 6223062d5d1b..37962c99ff1e 100644 --- a/drivers/usb/otg/Kconfig +++ b/drivers/usb/otg/Kconfig @@ -110,7 +110,7 @@ config AB8500_USB config FSL_USB2_OTG bool "Freescale USB OTG Transceiver Driver" - depends on USB_EHCI_FSL && USB_GADGET_FSL_USB2 && USB_SUSPEND + depends on USB_EHCI_FSL && USB_FSL_USB2 && USB_SUSPEND select USB_OTG select USB_OTG_UTILS help diff --git a/drivers/usb/otg/mv_otg.c b/drivers/usb/otg/mv_otg.c index 1dd57504186d..eace975991a8 100644 --- a/drivers/usb/otg/mv_otg.c +++ b/drivers/usb/otg/mv_otg.c @@ -240,7 +240,7 @@ static void otg_clock_enable(struct mv_otg *mvotg) unsigned int i; for (i = 0; i < mvotg->clknum; i++) - clk_enable(mvotg->clk[i]); + clk_prepare_enable(mvotg->clk[i]); } static void otg_clock_disable(struct mv_otg *mvotg) @@ -248,7 +248,7 @@ static void otg_clock_disable(struct mv_otg *mvotg) unsigned int i; for (i = 0; i < mvotg->clknum; i++) - clk_disable(mvotg->clk[i]); + clk_disable_unprepare(mvotg->clk[i]); } static int mv_otg_enable_internal(struct mv_otg *mvotg) diff --git a/drivers/usb/renesas_usbhs/mod_gadget.c b/drivers/usb/renesas_usbhs/mod_gadget.c index dd41f61893ef..f2985cd88021 100644 --- a/drivers/usb/renesas_usbhs/mod_gadget.c +++ b/drivers/usb/renesas_usbhs/mod_gadget.c @@ -545,15 +545,6 @@ static int usbhsg_pipe_disable(struct usbhsg_uep *uep) return 0; } -static void usbhsg_uep_init(struct usbhsg_gpriv *gpriv) -{ - int i; - struct usbhsg_uep *uep; - - usbhsg_for_each_uep_with_dcp(uep, gpriv, i) - uep->pipe = NULL; -} - /* * * usb_ep_ops @@ -610,7 +601,12 @@ static int usbhsg_ep_disable(struct usb_ep *ep) { struct usbhsg_uep *uep = usbhsg_ep_to_uep(ep); - return usbhsg_pipe_disable(uep); + usbhsg_pipe_disable(uep); + + uep->pipe->mod_private = NULL; + uep->pipe = NULL; + + return 0; } static struct usb_request *usbhsg_ep_alloc_request(struct usb_ep *ep, @@ -761,9 +757,8 @@ static int usbhsg_try_start(struct usbhs_priv *priv, u32 status) usbhs_pipe_init(priv, usbhsg_dma_map_ctrl); usbhs_fifo_init(priv); - usbhsg_uep_init(gpriv); - /* dcp init */ + /* dcp init instead of usbhsg_ep_enable() */ dcp->pipe = usbhs_dcp_malloc(priv); dcp->pipe->mod_private = dcp; usbhs_pipe_config_update(dcp->pipe, 0, 0, 64); @@ -825,7 +820,7 @@ static int usbhsg_try_stop(struct usbhs_priv *priv, u32 status) usbhs_sys_set_test_mode(priv, 0); usbhs_sys_function_ctrl(priv, 0); - usbhsg_pipe_disable(dcp); + usbhsg_ep_disable(&dcp->ep); dev_dbg(dev, "stop gadget\n"); @@ -998,6 +993,7 @@ int usbhs_mod_gadget_probe(struct usbhs_priv *priv) */ usbhsg_for_each_uep_with_dcp(uep, gpriv, i) { uep->gpriv = gpriv; + uep->pipe = NULL; snprintf(uep->ep_name, EP_NAME_SIZE, "ep%d", i); uep->ep.name = uep->ep_name; diff --git a/drivers/usb/renesas_usbhs/mod_host.c b/drivers/usb/renesas_usbhs/mod_host.c index 3d3cd6ca2689..b86815421c8d 100644 --- a/drivers/usb/renesas_usbhs/mod_host.c +++ b/drivers/usb/renesas_usbhs/mod_host.c @@ -661,9 +661,10 @@ static void usbhsh_queue_done(struct usbhs_priv *priv, struct usbhs_pkt *pkt) status = -ESHUTDOWN; urb->actual_length = pkt->actual; - usbhsh_ureq_free(hpriv, ureq); usbhsh_endpoint_sequence_save(hpriv, urb, pkt); + usbhsh_ureq_free(hpriv, ureq); + usbhsh_pipe_detach(hpriv, usbhsh_ep_to_uep(urb->ep)); usb_hcd_unlink_urb_from_ep(hcd, urb); diff --git a/drivers/usb/serial/cp210x.c b/drivers/usb/serial/cp210x.c index f14736f647ff..edc0f0dcad83 100644 --- a/drivers/usb/serial/cp210x.c +++ b/drivers/usb/serial/cp210x.c @@ -60,6 +60,7 @@ static const struct usb_device_id id_table[] = { { USB_DEVICE(0x0FCF, 0x1003) }, /* Dynastream ANT development board */ { USB_DEVICE(0x0FCF, 0x1004) }, /* Dynastream ANT2USB */ { USB_DEVICE(0x0FCF, 0x1006) }, /* Dynastream ANT development board */ + { USB_DEVICE(0x0FDE, 0xCA05) }, /* OWL Wireless Electricity Monitor CM-160 */ { USB_DEVICE(0x10A6, 0xAA26) }, /* Knock-off DCU-11 cable */ { USB_DEVICE(0x10AB, 0x10C5) }, /* Siemens MC60 Cable */ { USB_DEVICE(0x10B5, 0xAC70) }, /* Nokia CA-42 USB */ diff --git a/drivers/usb/serial/ftdi_sio.c b/drivers/usb/serial/ftdi_sio.c index 0a373b3ae96a..90ceef1776c3 100644 --- a/drivers/usb/serial/ftdi_sio.c +++ b/drivers/usb/serial/ftdi_sio.c @@ -584,6 +584,7 @@ static struct usb_device_id id_table_combined [] = { /* * ELV devices: */ + { USB_DEVICE(FTDI_ELV_VID, FTDI_ELV_WS300_PID) }, { USB_DEVICE(FTDI_VID, FTDI_ELV_USR_PID) }, { USB_DEVICE(FTDI_VID, FTDI_ELV_MSM1_PID) }, { USB_DEVICE(FTDI_VID, FTDI_ELV_KL100_PID) }, @@ -670,6 +671,7 @@ static struct usb_device_id id_table_combined [] = { { USB_DEVICE(FTDI_VID, XSENS_CONVERTER_5_PID) }, { USB_DEVICE(FTDI_VID, XSENS_CONVERTER_6_PID) }, { USB_DEVICE(FTDI_VID, XSENS_CONVERTER_7_PID) }, + { USB_DEVICE(FTDI_VID, FTDI_OMNI1509) }, { USB_DEVICE(MOBILITY_VID, MOBILITY_USB_SERIAL_PID) }, { USB_DEVICE(FTDI_VID, FTDI_ACTIVE_ROBOTS_PID) }, { USB_DEVICE(FTDI_VID, FTDI_MHAM_KW_PID) }, @@ -875,6 +877,8 @@ static struct usb_device_id id_table_combined [] = { { USB_DEVICE(FTDI_VID, FTDI_DISTORTEC_JTAG_LOCK_PICK_PID), .driver_info = (kernel_ulong_t)&ftdi_jtag_quirk }, { USB_DEVICE(FTDI_VID, FTDI_LUMEL_PD12_PID) }, + /* Crucible Devices */ + { USB_DEVICE(FTDI_VID, FTDI_CT_COMET_PID) }, { }, /* Optional parameter entry */ { } /* Terminating entry */ }; diff --git a/drivers/usb/serial/ftdi_sio_ids.h b/drivers/usb/serial/ftdi_sio_ids.h index 049b6e715fa4..9d359e189a64 100644 --- a/drivers/usb/serial/ftdi_sio_ids.h +++ b/drivers/usb/serial/ftdi_sio_ids.h @@ -147,6 +147,11 @@ #define XSENS_CONVERTER_6_PID 0xD38E #define XSENS_CONVERTER_7_PID 0xD38F +/** + * Zolix (www.zolix.com.cb) product ids + */ +#define FTDI_OMNI1509 0xD491 /* Omni1509 embedded USB-serial */ + /* * NDI (www.ndigital.com) product ids */ @@ -204,7 +209,7 @@ /* * ELV USB devices submitted by Christian Abt of ELV (www.elv.de). - * All of these devices use FTDI's vendor ID (0x0403). + * Almost all of these devices use FTDI's vendor ID (0x0403). * Further IDs taken from ELV Windows .inf file. * * The previously included PID for the UO 100 module was incorrect. @@ -212,6 +217,8 @@ * * Armin Laeuger originally sent the PID for the UM 100 module. */ +#define FTDI_ELV_VID 0x1B1F /* ELV AG */ +#define FTDI_ELV_WS300_PID 0xC006 /* eQ3 WS 300 PC II */ #define FTDI_ELV_USR_PID 0xE000 /* ELV Universal-Sound-Recorder */ #define FTDI_ELV_MSM1_PID 0xE001 /* ELV Mini-Sound-Modul */ #define FTDI_ELV_KL100_PID 0xE002 /* ELV Kfz-Leistungsmesser KL 100 */ @@ -1259,3 +1266,9 @@ * ATI command output: Cinterion MC55i */ #define FTDI_CINTERION_MC55I_PID 0xA951 + +/* + * Product: Comet Caller ID decoder + * Manufacturer: Crucible Technologies + */ +#define FTDI_CT_COMET_PID 0x8e08 diff --git a/drivers/usb/serial/io_ti.c b/drivers/usb/serial/io_ti.c index 58184f3de686..82afc4d6a327 100644 --- a/drivers/usb/serial/io_ti.c +++ b/drivers/usb/serial/io_ti.c @@ -530,6 +530,9 @@ static void chase_port(struct edgeport_port *port, unsigned long timeout, wait_queue_t wait; unsigned long flags; + if (!tty) + return; + if (!timeout) timeout = (HZ * EDGE_CLOSING_WAIT)/100; diff --git a/drivers/usb/serial/option.c b/drivers/usb/serial/option.c index e6f87b76c715..567bc77d6397 100644 --- a/drivers/usb/serial/option.c +++ b/drivers/usb/serial/option.c @@ -242,6 +242,7 @@ static void option_instat_callback(struct urb *urb); #define TELIT_PRODUCT_CC864_DUAL 0x1005 #define TELIT_PRODUCT_CC864_SINGLE 0x1006 #define TELIT_PRODUCT_DE910_DUAL 0x1010 +#define TELIT_PRODUCT_LE920 0x1200 /* ZTE PRODUCTS */ #define ZTE_VENDOR_ID 0x19d2 @@ -288,6 +289,7 @@ static void option_instat_callback(struct urb *urb); #define ALCATEL_VENDOR_ID 0x1bbb #define ALCATEL_PRODUCT_X060S_X200 0x0000 #define ALCATEL_PRODUCT_X220_X500D 0x0017 +#define ALCATEL_PRODUCT_L100V 0x011e #define PIRELLI_VENDOR_ID 0x1266 #define PIRELLI_PRODUCT_C100_1 0x1002 @@ -429,9 +431,12 @@ static void option_instat_callback(struct urb *urb); #define MEDIATEK_VENDOR_ID 0x0e8d #define MEDIATEK_PRODUCT_DC_1COM 0x00a0 #define MEDIATEK_PRODUCT_DC_4COM 0x00a5 +#define MEDIATEK_PRODUCT_DC_4COM2 0x00a7 #define MEDIATEK_PRODUCT_DC_5COM 0x00a4 #define MEDIATEK_PRODUCT_7208_1COM 0x7101 #define MEDIATEK_PRODUCT_7208_2COM 0x7102 +#define MEDIATEK_PRODUCT_7103_2COM 0x7103 +#define MEDIATEK_PRODUCT_7106_2COM 0x7106 #define MEDIATEK_PRODUCT_FP_1COM 0x0003 #define MEDIATEK_PRODUCT_FP_2COM 0x0023 #define MEDIATEK_PRODUCT_FPDC_1COM 0x0043 @@ -441,6 +446,18 @@ static void option_instat_callback(struct urb *urb); #define CELLIENT_VENDOR_ID 0x2692 #define CELLIENT_PRODUCT_MEN200 0x9005 +/* Hyundai Petatel Inc. products */ +#define PETATEL_VENDOR_ID 0x1ff4 +#define PETATEL_PRODUCT_NP10T 0x600e + +/* TP-LINK Incorporated products */ +#define TPLINK_VENDOR_ID 0x2357 +#define TPLINK_PRODUCT_MA180 0x0201 + +/* Changhong products */ +#define CHANGHONG_VENDOR_ID 0x2077 +#define CHANGHONG_PRODUCT_CH690 0x7001 + /* some devices interfaces need special handling due to a number of reasons */ enum option_blacklist_reason { OPTION_BLACKLIST_NONE = 0, @@ -522,6 +539,11 @@ static const struct option_blacklist_info zte_1255_blacklist = { .reserved = BIT(3) | BIT(4), }; +static const struct option_blacklist_info telit_le920_blacklist = { + .sendsetup = BIT(0), + .reserved = BIT(1) | BIT(5), +}; + static const struct usb_device_id option_ids[] = { { USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_COLT) }, { USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_RICOLA) }, @@ -772,6 +794,8 @@ static const struct usb_device_id option_ids[] = { { USB_DEVICE(TELIT_VENDOR_ID, TELIT_PRODUCT_CC864_DUAL) }, { USB_DEVICE(TELIT_VENDOR_ID, TELIT_PRODUCT_CC864_SINGLE) }, { USB_DEVICE(TELIT_VENDOR_ID, TELIT_PRODUCT_DE910_DUAL) }, + { USB_DEVICE(TELIT_VENDOR_ID, TELIT_PRODUCT_LE920), + .driver_info = (kernel_ulong_t)&telit_le920_blacklist }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, ZTE_PRODUCT_MF622, 0xff, 0xff, 0xff) }, /* ZTE WCDMA products */ { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0002, 0xff, 0xff, 0xff), .driver_info = (kernel_ulong_t)&net_intf1_blacklist }, @@ -922,8 +946,10 @@ static const struct usb_device_id option_ids[] = { { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0254, 0xff, 0xff, 0xff) }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0257, 0xff, 0xff, 0xff), /* ZTE MF821 */ .driver_info = (kernel_ulong_t)&net_intf3_blacklist }, - { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0265, 0xff, 0xff, 0xff) }, - { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0284, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0265, 0xff, 0xff, 0xff), /* ONDA MT8205 */ + .driver_info = (kernel_ulong_t)&net_intf4_blacklist }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0284, 0xff, 0xff, 0xff), /* ZTE MF880 */ + .driver_info = (kernel_ulong_t)&net_intf4_blacklist }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0317, 0xff, 0xff, 0xff) }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0326, 0xff, 0xff, 0xff), .driver_info = (kernel_ulong_t)&net_intf4_blacklist }, @@ -1190,6 +1216,8 @@ static const struct usb_device_id option_ids[] = { .driver_info = (kernel_ulong_t)&alcatel_x200_blacklist }, { USB_DEVICE(ALCATEL_VENDOR_ID, ALCATEL_PRODUCT_X220_X500D) }, + { USB_DEVICE(ALCATEL_VENDOR_ID, ALCATEL_PRODUCT_L100V), + .driver_info = (kernel_ulong_t)&net_intf4_blacklist }, { USB_DEVICE(AIRPLUS_VENDOR_ID, AIRPLUS_PRODUCT_MCD650) }, { USB_DEVICE(TLAYTECH_VENDOR_ID, TLAYTECH_PRODUCT_TEU800) }, { USB_DEVICE(LONGCHEER_VENDOR_ID, FOUR_G_SYSTEMS_PRODUCT_W14), @@ -1294,7 +1322,15 @@ static const struct usb_device_id option_ids[] = { { USB_DEVICE_AND_INTERFACE_INFO(MEDIATEK_VENDOR_ID, MEDIATEK_PRODUCT_FP_2COM, 0x0a, 0x00, 0x00) }, { USB_DEVICE_AND_INTERFACE_INFO(MEDIATEK_VENDOR_ID, MEDIATEK_PRODUCT_FPDC_1COM, 0x0a, 0x00, 0x00) }, { USB_DEVICE_AND_INTERFACE_INFO(MEDIATEK_VENDOR_ID, MEDIATEK_PRODUCT_FPDC_2COM, 0x0a, 0x00, 0x00) }, + { USB_DEVICE_AND_INTERFACE_INFO(MEDIATEK_VENDOR_ID, MEDIATEK_PRODUCT_7103_2COM, 0xff, 0x00, 0x00) }, + { USB_DEVICE_AND_INTERFACE_INFO(MEDIATEK_VENDOR_ID, MEDIATEK_PRODUCT_7106_2COM, 0x02, 0x02, 0x01) }, + { USB_DEVICE_AND_INTERFACE_INFO(MEDIATEK_VENDOR_ID, MEDIATEK_PRODUCT_DC_4COM2, 0xff, 0x02, 0x01) }, + { USB_DEVICE_AND_INTERFACE_INFO(MEDIATEK_VENDOR_ID, MEDIATEK_PRODUCT_DC_4COM2, 0xff, 0x00, 0x00) }, { USB_DEVICE(CELLIENT_VENDOR_ID, CELLIENT_PRODUCT_MEN200) }, + { USB_DEVICE(PETATEL_VENDOR_ID, PETATEL_PRODUCT_NP10T) }, + { USB_DEVICE(TPLINK_VENDOR_ID, TPLINK_PRODUCT_MA180), + .driver_info = (kernel_ulong_t)&net_intf4_blacklist }, + { USB_DEVICE(CHANGHONG_VENDOR_ID, CHANGHONG_PRODUCT_CH690) }, { } /* Terminating entry */ }; MODULE_DEVICE_TABLE(usb, option_ids); diff --git a/drivers/usb/serial/qcserial.c b/drivers/usb/serial/qcserial.c index aa148c21ea40..24662547dc5b 100644 --- a/drivers/usb/serial/qcserial.c +++ b/drivers/usb/serial/qcserial.c @@ -53,6 +53,7 @@ static const struct usb_device_id id_table[] = { {DEVICE_G1K(0x05c6, 0x9221)}, /* Generic Gobi QDL device */ {DEVICE_G1K(0x05c6, 0x9231)}, /* Generic Gobi QDL device */ {DEVICE_G1K(0x1f45, 0x0001)}, /* Unknown Gobi QDL device */ + {DEVICE_G1K(0x1bc7, 0x900e)}, /* Telit Gobi QDL device */ /* Gobi 2000 devices */ {USB_DEVICE(0x1410, 0xa010)}, /* Novatel Gobi 2000 QDL device */ diff --git a/drivers/usb/storage/initializers.c b/drivers/usb/storage/initializers.c index 105d900150c1..16b0bf055eeb 100644 --- a/drivers/usb/storage/initializers.c +++ b/drivers/usb/storage/initializers.c @@ -92,8 +92,8 @@ int usb_stor_ucr61s2b_init(struct us_data *us) return 0; } -/* This places the HUAWEI E220 devices in multi-port mode */ -int usb_stor_huawei_e220_init(struct us_data *us) +/* This places the HUAWEI usb dongles in multi-port mode */ +static int usb_stor_huawei_feature_init(struct us_data *us) { int result; @@ -104,3 +104,75 @@ int usb_stor_huawei_e220_init(struct us_data *us) US_DEBUGP("Huawei mode set result is %d\n", result); return 0; } + +/* + * It will send a scsi switch command called rewind' to huawei dongle. + * When the dongle receives this command at the first time, + * it will reboot immediately. After rebooted, it will ignore this command. + * So it is unnecessary to read its response. + */ +static int usb_stor_huawei_scsi_init(struct us_data *us) +{ + int result = 0; + int act_len = 0; + struct bulk_cb_wrap *bcbw = (struct bulk_cb_wrap *) us->iobuf; + char rewind_cmd[] = {0x11, 0x06, 0x20, 0x00, 0x00, 0x01, 0x01, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; + + bcbw->Signature = cpu_to_le32(US_BULK_CB_SIGN); + bcbw->Tag = 0; + bcbw->DataTransferLength = 0; + bcbw->Flags = bcbw->Lun = 0; + bcbw->Length = sizeof(rewind_cmd); + memset(bcbw->CDB, 0, sizeof(bcbw->CDB)); + memcpy(bcbw->CDB, rewind_cmd, sizeof(rewind_cmd)); + + result = usb_stor_bulk_transfer_buf(us, us->send_bulk_pipe, bcbw, + US_BULK_CB_WRAP_LEN, &act_len); + US_DEBUGP("transfer actual length=%d, result=%d\n", act_len, result); + return result; +} + +/* + * It tries to find the supported Huawei USB dongles. + * In Huawei, they assign the following product IDs + * for all of their mobile broadband dongles, + * including the new dongles in the future. + * So if the product ID is not included in this list, + * it means it is not Huawei's mobile broadband dongles. + */ +static int usb_stor_huawei_dongles_pid(struct us_data *us) +{ + struct usb_interface_descriptor *idesc; + int idProduct; + + idesc = &us->pusb_intf->cur_altsetting->desc; + idProduct = us->pusb_dev->descriptor.idProduct; + /* The first port is CDROM, + * means the dongle in the single port mode, + * and a switch command is required to be sent. */ + if (idesc && idesc->bInterfaceNumber == 0) { + if ((idProduct == 0x1001) + || (idProduct == 0x1003) + || (idProduct == 0x1004) + || (idProduct >= 0x1401 && idProduct <= 0x1500) + || (idProduct >= 0x1505 && idProduct <= 0x1600) + || (idProduct >= 0x1c02 && idProduct <= 0x2202)) { + return 1; + } + } + return 0; +} + +int usb_stor_huawei_init(struct us_data *us) +{ + int result = 0; + + if (usb_stor_huawei_dongles_pid(us)) { + if (us->pusb_dev->descriptor.idProduct >= 0x1446) + result = usb_stor_huawei_scsi_init(us); + else + result = usb_stor_huawei_feature_init(us); + } + return result; +} diff --git a/drivers/usb/storage/initializers.h b/drivers/usb/storage/initializers.h index 529327fbb06b..5376d4fc76f0 100644 --- a/drivers/usb/storage/initializers.h +++ b/drivers/usb/storage/initializers.h @@ -46,5 +46,5 @@ int usb_stor_euscsi_init(struct us_data *us); * flash reader */ int usb_stor_ucr61s2b_init(struct us_data *us); -/* This places the HUAWEI E220 devices in multi-port mode */ -int usb_stor_huawei_e220_init(struct us_data *us); +/* This places the HUAWEI usb dongles in multi-port mode */ +int usb_stor_huawei_init(struct us_data *us); diff --git a/drivers/usb/storage/unusual_devs.h b/drivers/usb/storage/unusual_devs.h index d305a5aa3a5d..72923b56bbf6 100644 --- a/drivers/usb/storage/unusual_devs.h +++ b/drivers/usb/storage/unusual_devs.h @@ -1527,335 +1527,10 @@ UNUSUAL_DEV( 0x1210, 0x0003, 0x0100, 0x0100, /* Reported by fangxiaozhi <huananhu@huawei.com> * This brings the HUAWEI data card devices into multi-port mode */ -UNUSUAL_DEV( 0x12d1, 0x1001, 0x0000, 0x0000, +UNUSUAL_VENDOR_INTF(0x12d1, 0x08, 0x06, 0x50, "HUAWEI MOBILE", "Mass Storage", - USB_SC_DEVICE, USB_PR_DEVICE, usb_stor_huawei_e220_init, - 0), -UNUSUAL_DEV( 0x12d1, 0x1003, 0x0000, 0x0000, - "HUAWEI MOBILE", - "Mass Storage", - USB_SC_DEVICE, USB_PR_DEVICE, usb_stor_huawei_e220_init, - 0), -UNUSUAL_DEV( 0x12d1, 0x1004, 0x0000, 0x0000, - "HUAWEI MOBILE", - "Mass Storage", - USB_SC_DEVICE, USB_PR_DEVICE, usb_stor_huawei_e220_init, - 0), -UNUSUAL_DEV( 0x12d1, 0x1401, 0x0000, 0x0000, - "HUAWEI MOBILE", - "Mass Storage", - USB_SC_DEVICE, USB_PR_DEVICE, usb_stor_huawei_e220_init, - 0), -UNUSUAL_DEV( 0x12d1, 0x1402, 0x0000, 0x0000, - "HUAWEI MOBILE", - "Mass Storage", - USB_SC_DEVICE, USB_PR_DEVICE, usb_stor_huawei_e220_init, - 0), -UNUSUAL_DEV( 0x12d1, 0x1403, 0x0000, 0x0000, - "HUAWEI MOBILE", - "Mass Storage", - USB_SC_DEVICE, USB_PR_DEVICE, usb_stor_huawei_e220_init, - 0), -UNUSUAL_DEV( 0x12d1, 0x1404, 0x0000, 0x0000, - "HUAWEI MOBILE", - "Mass Storage", - USB_SC_DEVICE, USB_PR_DEVICE, usb_stor_huawei_e220_init, - 0), -UNUSUAL_DEV( 0x12d1, 0x1405, 0x0000, 0x0000, - "HUAWEI MOBILE", - "Mass Storage", - USB_SC_DEVICE, USB_PR_DEVICE, usb_stor_huawei_e220_init, - 0), -UNUSUAL_DEV( 0x12d1, 0x1406, 0x0000, 0x0000, - "HUAWEI MOBILE", - "Mass Storage", - USB_SC_DEVICE, USB_PR_DEVICE, usb_stor_huawei_e220_init, - 0), -UNUSUAL_DEV( 0x12d1, 0x1407, 0x0000, 0x0000, - "HUAWEI MOBILE", - "Mass Storage", - USB_SC_DEVICE, USB_PR_DEVICE, usb_stor_huawei_e220_init, - 0), -UNUSUAL_DEV( 0x12d1, 0x1408, 0x0000, 0x0000, - "HUAWEI MOBILE", - "Mass Storage", - USB_SC_DEVICE, USB_PR_DEVICE, usb_stor_huawei_e220_init, - 0), -UNUSUAL_DEV( 0x12d1, 0x1409, 0x0000, 0x0000, - "HUAWEI MOBILE", - "Mass Storage", - USB_SC_DEVICE, USB_PR_DEVICE, usb_stor_huawei_e220_init, - 0), -UNUSUAL_DEV( 0x12d1, 0x140A, 0x0000, 0x0000, - "HUAWEI MOBILE", - "Mass Storage", - USB_SC_DEVICE, USB_PR_DEVICE, usb_stor_huawei_e220_init, - 0), -UNUSUAL_DEV( 0x12d1, 0x140B, 0x0000, 0x0000, - "HUAWEI MOBILE", - "Mass Storage", - USB_SC_DEVICE, USB_PR_DEVICE, usb_stor_huawei_e220_init, - 0), -UNUSUAL_DEV( 0x12d1, 0x140C, 0x0000, 0x0000, - "HUAWEI MOBILE", - "Mass Storage", - USB_SC_DEVICE, USB_PR_DEVICE, usb_stor_huawei_e220_init, - 0), -UNUSUAL_DEV( 0x12d1, 0x140D, 0x0000, 0x0000, - "HUAWEI MOBILE", - "Mass Storage", - USB_SC_DEVICE, USB_PR_DEVICE, usb_stor_huawei_e220_init, - 0), -UNUSUAL_DEV( 0x12d1, 0x140E, 0x0000, 0x0000, - "HUAWEI MOBILE", - "Mass Storage", - USB_SC_DEVICE, USB_PR_DEVICE, usb_stor_huawei_e220_init, - 0), -UNUSUAL_DEV( 0x12d1, 0x140F, 0x0000, 0x0000, - "HUAWEI MOBILE", - "Mass Storage", - USB_SC_DEVICE, USB_PR_DEVICE, usb_stor_huawei_e220_init, - 0), -UNUSUAL_DEV( 0x12d1, 0x1410, 0x0000, 0x0000, - "HUAWEI MOBILE", - "Mass Storage", - USB_SC_DEVICE, USB_PR_DEVICE, usb_stor_huawei_e220_init, - 0), -UNUSUAL_DEV( 0x12d1, 0x1411, 0x0000, 0x0000, - "HUAWEI MOBILE", - "Mass Storage", - USB_SC_DEVICE, USB_PR_DEVICE, usb_stor_huawei_e220_init, - 0), -UNUSUAL_DEV( 0x12d1, 0x1412, 0x0000, 0x0000, - "HUAWEI MOBILE", - "Mass Storage", - USB_SC_DEVICE, USB_PR_DEVICE, usb_stor_huawei_e220_init, - 0), -UNUSUAL_DEV( 0x12d1, 0x1413, 0x0000, 0x0000, - "HUAWEI MOBILE", - "Mass Storage", - USB_SC_DEVICE, USB_PR_DEVICE, usb_stor_huawei_e220_init, - 0), -UNUSUAL_DEV( 0x12d1, 0x1414, 0x0000, 0x0000, - "HUAWEI MOBILE", - "Mass Storage", - USB_SC_DEVICE, USB_PR_DEVICE, usb_stor_huawei_e220_init, - 0), -UNUSUAL_DEV( 0x12d1, 0x1415, 0x0000, 0x0000, - "HUAWEI MOBILE", - "Mass Storage", - USB_SC_DEVICE, USB_PR_DEVICE, usb_stor_huawei_e220_init, - 0), -UNUSUAL_DEV( 0x12d1, 0x1416, 0x0000, 0x0000, - "HUAWEI MOBILE", - "Mass Storage", - USB_SC_DEVICE, USB_PR_DEVICE, usb_stor_huawei_e220_init, - 0), -UNUSUAL_DEV( 0x12d1, 0x1417, 0x0000, 0x0000, - "HUAWEI MOBILE", - "Mass Storage", - USB_SC_DEVICE, USB_PR_DEVICE, usb_stor_huawei_e220_init, - 0), -UNUSUAL_DEV( 0x12d1, 0x1418, 0x0000, 0x0000, - "HUAWEI MOBILE", - "Mass Storage", - USB_SC_DEVICE, USB_PR_DEVICE, usb_stor_huawei_e220_init, - 0), -UNUSUAL_DEV( 0x12d1, 0x1419, 0x0000, 0x0000, - "HUAWEI MOBILE", - "Mass Storage", - USB_SC_DEVICE, USB_PR_DEVICE, usb_stor_huawei_e220_init, - 0), -UNUSUAL_DEV( 0x12d1, 0x141A, 0x0000, 0x0000, - "HUAWEI MOBILE", - "Mass Storage", - USB_SC_DEVICE, USB_PR_DEVICE, usb_stor_huawei_e220_init, - 0), -UNUSUAL_DEV( 0x12d1, 0x141B, 0x0000, 0x0000, - "HUAWEI MOBILE", - "Mass Storage", - USB_SC_DEVICE, USB_PR_DEVICE, usb_stor_huawei_e220_init, - 0), -UNUSUAL_DEV( 0x12d1, 0x141C, 0x0000, 0x0000, - "HUAWEI MOBILE", - "Mass Storage", - USB_SC_DEVICE, USB_PR_DEVICE, usb_stor_huawei_e220_init, - 0), -UNUSUAL_DEV( 0x12d1, 0x141D, 0x0000, 0x0000, - "HUAWEI MOBILE", - "Mass Storage", - USB_SC_DEVICE, USB_PR_DEVICE, usb_stor_huawei_e220_init, - 0), -UNUSUAL_DEV( 0x12d1, 0x141E, 0x0000, 0x0000, - "HUAWEI MOBILE", - "Mass Storage", - USB_SC_DEVICE, USB_PR_DEVICE, usb_stor_huawei_e220_init, - 0), -UNUSUAL_DEV( 0x12d1, 0x141F, 0x0000, 0x0000, - "HUAWEI MOBILE", - "Mass Storage", - USB_SC_DEVICE, USB_PR_DEVICE, usb_stor_huawei_e220_init, - 0), -UNUSUAL_DEV( 0x12d1, 0x1420, 0x0000, 0x0000, - "HUAWEI MOBILE", - "Mass Storage", - USB_SC_DEVICE, USB_PR_DEVICE, usb_stor_huawei_e220_init, - 0), -UNUSUAL_DEV( 0x12d1, 0x1421, 0x0000, 0x0000, - "HUAWEI MOBILE", - "Mass Storage", - USB_SC_DEVICE, USB_PR_DEVICE, usb_stor_huawei_e220_init, - 0), -UNUSUAL_DEV( 0x12d1, 0x1422, 0x0000, 0x0000, - "HUAWEI MOBILE", - "Mass Storage", - USB_SC_DEVICE, USB_PR_DEVICE, usb_stor_huawei_e220_init, - 0), -UNUSUAL_DEV( 0x12d1, 0x1423, 0x0000, 0x0000, - "HUAWEI MOBILE", - "Mass Storage", - USB_SC_DEVICE, USB_PR_DEVICE, usb_stor_huawei_e220_init, - 0), -UNUSUAL_DEV( 0x12d1, 0x1424, 0x0000, 0x0000, - "HUAWEI MOBILE", - "Mass Storage", - USB_SC_DEVICE, USB_PR_DEVICE, usb_stor_huawei_e220_init, - 0), -UNUSUAL_DEV( 0x12d1, 0x1425, 0x0000, 0x0000, - "HUAWEI MOBILE", - "Mass Storage", - USB_SC_DEVICE, USB_PR_DEVICE, usb_stor_huawei_e220_init, - 0), -UNUSUAL_DEV( 0x12d1, 0x1426, 0x0000, 0x0000, - "HUAWEI MOBILE", - "Mass Storage", - USB_SC_DEVICE, USB_PR_DEVICE, usb_stor_huawei_e220_init, - 0), -UNUSUAL_DEV( 0x12d1, 0x1427, 0x0000, 0x0000, - "HUAWEI MOBILE", - "Mass Storage", - USB_SC_DEVICE, USB_PR_DEVICE, usb_stor_huawei_e220_init, - 0), -UNUSUAL_DEV( 0x12d1, 0x1428, 0x0000, 0x0000, - "HUAWEI MOBILE", - "Mass Storage", - USB_SC_DEVICE, USB_PR_DEVICE, usb_stor_huawei_e220_init, - 0), -UNUSUAL_DEV( 0x12d1, 0x1429, 0x0000, 0x0000, - "HUAWEI MOBILE", - "Mass Storage", - USB_SC_DEVICE, USB_PR_DEVICE, usb_stor_huawei_e220_init, - 0), -UNUSUAL_DEV( 0x12d1, 0x142A, 0x0000, 0x0000, - "HUAWEI MOBILE", - "Mass Storage", - USB_SC_DEVICE, USB_PR_DEVICE, usb_stor_huawei_e220_init, - 0), -UNUSUAL_DEV( 0x12d1, 0x142B, 0x0000, 0x0000, - "HUAWEI MOBILE", - "Mass Storage", - USB_SC_DEVICE, USB_PR_DEVICE, usb_stor_huawei_e220_init, - 0), -UNUSUAL_DEV( 0x12d1, 0x142C, 0x0000, 0x0000, - "HUAWEI MOBILE", - "Mass Storage", - USB_SC_DEVICE, USB_PR_DEVICE, usb_stor_huawei_e220_init, - 0), -UNUSUAL_DEV( 0x12d1, 0x142D, 0x0000, 0x0000, - "HUAWEI MOBILE", - "Mass Storage", - USB_SC_DEVICE, USB_PR_DEVICE, usb_stor_huawei_e220_init, - 0), -UNUSUAL_DEV( 0x12d1, 0x142E, 0x0000, 0x0000, - "HUAWEI MOBILE", - "Mass Storage", - USB_SC_DEVICE, USB_PR_DEVICE, usb_stor_huawei_e220_init, - 0), -UNUSUAL_DEV( 0x12d1, 0x142F, 0x0000, 0x0000, - "HUAWEI MOBILE", - "Mass Storage", - USB_SC_DEVICE, USB_PR_DEVICE, usb_stor_huawei_e220_init, - 0), -UNUSUAL_DEV( 0x12d1, 0x1430, 0x0000, 0x0000, - "HUAWEI MOBILE", - "Mass Storage", - USB_SC_DEVICE, USB_PR_DEVICE, usb_stor_huawei_e220_init, - 0), -UNUSUAL_DEV( 0x12d1, 0x1431, 0x0000, 0x0000, - "HUAWEI MOBILE", - "Mass Storage", - USB_SC_DEVICE, USB_PR_DEVICE, usb_stor_huawei_e220_init, - 0), -UNUSUAL_DEV( 0x12d1, 0x1432, 0x0000, 0x0000, - "HUAWEI MOBILE", - "Mass Storage", - USB_SC_DEVICE, USB_PR_DEVICE, usb_stor_huawei_e220_init, - 0), -UNUSUAL_DEV( 0x12d1, 0x1433, 0x0000, 0x0000, - "HUAWEI MOBILE", - "Mass Storage", - USB_SC_DEVICE, USB_PR_DEVICE, usb_stor_huawei_e220_init, - 0), -UNUSUAL_DEV( 0x12d1, 0x1434, 0x0000, 0x0000, - "HUAWEI MOBILE", - "Mass Storage", - USB_SC_DEVICE, USB_PR_DEVICE, usb_stor_huawei_e220_init, - 0), -UNUSUAL_DEV( 0x12d1, 0x1435, 0x0000, 0x0000, - "HUAWEI MOBILE", - "Mass Storage", - USB_SC_DEVICE, USB_PR_DEVICE, usb_stor_huawei_e220_init, - 0), -UNUSUAL_DEV( 0x12d1, 0x1436, 0x0000, 0x0000, - "HUAWEI MOBILE", - "Mass Storage", - USB_SC_DEVICE, USB_PR_DEVICE, usb_stor_huawei_e220_init, - 0), -UNUSUAL_DEV( 0x12d1, 0x1437, 0x0000, 0x0000, - "HUAWEI MOBILE", - "Mass Storage", - USB_SC_DEVICE, USB_PR_DEVICE, usb_stor_huawei_e220_init, - 0), -UNUSUAL_DEV( 0x12d1, 0x1438, 0x0000, 0x0000, - "HUAWEI MOBILE", - "Mass Storage", - USB_SC_DEVICE, USB_PR_DEVICE, usb_stor_huawei_e220_init, - 0), -UNUSUAL_DEV( 0x12d1, 0x1439, 0x0000, 0x0000, - "HUAWEI MOBILE", - "Mass Storage", - USB_SC_DEVICE, USB_PR_DEVICE, usb_stor_huawei_e220_init, - 0), -UNUSUAL_DEV( 0x12d1, 0x143A, 0x0000, 0x0000, - "HUAWEI MOBILE", - "Mass Storage", - USB_SC_DEVICE, USB_PR_DEVICE, usb_stor_huawei_e220_init, - 0), -UNUSUAL_DEV( 0x12d1, 0x143B, 0x0000, 0x0000, - "HUAWEI MOBILE", - "Mass Storage", - USB_SC_DEVICE, USB_PR_DEVICE, usb_stor_huawei_e220_init, - 0), -UNUSUAL_DEV( 0x12d1, 0x143C, 0x0000, 0x0000, - "HUAWEI MOBILE", - "Mass Storage", - USB_SC_DEVICE, USB_PR_DEVICE, usb_stor_huawei_e220_init, - 0), -UNUSUAL_DEV( 0x12d1, 0x143D, 0x0000, 0x0000, - "HUAWEI MOBILE", - "Mass Storage", - USB_SC_DEVICE, USB_PR_DEVICE, usb_stor_huawei_e220_init, - 0), -UNUSUAL_DEV( 0x12d1, 0x143E, 0x0000, 0x0000, - "HUAWEI MOBILE", - "Mass Storage", - USB_SC_DEVICE, USB_PR_DEVICE, usb_stor_huawei_e220_init, - 0), -UNUSUAL_DEV( 0x12d1, 0x143F, 0x0000, 0x0000, - "HUAWEI MOBILE", - "Mass Storage", - USB_SC_DEVICE, USB_PR_DEVICE, usb_stor_huawei_e220_init, + USB_SC_DEVICE, USB_PR_DEVICE, usb_stor_huawei_init, 0), /* Reported by Vilius Bilinkevicius <vilisas AT xxx DOT lt) */ diff --git a/drivers/usb/storage/usb.c b/drivers/usb/storage/usb.c index 31b3e1a61bbd..cf09b6ba71ff 100644 --- a/drivers/usb/storage/usb.c +++ b/drivers/usb/storage/usb.c @@ -120,6 +120,17 @@ MODULE_PARM_DESC(quirks, "supplemental list of device IDs and their quirks"); .useTransport = use_transport, \ } +#define UNUSUAL_VENDOR_INTF(idVendor, cl, sc, pr, \ + vendor_name, product_name, use_protocol, use_transport, \ + init_function, Flags) \ +{ \ + .vendorName = vendor_name, \ + .productName = product_name, \ + .useProtocol = use_protocol, \ + .useTransport = use_transport, \ + .initFunction = init_function, \ +} + static struct us_unusual_dev us_unusual_dev_list[] = { # include "unusual_devs.h" { } /* Terminating entry */ @@ -131,6 +142,7 @@ static struct us_unusual_dev for_dynamic_ids = #undef UNUSUAL_DEV #undef COMPLIANT_DEV #undef USUAL_DEV +#undef UNUSUAL_VENDOR_INTF #ifdef CONFIG_LOCKDEP diff --git a/drivers/usb/storage/usual-tables.c b/drivers/usb/storage/usual-tables.c index b78a526910fb..5ef8ce74aae4 100644 --- a/drivers/usb/storage/usual-tables.c +++ b/drivers/usb/storage/usual-tables.c @@ -41,6 +41,20 @@ #define USUAL_DEV(useProto, useTrans) \ { USB_INTERFACE_INFO(USB_CLASS_MASS_STORAGE, useProto, useTrans) } +/* Define the device is matched with Vendor ID and interface descriptors */ +#define UNUSUAL_VENDOR_INTF(id_vendor, cl, sc, pr, \ + vendorName, productName, useProtocol, useTransport, \ + initFunction, flags) \ +{ \ + .match_flags = USB_DEVICE_ID_MATCH_INT_INFO \ + | USB_DEVICE_ID_MATCH_VENDOR, \ + .idVendor = (id_vendor), \ + .bInterfaceClass = (cl), \ + .bInterfaceSubClass = (sc), \ + .bInterfaceProtocol = (pr), \ + .driver_info = (flags) \ +} + struct usb_device_id usb_storage_usb_ids[] = { # include "unusual_devs.h" { } /* Terminating entry */ @@ -50,6 +64,7 @@ MODULE_DEVICE_TABLE(usb, usb_storage_usb_ids); #undef UNUSUAL_DEV #undef COMPLIANT_DEV #undef USUAL_DEV +#undef UNUSUAL_VENDOR_INTF /* * The table of devices to ignore diff --git a/drivers/vfio/pci/vfio_pci_rdwr.c b/drivers/vfio/pci/vfio_pci_rdwr.c index 4362d9e7baa3..f72323ef618f 100644 --- a/drivers/vfio/pci/vfio_pci_rdwr.c +++ b/drivers/vfio/pci/vfio_pci_rdwr.c @@ -240,17 +240,17 @@ ssize_t vfio_pci_mem_readwrite(struct vfio_pci_device *vdev, char __user *buf, filled = 1; } else { /* Drop writes, fill reads with FF */ + filled = min((size_t)(x_end - pos), count); if (!iswrite) { char val = 0xFF; size_t i; - for (i = 0; i < x_end - pos; i++) { + for (i = 0; i < filled; i++) { if (put_user(val, buf + i)) goto out; } } - filled = x_end - pos; } count -= filled; diff --git a/drivers/vhost/tcm_vhost.c b/drivers/vhost/tcm_vhost.c index b20df5c829f5..22321cf84fbe 100644 --- a/drivers/vhost/tcm_vhost.c +++ b/drivers/vhost/tcm_vhost.c @@ -575,10 +575,8 @@ static void vhost_scsi_handle_vq(struct vhost_scsi *vs) /* Must use ioctl VHOST_SCSI_SET_ENDPOINT */ tv_tpg = vs->vs_tpg; - if (unlikely(!tv_tpg)) { - pr_err("%s endpoint not set\n", __func__); + if (unlikely(!tv_tpg)) return; - } mutex_lock(&vq->mutex); vhost_disable_notify(&vs->dev, vq); diff --git a/drivers/video/Kconfig b/drivers/video/Kconfig index e7068c508800..09f1a18c1adf 100644 --- a/drivers/video/Kconfig +++ b/drivers/video/Kconfig @@ -21,8 +21,6 @@ source "drivers/gpu/vga/Kconfig" source "drivers/gpu/drm/Kconfig" -source "drivers/gpu/stub/Kconfig" - config VGASTATE tristate default n @@ -33,6 +31,27 @@ config VIDEO_OUTPUT_CONTROL This framework adds support for low-level control of the video output switch. +config DISPLAY_TIMING + bool + +config VIDEOMODE + bool + +config OF_DISPLAY_TIMING + bool "Enable device tree display timing support" + depends on OF + select DISPLAY_TIMING + help + helper to parse display timings from the devicetree + +config OF_VIDEOMODE + bool "Enable device tree videomode support" + depends on OF + select VIDEOMODE + select OF_DISPLAY_TIMING + help + helper to get videomodes from the devicetree + menuconfig FB tristate "Support for frame buffer devices" ---help--- diff --git a/drivers/video/Makefile b/drivers/video/Makefile index 768a137a1bac..f592f3b32ec7 100644 --- a/drivers/video/Makefile +++ b/drivers/video/Makefile @@ -168,3 +168,7 @@ obj-$(CONFIG_FB_VIRTUAL) += vfb.o #video output switch sysfs driver obj-$(CONFIG_VIDEO_OUTPUT_CONTROL) += output.o +obj-$(CONFIG_DISPLAY_TIMING) += display_timing.o +obj-$(CONFIG_OF_DISPLAY_TIMING) += of_display_timing.o +obj-$(CONFIG_VIDEOMODE) += videomode.o +obj-$(CONFIG_OF_VIDEOMODE) += of_videomode.o diff --git a/drivers/video/console/fbcon.c b/drivers/video/console/fbcon.c index fdefa8fd72c4..501c599e7549 100644 --- a/drivers/video/console/fbcon.c +++ b/drivers/video/console/fbcon.c @@ -529,6 +529,33 @@ static int search_for_mapped_con(void) return retval; } +static int do_fbcon_takeover(int show_logo) +{ + int err, i; + + if (!num_registered_fb) + return -ENODEV; + + if (!show_logo) + logo_shown = FBCON_LOGO_DONTSHOW; + + for (i = first_fb_vc; i <= last_fb_vc; i++) + con2fb_map[i] = info_idx; + + err = do_take_over_console(&fb_con, first_fb_vc, last_fb_vc, + fbcon_is_default); + + if (err) { + for (i = first_fb_vc; i <= last_fb_vc; i++) + con2fb_map[i] = -1; + info_idx = -1; + } else { + fbcon_has_console_bind = 1; + } + + return err; +} + static int fbcon_takeover(int show_logo) { int err, i; @@ -815,6 +842,8 @@ static void con2fb_init_display(struct vc_data *vc, struct fb_info *info, * * Maps a virtual console @unit to a frame buffer device * @newidx. + * + * This should be called with the console lock held. */ static int set_con2fb_map(int unit, int newidx, int user) { @@ -832,7 +861,7 @@ static int set_con2fb_map(int unit, int newidx, int user) if (!search_for_mapped_con() || !con_is_bound(&fb_con)) { info_idx = newidx; - return fbcon_takeover(0); + return do_fbcon_takeover(0); } if (oldidx != -1) @@ -840,7 +869,6 @@ static int set_con2fb_map(int unit, int newidx, int user) found = search_fb_in_map(newidx); - console_lock(); con2fb_map[unit] = newidx; if (!err && !found) err = con2fb_acquire_newinfo(vc, info, unit, oldidx); @@ -867,7 +895,6 @@ static int set_con2fb_map(int unit, int newidx, int user) if (!search_fb_in_map(info_idx)) info_idx = newidx; - console_unlock(); return err; } @@ -990,7 +1017,7 @@ static const char *fbcon_startup(void) } /* Setup default font */ - if (!p->fontdata) { + if (!p->fontdata && !vc->vc_font.data) { if (!fontname[0] || !(font = find_font(fontname))) font = get_default_font(info->var.xres, info->var.yres, @@ -1000,6 +1027,8 @@ static const char *fbcon_startup(void) vc->vc_font.height = font->height; vc->vc_font.data = (void *)(p->fontdata = font->data); vc->vc_font.charcount = 256; /* FIXME Need to support more fonts */ + } else { + p->fontdata = vc->vc_font.data; } cols = FBCON_SWAP(ops->rotate, info->var.xres, info->var.yres); @@ -1159,9 +1188,9 @@ static void fbcon_init(struct vc_data *vc, int init) ops->p = &fb_display[fg_console]; } -static void fbcon_free_font(struct display *p) +static void fbcon_free_font(struct display *p, bool freefont) { - if (p->userfont && p->fontdata && (--REFCOUNT(p->fontdata) == 0)) + if (freefont && p->userfont && p->fontdata && (--REFCOUNT(p->fontdata) == 0)) kfree(p->fontdata - FONT_EXTRA_WORDS * sizeof(int)); p->fontdata = NULL; p->userfont = 0; @@ -1173,8 +1202,8 @@ static void fbcon_deinit(struct vc_data *vc) struct fb_info *info; struct fbcon_ops *ops; int idx; + bool free_font = true; - fbcon_free_font(p); idx = con2fb_map[vc->vc_num]; if (idx == -1) @@ -1185,6 +1214,8 @@ static void fbcon_deinit(struct vc_data *vc) if (!info) goto finished; + if (info->flags & FBINFO_MISC_FIRMWARE) + free_font = false; ops = info->fbcon_par; if (!ops) @@ -1196,6 +1227,8 @@ static void fbcon_deinit(struct vc_data *vc) ops->flags &= ~FBCON_FLAGS_INIT; finished: + fbcon_free_font(p, free_font); + if (!con_is_bound(&fb_con)) fbcon_exit(); @@ -2977,7 +3010,7 @@ static int fbcon_unbind(void) { int ret; - ret = unbind_con_driver(&fb_con, first_fb_vc, last_fb_vc, + ret = do_unbind_con_driver(&fb_con, first_fb_vc, last_fb_vc, fbcon_is_default); if (!ret) @@ -2992,6 +3025,7 @@ static inline int fbcon_unbind(void) } #endif /* CONFIG_VT_HW_CONSOLE_BINDING */ +/* called with console_lock held */ static int fbcon_fb_unbind(int idx) { int i, new_idx = -1, ret = 0; @@ -3018,6 +3052,7 @@ static int fbcon_fb_unbind(int idx) return ret; } +/* called with console_lock held */ static int fbcon_fb_unregistered(struct fb_info *info) { int i, idx; @@ -3050,11 +3085,12 @@ static int fbcon_fb_unregistered(struct fb_info *info) primary_device = -1; if (!num_registered_fb) - unregister_con_driver(&fb_con); + do_unregister_con_driver(&fb_con); return 0; } +/* called with console_lock held */ static void fbcon_remap_all(int idx) { int i; @@ -3099,6 +3135,7 @@ static inline void fbcon_select_primary(struct fb_info *info) } #endif /* CONFIG_FRAMEBUFFER_DETECT_PRIMARY */ +/* called with console_lock held */ static int fbcon_fb_registered(struct fb_info *info) { int ret = 0, i, idx; @@ -3115,7 +3152,7 @@ static int fbcon_fb_registered(struct fb_info *info) } if (info_idx != -1) - ret = fbcon_takeover(1); + ret = do_fbcon_takeover(1); } else { for (i = first_fb_vc; i <= last_fb_vc; i++) { if (con2fb_map_boot[i] == idx) @@ -3251,6 +3288,7 @@ static int fbcon_event_notify(struct notifier_block *self, ret = fbcon_fb_unregistered(info); break; case FB_EVENT_SET_CONSOLE_MAP: + /* called with console lock held */ con2fb = event->data; ret = set_con2fb_map(con2fb->console - 1, con2fb->framebuffer, 1); diff --git a/drivers/video/console/vgacon.c b/drivers/video/console/vgacon.c index d449a74d4a31..5855d17d19ac 100644 --- a/drivers/video/console/vgacon.c +++ b/drivers/video/console/vgacon.c @@ -1064,7 +1064,7 @@ static int vgacon_do_font_op(struct vgastate *state,char *arg,int set,int ch512) unsigned short video_port_status = vga_video_port_reg + 6; int font_select = 0x00, beg, i; char *charmap; - + bool clear_attribs = false; if (vga_video_type != VIDEO_TYPE_EGAM) { charmap = (char *) VGA_MAP_MEM(colourmap, 0); beg = 0x0e; @@ -1169,12 +1169,6 @@ static int vgacon_do_font_op(struct vgastate *state,char *arg,int set,int ch512) /* if 512 char mode is already enabled don't re-enable it. */ if ((set) && (ch512 != vga_512_chars)) { - /* attribute controller */ - for (i = 0; i < MAX_NR_CONSOLES; i++) { - struct vc_data *c = vc_cons[i].d; - if (c && c->vc_sw == &vga_con) - c->vc_hi_font_mask = ch512 ? 0x0800 : 0; - } vga_512_chars = ch512; /* 256-char: enable intensity bit 512-char: disable intensity bit */ @@ -1185,8 +1179,22 @@ static int vgacon_do_font_op(struct vgastate *state,char *arg,int set,int ch512) it means, but it works, and it appears necessary */ inb_p(video_port_status); vga_wattr(state->vgabase, VGA_AR_ENABLE_DISPLAY, 0); + clear_attribs = true; } raw_spin_unlock_irq(&vga_lock); + + if (clear_attribs) { + for (i = 0; i < MAX_NR_CONSOLES; i++) { + struct vc_data *c = vc_cons[i].d; + if (c && c->vc_sw == &vga_con) { + /* force hi font mask to 0, so we always clear + the bit on either transition */ + c->vc_hi_font_mask = 0x00; + clear_buffer_attributes(c); + c->vc_hi_font_mask = ch512 ? 0x0800 : 0; + } + } + } return 0; } diff --git a/drivers/video/display_timing.c b/drivers/video/display_timing.c new file mode 100644 index 000000000000..5e1822cef571 --- /dev/null +++ b/drivers/video/display_timing.c @@ -0,0 +1,24 @@ +/* + * generic display timing functions + * + * Copyright (c) 2012 Steffen Trumtrar <s.trumtrar@pengutronix.de>, Pengutronix + * + * This file is released under the GPLv2 + */ + +#include <linux/export.h> +#include <linux/slab.h> +#include <video/display_timing.h> + +void display_timings_release(struct display_timings *disp) +{ + if (disp->timings) { + unsigned int i; + + for (i = 0; i < disp->num_timings; i++) + kfree(disp->timings[i]); + kfree(disp->timings); + } + kfree(disp); +} +EXPORT_SYMBOL_GPL(display_timings_release); diff --git a/drivers/video/fbmem.c b/drivers/video/fbmem.c index 3ff0105a496a..dc61c12ecf8c 100644 --- a/drivers/video/fbmem.c +++ b/drivers/video/fbmem.c @@ -1177,8 +1177,10 @@ static long do_fb_ioctl(struct fb_info *info, unsigned int cmd, event.data = &con2fb; if (!lock_fb_info(info)) return -ENODEV; + console_lock(); event.info = info; ret = fb_notifier_call_chain(FB_EVENT_SET_CONSOLE_MAP, &event); + console_unlock(); unlock_fb_info(info); break; case FBIOBLANK: @@ -1650,7 +1652,9 @@ static int do_register_framebuffer(struct fb_info *fb_info) event.info = fb_info; if (!lock_fb_info(fb_info)) return -ENODEV; + console_lock(); fb_notifier_call_chain(FB_EVENT_FB_REGISTERED, &event); + console_unlock(); unlock_fb_info(fb_info); return 0; } @@ -1666,8 +1670,10 @@ static int do_unregister_framebuffer(struct fb_info *fb_info) if (!lock_fb_info(fb_info)) return -ENODEV; + console_lock(); event.info = fb_info; ret = fb_notifier_call_chain(FB_EVENT_FB_UNBIND, &event); + console_unlock(); unlock_fb_info(fb_info); if (ret) @@ -1682,7 +1688,9 @@ static int do_unregister_framebuffer(struct fb_info *fb_info) num_registered_fb--; fb_cleanup_device(fb_info); event.info = fb_info; + console_lock(); fb_notifier_call_chain(FB_EVENT_FB_UNREGISTERED, &event); + console_unlock(); /* this may free fb info */ put_fb_info(fb_info); @@ -1853,11 +1861,8 @@ int fb_new_modelist(struct fb_info *info) err = 1; if (!list_empty(&info->modelist)) { - if (!lock_fb_info(info)) - return -ENODEV; event.info = info; err = fb_notifier_call_chain(FB_EVENT_NEW_MODELIST, &event); - unlock_fb_info(info); } return err; diff --git a/drivers/video/fbmon.c b/drivers/video/fbmon.c index cef65574db6c..94ad0f71383c 100644 --- a/drivers/video/fbmon.c +++ b/drivers/video/fbmon.c @@ -31,6 +31,8 @@ #include <linux/pci.h> #include <linux/slab.h> #include <video/edid.h> +#include <video/of_videomode.h> +#include <video/videomode.h> #ifdef CONFIG_PPC_OF #include <asm/prom.h> #include <asm/pci-bridge.h> @@ -1373,6 +1375,98 @@ int fb_get_mode(int flags, u32 val, struct fb_var_screeninfo *var, struct fb_inf kfree(timings); return err; } + +#if IS_ENABLED(CONFIG_VIDEOMODE) +int fb_videomode_from_videomode(const struct videomode *vm, + struct fb_videomode *fbmode) +{ + unsigned int htotal, vtotal; + + fbmode->xres = vm->hactive; + fbmode->left_margin = vm->hback_porch; + fbmode->right_margin = vm->hfront_porch; + fbmode->hsync_len = vm->hsync_len; + + fbmode->yres = vm->vactive; + fbmode->upper_margin = vm->vback_porch; + fbmode->lower_margin = vm->vfront_porch; + fbmode->vsync_len = vm->vsync_len; + + /* prevent division by zero in KHZ2PICOS macro */ + fbmode->pixclock = vm->pixelclock ? + KHZ2PICOS(vm->pixelclock / 1000) : 0; + + fbmode->sync = 0; + fbmode->vmode = 0; + if (vm->dmt_flags & VESA_DMT_HSYNC_HIGH) + fbmode->sync |= FB_SYNC_HOR_HIGH_ACT; + if (vm->dmt_flags & VESA_DMT_HSYNC_HIGH) + fbmode->sync |= FB_SYNC_VERT_HIGH_ACT; + if (vm->data_flags & DISPLAY_FLAGS_INTERLACED) + fbmode->vmode |= FB_VMODE_INTERLACED; + if (vm->data_flags & DISPLAY_FLAGS_DOUBLESCAN) + fbmode->vmode |= FB_VMODE_DOUBLE; + fbmode->flag = 0; + + htotal = vm->hactive + vm->hfront_porch + vm->hback_porch + + vm->hsync_len; + vtotal = vm->vactive + vm->vfront_porch + vm->vback_porch + + vm->vsync_len; + /* prevent division by zero */ + if (htotal && vtotal) { + fbmode->refresh = vm->pixelclock / (htotal * vtotal); + /* a mode must have htotal and vtotal != 0 or it is invalid */ + } else { + fbmode->refresh = 0; + return -EINVAL; + } + + return 0; +} +EXPORT_SYMBOL_GPL(fb_videomode_from_videomode); +#endif + +#if IS_ENABLED(CONFIG_OF_VIDEOMODE) +static inline void dump_fb_videomode(const struct fb_videomode *m) +{ + pr_debug("fb_videomode = %ux%u@%uHz (%ukHz) %u %u %u %u %u %u %u %u %u\n", + m->xres, m->yres, m->refresh, m->pixclock, m->left_margin, + m->right_margin, m->upper_margin, m->lower_margin, + m->hsync_len, m->vsync_len, m->sync, m->vmode, m->flag); +} + +/** + * of_get_fb_videomode - get a fb_videomode from devicetree + * @np: device_node with the timing specification + * @fb: will be set to the return value + * @index: index into the list of display timings in devicetree + * + * DESCRIPTION: + * This function is expensive and should only be used, if only one mode is to be + * read from DT. To get multiple modes start with of_get_display_timings ond + * work with that instead. + */ +int of_get_fb_videomode(struct device_node *np, struct fb_videomode *fb, + int index) +{ + struct videomode vm; + int ret; + + ret = of_get_videomode(np, &vm, index); + if (ret) + return ret; + + fb_videomode_from_videomode(&vm, fb); + + pr_debug("%s: got %dx%d display mode from %s\n", + of_node_full_name(np), vm.hactive, vm.vactive, np->name); + dump_fb_videomode(fb); + + return 0; +} +EXPORT_SYMBOL_GPL(of_get_fb_videomode); +#endif + #else int fb_parse_edid(unsigned char *edid, struct fb_var_screeninfo *var) { diff --git a/drivers/video/fbsysfs.c b/drivers/video/fbsysfs.c index a55e3669d135..ef476b02fbe5 100644 --- a/drivers/video/fbsysfs.c +++ b/drivers/video/fbsysfs.c @@ -177,6 +177,8 @@ static ssize_t store_modes(struct device *device, if (i * sizeof(struct fb_videomode) != count) return -EINVAL; + if (!lock_fb_info(fb_info)) + return -ENODEV; console_lock(); list_splice(&fb_info->modelist, &old_list); fb_videomode_to_modelist((const struct fb_videomode *)buf, i, @@ -188,6 +190,7 @@ static ssize_t store_modes(struct device *device, fb_destroy_modelist(&old_list); console_unlock(); + unlock_fb_info(fb_info); return 0; } diff --git a/drivers/video/imxfb.c b/drivers/video/imxfb.c index 12526787a7c7..0abf2bf20836 100644 --- a/drivers/video/imxfb.c +++ b/drivers/video/imxfb.c @@ -139,6 +139,7 @@ struct imxfb_info { struct clk *clk_ahb; struct clk *clk_per; enum imxfb_type devtype; + bool enabled; /* * These are the addresses we mapped @@ -536,6 +537,10 @@ static void imxfb_exit_backlight(struct imxfb_info *fbi) static void imxfb_enable_controller(struct imxfb_info *fbi) { + + if (fbi->enabled) + return; + pr_debug("Enabling LCD controller\n"); writel(fbi->screen_dma, fbi->regs + LCDC_SSA); @@ -556,6 +561,7 @@ static void imxfb_enable_controller(struct imxfb_info *fbi) clk_prepare_enable(fbi->clk_ipg); clk_prepare_enable(fbi->clk_ahb); clk_prepare_enable(fbi->clk_per); + fbi->enabled = true; if (fbi->backlight_power) fbi->backlight_power(1); @@ -565,6 +571,9 @@ static void imxfb_enable_controller(struct imxfb_info *fbi) static void imxfb_disable_controller(struct imxfb_info *fbi) { + if (!fbi->enabled) + return; + pr_debug("Disabling LCD controller\n"); if (fbi->backlight_power) @@ -575,6 +584,7 @@ static void imxfb_disable_controller(struct imxfb_info *fbi) clk_disable_unprepare(fbi->clk_per); clk_disable_unprepare(fbi->clk_ipg); clk_disable_unprepare(fbi->clk_ahb); + fbi->enabled = false; writel(0, fbi->regs + LCDC_RMCR); } @@ -729,6 +739,8 @@ static int __init imxfb_init_fbinfo(struct platform_device *pdev) memset(fbi, 0, sizeof(struct imxfb_info)); + fbi->devtype = pdev->id_entry->driver_data; + strlcpy(info->fix.id, IMX_NAME, sizeof(info->fix.id)); info->fix.type = FB_TYPE_PACKED_PIXELS; @@ -789,7 +801,6 @@ static int __init imxfb_probe(struct platform_device *pdev) return -ENOMEM; fbi = info->par; - fbi->devtype = pdev->id_entry->driver_data; if (!fb_mode) fb_mode = pdata->mode[0].mode.name; diff --git a/drivers/video/of_display_timing.c b/drivers/video/of_display_timing.c new file mode 100644 index 000000000000..13ecd9897010 --- /dev/null +++ b/drivers/video/of_display_timing.c @@ -0,0 +1,239 @@ +/* + * OF helpers for parsing display timings + * + * Copyright (c) 2012 Steffen Trumtrar <s.trumtrar@pengutronix.de>, Pengutronix + * + * based on of_videomode.c by Sascha Hauer <s.hauer@pengutronix.de> + * + * This file is released under the GPLv2 + */ +#include <linux/export.h> +#include <linux/of.h> +#include <linux/slab.h> +#include <video/display_timing.h> +#include <video/of_display_timing.h> + +/** + * parse_timing_property - parse timing_entry from device_node + * @np: device_node with the property + * @name: name of the property + * @result: will be set to the return value + * + * DESCRIPTION: + * Every display_timing can be specified with either just the typical value or + * a range consisting of min/typ/max. This function helps handling this + **/ +static int parse_timing_property(struct device_node *np, const char *name, + struct timing_entry *result) +{ + struct property *prop; + int length, cells, ret; + + prop = of_find_property(np, name, &length); + if (!prop) { + pr_err("%s: could not find property %s\n", + of_node_full_name(np), name); + return -EINVAL; + } + + cells = length / sizeof(u32); + if (cells == 1) { + ret = of_property_read_u32(np, name, &result->typ); + result->min = result->typ; + result->max = result->typ; + } else if (cells == 3) { + ret = of_property_read_u32_array(np, name, &result->min, cells); + } else { + pr_err("%s: illegal timing specification in %s\n", + of_node_full_name(np), name); + return -EINVAL; + } + + return ret; +} + +/** + * of_get_display_timing - parse display_timing entry from device_node + * @np: device_node with the properties + **/ +static struct display_timing *of_get_display_timing(struct device_node *np) +{ + struct display_timing *dt; + u32 val = 0; + int ret = 0; + + dt = kzalloc(sizeof(*dt), GFP_KERNEL); + if (!dt) { + pr_err("%s: could not allocate display_timing struct\n", + of_node_full_name(np)); + return NULL; + } + + ret |= parse_timing_property(np, "hback-porch", &dt->hback_porch); + ret |= parse_timing_property(np, "hfront-porch", &dt->hfront_porch); + ret |= parse_timing_property(np, "hactive", &dt->hactive); + ret |= parse_timing_property(np, "hsync-len", &dt->hsync_len); + ret |= parse_timing_property(np, "vback-porch", &dt->vback_porch); + ret |= parse_timing_property(np, "vfront-porch", &dt->vfront_porch); + ret |= parse_timing_property(np, "vactive", &dt->vactive); + ret |= parse_timing_property(np, "vsync-len", &dt->vsync_len); + ret |= parse_timing_property(np, "clock-frequency", &dt->pixelclock); + + dt->dmt_flags = 0; + dt->data_flags = 0; + if (!of_property_read_u32(np, "vsync-active", &val)) + dt->dmt_flags |= val ? VESA_DMT_VSYNC_HIGH : + VESA_DMT_VSYNC_LOW; + if (!of_property_read_u32(np, "hsync-active", &val)) + dt->dmt_flags |= val ? VESA_DMT_HSYNC_HIGH : + VESA_DMT_HSYNC_LOW; + if (!of_property_read_u32(np, "de-active", &val)) + dt->data_flags |= val ? DISPLAY_FLAGS_DE_HIGH : + DISPLAY_FLAGS_DE_LOW; + if (!of_property_read_u32(np, "pixelclk-active", &val)) + dt->data_flags |= val ? DISPLAY_FLAGS_PIXDATA_POSEDGE : + DISPLAY_FLAGS_PIXDATA_NEGEDGE; + + if (of_property_read_bool(np, "interlaced")) + dt->data_flags |= DISPLAY_FLAGS_INTERLACED; + if (of_property_read_bool(np, "doublescan")) + dt->data_flags |= DISPLAY_FLAGS_DOUBLESCAN; + + if (ret) { + pr_err("%s: error reading timing properties\n", + of_node_full_name(np)); + kfree(dt); + return NULL; + } + + return dt; +} + +/** + * of_get_display_timings - parse all display_timing entries from a device_node + * @np: device_node with the subnodes + **/ +struct display_timings *of_get_display_timings(struct device_node *np) +{ + struct device_node *timings_np; + struct device_node *entry; + struct device_node *native_mode; + struct display_timings *disp; + + if (!np) { + pr_err("%s: no devicenode given\n", of_node_full_name(np)); + return NULL; + } + + timings_np = of_find_node_by_name(np, "display-timings"); + if (!timings_np) { + pr_err("%s: could not find display-timings node\n", + of_node_full_name(np)); + return NULL; + } + + disp = kzalloc(sizeof(*disp), GFP_KERNEL); + if (!disp) { + pr_err("%s: could not allocate struct disp'\n", + of_node_full_name(np)); + goto dispfail; + } + + entry = of_parse_phandle(timings_np, "native-mode", 0); + /* assume first child as native mode if none provided */ + if (!entry) + entry = of_get_next_child(np, NULL); + /* if there is no child, it is useless to go on */ + if (!entry) { + pr_err("%s: no timing specifications given\n", + of_node_full_name(np)); + goto entryfail; + } + + pr_debug("%s: using %s as default timing\n", + of_node_full_name(np), entry->name); + + native_mode = entry; + + disp->num_timings = of_get_child_count(timings_np); + if (disp->num_timings == 0) { + /* should never happen, as entry was already found above */ + pr_err("%s: no timings specified\n", of_node_full_name(np)); + goto entryfail; + } + + disp->timings = kzalloc(sizeof(struct display_timing *) * + disp->num_timings, GFP_KERNEL); + if (!disp->timings) { + pr_err("%s: could not allocate timings array\n", + of_node_full_name(np)); + goto entryfail; + } + + disp->num_timings = 0; + disp->native_mode = 0; + + for_each_child_of_node(timings_np, entry) { + struct display_timing *dt; + + dt = of_get_display_timing(entry); + if (!dt) { + /* + * to not encourage wrong devicetrees, fail in case of + * an error + */ + pr_err("%s: error in timing %d\n", + of_node_full_name(np), disp->num_timings + 1); + goto timingfail; + } + + if (native_mode == entry) + disp->native_mode = disp->num_timings; + + disp->timings[disp->num_timings] = dt; + disp->num_timings++; + } + of_node_put(timings_np); + /* + * native_mode points to the device_node returned by of_parse_phandle + * therefore call of_node_put on it + */ + of_node_put(native_mode); + + pr_debug("%s: got %d timings. Using timing #%d as default\n", + of_node_full_name(np), disp->num_timings, + disp->native_mode + 1); + + return disp; + +timingfail: + if (native_mode) + of_node_put(native_mode); + display_timings_release(disp); +entryfail: + kfree(disp); +dispfail: + of_node_put(timings_np); + return NULL; +} +EXPORT_SYMBOL_GPL(of_get_display_timings); + +/** + * of_display_timings_exist - check if a display-timings node is provided + * @np: device_node with the timing + **/ +int of_display_timings_exist(struct device_node *np) +{ + struct device_node *timings_np; + + if (!np) + return -EINVAL; + + timings_np = of_parse_phandle(np, "display-timings", 0); + if (!timings_np) + return -EINVAL; + + of_node_put(timings_np); + return 1; +} +EXPORT_SYMBOL_GPL(of_display_timings_exist); diff --git a/drivers/video/of_videomode.c b/drivers/video/of_videomode.c new file mode 100644 index 000000000000..5b8066cd397f --- /dev/null +++ b/drivers/video/of_videomode.c @@ -0,0 +1,54 @@ +/* + * generic videomode helper + * + * Copyright (c) 2012 Steffen Trumtrar <s.trumtrar@pengutronix.de>, Pengutronix + * + * This file is released under the GPLv2 + */ +#include <linux/errno.h> +#include <linux/export.h> +#include <linux/of.h> +#include <video/display_timing.h> +#include <video/of_display_timing.h> +#include <video/of_videomode.h> +#include <video/videomode.h> + +/** + * of_get_videomode - get the videomode #<index> from devicetree + * @np - devicenode with the display_timings + * @vm - set to return value + * @index - index into list of display_timings + * (Set this to OF_USE_NATIVE_MODE to use whatever mode is + * specified as native mode in the DT.) + * + * DESCRIPTION: + * Get a list of all display timings and put the one + * specified by index into *vm. This function should only be used, if + * only one videomode is to be retrieved. A driver that needs to work + * with multiple/all videomodes should work with + * of_get_display_timings instead. + **/ +int of_get_videomode(struct device_node *np, struct videomode *vm, + int index) +{ + struct display_timings *disp; + int ret; + + disp = of_get_display_timings(np); + if (!disp) { + pr_err("%s: no timings specified\n", of_node_full_name(np)); + return -EINVAL; + } + + if (index == OF_USE_NATIVE_MODE) + index = disp->native_mode; + + ret = videomode_from_timing(disp, vm, index); + if (ret) + return ret; + + display_timings_release(disp); + + return 0; +} +EXPORT_SYMBOL_GPL(of_get_videomode); diff --git a/drivers/video/ssd1307fb.c b/drivers/video/ssd1307fb.c index 4d99dd7a6831..395cb6a8d8f3 100644 --- a/drivers/video/ssd1307fb.c +++ b/drivers/video/ssd1307fb.c @@ -145,8 +145,8 @@ static void ssd1307fb_update_display(struct ssd1307fb_par *par) u32 page_length = SSD1307FB_WIDTH * i; u32 index = page_length + (SSD1307FB_WIDTH * k + j) / 8; u8 byte = *(vmem + index); - u8 bit = byte & (1 << (7 - (j % 8))); - bit = bit >> (7 - (j % 8)); + u8 bit = byte & (1 << (j % 8)); + bit = bit >> (j % 8); buf |= bit << k; } ssd1307fb_write_data(par->client, buf); diff --git a/drivers/video/via/hw.c b/drivers/video/via/hw.c index 80233dae358a..22450908306c 100644 --- a/drivers/video/via/hw.c +++ b/drivers/video/via/hw.c @@ -1467,10 +1467,10 @@ void viafb_set_vclock(u32 clk, int set_iga) via_write_misc_reg_mask(0x0C, 0x0C); /* select external clock */ } -struct display_timing var_to_timing(const struct fb_var_screeninfo *var, +struct via_display_timing var_to_timing(const struct fb_var_screeninfo *var, u16 cxres, u16 cyres) { - struct display_timing timing; + struct via_display_timing timing; u16 dx = (var->xres - cxres) / 2, dy = (var->yres - cyres) / 2; timing.hor_addr = cxres; @@ -1491,7 +1491,7 @@ struct display_timing var_to_timing(const struct fb_var_screeninfo *var, void viafb_fill_crtc_timing(const struct fb_var_screeninfo *var, u16 cxres, u16 cyres, int iga) { - struct display_timing crt_reg = var_to_timing(var, + struct via_display_timing crt_reg = var_to_timing(var, cxres ? cxres : var->xres, cyres ? cyres : var->yres); if (iga == IGA1) diff --git a/drivers/video/via/hw.h b/drivers/video/via/hw.h index a8205754c736..3be073c58b03 100644 --- a/drivers/video/via/hw.h +++ b/drivers/video/via/hw.h @@ -637,7 +637,7 @@ extern int viafb_LCD_ON; extern int viafb_DVI_ON; extern int viafb_hotplug; -struct display_timing var_to_timing(const struct fb_var_screeninfo *var, +struct via_display_timing var_to_timing(const struct fb_var_screeninfo *var, u16 cxres, u16 cyres); void viafb_fill_crtc_timing(const struct fb_var_screeninfo *var, u16 cxres, u16 cyres, int iga); diff --git a/drivers/video/via/lcd.c b/drivers/video/via/lcd.c index 980ee1b1dcf3..5d21ff436ec8 100644 --- a/drivers/video/via/lcd.c +++ b/drivers/video/via/lcd.c @@ -549,7 +549,7 @@ void viafb_lcd_set_mode(const struct fb_var_screeninfo *var, u16 cxres, int panel_hres = plvds_setting_info->lcd_panel_hres; int panel_vres = plvds_setting_info->lcd_panel_vres; u32 clock; - struct display_timing timing; + struct via_display_timing timing; struct fb_var_screeninfo panel_var; const struct fb_videomode *mode_crt_table, *panel_crt_table; diff --git a/drivers/video/via/share.h b/drivers/video/via/share.h index 3158dfc90bed..65c65c611e0a 100644 --- a/drivers/video/via/share.h +++ b/drivers/video/via/share.h @@ -319,7 +319,7 @@ struct crt_mode_table { int refresh_rate; int h_sync_polarity; int v_sync_polarity; - struct display_timing crtc; + struct via_display_timing crtc; }; struct io_reg { diff --git a/drivers/video/via/via_modesetting.c b/drivers/video/via/via_modesetting.c index 0e431aee17bb..0b414b09b9b4 100644 --- a/drivers/video/via/via_modesetting.c +++ b/drivers/video/via/via_modesetting.c @@ -30,9 +30,9 @@ #include "debug.h" -void via_set_primary_timing(const struct display_timing *timing) +void via_set_primary_timing(const struct via_display_timing *timing) { - struct display_timing raw; + struct via_display_timing raw; raw.hor_total = timing->hor_total / 8 - 5; raw.hor_addr = timing->hor_addr / 8 - 1; @@ -88,9 +88,9 @@ void via_set_primary_timing(const struct display_timing *timing) via_write_reg_mask(VIACR, 0x17, 0x80, 0x80); } -void via_set_secondary_timing(const struct display_timing *timing) +void via_set_secondary_timing(const struct via_display_timing *timing) { - struct display_timing raw; + struct via_display_timing raw; raw.hor_total = timing->hor_total - 1; raw.hor_addr = timing->hor_addr - 1; diff --git a/drivers/video/via/via_modesetting.h b/drivers/video/via/via_modesetting.h index 06e09fe351ae..f6a6503da3b3 100644 --- a/drivers/video/via/via_modesetting.h +++ b/drivers/video/via/via_modesetting.h @@ -33,7 +33,7 @@ #define VIA_PITCH_MAX 0x3FF8 -struct display_timing { +struct via_display_timing { u16 hor_total; u16 hor_addr; u16 hor_blank_start; @@ -49,8 +49,8 @@ struct display_timing { }; -void via_set_primary_timing(const struct display_timing *timing); -void via_set_secondary_timing(const struct display_timing *timing); +void via_set_primary_timing(const struct via_display_timing *timing); +void via_set_secondary_timing(const struct via_display_timing *timing); void via_set_primary_address(u32 addr); void via_set_secondary_address(u32 addr); void via_set_primary_pitch(u32 pitch); diff --git a/drivers/video/videomode.c b/drivers/video/videomode.c new file mode 100644 index 000000000000..21c47a202afa --- /dev/null +++ b/drivers/video/videomode.c @@ -0,0 +1,39 @@ +/* + * generic display timing functions + * + * Copyright (c) 2012 Steffen Trumtrar <s.trumtrar@pengutronix.de>, Pengutronix + * + * This file is released under the GPLv2 + */ + +#include <linux/errno.h> +#include <linux/export.h> +#include <video/display_timing.h> +#include <video/videomode.h> + +int videomode_from_timing(const struct display_timings *disp, + struct videomode *vm, unsigned int index) +{ + struct display_timing *dt; + + dt = display_timings_get(disp, index); + if (!dt) + return -EINVAL; + + vm->pixelclock = display_timing_get_value(&dt->pixelclock, TE_TYP); + vm->hactive = display_timing_get_value(&dt->hactive, TE_TYP); + vm->hfront_porch = display_timing_get_value(&dt->hfront_porch, TE_TYP); + vm->hback_porch = display_timing_get_value(&dt->hback_porch, TE_TYP); + vm->hsync_len = display_timing_get_value(&dt->hsync_len, TE_TYP); + + vm->vactive = display_timing_get_value(&dt->vactive, TE_TYP); + vm->vfront_porch = display_timing_get_value(&dt->vfront_porch, TE_TYP); + vm->vback_porch = display_timing_get_value(&dt->vback_porch, TE_TYP); + vm->vsync_len = display_timing_get_value(&dt->vsync_len, TE_TYP); + + vm->dmt_flags = dt->dmt_flags; + vm->data_flags = dt->data_flags; + + return 0; +} +EXPORT_SYMBOL_GPL(videomode_from_timing); diff --git a/drivers/xen/cpu_hotplug.c b/drivers/xen/cpu_hotplug.c index 4dcfced107f5..084041d42c9a 100644 --- a/drivers/xen/cpu_hotplug.c +++ b/drivers/xen/cpu_hotplug.c @@ -25,10 +25,10 @@ static void disable_hotplug_cpu(int cpu) static int vcpu_online(unsigned int cpu) { int err; - char dir[32], state[32]; + char dir[16], state[16]; sprintf(dir, "cpu/%u", cpu); - err = xenbus_scanf(XBT_NIL, dir, "availability", "%s", state); + err = xenbus_scanf(XBT_NIL, dir, "availability", "%15s", state); if (err != 1) { if (!xen_initial_domain()) printk(KERN_ERR "XENBUS: Unable to read cpu state\n"); diff --git a/drivers/xen/gntdev.c b/drivers/xen/gntdev.c index 2e22df2f7a3f..3c8803feba26 100644 --- a/drivers/xen/gntdev.c +++ b/drivers/xen/gntdev.c @@ -56,10 +56,15 @@ MODULE_PARM_DESC(limit, "Maximum number of grants that may be mapped by " static atomic_t pages_mapped = ATOMIC_INIT(0); static int use_ptemod; +#define populate_freeable_maps use_ptemod struct gntdev_priv { + /* maps with visible offsets in the file descriptor */ struct list_head maps; - /* lock protects maps from concurrent changes */ + /* maps that are not visible; will be freed on munmap. + * Only populated if populate_freeable_maps == 1 */ + struct list_head freeable_maps; + /* lock protects maps and freeable_maps */ spinlock_t lock; struct mm_struct *mm; struct mmu_notifier mn; @@ -193,7 +198,7 @@ static struct grant_map *gntdev_find_map_index(struct gntdev_priv *priv, return NULL; } -static void gntdev_put_map(struct grant_map *map) +static void gntdev_put_map(struct gntdev_priv *priv, struct grant_map *map) { if (!map) return; @@ -208,6 +213,12 @@ static void gntdev_put_map(struct grant_map *map) evtchn_put(map->notify.event); } + if (populate_freeable_maps && priv) { + spin_lock(&priv->lock); + list_del(&map->next); + spin_unlock(&priv->lock); + } + if (map->pages && !use_ptemod) unmap_grant_pages(map, 0, map->count); gntdev_free_map(map); @@ -301,17 +312,10 @@ static int __unmap_grant_pages(struct grant_map *map, int offset, int pages) if (map->notify.flags & UNMAP_NOTIFY_CLEAR_BYTE) { int pgno = (map->notify.addr >> PAGE_SHIFT); - if (pgno >= offset && pgno < offset + pages && use_ptemod) { - void __user *tmp = (void __user *) - map->vma->vm_start + map->notify.addr; - err = copy_to_user(tmp, &err, 1); - if (err) - return -EFAULT; - map->notify.flags &= ~UNMAP_NOTIFY_CLEAR_BYTE; - } else if (pgno >= offset && pgno < offset + pages) { - uint8_t *tmp = kmap(map->pages[pgno]); + if (pgno >= offset && pgno < offset + pages) { + /* No need for kmap, pages are in lowmem */ + uint8_t *tmp = pfn_to_kaddr(page_to_pfn(map->pages[pgno])); tmp[map->notify.addr & (PAGE_SIZE-1)] = 0; - kunmap(map->pages[pgno]); map->notify.flags &= ~UNMAP_NOTIFY_CLEAR_BYTE; } } @@ -376,11 +380,24 @@ static void gntdev_vma_open(struct vm_area_struct *vma) static void gntdev_vma_close(struct vm_area_struct *vma) { struct grant_map *map = vma->vm_private_data; + struct file *file = vma->vm_file; + struct gntdev_priv *priv = file->private_data; pr_debug("gntdev_vma_close %p\n", vma); - map->vma = NULL; + if (use_ptemod) { + /* It is possible that an mmu notifier could be running + * concurrently, so take priv->lock to ensure that the vma won't + * vanishing during the unmap_grant_pages call, since we will + * spin here until that completes. Such a concurrent call will + * not do any unmapping, since that has been done prior to + * closing the vma, but it may still iterate the unmap_ops list. + */ + spin_lock(&priv->lock); + map->vma = NULL; + spin_unlock(&priv->lock); + } vma->vm_private_data = NULL; - gntdev_put_map(map); + gntdev_put_map(priv, map); } static struct vm_operations_struct gntdev_vmops = { @@ -390,33 +407,43 @@ static struct vm_operations_struct gntdev_vmops = { /* ------------------------------------------------------------------ */ +static void unmap_if_in_range(struct grant_map *map, + unsigned long start, unsigned long end) +{ + unsigned long mstart, mend; + int err; + + if (!map->vma) + return; + if (map->vma->vm_start >= end) + return; + if (map->vma->vm_end <= start) + return; + mstart = max(start, map->vma->vm_start); + mend = min(end, map->vma->vm_end); + pr_debug("map %d+%d (%lx %lx), range %lx %lx, mrange %lx %lx\n", + map->index, map->count, + map->vma->vm_start, map->vma->vm_end, + start, end, mstart, mend); + err = unmap_grant_pages(map, + (mstart - map->vma->vm_start) >> PAGE_SHIFT, + (mend - mstart) >> PAGE_SHIFT); + WARN_ON(err); +} + static void mn_invl_range_start(struct mmu_notifier *mn, struct mm_struct *mm, unsigned long start, unsigned long end) { struct gntdev_priv *priv = container_of(mn, struct gntdev_priv, mn); struct grant_map *map; - unsigned long mstart, mend; - int err; spin_lock(&priv->lock); list_for_each_entry(map, &priv->maps, next) { - if (!map->vma) - continue; - if (map->vma->vm_start >= end) - continue; - if (map->vma->vm_end <= start) - continue; - mstart = max(start, map->vma->vm_start); - mend = min(end, map->vma->vm_end); - pr_debug("map %d+%d (%lx %lx), range %lx %lx, mrange %lx %lx\n", - map->index, map->count, - map->vma->vm_start, map->vma->vm_end, - start, end, mstart, mend); - err = unmap_grant_pages(map, - (mstart - map->vma->vm_start) >> PAGE_SHIFT, - (mend - mstart) >> PAGE_SHIFT); - WARN_ON(err); + unmap_if_in_range(map, start, end); + } + list_for_each_entry(map, &priv->freeable_maps, next) { + unmap_if_in_range(map, start, end); } spin_unlock(&priv->lock); } @@ -445,6 +472,15 @@ static void mn_release(struct mmu_notifier *mn, err = unmap_grant_pages(map, /* offset */ 0, map->count); WARN_ON(err); } + list_for_each_entry(map, &priv->freeable_maps, next) { + if (!map->vma) + continue; + pr_debug("map %d+%d (%lx %lx)\n", + map->index, map->count, + map->vma->vm_start, map->vma->vm_end); + err = unmap_grant_pages(map, /* offset */ 0, map->count); + WARN_ON(err); + } spin_unlock(&priv->lock); } @@ -466,6 +502,7 @@ static int gntdev_open(struct inode *inode, struct file *flip) return -ENOMEM; INIT_LIST_HEAD(&priv->maps); + INIT_LIST_HEAD(&priv->freeable_maps); spin_lock_init(&priv->lock); if (use_ptemod) { @@ -500,8 +537,9 @@ static int gntdev_release(struct inode *inode, struct file *flip) while (!list_empty(&priv->maps)) { map = list_entry(priv->maps.next, struct grant_map, next); list_del(&map->next); - gntdev_put_map(map); + gntdev_put_map(NULL /* already removed */, map); } + WARN_ON(!list_empty(&priv->freeable_maps)); if (use_ptemod) mmu_notifier_unregister(&priv->mn, priv->mm); @@ -529,14 +567,14 @@ static long gntdev_ioctl_map_grant_ref(struct gntdev_priv *priv, if (unlikely(atomic_add_return(op.count, &pages_mapped) > limit)) { pr_debug("can't map: over limit\n"); - gntdev_put_map(map); + gntdev_put_map(NULL, map); return err; } if (copy_from_user(map->grants, &u->refs, sizeof(map->grants[0]) * op.count) != 0) { - gntdev_put_map(map); - return err; + gntdev_put_map(NULL, map); + return -EFAULT; } spin_lock(&priv->lock); @@ -565,11 +603,13 @@ static long gntdev_ioctl_unmap_grant_ref(struct gntdev_priv *priv, map = gntdev_find_map_index(priv, op.index >> PAGE_SHIFT, op.count); if (map) { list_del(&map->next); + if (populate_freeable_maps) + list_add_tail(&map->next, &priv->freeable_maps); err = 0; } spin_unlock(&priv->lock); if (map) - gntdev_put_map(map); + gntdev_put_map(priv, map); return err; } @@ -579,25 +619,31 @@ static long gntdev_ioctl_get_offset_for_vaddr(struct gntdev_priv *priv, struct ioctl_gntdev_get_offset_for_vaddr op; struct vm_area_struct *vma; struct grant_map *map; + int rv = -EINVAL; if (copy_from_user(&op, u, sizeof(op)) != 0) return -EFAULT; pr_debug("priv %p, offset for vaddr %lx\n", priv, (unsigned long)op.vaddr); + down_read(¤t->mm->mmap_sem); vma = find_vma(current->mm, op.vaddr); if (!vma || vma->vm_ops != &gntdev_vmops) - return -EINVAL; + goto out_unlock; map = vma->vm_private_data; if (!map) - return -EINVAL; + goto out_unlock; op.offset = map->index << PAGE_SHIFT; op.count = map->count; + rv = 0; - if (copy_to_user(u, &op, sizeof(op)) != 0) + out_unlock: + up_read(¤t->mm->mmap_sem); + + if (rv == 0 && copy_to_user(u, &op, sizeof(op)) != 0) return -EFAULT; - return 0; + return rv; } static long gntdev_ioctl_notify(struct gntdev_priv *priv, void __user *u) @@ -778,7 +824,7 @@ out_unlock_put: out_put_map: if (use_ptemod) map->vma = NULL; - gntdev_put_map(map); + gntdev_put_map(priv, map); return err; } diff --git a/drivers/xen/grant-table.c b/drivers/xen/grant-table.c index 7038de53652b..157c0ccda3ef 100644 --- a/drivers/xen/grant-table.c +++ b/drivers/xen/grant-table.c @@ -56,10 +56,6 @@ /* External tools reserve first few grant table entries. */ #define NR_RESERVED_ENTRIES 8 #define GNTTAB_LIST_END 0xffffffff -#define GREFS_PER_GRANT_FRAME \ -(grant_table_version == 1 ? \ -(PAGE_SIZE / sizeof(struct grant_entry_v1)) : \ -(PAGE_SIZE / sizeof(union grant_entry_v2))) static grant_ref_t **gnttab_list; static unsigned int nr_grant_frames; @@ -154,6 +150,7 @@ static struct gnttab_ops *gnttab_interface; static grant_status_t *grstatus; static int grant_table_version; +static int grefs_per_grant_frame; static struct gnttab_free_callback *gnttab_free_callback_list; @@ -767,12 +764,14 @@ static int grow_gnttab_list(unsigned int more_frames) unsigned int new_nr_grant_frames, extra_entries, i; unsigned int nr_glist_frames, new_nr_glist_frames; + BUG_ON(grefs_per_grant_frame == 0); + new_nr_grant_frames = nr_grant_frames + more_frames; - extra_entries = more_frames * GREFS_PER_GRANT_FRAME; + extra_entries = more_frames * grefs_per_grant_frame; - nr_glist_frames = (nr_grant_frames * GREFS_PER_GRANT_FRAME + RPP - 1) / RPP; + nr_glist_frames = (nr_grant_frames * grefs_per_grant_frame + RPP - 1) / RPP; new_nr_glist_frames = - (new_nr_grant_frames * GREFS_PER_GRANT_FRAME + RPP - 1) / RPP; + (new_nr_grant_frames * grefs_per_grant_frame + RPP - 1) / RPP; for (i = nr_glist_frames; i < new_nr_glist_frames; i++) { gnttab_list[i] = (grant_ref_t *)__get_free_page(GFP_ATOMIC); if (!gnttab_list[i]) @@ -780,12 +779,12 @@ static int grow_gnttab_list(unsigned int more_frames) } - for (i = GREFS_PER_GRANT_FRAME * nr_grant_frames; - i < GREFS_PER_GRANT_FRAME * new_nr_grant_frames - 1; i++) + for (i = grefs_per_grant_frame * nr_grant_frames; + i < grefs_per_grant_frame * new_nr_grant_frames - 1; i++) gnttab_entry(i) = i + 1; gnttab_entry(i) = gnttab_free_head; - gnttab_free_head = GREFS_PER_GRANT_FRAME * nr_grant_frames; + gnttab_free_head = grefs_per_grant_frame * nr_grant_frames; gnttab_free_count += extra_entries; nr_grant_frames = new_nr_grant_frames; @@ -957,7 +956,8 @@ EXPORT_SYMBOL_GPL(gnttab_unmap_refs); static unsigned nr_status_frames(unsigned nr_grant_frames) { - return (nr_grant_frames * GREFS_PER_GRANT_FRAME + SPP - 1) / SPP; + BUG_ON(grefs_per_grant_frame == 0); + return (nr_grant_frames * grefs_per_grant_frame + SPP - 1) / SPP; } static int gnttab_map_frames_v1(xen_pfn_t *frames, unsigned int nr_gframes) @@ -1115,6 +1115,7 @@ static void gnttab_request_version(void) rc = HYPERVISOR_grant_table_op(GNTTABOP_set_version, &gsv, 1); if (rc == 0 && gsv.version == 2) { grant_table_version = 2; + grefs_per_grant_frame = PAGE_SIZE / sizeof(union grant_entry_v2); gnttab_interface = &gnttab_v2_ops; } else if (grant_table_version == 2) { /* @@ -1127,17 +1128,17 @@ static void gnttab_request_version(void) panic("we need grant tables version 2, but only version 1 is available"); } else { grant_table_version = 1; + grefs_per_grant_frame = PAGE_SIZE / sizeof(struct grant_entry_v1); gnttab_interface = &gnttab_v1_ops; } printk(KERN_INFO "Grant tables using version %d layout.\n", grant_table_version); } -int gnttab_resume(void) +static int gnttab_setup(void) { unsigned int max_nr_gframes; - gnttab_request_version(); max_nr_gframes = gnttab_max_grant_frames(); if (max_nr_gframes < nr_grant_frames) return -ENOSYS; @@ -1160,6 +1161,12 @@ int gnttab_resume(void) return 0; } +int gnttab_resume(void) +{ + gnttab_request_version(); + return gnttab_setup(); +} + int gnttab_suspend(void) { gnttab_interface->unmap_frames(); @@ -1171,9 +1178,10 @@ static int gnttab_expand(unsigned int req_entries) int rc; unsigned int cur, extra; + BUG_ON(grefs_per_grant_frame == 0); cur = nr_grant_frames; - extra = ((req_entries + (GREFS_PER_GRANT_FRAME-1)) / - GREFS_PER_GRANT_FRAME); + extra = ((req_entries + (grefs_per_grant_frame-1)) / + grefs_per_grant_frame); if (cur + extra > gnttab_max_grant_frames()) return -ENOSPC; @@ -1191,21 +1199,23 @@ int gnttab_init(void) unsigned int nr_init_grefs; int ret; + gnttab_request_version(); nr_grant_frames = 1; boot_max_nr_grant_frames = __max_nr_grant_frames(); /* Determine the maximum number of frames required for the * grant reference free list on the current hypervisor. */ + BUG_ON(grefs_per_grant_frame == 0); max_nr_glist_frames = (boot_max_nr_grant_frames * - GREFS_PER_GRANT_FRAME / RPP); + grefs_per_grant_frame / RPP); gnttab_list = kmalloc(max_nr_glist_frames * sizeof(grant_ref_t *), GFP_KERNEL); if (gnttab_list == NULL) return -ENOMEM; - nr_glist_frames = (nr_grant_frames * GREFS_PER_GRANT_FRAME + RPP - 1) / RPP; + nr_glist_frames = (nr_grant_frames * grefs_per_grant_frame + RPP - 1) / RPP; for (i = 0; i < nr_glist_frames; i++) { gnttab_list[i] = (grant_ref_t *)__get_free_page(GFP_KERNEL); if (gnttab_list[i] == NULL) { @@ -1214,12 +1224,12 @@ int gnttab_init(void) } } - if (gnttab_resume() < 0) { + if (gnttab_setup() < 0) { ret = -ENODEV; goto ini_nomem; } - nr_init_grefs = nr_grant_frames * GREFS_PER_GRANT_FRAME; + nr_init_grefs = nr_grant_frames * grefs_per_grant_frame; for (i = NR_RESERVED_ENTRIES; i < nr_init_grefs - 1; i++) gnttab_entry(i) = i + 1; diff --git a/drivers/xen/privcmd.c b/drivers/xen/privcmd.c index 0bbbccbb1f12..ca2b00e9d558 100644 --- a/drivers/xen/privcmd.c +++ b/drivers/xen/privcmd.c @@ -199,9 +199,6 @@ static long privcmd_ioctl_mmap(void __user *udata) LIST_HEAD(pagelist); struct mmap_mfn_state state; - if (!xen_initial_domain()) - return -EPERM; - /* We only support privcmd_ioctl_mmap_batch for auto translated. */ if (xen_feature(XENFEAT_auto_translated_physmap)) return -ENOSYS; @@ -261,11 +258,12 @@ struct mmap_batch_state { * -ENOENT if at least 1 -ENOENT has happened. */ int global_error; - /* An array for individual errors */ - int *err; + int version; /* User-space mfn array to store errors in the second pass for V1. */ xen_pfn_t __user *user_mfn; + /* User-space int array to store errors in the second pass for V2. */ + int __user *user_err; }; /* auto translated dom0 note: if domU being created is PV, then mfn is @@ -288,7 +286,19 @@ static int mmap_batch_fn(void *data, void *state) &cur_page); /* Store error code for second pass. */ - *(st->err++) = ret; + if (st->version == 1) { + if (ret < 0) { + /* + * V1 encodes the error codes in the 32bit top nibble of the + * mfn (with its known limitations vis-a-vis 64 bit callers). + */ + *mfnp |= (ret == -ENOENT) ? + PRIVCMD_MMAPBATCH_PAGED_ERROR : + PRIVCMD_MMAPBATCH_MFN_ERROR; + } + } else { /* st->version == 2 */ + *((int *) mfnp) = ret; + } /* And see if it affects the global_error. */ if (ret < 0) { @@ -305,20 +315,25 @@ static int mmap_batch_fn(void *data, void *state) return 0; } -static int mmap_return_errors_v1(void *data, void *state) +static int mmap_return_errors(void *data, void *state) { - xen_pfn_t *mfnp = data; struct mmap_batch_state *st = state; - int err = *(st->err++); - /* - * V1 encodes the error codes in the 32bit top nibble of the - * mfn (with its known limitations vis-a-vis 64 bit callers). - */ - *mfnp |= (err == -ENOENT) ? - PRIVCMD_MMAPBATCH_PAGED_ERROR : - PRIVCMD_MMAPBATCH_MFN_ERROR; - return __put_user(*mfnp, st->user_mfn++); + if (st->version == 1) { + xen_pfn_t mfnp = *((xen_pfn_t *) data); + if (mfnp & PRIVCMD_MMAPBATCH_MFN_ERROR) + return __put_user(mfnp, st->user_mfn++); + else + st->user_mfn++; + } else { /* st->version == 2 */ + int err = *((int *) data); + if (err) + return __put_user(err, st->user_err++); + else + st->user_err++; + } + + return 0; } /* Allocate pfns that are then mapped with gmfns from foreign domid. Update @@ -357,12 +372,8 @@ static long privcmd_ioctl_mmap_batch(void __user *udata, int version) struct vm_area_struct *vma; unsigned long nr_pages; LIST_HEAD(pagelist); - int *err_array = NULL; struct mmap_batch_state state; - if (!xen_initial_domain()) - return -EPERM; - switch (version) { case 1: if (copy_from_user(&m, udata, sizeof(struct privcmd_mmapbatch))) @@ -396,10 +407,12 @@ static long privcmd_ioctl_mmap_batch(void __user *udata, int version) goto out; } - err_array = kcalloc(m.num, sizeof(int), GFP_KERNEL); - if (err_array == NULL) { - ret = -ENOMEM; - goto out; + if (version == 2) { + /* Zero error array now to only copy back actual errors. */ + if (clear_user(m.err, sizeof(int) * m.num)) { + ret = -EFAULT; + goto out; + } } down_write(&mm->mmap_sem); @@ -427,7 +440,7 @@ static long privcmd_ioctl_mmap_batch(void __user *udata, int version) state.va = m.addr; state.index = 0; state.global_error = 0; - state.err = err_array; + state.version = version; /* mmap_batch_fn guarantees ret == 0 */ BUG_ON(traverse_pages(m.num, sizeof(xen_pfn_t), @@ -435,21 +448,14 @@ static long privcmd_ioctl_mmap_batch(void __user *udata, int version) up_write(&mm->mmap_sem); - if (version == 1) { - if (state.global_error) { - /* Write back errors in second pass. */ - state.user_mfn = (xen_pfn_t *)m.arr; - state.err = err_array; - ret = traverse_pages(m.num, sizeof(xen_pfn_t), - &pagelist, mmap_return_errors_v1, &state); - } else - ret = 0; - - } else if (version == 2) { - ret = __copy_to_user(m.err, err_array, m.num * sizeof(int)); - if (ret) - ret = -EFAULT; - } + if (state.global_error) { + /* Write back errors in second pass. */ + state.user_mfn = (xen_pfn_t *)m.arr; + state.user_err = m.err; + ret = traverse_pages(m.num, sizeof(xen_pfn_t), + &pagelist, mmap_return_errors, &state); + } else + ret = 0; /* If we have not had any EFAULT-like global errors then set the global * error to -ENOENT if necessary. */ @@ -457,7 +463,6 @@ static long privcmd_ioctl_mmap_batch(void __user *udata, int version) ret = -ENOENT; out: - kfree(err_array); free_page_list(&pagelist); return ret; diff --git a/drivers/xen/xen-pciback/pciback.h b/drivers/xen/xen-pciback/pciback.h index a7def010eba3..f72af87640e0 100644 --- a/drivers/xen/xen-pciback/pciback.h +++ b/drivers/xen/xen-pciback/pciback.h @@ -124,7 +124,7 @@ static inline int xen_pcibk_add_pci_dev(struct xen_pcibk_device *pdev, static inline void xen_pcibk_release_pci_dev(struct xen_pcibk_device *pdev, struct pci_dev *dev) { - if (xen_pcibk_backend && xen_pcibk_backend->free) + if (xen_pcibk_backend && xen_pcibk_backend->release) return xen_pcibk_backend->release(pdev, dev); } |