diff options
Diffstat (limited to 'drivers')
91 files changed, 7023 insertions, 5917 deletions
diff --git a/drivers/char/agp/Makefile b/drivers/char/agp/Makefile index 627f542827c..8eb56e273e7 100644 --- a/drivers/char/agp/Makefile +++ b/drivers/char/agp/Makefile @@ -13,6 +13,7 @@ obj-$(CONFIG_AGP_HP_ZX1) += hp-agp.o obj-$(CONFIG_AGP_PARISC) += parisc-agp.o obj-$(CONFIG_AGP_I460) += i460-agp.o obj-$(CONFIG_AGP_INTEL) += intel-agp.o +obj-$(CONFIG_AGP_INTEL) += intel-gtt.o obj-$(CONFIG_AGP_NVIDIA) += nvidia-agp.o obj-$(CONFIG_AGP_SGI_TIOCA) += sgi-agp.o obj-$(CONFIG_AGP_SIS) += sis-agp.o diff --git a/drivers/char/agp/agp.h b/drivers/char/agp/agp.h index 12049094999..5259065f3c7 100644 --- a/drivers/char/agp/agp.h +++ b/drivers/char/agp/agp.h @@ -121,11 +121,6 @@ struct agp_bridge_driver { void (*agp_destroy_pages)(struct agp_memory *); int (*agp_type_to_mask_type) (struct agp_bridge_data *, int); void (*chipset_flush)(struct agp_bridge_data *); - - int (*agp_map_page)(struct page *page, dma_addr_t *ret); - void (*agp_unmap_page)(struct page *page, dma_addr_t dma); - int (*agp_map_memory)(struct agp_memory *mem); - void (*agp_unmap_memory)(struct agp_memory *mem); }; struct agp_bridge_data { diff --git a/drivers/char/agp/backend.c b/drivers/char/agp/backend.c index ee4f855611b..f27d0d0816d 100644 --- a/drivers/char/agp/backend.c +++ b/drivers/char/agp/backend.c @@ -151,17 +151,7 @@ static int agp_backend_initialize(struct agp_bridge_data *bridge) } bridge->scratch_page_page = page; - if (bridge->driver->agp_map_page) { - if (bridge->driver->agp_map_page(page, - &bridge->scratch_page_dma)) { - dev_err(&bridge->dev->dev, - "unable to dma-map scratch page\n"); - rc = -ENOMEM; - goto err_out_nounmap; - } - } else { - bridge->scratch_page_dma = page_to_phys(page); - } + bridge->scratch_page_dma = page_to_phys(page); bridge->scratch_page = bridge->driver->mask_memory(bridge, bridge->scratch_page_dma, 0); @@ -204,12 +194,6 @@ static int agp_backend_initialize(struct agp_bridge_data *bridge) return 0; err_out: - if (bridge->driver->needs_scratch_page && - bridge->driver->agp_unmap_page) { - bridge->driver->agp_unmap_page(bridge->scratch_page_page, - bridge->scratch_page_dma); - } -err_out_nounmap: if (bridge->driver->needs_scratch_page) { void *va = page_address(bridge->scratch_page_page); @@ -240,10 +224,6 @@ static void agp_backend_cleanup(struct agp_bridge_data *bridge) bridge->driver->needs_scratch_page) { void *va = page_address(bridge->scratch_page_page); - if (bridge->driver->agp_unmap_page) - bridge->driver->agp_unmap_page(bridge->scratch_page_page, - bridge->scratch_page_dma); - bridge->driver->agp_destroy_page(va, AGP_PAGE_DESTROY_UNMAP); bridge->driver->agp_destroy_page(va, AGP_PAGE_DESTROY_FREE); } diff --git a/drivers/char/agp/generic.c b/drivers/char/agp/generic.c index d2abf514398..78235ceccfa 100644 --- a/drivers/char/agp/generic.c +++ b/drivers/char/agp/generic.c @@ -437,11 +437,6 @@ int agp_bind_memory(struct agp_memory *curr, off_t pg_start) curr->is_flushed = true; } - if (curr->bridge->driver->agp_map_memory) { - ret_val = curr->bridge->driver->agp_map_memory(curr); - if (ret_val) - return ret_val; - } ret_val = curr->bridge->driver->insert_memory(curr, pg_start, curr->type); if (ret_val != 0) @@ -483,9 +478,6 @@ int agp_unbind_memory(struct agp_memory *curr) if (ret_val != 0) return ret_val; - if (curr->bridge->driver->agp_unmap_memory) - curr->bridge->driver->agp_unmap_memory(curr); - curr->is_bound = false; curr->pg_start = 0; spin_lock(&curr->bridge->mapped_lock); diff --git a/drivers/char/agp/intel-agp.c b/drivers/char/agp/intel-agp.c index cd18493c952..5cd2221ab47 100644 --- a/drivers/char/agp/intel-agp.c +++ b/drivers/char/agp/intel-agp.c @@ -12,9 +12,6 @@ #include <asm/smp.h> #include "agp.h" #include "intel-agp.h" -#include <linux/intel-gtt.h> - -#include "intel-gtt.c" int intel_agp_enabled; EXPORT_SYMBOL(intel_agp_enabled); @@ -703,179 +700,37 @@ static const struct agp_bridge_driver intel_7505_driver = { .agp_type_to_mask_type = agp_generic_type_to_mask_type, }; -static int find_gmch(u16 device) -{ - struct pci_dev *gmch_device; - - gmch_device = pci_get_device(PCI_VENDOR_ID_INTEL, device, NULL); - if (gmch_device && PCI_FUNC(gmch_device->devfn) != 0) { - gmch_device = pci_get_device(PCI_VENDOR_ID_INTEL, - device, gmch_device); - } - - if (!gmch_device) - return 0; - - intel_private.pcidev = gmch_device; - return 1; -} - /* Table to describe Intel GMCH and AGP/PCIE GART drivers. At least one of * driver and gmch_driver must be non-null, and find_gmch will determine * which one should be used if a gmch_chip_id is present. */ -static const struct intel_driver_description { +static const struct intel_agp_driver_description { unsigned int chip_id; - unsigned int gmch_chip_id; char *name; const struct agp_bridge_driver *driver; - const struct agp_bridge_driver *gmch_driver; } intel_agp_chipsets[] = { - { PCI_DEVICE_ID_INTEL_82443LX_0, 0, "440LX", &intel_generic_driver, NULL }, - { PCI_DEVICE_ID_INTEL_82443BX_0, 0, "440BX", &intel_generic_driver, NULL }, - { PCI_DEVICE_ID_INTEL_82443GX_0, 0, "440GX", &intel_generic_driver, NULL }, - { PCI_DEVICE_ID_INTEL_82810_MC1, PCI_DEVICE_ID_INTEL_82810_IG1, "i810", - NULL, &intel_810_driver }, - { PCI_DEVICE_ID_INTEL_82810_MC3, PCI_DEVICE_ID_INTEL_82810_IG3, "i810", - NULL, &intel_810_driver }, - { PCI_DEVICE_ID_INTEL_82810E_MC, PCI_DEVICE_ID_INTEL_82810E_IG, "i810", - NULL, &intel_810_driver }, - { PCI_DEVICE_ID_INTEL_82815_MC, PCI_DEVICE_ID_INTEL_82815_CGC, "i815", - &intel_815_driver, &intel_810_driver }, - { PCI_DEVICE_ID_INTEL_82820_HB, 0, "i820", &intel_820_driver, NULL }, - { PCI_DEVICE_ID_INTEL_82820_UP_HB, 0, "i820", &intel_820_driver, NULL }, - { PCI_DEVICE_ID_INTEL_82830_HB, PCI_DEVICE_ID_INTEL_82830_CGC, "830M", - &intel_830mp_driver, &intel_830_driver }, - { PCI_DEVICE_ID_INTEL_82840_HB, 0, "i840", &intel_840_driver, NULL }, - { PCI_DEVICE_ID_INTEL_82845_HB, 0, "845G", &intel_845_driver, NULL }, - { PCI_DEVICE_ID_INTEL_82845G_HB, PCI_DEVICE_ID_INTEL_82845G_IG, "830M", - &intel_845_driver, &intel_830_driver }, - { PCI_DEVICE_ID_INTEL_82850_HB, 0, "i850", &intel_850_driver, NULL }, - { PCI_DEVICE_ID_INTEL_82854_HB, PCI_DEVICE_ID_INTEL_82854_IG, "854", - &intel_845_driver, &intel_830_driver }, - { PCI_DEVICE_ID_INTEL_82855PM_HB, 0, "855PM", &intel_845_driver, NULL }, - { PCI_DEVICE_ID_INTEL_82855GM_HB, PCI_DEVICE_ID_INTEL_82855GM_IG, "855GM", - &intel_845_driver, &intel_830_driver }, - { PCI_DEVICE_ID_INTEL_82860_HB, 0, "i860", &intel_860_driver, NULL }, - { PCI_DEVICE_ID_INTEL_82865_HB, PCI_DEVICE_ID_INTEL_82865_IG, "865", - &intel_845_driver, &intel_830_driver }, - { PCI_DEVICE_ID_INTEL_82875_HB, 0, "i875", &intel_845_driver, NULL }, - { PCI_DEVICE_ID_INTEL_E7221_HB, PCI_DEVICE_ID_INTEL_E7221_IG, "E7221 (i915)", - NULL, &intel_915_driver }, - { PCI_DEVICE_ID_INTEL_82915G_HB, PCI_DEVICE_ID_INTEL_82915G_IG, "915G", - NULL, &intel_915_driver }, - { PCI_DEVICE_ID_INTEL_82915GM_HB, PCI_DEVICE_ID_INTEL_82915GM_IG, "915GM", - NULL, &intel_915_driver }, - { PCI_DEVICE_ID_INTEL_82945G_HB, PCI_DEVICE_ID_INTEL_82945G_IG, "945G", - NULL, &intel_915_driver }, - { PCI_DEVICE_ID_INTEL_82945GM_HB, PCI_DEVICE_ID_INTEL_82945GM_IG, "945GM", - NULL, &intel_915_driver }, - { PCI_DEVICE_ID_INTEL_82945GME_HB, PCI_DEVICE_ID_INTEL_82945GME_IG, "945GME", - NULL, &intel_915_driver }, - { PCI_DEVICE_ID_INTEL_82946GZ_HB, PCI_DEVICE_ID_INTEL_82946GZ_IG, "946GZ", - NULL, &intel_i965_driver }, - { PCI_DEVICE_ID_INTEL_82G35_HB, PCI_DEVICE_ID_INTEL_82G35_IG, "G35", - NULL, &intel_i965_driver }, - { PCI_DEVICE_ID_INTEL_82965Q_HB, PCI_DEVICE_ID_INTEL_82965Q_IG, "965Q", - NULL, &intel_i965_driver }, - { PCI_DEVICE_ID_INTEL_82965G_HB, PCI_DEVICE_ID_INTEL_82965G_IG, "965G", - NULL, &intel_i965_driver }, - { PCI_DEVICE_ID_INTEL_82965GM_HB, PCI_DEVICE_ID_INTEL_82965GM_IG, "965GM", - NULL, &intel_i965_driver }, - { PCI_DEVICE_ID_INTEL_82965GME_HB, PCI_DEVICE_ID_INTEL_82965GME_IG, "965GME/GLE", - NULL, &intel_i965_driver }, - { PCI_DEVICE_ID_INTEL_7505_0, 0, "E7505", &intel_7505_driver, NULL }, - { PCI_DEVICE_ID_INTEL_7205_0, 0, "E7205", &intel_7505_driver, NULL }, - { PCI_DEVICE_ID_INTEL_G33_HB, PCI_DEVICE_ID_INTEL_G33_IG, "G33", - NULL, &intel_g33_driver }, - { PCI_DEVICE_ID_INTEL_Q35_HB, PCI_DEVICE_ID_INTEL_Q35_IG, "Q35", - NULL, &intel_g33_driver }, - { PCI_DEVICE_ID_INTEL_Q33_HB, PCI_DEVICE_ID_INTEL_Q33_IG, "Q33", - NULL, &intel_g33_driver }, - { PCI_DEVICE_ID_INTEL_PINEVIEW_M_HB, PCI_DEVICE_ID_INTEL_PINEVIEW_M_IG, "GMA3150", - NULL, &intel_g33_driver }, - { PCI_DEVICE_ID_INTEL_PINEVIEW_HB, PCI_DEVICE_ID_INTEL_PINEVIEW_IG, "GMA3150", - NULL, &intel_g33_driver }, - { PCI_DEVICE_ID_INTEL_GM45_HB, PCI_DEVICE_ID_INTEL_GM45_IG, - "GM45", NULL, &intel_i965_driver }, - { PCI_DEVICE_ID_INTEL_EAGLELAKE_HB, PCI_DEVICE_ID_INTEL_EAGLELAKE_IG, - "Eaglelake", NULL, &intel_i965_driver }, - { PCI_DEVICE_ID_INTEL_Q45_HB, PCI_DEVICE_ID_INTEL_Q45_IG, - "Q45/Q43", NULL, &intel_i965_driver }, - { PCI_DEVICE_ID_INTEL_G45_HB, PCI_DEVICE_ID_INTEL_G45_IG, - "G45/G43", NULL, &intel_i965_driver }, - { PCI_DEVICE_ID_INTEL_B43_HB, PCI_DEVICE_ID_INTEL_B43_IG, - "B43", NULL, &intel_i965_driver }, - { PCI_DEVICE_ID_INTEL_B43_1_HB, PCI_DEVICE_ID_INTEL_B43_1_IG, - "B43", NULL, &intel_i965_driver }, - { PCI_DEVICE_ID_INTEL_G41_HB, PCI_DEVICE_ID_INTEL_G41_IG, - "G41", NULL, &intel_i965_driver }, - { PCI_DEVICE_ID_INTEL_IRONLAKE_D_HB, PCI_DEVICE_ID_INTEL_IRONLAKE_D_IG, - "HD Graphics", NULL, &intel_i965_driver }, - { PCI_DEVICE_ID_INTEL_IRONLAKE_M_HB, PCI_DEVICE_ID_INTEL_IRONLAKE_M_IG, - "HD Graphics", NULL, &intel_i965_driver }, - { PCI_DEVICE_ID_INTEL_IRONLAKE_MA_HB, PCI_DEVICE_ID_INTEL_IRONLAKE_M_IG, - "HD Graphics", NULL, &intel_i965_driver }, - { PCI_DEVICE_ID_INTEL_IRONLAKE_MC2_HB, PCI_DEVICE_ID_INTEL_IRONLAKE_M_IG, - "HD Graphics", NULL, &intel_i965_driver }, - { PCI_DEVICE_ID_INTEL_SANDYBRIDGE_HB, PCI_DEVICE_ID_INTEL_SANDYBRIDGE_GT1_IG, - "Sandybridge", NULL, &intel_gen6_driver }, - { PCI_DEVICE_ID_INTEL_SANDYBRIDGE_HB, PCI_DEVICE_ID_INTEL_SANDYBRIDGE_GT2_IG, - "Sandybridge", NULL, &intel_gen6_driver }, - { PCI_DEVICE_ID_INTEL_SANDYBRIDGE_HB, PCI_DEVICE_ID_INTEL_SANDYBRIDGE_GT2_PLUS_IG, - "Sandybridge", NULL, &intel_gen6_driver }, - { PCI_DEVICE_ID_INTEL_SANDYBRIDGE_M_HB, PCI_DEVICE_ID_INTEL_SANDYBRIDGE_M_GT1_IG, - "Sandybridge", NULL, &intel_gen6_driver }, - { PCI_DEVICE_ID_INTEL_SANDYBRIDGE_M_HB, PCI_DEVICE_ID_INTEL_SANDYBRIDGE_M_GT2_IG, - "Sandybridge", NULL, &intel_gen6_driver }, - { PCI_DEVICE_ID_INTEL_SANDYBRIDGE_M_HB, PCI_DEVICE_ID_INTEL_SANDYBRIDGE_M_GT2_PLUS_IG, - "Sandybridge", NULL, &intel_gen6_driver }, - { PCI_DEVICE_ID_INTEL_SANDYBRIDGE_S_HB, PCI_DEVICE_ID_INTEL_SANDYBRIDGE_S_IG, - "Sandybridge", NULL, &intel_gen6_driver }, - { 0, 0, NULL, NULL, NULL } + { PCI_DEVICE_ID_INTEL_82443LX_0, "440LX", &intel_generic_driver }, + { PCI_DEVICE_ID_INTEL_82443BX_0, "440BX", &intel_generic_driver }, + { PCI_DEVICE_ID_INTEL_82443GX_0, "440GX", &intel_generic_driver }, + { PCI_DEVICE_ID_INTEL_82815_MC, "i815", &intel_815_driver }, + { PCI_DEVICE_ID_INTEL_82820_HB, "i820", &intel_820_driver }, + { PCI_DEVICE_ID_INTEL_82820_UP_HB, "i820", &intel_820_driver }, + { PCI_DEVICE_ID_INTEL_82830_HB, "830M", &intel_830mp_driver }, + { PCI_DEVICE_ID_INTEL_82840_HB, "i840", &intel_840_driver }, + { PCI_DEVICE_ID_INTEL_82845_HB, "845G", &intel_845_driver }, + { PCI_DEVICE_ID_INTEL_82845G_HB, "830M", &intel_845_driver }, + { PCI_DEVICE_ID_INTEL_82850_HB, "i850", &intel_850_driver }, + { PCI_DEVICE_ID_INTEL_82854_HB, "854", &intel_845_driver }, + { PCI_DEVICE_ID_INTEL_82855PM_HB, "855PM", &intel_845_driver }, + { PCI_DEVICE_ID_INTEL_82855GM_HB, "855GM", &intel_845_driver }, + { PCI_DEVICE_ID_INTEL_82860_HB, "i860", &intel_860_driver }, + { PCI_DEVICE_ID_INTEL_82865_HB, "865", &intel_845_driver }, + { PCI_DEVICE_ID_INTEL_82875_HB, "i875", &intel_845_driver }, + { PCI_DEVICE_ID_INTEL_7505_0, "E7505", &intel_7505_driver }, + { PCI_DEVICE_ID_INTEL_7205_0, "E7205", &intel_7505_driver }, + { 0, NULL, NULL } }; -static int __devinit intel_gmch_probe(struct pci_dev *pdev, - struct agp_bridge_data *bridge) -{ - int i, mask; - - bridge->driver = NULL; - - for (i = 0; intel_agp_chipsets[i].name != NULL; i++) { - if ((intel_agp_chipsets[i].gmch_chip_id != 0) && - find_gmch(intel_agp_chipsets[i].gmch_chip_id)) { - bridge->driver = - intel_agp_chipsets[i].gmch_driver; - break; - } - } - - if (!bridge->driver) - return 0; - - bridge->dev_private_data = &intel_private; - bridge->dev = pdev; - - dev_info(&pdev->dev, "Intel %s Chipset\n", intel_agp_chipsets[i].name); - - if (bridge->driver->mask_memory == intel_gen6_mask_memory) - mask = 40; - else if (bridge->driver->mask_memory == intel_i965_mask_memory) - mask = 36; - else - mask = 32; - - if (pci_set_dma_mask(intel_private.pcidev, DMA_BIT_MASK(mask))) - dev_err(&intel_private.pcidev->dev, - "set gfx device dma mask %d-bit failed!\n", mask); - else - pci_set_consistent_dma_mask(intel_private.pcidev, - DMA_BIT_MASK(mask)); - - return 1; -} - static int __devinit agp_intel_probe(struct pci_dev *pdev, const struct pci_device_id *ent) { @@ -905,7 +760,7 @@ static int __devinit agp_intel_probe(struct pci_dev *pdev, } } - if (intel_agp_chipsets[i].name == NULL) { + if (!bridge->driver) { if (cap_ptr) dev_warn(&pdev->dev, "unsupported Intel chipset [%04x/%04x]\n", pdev->vendor, pdev->device); @@ -913,14 +768,6 @@ static int __devinit agp_intel_probe(struct pci_dev *pdev, return -ENODEV; } - if (!bridge->driver) { - if (cap_ptr) - dev_warn(&pdev->dev, "can't find bridge device (chip_id: %04x)\n", - intel_agp_chipsets[i].gmch_chip_id); - agp_put_bridge(bridge); - return -ENODEV; - } - bridge->dev = pdev; bridge->dev_private_data = NULL; @@ -972,8 +819,7 @@ static void __devexit agp_intel_remove(struct pci_dev *pdev) agp_remove_bridge(bridge); - if (intel_private.pcidev) - pci_dev_put(intel_private.pcidev); + intel_gmch_remove(pdev); agp_put_bridge(bridge); } diff --git a/drivers/char/agp/intel-agp.h b/drivers/char/agp/intel-agp.h index d09b1ab7e8a..90539df0250 100644 --- a/drivers/char/agp/intel-agp.h +++ b/drivers/char/agp/intel-agp.h @@ -215,44 +215,7 @@ #define PCI_DEVICE_ID_INTEL_SANDYBRIDGE_S_HB 0x0108 /* Server */ #define PCI_DEVICE_ID_INTEL_SANDYBRIDGE_S_IG 0x010A -/* cover 915 and 945 variants */ -#define IS_I915 (agp_bridge->dev->device == PCI_DEVICE_ID_INTEL_E7221_HB || \ - agp_bridge->dev->device == PCI_DEVICE_ID_INTEL_82915G_HB || \ - agp_bridge->dev->device == PCI_DEVICE_ID_INTEL_82915GM_HB || \ - agp_bridge->dev->device == PCI_DEVICE_ID_INTEL_82945G_HB || \ - agp_bridge->dev->device == PCI_DEVICE_ID_INTEL_82945GM_HB || \ - agp_bridge->dev->device == PCI_DEVICE_ID_INTEL_82945GME_HB) - -#define IS_I965 (agp_bridge->dev->device == PCI_DEVICE_ID_INTEL_82946GZ_HB || \ - agp_bridge->dev->device == PCI_DEVICE_ID_INTEL_82G35_HB || \ - agp_bridge->dev->device == PCI_DEVICE_ID_INTEL_82965Q_HB || \ - agp_bridge->dev->device == PCI_DEVICE_ID_INTEL_82965G_HB || \ - agp_bridge->dev->device == PCI_DEVICE_ID_INTEL_82965GM_HB || \ - agp_bridge->dev->device == PCI_DEVICE_ID_INTEL_82965GME_HB) - -#define IS_G33 (agp_bridge->dev->device == PCI_DEVICE_ID_INTEL_G33_HB || \ - agp_bridge->dev->device == PCI_DEVICE_ID_INTEL_Q35_HB || \ - agp_bridge->dev->device == PCI_DEVICE_ID_INTEL_Q33_HB || \ - agp_bridge->dev->device == PCI_DEVICE_ID_INTEL_PINEVIEW_M_HB || \ - agp_bridge->dev->device == PCI_DEVICE_ID_INTEL_PINEVIEW_HB) - -#define IS_PINEVIEW (agp_bridge->dev->device == PCI_DEVICE_ID_INTEL_PINEVIEW_M_HB || \ - agp_bridge->dev->device == PCI_DEVICE_ID_INTEL_PINEVIEW_HB) - -#define IS_SNB (agp_bridge->dev->device == PCI_DEVICE_ID_INTEL_SANDYBRIDGE_HB || \ - agp_bridge->dev->device == PCI_DEVICE_ID_INTEL_SANDYBRIDGE_M_HB || \ - agp_bridge->dev->device == PCI_DEVICE_ID_INTEL_SANDYBRIDGE_S_HB) - -#define IS_G4X (agp_bridge->dev->device == PCI_DEVICE_ID_INTEL_EAGLELAKE_HB || \ - agp_bridge->dev->device == PCI_DEVICE_ID_INTEL_Q45_HB || \ - agp_bridge->dev->device == PCI_DEVICE_ID_INTEL_G45_HB || \ - agp_bridge->dev->device == PCI_DEVICE_ID_INTEL_GM45_HB || \ - agp_bridge->dev->device == PCI_DEVICE_ID_INTEL_G41_HB || \ - agp_bridge->dev->device == PCI_DEVICE_ID_INTEL_B43_HB || \ - agp_bridge->dev->device == PCI_DEVICE_ID_INTEL_IRONLAKE_D_HB || \ - agp_bridge->dev->device == PCI_DEVICE_ID_INTEL_IRONLAKE_M_HB || \ - agp_bridge->dev->device == PCI_DEVICE_ID_INTEL_IRONLAKE_MA_HB || \ - agp_bridge->dev->device == PCI_DEVICE_ID_INTEL_IRONLAKE_MC2_HB || \ - IS_SNB) - +int intel_gmch_probe(struct pci_dev *pdev, + struct agp_bridge_data *bridge); +void intel_gmch_remove(struct pci_dev *pdev); #endif diff --git a/drivers/char/agp/intel-gtt.c b/drivers/char/agp/intel-gtt.c index 75e0a349788..0c8ff6d8824 100644 --- a/drivers/char/agp/intel-gtt.c +++ b/drivers/char/agp/intel-gtt.c @@ -15,6 +15,18 @@ * /fairy-tale-mode off */ +#include <linux/module.h> +#include <linux/pci.h> +#include <linux/init.h> +#include <linux/kernel.h> +#include <linux/pagemap.h> +#include <linux/agp_backend.h> +#include <asm/smp.h> +#include "agp.h" +#include "intel-agp.h" +#include <linux/intel-gtt.h> +#include <drm/intel-gtt.h> + /* * If we have Intel graphics, we're not going to have anything other than * an Intel IOMMU. So make the correct use of the PCI DMA API contingent @@ -23,11 +35,12 @@ */ #ifdef CONFIG_DMAR #define USE_PCI_DMA_API 1 +#else +#define USE_PCI_DMA_API 0 #endif /* Max amount of stolen space, anything above will be returned to Linux */ int intel_max_stolen = 32 * 1024 * 1024; -EXPORT_SYMBOL(intel_max_stolen); static const struct aper_size_info_fixed intel_i810_sizes[] = { @@ -55,32 +68,36 @@ static struct gatt_mask intel_i810_masks[] = #define INTEL_AGP_CACHED_MEMORY_LLC_MLC 3 #define INTEL_AGP_CACHED_MEMORY_LLC_MLC_GFDT 4 -static struct gatt_mask intel_gen6_masks[] = -{ - {.mask = I810_PTE_VALID | GEN6_PTE_UNCACHED, - .type = INTEL_AGP_UNCACHED_MEMORY }, - {.mask = I810_PTE_VALID | GEN6_PTE_LLC, - .type = INTEL_AGP_CACHED_MEMORY_LLC }, - {.mask = I810_PTE_VALID | GEN6_PTE_LLC | GEN6_PTE_GFDT, - .type = INTEL_AGP_CACHED_MEMORY_LLC_GFDT }, - {.mask = I810_PTE_VALID | GEN6_PTE_LLC_MLC, - .type = INTEL_AGP_CACHED_MEMORY_LLC_MLC }, - {.mask = I810_PTE_VALID | GEN6_PTE_LLC_MLC | GEN6_PTE_GFDT, - .type = INTEL_AGP_CACHED_MEMORY_LLC_MLC_GFDT }, +struct intel_gtt_driver { + unsigned int gen : 8; + unsigned int is_g33 : 1; + unsigned int is_pineview : 1; + unsigned int is_ironlake : 1; + unsigned int dma_mask_size : 8; + /* Chipset specific GTT setup */ + int (*setup)(void); + /* This should undo anything done in ->setup() save the unmapping + * of the mmio register file, that's done in the generic code. */ + void (*cleanup)(void); + void (*write_entry)(dma_addr_t addr, unsigned int entry, unsigned int flags); + /* Flags is a more or less chipset specific opaque value. + * For chipsets that need to support old ums (non-gem) code, this + * needs to be identical to the various supported agp memory types! */ + bool (*check_flags)(unsigned int flags); + void (*chipset_flush)(void); }; 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; u8 __iomem *registers; + phys_addr_t gtt_bus_addr; + phys_addr_t gma_bus_addr; + phys_addr_t pte_bus_addr; u32 __iomem *gtt; /* I915G */ int num_dcache_entries; - /* gtt_entries is the number of gtt entries that are already mapped - * to stolen memory. Stolen memory is larger than the memory mapped - * through gtt_entries, as it includes some reserved space for the BIOS - * popup and for the GTT. - */ - int gtt_entries; /* i830+ */ - int gtt_total_size; union { void __iomem *i9xx_flush_page; void *i8xx_flush_page; @@ -88,23 +105,14 @@ static struct _intel_private { struct page *i8xx_page; struct resource ifp_resource; int resource_valid; + struct page *scratch_page; + dma_addr_t scratch_page_dma; } intel_private; -#ifdef USE_PCI_DMA_API -static int intel_agp_map_page(struct page *page, dma_addr_t *ret) -{ - *ret = pci_map_page(intel_private.pcidev, page, 0, - PAGE_SIZE, PCI_DMA_BIDIRECTIONAL); - if (pci_dma_mapping_error(intel_private.pcidev, *ret)) - return -EINVAL; - return 0; -} - -static void intel_agp_unmap_page(struct page *page, dma_addr_t dma) -{ - pci_unmap_page(intel_private.pcidev, dma, - PAGE_SIZE, PCI_DMA_BIDIRECTIONAL); -} +#define INTEL_GTT_GEN intel_private.driver->gen +#define IS_G33 intel_private.driver->is_g33 +#define IS_PINEVIEW intel_private.driver->is_pineview +#define IS_IRONLAKE intel_private.driver->is_ironlake static void intel_agp_free_sglist(struct agp_memory *mem) { @@ -125,6 +133,9 @@ static int intel_agp_map_memory(struct agp_memory *mem) struct scatterlist *sg; int i; + if (mem->sg_list) + return 0; /* already mapped (for e.g. resume */ + DBG("try mapping %lu pages\n", (unsigned long)mem->page_count); if (sg_alloc_table(&st, mem->page_count, GFP_KERNEL)) @@ -156,70 +167,17 @@ static void intel_agp_unmap_memory(struct agp_memory *mem) intel_agp_free_sglist(mem); } -static void intel_agp_insert_sg_entries(struct agp_memory *mem, - off_t pg_start, int mask_type) -{ - struct scatterlist *sg; - int i, j; - - j = pg_start; - - WARN_ON(!mem->num_sg); - - if (mem->num_sg == mem->page_count) { - for_each_sg(mem->sg_list, sg, mem->page_count, i) { - writel(agp_bridge->driver->mask_memory(agp_bridge, - sg_dma_address(sg), mask_type), - intel_private.gtt+j); - j++; - } - } else { - /* sg may merge pages, but we have to separate - * per-page addr for GTT */ - unsigned int len, m; - - for_each_sg(mem->sg_list, sg, mem->num_sg, i) { - len = sg_dma_len(sg) / PAGE_SIZE; - for (m = 0; m < len; m++) { - writel(agp_bridge->driver->mask_memory(agp_bridge, - sg_dma_address(sg) + m * PAGE_SIZE, - mask_type), - intel_private.gtt+j); - j++; - } - } - } - readl(intel_private.gtt+j-1); -} - -#else - -static void intel_agp_insert_sg_entries(struct agp_memory *mem, - off_t pg_start, int mask_type) -{ - int i, j; - - for (i = 0, j = pg_start; i < mem->page_count; i++, j++) { - writel(agp_bridge->driver->mask_memory(agp_bridge, - page_to_phys(mem->pages[i]), mask_type), - intel_private.gtt+j); - } - - readl(intel_private.gtt+j-1); -} - -#endif - static int intel_i810_fetch_size(void) { u32 smram_miscc; struct aper_size_info_fixed *values; - pci_read_config_dword(agp_bridge->dev, I810_SMRAM_MISCC, &smram_miscc); + pci_read_config_dword(intel_private.bridge_dev, + I810_SMRAM_MISCC, &smram_miscc); values = A_SIZE_FIX(agp_bridge->driver->aperture_sizes); if ((smram_miscc & I810_GMS) == I810_GMS_DISABLE) { - dev_warn(&agp_bridge->dev->dev, "i810 is disabled\n"); + dev_warn(&intel_private.bridge_dev->dev, "i810 is disabled\n"); return 0; } if ((smram_miscc & I810_GFX_MEM_WIN_SIZE) == I810_GFX_MEM_WIN_32M) { @@ -284,7 +242,7 @@ static void intel_i810_cleanup(void) iounmap(intel_private.registers); } -static void intel_i810_agp_enable(struct agp_bridge_data *bridge, u32 mode) +static void intel_fake_agp_enable(struct agp_bridge_data *bridge, u32 mode) { return; } @@ -319,34 +277,6 @@ static void i8xx_destroy_pages(struct page *page) atomic_dec(&agp_bridge->current_memory_agp); } -static int intel_i830_type_to_mask_type(struct agp_bridge_data *bridge, - int type) -{ - if (type < AGP_USER_TYPES) - return type; - else if (type == AGP_USER_CACHED_MEMORY) - return INTEL_AGP_CACHED_MEMORY; - else - return 0; -} - -static int intel_gen6_type_to_mask_type(struct agp_bridge_data *bridge, - int type) -{ - unsigned int type_mask = type & ~AGP_USER_CACHED_MEMORY_GFDT; - unsigned int gfdt = type & AGP_USER_CACHED_MEMORY_GFDT; - - if (type_mask == AGP_USER_UNCACHED_MEMORY) - return INTEL_AGP_UNCACHED_MEMORY; - else if (type_mask == AGP_USER_CACHED_MEMORY_LLC_MLC) - return gfdt ? INTEL_AGP_CACHED_MEMORY_LLC_MLC_GFDT : - INTEL_AGP_CACHED_MEMORY_LLC_MLC; - else /* set 'normal'/'cached' to LLC by default */ - return gfdt ? INTEL_AGP_CACHED_MEMORY_LLC_GFDT : - INTEL_AGP_CACHED_MEMORY_LLC; -} - - static int intel_i810_insert_entries(struct agp_memory *mem, off_t pg_start, int type) { @@ -514,8 +444,33 @@ static unsigned long intel_i810_mask_memory(struct agp_bridge_data *bridge, return addr | bridge->driver->masks[type].mask; } -static struct aper_size_info_fixed intel_i830_sizes[] = +static int intel_gtt_setup_scratch_page(void) { + struct page *page; + dma_addr_t dma_addr; + + page = alloc_page(GFP_KERNEL | GFP_DMA32 | __GFP_ZERO); + if (page == NULL) + return -ENOMEM; + get_page(page); + set_pages_uc(page, 1); + + if (USE_PCI_DMA_API && INTEL_GTT_GEN > 2) { + 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.scratch_page_dma = dma_addr; + } else + intel_private.scratch_page_dma = page_to_phys(page); + + intel_private.scratch_page = page; + + return 0; +} + +static const struct aper_size_info_fixed const intel_fake_agp_sizes[] = { {128, 32768, 5}, /* The 64M mode still requires a 128k gatt */ {64, 16384, 5}, @@ -523,102 +478,49 @@ static struct aper_size_info_fixed intel_i830_sizes[] = {512, 131072, 7}, }; -static void intel_i830_init_gtt_entries(void) +static unsigned int intel_gtt_stolen_entries(void) { u16 gmch_ctrl; - int gtt_entries = 0; u8 rdct; int local = 0; static const int ddt[4] = { 0, 16, 32, 64 }; - int size; /* reserved space (in kb) at the top of stolen memory */ + unsigned int overhead_entries, stolen_entries; + unsigned int stolen_size = 0; - pci_read_config_word(agp_bridge->dev, I830_GMCH_CTRL, &gmch_ctrl); + pci_read_config_word(intel_private.bridge_dev, + I830_GMCH_CTRL, &gmch_ctrl); - if (IS_I965) { - u32 pgetbl_ctl; - pgetbl_ctl = readl(intel_private.registers+I810_PGETBL_CTL); + if (INTEL_GTT_GEN > 4 || IS_PINEVIEW) + overhead_entries = 0; + else + overhead_entries = intel_private.base.gtt_mappable_entries + / 1024; - /* The 965 has a field telling us the size of the GTT, - * which may be larger than what is necessary to map the - * aperture. - */ - switch (pgetbl_ctl & I965_PGETBL_SIZE_MASK) { - case I965_PGETBL_SIZE_128KB: - size = 128; - break; - case I965_PGETBL_SIZE_256KB: - size = 256; - break; - case I965_PGETBL_SIZE_512KB: - size = 512; - break; - case I965_PGETBL_SIZE_1MB: - size = 1024; - break; - case I965_PGETBL_SIZE_2MB: - size = 2048; - break; - case I965_PGETBL_SIZE_1_5MB: - size = 1024 + 512; - break; - default: - dev_info(&intel_private.pcidev->dev, - "unknown page table size, assuming 512KB\n"); - size = 512; - } - size += 4; /* add in BIOS popup space */ - } else if (IS_G33 && !IS_PINEVIEW) { - /* G33's GTT size defined in gmch_ctrl */ - switch (gmch_ctrl & G33_PGETBL_SIZE_MASK) { - case G33_PGETBL_SIZE_1M: - size = 1024; - break; - case G33_PGETBL_SIZE_2M: - size = 2048; - break; - default: - dev_info(&agp_bridge->dev->dev, - "unknown page table size 0x%x, assuming 512KB\n", - (gmch_ctrl & G33_PGETBL_SIZE_MASK)); - size = 512; - } - size += 4; - } else if (IS_G4X || IS_PINEVIEW) { - /* On 4 series hardware, GTT stolen is separate from graphics - * stolen, ignore it in stolen gtt entries counting. However, - * 4KB of the stolen memory doesn't get mapped to the GTT. - */ - size = 4; - } else { - /* On previous hardware, the GTT size was just what was - * required to map the aperture. - */ - size = agp_bridge->driver->fetch_size() + 4; - } + overhead_entries += 1; /* BIOS popup */ - if (agp_bridge->dev->device == PCI_DEVICE_ID_INTEL_82830_HB || - agp_bridge->dev->device == PCI_DEVICE_ID_INTEL_82845G_HB) { + if (intel_private.bridge_dev->device == PCI_DEVICE_ID_INTEL_82830_HB || + intel_private.bridge_dev->device == PCI_DEVICE_ID_INTEL_82845G_HB) { switch (gmch_ctrl & I830_GMCH_GMS_MASK) { case I830_GMCH_GMS_STOLEN_512: - gtt_entries = KB(512) - KB(size); + stolen_size = KB(512); break; case I830_GMCH_GMS_STOLEN_1024: - gtt_entries = MB(1) - KB(size); + stolen_size = MB(1); break; case I830_GMCH_GMS_STOLEN_8192: - gtt_entries = MB(8) - KB(size); + stolen_size = MB(8); break; case I830_GMCH_GMS_LOCAL: rdct = readb(intel_private.registers+I830_RDRAM_CHANNEL_TYPE); - gtt_entries = (I830_RDRAM_ND(rdct) + 1) * + stolen_size = (I830_RDRAM_ND(rdct) + 1) * MB(ddt[I830_RDRAM_DDT(rdct)]); local = 1; break; default: - gtt_entries = 0; + stolen_size = 0; break; } - } else if (IS_SNB) { + } else if (INTEL_GTT_GEN == 6) { /* * SandyBridge has new memory control reg at 0x50.w */ @@ -626,149 +528,292 @@ static void intel_i830_init_gtt_entries(void) pci_read_config_word(intel_private.pcidev, SNB_GMCH_CTRL, &snb_gmch_ctl); switch (snb_gmch_ctl & SNB_GMCH_GMS_STOLEN_MASK) { case SNB_GMCH_GMS_STOLEN_32M: - gtt_entries = MB(32) - KB(size); + stolen_size = MB(32); break; case SNB_GMCH_GMS_STOLEN_64M: - gtt_entries = MB(64) - KB(size); + stolen_size = MB(64); break; case SNB_GMCH_GMS_STOLEN_96M: - gtt_entries = MB(96) - KB(size); + stolen_size = MB(96); break; case SNB_GMCH_GMS_STOLEN_128M: - gtt_entries = MB(128) - KB(size); + stolen_size = MB(128); break; case SNB_GMCH_GMS_STOLEN_160M: - gtt_entries = MB(160) - KB(size); + stolen_size = MB(160); break; case SNB_GMCH_GMS_STOLEN_192M: - gtt_entries = MB(192) - KB(size); + stolen_size = MB(192); break; case SNB_GMCH_GMS_STOLEN_224M: - gtt_entries = MB(224) - KB(size); + stolen_size = MB(224); break; case SNB_GMCH_GMS_STOLEN_256M: - gtt_entries = MB(256) - KB(size); + stolen_size = MB(256); break; case SNB_GMCH_GMS_STOLEN_288M: - gtt_entries = MB(288) - KB(size); + stolen_size = MB(288); break; case SNB_GMCH_GMS_STOLEN_320M: - gtt_entries = MB(320) - KB(size); + stolen_size = MB(320); break; case SNB_GMCH_GMS_STOLEN_352M: - gtt_entries = MB(352) - KB(size); + stolen_size = MB(352); break; case SNB_GMCH_GMS_STOLEN_384M: - gtt_entries = MB(384) - KB(size); + stolen_size = MB(384); break; case SNB_GMCH_GMS_STOLEN_416M: - gtt_entries = MB(416) - KB(size); + stolen_size = MB(416); break; case SNB_GMCH_GMS_STOLEN_448M: - gtt_entries = MB(448) - KB(size); + stolen_size = MB(448); break; case SNB_GMCH_GMS_STOLEN_480M: - gtt_entries = MB(480) - KB(size); + stolen_size = MB(480); break; case SNB_GMCH_GMS_STOLEN_512M: - gtt_entries = MB(512) - KB(size); + stolen_size = MB(512); break; } } else { switch (gmch_ctrl & I855_GMCH_GMS_MASK) { case I855_GMCH_GMS_STOLEN_1M: - gtt_entries = MB(1) - KB(size); + stolen_size = MB(1); break; case I855_GMCH_GMS_STOLEN_4M: - gtt_entries = MB(4) - KB(size); + stolen_size = MB(4); break; case I855_GMCH_GMS_STOLEN_8M: - gtt_entries = MB(8) - KB(size); + stolen_size = MB(8); break; case I855_GMCH_GMS_STOLEN_16M: - gtt_entries = MB(16) - KB(size); + stolen_size = MB(16); break; case I855_GMCH_GMS_STOLEN_32M: - gtt_entries = MB(32) - KB(size); + stolen_size = MB(32); break; case I915_GMCH_GMS_STOLEN_48M: - /* Check it's really I915G */ - if (IS_I915 || IS_I965 || IS_G33 || IS_G4X) - gtt_entries = MB(48) - KB(size); - else - gtt_entries = 0; + stolen_size = MB(48); break; case I915_GMCH_GMS_STOLEN_64M: - /* Check it's really I915G */ - if (IS_I915 || IS_I965 || IS_G33 || IS_G4X) - gtt_entries = MB(64) - KB(size); - else - gtt_entries = 0; + stolen_size = MB(64); break; case G33_GMCH_GMS_STOLEN_128M: - if (IS_G33 || IS_I965 || IS_G4X) - gtt_entries = MB(128) - KB(size); - else - gtt_entries = 0; + stolen_size = MB(128); break; case G33_GMCH_GMS_STOLEN_256M: - if (IS_G33 || IS_I965 || IS_G4X) - gtt_entries = MB(256) - KB(size); - else - gtt_entries = 0; + stolen_size = MB(256); break; case INTEL_GMCH_GMS_STOLEN_96M: - if (IS_I965 || IS_G4X) - gtt_entries = MB(96) - KB(size); - else - gtt_entries = 0; + stolen_size = MB(96); break; case INTEL_GMCH_GMS_STOLEN_160M: - if (IS_I965 || IS_G4X) - gtt_entries = MB(160) - KB(size); - else - gtt_entries = 0; + stolen_size = MB(160); break; case INTEL_GMCH_GMS_STOLEN_224M: - if (IS_I965 || IS_G4X) - gtt_entries = MB(224) - KB(size); - else - gtt_entries = 0; + stolen_size = MB(224); break; case INTEL_GMCH_GMS_STOLEN_352M: - if (IS_I965 || IS_G4X) - gtt_entries = MB(352) - KB(size); - else - gtt_entries = 0; + stolen_size = MB(352); break; default: - gtt_entries = 0; + stolen_size = 0; break; } } - if (!local && gtt_entries > intel_max_stolen) { - dev_info(&agp_bridge->dev->dev, + + if (!local && stolen_size > intel_max_stolen) { + dev_info(&intel_private.bridge_dev->dev, "detected %dK stolen memory, trimming to %dK\n", - gtt_entries / KB(1), intel_max_stolen / KB(1)); - gtt_entries = intel_max_stolen / KB(4); - } else if (gtt_entries > 0) { - dev_info(&agp_bridge->dev->dev, "detected %dK %s memory\n", - gtt_entries / KB(1), local ? "local" : "stolen"); - gtt_entries /= KB(4); + stolen_size / KB(1), intel_max_stolen / KB(1)); + stolen_size = intel_max_stolen; + } else if (stolen_size > 0) { + dev_info(&intel_private.bridge_dev->dev, "detected %dK %s memory\n", + stolen_size / KB(1), local ? "local" : "stolen"); } else { - dev_info(&agp_bridge->dev->dev, + dev_info(&intel_private.bridge_dev->dev, "no pre-allocated video memory detected\n"); - gtt_entries = 0; + stolen_size = 0; + } + + stolen_entries = stolen_size/KB(4) - overhead_entries; + + return stolen_entries; +} + +static unsigned int intel_gtt_total_entries(void) +{ + int size; + + if (IS_G33 || INTEL_GTT_GEN == 4 || INTEL_GTT_GEN == 5) { + u32 pgetbl_ctl; + pgetbl_ctl = readl(intel_private.registers+I810_PGETBL_CTL); + + switch (pgetbl_ctl & I965_PGETBL_SIZE_MASK) { + case I965_PGETBL_SIZE_128KB: + size = KB(128); + break; + case I965_PGETBL_SIZE_256KB: + size = KB(256); + break; + case I965_PGETBL_SIZE_512KB: + size = KB(512); + break; + case I965_PGETBL_SIZE_1MB: + size = KB(1024); + break; + case I965_PGETBL_SIZE_2MB: + size = KB(2048); + break; + case I965_PGETBL_SIZE_1_5MB: + size = KB(1024 + 512); + break; + default: + dev_info(&intel_private.pcidev->dev, + "unknown page table size, assuming 512KB\n"); + size = KB(512); + } + + return size/4; + } else if (INTEL_GTT_GEN == 6) { + u16 snb_gmch_ctl; + + pci_read_config_word(intel_private.pcidev, SNB_GMCH_CTRL, &snb_gmch_ctl); + switch (snb_gmch_ctl & SNB_GTT_SIZE_MASK) { + default: + case SNB_GTT_SIZE_0M: + printk(KERN_ERR "Bad GTT size mask: 0x%04x.\n", snb_gmch_ctl); + size = MB(0); + break; + case SNB_GTT_SIZE_1M: + size = MB(1); + break; + case SNB_GTT_SIZE_2M: + size = MB(2); + break; + } + return size/4; + } else { + /* On previous hardware, the GTT size was just what was + * required to map the aperture. + */ + return intel_private.base.gtt_mappable_entries; + } +} + +static unsigned int intel_gtt_mappable_entries(void) +{ + unsigned int aperture_size; + + if (INTEL_GTT_GEN == 2) { + u16 gmch_ctrl; + + pci_read_config_word(intel_private.bridge_dev, + I830_GMCH_CTRL, &gmch_ctrl); + + if ((gmch_ctrl & I830_GMCH_MEM_MASK) == I830_GMCH_MEM_64M) + aperture_size = MB(64); + else + aperture_size = MB(128); + } else { + /* 9xx supports large sizes, just look at the length */ + aperture_size = pci_resource_len(intel_private.pcidev, 2); } - intel_private.gtt_entries = gtt_entries; + return aperture_size >> PAGE_SHIFT; } -static void intel_i830_fini_flush(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.scratch_page_dma, + PAGE_SIZE, PCI_DMA_BIDIRECTIONAL); + put_page(intel_private.scratch_page); + __free_page(intel_private.scratch_page); +} + +static void intel_gtt_cleanup(void) +{ + intel_private.driver->cleanup(); + + iounmap(intel_private.gtt); + iounmap(intel_private.registers); + + intel_gtt_teardown_scratch_page(); +} + +static int intel_gtt_init(void) +{ + u32 gtt_map_size; + int ret; + + ret = intel_private.driver->setup(); + 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(); + + 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); + + gtt_map_size = intel_private.base.gtt_total_entries * 4; + + intel_private.gtt = ioremap(intel_private.gtt_bus_addr, + gtt_map_size); + if (!intel_private.gtt) { + intel_private.driver->cleanup(); + iounmap(intel_private.registers); + return -ENOMEM; + } + + global_cache_flush(); /* FIXME: ? */ + + /* we have to call this as early as possible after the MMIO base address is known */ + intel_private.base.gtt_stolen_entries = intel_gtt_stolen_entries(); + if (intel_private.base.gtt_stolen_entries == 0) { + intel_private.driver->cleanup(); + iounmap(intel_private.registers); + iounmap(intel_private.gtt); + return -ENOMEM; + } + + ret = intel_gtt_setup_scratch_page(); + if (ret != 0) { + intel_gtt_cleanup(); + return ret; + } + + return 0; +} + +static int intel_fake_agp_fetch_size(void) +{ + int num_sizes = ARRAY_SIZE(intel_fake_agp_sizes); + unsigned int aper_size; + int i; + + aper_size = (intel_private.base.gtt_mappable_entries << PAGE_SHIFT) + / MB(1); + + for (i = 0; i < num_sizes; i++) { + if (aper_size == intel_fake_agp_sizes[i].size) { + agp_bridge->current_size = + (void *) (intel_fake_agp_sizes + i); + return aper_size; + } + } + + return 0; +} + +static void i830_cleanup(void) { kunmap(intel_private.i8xx_page); intel_private.i8xx_flush_page = NULL; - unmap_page_from_agp(intel_private.i8xx_page); __free_page(intel_private.i8xx_page); intel_private.i8xx_page = NULL; @@ -780,13 +825,13 @@ static void intel_i830_setup_flush(void) if (intel_private.i8xx_page) return; - intel_private.i8xx_page = alloc_page(GFP_KERNEL | __GFP_ZERO | GFP_DMA32); + intel_private.i8xx_page = alloc_page(GFP_KERNEL); if (!intel_private.i8xx_page) return; intel_private.i8xx_flush_page = kmap(intel_private.i8xx_page); if (!intel_private.i8xx_flush_page) - intel_i830_fini_flush(); + i830_cleanup(); } /* The chipset_flush interface needs to get data that has already been @@ -799,7 +844,7 @@ static void intel_i830_setup_flush(void) * that buffer out, we just fill 1KB and clflush it out, on the assumption * that it'll push whatever was in there out. It appears to work. */ -static void intel_i830_chipset_flush(struct agp_bridge_data *bridge) +static void i830_chipset_flush(void) { unsigned int *pg = intel_private.i8xx_flush_page; @@ -811,169 +856,184 @@ static void intel_i830_chipset_flush(struct agp_bridge_data *bridge) printk(KERN_ERR "Timed out waiting for cache flush.\n"); } -/* The intel i830 automatically initializes the agp aperture during POST. - * Use the memory already set aside for in the GTT. - */ -static int intel_i830_create_gatt_table(struct agp_bridge_data *bridge) +static void i830_write_entry(dma_addr_t addr, unsigned int entry, + unsigned int flags) { - int page_order; - struct aper_size_info_fixed *size; - int num_entries; - u32 temp; + u32 pte_flags = I810_PTE_VALID; + + switch (flags) { + case AGP_DCACHE_MEMORY: + pte_flags |= I810_PTE_LOCAL; + break; + case AGP_USER_CACHED_MEMORY: + pte_flags |= I830_PTE_SYSTEM_CACHED; + break; + } - size = agp_bridge->current_size; - page_order = size->page_order; - num_entries = size->num_entries; - agp_bridge->gatt_table_real = NULL; + writel(addr | pte_flags, intel_private.gtt + entry); +} - pci_read_config_dword(intel_private.pcidev, I810_MMADDR, &temp); - temp &= 0xfff80000; +static void intel_enable_gtt(void) +{ + u32 gma_addr; + u16 gmch_ctrl; - intel_private.registers = ioremap(temp, 128 * 4096); - if (!intel_private.registers) - return -ENOMEM; + if (INTEL_GTT_GEN == 2) + pci_read_config_dword(intel_private.pcidev, I810_GMADDR, + &gma_addr); + else + pci_read_config_dword(intel_private.pcidev, I915_GMADDR, + &gma_addr); - temp = readl(intel_private.registers+I810_PGETBL_CTL) & 0xfffff000; - global_cache_flush(); /* FIXME: ?? */ + intel_private.gma_bus_addr = (gma_addr & PCI_BASE_ADDRESS_MEM_MASK); - /* we have to call this as early as possible after the MMIO base address is known */ - intel_i830_init_gtt_entries(); - if (intel_private.gtt_entries == 0) { - iounmap(intel_private.registers); + pci_read_config_word(intel_private.bridge_dev, I830_GMCH_CTRL, &gmch_ctrl); + gmch_ctrl |= I830_GMCH_ENABLED; + pci_write_config_word(intel_private.bridge_dev, I830_GMCH_CTRL, gmch_ctrl); + + writel(intel_private.pte_bus_addr|I810_PGETBL_ENABLED, + intel_private.registers+I810_PGETBL_CTL); + readl(intel_private.registers+I810_PGETBL_CTL); /* PCI Posting. */ +} + +static int i830_setup(void) +{ + u32 reg_addr; + + pci_read_config_dword(intel_private.pcidev, I810_MMADDR, ®_addr); + reg_addr &= 0xfff80000; + + intel_private.registers = ioremap(reg_addr, KB(64)); + if (!intel_private.registers) return -ENOMEM; - } - agp_bridge->gatt_table = NULL; + intel_private.gtt_bus_addr = reg_addr + I810_PTE_BASE; + intel_private.pte_bus_addr = + readl(intel_private.registers+I810_PGETBL_CTL) & 0xfffff000; - agp_bridge->gatt_bus_addr = temp; + intel_i830_setup_flush(); return 0; } -/* Return the gatt table to a sane state. Use the top of stolen - * memory for the GTT. - */ -static int intel_i830_free_gatt_table(struct agp_bridge_data *bridge) +static int intel_fake_agp_create_gatt_table(struct agp_bridge_data *bridge) { + agp_bridge->gatt_table_real = NULL; + agp_bridge->gatt_table = NULL; + agp_bridge->gatt_bus_addr = 0; + return 0; } -static int intel_i830_fetch_size(void) +static int intel_fake_agp_free_gatt_table(struct agp_bridge_data *bridge) { - u16 gmch_ctrl; - struct aper_size_info_fixed *values; - - values = A_SIZE_FIX(agp_bridge->driver->aperture_sizes); - - if (agp_bridge->dev->device != PCI_DEVICE_ID_INTEL_82830_HB && - agp_bridge->dev->device != PCI_DEVICE_ID_INTEL_82845G_HB) { - /* 855GM/852GM/865G has 128MB aperture size */ - agp_bridge->current_size = (void *) values; - agp_bridge->aperture_size_idx = 0; - return values[0].size; - } - - pci_read_config_word(agp_bridge->dev, I830_GMCH_CTRL, &gmch_ctrl); - - if ((gmch_ctrl & I830_GMCH_MEM_MASK) == I830_GMCH_MEM_128M) { - agp_bridge->current_size = (void *) values; - agp_bridge->aperture_size_idx = 0; - return values[0].size; - } else { - agp_bridge->current_size = (void *) (values + 1); - agp_bridge->aperture_size_idx = 1; - return values[1].size; - } - return 0; } -static int intel_i830_configure(void) +static int intel_fake_agp_configure(void) { - struct aper_size_info_fixed *current_size; - u32 temp; - u16 gmch_ctrl; int i; - current_size = A_SIZE_FIX(agp_bridge->current_size); - - pci_read_config_dword(intel_private.pcidev, I810_GMADDR, &temp); - agp_bridge->gart_bus_addr = (temp & PCI_BASE_ADDRESS_MEM_MASK); - - pci_read_config_word(agp_bridge->dev, I830_GMCH_CTRL, &gmch_ctrl); - gmch_ctrl |= I830_GMCH_ENABLED; - pci_write_config_word(agp_bridge->dev, I830_GMCH_CTRL, gmch_ctrl); + intel_enable_gtt(); - writel(agp_bridge->gatt_bus_addr|I810_PGETBL_ENABLED, intel_private.registers+I810_PGETBL_CTL); - readl(intel_private.registers+I810_PGETBL_CTL); /* PCI Posting. */ + agp_bridge->gart_bus_addr = intel_private.gma_bus_addr; - if (agp_bridge->driver->needs_scratch_page) { - for (i = intel_private.gtt_entries; i < current_size->num_entries; i++) { - writel(agp_bridge->scratch_page, intel_private.registers+I810_PTE_BASE+(i*4)); - } - readl(intel_private.registers+I810_PTE_BASE+((i-1)*4)); /* PCI Posting. */ + for (i = intel_private.base.gtt_stolen_entries; + i < intel_private.base.gtt_total_entries; i++) { + intel_private.driver->write_entry(intel_private.scratch_page_dma, + i, 0); } + readl(intel_private.gtt+i-1); /* PCI Posting. */ global_cache_flush(); - intel_i830_setup_flush(); return 0; } -static void intel_i830_cleanup(void) +static bool i830_check_flags(unsigned int flags) { - iounmap(intel_private.registers); + switch (flags) { + case 0: + case AGP_PHYS_MEMORY: + case AGP_USER_CACHED_MEMORY: + case AGP_USER_MEMORY: + return true; + } + + return false; } -static int intel_i830_insert_entries(struct agp_memory *mem, off_t pg_start, - int type) +static void intel_gtt_insert_sg_entries(struct scatterlist *sg_list, + unsigned int sg_len, + unsigned int pg_start, + unsigned int flags) { - int i, j, num_entries; - void *temp; + struct scatterlist *sg; + unsigned int len, m; + int i, j; + + j = pg_start; + + /* sg may merge pages, but we have to separate + * per-page addr for GTT */ + for_each_sg(sg_list, sg, sg_len, i) { + len = sg_dma_len(sg) >> PAGE_SHIFT; + for (m = 0; m < len; m++) { + dma_addr_t addr = sg_dma_address(sg) + (m << PAGE_SHIFT); + intel_private.driver->write_entry(addr, + j, flags); + j++; + } + } + readl(intel_private.gtt+j-1); +} + +static int intel_fake_agp_insert_entries(struct agp_memory *mem, + off_t pg_start, int type) +{ + int i, j; int ret = -EINVAL; - int mask_type; if (mem->page_count == 0) goto out; - temp = agp_bridge->current_size; - num_entries = A_SIZE_FIX(temp)->num_entries; - - if (pg_start < intel_private.gtt_entries) { + if (pg_start < intel_private.base.gtt_stolen_entries) { dev_printk(KERN_DEBUG, &intel_private.pcidev->dev, - "pg_start == 0x%.8lx, intel_private.gtt_entries == 0x%.8x\n", - pg_start, intel_private.gtt_entries); + "pg_start == 0x%.8lx, gtt_stolen_entries == 0x%.8x\n", + pg_start, intel_private.base.gtt_stolen_entries); dev_info(&intel_private.pcidev->dev, "trying to insert into local/stolen memory\n"); goto out_err; } - if ((pg_start + mem->page_count) > num_entries) + if ((pg_start + mem->page_count) > intel_private.base.gtt_total_entries) goto out_err; - /* The i830 can't check the GTT for entries since its read only, - * depend on the caller to make the correct offset decisions. - */ - if (type != mem->type) goto out_err; - mask_type = agp_bridge->driver->agp_type_to_mask_type(agp_bridge, type); - - if (mask_type != 0 && mask_type != AGP_PHYS_MEMORY && - mask_type != INTEL_AGP_CACHED_MEMORY) + if (!intel_private.driver->check_flags(type)) goto out_err; if (!mem->is_flushed) global_cache_flush(); - for (i = 0, j = pg_start; i < mem->page_count; i++, j++) { - writel(agp_bridge->driver->mask_memory(agp_bridge, - page_to_phys(mem->pages[i]), mask_type), - intel_private.registers+I810_PTE_BASE+(j*4)); + if (USE_PCI_DMA_API && INTEL_GTT_GEN > 2) { + ret = intel_agp_map_memory(mem); + if (ret != 0) + return ret; + + intel_gtt_insert_sg_entries(mem->sg_list, mem->num_sg, + pg_start, type); + } else { + for (i = 0, j = pg_start; i < mem->page_count; i++, j++) { + dma_addr_t addr = page_to_phys(mem->pages[i]); + intel_private.driver->write_entry(addr, + j, type); + } + readl(intel_private.gtt+j-1); } - readl(intel_private.registers+I810_PTE_BASE+((j-1)*4)); out: ret = 0; @@ -982,29 +1042,39 @@ out_err: return ret; } -static int intel_i830_remove_entries(struct agp_memory *mem, off_t pg_start, - int type) +static int intel_fake_agp_remove_entries(struct agp_memory *mem, + off_t pg_start, int type) { int i; if (mem->page_count == 0) return 0; - if (pg_start < intel_private.gtt_entries) { + if (pg_start < intel_private.base.gtt_stolen_entries) { dev_info(&intel_private.pcidev->dev, "trying to disable local/stolen memory\n"); return -EINVAL; } + if (USE_PCI_DMA_API && INTEL_GTT_GEN > 2) + intel_agp_unmap_memory(mem); + for (i = pg_start; i < (mem->page_count + pg_start); i++) { - writel(agp_bridge->scratch_page, intel_private.registers+I810_PTE_BASE+(i*4)); + intel_private.driver->write_entry(intel_private.scratch_page_dma, + i, 0); } - readl(intel_private.registers+I810_PTE_BASE+((i-1)*4)); + readl(intel_private.gtt+i-1); return 0; } -static struct agp_memory *intel_i830_alloc_by_type(size_t pg_count, int type) +static void intel_fake_agp_chipset_flush(struct agp_bridge_data *bridge) +{ + intel_private.driver->chipset_flush(); +} + +static struct agp_memory *intel_fake_agp_alloc_by_type(size_t pg_count, + int type) { if (type == AGP_PHYS_MEMORY) return alloc_agpphysmem_i8xx(pg_count, type); @@ -1015,9 +1085,9 @@ static struct agp_memory *intel_i830_alloc_by_type(size_t pg_count, int type) static int intel_alloc_chipset_flush_resource(void) { int ret; - ret = pci_bus_alloc_resource(agp_bridge->dev->bus, &intel_private.ifp_resource, PAGE_SIZE, + ret = pci_bus_alloc_resource(intel_private.bridge_dev->bus, &intel_private.ifp_resource, PAGE_SIZE, PAGE_SIZE, PCIBIOS_MIN_MEM, 0, - pcibios_align_resource, agp_bridge->dev); + pcibios_align_resource, intel_private.bridge_dev); return ret; } @@ -1027,11 +1097,11 @@ static void intel_i915_setup_chipset_flush(void) int ret; u32 temp; - pci_read_config_dword(agp_bridge->dev, I915_IFPADDR, &temp); + pci_read_config_dword(intel_private.bridge_dev, I915_IFPADDR, &temp); if (!(temp & 0x1)) { intel_alloc_chipset_flush_resource(); intel_private.resource_valid = 1; - pci_write_config_dword(agp_bridge->dev, I915_IFPADDR, (intel_private.ifp_resource.start & 0xffffffff) | 0x1); + pci_write_config_dword(intel_private.bridge_dev, I915_IFPADDR, (intel_private.ifp_resource.start & 0xffffffff) | 0x1); } else { temp &= ~1; @@ -1050,17 +1120,17 @@ static void intel_i965_g33_setup_chipset_flush(void) u32 temp_hi, temp_lo; int ret; - pci_read_config_dword(agp_bridge->dev, I965_IFPADDR + 4, &temp_hi); - pci_read_config_dword(agp_bridge->dev, I965_IFPADDR, &temp_lo); + pci_read_config_dword(intel_private.bridge_dev, I965_IFPADDR + 4, &temp_hi); + pci_read_config_dword(intel_private.bridge_dev, I965_IFPADDR, &temp_lo); if (!(temp_lo & 0x1)) { intel_alloc_chipset_flush_resource(); intel_private.resource_valid = 1; - pci_write_config_dword(agp_bridge->dev, I965_IFPADDR + 4, + pci_write_config_dword(intel_private.bridge_dev, I965_IFPADDR + 4, upper_32_bits(intel_private.ifp_resource.start)); - pci_write_config_dword(agp_bridge->dev, I965_IFPADDR, (intel_private.ifp_resource.start & 0xffffffff) | 0x1); + pci_write_config_dword(intel_private.bridge_dev, I965_IFPADDR, (intel_private.ifp_resource.start & 0xffffffff) | 0x1); } else { u64 l64; @@ -1083,7 +1153,7 @@ static void intel_i9xx_setup_flush(void) if (intel_private.ifp_resource.start) return; - if (IS_SNB) + if (INTEL_GTT_GEN == 6) return; /* setup a resource for this object */ @@ -1091,7 +1161,7 @@ static void intel_i9xx_setup_flush(void) intel_private.ifp_resource.flags = IORESOURCE_MEM; /* Setup chipset flush for 915 */ - if (IS_I965 || IS_G33 || IS_G4X) { + if (IS_G33 || INTEL_GTT_GEN >= 4) { intel_i965_g33_setup_chipset_flush(); } else { intel_i915_setup_chipset_flush(); @@ -1104,41 +1174,7 @@ static void intel_i9xx_setup_flush(void) "can't ioremap flush page - no chipset flushing\n"); } -static int intel_i9xx_configure(void) -{ - struct aper_size_info_fixed *current_size; - u32 temp; - u16 gmch_ctrl; - int i; - - current_size = A_SIZE_FIX(agp_bridge->current_size); - - pci_read_config_dword(intel_private.pcidev, I915_GMADDR, &temp); - - agp_bridge->gart_bus_addr = (temp & PCI_BASE_ADDRESS_MEM_MASK); - - pci_read_config_word(agp_bridge->dev, I830_GMCH_CTRL, &gmch_ctrl); - gmch_ctrl |= I830_GMCH_ENABLED; - pci_write_config_word(agp_bridge->dev, I830_GMCH_CTRL, gmch_ctrl); - - writel(agp_bridge->gatt_bus_addr|I810_PGETBL_ENABLED, intel_private.registers+I810_PGETBL_CTL); - readl(intel_private.registers+I810_PGETBL_CTL); /* PCI Posting. */ - - if (agp_bridge->driver->needs_scratch_page) { - for (i = intel_private.gtt_entries; i < intel_private.gtt_total_size; i++) { - writel(agp_bridge->scratch_page, intel_private.gtt+i); - } - readl(intel_private.gtt+i-1); /* PCI Posting. */ - } - - global_cache_flush(); - - intel_i9xx_setup_flush(); - - return 0; -} - -static void intel_i915_cleanup(void) +static void i9xx_cleanup(void) { if (intel_private.i9xx_flush_page) iounmap(intel_private.i9xx_flush_page); @@ -1146,320 +1182,93 @@ static void intel_i915_cleanup(void) release_resource(&intel_private.ifp_resource); intel_private.ifp_resource.start = 0; intel_private.resource_valid = 0; - iounmap(intel_private.gtt); - iounmap(intel_private.registers); } -static void intel_i915_chipset_flush(struct agp_bridge_data *bridge) +static void i9xx_chipset_flush(void) { if (intel_private.i9xx_flush_page) writel(1, intel_private.i9xx_flush_page); } -static int intel_i915_insert_entries(struct agp_memory *mem, off_t pg_start, - int type) +static void i965_write_entry(dma_addr_t addr, unsigned int entry, + unsigned int flags) { - int num_entries; - void *temp; - int ret = -EINVAL; - int mask_type; - - if (mem->page_count == 0) - goto out; - - temp = agp_bridge->current_size; - num_entries = A_SIZE_FIX(temp)->num_entries; - - if (pg_start < intel_private.gtt_entries) { - dev_printk(KERN_DEBUG, &intel_private.pcidev->dev, - "pg_start == 0x%.8lx, intel_private.gtt_entries == 0x%.8x\n", - pg_start, intel_private.gtt_entries); - - dev_info(&intel_private.pcidev->dev, - "trying to insert into local/stolen memory\n"); - goto out_err; - } - - if ((pg_start + mem->page_count) > num_entries) - goto out_err; - - /* The i915 can't check the GTT for entries since it's read only; - * depend on the caller to make the correct offset decisions. - */ - - if (type != mem->type) - goto out_err; - - mask_type = agp_bridge->driver->agp_type_to_mask_type(agp_bridge, type); - - if (!IS_SNB && mask_type != 0 && mask_type != AGP_PHYS_MEMORY && - mask_type != INTEL_AGP_CACHED_MEMORY) - goto out_err; - - if (!mem->is_flushed) - global_cache_flush(); - - intel_agp_insert_sg_entries(mem, pg_start, mask_type); - - out: - ret = 0; - out_err: - mem->is_flushed = true; - return ret; + /* Shift high bits down */ + addr |= (addr >> 28) & 0xf0; + writel(addr | I810_PTE_VALID, intel_private.gtt + entry); } -static int intel_i915_remove_entries(struct agp_memory *mem, off_t pg_start, - int type) +static bool gen6_check_flags(unsigned int flags) { - int i; - - if (mem->page_count == 0) - return 0; - - if (pg_start < intel_private.gtt_entries) { - dev_info(&intel_private.pcidev->dev, - "trying to disable local/stolen memory\n"); - return -EINVAL; - } - - for (i = pg_start; i < (mem->page_count + pg_start); i++) - writel(agp_bridge->scratch_page, intel_private.gtt+i); - - readl(intel_private.gtt+i-1); - - return 0; + return true; } -/* Return the aperture size by just checking the resource length. The effect - * described in the spec of the MSAC registers is just changing of the - * resource size. - */ -static int intel_i9xx_fetch_size(void) +static void gen6_write_entry(dma_addr_t addr, unsigned int entry, + unsigned int flags) { - int num_sizes = ARRAY_SIZE(intel_i830_sizes); - int aper_size; /* size in megabytes */ - int i; - - aper_size = pci_resource_len(intel_private.pcidev, 2) / MB(1); + unsigned int type_mask = flags & ~AGP_USER_CACHED_MEMORY_GFDT; + unsigned int gfdt = flags & AGP_USER_CACHED_MEMORY_GFDT; + u32 pte_flags; - for (i = 0; i < num_sizes; i++) { - if (aper_size == intel_i830_sizes[i].size) { - agp_bridge->current_size = intel_i830_sizes + i; - return aper_size; - } + if (type_mask == AGP_USER_UNCACHED_MEMORY) + pte_flags = GEN6_PTE_UNCACHED; + else if (type_mask == AGP_USER_CACHED_MEMORY_LLC_MLC) { + pte_flags = GEN6_PTE_LLC; + if (gfdt) + pte_flags |= GEN6_PTE_GFDT; + } else { /* set 'normal'/'cached' to LLC by default */ + pte_flags = GEN6_PTE_LLC_MLC; + if (gfdt) + pte_flags |= GEN6_PTE_GFDT; } - return 0; + /* gen6 has bit11-4 for physical addr bit39-32 */ + addr |= (addr >> 28) & 0xff0; + writel(addr | pte_flags, intel_private.gtt + entry); } -static int intel_i915_get_gtt_size(void) +static void gen6_cleanup(void) { - int size; - - if (IS_G33) { - u16 gmch_ctrl; - - /* G33's GTT size defined in gmch_ctrl */ - pci_read_config_word(agp_bridge->dev, I830_GMCH_CTRL, &gmch_ctrl); - switch (gmch_ctrl & I830_GMCH_GMS_MASK) { - case I830_GMCH_GMS_STOLEN_512: - size = 512; - break; - case I830_GMCH_GMS_STOLEN_1024: - size = 1024; - break; - case I830_GMCH_GMS_STOLEN_8192: - size = 8*1024; - break; - default: - dev_info(&agp_bridge->dev->dev, - "unknown page table size 0x%x, assuming 512KB\n", - (gmch_ctrl & I830_GMCH_GMS_MASK)); - size = 512; - } - } else { - /* On previous hardware, the GTT size was just what was - * required to map the aperture. - */ - size = agp_bridge->driver->fetch_size(); - } - - return KB(size); } -/* The intel i915 automatically initializes the agp aperture during POST. - * Use the memory already set aside for in the GTT. - */ -static int intel_i915_create_gatt_table(struct agp_bridge_data *bridge) +static int i9xx_setup(void) { - int page_order; - struct aper_size_info_fixed *size; - int num_entries; - u32 temp, temp2; - int gtt_map_size; - - size = agp_bridge->current_size; - page_order = size->page_order; - num_entries = size->num_entries; - agp_bridge->gatt_table_real = NULL; - - pci_read_config_dword(intel_private.pcidev, I915_MMADDR, &temp); - pci_read_config_dword(intel_private.pcidev, I915_PTEADDR, &temp2); + u32 reg_addr; - gtt_map_size = intel_i915_get_gtt_size(); - - intel_private.gtt = ioremap(temp2, gtt_map_size); - if (!intel_private.gtt) - return -ENOMEM; - - intel_private.gtt_total_size = gtt_map_size / 4; - - temp &= 0xfff80000; - - intel_private.registers = ioremap(temp, 128 * 4096); - if (!intel_private.registers) { - iounmap(intel_private.gtt); - return -ENOMEM; - } + pci_read_config_dword(intel_private.pcidev, I915_MMADDR, ®_addr); - temp = readl(intel_private.registers+I810_PGETBL_CTL) & 0xfffff000; - global_cache_flush(); /* FIXME: ? */ + reg_addr &= 0xfff80000; - /* we have to call this as early as possible after the MMIO base address is known */ - intel_i830_init_gtt_entries(); - if (intel_private.gtt_entries == 0) { - iounmap(intel_private.gtt); - iounmap(intel_private.registers); + intel_private.registers = ioremap(reg_addr, 128 * 4096); + if (!intel_private.registers) return -ENOMEM; - } - - agp_bridge->gatt_table = NULL; - - agp_bridge->gatt_bus_addr = temp; - - return 0; -} - -/* - * The i965 supports 36-bit physical addresses, but to keep - * the format of the GTT the same, the bits that don't fit - * in a 32-bit word are shifted down to bits 4..7. - * - * Gcc is smart enough to notice that "(addr >> 28) & 0xf0" - * is always zero on 32-bit architectures, so no need to make - * this conditional. - */ -static unsigned long intel_i965_mask_memory(struct agp_bridge_data *bridge, - dma_addr_t addr, int type) -{ - /* Shift high bits down */ - addr |= (addr >> 28) & 0xf0; - - /* Type checking must be done elsewhere */ - return addr | bridge->driver->masks[type].mask; -} -static unsigned long intel_gen6_mask_memory(struct agp_bridge_data *bridge, - dma_addr_t addr, int type) -{ - /* gen6 has bit11-4 for physical addr bit39-32 */ - addr |= (addr >> 28) & 0xff0; + if (INTEL_GTT_GEN == 3) { + u32 gtt_addr; - /* Type checking must be done elsewhere */ - return addr | bridge->driver->masks[type].mask; -} - -static void intel_i965_get_gtt_range(int *gtt_offset, int *gtt_size) -{ - u16 snb_gmch_ctl; - - switch (agp_bridge->dev->device) { - case PCI_DEVICE_ID_INTEL_GM45_HB: - case PCI_DEVICE_ID_INTEL_EAGLELAKE_HB: - case PCI_DEVICE_ID_INTEL_Q45_HB: - case PCI_DEVICE_ID_INTEL_G45_HB: - case PCI_DEVICE_ID_INTEL_G41_HB: - case PCI_DEVICE_ID_INTEL_B43_HB: - case PCI_DEVICE_ID_INTEL_IRONLAKE_D_HB: - case PCI_DEVICE_ID_INTEL_IRONLAKE_M_HB: - case PCI_DEVICE_ID_INTEL_IRONLAKE_MA_HB: - case PCI_DEVICE_ID_INTEL_IRONLAKE_MC2_HB: - *gtt_offset = *gtt_size = MB(2); - break; - case PCI_DEVICE_ID_INTEL_SANDYBRIDGE_HB: - case PCI_DEVICE_ID_INTEL_SANDYBRIDGE_M_HB: - case PCI_DEVICE_ID_INTEL_SANDYBRIDGE_S_HB: - *gtt_offset = MB(2); + pci_read_config_dword(intel_private.pcidev, + I915_PTEADDR, >t_addr); + intel_private.gtt_bus_addr = gtt_addr; + } else { + u32 gtt_offset; - pci_read_config_word(intel_private.pcidev, SNB_GMCH_CTRL, &snb_gmch_ctl); - switch (snb_gmch_ctl & SNB_GTT_SIZE_MASK) { - default: - case SNB_GTT_SIZE_0M: - printk(KERN_ERR "Bad GTT size mask: 0x%04x.\n", snb_gmch_ctl); - *gtt_size = MB(0); + switch (INTEL_GTT_GEN) { + case 5: + case 6: + gtt_offset = MB(2); break; - case SNB_GTT_SIZE_1M: - *gtt_size = MB(1); - break; - case SNB_GTT_SIZE_2M: - *gtt_size = MB(2); + case 4: + default: + gtt_offset = KB(512); break; } - break; - default: - *gtt_offset = *gtt_size = KB(512); + intel_private.gtt_bus_addr = reg_addr + gtt_offset; } -} - -/* The intel i965 automatically initializes the agp aperture during POST. - * Use the memory already set aside for in the GTT. - */ -static int intel_i965_create_gatt_table(struct agp_bridge_data *bridge) -{ - int page_order; - struct aper_size_info_fixed *size; - int num_entries; - u32 temp; - int gtt_offset, gtt_size; - - size = agp_bridge->current_size; - page_order = size->page_order; - num_entries = size->num_entries; - agp_bridge->gatt_table_real = NULL; - - pci_read_config_dword(intel_private.pcidev, I915_MMADDR, &temp); - - temp &= 0xfff00000; - - intel_i965_get_gtt_range(>t_offset, >t_size); - intel_private.gtt = ioremap((temp + gtt_offset) , gtt_size); + intel_private.pte_bus_addr = + readl(intel_private.registers+I810_PGETBL_CTL) & 0xfffff000; - if (!intel_private.gtt) - return -ENOMEM; - - intel_private.gtt_total_size = gtt_size / 4; - - intel_private.registers = ioremap(temp, 128 * 4096); - if (!intel_private.registers) { - iounmap(intel_private.gtt); - return -ENOMEM; - } - - temp = readl(intel_private.registers+I810_PGETBL_CTL) & 0xfffff000; - global_cache_flush(); /* FIXME: ? */ - - /* we have to call this as early as possible after the MMIO base address is known */ - intel_i830_init_gtt_entries(); - if (intel_private.gtt_entries == 0) { - iounmap(intel_private.gtt); - iounmap(intel_private.registers); - return -ENOMEM; - } - - agp_bridge->gatt_table = NULL; - - agp_bridge->gatt_bus_addr = temp; + intel_i9xx_setup_flush(); return 0; } @@ -1475,7 +1284,7 @@ static const struct agp_bridge_driver intel_810_driver = { .cleanup = intel_i810_cleanup, .mask_memory = intel_i810_mask_memory, .masks = intel_i810_masks, - .agp_enable = intel_i810_agp_enable, + .agp_enable = intel_fake_agp_enable, .cache_flush = global_cache_flush, .create_gatt_table = agp_generic_create_gatt_table, .free_gatt_table = agp_generic_free_gatt_table, @@ -1490,161 +1299,282 @@ static const struct agp_bridge_driver intel_810_driver = { .agp_type_to_mask_type = agp_generic_type_to_mask_type, }; -static const struct agp_bridge_driver intel_830_driver = { +static const struct agp_bridge_driver intel_fake_agp_driver = { .owner = THIS_MODULE, - .aperture_sizes = intel_i830_sizes, .size_type = FIXED_APER_SIZE, - .num_aperture_sizes = 4, - .needs_scratch_page = true, - .configure = intel_i830_configure, - .fetch_size = intel_i830_fetch_size, - .cleanup = intel_i830_cleanup, - .mask_memory = intel_i810_mask_memory, - .masks = intel_i810_masks, - .agp_enable = intel_i810_agp_enable, + .aperture_sizes = intel_fake_agp_sizes, + .num_aperture_sizes = ARRAY_SIZE(intel_fake_agp_sizes), + .configure = intel_fake_agp_configure, + .fetch_size = intel_fake_agp_fetch_size, + .cleanup = intel_gtt_cleanup, + .agp_enable = intel_fake_agp_enable, .cache_flush = global_cache_flush, - .create_gatt_table = intel_i830_create_gatt_table, - .free_gatt_table = intel_i830_free_gatt_table, - .insert_memory = intel_i830_insert_entries, - .remove_memory = intel_i830_remove_entries, - .alloc_by_type = intel_i830_alloc_by_type, + .create_gatt_table = intel_fake_agp_create_gatt_table, + .free_gatt_table = intel_fake_agp_free_gatt_table, + .insert_memory = intel_fake_agp_insert_entries, + .remove_memory = intel_fake_agp_remove_entries, + .alloc_by_type = intel_fake_agp_alloc_by_type, .free_by_type = intel_i810_free_by_type, .agp_alloc_page = agp_generic_alloc_page, .agp_alloc_pages = agp_generic_alloc_pages, .agp_destroy_page = agp_generic_destroy_page, .agp_destroy_pages = agp_generic_destroy_pages, - .agp_type_to_mask_type = intel_i830_type_to_mask_type, - .chipset_flush = intel_i830_chipset_flush, + .chipset_flush = intel_fake_agp_chipset_flush, }; -static const struct agp_bridge_driver intel_915_driver = { - .owner = THIS_MODULE, - .aperture_sizes = intel_i830_sizes, - .size_type = FIXED_APER_SIZE, - .num_aperture_sizes = 4, - .needs_scratch_page = true, - .configure = intel_i9xx_configure, - .fetch_size = intel_i9xx_fetch_size, - .cleanup = intel_i915_cleanup, - .mask_memory = intel_i810_mask_memory, - .masks = intel_i810_masks, - .agp_enable = intel_i810_agp_enable, - .cache_flush = global_cache_flush, - .create_gatt_table = intel_i915_create_gatt_table, - .free_gatt_table = intel_i830_free_gatt_table, - .insert_memory = intel_i915_insert_entries, - .remove_memory = intel_i915_remove_entries, - .alloc_by_type = intel_i830_alloc_by_type, - .free_by_type = intel_i810_free_by_type, - .agp_alloc_page = agp_generic_alloc_page, - .agp_alloc_pages = agp_generic_alloc_pages, - .agp_destroy_page = agp_generic_destroy_page, - .agp_destroy_pages = agp_generic_destroy_pages, - .agp_type_to_mask_type = intel_i830_type_to_mask_type, - .chipset_flush = intel_i915_chipset_flush, -#ifdef USE_PCI_DMA_API - .agp_map_page = intel_agp_map_page, - .agp_unmap_page = intel_agp_unmap_page, - .agp_map_memory = intel_agp_map_memory, - .agp_unmap_memory = intel_agp_unmap_memory, -#endif +static const struct intel_gtt_driver i81x_gtt_driver = { + .gen = 1, + .dma_mask_size = 32, }; - -static const struct agp_bridge_driver intel_i965_driver = { - .owner = THIS_MODULE, - .aperture_sizes = intel_i830_sizes, - .size_type = FIXED_APER_SIZE, - .num_aperture_sizes = 4, - .needs_scratch_page = true, - .configure = intel_i9xx_configure, - .fetch_size = intel_i9xx_fetch_size, - .cleanup = intel_i915_cleanup, - .mask_memory = intel_i965_mask_memory, - .masks = intel_i810_masks, - .agp_enable = intel_i810_agp_enable, - .cache_flush = global_cache_flush, - .create_gatt_table = intel_i965_create_gatt_table, - .free_gatt_table = intel_i830_free_gatt_table, - .insert_memory = intel_i915_insert_entries, - .remove_memory = intel_i915_remove_entries, - .alloc_by_type = intel_i830_alloc_by_type, - .free_by_type = intel_i810_free_by_type, - .agp_alloc_page = agp_generic_alloc_page, - .agp_alloc_pages = agp_generic_alloc_pages, - .agp_destroy_page = agp_generic_destroy_page, - .agp_destroy_pages = agp_generic_destroy_pages, - .agp_type_to_mask_type = intel_i830_type_to_mask_type, - .chipset_flush = intel_i915_chipset_flush, -#ifdef USE_PCI_DMA_API - .agp_map_page = intel_agp_map_page, - .agp_unmap_page = intel_agp_unmap_page, - .agp_map_memory = intel_agp_map_memory, - .agp_unmap_memory = intel_agp_unmap_memory, -#endif +static const struct intel_gtt_driver i8xx_gtt_driver = { + .gen = 2, + .setup = i830_setup, + .cleanup = i830_cleanup, + .write_entry = i830_write_entry, + .dma_mask_size = 32, + .check_flags = i830_check_flags, + .chipset_flush = i830_chipset_flush, }; - -static const struct agp_bridge_driver intel_gen6_driver = { - .owner = THIS_MODULE, - .aperture_sizes = intel_i830_sizes, - .size_type = FIXED_APER_SIZE, - .num_aperture_sizes = 4, - .needs_scratch_page = true, - .configure = intel_i9xx_configure, - .fetch_size = intel_i9xx_fetch_size, - .cleanup = intel_i915_cleanup, - .mask_memory = intel_gen6_mask_memory, - .masks = intel_gen6_masks, - .agp_enable = intel_i810_agp_enable, - .cache_flush = global_cache_flush, - .create_gatt_table = intel_i965_create_gatt_table, - .free_gatt_table = intel_i830_free_gatt_table, - .insert_memory = intel_i915_insert_entries, - .remove_memory = intel_i915_remove_entries, - .alloc_by_type = intel_i830_alloc_by_type, - .free_by_type = intel_i810_free_by_type, - .agp_alloc_page = agp_generic_alloc_page, - .agp_alloc_pages = agp_generic_alloc_pages, - .agp_destroy_page = agp_generic_destroy_page, - .agp_destroy_pages = agp_generic_destroy_pages, - .agp_type_to_mask_type = intel_gen6_type_to_mask_type, - .chipset_flush = intel_i915_chipset_flush, -#ifdef USE_PCI_DMA_API - .agp_map_page = intel_agp_map_page, - .agp_unmap_page = intel_agp_unmap_page, - .agp_map_memory = intel_agp_map_memory, - .agp_unmap_memory = intel_agp_unmap_memory, -#endif +static const struct intel_gtt_driver i915_gtt_driver = { + .gen = 3, + .setup = i9xx_setup, + .cleanup = i9xx_cleanup, + /* i945 is the last gpu to need phys mem (for overlay and cursors). */ + .write_entry = i830_write_entry, + .dma_mask_size = 32, + .check_flags = i830_check_flags, + .chipset_flush = i9xx_chipset_flush, +}; +static const struct intel_gtt_driver g33_gtt_driver = { + .gen = 3, + .is_g33 = 1, + .setup = i9xx_setup, + .cleanup = i9xx_cleanup, + .write_entry = i965_write_entry, + .dma_mask_size = 36, + .check_flags = i830_check_flags, + .chipset_flush = i9xx_chipset_flush, +}; +static const struct intel_gtt_driver pineview_gtt_driver = { + .gen = 3, + .is_pineview = 1, .is_g33 = 1, + .setup = i9xx_setup, + .cleanup = i9xx_cleanup, + .write_entry = i965_write_entry, + .dma_mask_size = 36, + .check_flags = i830_check_flags, + .chipset_flush = i9xx_chipset_flush, +}; +static const struct intel_gtt_driver i965_gtt_driver = { + .gen = 4, + .setup = i9xx_setup, + .cleanup = i9xx_cleanup, + .write_entry = i965_write_entry, + .dma_mask_size = 36, + .check_flags = i830_check_flags, + .chipset_flush = i9xx_chipset_flush, +}; +static const struct intel_gtt_driver g4x_gtt_driver = { + .gen = 5, + .setup = i9xx_setup, + .cleanup = i9xx_cleanup, + .write_entry = i965_write_entry, + .dma_mask_size = 36, + .check_flags = i830_check_flags, + .chipset_flush = i9xx_chipset_flush, +}; +static const struct intel_gtt_driver ironlake_gtt_driver = { + .gen = 5, + .is_ironlake = 1, + .setup = i9xx_setup, + .cleanup = i9xx_cleanup, + .write_entry = i965_write_entry, + .dma_mask_size = 36, + .check_flags = i830_check_flags, + .chipset_flush = i9xx_chipset_flush, +}; +static const struct intel_gtt_driver sandybridge_gtt_driver = { + .gen = 6, + .setup = i9xx_setup, + .cleanup = gen6_cleanup, + .write_entry = gen6_write_entry, + .dma_mask_size = 40, + .check_flags = gen6_check_flags, + .chipset_flush = i9xx_chipset_flush, }; -static const struct agp_bridge_driver intel_g33_driver = { - .owner = THIS_MODULE, - .aperture_sizes = intel_i830_sizes, - .size_type = FIXED_APER_SIZE, - .num_aperture_sizes = 4, - .needs_scratch_page = true, - .configure = intel_i9xx_configure, - .fetch_size = intel_i9xx_fetch_size, - .cleanup = intel_i915_cleanup, - .mask_memory = intel_i965_mask_memory, - .masks = intel_i810_masks, - .agp_enable = intel_i810_agp_enable, - .cache_flush = global_cache_flush, - .create_gatt_table = intel_i915_create_gatt_table, - .free_gatt_table = intel_i830_free_gatt_table, - .insert_memory = intel_i915_insert_entries, - .remove_memory = intel_i915_remove_entries, - .alloc_by_type = intel_i830_alloc_by_type, - .free_by_type = intel_i810_free_by_type, - .agp_alloc_page = agp_generic_alloc_page, - .agp_alloc_pages = agp_generic_alloc_pages, - .agp_destroy_page = agp_generic_destroy_page, - .agp_destroy_pages = agp_generic_destroy_pages, - .agp_type_to_mask_type = intel_i830_type_to_mask_type, - .chipset_flush = intel_i915_chipset_flush, -#ifdef USE_PCI_DMA_API - .agp_map_page = intel_agp_map_page, - .agp_unmap_page = intel_agp_unmap_page, - .agp_map_memory = intel_agp_map_memory, - .agp_unmap_memory = intel_agp_unmap_memory, -#endif +/* Table to describe Intel GMCH and AGP/PCIE GART drivers. At least one of + * driver and gmch_driver must be non-null, and find_gmch will determine + * which one should be used if a gmch_chip_id is present. + */ +static const struct intel_gtt_driver_description { + unsigned int gmch_chip_id; + char *name; + const struct agp_bridge_driver *gmch_driver; + const struct intel_gtt_driver *gtt_driver; +} intel_gtt_chipsets[] = { + { PCI_DEVICE_ID_INTEL_82810_IG1, "i810", &intel_810_driver, + &i81x_gtt_driver}, + { PCI_DEVICE_ID_INTEL_82810_IG3, "i810", &intel_810_driver, + &i81x_gtt_driver}, + { PCI_DEVICE_ID_INTEL_82810E_IG, "i810", &intel_810_driver, + &i81x_gtt_driver}, + { PCI_DEVICE_ID_INTEL_82815_CGC, "i815", &intel_810_driver, + &i81x_gtt_driver}, + { PCI_DEVICE_ID_INTEL_82830_CGC, "830M", + &intel_fake_agp_driver, &i8xx_gtt_driver}, + { PCI_DEVICE_ID_INTEL_82845G_IG, "830M", + &intel_fake_agp_driver, &i8xx_gtt_driver}, + { PCI_DEVICE_ID_INTEL_82854_IG, "854", + &intel_fake_agp_driver, &i8xx_gtt_driver}, + { PCI_DEVICE_ID_INTEL_82855GM_IG, "855GM", + &intel_fake_agp_driver, &i8xx_gtt_driver}, + { PCI_DEVICE_ID_INTEL_82865_IG, "865", + &intel_fake_agp_driver, &i8xx_gtt_driver}, + { PCI_DEVICE_ID_INTEL_E7221_IG, "E7221 (i915)", + &intel_fake_agp_driver, &i915_gtt_driver }, + { PCI_DEVICE_ID_INTEL_82915G_IG, "915G", + &intel_fake_agp_driver, &i915_gtt_driver }, + { PCI_DEVICE_ID_INTEL_82915GM_IG, "915GM", + &intel_fake_agp_driver, &i915_gtt_driver }, + { PCI_DEVICE_ID_INTEL_82945G_IG, "945G", + &intel_fake_agp_driver, &i915_gtt_driver }, + { PCI_DEVICE_ID_INTEL_82945GM_IG, "945GM", + &intel_fake_agp_driver, &i915_gtt_driver }, + { PCI_DEVICE_ID_INTEL_82945GME_IG, "945GME", + &intel_fake_agp_driver, &i915_gtt_driver }, + { PCI_DEVICE_ID_INTEL_82946GZ_IG, "946GZ", + &intel_fake_agp_driver, &i965_gtt_driver }, + { PCI_DEVICE_ID_INTEL_82G35_IG, "G35", + &intel_fake_agp_driver, &i965_gtt_driver }, + { PCI_DEVICE_ID_INTEL_82965Q_IG, "965Q", + &intel_fake_agp_driver, &i965_gtt_driver }, + { PCI_DEVICE_ID_INTEL_82965G_IG, "965G", + &intel_fake_agp_driver, &i965_gtt_driver }, + { PCI_DEVICE_ID_INTEL_82965GM_IG, "965GM", + &intel_fake_agp_driver, &i965_gtt_driver }, + { PCI_DEVICE_ID_INTEL_82965GME_IG, "965GME/GLE", + &intel_fake_agp_driver, &i965_gtt_driver }, + { PCI_DEVICE_ID_INTEL_G33_IG, "G33", + &intel_fake_agp_driver, &g33_gtt_driver }, + { PCI_DEVICE_ID_INTEL_Q35_IG, "Q35", + &intel_fake_agp_driver, &g33_gtt_driver }, + { PCI_DEVICE_ID_INTEL_Q33_IG, "Q33", + &intel_fake_agp_driver, &g33_gtt_driver }, + { PCI_DEVICE_ID_INTEL_PINEVIEW_M_IG, "GMA3150", + &intel_fake_agp_driver, &pineview_gtt_driver }, + { PCI_DEVICE_ID_INTEL_PINEVIEW_IG, "GMA3150", + &intel_fake_agp_driver, &pineview_gtt_driver }, + { PCI_DEVICE_ID_INTEL_GM45_IG, "GM45", + &intel_fake_agp_driver, &g4x_gtt_driver }, + { PCI_DEVICE_ID_INTEL_EAGLELAKE_IG, "Eaglelake", + &intel_fake_agp_driver, &g4x_gtt_driver }, + { PCI_DEVICE_ID_INTEL_Q45_IG, "Q45/Q43", + &intel_fake_agp_driver, &g4x_gtt_driver }, + { PCI_DEVICE_ID_INTEL_G45_IG, "G45/G43", + &intel_fake_agp_driver, &g4x_gtt_driver }, + { PCI_DEVICE_ID_INTEL_B43_IG, "B43", + &intel_fake_agp_driver, &g4x_gtt_driver }, + { PCI_DEVICE_ID_INTEL_B43_1_IG, "B43", + &intel_fake_agp_driver, &g4x_gtt_driver }, + { PCI_DEVICE_ID_INTEL_G41_IG, "G41", + &intel_fake_agp_driver, &g4x_gtt_driver }, + { PCI_DEVICE_ID_INTEL_IRONLAKE_D_IG, + "HD Graphics", &intel_fake_agp_driver, &ironlake_gtt_driver }, + { PCI_DEVICE_ID_INTEL_IRONLAKE_M_IG, + "HD Graphics", &intel_fake_agp_driver, &ironlake_gtt_driver }, + { PCI_DEVICE_ID_INTEL_SANDYBRIDGE_GT1_IG, + "Sandybridge", &intel_fake_agp_driver, &sandybridge_gtt_driver }, + { PCI_DEVICE_ID_INTEL_SANDYBRIDGE_GT2_IG, + "Sandybridge", &intel_fake_agp_driver, &sandybridge_gtt_driver }, + { PCI_DEVICE_ID_INTEL_SANDYBRIDGE_GT2_PLUS_IG, + "Sandybridge", &intel_fake_agp_driver, &sandybridge_gtt_driver }, + { PCI_DEVICE_ID_INTEL_SANDYBRIDGE_M_GT1_IG, + "Sandybridge", &intel_fake_agp_driver, &sandybridge_gtt_driver }, + { PCI_DEVICE_ID_INTEL_SANDYBRIDGE_M_GT2_IG, + "Sandybridge", &intel_fake_agp_driver, &sandybridge_gtt_driver }, + { PCI_DEVICE_ID_INTEL_SANDYBRIDGE_M_GT2_PLUS_IG, + "Sandybridge", &intel_fake_agp_driver, &sandybridge_gtt_driver }, + { PCI_DEVICE_ID_INTEL_SANDYBRIDGE_S_IG, + "Sandybridge", &intel_fake_agp_driver, &sandybridge_gtt_driver }, + { 0, NULL, NULL } }; + +static int find_gmch(u16 device) +{ + struct pci_dev *gmch_device; + + gmch_device = pci_get_device(PCI_VENDOR_ID_INTEL, device, NULL); + if (gmch_device && PCI_FUNC(gmch_device->devfn) != 0) { + gmch_device = pci_get_device(PCI_VENDOR_ID_INTEL, + device, gmch_device); + } + + if (!gmch_device) + return 0; + + intel_private.pcidev = gmch_device; + return 1; +} + +int intel_gmch_probe(struct pci_dev *pdev, + struct agp_bridge_data *bridge) +{ + int i, mask; + bridge->driver = NULL; + + for (i = 0; intel_gtt_chipsets[i].name != NULL; i++) { + if (find_gmch(intel_gtt_chipsets[i].gmch_chip_id)) { + bridge->driver = + intel_gtt_chipsets[i].gmch_driver; + intel_private.driver = + intel_gtt_chipsets[i].gtt_driver; + break; + } + } + + if (!bridge->driver) + return 0; + + bridge->dev_private_data = &intel_private; + bridge->dev = pdev; + + intel_private.bridge_dev = pci_dev_get(pdev); + + dev_info(&pdev->dev, "Intel %s Chipset\n", intel_gtt_chipsets[i].name); + + mask = intel_private.driver->dma_mask_size; + if (pci_set_dma_mask(intel_private.pcidev, DMA_BIT_MASK(mask))) + dev_err(&intel_private.pcidev->dev, + "set gfx device dma mask %d-bit failed!\n", mask); + else + pci_set_consistent_dma_mask(intel_private.pcidev, + DMA_BIT_MASK(mask)); + + if (bridge->driver == &intel_810_driver) + return 1; + + if (intel_gtt_init() != 0) + return 0; + + return 1; +} +EXPORT_SYMBOL(intel_gmch_probe); + +struct intel_gtt *intel_gtt_get(void) +{ + return &intel_private.base; +} +EXPORT_SYMBOL(intel_gtt_get); + +void intel_gmch_remove(struct pci_dev *pdev) +{ + if (intel_private.pcidev) + pci_dev_put(intel_private.pcidev); + if (intel_private.bridge_dev) + pci_dev_put(intel_private.bridge_dev); +} +EXPORT_SYMBOL(intel_gmch_remove); + +MODULE_AUTHOR("Dave Jones <davej@redhat.com>"); +MODULE_LICENSE("GPL and additional rights"); diff --git a/drivers/gpu/drm/drm_buffer.c b/drivers/gpu/drm/drm_buffer.c index 55d03ed0500..529a0dbe9fc 100644 --- a/drivers/gpu/drm/drm_buffer.c +++ b/drivers/gpu/drm/drm_buffer.c @@ -98,8 +98,8 @@ EXPORT_SYMBOL(drm_buffer_alloc); * user_data: A pointer the data that is copied to the buffer. * size: The Number of bytes to copy. */ -extern int drm_buffer_copy_from_user(struct drm_buffer *buf, - void __user *user_data, int size) +int drm_buffer_copy_from_user(struct drm_buffer *buf, + void __user *user_data, int size) { int nr_pages = size / PAGE_SIZE + 1; int idx; @@ -163,7 +163,7 @@ void *drm_buffer_read_object(struct drm_buffer *buf, { int idx = drm_buffer_index(buf); int page = drm_buffer_page(buf); - void *obj = 0; + void *obj = NULL; if (idx + objsize <= PAGE_SIZE) { obj = &buf->data[page][idx]; diff --git a/drivers/gpu/drm/drm_crtc.c b/drivers/gpu/drm/drm_crtc.c index 37e0b4fa482..6985cb1da72 100644 --- a/drivers/gpu/drm/drm_crtc.c +++ b/drivers/gpu/drm/drm_crtc.c @@ -1854,7 +1854,8 @@ int drm_mode_dirtyfb_ioctl(struct drm_device *dev, } if (fb->funcs->dirty) { - ret = fb->funcs->dirty(fb, flags, r->color, clips, num_clips); + ret = fb->funcs->dirty(fb, file_priv, flags, r->color, + clips, num_clips); } else { ret = -ENOSYS; goto out_err2; diff --git a/drivers/gpu/drm/drm_debugfs.c b/drivers/gpu/drm/drm_debugfs.c index 677b275fa72..9d8c892d07c 100644 --- a/drivers/gpu/drm/drm_debugfs.c +++ b/drivers/gpu/drm/drm_debugfs.c @@ -48,7 +48,6 @@ static struct drm_info_list drm_debugfs_list[] = { {"queues", drm_queues_info, 0}, {"bufs", drm_bufs_info, 0}, {"gem_names", drm_gem_name_info, DRIVER_GEM}, - {"gem_objects", drm_gem_object_info, DRIVER_GEM}, #if DRM_DEBUG_CODE {"vma", drm_vma_info, 0}, #endif diff --git a/drivers/gpu/drm/drm_edid.c b/drivers/gpu/drm/drm_edid.c index 96e96310822..fd033ebbdf8 100644 --- a/drivers/gpu/drm/drm_edid.c +++ b/drivers/gpu/drm/drm_edid.c @@ -30,7 +30,6 @@ #include <linux/kernel.h> #include <linux/slab.h> #include <linux/i2c.h> -#include <linux/i2c-algo-bit.h> #include "drmP.h" #include "drm_edid.h" #include "drm_edid_modes.h" diff --git a/drivers/gpu/drm/drm_gem.c b/drivers/gpu/drm/drm_gem.c index cff7317d383..ea1c4b019eb 100644 --- a/drivers/gpu/drm/drm_gem.c +++ b/drivers/gpu/drm/drm_gem.c @@ -92,12 +92,6 @@ drm_gem_init(struct drm_device *dev) spin_lock_init(&dev->object_name_lock); idr_init(&dev->object_name_idr); - atomic_set(&dev->object_count, 0); - atomic_set(&dev->object_memory, 0); - atomic_set(&dev->pin_count, 0); - atomic_set(&dev->pin_memory, 0); - atomic_set(&dev->gtt_count, 0); - atomic_set(&dev->gtt_memory, 0); mm = kzalloc(sizeof(struct drm_gem_mm), GFP_KERNEL); if (!mm) { @@ -148,12 +142,9 @@ int drm_gem_object_init(struct drm_device *dev, return -ENOMEM; kref_init(&obj->refcount); - kref_init(&obj->handlecount); + atomic_set(&obj->handle_count, 0); obj->size = size; - atomic_inc(&dev->object_count); - atomic_add(obj->size, &dev->object_memory); - return 0; } EXPORT_SYMBOL(drm_gem_object_init); @@ -180,8 +171,6 @@ drm_gem_object_alloc(struct drm_device *dev, size_t size) return obj; fput: /* Object_init mangles the global counters - readjust them. */ - atomic_dec(&dev->object_count); - atomic_sub(obj->size, &dev->object_memory); fput(obj->filp); free: kfree(obj); @@ -436,10 +425,7 @@ drm_gem_release(struct drm_device *dev, struct drm_file *file_private) void drm_gem_object_release(struct drm_gem_object *obj) { - struct drm_device *dev = obj->dev; fput(obj->filp); - atomic_dec(&dev->object_count); - atomic_sub(obj->size, &dev->object_memory); } EXPORT_SYMBOL(drm_gem_object_release); @@ -462,26 +448,6 @@ drm_gem_object_free(struct kref *kref) } EXPORT_SYMBOL(drm_gem_object_free); -/** - * Called after the last reference to the object has been lost. - * Must be called without holding struct_mutex - * - * Frees the object - */ -void -drm_gem_object_free_unlocked(struct kref *kref) -{ - struct drm_gem_object *obj = (struct drm_gem_object *) kref; - struct drm_device *dev = obj->dev; - - if (dev->driver->gem_free_object != NULL) { - mutex_lock(&dev->struct_mutex); - dev->driver->gem_free_object(obj); - mutex_unlock(&dev->struct_mutex); - } -} -EXPORT_SYMBOL(drm_gem_object_free_unlocked); - static void drm_gem_object_ref_bug(struct kref *list_kref) { BUG(); @@ -494,12 +460,8 @@ static void drm_gem_object_ref_bug(struct kref *list_kref) * called before drm_gem_object_free or we'll be touching * freed memory */ -void -drm_gem_object_handle_free(struct kref *kref) +void drm_gem_object_handle_free(struct drm_gem_object *obj) { - struct drm_gem_object *obj = container_of(kref, - struct drm_gem_object, - handlecount); struct drm_device *dev = obj->dev; /* Remove any name for this object */ @@ -526,6 +488,10 @@ void drm_gem_vm_open(struct vm_area_struct *vma) struct drm_gem_object *obj = vma->vm_private_data; drm_gem_object_reference(obj); + + mutex_lock(&obj->dev->struct_mutex); + drm_vm_open_locked(vma); + mutex_unlock(&obj->dev->struct_mutex); } EXPORT_SYMBOL(drm_gem_vm_open); @@ -533,7 +499,10 @@ void drm_gem_vm_close(struct vm_area_struct *vma) { struct drm_gem_object *obj = vma->vm_private_data; - drm_gem_object_unreference_unlocked(obj); + mutex_lock(&obj->dev->struct_mutex); + drm_vm_close_locked(vma); + drm_gem_object_unreference(obj); + mutex_unlock(&obj->dev->struct_mutex); } EXPORT_SYMBOL(drm_gem_vm_close); diff --git a/drivers/gpu/drm/drm_info.c b/drivers/gpu/drm/drm_info.c index 2ef2c782724..3cdbaf379bb 100644 --- a/drivers/gpu/drm/drm_info.c +++ b/drivers/gpu/drm/drm_info.c @@ -255,7 +255,7 @@ int drm_gem_one_name_info(int id, void *ptr, void *data) seq_printf(m, "%6d %8zd %7d %8d\n", obj->name, obj->size, - atomic_read(&obj->handlecount.refcount), + atomic_read(&obj->handle_count), atomic_read(&obj->refcount.refcount)); return 0; } @@ -270,20 +270,6 @@ int drm_gem_name_info(struct seq_file *m, void *data) return 0; } -int drm_gem_object_info(struct seq_file *m, void* data) -{ - struct drm_info_node *node = (struct drm_info_node *) m->private; - struct drm_device *dev = node->minor->dev; - - seq_printf(m, "%d objects\n", atomic_read(&dev->object_count)); - seq_printf(m, "%d object bytes\n", atomic_read(&dev->object_memory)); - seq_printf(m, "%d pinned\n", atomic_read(&dev->pin_count)); - seq_printf(m, "%d pin bytes\n", atomic_read(&dev->pin_memory)); - seq_printf(m, "%d gtt bytes\n", atomic_read(&dev->gtt_memory)); - seq_printf(m, "%d gtt total\n", dev->gtt_total); - return 0; -} - #if DRM_DEBUG_CODE int drm_vma_info(struct seq_file *m, void *data) diff --git a/drivers/gpu/drm/drm_lock.c b/drivers/gpu/drm/drm_lock.c index 1e28b907206..632ae243ede 100644 --- a/drivers/gpu/drm/drm_lock.c +++ b/drivers/gpu/drm/drm_lock.c @@ -152,6 +152,7 @@ int drm_lock(struct drm_device *dev, void *data, struct drm_file *file_priv) int drm_unlock(struct drm_device *dev, void *data, struct drm_file *file_priv) { struct drm_lock *lock = data; + struct drm_master *master = file_priv->master; if (lock->context == DRM_KERNEL_CONTEXT) { DRM_ERROR("Process %d using kernel context %d\n", @@ -161,6 +162,10 @@ int drm_unlock(struct drm_device *dev, void *data, struct drm_file *file_priv) atomic_inc(&dev->counts[_DRM_STAT_UNLOCKS]); + if (drm_lock_free(&master->lock, lock->context)) { + /* FIXME: Should really bail out here. */ + } + unblock_all_signals(); return 0; } diff --git a/drivers/gpu/drm/drm_proc.c b/drivers/gpu/drm/drm_proc.c index e571de536dc..9e5b07efebb 100644 --- a/drivers/gpu/drm/drm_proc.c +++ b/drivers/gpu/drm/drm_proc.c @@ -55,7 +55,6 @@ static struct drm_info_list drm_proc_list[] = { {"queues", drm_queues_info, 0}, {"bufs", drm_bufs_info, 0}, {"gem_names", drm_gem_name_info, DRIVER_GEM}, - {"gem_objects", drm_gem_object_info, DRIVER_GEM}, #if DRM_DEBUG_CODE {"vma", drm_vma_info, 0}, #endif diff --git a/drivers/gpu/drm/drm_vm.c b/drivers/gpu/drm/drm_vm.c index ee879d6bb52..2c3fcbdfd8f 100644 --- a/drivers/gpu/drm/drm_vm.c +++ b/drivers/gpu/drm/drm_vm.c @@ -433,15 +433,7 @@ static void drm_vm_open(struct vm_area_struct *vma) mutex_unlock(&dev->struct_mutex); } -/** - * \c close method for all virtual memory types. - * - * \param vma virtual memory area. - * - * Search the \p vma private data entry in drm_device::vmalist, unlink it, and - * free it. - */ -static void drm_vm_close(struct vm_area_struct *vma) +void drm_vm_close_locked(struct vm_area_struct *vma) { struct drm_file *priv = vma->vm_file->private_data; struct drm_device *dev = priv->minor->dev; @@ -451,7 +443,6 @@ static void drm_vm_close(struct vm_area_struct *vma) vma->vm_start, vma->vm_end - vma->vm_start); atomic_dec(&dev->vma_count); - mutex_lock(&dev->struct_mutex); list_for_each_entry_safe(pt, temp, &dev->vmalist, head) { if (pt->vma == vma) { list_del(&pt->head); @@ -459,6 +450,23 @@ static void drm_vm_close(struct vm_area_struct *vma) break; } } +} + +/** + * \c close method for all virtual memory types. + * + * \param vma virtual memory area. + * + * Search the \p vma private data entry in drm_device::vmalist, unlink it, and + * free it. + */ +static void drm_vm_close(struct vm_area_struct *vma) +{ + struct drm_file *priv = vma->vm_file->private_data; + struct drm_device *dev = priv->minor->dev; + + mutex_lock(&dev->struct_mutex); + drm_vm_close_locked(vma); mutex_unlock(&dev->struct_mutex); } diff --git a/drivers/gpu/drm/i810/i810_dma.c b/drivers/gpu/drm/i810/i810_dma.c index 61b4caf220f..fb07e73581e 100644 --- a/drivers/gpu/drm/i810/i810_dma.c +++ b/drivers/gpu/drm/i810/i810_dma.c @@ -116,7 +116,7 @@ static int i810_mmap_buffers(struct file *filp, struct vm_area_struct *vma) static const struct file_operations i810_buffer_fops = { .open = drm_open, .release = drm_release, - .unlocked_ioctl = drm_ioctl, + .unlocked_ioctl = i810_ioctl, .mmap = i810_mmap_buffers, .fasync = drm_fasync, }; diff --git a/drivers/gpu/drm/i830/i830_dma.c b/drivers/gpu/drm/i830/i830_dma.c index 671aa18415a..cc92c7e6236 100644 --- a/drivers/gpu/drm/i830/i830_dma.c +++ b/drivers/gpu/drm/i830/i830_dma.c @@ -118,7 +118,7 @@ static int i830_mmap_buffers(struct file *filp, struct vm_area_struct *vma) static const struct file_operations i830_buffer_fops = { .open = drm_open, .release = drm_release, - .unlocked_ioctl = drm_ioctl, + .unlocked_ioctl = i830_ioctl, .mmap = i830_mmap_buffers, .fasync = drm_fasync, }; diff --git a/drivers/gpu/drm/i915/Makefile b/drivers/gpu/drm/i915/Makefile index 5c8e53458ed..f6e98dd416c 100644 --- a/drivers/gpu/drm/i915/Makefile +++ b/drivers/gpu/drm/i915/Makefile @@ -26,13 +26,13 @@ i915-y := i915_drv.o i915_dma.o i915_irq.o i915_mem.o \ intel_dvo.o \ intel_ringbuffer.o \ intel_overlay.o \ + intel_opregion.o \ dvo_ch7xxx.o \ dvo_ch7017.o \ dvo_ivch.o \ dvo_tfp410.o \ dvo_sil164.o -i915-$(CONFIG_ACPI) += i915_opregion.o i915-$(CONFIG_COMPAT) += i915_ioc32.o obj-$(CONFIG_DRM_I915) += i915.o diff --git a/drivers/gpu/drm/i915/dvo_ch7017.c b/drivers/gpu/drm/i915/dvo_ch7017.c index 14d59804acd..af70337567c 100644 --- a/drivers/gpu/drm/i915/dvo_ch7017.c +++ b/drivers/gpu/drm/i915/dvo_ch7017.c @@ -165,67 +165,44 @@ struct ch7017_priv { static void ch7017_dump_regs(struct intel_dvo_device *dvo); static void ch7017_dpms(struct intel_dvo_device *dvo, int mode); -static bool ch7017_read(struct intel_dvo_device *dvo, int addr, uint8_t *val) +static bool ch7017_read(struct intel_dvo_device *dvo, u8 addr, u8 *val) { - struct i2c_adapter *adapter = dvo->i2c_bus; - struct intel_i2c_chan *i2cbus = container_of(adapter, struct intel_i2c_chan, adapter); - u8 out_buf[2]; - u8 in_buf[2]; - struct i2c_msg msgs[] = { { .addr = dvo->slave_addr, .flags = 0, .len = 1, - .buf = out_buf, + .buf = &addr, }, { .addr = dvo->slave_addr, .flags = I2C_M_RD, .len = 1, - .buf = in_buf, + .buf = val, } }; - - out_buf[0] = addr; - out_buf[1] = 0; - - if (i2c_transfer(&i2cbus->adapter, msgs, 2) == 2) { - *val= in_buf[0]; - return true; - }; - - return false; + return i2c_transfer(dvo->i2c_bus, msgs, 2) == 2; } -static bool ch7017_write(struct intel_dvo_device *dvo, int addr, uint8_t val) +static bool ch7017_write(struct intel_dvo_device *dvo, u8 addr, u8 val) { - struct i2c_adapter *adapter = dvo->i2c_bus; - struct intel_i2c_chan *i2cbus = container_of(adapter, struct intel_i2c_chan, adapter); - uint8_t out_buf[2]; + uint8_t buf[2] = { addr, val }; struct i2c_msg msg = { .addr = dvo->slave_addr, .flags = 0, .len = 2, - .buf = out_buf, + .buf = buf, }; - - out_buf[0] = addr; - out_buf[1] = val; - - if (i2c_transfer(&i2cbus->adapter, &msg, 1) == 1) - return true; - - return false; + return i2c_transfer(dvo->i2c_bus, &msg, 1) == 1; } /** Probes for a CH7017 on the given bus and slave address. */ static bool ch7017_init(struct intel_dvo_device *dvo, struct i2c_adapter *adapter) { - struct intel_i2c_chan *i2cbus = container_of(adapter, struct intel_i2c_chan, adapter); struct ch7017_priv *priv; - uint8_t val; + const char *str; + u8 val; priv = kzalloc(sizeof(struct ch7017_priv), GFP_KERNEL); if (priv == NULL) @@ -237,16 +214,27 @@ static bool ch7017_init(struct intel_dvo_device *dvo, if (!ch7017_read(dvo, CH7017_DEVICE_ID, &val)) goto fail; - if (val != CH7017_DEVICE_ID_VALUE && - val != CH7018_DEVICE_ID_VALUE && - val != CH7019_DEVICE_ID_VALUE) { + switch (val) { + case CH7017_DEVICE_ID_VALUE: + str = "ch7017"; + break; + case CH7018_DEVICE_ID_VALUE: + str = "ch7018"; + break; + case CH7019_DEVICE_ID_VALUE: + str = "ch7019"; + break; + default: DRM_DEBUG_KMS("ch701x not detected, got %d: from %s " - "Slave %d.\n", - val, i2cbus->adapter.name,dvo->slave_addr); + "slave %d.\n", + val, adapter->name,dvo->slave_addr); goto fail; } + DRM_DEBUG_KMS("%s detected on %s, addr %d\n", + str, adapter->name, dvo->slave_addr); return true; + fail: kfree(priv); return false; @@ -368,7 +356,7 @@ static void ch7017_dpms(struct intel_dvo_device *dvo, int mode) } /* XXX: Should actually wait for update power status somehow */ - udelay(20000); + msleep(20); } static void ch7017_dump_regs(struct intel_dvo_device *dvo) diff --git a/drivers/gpu/drm/i915/dvo_ch7xxx.c b/drivers/gpu/drm/i915/dvo_ch7xxx.c index 6f1944b2444..7eaa94e4ff0 100644 --- a/drivers/gpu/drm/i915/dvo_ch7xxx.c +++ b/drivers/gpu/drm/i915/dvo_ch7xxx.c @@ -113,7 +113,6 @@ static bool ch7xxx_readb(struct intel_dvo_device *dvo, int addr, uint8_t *ch) { struct ch7xxx_priv *ch7xxx= dvo->dev_priv; struct i2c_adapter *adapter = dvo->i2c_bus; - struct intel_i2c_chan *i2cbus = container_of(adapter, struct intel_i2c_chan, adapter); u8 out_buf[2]; u8 in_buf[2]; @@ -135,14 +134,14 @@ static bool ch7xxx_readb(struct intel_dvo_device *dvo, int addr, uint8_t *ch) out_buf[0] = addr; out_buf[1] = 0; - if (i2c_transfer(&i2cbus->adapter, msgs, 2) == 2) { + if (i2c_transfer(adapter, msgs, 2) == 2) { *ch = in_buf[0]; return true; }; if (!ch7xxx->quiet) { DRM_DEBUG_KMS("Unable to read register 0x%02x from %s:%02x.\n", - addr, i2cbus->adapter.name, dvo->slave_addr); + addr, adapter->name, dvo->slave_addr); } return false; } @@ -152,7 +151,6 @@ static bool ch7xxx_writeb(struct intel_dvo_device *dvo, int addr, uint8_t ch) { struct ch7xxx_priv *ch7xxx = dvo->dev_priv; struct i2c_adapter *adapter = dvo->i2c_bus; - struct intel_i2c_chan *i2cbus = container_of(adapter, struct intel_i2c_chan, adapter); uint8_t out_buf[2]; struct i2c_msg msg = { .addr = dvo->slave_addr, @@ -164,12 +162,12 @@ static bool ch7xxx_writeb(struct intel_dvo_device *dvo, int addr, uint8_t ch) out_buf[0] = addr; out_buf[1] = ch; - if (i2c_transfer(&i2cbus->adapter, &msg, 1) == 1) + if (i2c_transfer(adapter, &msg, 1) == 1) return true; if (!ch7xxx->quiet) { DRM_DEBUG_KMS("Unable to write register 0x%02x to %s:%d.\n", - addr, i2cbus->adapter.name, dvo->slave_addr); + addr, adapter->name, dvo->slave_addr); } return false; diff --git a/drivers/gpu/drm/i915/dvo_ivch.c b/drivers/gpu/drm/i915/dvo_ivch.c index a2ec3f48720..a12ed9414cc 100644 --- a/drivers/gpu/drm/i915/dvo_ivch.c +++ b/drivers/gpu/drm/i915/dvo_ivch.c @@ -167,7 +167,6 @@ static bool ivch_read(struct intel_dvo_device *dvo, int addr, uint16_t *data) { struct ivch_priv *priv = dvo->dev_priv; struct i2c_adapter *adapter = dvo->i2c_bus; - struct intel_i2c_chan *i2cbus = container_of(adapter, struct intel_i2c_chan, adapter); u8 out_buf[1]; u8 in_buf[2]; @@ -193,7 +192,7 @@ static bool ivch_read(struct intel_dvo_device *dvo, int addr, uint16_t *data) out_buf[0] = addr; - if (i2c_transfer(&i2cbus->adapter, msgs, 3) == 3) { + if (i2c_transfer(adapter, msgs, 3) == 3) { *data = (in_buf[1] << 8) | in_buf[0]; return true; }; @@ -201,7 +200,7 @@ static bool ivch_read(struct intel_dvo_device *dvo, int addr, uint16_t *data) if (!priv->quiet) { DRM_DEBUG_KMS("Unable to read register 0x%02x from " "%s:%02x.\n", - addr, i2cbus->adapter.name, dvo->slave_addr); + addr, adapter->name, dvo->slave_addr); } return false; } @@ -211,7 +210,6 @@ static bool ivch_write(struct intel_dvo_device *dvo, int addr, uint16_t data) { struct ivch_priv *priv = dvo->dev_priv; struct i2c_adapter *adapter = dvo->i2c_bus; - struct intel_i2c_chan *i2cbus = container_of(adapter, struct intel_i2c_chan, adapter); u8 out_buf[3]; struct i2c_msg msg = { .addr = dvo->slave_addr, @@ -224,12 +222,12 @@ static bool ivch_write(struct intel_dvo_device *dvo, int addr, uint16_t data) out_buf[1] = data & 0xff; out_buf[2] = data >> 8; - if (i2c_transfer(&i2cbus->adapter, &msg, 1) == 1) + if (i2c_transfer(adapter, &msg, 1) == 1) return true; if (!priv->quiet) { DRM_DEBUG_KMS("Unable to write register 0x%02x to %s:%d.\n", - addr, i2cbus->adapter.name, dvo->slave_addr); + addr, adapter->name, dvo->slave_addr); } return false; diff --git a/drivers/gpu/drm/i915/dvo_sil164.c b/drivers/gpu/drm/i915/dvo_sil164.c index 9b8e6765cf2..e4b4091df94 100644 --- a/drivers/gpu/drm/i915/dvo_sil164.c +++ b/drivers/gpu/drm/i915/dvo_sil164.c @@ -69,7 +69,6 @@ static bool sil164_readb(struct intel_dvo_device *dvo, int addr, uint8_t *ch) { struct sil164_priv *sil = dvo->dev_priv; struct i2c_adapter *adapter = dvo->i2c_bus; - struct intel_i2c_chan *i2cbus = container_of(adapter, struct intel_i2c_chan, adapter); u8 out_buf[2]; u8 in_buf[2]; @@ -91,14 +90,14 @@ static bool sil164_readb(struct intel_dvo_device *dvo, int addr, uint8_t *ch) out_buf[0] = addr; out_buf[1] = 0; - if (i2c_transfer(&i2cbus->adapter, msgs, 2) == 2) { + if (i2c_transfer(adapter, msgs, 2) == 2) { *ch = in_buf[0]; return true; }; if (!sil->quiet) { DRM_DEBUG_KMS("Unable to read register 0x%02x from %s:%02x.\n", - addr, i2cbus->adapter.name, dvo->slave_addr); + addr, adapter->name, dvo->slave_addr); } return false; } @@ -107,7 +106,6 @@ static bool sil164_writeb(struct intel_dvo_device *dvo, int addr, uint8_t ch) { struct sil164_priv *sil= dvo->dev_priv; struct i2c_adapter *adapter = dvo->i2c_bus; - struct intel_i2c_chan *i2cbus = container_of(adapter, struct intel_i2c_chan, adapter); uint8_t out_buf[2]; struct i2c_msg msg = { .addr = dvo->slave_addr, @@ -119,12 +117,12 @@ static bool sil164_writeb(struct intel_dvo_device *dvo, int addr, uint8_t ch) out_buf[0] = addr; out_buf[1] = ch; - if (i2c_transfer(&i2cbus->adapter, &msg, 1) == 1) + if (i2c_transfer(adapter, &msg, 1) == 1) return true; if (!sil->quiet) { DRM_DEBUG_KMS("Unable to write register 0x%02x to %s:%d.\n", - addr, i2cbus->adapter.name, dvo->slave_addr); + addr, adapter->name, dvo->slave_addr); } return false; diff --git a/drivers/gpu/drm/i915/dvo_tfp410.c b/drivers/gpu/drm/i915/dvo_tfp410.c index 56f66426207..8ab2855bb54 100644 --- a/drivers/gpu/drm/i915/dvo_tfp410.c +++ b/drivers/gpu/drm/i915/dvo_tfp410.c @@ -94,7 +94,6 @@ static bool tfp410_readb(struct intel_dvo_device *dvo, int addr, uint8_t *ch) { struct tfp410_priv *tfp = dvo->dev_priv; struct i2c_adapter *adapter = dvo->i2c_bus; - struct intel_i2c_chan *i2cbus = container_of(adapter, struct intel_i2c_chan, adapter); u8 out_buf[2]; u8 in_buf[2]; @@ -116,14 +115,14 @@ static bool tfp410_readb(struct intel_dvo_device *dvo, int addr, uint8_t *ch) out_buf[0] = addr; out_buf[1] = 0; - if (i2c_transfer(&i2cbus->adapter, msgs, 2) == 2) { + if (i2c_transfer(adapter, msgs, 2) == 2) { *ch = in_buf[0]; return true; }; if (!tfp->quiet) { DRM_DEBUG_KMS("Unable to read register 0x%02x from %s:%02x.\n", - addr, i2cbus->adapter.name, dvo->slave_addr); + addr, adapter->name, dvo->slave_addr); } return false; } @@ -132,7 +131,6 @@ static bool tfp410_writeb(struct intel_dvo_device *dvo, int addr, uint8_t ch) { struct tfp410_priv *tfp = dvo->dev_priv; struct i2c_adapter *adapter = dvo->i2c_bus; - struct intel_i2c_chan *i2cbus = container_of(adapter, struct intel_i2c_chan, adapter); uint8_t out_buf[2]; struct i2c_msg msg = { .addr = dvo->slave_addr, @@ -144,12 +142,12 @@ static bool tfp410_writeb(struct intel_dvo_device *dvo, int addr, uint8_t ch) out_buf[0] = addr; out_buf[1] = ch; - if (i2c_transfer(&i2cbus->adapter, &msg, 1) == 1) + if (i2c_transfer(adapter, &msg, 1) == 1) return true; if (!tfp->quiet) { DRM_DEBUG_KMS("Unable to write register 0x%02x to %s:%d.\n", - addr, i2cbus->adapter.name, dvo->slave_addr); + addr, adapter->name, dvo->slave_addr); } return false; diff --git a/drivers/gpu/drm/i915/i915_debugfs.c b/drivers/gpu/drm/i915/i915_debugfs.c index 5e43d707678..d598070fb27 100644 --- a/drivers/gpu/drm/i915/i915_debugfs.c +++ b/drivers/gpu/drm/i915/i915_debugfs.c @@ -40,9 +40,51 @@ #if defined(CONFIG_DEBUG_FS) -#define ACTIVE_LIST 1 -#define FLUSHING_LIST 2 -#define INACTIVE_LIST 3 +enum { + RENDER_LIST, + BSD_LIST, + FLUSHING_LIST, + INACTIVE_LIST, + PINNED_LIST, + DEFERRED_FREE_LIST, +}; + +static const char *yesno(int v) +{ + return v ? "yes" : "no"; +} + +static int i915_capabilities(struct seq_file *m, void *data) +{ + struct drm_info_node *node = (struct drm_info_node *) m->private; + struct drm_device *dev = node->minor->dev; + const struct intel_device_info *info = INTEL_INFO(dev); + + seq_printf(m, "gen: %d\n", info->gen); +#define B(x) seq_printf(m, #x ": %s\n", yesno(info->x)) + B(is_mobile); + B(is_i85x); + B(is_i915g); + B(is_i945gm); + B(is_g33); + B(need_gfx_hws); + B(is_g4x); + B(is_pineview); + B(is_broadwater); + B(is_crestline); + B(is_ironlake); + B(has_fbc); + B(has_rc6); + B(has_pipe_cxsr); + B(has_hotplug); + B(cursor_needs_physical); + B(has_overlay); + B(overlay_needs_physical); + B(supports_tv); +#undef B + + return 0; +} static const char *get_pin_flag(struct drm_i915_gem_object *obj_priv) { @@ -64,6 +106,27 @@ static const char *get_tiling_flag(struct drm_i915_gem_object *obj_priv) } } +static void +describe_obj(struct seq_file *m, struct drm_i915_gem_object *obj) +{ + seq_printf(m, "%p: %s%s %8zd %08x %08x %d%s%s", + &obj->base, + get_pin_flag(obj), + get_tiling_flag(obj), + obj->base.size, + obj->base.read_domains, + obj->base.write_domain, + obj->last_rendering_seqno, + obj->dirty ? " dirty" : "", + obj->madv == I915_MADV_DONTNEED ? " purgeable" : ""); + if (obj->base.name) + seq_printf(m, " (name: %d)", obj->base.name); + if (obj->fence_reg != I915_FENCE_REG_NONE) + seq_printf(m, " (fence: %d)", obj->fence_reg); + if (obj->gtt_space != NULL) + seq_printf(m, " (gtt_offset: %08x)", obj->gtt_offset); +} + static int i915_gem_object_list_info(struct seq_file *m, void *data) { struct drm_info_node *node = (struct drm_info_node *) m->private; @@ -72,56 +135,84 @@ static int i915_gem_object_list_info(struct seq_file *m, void *data) struct drm_device *dev = node->minor->dev; drm_i915_private_t *dev_priv = dev->dev_private; struct drm_i915_gem_object *obj_priv; - spinlock_t *lock = NULL; + size_t total_obj_size, total_gtt_size; + int count, ret; + + ret = mutex_lock_interruptible(&dev->struct_mutex); + if (ret) + return ret; switch (list) { - case ACTIVE_LIST: - seq_printf(m, "Active:\n"); - lock = &dev_priv->mm.active_list_lock; + case RENDER_LIST: + seq_printf(m, "Render:\n"); head = &dev_priv->render_ring.active_list; break; + case BSD_LIST: + seq_printf(m, "BSD:\n"); + head = &dev_priv->bsd_ring.active_list; + break; case INACTIVE_LIST: seq_printf(m, "Inactive:\n"); head = &dev_priv->mm.inactive_list; break; + case PINNED_LIST: + seq_printf(m, "Pinned:\n"); + head = &dev_priv->mm.pinned_list; + break; case FLUSHING_LIST: seq_printf(m, "Flushing:\n"); head = &dev_priv->mm.flushing_list; break; + case DEFERRED_FREE_LIST: + seq_printf(m, "Deferred free:\n"); + head = &dev_priv->mm.deferred_free_list; + break; default: - DRM_INFO("Ooops, unexpected list\n"); - return 0; + mutex_unlock(&dev->struct_mutex); + return -EINVAL; } - if (lock) - spin_lock(lock); - list_for_each_entry(obj_priv, head, list) - { - seq_printf(m, " %p: %s %8zd %08x %08x %d%s%s", - &obj_priv->base, - get_pin_flag(obj_priv), - obj_priv->base.size, - obj_priv->base.read_domains, - obj_priv->base.write_domain, - obj_priv->last_rendering_seqno, - obj_priv->dirty ? " dirty" : "", - obj_priv->madv == I915_MADV_DONTNEED ? " purgeable" : ""); - - if (obj_priv->base.name) - seq_printf(m, " (name: %d)", obj_priv->base.name); - if (obj_priv->fence_reg != I915_FENCE_REG_NONE) - seq_printf(m, " (fence: %d)", obj_priv->fence_reg); - if (obj_priv->gtt_space != NULL) - seq_printf(m, " (gtt_offset: %08x)", obj_priv->gtt_offset); - + total_obj_size = total_gtt_size = count = 0; + list_for_each_entry(obj_priv, head, list) { + seq_printf(m, " "); + describe_obj(m, obj_priv); seq_printf(m, "\n"); + total_obj_size += obj_priv->base.size; + total_gtt_size += obj_priv->gtt_space->size; + count++; } + mutex_unlock(&dev->struct_mutex); - if (lock) - spin_unlock(lock); + seq_printf(m, "Total %d objects, %zu bytes, %zu GTT size\n", + count, total_obj_size, total_gtt_size); return 0; } +static int i915_gem_object_info(struct seq_file *m, void* data) +{ + struct drm_info_node *node = (struct drm_info_node *) m->private; + struct drm_device *dev = node->minor->dev; + struct drm_i915_private *dev_priv = dev->dev_private; + int ret; + + ret = mutex_lock_interruptible(&dev->struct_mutex); + if (ret) + return ret; + + seq_printf(m, "%u objects\n", dev_priv->mm.object_count); + seq_printf(m, "%zu object bytes\n", dev_priv->mm.object_memory); + seq_printf(m, "%u pinned\n", dev_priv->mm.pin_count); + seq_printf(m, "%zu pin bytes\n", dev_priv->mm.pin_memory); + seq_printf(m, "%u objects in gtt\n", dev_priv->mm.gtt_count); + seq_printf(m, "%zu gtt bytes\n", dev_priv->mm.gtt_memory); + seq_printf(m, "%zu gtt total\n", dev_priv->mm.gtt_total); + + mutex_unlock(&dev->struct_mutex); + + return 0; +} + + static int i915_gem_pageflip_info(struct seq_file *m, void *data) { struct drm_info_node *node = (struct drm_info_node *) m->private; @@ -176,6 +267,11 @@ static int i915_gem_request_info(struct seq_file *m, void *data) struct drm_device *dev = node->minor->dev; drm_i915_private_t *dev_priv = dev->dev_private; struct drm_i915_gem_request *gem_request; + int ret; + + ret = mutex_lock_interruptible(&dev->struct_mutex); + if (ret) + return ret; seq_printf(m, "Request:\n"); list_for_each_entry(gem_request, &dev_priv->render_ring.request_list, @@ -184,6 +280,8 @@ static int i915_gem_request_info(struct seq_file *m, void *data) gem_request->seqno, (int) (jiffies - gem_request->emitted_jiffies)); } + mutex_unlock(&dev->struct_mutex); + return 0; } @@ -192,16 +290,24 @@ static int i915_gem_seqno_info(struct seq_file *m, void *data) 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; + + ret = mutex_lock_interruptible(&dev->struct_mutex); + if (ret) + return ret; if (dev_priv->render_ring.status_page.page_addr != NULL) { seq_printf(m, "Current sequence: %d\n", - i915_get_gem_seqno(dev, &dev_priv->render_ring)); + dev_priv->render_ring.get_seqno(dev, &dev_priv->render_ring)); } else { seq_printf(m, "Current sequence: hws uninitialized\n"); } seq_printf(m, "Waiter sequence: %d\n", dev_priv->mm.waiting_gem_seqno); seq_printf(m, "IRQ sequence: %d\n", dev_priv->mm.irq_gem_seqno); + + mutex_unlock(&dev->struct_mutex); + return 0; } @@ -211,6 +317,11 @@ static int i915_interrupt_info(struct seq_file *m, void *data) 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; + + ret = mutex_lock_interruptible(&dev->struct_mutex); + if (ret) + return ret; if (!HAS_PCH_SPLIT(dev)) { seq_printf(m, "Interrupt enable: %08x\n", @@ -247,7 +358,7 @@ static int i915_interrupt_info(struct seq_file *m, void *data) atomic_read(&dev_priv->irq_received)); if (dev_priv->render_ring.status_page.page_addr != NULL) { seq_printf(m, "Current sequence: %d\n", - i915_get_gem_seqno(dev, &dev_priv->render_ring)); + dev_priv->render_ring.get_seqno(dev, &dev_priv->render_ring)); } else { seq_printf(m, "Current sequence: hws uninitialized\n"); } @@ -255,6 +366,8 @@ static int i915_interrupt_info(struct seq_file *m, void *data) dev_priv->mm.waiting_gem_seqno); seq_printf(m, "IRQ sequence: %d\n", dev_priv->mm.irq_gem_seqno); + mutex_unlock(&dev->struct_mutex); + return 0; } @@ -263,7 +376,11 @@ static int i915_gem_fence_regs_info(struct seq_file *m, void *data) 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 i; + int i, ret; + + ret = mutex_lock_interruptible(&dev->struct_mutex); + if (ret) + return ret; seq_printf(m, "Reserved fences = %d\n", dev_priv->fence_reg_start); seq_printf(m, "Total fences = %d\n", dev_priv->num_fence_regs); @@ -289,6 +406,7 @@ static int i915_gem_fence_regs_info(struct seq_file *m, void *data) seq_printf(m, "\n"); } } + mutex_unlock(&dev->struct_mutex); return 0; } @@ -313,16 +431,19 @@ static int i915_hws_info(struct seq_file *m, void *data) return 0; } -static void i915_dump_pages(struct seq_file *m, struct page **pages, int page_count) +static void i915_dump_object(struct seq_file *m, + struct io_mapping *mapping, + struct drm_i915_gem_object *obj_priv) { - int page, i; - uint32_t *mem; + int page, page_count, i; + page_count = obj_priv->base.size / PAGE_SIZE; for (page = 0; page < page_count; page++) { - mem = kmap_atomic(pages[page], KM_USER0); + u32 *mem = io_mapping_map_wc(mapping, + obj_priv->gtt_offset + page * PAGE_SIZE); for (i = 0; i < PAGE_SIZE; i += 4) seq_printf(m, "%08x : %08x\n", i, mem[i / 4]); - kunmap_atomic(mem, KM_USER0); + io_mapping_unmap(mem); } } @@ -335,27 +456,21 @@ static int i915_batchbuffer_info(struct seq_file *m, void *data) struct drm_i915_gem_object *obj_priv; int ret; - spin_lock(&dev_priv->mm.active_list_lock); + ret = mutex_lock_interruptible(&dev->struct_mutex); + if (ret) + return ret; list_for_each_entry(obj_priv, &dev_priv->render_ring.active_list, list) { obj = &obj_priv->base; if (obj->read_domains & I915_GEM_DOMAIN_COMMAND) { - ret = i915_gem_object_get_pages(obj, 0); - if (ret) { - DRM_ERROR("Failed to get pages: %d\n", ret); - spin_unlock(&dev_priv->mm.active_list_lock); - return ret; - } - - seq_printf(m, "--- gtt_offset = 0x%08x\n", obj_priv->gtt_offset); - i915_dump_pages(m, obj_priv->pages, obj->size / PAGE_SIZE); - - i915_gem_object_put_pages(obj); + seq_printf(m, "--- gtt_offset = 0x%08x\n", + obj_priv->gtt_offset); + i915_dump_object(m, dev_priv->mm.gtt_mapping, obj_priv); } } - spin_unlock(&dev_priv->mm.active_list_lock); + mutex_unlock(&dev->struct_mutex); return 0; } @@ -365,20 +480,24 @@ static int i915_ringbuffer_data(struct seq_file *m, void *data) 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; - u8 *virt; - uint32_t *ptr, off; + int ret; + + ret = mutex_lock_interruptible(&dev->struct_mutex); + if (ret) + return ret; if (!dev_priv->render_ring.gem_object) { seq_printf(m, "No ringbuffer setup\n"); - return 0; - } - - virt = dev_priv->render_ring.virtual_start; + } else { + u8 *virt = dev_priv->render_ring.virtual_start; + uint32_t off; - for (off = 0; off < dev_priv->render_ring.size; off += 4) { - ptr = (uint32_t *)(virt + off); - seq_printf(m, "%08x : %08x\n", off, *ptr); + for (off = 0; off < dev_priv->render_ring.size; off += 4) { + uint32_t *ptr = (uint32_t *)(virt + off); + seq_printf(m, "%08x : %08x\n", off, *ptr); + } } + mutex_unlock(&dev->struct_mutex); return 0; } @@ -396,7 +515,7 @@ static int i915_ringbuffer_info(struct seq_file *m, void *data) seq_printf(m, "RingHead : %08x\n", head); seq_printf(m, "RingTail : %08x\n", tail); seq_printf(m, "RingSize : %08lx\n", dev_priv->render_ring.size); - seq_printf(m, "Acthd : %08x\n", I915_READ(IS_I965G(dev) ? ACTHD_I965 : ACTHD)); + seq_printf(m, "Acthd : %08x\n", I915_READ(INTEL_INFO(dev)->gen >= 4 ? ACTHD_I965 : ACTHD)); return 0; } @@ -458,7 +577,7 @@ static int i915_error_state(struct seq_file *m, void *unused) seq_printf(m, " IPEHR: 0x%08x\n", error->ipehr); seq_printf(m, " INSTDONE: 0x%08x\n", error->instdone); seq_printf(m, " ACTHD: 0x%08x\n", error->acthd); - if (IS_I965G(dev)) { + if (INTEL_INFO(dev)->gen >= 4) { seq_printf(m, " INSTPS: 0x%08x\n", error->instps); seq_printf(m, " INSTDONE1: 0x%08x\n", error->instdone1); } @@ -642,6 +761,9 @@ static int i915_fbc_status(struct seq_file *m, void *unused) } else { seq_printf(m, "FBC disabled: "); switch (dev_priv->no_fbc_reason) { + case FBC_NO_OUTPUT: + seq_printf(m, "no outputs"); + break; case FBC_STOLEN_TOO_SMALL: seq_printf(m, "not enough stolen memory"); break; @@ -675,15 +797,17 @@ static int i915_sr_status(struct seq_file *m, void *unused) drm_i915_private_t *dev_priv = dev->dev_private; bool sr_enabled = false; - if (IS_I965GM(dev) || IS_I945G(dev) || IS_I945GM(dev)) + if (IS_IRONLAKE(dev)) + sr_enabled = I915_READ(WM1_LP_ILK) & WM1_LP_SR_EN; + else if (IS_CRESTLINE(dev) || IS_I945G(dev) || IS_I945GM(dev)) sr_enabled = I915_READ(FW_BLC_SELF) & FW_BLC_SELF_EN; else if (IS_I915GM(dev)) sr_enabled = I915_READ(INSTPM) & INSTPM_SELF_EN; else if (IS_PINEVIEW(dev)) sr_enabled = I915_READ(DSPFW3) & PINEVIEW_SELF_REFRESH_EN; - seq_printf(m, "self-refresh: %s\n", sr_enabled ? "enabled" : - "disabled"); + seq_printf(m, "self-refresh: %s\n", + sr_enabled ? "enabled" : "disabled"); return 0; } @@ -694,10 +818,16 @@ static int i915_emon_status(struct seq_file *m, void *unused) struct drm_device *dev = node->minor->dev; drm_i915_private_t *dev_priv = dev->dev_private; unsigned long temp, chipset, gfx; + int ret; + + ret = mutex_lock_interruptible(&dev->struct_mutex); + if (ret) + return ret; temp = i915_mch_val(dev_priv); chipset = i915_chipset_val(dev_priv); gfx = i915_gfx_val(dev_priv); + mutex_unlock(&dev->struct_mutex); seq_printf(m, "GMCH temp: %ld\n", temp); seq_printf(m, "Chipset power: %ld\n", chipset); @@ -718,6 +848,68 @@ static int i915_gfxec(struct seq_file *m, void *unused) return 0; } +static int i915_opregion(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; + struct intel_opregion *opregion = &dev_priv->opregion; + int ret; + + ret = mutex_lock_interruptible(&dev->struct_mutex); + if (ret) + return ret; + + if (opregion->header) + seq_write(m, opregion->header, OPREGION_SIZE); + + mutex_unlock(&dev->struct_mutex); + + return 0; +} + +static int i915_gem_framebuffer_info(struct seq_file *m, void *data) +{ + 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; + struct intel_fbdev *ifbdev; + struct intel_framebuffer *fb; + int ret; + + ret = mutex_lock_interruptible(&dev->mode_config.mutex); + if (ret) + return ret; + + ifbdev = dev_priv->fbdev; + fb = to_intel_framebuffer(ifbdev->helper.fb); + + seq_printf(m, "fbcon size: %d x %d, depth %d, %d bpp, obj ", + fb->base.width, + fb->base.height, + fb->base.depth, + fb->base.bits_per_pixel); + describe_obj(m, to_intel_bo(fb->obj)); + seq_printf(m, "\n"); + + 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 ", + fb->base.width, + fb->base.height, + fb->base.depth, + fb->base.bits_per_pixel); + describe_obj(m, to_intel_bo(fb->obj)); + seq_printf(m, "\n"); + } + + mutex_unlock(&dev->mode_config.mutex); + + return 0; +} + static int i915_wedged_open(struct inode *inode, struct file *filp) @@ -741,6 +933,9 @@ i915_wedged_read(struct file *filp, "wedged : %d\n", atomic_read(&dev_priv->mm.wedged)); + if (len > sizeof (buf)) + len = sizeof (buf); + return simple_read_from_buffer(ubuf, max, ppos, buf, len); } @@ -770,7 +965,7 @@ i915_wedged_write(struct file *filp, atomic_set(&dev_priv->mm.wedged, val); if (val) { - DRM_WAKEUP(&dev_priv->irq_queue); + wake_up_all(&dev_priv->irq_queue); queue_work(dev_priv->wq, &dev_priv->error_work); } @@ -823,9 +1018,14 @@ static int i915_wedged_create(struct dentry *root, struct drm_minor *minor) } static struct drm_info_list i915_debugfs_list[] = { - {"i915_gem_active", i915_gem_object_list_info, 0, (void *) ACTIVE_LIST}, + {"i915_capabilities", i915_capabilities, 0, 0}, + {"i915_gem_objects", i915_gem_object_info, 0}, + {"i915_gem_render_active", i915_gem_object_list_info, 0, (void *) RENDER_LIST}, + {"i915_gem_bsd_active", i915_gem_object_list_info, 0, (void *) BSD_LIST}, {"i915_gem_flushing", i915_gem_object_list_info, 0, (void *) FLUSHING_LIST}, {"i915_gem_inactive", i915_gem_object_list_info, 0, (void *) INACTIVE_LIST}, + {"i915_gem_pinned", i915_gem_object_list_info, 0, (void *) PINNED_LIST}, + {"i915_gem_deferred_free", i915_gem_object_list_info, 0, (void *) DEFERRED_FREE_LIST}, {"i915_gem_pageflip", i915_gem_pageflip_info, 0}, {"i915_gem_request", i915_gem_request_info, 0}, {"i915_gem_seqno", i915_gem_seqno_info, 0}, @@ -845,6 +1045,8 @@ static struct drm_info_list i915_debugfs_list[] = { {"i915_gfxec", i915_gfxec, 0}, {"i915_fbc_status", i915_fbc_status, 0}, {"i915_sr_status", i915_sr_status, 0}, + {"i915_opregion", i915_opregion, 0}, + {"i915_gem_framebuffer", i915_gem_framebuffer_info, 0}, }; #define I915_DEBUGFS_ENTRIES ARRAY_SIZE(i915_debugfs_list) diff --git a/drivers/gpu/drm/i915/i915_dma.c b/drivers/gpu/drm/i915/i915_dma.c index 9d67b485303..726c3736082 100644 --- a/drivers/gpu/drm/i915/i915_dma.c +++ b/drivers/gpu/drm/i915/i915_dma.c @@ -40,8 +40,7 @@ #include <linux/pnp.h> #include <linux/vga_switcheroo.h> #include <linux/slab.h> - -extern int intel_max_stolen; /* from AGP driver */ +#include <acpi/video.h> /** * Sets up the hardware status page for devices that need a physical address @@ -64,7 +63,7 @@ static int i915_init_phys_hws(struct drm_device *dev) memset(dev_priv->render_ring.status_page.page_addr, 0, PAGE_SIZE); - if (IS_I965G(dev)) + if (INTEL_INFO(dev)->gen >= 4) dev_priv->dma_status_page |= (dev_priv->dma_status_page >> 28) & 0xf0; @@ -222,7 +221,7 @@ static int i915_dma_resume(struct drm_device * dev) DRM_DEBUG_DRIVER("hw status page @ %p\n", ring->status_page.page_addr); if (ring->status_page.gfx_addr != 0) - ring->setup_status_page(dev, ring); + intel_ring_setup_status_page(dev, ring); else I915_WRITE(HWS_PGA, dev_priv->dma_status_page); @@ -377,7 +376,7 @@ i915_emit_box(struct drm_device *dev, return -EINVAL; } - if (IS_I965G(dev)) { + if (INTEL_INFO(dev)->gen >= 4) { BEGIN_LP_RING(4); OUT_RING(GFX_OP_DRAWRECT_INFO_I965); OUT_RING((box.x1 & 0xffff) | (box.y1 << 16)); @@ -481,7 +480,7 @@ static int i915_dispatch_batchbuffer(struct drm_device * dev, if (!IS_I830(dev) && !IS_845G(dev)) { BEGIN_LP_RING(2); - if (IS_I965G(dev)) { + if (INTEL_INFO(dev)->gen >= 4) { OUT_RING(MI_BATCH_BUFFER_START | (2 << 6) | MI_BATCH_NON_SECURE_I965); OUT_RING(batch->start); } else { @@ -888,12 +887,12 @@ static int intel_alloc_mchbar_resource(struct drm_device *dev) { drm_i915_private_t *dev_priv = dev->dev_private; - int reg = IS_I965G(dev) ? MCHBAR_I965 : MCHBAR_I915; + int reg = INTEL_INFO(dev)->gen >= 4 ? MCHBAR_I965 : MCHBAR_I915; u32 temp_lo, temp_hi = 0; u64 mchbar_addr; int ret; - if (IS_I965G(dev)) + if (INTEL_INFO(dev)->gen >= 4) pci_read_config_dword(dev_priv->bridge_dev, reg + 4, &temp_hi); pci_read_config_dword(dev_priv->bridge_dev, reg, &temp_lo); mchbar_addr = ((u64)temp_hi << 32) | temp_lo; @@ -920,7 +919,7 @@ intel_alloc_mchbar_resource(struct drm_device *dev) return ret; } - if (IS_I965G(dev)) + if (INTEL_INFO(dev)->gen >= 4) pci_write_config_dword(dev_priv->bridge_dev, reg + 4, upper_32_bits(dev_priv->mch_res.start)); @@ -934,7 +933,7 @@ static void intel_setup_mchbar(struct drm_device *dev) { drm_i915_private_t *dev_priv = dev->dev_private; - int mchbar_reg = IS_I965G(dev) ? MCHBAR_I965 : MCHBAR_I915; + int mchbar_reg = INTEL_INFO(dev)->gen >= 4 ? MCHBAR_I965 : MCHBAR_I915; u32 temp; bool enabled; @@ -971,7 +970,7 @@ static void intel_teardown_mchbar(struct drm_device *dev) { drm_i915_private_t *dev_priv = dev->dev_private; - int mchbar_reg = IS_I965G(dev) ? MCHBAR_I965 : MCHBAR_I915; + int mchbar_reg = INTEL_INFO(dev)->gen >= 4 ? MCHBAR_I965 : MCHBAR_I915; u32 temp; if (dev_priv->mchbar_need_disable) { @@ -990,174 +989,6 @@ intel_teardown_mchbar(struct drm_device *dev) release_resource(&dev_priv->mch_res); } -/** - * i915_probe_agp - get AGP bootup configuration - * @pdev: PCI device - * @aperture_size: returns AGP aperture configured size - * @preallocated_size: returns size of BIOS preallocated AGP space - * - * Since Intel integrated graphics are UMA, the BIOS has to set aside - * some RAM for the framebuffer at early boot. This code figures out - * how much was set aside so we can use it for our own purposes. - */ -static int i915_probe_agp(struct drm_device *dev, uint32_t *aperture_size, - uint32_t *preallocated_size, - uint32_t *start) -{ - struct drm_i915_private *dev_priv = dev->dev_private; - u16 tmp = 0; - unsigned long overhead; - unsigned long stolen; - - /* Get the fb aperture size and "stolen" memory amount. */ - pci_read_config_word(dev_priv->bridge_dev, INTEL_GMCH_CTRL, &tmp); - - *aperture_size = 1024 * 1024; - *preallocated_size = 1024 * 1024; - - switch (dev->pdev->device) { - case PCI_DEVICE_ID_INTEL_82830_CGC: - case PCI_DEVICE_ID_INTEL_82845G_IG: - case PCI_DEVICE_ID_INTEL_82855GM_IG: - case PCI_DEVICE_ID_INTEL_82865_IG: - if ((tmp & INTEL_GMCH_MEM_MASK) == INTEL_GMCH_MEM_64M) - *aperture_size *= 64; - else - *aperture_size *= 128; - break; - default: - /* 9xx supports large sizes, just look at the length */ - *aperture_size = pci_resource_len(dev->pdev, 2); - break; - } - - /* - * Some of the preallocated space is taken by the GTT - * and popup. GTT is 1K per MB of aperture size, and popup is 4K. - */ - if (IS_G4X(dev) || IS_PINEVIEW(dev) || IS_IRONLAKE(dev) || IS_GEN6(dev)) - overhead = 4096; - else - overhead = (*aperture_size / 1024) + 4096; - - if (IS_GEN6(dev)) { - /* SNB has memory control reg at 0x50.w */ - pci_read_config_word(dev->pdev, SNB_GMCH_CTRL, &tmp); - - switch (tmp & SNB_GMCH_GMS_STOLEN_MASK) { - case INTEL_855_GMCH_GMS_DISABLED: - DRM_ERROR("video memory is disabled\n"); - return -1; - case SNB_GMCH_GMS_STOLEN_32M: - stolen = 32 * 1024 * 1024; - break; - case SNB_GMCH_GMS_STOLEN_64M: - stolen = 64 * 1024 * 1024; - break; - case SNB_GMCH_GMS_STOLEN_96M: - stolen = 96 * 1024 * 1024; - break; - case SNB_GMCH_GMS_STOLEN_128M: - stolen = 128 * 1024 * 1024; - break; - case SNB_GMCH_GMS_STOLEN_160M: - stolen = 160 * 1024 * 1024; - break; - case SNB_GMCH_GMS_STOLEN_192M: - stolen = 192 * 1024 * 1024; - break; - case SNB_GMCH_GMS_STOLEN_224M: - stolen = 224 * 1024 * 1024; - break; - case SNB_GMCH_GMS_STOLEN_256M: - stolen = 256 * 1024 * 1024; - break; - case SNB_GMCH_GMS_STOLEN_288M: - stolen = 288 * 1024 * 1024; - break; - case SNB_GMCH_GMS_STOLEN_320M: - stolen = 320 * 1024 * 1024; - break; - case SNB_GMCH_GMS_STOLEN_352M: - stolen = 352 * 1024 * 1024; - break; - case SNB_GMCH_GMS_STOLEN_384M: - stolen = 384 * 1024 * 1024; - break; - case SNB_GMCH_GMS_STOLEN_416M: - stolen = 416 * 1024 * 1024; - break; - case SNB_GMCH_GMS_STOLEN_448M: - stolen = 448 * 1024 * 1024; - break; - case SNB_GMCH_GMS_STOLEN_480M: - stolen = 480 * 1024 * 1024; - break; - case SNB_GMCH_GMS_STOLEN_512M: - stolen = 512 * 1024 * 1024; - break; - default: - DRM_ERROR("unexpected GMCH_GMS value: 0x%02x\n", - tmp & SNB_GMCH_GMS_STOLEN_MASK); - return -1; - } - } else { - switch (tmp & INTEL_GMCH_GMS_MASK) { - case INTEL_855_GMCH_GMS_DISABLED: - DRM_ERROR("video memory is disabled\n"); - return -1; - case INTEL_855_GMCH_GMS_STOLEN_1M: - stolen = 1 * 1024 * 1024; - break; - case INTEL_855_GMCH_GMS_STOLEN_4M: - stolen = 4 * 1024 * 1024; - break; - case INTEL_855_GMCH_GMS_STOLEN_8M: - stolen = 8 * 1024 * 1024; - break; - case INTEL_855_GMCH_GMS_STOLEN_16M: - stolen = 16 * 1024 * 1024; - break; - case INTEL_855_GMCH_GMS_STOLEN_32M: - stolen = 32 * 1024 * 1024; - break; - case INTEL_915G_GMCH_GMS_STOLEN_48M: - stolen = 48 * 1024 * 1024; - break; - case INTEL_915G_GMCH_GMS_STOLEN_64M: - stolen = 64 * 1024 * 1024; - break; - case INTEL_GMCH_GMS_STOLEN_128M: - stolen = 128 * 1024 * 1024; - break; - case INTEL_GMCH_GMS_STOLEN_256M: - stolen = 256 * 1024 * 1024; - break; - case INTEL_GMCH_GMS_STOLEN_96M: - stolen = 96 * 1024 * 1024; - break; - case INTEL_GMCH_GMS_STOLEN_160M: - stolen = 160 * 1024 * 1024; - break; - case INTEL_GMCH_GMS_STOLEN_224M: - stolen = 224 * 1024 * 1024; - break; - case INTEL_GMCH_GMS_STOLEN_352M: - stolen = 352 * 1024 * 1024; - break; - default: - DRM_ERROR("unexpected GMCH_GMS value: 0x%02x\n", - tmp & INTEL_GMCH_GMS_MASK); - return -1; - } - } - - *preallocated_size = stolen - overhead; - *start = overhead; - - return 0; -} - #define PTE_ADDRESS_MASK 0xfffff000 #define PTE_ADDRESS_MASK_HIGH 0x000000f0 /* i915+ */ #define PTE_MAPPING_TYPE_UNCACHED (0 << 1) @@ -1181,11 +1012,11 @@ static unsigned long i915_gtt_to_phys(struct drm_device *dev, { unsigned long *gtt; unsigned long entry, phys; - int gtt_bar = IS_I9XX(dev) ? 0 : 1; + int gtt_bar = IS_GEN2(dev) ? 1 : 0; int gtt_offset, gtt_size; - if (IS_I965G(dev)) { - if (IS_G4X(dev) || IS_IRONLAKE(dev) || IS_GEN6(dev)) { + if (INTEL_INFO(dev)->gen >= 4) { + if (IS_G4X(dev) || INTEL_INFO(dev)->gen > 4) { gtt_offset = 2*1024*1024; gtt_size = 2*1024*1024; } else { @@ -1210,10 +1041,8 @@ static unsigned long i915_gtt_to_phys(struct drm_device *dev, DRM_DEBUG_DRIVER("GTT addr: 0x%08lx, PTE: 0x%08lx\n", gtt_addr, entry); /* Mask out these reserved bits on this hardware. */ - if (!IS_I9XX(dev) || IS_I915G(dev) || IS_I915GM(dev) || - IS_I945G(dev) || IS_I945GM(dev)) { + if (INTEL_INFO(dev)->gen < 4 && !IS_G33(dev)) entry &= ~PTE_ADDRESS_MASK_HIGH; - } /* If it's not a mapping type we know, then bail. */ if ((entry & PTE_MAPPING_TYPE_MASK) != PTE_MAPPING_TYPE_UNCACHED && @@ -1252,7 +1081,7 @@ static void i915_setup_compression(struct drm_device *dev, int size) unsigned long ll_base = 0; /* Leave 1M for line length buffer & misc. */ - compressed_fb = drm_mm_search_free(&dev_priv->vram, size, 4096, 0); + compressed_fb = drm_mm_search_free(&dev_priv->mm.vram, size, 4096, 0); if (!compressed_fb) { dev_priv->no_fbc_reason = FBC_STOLEN_TOO_SMALL; i915_warn_stolen(dev); @@ -1273,7 +1102,7 @@ static void i915_setup_compression(struct drm_device *dev, int size) } if (!(IS_GM45(dev) || IS_IRONLAKE_M(dev))) { - compressed_llb = drm_mm_search_free(&dev_priv->vram, 4096, + compressed_llb = drm_mm_search_free(&dev_priv->mm.vram, 4096, 4096, 0); if (!compressed_llb) { i915_warn_stolen(dev); @@ -1343,10 +1172,8 @@ static void i915_switcheroo_set_state(struct pci_dev *pdev, enum vga_switcheroo_ /* i915 resume handler doesn't set to D0 */ pci_set_power_state(dev->pdev, PCI_D0); i915_resume(dev); - drm_kms_helper_poll_enable(dev); } else { printk(KERN_ERR "i915: switched off\n"); - drm_kms_helper_poll_disable(dev); i915_suspend(dev, pmm); } } @@ -1363,20 +1190,14 @@ static bool i915_switcheroo_can_switch(struct pci_dev *pdev) } static int i915_load_modeset_init(struct drm_device *dev, - unsigned long prealloc_start, unsigned long prealloc_size, unsigned long agp_size) { struct drm_i915_private *dev_priv = dev->dev_private; - int fb_bar = IS_I9XX(dev) ? 2 : 0; int ret = 0; - dev->mode_config.fb_base = pci_resource_start(dev->pdev, fb_bar) & - 0xff000000; - - /* Basic memrange allocator for stolen space (aka vram) */ - drm_mm_init(&dev_priv->vram, 0, prealloc_size); - DRM_INFO("set up %ldM of stolen space\n", prealloc_size / (1024*1024)); + /* Basic memrange allocator for stolen space (aka mm.vram) */ + drm_mm_init(&dev_priv->mm.vram, 0, prealloc_size); /* We're off and running w/KMS */ dev_priv->mm.suspended = 0; @@ -1443,12 +1264,6 @@ static int i915_load_modeset_init(struct drm_device *dev, /* FIXME: do pre/post-mode set stuff in core KMS code */ dev->vblank_disable_allowed = 1; - /* - * Initialize the hardware status page IRQ location. - */ - - I915_WRITE(INSTPM, (1 << 5) | (1 << 21)); - ret = intel_fbdev_init(dev); if (ret) goto cleanup_irq; @@ -1787,9 +1602,9 @@ unsigned long i915_chipset_val(struct drm_i915_private *dev_priv) } } - div_u64(diff, diff1); + diff = div_u64(diff, diff1); ret = ((m * diff) + c); - div_u64(ret, 10); + ret = div_u64(ret, 10); dev_priv->last_count1 = total_count; dev_priv->last_time1 = now; @@ -1858,7 +1673,7 @@ void i915_update_gfx_val(struct drm_i915_private *dev_priv) /* More magic constants... */ diff = diff * 1181; - div_u64(diff, diffms * 10); + diff = div_u64(diff, diffms * 10); dev_priv->gfx_power = diff; } @@ -1907,7 +1722,7 @@ static struct drm_i915_private *i915_mch_dev; * - dev_priv->fmax * - dev_priv->gpu_busy */ -DEFINE_SPINLOCK(mchdev_lock); +static DEFINE_SPINLOCK(mchdev_lock); /** * i915_read_mch_val - return value for IPS use @@ -2062,7 +1877,7 @@ int i915_driver_load(struct drm_device *dev, unsigned long flags) struct drm_i915_private *dev_priv; resource_size_t base, size; int ret = 0, mmio_bar; - uint32_t agp_size, prealloc_size, prealloc_start; + uint32_t agp_size, prealloc_size; /* i915 has 4 more counters */ dev->counters += 4; dev->types[6] = _DRM_STAT_IRQ; @@ -2079,7 +1894,7 @@ int i915_driver_load(struct drm_device *dev, unsigned long flags) dev_priv->info = (struct intel_device_info *) flags; /* Add register map (needed for suspend/resume) */ - mmio_bar = IS_I9XX(dev) ? 0 : 1; + mmio_bar = IS_GEN2(dev) ? 1 : 0; base = pci_resource_start(dev->pdev, mmio_bar); size = pci_resource_len(dev->pdev, mmio_bar); @@ -2121,17 +1936,32 @@ int i915_driver_load(struct drm_device *dev, unsigned long flags) "performance may suffer.\n"); } - ret = i915_probe_agp(dev, &agp_size, &prealloc_size, &prealloc_start); - if (ret) + dev_priv->mm.gtt = intel_gtt_get(); + if (!dev_priv->mm.gtt) { + DRM_ERROR("Failed to initialize GTT\n"); + ret = -ENODEV; goto out_iomapfree; - - if (prealloc_size > intel_max_stolen) { - DRM_INFO("detected %dM stolen memory, trimming to %dM\n", - prealloc_size >> 20, intel_max_stolen >> 20); - prealloc_size = intel_max_stolen; } - dev_priv->wq = create_singlethread_workqueue("i915"); + prealloc_size = dev_priv->mm.gtt->gtt_stolen_entries << PAGE_SHIFT; + agp_size = dev_priv->mm.gtt->gtt_mappable_entries << PAGE_SHIFT; + + /* The i915 workqueue is primarily used for batched retirement of + * requests (and thus managing bo) once the task has been completed + * by the GPU. i915_gem_retire_requests() is called directly when we + * need high-priority retirement, such as waiting for an explicit + * bo. + * + * It is also used for periodic low-priority events, such as + * idle-timers and hangcheck. + * + * All tasks on the workqueue are expected to acquire the dev mutex + * so there is no point in running more than one instance of the + * workqueue at any time: max_active = 1 and NON_REENTRANT. + */ + dev_priv->wq = alloc_workqueue("i915", + WQ_UNBOUND | WQ_NON_REENTRANT, + 1); if (dev_priv->wq == NULL) { DRM_ERROR("Failed to create our workqueue.\n"); ret = -ENOMEM; @@ -2166,6 +1996,8 @@ int i915_driver_load(struct drm_device *dev, unsigned long flags) /* Try to make sure MCHBAR is enabled before poking at it */ intel_setup_mchbar(dev); + intel_setup_gmbus(dev); + intel_opregion_setup(dev); i915_gem_load(dev); @@ -2212,8 +2044,7 @@ int i915_driver_load(struct drm_device *dev, unsigned long flags) intel_detect_pch(dev); if (drm_core_check_feature(dev, DRIVER_MODESET)) { - ret = i915_load_modeset_init(dev, prealloc_start, - prealloc_size, agp_size); + ret = i915_load_modeset_init(dev, prealloc_size, agp_size); if (ret < 0) { DRM_ERROR("failed to init modeset\n"); goto out_workqueue_free; @@ -2221,7 +2052,8 @@ int i915_driver_load(struct drm_device *dev, unsigned long flags) } /* Must be done after probing outputs */ - intel_opregion_init(dev, 0); + intel_opregion_init(dev); + acpi_video_register(); setup_timer(&dev_priv->hangcheck_timer, i915_hangcheck_elapsed, (unsigned long) dev); @@ -2249,15 +2081,20 @@ free_priv: int i915_driver_unload(struct drm_device *dev) { struct drm_i915_private *dev_priv = dev->dev_private; - - i915_destroy_error_state(dev); + int ret; spin_lock(&mchdev_lock); i915_mch_dev = NULL; spin_unlock(&mchdev_lock); - destroy_workqueue(dev_priv->wq); - del_timer_sync(&dev_priv->hangcheck_timer); + mutex_lock(&dev->struct_mutex); + ret = i915_gpu_idle(dev); + if (ret) + DRM_ERROR("failed to idle hardware: %d\n", ret); + mutex_unlock(&dev->struct_mutex); + + /* 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); if (dev_priv->mm.gtt_mtrr >= 0) { @@ -2266,7 +2103,10 @@ int i915_driver_unload(struct drm_device *dev) dev_priv->mm.gtt_mtrr = -1; } + acpi_video_unregister(); + if (drm_core_check_feature(dev, DRIVER_MODESET)) { + intel_fbdev_fini(dev); intel_modeset_cleanup(dev); /* @@ -2278,20 +2118,28 @@ int i915_driver_unload(struct drm_device *dev) dev_priv->child_dev = NULL; dev_priv->child_dev_num = 0; } - drm_irq_uninstall(dev); + vga_switcheroo_unregister_client(dev->pdev); vga_client_register(dev->pdev, NULL, NULL, NULL); } + /* Free error state after interrupts are fully disabled. */ + del_timer_sync(&dev_priv->hangcheck_timer); + cancel_work_sync(&dev_priv->error_work); + i915_destroy_error_state(dev); + if (dev->pdev->msi_enabled) pci_disable_msi(dev->pdev); if (dev_priv->regs != NULL) iounmap(dev_priv->regs); - intel_opregion_free(dev, 0); + intel_opregion_fini(dev); if (drm_core_check_feature(dev, DRIVER_MODESET)) { + /* Flush any outstanding unpin_work. */ + flush_workqueue(dev_priv->wq); + i915_gem_free_all_phys_object(dev); mutex_lock(&dev->struct_mutex); @@ -2299,34 +2147,35 @@ int i915_driver_unload(struct drm_device *dev) mutex_unlock(&dev->struct_mutex); if (I915_HAS_FBC(dev) && i915_powersave) i915_cleanup_compression(dev); - drm_mm_takedown(&dev_priv->vram); - i915_gem_lastclose(dev); + drm_mm_takedown(&dev_priv->mm.vram); intel_cleanup_overlay(dev); } + intel_teardown_gmbus(dev); intel_teardown_mchbar(dev); + destroy_workqueue(dev_priv->wq); + pci_dev_put(dev_priv->bridge_dev); kfree(dev->dev_private); return 0; } -int i915_driver_open(struct drm_device *dev, struct drm_file *file_priv) +int i915_driver_open(struct drm_device *dev, struct drm_file *file) { - struct drm_i915_file_private *i915_file_priv; + struct drm_i915_file_private *file_priv; DRM_DEBUG_DRIVER("\n"); - i915_file_priv = (struct drm_i915_file_private *) - kmalloc(sizeof(*i915_file_priv), GFP_KERNEL); - - if (!i915_file_priv) + file_priv = kmalloc(sizeof(*file_priv), GFP_KERNEL); + if (!file_priv) return -ENOMEM; - file_priv->driver_priv = i915_file_priv; + file->driver_priv = file_priv; - INIT_LIST_HEAD(&i915_file_priv->mm.request_list); + spin_lock_init(&file_priv->mm.lock); + INIT_LIST_HEAD(&file_priv->mm.request_list); return 0; } @@ -2369,11 +2218,11 @@ void i915_driver_preclose(struct drm_device * dev, struct drm_file *file_priv) i915_mem_release(dev, file_priv, dev_priv->agp_heap); } -void i915_driver_postclose(struct drm_device *dev, struct drm_file *file_priv) +void i915_driver_postclose(struct drm_device *dev, struct drm_file *file) { - struct drm_i915_file_private *i915_file_priv = file_priv->driver_priv; + struct drm_i915_file_private *file_priv = file->driver_priv; - kfree(i915_file_priv); + kfree(file_priv); } struct drm_ioctl_desc i915_ioctls[] = { diff --git a/drivers/gpu/drm/i915/i915_drv.c b/drivers/gpu/drm/i915/i915_drv.c index 9ed9d23caf1..c3decb2fef4 100644 --- a/drivers/gpu/drm/i915/i915_drv.c +++ b/drivers/gpu/drm/i915/i915_drv.c @@ -32,6 +32,7 @@ #include "drm.h" #include "i915_drm.h" #include "i915_drv.h" +#include "intel_drv.h" #include <linux/console.h> #include "drm_crtc_helper.h" @@ -61,86 +62,108 @@ extern int intel_agp_enabled; .driver_data = (unsigned long) info } static const struct intel_device_info intel_i830_info = { - .gen = 2, .is_i8xx = 1, .is_mobile = 1, .cursor_needs_physical = 1, + .gen = 2, .is_mobile = 1, .cursor_needs_physical = 1, + .has_overlay = 1, .overlay_needs_physical = 1, }; static const struct intel_device_info intel_845g_info = { - .gen = 2, .is_i8xx = 1, + .gen = 2, + .has_overlay = 1, .overlay_needs_physical = 1, }; static const struct intel_device_info intel_i85x_info = { - .gen = 2, .is_i8xx = 1, .is_i85x = 1, .is_mobile = 1, + .gen = 2, .is_i85x = 1, .is_mobile = 1, .cursor_needs_physical = 1, + .has_overlay = 1, .overlay_needs_physical = 1, }; static const struct intel_device_info intel_i865g_info = { - .gen = 2, .is_i8xx = 1, + .gen = 2, + .has_overlay = 1, .overlay_needs_physical = 1, }; static const struct intel_device_info intel_i915g_info = { - .gen = 3, .is_i915g = 1, .is_i9xx = 1, .cursor_needs_physical = 1, + .gen = 3, .is_i915g = 1, .cursor_needs_physical = 1, + .has_overlay = 1, .overlay_needs_physical = 1, }; static const struct intel_device_info intel_i915gm_info = { - .gen = 3, .is_i9xx = 1, .is_mobile = 1, + .gen = 3, .is_mobile = 1, .cursor_needs_physical = 1, + .has_overlay = 1, .overlay_needs_physical = 1, + .supports_tv = 1, }; static const struct intel_device_info intel_i945g_info = { - .gen = 3, .is_i9xx = 1, .has_hotplug = 1, .cursor_needs_physical = 1, + .gen = 3, .has_hotplug = 1, .cursor_needs_physical = 1, + .has_overlay = 1, .overlay_needs_physical = 1, }; static const struct intel_device_info intel_i945gm_info = { - .gen = 3, .is_i945gm = 1, .is_i9xx = 1, .is_mobile = 1, + .gen = 3, .is_i945gm = 1, .is_mobile = 1, .has_hotplug = 1, .cursor_needs_physical = 1, + .has_overlay = 1, .overlay_needs_physical = 1, + .supports_tv = 1, }; static const struct intel_device_info intel_i965g_info = { - .gen = 4, .is_broadwater = 1, .is_i965g = 1, .is_i9xx = 1, + .gen = 4, .is_broadwater = 1, .has_hotplug = 1, + .has_overlay = 1, }; static const struct intel_device_info intel_i965gm_info = { - .gen = 4, .is_crestline = 1, .is_i965g = 1, .is_i965gm = 1, .is_i9xx = 1, + .gen = 4, .is_crestline = 1, .is_mobile = 1, .has_fbc = 1, .has_rc6 = 1, .has_hotplug = 1, + .has_overlay = 1, + .supports_tv = 1, }; static const struct intel_device_info intel_g33_info = { - .gen = 3, .is_g33 = 1, .is_i9xx = 1, + .gen = 3, .is_g33 = 1, .need_gfx_hws = 1, .has_hotplug = 1, + .has_overlay = 1, }; static const struct intel_device_info intel_g45_info = { - .gen = 4, .is_i965g = 1, .is_g4x = 1, .is_i9xx = 1, .need_gfx_hws = 1, + .gen = 4, .is_g4x = 1, .need_gfx_hws = 1, .has_pipe_cxsr = 1, .has_hotplug = 1, + .has_bsd_ring = 1, }; static const struct intel_device_info intel_gm45_info = { - .gen = 4, .is_i965g = 1, .is_g4x = 1, .is_i9xx = 1, + .gen = 4, .is_g4x = 1, .is_mobile = 1, .need_gfx_hws = 1, .has_fbc = 1, .has_rc6 = 1, .has_pipe_cxsr = 1, .has_hotplug = 1, + .supports_tv = 1, + .has_bsd_ring = 1, }; static const struct intel_device_info intel_pineview_info = { - .gen = 3, .is_g33 = 1, .is_pineview = 1, .is_mobile = 1, .is_i9xx = 1, + .gen = 3, .is_g33 = 1, .is_pineview = 1, .is_mobile = 1, .need_gfx_hws = 1, .has_hotplug = 1, + .has_overlay = 1, }; static const struct intel_device_info intel_ironlake_d_info = { - .gen = 5, .is_ironlake = 1, .is_i965g = 1, .is_i9xx = 1, + .gen = 5, .is_ironlake = 1, .need_gfx_hws = 1, .has_pipe_cxsr = 1, .has_hotplug = 1, + .has_bsd_ring = 1, }; static const struct intel_device_info intel_ironlake_m_info = { - .gen = 5, .is_ironlake = 1, .is_mobile = 1, .is_i965g = 1, .is_i9xx = 1, + .gen = 5, .is_ironlake = 1, .is_mobile = 1, .need_gfx_hws = 1, .has_fbc = 1, .has_rc6 = 1, .has_hotplug = 1, + .has_bsd_ring = 1, }; static const struct intel_device_info intel_sandybridge_d_info = { - .gen = 6, .is_i965g = 1, .is_i9xx = 1, + .gen = 6, .need_gfx_hws = 1, .has_hotplug = 1, + .has_bsd_ring = 1, }; static const struct intel_device_info intel_sandybridge_m_info = { - .gen = 6, .is_i965g = 1, .is_mobile = 1, .is_i9xx = 1, + .gen = 6, .is_mobile = 1, .need_gfx_hws = 1, .has_hotplug = 1, + .has_bsd_ring = 1, }; static const struct pci_device_id pciidlist[] = { /* aka */ @@ -237,7 +260,7 @@ static int i915_drm_freeze(struct drm_device *dev) i915_save_state(dev); - intel_opregion_free(dev, 1); + intel_opregion_fini(dev); /* Modeset on resume, not lid events */ dev_priv->modeset_on_lid = 0; @@ -258,6 +281,8 @@ int i915_suspend(struct drm_device *dev, pm_message_t state) if (state.event == PM_EVENT_PRETHAW) return 0; + drm_kms_helper_poll_disable(dev); + error = i915_drm_freeze(dev); if (error) return error; @@ -277,8 +302,7 @@ static int i915_drm_thaw(struct drm_device *dev) int error = 0; i915_restore_state(dev); - - intel_opregion_init(dev, 1); + intel_opregion_setup(dev); /* KMS EnterVT equivalent */ if (drm_core_check_feature(dev, DRIVER_MODESET)) { @@ -294,6 +318,8 @@ static int i915_drm_thaw(struct drm_device *dev) drm_helper_resume_force_mode(dev); } + intel_opregion_init(dev); + dev_priv->modeset_on_lid = 0; return error; @@ -301,12 +327,79 @@ static int i915_drm_thaw(struct drm_device *dev) int i915_resume(struct drm_device *dev) { + int ret; + if (pci_enable_device(dev->pdev)) return -EIO; pci_set_master(dev->pdev); - return i915_drm_thaw(dev); + ret = i915_drm_thaw(dev); + if (ret) + return ret; + + drm_kms_helper_poll_enable(dev); + return 0; +} + +static int i8xx_do_reset(struct drm_device *dev, u8 flags) +{ + struct drm_i915_private *dev_priv = dev->dev_private; + + if (IS_I85X(dev)) + return -ENODEV; + + I915_WRITE(D_STATE, I915_READ(D_STATE) | DSTATE_GFX_RESET_I830); + POSTING_READ(D_STATE); + + if (IS_I830(dev) || IS_845G(dev)) { + I915_WRITE(DEBUG_RESET_I830, + DEBUG_RESET_DISPLAY | + DEBUG_RESET_RENDER | + DEBUG_RESET_FULL); + POSTING_READ(DEBUG_RESET_I830); + msleep(1); + + I915_WRITE(DEBUG_RESET_I830, 0); + POSTING_READ(DEBUG_RESET_I830); + } + + msleep(1); + + I915_WRITE(D_STATE, I915_READ(D_STATE) & ~DSTATE_GFX_RESET_I830); + POSTING_READ(D_STATE); + + return 0; +} + +static int i965_reset_complete(struct drm_device *dev) +{ + u8 gdrst; + pci_read_config_byte(dev->pdev, I965_GDRST, &gdrst); + return gdrst & 0x1; +} + +static int i965_do_reset(struct drm_device *dev, u8 flags) +{ + u8 gdrst; + + /* + * Set the domains we want to reset (GRDOM/bits 2 and 3) as + * well as the reset bit (GR/bit 0). Setting the GR bit + * triggers the reset; when done, the hardware will clear it. + */ + pci_read_config_byte(dev->pdev, I965_GDRST, &gdrst); + pci_write_config_byte(dev->pdev, I965_GDRST, gdrst | flags | 0x1); + + return wait_for(i965_reset_complete(dev), 500); +} + +static int ironlake_do_reset(struct drm_device *dev, u8 flags) +{ + struct drm_i915_private *dev_priv = dev->dev_private; + u32 gdrst = I915_READ(MCHBAR_MIRROR_BASE + ILK_GDSR); + I915_WRITE(MCHBAR_MIRROR_BASE + ILK_GDSR, gdrst | flags | 0x1); + return wait_for(I915_READ(MCHBAR_MIRROR_BASE + ILK_GDSR) & 0x1, 500); } /** @@ -325,54 +418,39 @@ int i915_resume(struct drm_device *dev) * - re-init interrupt state * - re-init display */ -int i965_reset(struct drm_device *dev, u8 flags) +int i915_reset(struct drm_device *dev, u8 flags) { drm_i915_private_t *dev_priv = dev->dev_private; - unsigned long timeout; - u8 gdrst; /* * We really should only reset the display subsystem if we actually * need to */ bool need_display = true; + int ret; mutex_lock(&dev->struct_mutex); - /* - * Clear request list - */ - i915_gem_retire_requests(dev); - - if (need_display) - i915_save_display(dev); - - if (IS_I965G(dev) || IS_G4X(dev)) { - /* - * Set the domains we want to reset, then the reset bit (bit 0). - * Clear the reset bit after a while and wait for hardware status - * bit (bit 1) to be set - */ - pci_read_config_byte(dev->pdev, GDRST, &gdrst); - pci_write_config_byte(dev->pdev, GDRST, gdrst | flags | ((flags == GDRST_FULL) ? 0x1 : 0x0)); - udelay(50); - pci_write_config_byte(dev->pdev, GDRST, gdrst & 0xfe); - - /* ...we don't want to loop forever though, 500ms should be plenty */ - timeout = jiffies + msecs_to_jiffies(500); - do { - udelay(100); - pci_read_config_byte(dev->pdev, GDRST, &gdrst); - } while ((gdrst & 0x1) && time_after(timeout, jiffies)); - - if (gdrst & 0x1) { - WARN(true, "i915: Failed to reset chip\n"); - mutex_unlock(&dev->struct_mutex); - return -EIO; - } - } else { - DRM_ERROR("Error occurred. Don't know how to reset this chip.\n"); + i915_gem_reset(dev); + + ret = -ENODEV; + if (get_seconds() - dev_priv->last_gpu_reset < 5) { + DRM_ERROR("GPU hanging too fast, declaring wedged!\n"); + } else switch (INTEL_INFO(dev)->gen) { + case 5: + ret = ironlake_do_reset(dev, flags); + break; + case 4: + ret = i965_do_reset(dev, flags); + break; + case 2: + ret = i8xx_do_reset(dev, flags); + break; + } + dev_priv->last_gpu_reset = get_seconds(); + if (ret) { + DRM_ERROR("Failed to reset chip.\n"); mutex_unlock(&dev->struct_mutex); - return -ENODEV; + return ret; } /* Ok, now get things going again... */ @@ -400,13 +478,19 @@ int i965_reset(struct drm_device *dev, u8 flags) mutex_lock(&dev->struct_mutex); } + mutex_unlock(&dev->struct_mutex); + /* - * Display needs restore too... + * Perform a full modeset as on later generations, e.g. Ironlake, we may + * need to retrain the display link and cannot just restore the register + * values. */ - if (need_display) - i915_restore_display(dev); + if (need_display) { + mutex_lock(&dev->mode_config.mutex); + drm_helper_resume_force_mode(dev); + mutex_unlock(&dev->mode_config.mutex); + } - mutex_unlock(&dev->struct_mutex); return 0; } diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h index af4a263cf25..73ad8bff2c2 100644 --- a/drivers/gpu/drm/i915/i915_drv.h +++ b/drivers/gpu/drm/i915/i915_drv.h @@ -34,6 +34,8 @@ #include "intel_bios.h" #include "intel_ringbuffer.h" #include <linux/io-mapping.h> +#include <linux/i2c.h> +#include <drm/intel-gtt.h> /* General customization: */ @@ -73,11 +75,9 @@ enum plane { #define DRIVER_PATCHLEVEL 0 #define WATCH_COHERENCY 0 -#define WATCH_BUF 0 #define WATCH_EXEC 0 -#define WATCH_LRU 0 #define WATCH_RELOC 0 -#define WATCH_INACTIVE 0 +#define WATCH_LISTS 0 #define WATCH_PWRITE 0 #define I915_GEM_PHYS_CURSOR_0 1 @@ -110,8 +110,9 @@ struct intel_opregion { struct opregion_acpi *acpi; struct opregion_swsci *swsci; struct opregion_asle *asle; - int enabled; + void *vbt; }; +#define OPREGION_SIZE (8*1024) struct intel_overlay; struct intel_overlay_error_state; @@ -125,13 +126,16 @@ struct drm_i915_master_private { struct drm_i915_fence_reg { struct drm_gem_object *obj; struct list_head lru_list; + bool gpu; }; struct sdvo_device_mapping { + u8 initialized; u8 dvo_port; u8 slave_addr; u8 dvo_wiring; - u8 initialized; + u8 i2c_pin; + u8 i2c_speed; u8 ddc_pin; }; @@ -193,13 +197,9 @@ struct drm_i915_display_funcs { struct intel_device_info { u8 gen; u8 is_mobile : 1; - u8 is_i8xx : 1; u8 is_i85x : 1; u8 is_i915g : 1; - u8 is_i9xx : 1; u8 is_i945gm : 1; - u8 is_i965g : 1; - u8 is_i965gm : 1; u8 is_g33 : 1; u8 need_gfx_hws : 1; u8 is_g4x : 1; @@ -212,9 +212,14 @@ struct intel_device_info { u8 has_pipe_cxsr : 1; u8 has_hotplug : 1; u8 cursor_needs_physical : 1; + u8 has_overlay : 1; + u8 overlay_needs_physical : 1; + u8 supports_tv : 1; + u8 has_bsd_ring : 1; }; enum no_fbc_reason { + FBC_NO_OUTPUT, /* no outputs enabled to compress */ FBC_STOLEN_TOO_SMALL, /* not enough space to hold compressed buffers */ FBC_UNSUPPORTED_MODE, /* interlace or doublescanned mode */ FBC_MODE_TOO_LARGE, /* mode too large for compression */ @@ -241,6 +246,12 @@ typedef struct drm_i915_private { void __iomem *regs; + struct intel_gmbus { + struct i2c_adapter adapter; + struct i2c_adapter *force_bit; + u32 reg0; + } *gmbus; + struct pci_dev *bridge_dev; struct intel_ring_buffer render_ring; struct intel_ring_buffer bsd_ring; @@ -263,6 +274,9 @@ typedef struct drm_i915_private { int front_offset; int current_page; int page_flipping; +#define I915_DEBUG_READ (1<<0) +#define I915_DEBUG_WRITE (1<<1) + unsigned long debug_flags; wait_queue_head_t irq_queue; atomic_t irq_received; @@ -289,24 +303,21 @@ typedef struct drm_i915_private { unsigned int sr01, adpa, ppcr, dvob, dvoc, lvds; int vblank_pipe; int num_pipe; - u32 flush_rings; -#define FLUSH_RENDER_RING 0x1 -#define FLUSH_BSD_RING 0x2 /* For hangcheck timer */ -#define DRM_I915_HANGCHECK_PERIOD 75 /* in jiffies */ +#define DRM_I915_HANGCHECK_PERIOD 250 /* in ms */ struct timer_list hangcheck_timer; int hangcheck_count; uint32_t last_acthd; uint32_t last_instdone; uint32_t last_instdone1; - struct drm_mm vram; - unsigned long cfb_size; unsigned long cfb_pitch; + unsigned long cfb_offset; int cfb_fence; int cfb_plane; + int cfb_y; int irq_enabled; @@ -316,8 +327,7 @@ typedef struct drm_i915_private { struct intel_overlay *overlay; /* LVDS info */ - int backlight_duty_cycle; /* restore backlight to this value */ - bool panel_wants_dither; + int backlight_level; /* restore backlight to this value */ struct drm_display_mode *panel_fixed_mode; struct drm_display_mode *lfp_lvds_vbt_mode; /* if any */ struct drm_display_mode *sdvo_lvds_vbt_mode; /* if any */ @@ -328,13 +338,22 @@ typedef struct drm_i915_private { unsigned int lvds_vbt:1; unsigned int int_crt_support:1; unsigned int lvds_use_ssc:1; - unsigned int edp_support:1; int lvds_ssc_freq; - int edp_bpp; + + struct { + u8 rate:4; + u8 lanes:4; + u8 preemphasis:4; + u8 vswing:4; + + u8 initialized:1; + u8 support:1; + u8 bpp:6; + } edp; struct notifier_block lid_notifier; - int crt_ddc_bus; /* 0 = unknown, else GPIO to use for CRT DDC */ + int crt_ddc_pin; struct drm_i915_fence_reg fence_regs[16]; /* assume 965 */ int fence_reg_start; /* 4 if userland hasn't ioctl'd us yet */ int num_fence_regs; /* 8 on pre-965, 16 otherwise */ @@ -344,6 +363,7 @@ typedef struct drm_i915_private { spinlock_t error_lock; struct drm_i915_error_state *first_error; struct work_struct error_work; + struct completion error_completion; struct workqueue_struct *wq; /* Display functions */ @@ -507,6 +527,11 @@ typedef struct drm_i915_private { u32 saveMCHBAR_RENDER_STANDBY; struct { + /** Bridge to intel-gtt-ko */ + struct intel_gtt *gtt; + /** Memory allocator for GTT stolen memory */ + struct drm_mm vram; + /** Memory allocator for GTT */ struct drm_mm gtt_space; struct io_mapping *gtt_mapping; @@ -521,8 +546,6 @@ typedef struct drm_i915_private { */ struct list_head shrink_list; - spinlock_t active_list_lock; - /** * List of objects which are not in the ringbuffer but which * still have a write_domain which needs to be flushed before @@ -555,6 +578,12 @@ typedef struct drm_i915_private { */ struct list_head inactive_list; + /** + * LRU list of objects which are not in the ringbuffer but + * are still pinned in the GTT. + */ + struct list_head pinned_list; + /** LRU list of objects with fence regs on them. */ struct list_head fence_list; @@ -611,6 +640,17 @@ typedef struct drm_i915_private { /* storage for physical objects */ struct drm_i915_gem_phys_object *phys_objs[I915_MAX_PHYS_OBJECT]; + + uint32_t flush_rings; + + /* accounting, useful for userland debugging */ + size_t object_memory; + size_t pin_memory; + size_t gtt_memory; + size_t gtt_total; + u32 object_count; + u32 pin_count; + u32 gtt_count; } mm; struct sdvo_device_mapping sdvo_mappings[2]; /* indicate whether the LVDS_BORDER should be enabled or not */ @@ -626,8 +666,6 @@ typedef struct drm_i915_private { /* Reclocking support */ bool render_reclock_avail; bool lvds_downclock_avail; - /* indicate whether the LVDS EDID is OK */ - bool lvds_edid_good; /* indicates the reduced downclock for LVDS*/ int lvds_downclock; struct work_struct idle_work; @@ -661,6 +699,8 @@ typedef struct drm_i915_private { struct drm_mm_node *compressed_fb; struct drm_mm_node *compressed_llb; + unsigned long last_gpu_reset; + /* list of fbdev register on this device */ struct intel_fbdev *fbdev; } drm_i915_private_t; @@ -816,12 +856,14 @@ struct drm_i915_gem_request { /** global list entry for this request */ struct list_head list; + struct drm_i915_file_private *file_priv; /** file_priv list entry for this request */ struct list_head client_list; }; struct drm_i915_file_private { struct { + struct spinlock lock; struct list_head request_list; } mm; }; @@ -862,7 +904,7 @@ extern long i915_compat_ioctl(struct file *filp, unsigned int cmd, extern int i915_emit_box(struct drm_device *dev, struct drm_clip_rect *boxes, int i, int DR1, int DR4); -extern int i965_reset(struct drm_device *dev, u8 flags); +extern int i915_reset(struct drm_device *dev, u8 flags); extern unsigned long i915_chipset_val(struct drm_i915_private *dev_priv); extern unsigned long i915_mch_val(struct drm_i915_private *dev_priv); extern unsigned long i915_gfx_val(struct drm_i915_private *dev_priv); @@ -871,7 +913,6 @@ extern void i915_update_gfx_val(struct drm_i915_private *dev_priv); /* i915_irq.c */ void i915_hangcheck_elapsed(unsigned long data); -void i915_destroy_error_state(struct drm_device *dev); extern int i915_irq_emit(struct drm_device *dev, void *data, struct drm_file *file_priv); extern int i915_irq_wait(struct drm_device *dev, void *data, @@ -908,6 +949,12 @@ i915_disable_pipestat(drm_i915_private_t *dev_priv, int pipe, u32 mask); void intel_enable_asle (struct drm_device *dev); +#ifdef CONFIG_DEBUG_FS +extern void i915_destroy_error_state(struct drm_device *dev); +#else +#define i915_destroy_error_state(x) +#endif + /* i915_mem.c */ extern int i915_mem_alloc(struct drm_device *dev, void *data, @@ -922,6 +969,7 @@ extern void i915_mem_takedown(struct mem_block **heap); extern void i915_mem_release(struct drm_device * dev, struct drm_file *file_priv, struct mem_block *heap); /* i915_gem.c */ +int i915_gem_check_is_wedged(struct drm_device *dev); int i915_gem_init_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv); int i915_gem_create_ioctl(struct drm_device *dev, void *data, @@ -972,13 +1020,22 @@ void i915_gem_object_unpin(struct drm_gem_object *obj); int i915_gem_object_unbind(struct drm_gem_object *obj); void i915_gem_release_mmap(struct drm_gem_object *obj); void i915_gem_lastclose(struct drm_device *dev); -uint32_t i915_get_gem_seqno(struct drm_device *dev, - struct intel_ring_buffer *ring); -bool i915_seqno_passed(uint32_t seq1, uint32_t seq2); -int i915_gem_object_get_fence_reg(struct drm_gem_object *obj); -int i915_gem_object_put_fence_reg(struct drm_gem_object *obj); + +/** + * Returns true if seq1 is later than seq2. + */ +static inline bool +i915_seqno_passed(uint32_t seq1, uint32_t seq2) +{ + return (int32_t)(seq1 - seq2) >= 0; +} + +int i915_gem_object_get_fence_reg(struct drm_gem_object *obj, + bool interruptible); +int i915_gem_object_put_fence_reg(struct drm_gem_object *obj, + bool interruptible); void i915_gem_retire_requests(struct drm_device *dev); -void i915_gem_retire_work_handler(struct work_struct *work); +void i915_gem_reset(struct drm_device *dev); void i915_gem_clflush_object(struct drm_gem_object *obj); int i915_gem_object_set_domain(struct drm_gem_object *obj, uint32_t read_domains, @@ -990,16 +1047,18 @@ int i915_gem_do_init(struct drm_device *dev, unsigned long start, int i915_gpu_idle(struct drm_device *dev); int i915_gem_idle(struct drm_device *dev); uint32_t i915_add_request(struct drm_device *dev, - struct drm_file *file_priv, - uint32_t flush_domains, - struct intel_ring_buffer *ring); + struct drm_file *file_priv, + struct drm_i915_gem_request *request, + struct intel_ring_buffer *ring); int i915_do_wait_request(struct drm_device *dev, - uint32_t seqno, int interruptible, - struct intel_ring_buffer *ring); + uint32_t seqno, + bool interruptible, + struct intel_ring_buffer *ring); int i915_gem_fault(struct vm_area_struct *vma, struct vm_fault *vmf); int i915_gem_object_set_to_gtt_domain(struct drm_gem_object *obj, int write); -int i915_gem_object_set_to_display_plane(struct drm_gem_object *obj); +int i915_gem_object_set_to_display_plane(struct drm_gem_object *obj, + bool pipelined); int i915_gem_attach_phys_object(struct drm_device *dev, struct drm_gem_object *obj, int id, @@ -1007,10 +1066,7 @@ int i915_gem_attach_phys_object(struct drm_device *dev, void i915_gem_detach_phys_object(struct drm_device *dev, struct drm_gem_object *obj); void i915_gem_free_all_phys_object(struct drm_device *dev); -int i915_gem_object_get_pages(struct drm_gem_object *obj, gfp_t gfpmask); -void i915_gem_object_put_pages(struct drm_gem_object *obj); void i915_gem_release(struct drm_device * dev, struct drm_file *file_priv); -int i915_gem_object_flush_write_domain(struct drm_gem_object *obj); void i915_gem_shrinker_init(void); void i915_gem_shrinker_exit(void); @@ -1032,15 +1088,14 @@ bool i915_gem_object_fence_offset_ok(struct drm_gem_object *obj, /* i915_gem_debug.c */ void i915_gem_dump_object(struct drm_gem_object *obj, int len, const char *where, uint32_t mark); -#if WATCH_INACTIVE -void i915_verify_inactive(struct drm_device *dev, char *file, int line); +#if WATCH_LISTS +int i915_verify_lists(struct drm_device *dev); #else -#define i915_verify_inactive(dev, file, line) +#define i915_verify_lists(dev) 0 #endif void i915_gem_object_check_coherency(struct drm_gem_object *obj, int handle); void i915_gem_dump_object(struct drm_gem_object *obj, int len, const char *where, uint32_t mark); -void i915_dump_lru(struct drm_device *dev, const char *where); /* i915_debugfs.c */ int i915_debugfs_init(struct drm_minor *minor); @@ -1054,19 +1109,31 @@ extern int i915_restore_state(struct drm_device *dev); extern int i915_save_state(struct drm_device *dev); extern int i915_restore_state(struct drm_device *dev); +/* intel_i2c.c */ +extern int intel_setup_gmbus(struct drm_device *dev); +extern void intel_teardown_gmbus(struct drm_device *dev); +extern void intel_gmbus_set_speed(struct i2c_adapter *adapter, int speed); +extern void intel_gmbus_force_bit(struct i2c_adapter *adapter, bool force_bit); +extern inline bool intel_gmbus_is_forced_bit(struct i2c_adapter *adapter) +{ + return container_of(adapter, struct intel_gmbus, adapter)->force_bit; +} +extern void intel_i2c_reset(struct drm_device *dev); + +/* intel_opregion.c */ +extern int intel_opregion_setup(struct drm_device *dev); #ifdef CONFIG_ACPI -/* i915_opregion.c */ -extern int intel_opregion_init(struct drm_device *dev, int resume); -extern void intel_opregion_free(struct drm_device *dev, int suspend); -extern void opregion_asle_intr(struct drm_device *dev); -extern void ironlake_opregion_gse_intr(struct drm_device *dev); -extern void opregion_enable_asle(struct drm_device *dev); +extern void intel_opregion_init(struct drm_device *dev); +extern void intel_opregion_fini(struct drm_device *dev); +extern void intel_opregion_asle_intr(struct drm_device *dev); +extern void intel_opregion_gse_intr(struct drm_device *dev); +extern void intel_opregion_enable_asle(struct drm_device *dev); #else -static inline int intel_opregion_init(struct drm_device *dev, int resume) { return 0; } -static inline void intel_opregion_free(struct drm_device *dev, int suspend) { return; } -static inline void opregion_asle_intr(struct drm_device *dev) { return; } -static inline void ironlake_opregion_gse_intr(struct drm_device *dev) { return; } -static inline void opregion_enable_asle(struct drm_device *dev) { return; } +static inline void intel_opregion_init(struct drm_device *dev) { return; } +static inline void intel_opregion_fini(struct drm_device *dev) { return; } +static inline void intel_opregion_asle_intr(struct drm_device *dev) { return; } +static inline void intel_opregion_gse_intr(struct drm_device *dev) { return; } +static inline void intel_opregion_enable_asle(struct drm_device *dev) { return; } #endif /* modesetting */ @@ -1084,8 +1151,10 @@ extern void intel_detect_pch (struct drm_device *dev); extern int intel_trans_dp_port_sel (struct drm_crtc *crtc); /* overlay */ +#ifdef CONFIG_DEBUG_FS extern struct intel_overlay_error_state *intel_overlay_capture_error_state(struct drm_device *dev); extern void intel_overlay_print_error_state(struct seq_file *m, struct intel_overlay_error_state *error); +#endif /** * Lock test for when it's just for synchronization of ring access. @@ -1099,8 +1168,26 @@ extern void intel_overlay_print_error_state(struct seq_file *m, struct intel_ove LOCK_TEST_WITH_RETURN(dev, file_priv); \ } while (0) -#define I915_READ(reg) readl(dev_priv->regs + (reg)) -#define I915_WRITE(reg, val) writel(val, dev_priv->regs + (reg)) +static inline u32 i915_read(struct drm_i915_private *dev_priv, u32 reg) +{ + u32 val; + + val = readl(dev_priv->regs + reg); + if (dev_priv->debug_flags & I915_DEBUG_READ) + printk(KERN_ERR "read 0x%08x from 0x%08x\n", val, reg); + return val; +} + +static inline void i915_write(struct drm_i915_private *dev_priv, u32 reg, + u32 val) +{ + writel(val, dev_priv->regs + reg); + if (dev_priv->debug_flags & I915_DEBUG_WRITE) + printk(KERN_ERR "wrote 0x%08x to 0x%08x\n", val, reg); +} + +#define I915_READ(reg) i915_read(dev_priv, (reg)) +#define I915_WRITE(reg, val) i915_write(dev_priv, (reg), (val)) #define I915_READ16(reg) readw(dev_priv->regs + (reg)) #define I915_WRITE16(reg, val) writel(val, dev_priv->regs + (reg)) #define I915_READ8(reg) readb(dev_priv->regs + (reg)) @@ -1110,6 +1197,11 @@ extern void intel_overlay_print_error_state(struct seq_file *m, struct intel_ove #define POSTING_READ(reg) (void)I915_READ(reg) #define POSTING_READ16(reg) (void)I915_READ16(reg) +#define I915_DEBUG_ENABLE_IO() (dev_priv->debug_flags |= I915_DEBUG_READ | \ + I915_DEBUG_WRITE) +#define I915_DEBUG_DISABLE_IO() (dev_priv->debug_flags &= ~(I915_DEBUG_READ | \ + I915_DEBUG_WRITE)) + #define I915_VERBOSE 0 #define BEGIN_LP_RING(n) do { \ @@ -1166,8 +1258,6 @@ extern void intel_overlay_print_error_state(struct seq_file *m, struct intel_ove #define IS_I915GM(dev) ((dev)->pci_device == 0x2592) #define IS_I945G(dev) ((dev)->pci_device == 0x2772) #define IS_I945GM(dev) (INTEL_INFO(dev)->is_i945gm) -#define IS_I965G(dev) (INTEL_INFO(dev)->is_i965g) -#define IS_I965GM(dev) (INTEL_INFO(dev)->is_i965gm) #define IS_BROADWATER(dev) (INTEL_INFO(dev)->is_broadwater) #define IS_CRESTLINE(dev) (INTEL_INFO(dev)->is_crestline) #define IS_GM45(dev) ((dev)->pci_device == 0x2A42) @@ -1179,7 +1269,6 @@ extern void intel_overlay_print_error_state(struct seq_file *m, struct intel_ove #define IS_IRONLAKE_D(dev) ((dev)->pci_device == 0x0042) #define IS_IRONLAKE_M(dev) ((dev)->pci_device == 0x0046) #define IS_IRONLAKE(dev) (INTEL_INFO(dev)->is_ironlake) -#define IS_I9XX(dev) (INTEL_INFO(dev)->is_i9xx) #define IS_MOBILE(dev) (INTEL_INFO(dev)->is_mobile) #define IS_GEN2(dev) (INTEL_INFO(dev)->gen == 2) @@ -1188,26 +1277,27 @@ extern void intel_overlay_print_error_state(struct seq_file *m, struct intel_ove #define IS_GEN5(dev) (INTEL_INFO(dev)->gen == 5) #define IS_GEN6(dev) (INTEL_INFO(dev)->gen == 6) -#define HAS_BSD(dev) (IS_IRONLAKE(dev) || IS_G4X(dev)) +#define HAS_BSD(dev) (INTEL_INFO(dev)->has_bsd_ring) #define I915_NEED_GFX_HWS(dev) (INTEL_INFO(dev)->need_gfx_hws) +#define HAS_OVERLAY(dev) (INTEL_INFO(dev)->has_overlay) +#define OVERLAY_NEEDS_PHYSICAL(dev) (INTEL_INFO(dev)->overlay_needs_physical) + /* With the 945 and later, Y tiling got adjusted so that it was 32 128-byte * rows, which changed the alignment requirements and fence programming. */ -#define HAS_128_BYTE_Y_TILING(dev) (IS_I9XX(dev) && !(IS_I915G(dev) || \ +#define HAS_128_BYTE_Y_TILING(dev) (!IS_GEN2(dev) && !(IS_I915G(dev) || \ IS_I915GM(dev))) -#define SUPPORTS_DIGITAL_OUTPUTS(dev) (IS_I9XX(dev) && !IS_PINEVIEW(dev)) +#define SUPPORTS_DIGITAL_OUTPUTS(dev) (!IS_GEN2(dev) && !IS_PINEVIEW(dev)) #define SUPPORTS_INTEGRATED_HDMI(dev) (IS_G4X(dev) || IS_IRONLAKE(dev)) #define SUPPORTS_INTEGRATED_DP(dev) (IS_G4X(dev) || IS_IRONLAKE(dev)) #define SUPPORTS_EDP(dev) (IS_IRONLAKE_M(dev)) -#define SUPPORTS_TV(dev) (IS_I9XX(dev) && IS_MOBILE(dev) && \ - !IS_IRONLAKE(dev) && !IS_PINEVIEW(dev) && \ - !IS_GEN6(dev)) +#define SUPPORTS_TV(dev) (INTEL_INFO(dev)->supports_tv) #define I915_HAS_HOTPLUG(dev) (INTEL_INFO(dev)->has_hotplug) /* dsparb controlled by hw only */ #define DSPARB_HWCONTROL(dev) (IS_G4X(dev) || IS_IRONLAKE(dev)) -#define HAS_FW_BLC(dev) (IS_I9XX(dev) || IS_G4X(dev) || IS_IRONLAKE(dev)) +#define HAS_FW_BLC(dev) (INTEL_INFO(dev)->gen > 2) #define HAS_PIPE_CXSR(dev) (INTEL_INFO(dev)->has_pipe_cxsr) #define I915_HAS_FBC(dev) (INTEL_INFO(dev)->has_fbc) #define I915_HAS_RC6(dev) (INTEL_INFO(dev)->has_rc6) diff --git a/drivers/gpu/drm/i915/i915_gem.c b/drivers/gpu/drm/i915/i915_gem.c index cf4ffbee1c0..100a7537980 100644 --- a/drivers/gpu/drm/i915/i915_gem.c +++ b/drivers/gpu/drm/i915/i915_gem.c @@ -37,7 +37,9 @@ #include <linux/intel-gtt.h> static uint32_t i915_gem_get_gtt_alignment(struct drm_gem_object *obj); -static int i915_gem_object_flush_gpu_write_domain(struct drm_gem_object *obj); + +static int i915_gem_object_flush_gpu_write_domain(struct drm_gem_object *obj, + bool pipelined); static void i915_gem_object_flush_gtt_write_domain(struct drm_gem_object *obj); static void i915_gem_object_flush_cpu_write_domain(struct drm_gem_object *obj); static int i915_gem_object_set_to_cpu_domain(struct drm_gem_object *obj, @@ -46,7 +48,8 @@ static int i915_gem_object_set_cpu_read_domain_range(struct drm_gem_object *obj, uint64_t offset, uint64_t size); static void i915_gem_object_set_to_full_cpu_read_domain(struct drm_gem_object *obj); -static int i915_gem_object_wait_rendering(struct drm_gem_object *obj); +static int i915_gem_object_wait_rendering(struct drm_gem_object *obj, + bool interruptible); static int i915_gem_object_bind_to_gtt(struct drm_gem_object *obj, unsigned alignment); static void i915_gem_clear_fence_reg(struct drm_gem_object *obj); @@ -55,9 +58,111 @@ static int i915_gem_phys_pwrite(struct drm_device *dev, struct drm_gem_object *o struct drm_file *file_priv); static void i915_gem_free_object_tail(struct drm_gem_object *obj); +static int +i915_gem_object_get_pages(struct drm_gem_object *obj, + gfp_t gfpmask); + +static void +i915_gem_object_put_pages(struct drm_gem_object *obj); + static LIST_HEAD(shrink_list); static DEFINE_SPINLOCK(shrink_list_lock); +/* some bookkeeping */ +static void i915_gem_info_add_obj(struct drm_i915_private *dev_priv, + size_t size) +{ + dev_priv->mm.object_count++; + dev_priv->mm.object_memory += size; +} + +static void i915_gem_info_remove_obj(struct drm_i915_private *dev_priv, + size_t size) +{ + dev_priv->mm.object_count--; + dev_priv->mm.object_memory -= size; +} + +static void i915_gem_info_add_gtt(struct drm_i915_private *dev_priv, + size_t size) +{ + dev_priv->mm.gtt_count++; + dev_priv->mm.gtt_memory += size; +} + +static void i915_gem_info_remove_gtt(struct drm_i915_private *dev_priv, + size_t size) +{ + dev_priv->mm.gtt_count--; + dev_priv->mm.gtt_memory -= size; +} + +static void i915_gem_info_add_pin(struct drm_i915_private *dev_priv, + size_t size) +{ + dev_priv->mm.pin_count++; + dev_priv->mm.pin_memory += size; +} + +static void i915_gem_info_remove_pin(struct drm_i915_private *dev_priv, + size_t size) +{ + dev_priv->mm.pin_count--; + dev_priv->mm.pin_memory -= size; +} + +int +i915_gem_check_is_wedged(struct drm_device *dev) +{ + 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)) + return 0; + + ret = wait_for_completion_interruptible(x); + if (ret) + return ret; + + /* Success, we reset the GPU! */ + if (!atomic_read(&dev_priv->mm.wedged)) + return 0; + + /* 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 -EIO; +} + +static int i915_mutex_lock_interruptible(struct drm_device *dev) +{ + struct drm_i915_private *dev_priv = dev->dev_private; + int ret; + + ret = i915_gem_check_is_wedged(dev); + if (ret) + return ret; + + ret = mutex_lock_interruptible(&dev->struct_mutex); + if (ret) + return ret; + + if (atomic_read(&dev_priv->mm.wedged)) { + mutex_unlock(&dev->struct_mutex); + return -EAGAIN; + } + + WARN_ON(i915_verify_lists(dev)); + return 0; +} + static inline bool i915_gem_object_is_inactive(struct drm_i915_gem_object *obj_priv) { @@ -66,7 +171,8 @@ i915_gem_object_is_inactive(struct drm_i915_gem_object *obj_priv) obj_priv->pin_count == 0; } -int i915_gem_do_init(struct drm_device *dev, unsigned long start, +int i915_gem_do_init(struct drm_device *dev, + unsigned long start, unsigned long end) { drm_i915_private_t *dev_priv = dev->dev_private; @@ -80,7 +186,7 @@ int i915_gem_do_init(struct drm_device *dev, unsigned long start, drm_mm_init(&dev_priv->mm.gtt_space, start, end - start); - dev->gtt_total = (uint32_t) (end - start); + dev_priv->mm.gtt_total = end - start; return 0; } @@ -103,14 +209,16 @@ int i915_gem_get_aperture_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv) { + struct drm_i915_private *dev_priv = dev->dev_private; struct drm_i915_gem_get_aperture *args = data; if (!(dev->driver->driver_features & DRIVER_GEM)) return -ENODEV; - args->aper_size = dev->gtt_total; - args->aper_available_size = (args->aper_size - - atomic_read(&dev->pin_memory)); + mutex_lock(&dev->struct_mutex); + args->aper_size = dev_priv->mm.gtt_total; + args->aper_available_size = args->aper_size - dev_priv->mm.pin_memory; + mutex_unlock(&dev->struct_mutex); return 0; } @@ -136,14 +244,12 @@ i915_gem_create_ioctl(struct drm_device *dev, void *data, return -ENOMEM; ret = drm_gem_handle_create(file_priv, obj, &handle); + /* drop reference from allocate - handle holds it now */ + drm_gem_object_unreference_unlocked(obj); if (ret) { - drm_gem_object_unreference_unlocked(obj); return ret; } - /* Sink the floating reference from kref_init(handlecount) */ - drm_gem_object_handle_unreference_unlocked(obj); - args->handle = handle; return 0; } @@ -265,7 +371,9 @@ i915_gem_shmem_pread_fast(struct drm_device *dev, struct drm_gem_object *obj, user_data = (char __user *) (uintptr_t) args->data_ptr; remain = args->size; - mutex_lock(&dev->struct_mutex); + ret = i915_mutex_lock_interruptible(dev); + if (ret) + return ret; ret = i915_gem_object_get_pages(obj, 0); if (ret != 0) @@ -384,7 +492,9 @@ i915_gem_shmem_pread_slow(struct drm_device *dev, struct drm_gem_object *obj, do_bit17_swizzling = i915_gem_object_needs_bit17_swizzle(obj); - mutex_lock(&dev->struct_mutex); + ret = i915_mutex_lock_interruptible(dev); + if (ret) + goto fail_put_user_pages; ret = i915_gem_object_get_pages_or_evict(obj); if (ret) @@ -464,21 +574,27 @@ i915_gem_pread_ioctl(struct drm_device *dev, void *data, struct drm_i915_gem_pread *args = data; struct drm_gem_object *obj; struct drm_i915_gem_object *obj_priv; - int ret; + int ret = 0; obj = drm_gem_object_lookup(dev, file_priv, args->handle); if (obj == NULL) return -ENOENT; obj_priv = to_intel_bo(obj); - /* Bounds check source. - * - * XXX: This could use review for overflow issues... - */ - if (args->offset > obj->size || args->size > obj->size || - args->offset + args->size > obj->size) { - drm_gem_object_unreference_unlocked(obj); - return -EINVAL; + /* Bounds check source. */ + if (args->offset > obj->size || args->size > obj->size - args->offset) { + ret = -EINVAL; + goto out; + } + + if (args->size == 0) + goto out; + + if (!access_ok(VERIFY_WRITE, + (char __user *)(uintptr_t)args->data_ptr, + args->size)) { + ret = -EFAULT; + goto out; } if (i915_gem_object_needs_bit17_swizzle(obj)) { @@ -490,8 +606,8 @@ i915_gem_pread_ioctl(struct drm_device *dev, void *data, file_priv); } +out: drm_gem_object_unreference_unlocked(obj); - return ret; } @@ -580,11 +696,11 @@ i915_gem_gtt_pwrite_fast(struct drm_device *dev, struct drm_gem_object *obj, user_data = (char __user *) (uintptr_t) args->data_ptr; remain = args->size; - if (!access_ok(VERIFY_READ, user_data, remain)) - return -EFAULT; + ret = i915_mutex_lock_interruptible(dev); + if (ret) + return ret; - mutex_lock(&dev->struct_mutex); ret = i915_gem_object_pin(obj, 0); if (ret) { mutex_unlock(&dev->struct_mutex); @@ -679,7 +795,10 @@ i915_gem_gtt_pwrite_slow(struct drm_device *dev, struct drm_gem_object *obj, goto out_unpin_pages; } - mutex_lock(&dev->struct_mutex); + ret = i915_mutex_lock_interruptible(dev); + if (ret) + goto out_unpin_pages; + ret = i915_gem_object_pin(obj, 0); if (ret) goto out_unlock; @@ -753,7 +872,9 @@ i915_gem_shmem_pwrite_fast(struct drm_device *dev, struct drm_gem_object *obj, user_data = (char __user *) (uintptr_t) args->data_ptr; remain = args->size; - mutex_lock(&dev->struct_mutex); + ret = i915_mutex_lock_interruptible(dev); + if (ret) + return ret; ret = i915_gem_object_get_pages(obj, 0); if (ret != 0) @@ -849,7 +970,9 @@ i915_gem_shmem_pwrite_slow(struct drm_device *dev, struct drm_gem_object *obj, do_bit17_swizzling = i915_gem_object_needs_bit17_swizzle(obj); - mutex_lock(&dev->struct_mutex); + ret = i915_mutex_lock_interruptible(dev); + if (ret) + goto fail_put_user_pages; ret = i915_gem_object_get_pages_or_evict(obj); if (ret) @@ -934,14 +1057,20 @@ i915_gem_pwrite_ioctl(struct drm_device *dev, void *data, return -ENOENT; obj_priv = to_intel_bo(obj); - /* Bounds check destination. - * - * XXX: This could use review for overflow issues... - */ - if (args->offset > obj->size || args->size > obj->size || - args->offset + args->size > obj->size) { - drm_gem_object_unreference_unlocked(obj); - return -EINVAL; + /* Bounds check destination. */ + if (args->offset > obj->size || args->size > obj->size - args->offset) { + ret = -EINVAL; + goto out; + } + + if (args->size == 0) + goto out; + + if (!access_ok(VERIFY_READ, + (char __user *)(uintptr_t)args->data_ptr, + args->size)) { + ret = -EFAULT; + goto out; } /* We can only do the GTT pwrite on untiled buffers, as otherwise @@ -953,7 +1082,7 @@ i915_gem_pwrite_ioctl(struct drm_device *dev, void *data, if (obj_priv->phys_obj) ret = i915_gem_phys_pwrite(dev, obj, args, file_priv); else if (obj_priv->tiling_mode == I915_TILING_NONE && - dev->gtt_total != 0 && + obj_priv->gtt_space && obj->write_domain != I915_GEM_DOMAIN_CPU) { ret = i915_gem_gtt_pwrite_fast(dev, obj, args, file_priv); if (ret == -EFAULT) { @@ -975,8 +1104,8 @@ i915_gem_pwrite_ioctl(struct drm_device *dev, void *data, DRM_INFO("pwrite failed %d\n", ret); #endif +out: drm_gem_object_unreference_unlocked(obj); - return ret; } @@ -1017,14 +1146,14 @@ i915_gem_set_domain_ioctl(struct drm_device *dev, void *data, return -ENOENT; obj_priv = to_intel_bo(obj); - mutex_lock(&dev->struct_mutex); + ret = i915_mutex_lock_interruptible(dev); + if (ret) { + drm_gem_object_unreference_unlocked(obj); + return ret; + } intel_mark_busy(dev, obj); -#if WATCH_BUF - DRM_INFO("set_domain_ioctl %p(%zd), %08x %08x\n", - obj, obj->size, read_domains, write_domain); -#endif if (read_domains & I915_GEM_DOMAIN_GTT) { ret = i915_gem_object_set_to_gtt_domain(obj, write_domain != 0); @@ -1048,7 +1177,6 @@ i915_gem_set_domain_ioctl(struct drm_device *dev, void *data, ret = i915_gem_object_set_to_cpu_domain(obj, write_domain != 0); } - /* Maintain LRU order of "inactive" objects */ if (ret == 0 && i915_gem_object_is_inactive(obj_priv)) list_move_tail(&obj_priv->list, &dev_priv->mm.inactive_list); @@ -1067,27 +1195,23 @@ i915_gem_sw_finish_ioctl(struct drm_device *dev, void *data, { struct drm_i915_gem_sw_finish *args = data; struct drm_gem_object *obj; - struct drm_i915_gem_object *obj_priv; int ret = 0; if (!(dev->driver->driver_features & DRIVER_GEM)) return -ENODEV; - mutex_lock(&dev->struct_mutex); obj = drm_gem_object_lookup(dev, file_priv, args->handle); - if (obj == NULL) { - mutex_unlock(&dev->struct_mutex); + if (obj == NULL) return -ENOENT; - } -#if WATCH_BUF - DRM_INFO("%s: sw_finish %d (%p %zd)\n", - __func__, args->handle, obj, obj->size); -#endif - obj_priv = to_intel_bo(obj); + ret = i915_mutex_lock_interruptible(dev); + if (ret) { + drm_gem_object_unreference_unlocked(obj); + return ret; + } /* Pinned buffers may be scanout, so flush the cache */ - if (obj_priv->pin_count) + if (to_intel_bo(obj)->pin_count) i915_gem_object_flush_cpu_write_domain(obj); drm_gem_object_unreference(obj); @@ -1179,7 +1303,7 @@ int i915_gem_fault(struct vm_area_struct *vma, struct vm_fault *vmf) /* Need a new fence register? */ if (obj_priv->tiling_mode != I915_TILING_NONE) { - ret = i915_gem_object_get_fence_reg(obj); + ret = i915_gem_object_get_fence_reg(obj, true); if (ret) goto unlock; } @@ -1244,7 +1368,7 @@ i915_gem_create_mmap_offset(struct drm_gem_object *obj) obj->size / PAGE_SIZE, 0, 0); if (!list->file_offset_node) { DRM_ERROR("failed to allocate offset for bo %d\n", obj->name); - ret = -ENOMEM; + ret = -ENOSPC; goto out_free_list; } @@ -1256,9 +1380,9 @@ i915_gem_create_mmap_offset(struct drm_gem_object *obj) } list->hash.key = list->file_offset_node->start; - if (drm_ht_insert_item(&mm->offset_hash, &list->hash)) { + ret = drm_ht_insert_item(&mm->offset_hash, &list->hash); + if (ret) { DRM_ERROR("failed to add to map hash\n"); - ret = -ENOMEM; goto out_free_mm; } @@ -1343,14 +1467,14 @@ i915_gem_get_gtt_alignment(struct drm_gem_object *obj) * Minimum alignment is 4k (GTT page size), but might be greater * if a fence register is needed for the object. */ - if (IS_I965G(dev) || obj_priv->tiling_mode == I915_TILING_NONE) + if (INTEL_INFO(dev)->gen >= 4 || obj_priv->tiling_mode == I915_TILING_NONE) return 4096; /* * Previous chips need to be aligned to the size of the smallest * fence register that can contain the object. */ - if (IS_I9XX(dev)) + if (INTEL_INFO(dev)->gen == 3) start = 1024*1024; else start = 512*1024; @@ -1392,7 +1516,11 @@ i915_gem_mmap_gtt_ioctl(struct drm_device *dev, void *data, if (obj == NULL) return -ENOENT; - mutex_lock(&dev->struct_mutex); + ret = i915_mutex_lock_interruptible(dev); + if (ret) { + drm_gem_object_unreference_unlocked(obj); + return ret; + } obj_priv = to_intel_bo(obj); @@ -1434,7 +1562,7 @@ i915_gem_mmap_gtt_ioctl(struct drm_device *dev, void *data, return 0; } -void +static void i915_gem_object_put_pages(struct drm_gem_object *obj) { struct drm_i915_gem_object *obj_priv = to_intel_bo(obj); @@ -1468,13 +1596,24 @@ i915_gem_object_put_pages(struct drm_gem_object *obj) obj_priv->pages = NULL; } +static uint32_t +i915_gem_next_request_seqno(struct drm_device *dev, + struct intel_ring_buffer *ring) +{ + drm_i915_private_t *dev_priv = dev->dev_private; + + ring->outstanding_lazy_request = true; + return dev_priv->next_seqno; +} + static void -i915_gem_object_move_to_active(struct drm_gem_object *obj, uint32_t seqno, +i915_gem_object_move_to_active(struct drm_gem_object *obj, struct intel_ring_buffer *ring) { struct drm_device *dev = obj->dev; - drm_i915_private_t *dev_priv = dev->dev_private; struct drm_i915_gem_object *obj_priv = to_intel_bo(obj); + uint32_t seqno = i915_gem_next_request_seqno(dev, ring); + BUG_ON(ring == NULL); obj_priv->ring = ring; @@ -1483,10 +1622,9 @@ i915_gem_object_move_to_active(struct drm_gem_object *obj, uint32_t seqno, drm_gem_object_reference(obj); obj_priv->active = 1; } + /* Move from whatever list we were on to the tail of execution. */ - spin_lock(&dev_priv->mm.active_list_lock); list_move_tail(&obj_priv->list, &ring->active_list); - spin_unlock(&dev_priv->mm.active_list_lock); obj_priv->last_rendering_seqno = seqno; } @@ -1536,9 +1674,8 @@ i915_gem_object_move_to_inactive(struct drm_gem_object *obj) drm_i915_private_t *dev_priv = dev->dev_private; struct drm_i915_gem_object *obj_priv = to_intel_bo(obj); - i915_verify_inactive(dev, __FILE__, __LINE__); if (obj_priv->pin_count != 0) - list_del_init(&obj_priv->list); + list_move_tail(&obj_priv->list, &dev_priv->mm.pinned_list); else list_move_tail(&obj_priv->list, &dev_priv->mm.inactive_list); @@ -1550,12 +1687,12 @@ i915_gem_object_move_to_inactive(struct drm_gem_object *obj) obj_priv->active = 0; drm_gem_object_unreference(obj); } - i915_verify_inactive(dev, __FILE__, __LINE__); + WARN_ON(i915_verify_lists(dev)); } static void i915_gem_process_flushing_list(struct drm_device *dev, - uint32_t flush_domains, uint32_t seqno, + uint32_t flush_domains, struct intel_ring_buffer *ring) { drm_i915_private_t *dev_priv = dev->dev_private; @@ -1566,14 +1703,13 @@ i915_gem_process_flushing_list(struct drm_device *dev, gpu_write_list) { struct drm_gem_object *obj = &obj_priv->base; - if ((obj->write_domain & flush_domains) == - obj->write_domain && - obj_priv->ring->ring_flag == ring->ring_flag) { + if (obj->write_domain & flush_domains && + obj_priv->ring == ring) { uint32_t old_write_domain = obj->write_domain; obj->write_domain = 0; list_del_init(&obj_priv->gpu_write_list); - i915_gem_object_move_to_active(obj, seqno, ring); + i915_gem_object_move_to_active(obj, ring); /* update the fence lru list */ if (obj_priv->fence_reg != I915_FENCE_REG_NONE) { @@ -1591,23 +1727,27 @@ i915_gem_process_flushing_list(struct drm_device *dev, } uint32_t -i915_add_request(struct drm_device *dev, struct drm_file *file_priv, - uint32_t flush_domains, struct intel_ring_buffer *ring) +i915_add_request(struct drm_device *dev, + struct drm_file *file, + struct drm_i915_gem_request *request, + struct intel_ring_buffer *ring) { drm_i915_private_t *dev_priv = dev->dev_private; - struct drm_i915_file_private *i915_file_priv = NULL; - struct drm_i915_gem_request *request; + struct drm_i915_file_private *file_priv = NULL; uint32_t seqno; int was_empty; - if (file_priv != NULL) - i915_file_priv = file_priv->driver_priv; + if (file != NULL) + file_priv = file->driver_priv; - request = kzalloc(sizeof(*request), GFP_KERNEL); - if (request == NULL) - return 0; + if (request == NULL) { + request = kzalloc(sizeof(*request), GFP_KERNEL); + if (request == NULL) + return 0; + } - seqno = ring->add_request(dev, ring, file_priv, flush_domains); + seqno = ring->add_request(dev, ring, 0); + ring->outstanding_lazy_request = false; request->seqno = seqno; request->ring = ring; @@ -1615,23 +1755,20 @@ i915_add_request(struct drm_device *dev, struct drm_file *file_priv, was_empty = list_empty(&ring->request_list); list_add_tail(&request->list, &ring->request_list); - if (i915_file_priv) { + if (file_priv) { + spin_lock(&file_priv->mm.lock); + request->file_priv = file_priv; list_add_tail(&request->client_list, - &i915_file_priv->mm.request_list); - } else { - INIT_LIST_HEAD(&request->client_list); + &file_priv->mm.request_list); + spin_unlock(&file_priv->mm.lock); } - /* Associate any objects on the flushing list matching the write - * domain we're flushing with our flush. - */ - if (flush_domains != 0) - i915_gem_process_flushing_list(dev, flush_domains, seqno, ring); - if (!dev_priv->mm.suspended) { - mod_timer(&dev_priv->hangcheck_timer, jiffies + DRM_I915_HANGCHECK_PERIOD); + mod_timer(&dev_priv->hangcheck_timer, + jiffies + msecs_to_jiffies(DRM_I915_HANGCHECK_PERIOD)); if (was_empty) - queue_delayed_work(dev_priv->wq, &dev_priv->mm.retire_work, HZ); + queue_delayed_work(dev_priv->wq, + &dev_priv->mm.retire_work, HZ); } return seqno; } @@ -1642,91 +1779,105 @@ i915_add_request(struct drm_device *dev, struct drm_file *file_priv, * Ensures that all commands in the ring are finished * before signalling the CPU */ -static uint32_t +static void i915_retire_commands(struct drm_device *dev, struct intel_ring_buffer *ring) { uint32_t flush_domains = 0; /* The sampler always gets flushed on i965 (sigh) */ - if (IS_I965G(dev)) + if (INTEL_INFO(dev)->gen >= 4) flush_domains |= I915_GEM_DOMAIN_SAMPLER; ring->flush(dev, ring, I915_GEM_DOMAIN_COMMAND, flush_domains); - return flush_domains; } -/** - * Moves buffers associated only with the given active seqno from the active - * to inactive list, potentially freeing them. - */ -static void -i915_gem_retire_request(struct drm_device *dev, - struct drm_i915_gem_request *request) +static inline void +i915_gem_request_remove_from_client(struct drm_i915_gem_request *request) { - drm_i915_private_t *dev_priv = dev->dev_private; + struct drm_i915_file_private *file_priv = request->file_priv; - trace_i915_gem_request_retire(dev, request->seqno); + if (!file_priv) + return; - /* Move any buffers on the active list that are no longer referenced - * by the ringbuffer to the flushing/inactive lists as appropriate. - */ - spin_lock(&dev_priv->mm.active_list_lock); - while (!list_empty(&request->ring->active_list)) { - struct drm_gem_object *obj; + spin_lock(&file_priv->mm.lock); + list_del(&request->client_list); + request->file_priv = NULL; + spin_unlock(&file_priv->mm.lock); +} + +static void i915_gem_reset_ring_lists(struct drm_i915_private *dev_priv, + struct intel_ring_buffer *ring) +{ + while (!list_empty(&ring->request_list)) { + struct drm_i915_gem_request *request; + + request = list_first_entry(&ring->request_list, + struct drm_i915_gem_request, + list); + + list_del(&request->list); + i915_gem_request_remove_from_client(request); + kfree(request); + } + + while (!list_empty(&ring->active_list)) { struct drm_i915_gem_object *obj_priv; - obj_priv = list_first_entry(&request->ring->active_list, + obj_priv = list_first_entry(&ring->active_list, struct drm_i915_gem_object, list); - obj = &obj_priv->base; - /* If the seqno being retired doesn't match the oldest in the - * list, then the oldest in the list must still be newer than - * this seqno. - */ - if (obj_priv->last_rendering_seqno != request->seqno) - goto out; - -#if WATCH_LRU - DRM_INFO("%s: retire %d moves to inactive list %p\n", - __func__, request->seqno, obj); -#endif - - if (obj->write_domain != 0) - i915_gem_object_move_to_flushing(obj); - else { - /* Take a reference on the object so it won't be - * freed while the spinlock is held. The list - * protection for this spinlock is safe when breaking - * the lock like this since the next thing we do - * is just get the head of the list again. - */ - drm_gem_object_reference(obj); - i915_gem_object_move_to_inactive(obj); - spin_unlock(&dev_priv->mm.active_list_lock); - drm_gem_object_unreference(obj); - spin_lock(&dev_priv->mm.active_list_lock); - } + obj_priv->base.write_domain = 0; + list_del_init(&obj_priv->gpu_write_list); + i915_gem_object_move_to_inactive(&obj_priv->base); } -out: - spin_unlock(&dev_priv->mm.active_list_lock); } -/** - * Returns true if seq1 is later than seq2. - */ -bool -i915_seqno_passed(uint32_t seq1, uint32_t seq2) +void i915_gem_reset(struct drm_device *dev) { - return (int32_t)(seq1 - seq2) >= 0; -} + struct drm_i915_private *dev_priv = dev->dev_private; + struct drm_i915_gem_object *obj_priv; + int i; -uint32_t -i915_get_gem_seqno(struct drm_device *dev, - struct intel_ring_buffer *ring) -{ - return ring->get_gem_seqno(dev, ring); + i915_gem_reset_ring_lists(dev_priv, &dev_priv->render_ring); + if (HAS_BSD(dev)) + i915_gem_reset_ring_lists(dev_priv, &dev_priv->bsd_ring); + + /* Remove anything from the flushing lists. The GPU cache is likely + * to be lost on reset along with the data, so simply move the + * lost bo to the inactive list. + */ + while (!list_empty(&dev_priv->mm.flushing_list)) { + obj_priv = list_first_entry(&dev_priv->mm.flushing_list, + struct drm_i915_gem_object, + list); + + obj_priv->base.write_domain = 0; + list_del_init(&obj_priv->gpu_write_list); + i915_gem_object_move_to_inactive(&obj_priv->base); + } + + /* Move everything out of the GPU domains to ensure we do any + * necessary invalidation upon reuse. + */ + list_for_each_entry(obj_priv, + &dev_priv->mm.inactive_list, + list) + { + obj_priv->base.read_domains &= ~I915_GEM_GPU_DOMAINS; + } + + /* The fence registers are invalidated so clear them out */ + for (i = 0; i < 16; i++) { + struct drm_i915_fence_reg *reg; + + reg = &dev_priv->fence_regs[i]; + if (!reg->obj) + continue; + + i915_gem_clear_fence_reg(reg->obj); + } } /** @@ -1739,38 +1890,58 @@ i915_gem_retire_requests_ring(struct drm_device *dev, drm_i915_private_t *dev_priv = dev->dev_private; uint32_t seqno; - if (!ring->status_page.page_addr - || list_empty(&ring->request_list)) + if (!ring->status_page.page_addr || + list_empty(&ring->request_list)) return; - seqno = i915_get_gem_seqno(dev, ring); + WARN_ON(i915_verify_lists(dev)); + seqno = ring->get_seqno(dev, ring); while (!list_empty(&ring->request_list)) { struct drm_i915_gem_request *request; - uint32_t retiring_seqno; request = list_first_entry(&ring->request_list, struct drm_i915_gem_request, list); - retiring_seqno = request->seqno; - if (i915_seqno_passed(seqno, retiring_seqno) || - atomic_read(&dev_priv->mm.wedged)) { - i915_gem_retire_request(dev, request); + if (!i915_seqno_passed(seqno, request->seqno)) + break; + + trace_i915_gem_request_retire(dev, request->seqno); + + list_del(&request->list); + i915_gem_request_remove_from_client(request); + kfree(request); + } + + /* Move any buffers on the active list that are no longer referenced + * by the ringbuffer to the flushing/inactive lists as appropriate. + */ + while (!list_empty(&ring->active_list)) { + struct drm_gem_object *obj; + struct drm_i915_gem_object *obj_priv; + + obj_priv = list_first_entry(&ring->active_list, + struct drm_i915_gem_object, + list); - list_del(&request->list); - list_del(&request->client_list); - kfree(request); - } else + if (!i915_seqno_passed(seqno, obj_priv->last_rendering_seqno)) break; + + obj = &obj_priv->base; + if (obj->write_domain != 0) + i915_gem_object_move_to_flushing(obj); + else + i915_gem_object_move_to_inactive(obj); } if (unlikely (dev_priv->trace_irq_seqno && i915_seqno_passed(dev_priv->trace_irq_seqno, seqno))) { - ring->user_irq_put(dev, ring); dev_priv->trace_irq_seqno = 0; } + + WARN_ON(i915_verify_lists(dev)); } void @@ -1797,7 +1968,7 @@ i915_gem_retire_requests(struct drm_device *dev) i915_gem_retire_requests_ring(dev, &dev_priv->bsd_ring); } -void +static void i915_gem_retire_work_handler(struct work_struct *work) { drm_i915_private_t *dev_priv; @@ -1807,7 +1978,12 @@ i915_gem_retire_work_handler(struct work_struct *work) mm.retire_work.work); dev = dev_priv->dev; - mutex_lock(&dev->struct_mutex); + /* Come back later if the device is busy... */ + if (!mutex_trylock(&dev->struct_mutex)) { + queue_delayed_work(dev_priv->wq, &dev_priv->mm.retire_work, HZ); + return; + } + i915_gem_retire_requests(dev); if (!dev_priv->mm.suspended && @@ -1820,7 +1996,7 @@ i915_gem_retire_work_handler(struct work_struct *work) int i915_do_wait_request(struct drm_device *dev, uint32_t seqno, - int interruptible, struct intel_ring_buffer *ring) + bool interruptible, struct intel_ring_buffer *ring) { drm_i915_private_t *dev_priv = dev->dev_private; u32 ier; @@ -1829,9 +2005,16 @@ i915_do_wait_request(struct drm_device *dev, uint32_t seqno, BUG_ON(seqno == 0); if (atomic_read(&dev_priv->mm.wedged)) - return -EIO; + return -EAGAIN; + + if (ring->outstanding_lazy_request) { + seqno = i915_add_request(dev, NULL, NULL, ring); + if (seqno == 0) + return -ENOMEM; + } + BUG_ON(seqno == dev_priv->next_seqno); - if (!i915_seqno_passed(ring->get_gem_seqno(dev, ring), seqno)) { + if (!i915_seqno_passed(ring->get_seqno(dev, ring), seqno)) { if (HAS_PCH_SPLIT(dev)) ier = I915_READ(DEIER) | I915_READ(GTIER); else @@ -1850,12 +2033,12 @@ i915_do_wait_request(struct drm_device *dev, uint32_t seqno, if (interruptible) ret = wait_event_interruptible(ring->irq_queue, i915_seqno_passed( - ring->get_gem_seqno(dev, ring), seqno) + ring->get_seqno(dev, ring), seqno) || atomic_read(&dev_priv->mm.wedged)); else wait_event(ring->irq_queue, i915_seqno_passed( - ring->get_gem_seqno(dev, ring), seqno) + ring->get_seqno(dev, ring), seqno) || atomic_read(&dev_priv->mm.wedged)); ring->user_irq_put(dev, ring); @@ -1864,11 +2047,12 @@ i915_do_wait_request(struct drm_device *dev, uint32_t seqno, trace_i915_gem_request_wait_end(dev, seqno); } if (atomic_read(&dev_priv->mm.wedged)) - ret = -EIO; + ret = -EAGAIN; if (ret && ret != -ERESTARTSYS) - DRM_ERROR("%s returns %d (awaiting %d at %d)\n", - __func__, ret, seqno, ring->get_gem_seqno(dev, ring)); + DRM_ERROR("%s returns %d (awaiting %d at %d, next %d)\n", + __func__, ret, seqno, ring->get_seqno(dev, ring), + dev_priv->next_seqno); /* Directly dispatch request retiring. While we have the work queue * to handle this, the waiter on a request often wants an associated @@ -1887,27 +2071,44 @@ i915_do_wait_request(struct drm_device *dev, uint32_t seqno, */ static int i915_wait_request(struct drm_device *dev, uint32_t seqno, - struct intel_ring_buffer *ring) + struct intel_ring_buffer *ring) { return i915_do_wait_request(dev, seqno, 1, ring); } static void +i915_gem_flush_ring(struct drm_device *dev, + struct drm_file *file_priv, + struct intel_ring_buffer *ring, + uint32_t invalidate_domains, + uint32_t flush_domains) +{ + ring->flush(dev, ring, invalidate_domains, flush_domains); + i915_gem_process_flushing_list(dev, flush_domains, ring); +} + +static void i915_gem_flush(struct drm_device *dev, + struct drm_file *file_priv, uint32_t invalidate_domains, - uint32_t flush_domains) + uint32_t flush_domains, + uint32_t flush_rings) { drm_i915_private_t *dev_priv = dev->dev_private; + if (flush_domains & I915_GEM_DOMAIN_CPU) drm_agp_chipset_flush(dev); - dev_priv->render_ring.flush(dev, &dev_priv->render_ring, - invalidate_domains, - flush_domains); - if (HAS_BSD(dev)) - dev_priv->bsd_ring.flush(dev, &dev_priv->bsd_ring, - invalidate_domains, - flush_domains); + if ((flush_domains | invalidate_domains) & I915_GEM_GPU_DOMAINS) { + if (flush_rings & RING_RENDER) + i915_gem_flush_ring(dev, file_priv, + &dev_priv->render_ring, + invalidate_domains, flush_domains); + if (flush_rings & RING_BSD) + i915_gem_flush_ring(dev, file_priv, + &dev_priv->bsd_ring, + invalidate_domains, flush_domains); + } } /** @@ -1915,7 +2116,8 @@ i915_gem_flush(struct drm_device *dev, * safe to unbind from the GTT or access from the CPU. */ static int -i915_gem_object_wait_rendering(struct drm_gem_object *obj) +i915_gem_object_wait_rendering(struct drm_gem_object *obj, + bool interruptible) { struct drm_device *dev = obj->dev; struct drm_i915_gem_object *obj_priv = to_intel_bo(obj); @@ -1930,13 +2132,11 @@ i915_gem_object_wait_rendering(struct drm_gem_object *obj) * it. */ if (obj_priv->active) { -#if WATCH_BUF - DRM_INFO("%s: object %p wait for seqno %08x\n", - __func__, obj, obj_priv->last_rendering_seqno); -#endif - ret = i915_wait_request(dev, - obj_priv->last_rendering_seqno, obj_priv->ring); - if (ret != 0) + ret = i915_do_wait_request(dev, + obj_priv->last_rendering_seqno, + interruptible, + obj_priv->ring); + if (ret) return ret; } @@ -1950,14 +2150,10 @@ int i915_gem_object_unbind(struct drm_gem_object *obj) { struct drm_device *dev = obj->dev; - drm_i915_private_t *dev_priv = dev->dev_private; + struct drm_i915_private *dev_priv = dev->dev_private; struct drm_i915_gem_object *obj_priv = to_intel_bo(obj); int ret = 0; -#if WATCH_BUF - DRM_INFO("%s:%d %p\n", __func__, __LINE__, obj); - DRM_INFO("gtt_space %p\n", obj_priv->gtt_space); -#endif if (obj_priv->gtt_space == NULL) return 0; @@ -1982,33 +2178,26 @@ i915_gem_object_unbind(struct drm_gem_object *obj) * should be safe and we need to cleanup or else we might * cause memory corruption through use-after-free. */ + if (ret) { + i915_gem_clflush_object(obj); + obj->read_domains = obj->write_domain = I915_GEM_DOMAIN_CPU; + } /* release the fence reg _after_ flushing */ if (obj_priv->fence_reg != I915_FENCE_REG_NONE) i915_gem_clear_fence_reg(obj); - if (obj_priv->agp_mem != NULL) { - drm_unbind_agp(obj_priv->agp_mem); - drm_free_agp(obj_priv->agp_mem, obj->size / PAGE_SIZE); - obj_priv->agp_mem = NULL; - } + drm_unbind_agp(obj_priv->agp_mem); + drm_free_agp(obj_priv->agp_mem, obj->size / PAGE_SIZE); i915_gem_object_put_pages(obj); BUG_ON(obj_priv->pages_refcount); - if (obj_priv->gtt_space) { - atomic_dec(&dev->gtt_count); - atomic_sub(obj->size, &dev->gtt_memory); + i915_gem_info_remove_gtt(dev_priv, obj->size); + list_del_init(&obj_priv->list); - drm_mm_put_block(obj_priv->gtt_space); - obj_priv->gtt_space = NULL; - } - - /* Remove ourselves from the LRU list if present. */ - spin_lock(&dev_priv->mm.active_list_lock); - if (!list_empty(&obj_priv->list)) - list_del_init(&obj_priv->list); - spin_unlock(&dev_priv->mm.active_list_lock); + drm_mm_put_block(obj_priv->gtt_space); + obj_priv->gtt_space = NULL; if (i915_gem_object_is_purgeable(obj_priv)) i915_gem_object_truncate(obj); @@ -2018,48 +2207,45 @@ i915_gem_object_unbind(struct drm_gem_object *obj) return ret; } +static int i915_ring_idle(struct drm_device *dev, + struct intel_ring_buffer *ring) +{ + i915_gem_flush_ring(dev, NULL, ring, + I915_GEM_GPU_DOMAINS, I915_GEM_GPU_DOMAINS); + return i915_wait_request(dev, + i915_gem_next_request_seqno(dev, ring), + ring); +} + int i915_gpu_idle(struct drm_device *dev) { drm_i915_private_t *dev_priv = dev->dev_private; bool lists_empty; - uint32_t seqno1, seqno2; int ret; - spin_lock(&dev_priv->mm.active_list_lock); lists_empty = (list_empty(&dev_priv->mm.flushing_list) && list_empty(&dev_priv->render_ring.active_list) && (!HAS_BSD(dev) || list_empty(&dev_priv->bsd_ring.active_list))); - spin_unlock(&dev_priv->mm.active_list_lock); - if (lists_empty) return 0; /* Flush everything onto the inactive list. */ - i915_gem_flush(dev, I915_GEM_GPU_DOMAINS, I915_GEM_GPU_DOMAINS); - seqno1 = i915_add_request(dev, NULL, I915_GEM_GPU_DOMAINS, - &dev_priv->render_ring); - if (seqno1 == 0) - return -ENOMEM; - ret = i915_wait_request(dev, seqno1, &dev_priv->render_ring); + ret = i915_ring_idle(dev, &dev_priv->render_ring); + if (ret) + return ret; if (HAS_BSD(dev)) { - seqno2 = i915_add_request(dev, NULL, I915_GEM_GPU_DOMAINS, - &dev_priv->bsd_ring); - if (seqno2 == 0) - return -ENOMEM; - - ret = i915_wait_request(dev, seqno2, &dev_priv->bsd_ring); + ret = i915_ring_idle(dev, &dev_priv->bsd_ring); if (ret) return ret; } - - return ret; + return 0; } -int +static int i915_gem_object_get_pages(struct drm_gem_object *obj, gfp_t gfpmask) { @@ -2239,7 +2425,8 @@ static void i830_write_fence_reg(struct drm_i915_fence_reg *reg) I915_WRITE(FENCE_REG_830_0 + (regnum * 4), val); } -static int i915_find_fence_reg(struct drm_device *dev) +static int i915_find_fence_reg(struct drm_device *dev, + bool interruptible) { struct drm_i915_fence_reg *reg = NULL; struct drm_i915_gem_object *obj_priv = NULL; @@ -2284,7 +2471,7 @@ static int i915_find_fence_reg(struct drm_device *dev) * private reference to obj like the other callers of put_fence_reg * (set_tiling ioctl) do. */ drm_gem_object_reference(obj); - ret = i915_gem_object_put_fence_reg(obj); + ret = i915_gem_object_put_fence_reg(obj, interruptible); drm_gem_object_unreference(obj); if (ret != 0) return ret; @@ -2306,7 +2493,8 @@ static int i915_find_fence_reg(struct drm_device *dev) * and tiling format. */ int -i915_gem_object_get_fence_reg(struct drm_gem_object *obj) +i915_gem_object_get_fence_reg(struct drm_gem_object *obj, + bool interruptible) { struct drm_device *dev = obj->dev; struct drm_i915_private *dev_priv = dev->dev_private; @@ -2341,7 +2529,7 @@ i915_gem_object_get_fence_reg(struct drm_gem_object *obj) break; } - ret = i915_find_fence_reg(dev); + ret = i915_find_fence_reg(dev, interruptible); if (ret < 0) return ret; @@ -2400,7 +2588,7 @@ i915_gem_clear_fence_reg(struct drm_gem_object *obj) I915_WRITE64(FENCE_REG_965_0 + (obj_priv->fence_reg * 8), 0); break; case 3: - if (obj_priv->fence_reg > 8) + if (obj_priv->fence_reg >= 8) fence_reg = FENCE_REG_945_8 + (obj_priv->fence_reg - 8) * 4; else case 2: @@ -2419,15 +2607,19 @@ i915_gem_clear_fence_reg(struct drm_gem_object *obj) * i915_gem_object_put_fence_reg - waits on outstanding fenced access * to the buffer to finish, and then resets the fence register. * @obj: tiled object holding a fence register. + * @bool: whether the wait upon the fence is interruptible * * Zeroes out the fence register itself and clears out the associated * data structures in dev_priv and obj_priv. */ int -i915_gem_object_put_fence_reg(struct drm_gem_object *obj) +i915_gem_object_put_fence_reg(struct drm_gem_object *obj, + bool interruptible) { struct drm_device *dev = obj->dev; + struct drm_i915_private *dev_priv = dev->dev_private; struct drm_i915_gem_object *obj_priv = to_intel_bo(obj); + struct drm_i915_fence_reg *reg; if (obj_priv->fence_reg == I915_FENCE_REG_NONE) return 0; @@ -2442,20 +2634,23 @@ i915_gem_object_put_fence_reg(struct drm_gem_object *obj) * therefore we must wait for any outstanding access to complete * before clearing the fence. */ - if (!IS_I965G(dev)) { + reg = &dev_priv->fence_regs[obj_priv->fence_reg]; + if (reg->gpu) { int ret; - ret = i915_gem_object_flush_gpu_write_domain(obj); - if (ret != 0) + ret = i915_gem_object_flush_gpu_write_domain(obj, true); + if (ret) return ret; - ret = i915_gem_object_wait_rendering(obj); - if (ret != 0) + ret = i915_gem_object_wait_rendering(obj, interruptible); + if (ret) return ret; + + reg->gpu = false; } i915_gem_object_flush_gtt_write_domain(obj); - i915_gem_clear_fence_reg (obj); + i915_gem_clear_fence_reg(obj); return 0; } @@ -2488,7 +2683,7 @@ i915_gem_object_bind_to_gtt(struct drm_gem_object *obj, unsigned alignment) /* If the object is bigger than the entire aperture, reject it early * before evicting everything in a vain attempt to find space. */ - if (obj->size > dev->gtt_total) { + if (obj->size > dev_priv->mm.gtt_total) { DRM_ERROR("Attempting to bind an object larger than the aperture\n"); return -E2BIG; } @@ -2506,9 +2701,6 @@ i915_gem_object_bind_to_gtt(struct drm_gem_object *obj, unsigned alignment) /* If the gtt is empty and we're still having trouble * fitting our object in, we're out of memory. */ -#if WATCH_LRU - DRM_INFO("%s: GTT full, evicting something\n", __func__); -#endif ret = i915_gem_evict_something(dev, obj->size, alignment); if (ret) return ret; @@ -2516,10 +2708,6 @@ i915_gem_object_bind_to_gtt(struct drm_gem_object *obj, unsigned alignment) goto search_free; } -#if WATCH_BUF - DRM_INFO("Binding object of size %zd at 0x%08x\n", - obj->size, obj_priv->gtt_offset); -#endif ret = i915_gem_object_get_pages(obj, gfpmask); if (ret) { drm_mm_put_block(obj_priv->gtt_space); @@ -2564,11 +2752,10 @@ i915_gem_object_bind_to_gtt(struct drm_gem_object *obj, unsigned alignment) goto search_free; } - atomic_inc(&dev->gtt_count); - atomic_add(obj->size, &dev->gtt_memory); /* keep track of bounds object by adding it to the inactive list */ list_add_tail(&obj_priv->list, &dev_priv->mm.inactive_list); + i915_gem_info_add_gtt(dev_priv, obj->size); /* Assert that the object is not currently in any GPU domain. As it * wasn't in the GTT, there shouldn't be any way it could have been in @@ -2601,25 +2788,30 @@ i915_gem_clflush_object(struct drm_gem_object *obj) /** Flushes any GPU write domain for the object if it's dirty. */ static int -i915_gem_object_flush_gpu_write_domain(struct drm_gem_object *obj) +i915_gem_object_flush_gpu_write_domain(struct drm_gem_object *obj, + bool pipelined) { struct drm_device *dev = obj->dev; uint32_t old_write_domain; - struct drm_i915_gem_object *obj_priv = to_intel_bo(obj); if ((obj->write_domain & I915_GEM_GPU_DOMAINS) == 0) return 0; /* Queue the GPU write cache flushing we need. */ old_write_domain = obj->write_domain; - i915_gem_flush(dev, 0, obj->write_domain); - if (i915_add_request(dev, NULL, obj->write_domain, obj_priv->ring) == 0) - return -ENOMEM; + i915_gem_flush_ring(dev, NULL, + to_intel_bo(obj)->ring, + 0, obj->write_domain); + BUG_ON(obj->write_domain); trace_i915_gem_object_change_domain(obj, obj->read_domains, old_write_domain); - return 0; + + if (pipelined) + return 0; + + return i915_gem_object_wait_rendering(obj, true); } /** Flushes the GTT write domain for the object if it's dirty. */ @@ -2663,26 +2855,6 @@ i915_gem_object_flush_cpu_write_domain(struct drm_gem_object *obj) old_write_domain); } -int -i915_gem_object_flush_write_domain(struct drm_gem_object *obj) -{ - int ret = 0; - - switch (obj->write_domain) { - case I915_GEM_DOMAIN_GTT: - i915_gem_object_flush_gtt_write_domain(obj); - break; - case I915_GEM_DOMAIN_CPU: - i915_gem_object_flush_cpu_write_domain(obj); - break; - default: - ret = i915_gem_object_flush_gpu_write_domain(obj); - break; - } - - return ret; -} - /** * Moves a single object to the GTT read, and possibly write domain. * @@ -2700,32 +2872,28 @@ i915_gem_object_set_to_gtt_domain(struct drm_gem_object *obj, int write) if (obj_priv->gtt_space == NULL) return -EINVAL; - ret = i915_gem_object_flush_gpu_write_domain(obj); + ret = i915_gem_object_flush_gpu_write_domain(obj, false); if (ret != 0) return ret; - /* Wait on any GPU rendering and flushing to occur. */ - ret = i915_gem_object_wait_rendering(obj); - if (ret != 0) - return ret; + i915_gem_object_flush_cpu_write_domain(obj); + + if (write) { + ret = i915_gem_object_wait_rendering(obj, true); + if (ret) + return ret; + } old_write_domain = obj->write_domain; old_read_domains = obj->read_domains; - /* If we're writing through the GTT domain, then CPU and GPU caches - * will need to be invalidated at next use. - */ - if (write) - obj->read_domains &= I915_GEM_DOMAIN_GTT; - - i915_gem_object_flush_cpu_write_domain(obj); - /* It should now be out of any other write domains, and we can update * the domain values for our changes. */ BUG_ON((obj->write_domain & ~I915_GEM_DOMAIN_GTT) != 0); obj->read_domains |= I915_GEM_DOMAIN_GTT; if (write) { + obj->read_domains = I915_GEM_DOMAIN_GTT; obj->write_domain = I915_GEM_DOMAIN_GTT; obj_priv->dirty = 1; } @@ -2742,51 +2910,36 @@ i915_gem_object_set_to_gtt_domain(struct drm_gem_object *obj, int write) * wait, as in modesetting process we're not supposed to be interrupted. */ int -i915_gem_object_set_to_display_plane(struct drm_gem_object *obj) +i915_gem_object_set_to_display_plane(struct drm_gem_object *obj, + bool pipelined) { - struct drm_device *dev = obj->dev; struct drm_i915_gem_object *obj_priv = to_intel_bo(obj); - uint32_t old_write_domain, old_read_domains; + uint32_t old_read_domains; int ret; /* Not valid to be called on unbound objects. */ if (obj_priv->gtt_space == NULL) return -EINVAL; - ret = i915_gem_object_flush_gpu_write_domain(obj); + ret = i915_gem_object_flush_gpu_write_domain(obj, true); if (ret) return ret; - /* Wait on any GPU rendering and flushing to occur. */ - if (obj_priv->active) { -#if WATCH_BUF - DRM_INFO("%s: object %p wait for seqno %08x\n", - __func__, obj, obj_priv->last_rendering_seqno); -#endif - ret = i915_do_wait_request(dev, - obj_priv->last_rendering_seqno, - 0, - obj_priv->ring); - if (ret != 0) + /* Currently, we are always called from an non-interruptible context. */ + if (!pipelined) { + ret = i915_gem_object_wait_rendering(obj, false); + if (ret) return ret; } i915_gem_object_flush_cpu_write_domain(obj); - old_write_domain = obj->write_domain; old_read_domains = obj->read_domains; - - /* It should now be out of any other write domains, and we can update - * the domain values for our changes. - */ - BUG_ON((obj->write_domain & ~I915_GEM_DOMAIN_GTT) != 0); - obj->read_domains = I915_GEM_DOMAIN_GTT; - obj->write_domain = I915_GEM_DOMAIN_GTT; - obj_priv->dirty = 1; + obj->read_domains |= I915_GEM_DOMAIN_GTT; trace_i915_gem_object_change_domain(obj, old_read_domains, - old_write_domain); + obj->write_domain); return 0; } @@ -2803,12 +2956,7 @@ i915_gem_object_set_to_cpu_domain(struct drm_gem_object *obj, int write) uint32_t old_write_domain, old_read_domains; int ret; - ret = i915_gem_object_flush_gpu_write_domain(obj); - if (ret) - return ret; - - /* Wait on any GPU rendering and flushing to occur. */ - ret = i915_gem_object_wait_rendering(obj); + ret = i915_gem_object_flush_gpu_write_domain(obj, false); if (ret != 0) return ret; @@ -2819,6 +2967,12 @@ i915_gem_object_set_to_cpu_domain(struct drm_gem_object *obj, int write) */ i915_gem_object_set_to_full_cpu_read_domain(obj); + if (write) { + ret = i915_gem_object_wait_rendering(obj, true); + if (ret) + return ret; + } + old_write_domain = obj->write_domain; old_read_domains = obj->read_domains; @@ -2838,7 +2992,7 @@ i915_gem_object_set_to_cpu_domain(struct drm_gem_object *obj, int write) * need to be invalidated at next use. */ if (write) { - obj->read_domains &= I915_GEM_DOMAIN_CPU; + obj->read_domains = I915_GEM_DOMAIN_CPU; obj->write_domain = I915_GEM_DOMAIN_CPU; } @@ -2964,7 +3118,7 @@ static void i915_gem_object_set_to_gpu_domain(struct drm_gem_object *obj) { struct drm_device *dev = obj->dev; - drm_i915_private_t *dev_priv = dev->dev_private; + struct drm_i915_private *dev_priv = dev->dev_private; struct drm_i915_gem_object *obj_priv = to_intel_bo(obj); uint32_t invalidate_domains = 0; uint32_t flush_domains = 0; @@ -2975,12 +3129,6 @@ i915_gem_object_set_to_gpu_domain(struct drm_gem_object *obj) intel_mark_busy(dev, obj); -#if WATCH_BUF - DRM_INFO("%s: object %p read %08x -> %08x write %08x -> %08x\n", - __func__, obj, - obj->read_domains, obj->pending_read_domains, - obj->write_domain, obj->pending_write_domain); -#endif /* * If the object isn't moving to a new write domain, * let the object stay in multiple read domains @@ -3007,13 +3155,8 @@ i915_gem_object_set_to_gpu_domain(struct drm_gem_object *obj) * stale data. That is, any new read domains. */ invalidate_domains |= obj->pending_read_domains & ~obj->read_domains; - if ((flush_domains | invalidate_domains) & I915_GEM_DOMAIN_CPU) { -#if WATCH_BUF - DRM_INFO("%s: CPU domain flush %08x invalidate %08x\n", - __func__, flush_domains, invalidate_domains); -#endif + if ((flush_domains | invalidate_domains) & I915_GEM_DOMAIN_CPU) i915_gem_clflush_object(obj); - } old_read_domains = obj->read_domains; @@ -3027,21 +3170,10 @@ i915_gem_object_set_to_gpu_domain(struct drm_gem_object *obj) obj->pending_write_domain = obj->write_domain; obj->read_domains = obj->pending_read_domains; - if (flush_domains & I915_GEM_GPU_DOMAINS) { - if (obj_priv->ring == &dev_priv->render_ring) - dev_priv->flush_rings |= FLUSH_RENDER_RING; - else if (obj_priv->ring == &dev_priv->bsd_ring) - dev_priv->flush_rings |= FLUSH_BSD_RING; - } - dev->invalidate_domains |= invalidate_domains; dev->flush_domains |= flush_domains; -#if WATCH_BUF - DRM_INFO("%s: read %08x write %08x invalidate %08x flush %08x\n", - __func__, - obj->read_domains, obj->write_domain, - dev->invalidate_domains, dev->flush_domains); -#endif + if (obj_priv->ring) + dev_priv->mm.flush_rings |= obj_priv->ring->id; trace_i915_gem_object_change_domain(obj, old_read_domains, @@ -3104,12 +3236,7 @@ i915_gem_object_set_cpu_read_domain_range(struct drm_gem_object *obj, if (offset == 0 && size == obj->size) return i915_gem_object_set_to_cpu_domain(obj, 0); - ret = i915_gem_object_flush_gpu_write_domain(obj); - if (ret) - return ret; - - /* Wait on any GPU rendering and flushing to occur. */ - ret = i915_gem_object_wait_rendering(obj); + ret = i915_gem_object_flush_gpu_write_domain(obj, false); if (ret != 0) return ret; i915_gem_object_flush_gtt_write_domain(obj); @@ -3196,11 +3323,13 @@ i915_gem_object_pin_and_relocate(struct drm_gem_object *obj, * properly handle blits to/from tiled surfaces. */ if (need_fence) { - ret = i915_gem_object_get_fence_reg(obj); + ret = i915_gem_object_get_fence_reg(obj, true); if (ret != 0) { i915_gem_object_unpin(obj); return ret; } + + dev_priv->fence_regs[obj_priv->fence_reg].gpu = true; } entry->offset = obj_priv->gtt_offset; @@ -3258,6 +3387,8 @@ i915_gem_object_pin_and_relocate(struct drm_gem_object *obj, (int) reloc->offset, reloc->read_domains, reloc->write_domain); + drm_gem_object_unreference(target_obj); + i915_gem_object_unpin(obj); return -EINVAL; } if (reloc->write_domain & I915_GEM_DOMAIN_CPU || @@ -3333,7 +3464,7 @@ i915_gem_object_pin_and_relocate(struct drm_gem_object *obj, if (ret != 0) { drm_gem_object_unreference(target_obj); i915_gem_object_unpin(obj); - return -EINVAL; + return ret; } /* Map the page containing the relocation we're going to @@ -3348,11 +3479,6 @@ i915_gem_object_pin_and_relocate(struct drm_gem_object *obj, (reloc_offset & (PAGE_SIZE - 1))); reloc_val = target_obj_priv->gtt_offset + reloc->delta; -#if WATCH_BUF - DRM_INFO("Applied relocation: %p@0x%08x %08x -> %08x\n", - obj, (unsigned int) reloc->offset, - readl(reloc_entry), reloc_val); -#endif writel(reloc_val, reloc_entry); io_mapping_unmap_atomic(reloc_page, KM_USER0); @@ -3364,10 +3490,6 @@ i915_gem_object_pin_and_relocate(struct drm_gem_object *obj, drm_gem_object_unreference(target_obj); } -#if WATCH_BUF - if (0) - i915_gem_dump_object(obj, 128, __func__, ~0); -#endif return 0; } @@ -3382,28 +3504,48 @@ i915_gem_object_pin_and_relocate(struct drm_gem_object *obj, * relatively low latency when blocking on a particular request to finish. */ static int -i915_gem_ring_throttle(struct drm_device *dev, struct drm_file *file_priv) +i915_gem_ring_throttle(struct drm_device *dev, struct drm_file *file) { - struct drm_i915_file_private *i915_file_priv = file_priv->driver_priv; - int ret = 0; + struct drm_i915_private *dev_priv = dev->dev_private; + struct drm_i915_file_private *file_priv = file->driver_priv; unsigned long recent_enough = jiffies - msecs_to_jiffies(20); + struct drm_i915_gem_request *request; + struct intel_ring_buffer *ring = NULL; + u32 seqno = 0; + int ret; - mutex_lock(&dev->struct_mutex); - while (!list_empty(&i915_file_priv->mm.request_list)) { - struct drm_i915_gem_request *request; - - request = list_first_entry(&i915_file_priv->mm.request_list, - struct drm_i915_gem_request, - client_list); - + spin_lock(&file_priv->mm.lock); + list_for_each_entry(request, &file_priv->mm.request_list, client_list) { if (time_after_eq(request->emitted_jiffies, recent_enough)) break; - ret = i915_wait_request(dev, request->seqno, request->ring); - if (ret != 0) - break; + ring = request->ring; + seqno = request->seqno; } - mutex_unlock(&dev->struct_mutex); + spin_unlock(&file_priv->mm.lock); + + if (seqno == 0) + return 0; + + ret = 0; + if (!i915_seqno_passed(ring->get_seqno(dev, ring), seqno)) { + /* And wait for the seqno passing without holding any locks and + * causing extra latency for others. This is safe as the irq + * generation is designed to be run atomically and so is + * lockless. + */ + ring->user_irq_get(dev, ring); + ret = wait_event_interruptible(ring->irq_queue, + i915_seqno_passed(ring->get_seqno(dev, ring), seqno) + || atomic_read(&dev_priv->mm.wedged)); + ring->user_irq_put(dev, ring); + + if (ret == 0 && atomic_read(&dev_priv->mm.wedged)) + ret = -EIO; + } + + if (ret == 0) + queue_delayed_work(dev_priv->wq, &dev_priv->mm.retire_work, 0); return ret; } @@ -3539,8 +3681,7 @@ i915_gem_wait_for_pending_flip(struct drm_device *dev, return ret; } - -int +static int i915_gem_do_execbuffer(struct drm_device *dev, void *data, struct drm_file *file_priv, struct drm_i915_gem_execbuffer2 *args, @@ -3552,13 +3693,18 @@ i915_gem_do_execbuffer(struct drm_device *dev, void *data, struct drm_i915_gem_object *obj_priv; struct drm_clip_rect *cliprects = NULL; struct drm_i915_gem_relocation_entry *relocs = NULL; - int ret = 0, ret2, i, pinned = 0; + struct drm_i915_gem_request *request = NULL; + int ret, ret2, i, pinned = 0; uint64_t exec_offset; - uint32_t seqno, flush_domains, reloc_index; + uint32_t reloc_index; int pin_tries, flips; struct intel_ring_buffer *ring = NULL; + ret = i915_gem_check_is_wedged(dev); + if (ret) + return ret; + #if WATCH_EXEC DRM_INFO("buffers_ptr %d buffer_count %d len %08x\n", (int) args->buffers_ptr, args->buffer_count, args->batch_len); @@ -3605,20 +3751,20 @@ i915_gem_do_execbuffer(struct drm_device *dev, void *data, } } + request = kzalloc(sizeof(*request), GFP_KERNEL); + if (request == NULL) { + ret = -ENOMEM; + goto pre_mutex_err; + } + ret = i915_gem_get_relocs_from_user(exec_list, args->buffer_count, &relocs); if (ret != 0) goto pre_mutex_err; - mutex_lock(&dev->struct_mutex); - - i915_verify_inactive(dev, __FILE__, __LINE__); - - if (atomic_read(&dev_priv->mm.wedged)) { - mutex_unlock(&dev->struct_mutex); - ret = -EIO; + ret = i915_mutex_lock_interruptible(dev); + if (ret) goto pre_mutex_err; - } if (dev_priv->mm.suspended) { mutex_unlock(&dev->struct_mutex); @@ -3698,15 +3844,16 @@ i915_gem_do_execbuffer(struct drm_device *dev, void *data, pinned+1, args->buffer_count, total_size, num_fences, ret); - DRM_ERROR("%d objects [%d pinned], " - "%d object bytes [%d pinned], " - "%d/%d gtt bytes\n", - atomic_read(&dev->object_count), - atomic_read(&dev->pin_count), - atomic_read(&dev->object_memory), - atomic_read(&dev->pin_memory), - atomic_read(&dev->gtt_memory), - dev->gtt_total); + DRM_ERROR("%u objects [%u pinned, %u GTT], " + "%zu object bytes [%zu pinned], " + "%zu /%zu gtt bytes\n", + dev_priv->mm.object_count, + dev_priv->mm.pin_count, + dev_priv->mm.gtt_count, + dev_priv->mm.object_memory, + dev_priv->mm.pin_memory, + dev_priv->mm.gtt_memory, + dev_priv->mm.gtt_total); } goto err; } @@ -3739,15 +3886,13 @@ i915_gem_do_execbuffer(struct drm_device *dev, void *data, goto err; } - i915_verify_inactive(dev, __FILE__, __LINE__); - /* Zero the global flush/invalidate flags. These * will be modified as new domains are computed * for each object */ dev->invalidate_domains = 0; dev->flush_domains = 0; - dev_priv->flush_rings = 0; + dev_priv->mm.flush_rings = 0; for (i = 0; i < args->buffer_count; i++) { struct drm_gem_object *obj = object_list[i]; @@ -3756,8 +3901,6 @@ i915_gem_do_execbuffer(struct drm_device *dev, void *data, i915_gem_object_set_to_gpu_domain(obj); } - i915_verify_inactive(dev, __FILE__, __LINE__); - if (dev->invalidate_domains | dev->flush_domains) { #if WATCH_EXEC DRM_INFO("%s: invalidate_domains %08x flush_domains %08x\n", @@ -3765,17 +3908,10 @@ i915_gem_do_execbuffer(struct drm_device *dev, void *data, dev->invalidate_domains, dev->flush_domains); #endif - i915_gem_flush(dev, + i915_gem_flush(dev, file_priv, dev->invalidate_domains, - dev->flush_domains); - if (dev_priv->flush_rings & FLUSH_RENDER_RING) - (void)i915_add_request(dev, file_priv, - dev->flush_domains, - &dev_priv->render_ring); - if (dev_priv->flush_rings & FLUSH_BSD_RING) - (void)i915_add_request(dev, file_priv, - dev->flush_domains, - &dev_priv->bsd_ring); + dev->flush_domains, + dev_priv->mm.flush_rings); } for (i = 0; i < args->buffer_count; i++) { @@ -3787,16 +3923,12 @@ i915_gem_do_execbuffer(struct drm_device *dev, void *data, if (obj->write_domain) list_move_tail(&obj_priv->gpu_write_list, &dev_priv->mm.gpu_write_list); - else - list_del_init(&obj_priv->gpu_write_list); trace_i915_gem_object_change_domain(obj, obj->read_domains, old_write_domain); } - i915_verify_inactive(dev, __FILE__, __LINE__); - #if WATCH_COHERENCY for (i = 0; i < args->buffer_count; i++) { i915_gem_object_check_coherency(object_list[i], @@ -3823,33 +3955,17 @@ i915_gem_do_execbuffer(struct drm_device *dev, void *data, * Ensure that the commands in the batch buffer are * finished before the interrupt fires */ - flush_domains = i915_retire_commands(dev, ring); - - i915_verify_inactive(dev, __FILE__, __LINE__); + i915_retire_commands(dev, ring); - /* - * Get a seqno representing the execution of the current buffer, - * which we can wait on. We would like to mitigate these interrupts, - * likely by only creating seqnos occasionally (so that we have - * *some* interrupts representing completion of buffers that we can - * wait on when trying to clear up gtt space). - */ - seqno = i915_add_request(dev, file_priv, flush_domains, ring); - BUG_ON(seqno == 0); for (i = 0; i < args->buffer_count; i++) { struct drm_gem_object *obj = object_list[i]; obj_priv = to_intel_bo(obj); - i915_gem_object_move_to_active(obj, seqno, ring); -#if WATCH_LRU - DRM_INFO("%s: move to exec list %p\n", __func__, obj); -#endif + i915_gem_object_move_to_active(obj, ring); } -#if WATCH_LRU - i915_dump_lru(dev, __func__); -#endif - i915_verify_inactive(dev, __FILE__, __LINE__); + i915_add_request(dev, file_priv, request, ring); + request = NULL; err: for (i = 0; i < pinned; i++) @@ -3882,6 +3998,7 @@ pre_mutex_err: drm_free_large(object_list); kfree(cliprects); + kfree(request); return ret; } @@ -3938,7 +4055,7 @@ i915_gem_execbuffer(struct drm_device *dev, void *data, exec2_list[i].relocs_ptr = exec_list[i].relocs_ptr; exec2_list[i].alignment = exec_list[i].alignment; exec2_list[i].offset = exec_list[i].offset; - if (!IS_I965G(dev)) + if (INTEL_INFO(dev)->gen < 4) exec2_list[i].flags = EXEC_OBJECT_NEEDS_FENCE; else exec2_list[i].flags = 0; @@ -4035,12 +4152,12 @@ int i915_gem_object_pin(struct drm_gem_object *obj, uint32_t alignment) { struct drm_device *dev = obj->dev; + struct drm_i915_private *dev_priv = dev->dev_private; struct drm_i915_gem_object *obj_priv = to_intel_bo(obj); int ret; BUG_ON(obj_priv->pin_count == DRM_I915_GEM_OBJECT_MAX_PIN_COUNT); - - i915_verify_inactive(dev, __FILE__, __LINE__); + WARN_ON(i915_verify_lists(dev)); if (obj_priv->gtt_space != NULL) { if (alignment == 0) @@ -4068,14 +4185,13 @@ i915_gem_object_pin(struct drm_gem_object *obj, uint32_t alignment) * remove it from the inactive list */ if (obj_priv->pin_count == 1) { - atomic_inc(&dev->pin_count); - atomic_add(obj->size, &dev->pin_memory); - if (!obj_priv->active && - (obj->write_domain & I915_GEM_GPU_DOMAINS) == 0) - list_del_init(&obj_priv->list); + i915_gem_info_add_pin(dev_priv, obj->size); + if (!obj_priv->active) + list_move_tail(&obj_priv->list, + &dev_priv->mm.pinned_list); } - i915_verify_inactive(dev, __FILE__, __LINE__); + WARN_ON(i915_verify_lists(dev)); return 0; } @@ -4086,7 +4202,7 @@ i915_gem_object_unpin(struct drm_gem_object *obj) drm_i915_private_t *dev_priv = dev->dev_private; struct drm_i915_gem_object *obj_priv = to_intel_bo(obj); - i915_verify_inactive(dev, __FILE__, __LINE__); + WARN_ON(i915_verify_lists(dev)); obj_priv->pin_count--; BUG_ON(obj_priv->pin_count < 0); BUG_ON(obj_priv->gtt_space == NULL); @@ -4096,14 +4212,12 @@ i915_gem_object_unpin(struct drm_gem_object *obj) * the inactive list */ if (obj_priv->pin_count == 0) { - if (!obj_priv->active && - (obj->write_domain & I915_GEM_GPU_DOMAINS) == 0) + if (!obj_priv->active) list_move_tail(&obj_priv->list, &dev_priv->mm.inactive_list); - atomic_dec(&dev->pin_count); - atomic_sub(obj->size, &dev->pin_memory); + i915_gem_info_remove_pin(dev_priv, obj->size); } - i915_verify_inactive(dev, __FILE__, __LINE__); + WARN_ON(i915_verify_lists(dev)); } int @@ -4115,17 +4229,20 @@ i915_gem_pin_ioctl(struct drm_device *dev, void *data, struct drm_i915_gem_object *obj_priv; int ret; - mutex_lock(&dev->struct_mutex); - obj = drm_gem_object_lookup(dev, file_priv, args->handle); if (obj == NULL) { DRM_ERROR("Bad handle in i915_gem_pin_ioctl(): %d\n", args->handle); - mutex_unlock(&dev->struct_mutex); return -ENOENT; } obj_priv = to_intel_bo(obj); + ret = i915_mutex_lock_interruptible(dev); + if (ret) { + drm_gem_object_unreference_unlocked(obj); + return ret; + } + if (obj_priv->madv != I915_MADV_WILLNEED) { DRM_ERROR("Attempting to pin a purgeable buffer\n"); drm_gem_object_unreference(obj); @@ -4170,18 +4287,23 @@ i915_gem_unpin_ioctl(struct drm_device *dev, void *data, struct drm_i915_gem_pin *args = data; struct drm_gem_object *obj; struct drm_i915_gem_object *obj_priv; - - mutex_lock(&dev->struct_mutex); + int ret; obj = drm_gem_object_lookup(dev, file_priv, args->handle); if (obj == NULL) { DRM_ERROR("Bad handle in i915_gem_unpin_ioctl(): %d\n", args->handle); - mutex_unlock(&dev->struct_mutex); return -ENOENT; } obj_priv = to_intel_bo(obj); + + ret = i915_mutex_lock_interruptible(dev); + if (ret) { + drm_gem_object_unreference_unlocked(obj); + return ret; + } + if (obj_priv->pin_filp != file_priv) { DRM_ERROR("Not pinned by caller in i915_gem_pin_ioctl(): %d\n", args->handle); @@ -4207,6 +4329,7 @@ i915_gem_busy_ioctl(struct drm_device *dev, void *data, struct drm_i915_gem_busy *args = data; struct drm_gem_object *obj; struct drm_i915_gem_object *obj_priv; + int ret; obj = drm_gem_object_lookup(dev, file_priv, args->handle); if (obj == NULL) { @@ -4215,7 +4338,11 @@ i915_gem_busy_ioctl(struct drm_device *dev, void *data, return -ENOENT; } - mutex_lock(&dev->struct_mutex); + ret = i915_mutex_lock_interruptible(dev); + if (ret) { + drm_gem_object_unreference_unlocked(obj); + return ret; + } /* Count all active objects as busy, even if they are currently not used * by the gpu. Users of this interface expect objects to eventually @@ -4230,10 +4357,10 @@ i915_gem_busy_ioctl(struct drm_device *dev, void *data, * use this buffer rather sooner than later, so issuing the required * flush earlier is beneficial. */ - if (obj->write_domain) { - i915_gem_flush(dev, 0, obj->write_domain); - (void)i915_add_request(dev, file_priv, obj->write_domain, obj_priv->ring); - } + if (obj->write_domain & I915_GEM_GPU_DOMAINS) + i915_gem_flush_ring(dev, file_priv, + obj_priv->ring, + 0, obj->write_domain); /* Update the active list for the hardware's current position. * Otherwise this only updates on a delayed timer or when irqs @@ -4264,6 +4391,7 @@ i915_gem_madvise_ioctl(struct drm_device *dev, void *data, struct drm_i915_gem_madvise *args = data; struct drm_gem_object *obj; struct drm_i915_gem_object *obj_priv; + int ret; switch (args->madv) { case I915_MADV_DONTNEED: @@ -4279,10 +4407,14 @@ i915_gem_madvise_ioctl(struct drm_device *dev, void *data, args->handle); return -ENOENT; } - - mutex_lock(&dev->struct_mutex); obj_priv = to_intel_bo(obj); + ret = i915_mutex_lock_interruptible(dev); + if (ret) { + drm_gem_object_unreference_unlocked(obj); + return ret; + } + if (obj_priv->pin_count) { drm_gem_object_unreference(obj); mutex_unlock(&dev->struct_mutex); @@ -4310,6 +4442,7 @@ i915_gem_madvise_ioctl(struct drm_device *dev, void *data, struct drm_gem_object * i915_gem_alloc_object(struct drm_device *dev, size_t size) { + struct drm_i915_private *dev_priv = dev->dev_private; struct drm_i915_gem_object *obj; obj = kzalloc(sizeof(*obj), GFP_KERNEL); @@ -4321,6 +4454,8 @@ struct drm_gem_object * i915_gem_alloc_object(struct drm_device *dev, return NULL; } + i915_gem_info_add_obj(dev_priv, size); + obj->base.write_domain = I915_GEM_DOMAIN_CPU; obj->base.read_domains = I915_GEM_DOMAIN_CPU; @@ -4361,6 +4496,7 @@ static void i915_gem_free_object_tail(struct drm_gem_object *obj) i915_gem_free_mmap_offset(obj); drm_gem_object_release(obj); + i915_gem_info_remove_obj(dev_priv, obj->size); kfree(obj_priv->page_cpu_valid); kfree(obj_priv->bit_17); @@ -4419,7 +4555,7 @@ i915_gem_idle(struct drm_device *dev) * And not confound mm.suspended! */ dev_priv->mm.suspended = 1; - del_timer(&dev_priv->hangcheck_timer); + del_timer_sync(&dev_priv->hangcheck_timer); i915_kernel_lost_context(dev); i915_gem_cleanup_ringbuffer(dev); @@ -4499,28 +4635,18 @@ i915_gem_init_ringbuffer(struct drm_device *dev) drm_i915_private_t *dev_priv = dev->dev_private; int ret; - dev_priv->render_ring = render_ring; - - if (!I915_NEED_GFX_HWS(dev)) { - dev_priv->render_ring.status_page.page_addr - = dev_priv->status_page_dmah->vaddr; - memset(dev_priv->render_ring.status_page.page_addr, - 0, PAGE_SIZE); - } - if (HAS_PIPE_CONTROL(dev)) { ret = i915_gem_init_pipe_control(dev); if (ret) return ret; } - ret = intel_init_ring_buffer(dev, &dev_priv->render_ring); + ret = intel_init_render_ring_buffer(dev); if (ret) goto cleanup_pipe_control; if (HAS_BSD(dev)) { - dev_priv->bsd_ring = bsd_ring; - ret = intel_init_ring_buffer(dev, &dev_priv->bsd_ring); + ret = intel_init_bsd_ring_buffer(dev); if (ret) goto cleanup_render_ring; } @@ -4573,11 +4699,8 @@ i915_gem_entervt_ioctl(struct drm_device *dev, void *data, return ret; } - spin_lock(&dev_priv->mm.active_list_lock); BUG_ON(!list_empty(&dev_priv->render_ring.active_list)); BUG_ON(HAS_BSD(dev) && !list_empty(&dev_priv->bsd_ring.active_list)); - spin_unlock(&dev_priv->mm.active_list_lock); - BUG_ON(!list_empty(&dev_priv->mm.flushing_list)); BUG_ON(!list_empty(&dev_priv->mm.inactive_list)); BUG_ON(!list_empty(&dev_priv->render_ring.request_list)); @@ -4629,10 +4752,10 @@ i915_gem_load(struct drm_device *dev) int i; drm_i915_private_t *dev_priv = dev->dev_private; - spin_lock_init(&dev_priv->mm.active_list_lock); INIT_LIST_HEAD(&dev_priv->mm.flushing_list); INIT_LIST_HEAD(&dev_priv->mm.gpu_write_list); INIT_LIST_HEAD(&dev_priv->mm.inactive_list); + INIT_LIST_HEAD(&dev_priv->mm.pinned_list); INIT_LIST_HEAD(&dev_priv->mm.fence_list); INIT_LIST_HEAD(&dev_priv->mm.deferred_free_list); INIT_LIST_HEAD(&dev_priv->render_ring.active_list); @@ -4645,6 +4768,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); spin_lock(&shrink_list_lock); list_add(&dev_priv->mm.shrink_list, &shrink_list); spin_unlock(&shrink_list_lock); @@ -4663,21 +4787,30 @@ i915_gem_load(struct drm_device *dev) if (!drm_core_check_feature(dev, DRIVER_MODESET)) dev_priv->fence_reg_start = 3; - if (IS_I965G(dev) || IS_I945G(dev) || IS_I945GM(dev) || IS_G33(dev)) + if (INTEL_INFO(dev)->gen >= 4 || IS_I945G(dev) || IS_I945GM(dev) || IS_G33(dev)) dev_priv->num_fence_regs = 16; else dev_priv->num_fence_regs = 8; /* Initialize fence registers to zero */ - if (IS_I965G(dev)) { + switch (INTEL_INFO(dev)->gen) { + case 6: + for (i = 0; i < 16; i++) + I915_WRITE64(FENCE_REG_SANDYBRIDGE_0 + (i * 8), 0); + break; + case 5: + case 4: for (i = 0; i < 16; i++) I915_WRITE64(FENCE_REG_965_0 + (i * 8), 0); - } else { - for (i = 0; i < 8; i++) - I915_WRITE(FENCE_REG_830_0 + (i * 4), 0); + break; + case 3: if (IS_I945G(dev) || IS_I945GM(dev) || IS_G33(dev)) for (i = 0; i < 8; i++) I915_WRITE(FENCE_REG_945_8 + (i * 4), 0); + case 2: + for (i = 0; i < 8; i++) + I915_WRITE(FENCE_REG_830_0 + (i * 4), 0); + break; } i915_gem_detect_bit_6_swizzle(dev); init_waitqueue_head(&dev_priv->pending_flip_queue); @@ -4687,8 +4820,8 @@ i915_gem_load(struct drm_device *dev) * Create a physically contiguous memory object for this object * e.g. for cursor + overlay regs */ -int i915_gem_init_phys_object(struct drm_device *dev, - int id, int size, int align) +static int i915_gem_init_phys_object(struct drm_device *dev, + int id, int size, int align) { drm_i915_private_t *dev_priv = dev->dev_private; struct drm_i915_gem_phys_object *phys_obj; @@ -4720,7 +4853,7 @@ kfree_obj: return ret; } -void i915_gem_free_phys_object(struct drm_device *dev, int id) +static void i915_gem_free_phys_object(struct drm_device *dev, int id) { drm_i915_private_t *dev_priv = dev->dev_private; struct drm_i915_gem_phys_object *phys_obj; @@ -4865,18 +4998,25 @@ i915_gem_phys_pwrite(struct drm_device *dev, struct drm_gem_object *obj, return 0; } -void i915_gem_release(struct drm_device * dev, struct drm_file *file_priv) +void i915_gem_release(struct drm_device *dev, struct drm_file *file) { - struct drm_i915_file_private *i915_file_priv = file_priv->driver_priv; + struct drm_i915_file_private *file_priv = file->driver_priv; /* Clean up our request list when the client is going away, so that * later retire_requests won't dereference our soon-to-be-gone * file_priv. */ - mutex_lock(&dev->struct_mutex); - while (!list_empty(&i915_file_priv->mm.request_list)) - list_del_init(i915_file_priv->mm.request_list.next); - mutex_unlock(&dev->struct_mutex); + spin_lock(&file_priv->mm.lock); + while (!list_empty(&file_priv->mm.request_list)) { + struct drm_i915_gem_request *request; + + request = list_first_entry(&file_priv->mm.request_list, + struct drm_i915_gem_request, + client_list); + list_del(&request->client_list); + request->file_priv = NULL; + } + spin_unlock(&file_priv->mm.lock); } static int @@ -4885,12 +5025,10 @@ i915_gpu_is_active(struct drm_device *dev) drm_i915_private_t *dev_priv = dev->dev_private; int lists_empty; - spin_lock(&dev_priv->mm.active_list_lock); lists_empty = list_empty(&dev_priv->mm.flushing_list) && list_empty(&dev_priv->render_ring.active_list); if (HAS_BSD(dev)) lists_empty &= list_empty(&dev_priv->bsd_ring.active_list); - spin_unlock(&dev_priv->mm.active_list_lock); return !lists_empty; } diff --git a/drivers/gpu/drm/i915/i915_gem_debug.c b/drivers/gpu/drm/i915/i915_gem_debug.c index 80f380b1d95..48644b840a8 100644 --- a/drivers/gpu/drm/i915/i915_gem_debug.c +++ b/drivers/gpu/drm/i915/i915_gem_debug.c @@ -30,29 +30,112 @@ #include "i915_drm.h" #include "i915_drv.h" -#if WATCH_INACTIVE -void -i915_verify_inactive(struct drm_device *dev, char *file, int line) +#if WATCH_LISTS +int +i915_verify_lists(struct drm_device *dev) { + static int warned; drm_i915_private_t *dev_priv = dev->dev_private; - struct drm_gem_object *obj; - struct drm_i915_gem_object *obj_priv; - - list_for_each_entry(obj_priv, &dev_priv->mm.inactive_list, list) { - obj = &obj_priv->base; - if (obj_priv->pin_count || obj_priv->active || - (obj->write_domain & ~(I915_GEM_DOMAIN_CPU | - I915_GEM_DOMAIN_GTT))) - DRM_ERROR("inactive %p (p %d a %d w %x) %s:%d\n", + struct drm_i915_gem_object *obj; + int err = 0; + + if (warned) + return 0; + + list_for_each_entry(obj, &dev_priv->render_ring.active_list, list) { + if (obj->base.dev != dev || + !atomic_read(&obj->base.refcount.refcount)) { + DRM_ERROR("freed render active %p\n", obj); + err++; + break; + } else if (!obj->active || + (obj->base.read_domains & I915_GEM_GPU_DOMAINS) == 0) { + DRM_ERROR("invalid render active %p (a %d r %x)\n", + obj, + obj->active, + obj->base.read_domains); + err++; + } else if (obj->base.write_domain && list_empty(&obj->gpu_write_list)) { + DRM_ERROR("invalid render active %p (w %x, gwl %d)\n", + obj, + obj->base.write_domain, + !list_empty(&obj->gpu_write_list)); + err++; + } + } + + list_for_each_entry(obj, &dev_priv->mm.flushing_list, list) { + if (obj->base.dev != dev || + !atomic_read(&obj->base.refcount.refcount)) { + DRM_ERROR("freed flushing %p\n", obj); + err++; + break; + } else if (!obj->active || + (obj->base.write_domain & I915_GEM_GPU_DOMAINS) == 0 || + list_empty(&obj->gpu_write_list)){ + DRM_ERROR("invalid flushing %p (a %d w %x gwl %d)\n", obj, - obj_priv->pin_count, obj_priv->active, - obj->write_domain, file, line); + obj->active, + obj->base.write_domain, + !list_empty(&obj->gpu_write_list)); + err++; + } + } + + list_for_each_entry(obj, &dev_priv->mm.gpu_write_list, gpu_write_list) { + if (obj->base.dev != dev || + !atomic_read(&obj->base.refcount.refcount)) { + DRM_ERROR("freed gpu write %p\n", obj); + err++; + break; + } else if (!obj->active || + (obj->base.write_domain & I915_GEM_GPU_DOMAINS) == 0) { + DRM_ERROR("invalid gpu write %p (a %d w %x)\n", + obj, + obj->active, + obj->base.write_domain); + err++; + } + } + + list_for_each_entry(obj, &dev_priv->mm.inactive_list, list) { + if (obj->base.dev != dev || + !atomic_read(&obj->base.refcount.refcount)) { + DRM_ERROR("freed inactive %p\n", obj); + err++; + break; + } else if (obj->pin_count || obj->active || + (obj->base.write_domain & I915_GEM_GPU_DOMAINS)) { + DRM_ERROR("invalid inactive %p (p %d a %d w %x)\n", + obj, + obj->pin_count, obj->active, + obj->base.write_domain); + err++; + } } + + list_for_each_entry(obj, &dev_priv->mm.pinned_list, list) { + if (obj->base.dev != dev || + !atomic_read(&obj->base.refcount.refcount)) { + DRM_ERROR("freed pinned %p\n", obj); + err++; + break; + } else if (!obj->pin_count || obj->active || + (obj->base.write_domain & I915_GEM_GPU_DOMAINS)) { + DRM_ERROR("invalid pinned %p (p %d a %d w %x)\n", + obj, + obj->pin_count, obj->active, + obj->base.write_domain); + err++; + } + } + + return warned = err; } #endif /* WATCH_INACTIVE */ -#if WATCH_BUF | WATCH_EXEC | WATCH_PWRITE +#if WATCH_EXEC | WATCH_PWRITE static void i915_gem_dump_page(struct page *page, uint32_t start, uint32_t end, uint32_t bias, uint32_t mark) @@ -97,41 +180,6 @@ i915_gem_dump_object(struct drm_gem_object *obj, int len, } #endif -#if WATCH_LRU -void -i915_dump_lru(struct drm_device *dev, const char *where) -{ - drm_i915_private_t *dev_priv = dev->dev_private; - struct drm_i915_gem_object *obj_priv; - - DRM_INFO("active list %s {\n", where); - spin_lock(&dev_priv->mm.active_list_lock); - list_for_each_entry(obj_priv, &dev_priv->mm.active_list, - list) - { - DRM_INFO(" %p: %08x\n", obj_priv, - obj_priv->last_rendering_seqno); - } - spin_unlock(&dev_priv->mm.active_list_lock); - DRM_INFO("}\n"); - DRM_INFO("flushing list %s {\n", where); - list_for_each_entry(obj_priv, &dev_priv->mm.flushing_list, - list) - { - DRM_INFO(" %p: %08x\n", obj_priv, - obj_priv->last_rendering_seqno); - } - DRM_INFO("}\n"); - DRM_INFO("inactive %s {\n", where); - list_for_each_entry(obj_priv, &dev_priv->mm.inactive_list, list) { - DRM_INFO(" %p: %08x\n", obj_priv, - obj_priv->last_rendering_seqno); - } - DRM_INFO("}\n"); -} -#endif - - #if WATCH_COHERENCY void i915_gem_object_check_coherency(struct drm_gem_object *obj, int handle) diff --git a/drivers/gpu/drm/i915/i915_gem_evict.c b/drivers/gpu/drm/i915/i915_gem_evict.c index e85246ef691..3d7fbf32bb1 100644 --- a/drivers/gpu/drm/i915/i915_gem_evict.c +++ b/drivers/gpu/drm/i915/i915_gem_evict.c @@ -93,7 +93,7 @@ i915_gem_evict_something(struct drm_device *dev, int min_size, unsigned alignmen { drm_i915_private_t *dev_priv = dev->dev_private; struct list_head eviction_list, unwind_list; - struct drm_i915_gem_object *obj_priv, *tmp_obj_priv; + struct drm_i915_gem_object *obj_priv; struct list_head *render_iter, *bsd_iter; int ret = 0; @@ -175,39 +175,34 @@ i915_gem_evict_something(struct drm_device *dev, int min_size, unsigned alignmen return -ENOSPC; found: + /* drm_mm doesn't allow any other other operations while + * scanning, therefore store to be evicted objects on a + * temporary list. */ INIT_LIST_HEAD(&eviction_list); - list_for_each_entry_safe(obj_priv, tmp_obj_priv, - &unwind_list, evict_list) { + while (!list_empty(&unwind_list)) { + obj_priv = list_first_entry(&unwind_list, + struct drm_i915_gem_object, + evict_list); if (drm_mm_scan_remove_block(obj_priv->gtt_space)) { - /* drm_mm doesn't allow any other other operations while - * scanning, therefore store to be evicted objects on a - * temporary list. */ list_move(&obj_priv->evict_list, &eviction_list); - } else - drm_gem_object_unreference(&obj_priv->base); + continue; + } + list_del(&obj_priv->evict_list); + drm_gem_object_unreference(&obj_priv->base); } /* Unbinding will emit any required flushes */ - list_for_each_entry_safe(obj_priv, tmp_obj_priv, - &eviction_list, evict_list) { -#if WATCH_LRU - DRM_INFO("%s: evicting %p\n", __func__, &obj_priv->base); -#endif - ret = i915_gem_object_unbind(&obj_priv->base); - if (ret) - return ret; - + while (!list_empty(&eviction_list)) { + obj_priv = list_first_entry(&eviction_list, + struct drm_i915_gem_object, + evict_list); + if (ret == 0) + ret = i915_gem_object_unbind(&obj_priv->base); + list_del(&obj_priv->evict_list); drm_gem_object_unreference(&obj_priv->base); } - /* The just created free hole should be on the top of the free stack - * maintained by drm_mm, so this BUG_ON actually executes in O(1). - * Furthermore all accessed data has just recently been used, so it - * should be really fast, too. */ - BUG_ON(!drm_mm_search_free(&dev_priv->mm.gtt_space, min_size, - alignment, 0)); - - return 0; + return ret; } int @@ -217,14 +212,11 @@ i915_gem_evict_everything(struct drm_device *dev) int ret; bool lists_empty; - spin_lock(&dev_priv->mm.active_list_lock); lists_empty = (list_empty(&dev_priv->mm.inactive_list) && list_empty(&dev_priv->mm.flushing_list) && list_empty(&dev_priv->render_ring.active_list) && (!HAS_BSD(dev) || list_empty(&dev_priv->bsd_ring.active_list))); - spin_unlock(&dev_priv->mm.active_list_lock); - if (lists_empty) return -ENOSPC; @@ -239,13 +231,11 @@ i915_gem_evict_everything(struct drm_device *dev) if (ret) return ret; - spin_lock(&dev_priv->mm.active_list_lock); lists_empty = (list_empty(&dev_priv->mm.inactive_list) && list_empty(&dev_priv->mm.flushing_list) && list_empty(&dev_priv->render_ring.active_list) && (!HAS_BSD(dev) || list_empty(&dev_priv->bsd_ring.active_list))); - spin_unlock(&dev_priv->mm.active_list_lock); BUG_ON(!lists_empty); return 0; diff --git a/drivers/gpu/drm/i915/i915_gem_tiling.c b/drivers/gpu/drm/i915/i915_gem_tiling.c index 710eca70b32..8c9ffc4768e 100644 --- a/drivers/gpu/drm/i915/i915_gem_tiling.c +++ b/drivers/gpu/drm/i915/i915_gem_tiling.c @@ -98,7 +98,7 @@ i915_gem_detect_bit_6_swizzle(struct drm_device *dev) */ swizzle_x = I915_BIT_6_SWIZZLE_9_10; swizzle_y = I915_BIT_6_SWIZZLE_9; - } else if (!IS_I9XX(dev)) { + } else if (IS_GEN2(dev)) { /* As far as we know, the 865 doesn't have these bit 6 * swizzling issues. */ @@ -190,19 +190,19 @@ i915_tiling_ok(struct drm_device *dev, int stride, int size, int tiling_mode) if (tiling_mode == I915_TILING_NONE) return true; - if (!IS_I9XX(dev) || + if (IS_GEN2(dev) || (tiling_mode == I915_TILING_Y && HAS_128_BYTE_Y_TILING(dev))) tile_width = 128; else tile_width = 512; /* check maximum stride & object size */ - if (IS_I965G(dev)) { + if (INTEL_INFO(dev)->gen >= 4) { /* i965 stores the end address of the gtt mapping in the fence * reg, so dont bother to check the size */ if (stride / 128 > I965_FENCE_MAX_PITCH_VAL) return false; - } else if (IS_GEN3(dev) || IS_GEN2(dev)) { + } else { if (stride > 8192) return false; @@ -216,7 +216,7 @@ i915_tiling_ok(struct drm_device *dev, int stride, int size, int tiling_mode) } /* 965+ just needs multiples of tile width */ - if (IS_I965G(dev)) { + if (INTEL_INFO(dev)->gen >= 4) { if (stride & (tile_width - 1)) return false; return true; @@ -244,16 +244,18 @@ i915_gem_object_fence_offset_ok(struct drm_gem_object *obj, int tiling_mode) if (tiling_mode == I915_TILING_NONE) return true; - if (!IS_I965G(dev)) { - if (obj_priv->gtt_offset & (obj->size - 1)) + if (INTEL_INFO(dev)->gen >= 4) + return true; + + if (obj_priv->gtt_offset & (obj->size - 1)) + return false; + + if (IS_GEN3(dev)) { + if (obj_priv->gtt_offset & ~I915_FENCE_START_MASK) + return false; + } else { + if (obj_priv->gtt_offset & ~I830_FENCE_START_MASK) return false; - if (IS_I9XX(dev)) { - if (obj_priv->gtt_offset & ~I915_FENCE_START_MASK) - return false; - } else { - if (obj_priv->gtt_offset & ~I830_FENCE_START_MASK) - return false; - } } return true; @@ -271,7 +273,11 @@ i915_gem_set_tiling(struct drm_device *dev, void *data, drm_i915_private_t *dev_priv = dev->dev_private; struct drm_gem_object *obj; struct drm_i915_gem_object *obj_priv; - int ret = 0; + int ret; + + ret = i915_gem_check_is_wedged(dev); + if (ret) + return ret; obj = drm_gem_object_lookup(dev, file_priv, args->handle); if (obj == NULL) @@ -328,7 +334,7 @@ i915_gem_set_tiling(struct drm_device *dev, void *data, if (!i915_gem_object_fence_offset_ok(obj, args->tiling_mode)) ret = i915_gem_object_unbind(obj); else if (obj_priv->fence_reg != I915_FENCE_REG_NONE) - ret = i915_gem_object_put_fence_reg(obj); + ret = i915_gem_object_put_fence_reg(obj, true); else i915_gem_release_mmap(obj); @@ -399,16 +405,14 @@ i915_gem_get_tiling(struct drm_device *dev, void *data, * bit 17 of its physical address and therefore being interpreted differently * by the GPU. */ -static int +static void i915_gem_swizzle_page(struct page *page) { + char temp[64]; char *vaddr; int i; - char temp[64]; vaddr = kmap(page); - if (vaddr == NULL) - return -ENOMEM; for (i = 0; i < PAGE_SIZE; i += 128) { memcpy(temp, &vaddr[i], 64); @@ -417,8 +421,6 @@ i915_gem_swizzle_page(struct page *page) } kunmap(page); - - return 0; } void @@ -440,11 +442,7 @@ i915_gem_object_do_bit_17_swizzle(struct drm_gem_object *obj) char new_bit_17 = page_to_phys(obj_priv->pages[i]) >> 17; if ((new_bit_17 & 0x1) != (test_bit(i, obj_priv->bit_17) != 0)) { - int ret = i915_gem_swizzle_page(obj_priv->pages[i]); - if (ret != 0) { - DRM_ERROR("Failed to swizzle page\n"); - return; - } + i915_gem_swizzle_page(obj_priv->pages[i]); set_page_dirty(obj_priv->pages[i]); } } diff --git a/drivers/gpu/drm/i915/i915_irq.c b/drivers/gpu/drm/i915/i915_irq.c index 744225ebb4b..64c07c24e30 100644 --- a/drivers/gpu/drm/i915/i915_irq.c +++ b/drivers/gpu/drm/i915/i915_irq.c @@ -85,7 +85,7 @@ ironlake_disable_graphics_irq(drm_i915_private_t *dev_priv, u32 mask) } /* For display hotplug interrupt */ -void +static void ironlake_enable_display_irq(drm_i915_private_t *dev_priv, u32 mask) { if ((dev_priv->irq_mask_reg & mask) != 0) { @@ -172,7 +172,7 @@ void intel_enable_asle (struct drm_device *dev) else { i915_enable_pipestat(dev_priv, 1, PIPE_LEGACY_BLC_EVENT_ENABLE); - if (IS_I965G(dev)) + if (INTEL_INFO(dev)->gen >= 4) i915_enable_pipestat(dev_priv, 0, PIPE_LEGACY_BLC_EVENT_ENABLE); } @@ -191,12 +191,7 @@ static int i915_pipe_enabled(struct drm_device *dev, int pipe) { drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private; - unsigned long pipeconf = pipe ? PIPEBCONF : PIPEACONF; - - if (I915_READ(pipeconf) & PIPEACONF_ENABLE) - return 1; - - return 0; + return I915_READ(PIPECONF(pipe)) & PIPECONF_ENABLE; } /* Called from drm generic code, passed a 'crtc', which @@ -207,10 +202,7 @@ u32 i915_get_vblank_counter(struct drm_device *dev, int pipe) drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private; unsigned long high_frame; unsigned long low_frame; - u32 high1, high2, low, count; - - high_frame = pipe ? PIPEBFRAMEHIGH : PIPEAFRAMEHIGH; - low_frame = pipe ? PIPEBFRAMEPIXEL : PIPEAFRAMEPIXEL; + u32 high1, high2, low; if (!i915_pipe_enabled(dev, pipe)) { DRM_DEBUG_DRIVER("trying to get vblank count for disabled " @@ -218,23 +210,23 @@ u32 i915_get_vblank_counter(struct drm_device *dev, int pipe) return 0; } + high_frame = pipe ? PIPEBFRAMEHIGH : PIPEAFRAMEHIGH; + low_frame = pipe ? PIPEBFRAMEPIXEL : PIPEAFRAMEPIXEL; + /* * High & low register fields aren't synchronized, so make sure * we get a low value that's stable across two reads of the high * register. */ do { - high1 = ((I915_READ(high_frame) & PIPE_FRAME_HIGH_MASK) >> - PIPE_FRAME_HIGH_SHIFT); - low = ((I915_READ(low_frame) & PIPE_FRAME_LOW_MASK) >> - PIPE_FRAME_LOW_SHIFT); - high2 = ((I915_READ(high_frame) & PIPE_FRAME_HIGH_MASK) >> - PIPE_FRAME_HIGH_SHIFT); + high1 = I915_READ(high_frame) & PIPE_FRAME_HIGH_MASK; + low = I915_READ(low_frame) & PIPE_FRAME_LOW_MASK; + high2 = I915_READ(high_frame) & PIPE_FRAME_HIGH_MASK; } while (high1 != high2); - count = (high1 << 8) | low; - - return count; + high1 >>= PIPE_FRAME_HIGH_SHIFT; + low >>= PIPE_FRAME_LOW_SHIFT; + return (high1 << 8) | low; } u32 gm45_get_vblank_counter(struct drm_device *dev, int pipe) @@ -260,16 +252,12 @@ static void i915_hotplug_work_func(struct work_struct *work) hotplug_work); struct drm_device *dev = dev_priv->dev; struct drm_mode_config *mode_config = &dev->mode_config; - struct drm_encoder *encoder; - - if (mode_config->num_encoder) { - list_for_each_entry(encoder, &mode_config->encoder_list, head) { - struct intel_encoder *intel_encoder = enc_to_intel_encoder(encoder); - - if (intel_encoder->hot_plug) - (*intel_encoder->hot_plug) (intel_encoder); - } - } + struct intel_encoder *encoder; + + list_for_each_entry(encoder, &mode_config->encoder_list, base.head) + if (encoder->hot_plug) + encoder->hot_plug(encoder); + /* Just fire off a uevent and let userspace tell us what to do */ drm_helper_hpd_irq_event(dev); } @@ -305,13 +293,17 @@ static void i915_handle_rps_change(struct drm_device *dev) return; } -irqreturn_t ironlake_irq_handler(struct drm_device *dev) +static irqreturn_t ironlake_irq_handler(struct drm_device *dev) { 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; struct drm_i915_master_private *master_priv; struct intel_ring_buffer *render_ring = &dev_priv->render_ring; + u32 bsd_usr_interrupt = GT_BSD_USER_INTERRUPT; + + if (IS_GEN6(dev)) + bsd_usr_interrupt = GT_GEN6_BSD_USER_INTERRUPT; /* disable master interrupt before clearing iir */ de_ier = I915_READ(DEIER); @@ -335,28 +327,28 @@ irqreturn_t ironlake_irq_handler(struct drm_device *dev) } if (gt_iir & GT_PIPE_NOTIFY) { - u32 seqno = render_ring->get_gem_seqno(dev, render_ring); + u32 seqno = render_ring->get_seqno(dev, render_ring); render_ring->irq_gem_seqno = seqno; trace_i915_gem_request_complete(dev, seqno); - DRM_WAKEUP(&dev_priv->render_ring.irq_queue); + wake_up_all(&dev_priv->render_ring.irq_queue); dev_priv->hangcheck_count = 0; - mod_timer(&dev_priv->hangcheck_timer, jiffies + DRM_I915_HANGCHECK_PERIOD); + mod_timer(&dev_priv->hangcheck_timer, + jiffies + msecs_to_jiffies(DRM_I915_HANGCHECK_PERIOD)); } - if (gt_iir & GT_BSD_USER_INTERRUPT) - DRM_WAKEUP(&dev_priv->bsd_ring.irq_queue); - + if (gt_iir & bsd_usr_interrupt) + wake_up_all(&dev_priv->bsd_ring.irq_queue); if (de_iir & DE_GSE) - ironlake_opregion_gse_intr(dev); + intel_opregion_gse_intr(dev); if (de_iir & DE_PLANEA_FLIP_DONE) { intel_prepare_page_flip(dev, 0); - intel_finish_page_flip(dev, 0); + intel_finish_page_flip_plane(dev, 0); } if (de_iir & DE_PLANEB_FLIP_DONE) { intel_prepare_page_flip(dev, 1); - intel_finish_page_flip(dev, 1); + intel_finish_page_flip_plane(dev, 1); } if (de_iir & DE_PIPEA_VBLANK) @@ -404,23 +396,20 @@ static void i915_error_work_func(struct work_struct *work) char *reset_event[] = { "RESET=1", NULL }; char *reset_done_event[] = { "ERROR=0", NULL }; - DRM_DEBUG_DRIVER("generating error event\n"); kobject_uevent_env(&dev->primary->kdev.kobj, KOBJ_CHANGE, error_event); if (atomic_read(&dev_priv->mm.wedged)) { - if (IS_I965G(dev)) { - DRM_DEBUG_DRIVER("resetting chip\n"); - kobject_uevent_env(&dev->primary->kdev.kobj, KOBJ_CHANGE, reset_event); - if (!i965_reset(dev, GDRST_RENDER)) { - atomic_set(&dev_priv->mm.wedged, 0); - kobject_uevent_env(&dev->primary->kdev.kobj, KOBJ_CHANGE, reset_done_event); - } - } else { - DRM_DEBUG_DRIVER("reboot required\n"); + DRM_DEBUG_DRIVER("resetting chip\n"); + kobject_uevent_env(&dev->primary->kdev.kobj, KOBJ_CHANGE, reset_event); + if (!i915_reset(dev, GRDOM_RENDER)) { + atomic_set(&dev_priv->mm.wedged, 0); + kobject_uevent_env(&dev->primary->kdev.kobj, KOBJ_CHANGE, reset_done_event); } + complete_all(&dev_priv->error_completion); } } +#ifdef CONFIG_DEBUG_FS static struct drm_i915_error_object * i915_error_object_create(struct drm_device *dev, struct drm_gem_object *src) @@ -511,7 +500,7 @@ i915_get_bbaddr(struct drm_device *dev, u32 *ring) if (IS_I830(dev) || IS_845G(dev)) cmd = MI_BATCH_BUFFER; - else if (IS_I965G(dev)) + else if (INTEL_INFO(dev)->gen >= 4) cmd = (MI_BATCH_BUFFER_START | (2 << 6) | MI_BATCH_NON_SECURE_I965); else @@ -584,13 +573,16 @@ static void i915_capture_error_state(struct drm_device *dev) return; } - error->seqno = i915_get_gem_seqno(dev, &dev_priv->render_ring); + DRM_DEBUG_DRIVER("generating error event\n"); + + error->seqno = + dev_priv->render_ring.get_seqno(dev, &dev_priv->render_ring); error->eir = I915_READ(EIR); error->pgtbl_er = I915_READ(PGTBL_ER); error->pipeastat = I915_READ(PIPEASTAT); error->pipebstat = I915_READ(PIPEBSTAT); error->instpm = I915_READ(INSTPM); - if (!IS_I965G(dev)) { + if (INTEL_INFO(dev)->gen < 4) { error->ipeir = I915_READ(IPEIR); error->ipehr = I915_READ(IPEHR); error->instdone = I915_READ(INSTDONE); @@ -744,6 +736,9 @@ void i915_destroy_error_state(struct drm_device *dev) if (error) i915_error_state_free(dev, error); } +#else +#define i915_capture_error_state(x) +#endif static void i915_report_and_clear_eir(struct drm_device *dev) { @@ -785,7 +780,7 @@ static void i915_report_and_clear_eir(struct drm_device *dev) } } - if (IS_I9XX(dev)) { + if (!IS_GEN2(dev)) { if (eir & I915_ERROR_PAGE_TABLE) { u32 pgtbl_err = I915_READ(PGTBL_ER); printk(KERN_ERR "page table error\n"); @@ -811,7 +806,7 @@ static void i915_report_and_clear_eir(struct drm_device *dev) printk(KERN_ERR "instruction error\n"); printk(KERN_ERR " INSTPM: 0x%08x\n", I915_READ(INSTPM)); - if (!IS_I965G(dev)) { + if (INTEL_INFO(dev)->gen < 4) { u32 ipeir = I915_READ(IPEIR); printk(KERN_ERR " IPEIR: 0x%08x\n", @@ -876,12 +871,15 @@ static 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); /* * Wakeup waiting processes so they don't hang */ - DRM_WAKEUP(&dev_priv->render_ring.irq_queue); + wake_up_all(&dev_priv->render_ring.irq_queue); + if (HAS_BSD(dev)) + wake_up_all(&dev_priv->bsd_ring.irq_queue); } queue_work(dev_priv->wq, &dev_priv->error_work); @@ -912,7 +910,7 @@ static void i915_pageflip_stall_check(struct drm_device *dev, int pipe) /* Potential stall - if we see that the flip has happened, assume a missed interrupt */ obj_priv = to_intel_bo(work->pending_flip_obj); - if(IS_I965G(dev)) { + if (INTEL_INFO(dev)->gen >= 4) { int dspsurf = intel_crtc->plane == 0 ? DSPASURF : DSPBSURF; stall_detected = I915_READ(dspsurf) == obj_priv->gtt_offset; } else { @@ -951,7 +949,7 @@ irqreturn_t i915_driver_irq_handler(DRM_IRQ_ARGS) iir = I915_READ(IIR); - if (IS_I965G(dev)) + if (INTEL_INFO(dev)->gen >= 4) vblank_status = PIPE_START_VBLANK_INTERRUPT_STATUS; else vblank_status = PIPE_VBLANK_INTERRUPT_STATUS; @@ -1020,17 +1018,17 @@ irqreturn_t i915_driver_irq_handler(DRM_IRQ_ARGS) } if (iir & I915_USER_INTERRUPT) { - u32 seqno = - render_ring->get_gem_seqno(dev, render_ring); + u32 seqno = render_ring->get_seqno(dev, render_ring); render_ring->irq_gem_seqno = seqno; trace_i915_gem_request_complete(dev, seqno); - DRM_WAKEUP(&dev_priv->render_ring.irq_queue); + wake_up_all(&dev_priv->render_ring.irq_queue); dev_priv->hangcheck_count = 0; - mod_timer(&dev_priv->hangcheck_timer, jiffies + DRM_I915_HANGCHECK_PERIOD); + mod_timer(&dev_priv->hangcheck_timer, + jiffies + msecs_to_jiffies(DRM_I915_HANGCHECK_PERIOD)); } if (HAS_BSD(dev) && (iir & I915_BSD_USER_INTERRUPT)) - DRM_WAKEUP(&dev_priv->bsd_ring.irq_queue); + wake_up_all(&dev_priv->bsd_ring.irq_queue); if (iir & I915_DISPLAY_PLANE_A_FLIP_PENDING_INTERRUPT) { intel_prepare_page_flip(dev, 0); @@ -1065,7 +1063,7 @@ irqreturn_t i915_driver_irq_handler(DRM_IRQ_ARGS) if ((pipea_stats & PIPE_LEGACY_BLC_EVENT_STATUS) || (pipeb_stats & PIPE_LEGACY_BLC_EVENT_STATUS) || (iir & I915_ASLE_INTERRUPT)) - opregion_asle_intr(dev); + intel_opregion_asle_intr(dev); /* With MSI, interrupts are only generated when iir * transitions from zero to nonzero. If another bit got @@ -1207,18 +1205,15 @@ int i915_enable_vblank(struct drm_device *dev, int pipe) { drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private; unsigned long irqflags; - int pipeconf_reg = (pipe == 0) ? PIPEACONF : PIPEBCONF; - u32 pipeconf; - pipeconf = I915_READ(pipeconf_reg); - if (!(pipeconf & PIPEACONF_ENABLE)) + if (!i915_pipe_enabled(dev, pipe)) return -EINVAL; spin_lock_irqsave(&dev_priv->user_irq_lock, irqflags); if (HAS_PCH_SPLIT(dev)) ironlake_enable_display_irq(dev_priv, (pipe == 0) ? DE_PIPEA_VBLANK: DE_PIPEB_VBLANK); - else if (IS_I965G(dev)) + else if (INTEL_INFO(dev)->gen >= 4) i915_enable_pipestat(dev_priv, pipe, PIPE_START_VBLANK_INTERRUPT_ENABLE); else @@ -1252,7 +1247,7 @@ void i915_enable_interrupt (struct drm_device *dev) struct drm_i915_private *dev_priv = dev->dev_private; if (!HAS_PCH_SPLIT(dev)) - opregion_enable_asle(dev); + intel_opregion_enable_asle(dev); dev_priv->irq_enabled = 1; } @@ -1311,7 +1306,7 @@ int i915_vblank_swap(struct drm_device *dev, void *data, return -EINVAL; } -struct drm_i915_gem_request * +static struct drm_i915_gem_request * i915_get_tail_request(struct drm_device *dev) { drm_i915_private_t *dev_priv = dev->dev_private; @@ -1331,11 +1326,7 @@ void i915_hangcheck_elapsed(unsigned long data) drm_i915_private_t *dev_priv = dev->dev_private; uint32_t acthd, instdone, instdone1; - /* No reset support on this chip yet. */ - if (IS_GEN6(dev)) - return; - - if (!IS_I965G(dev)) { + if (INTEL_INFO(dev)->gen < 4) { acthd = I915_READ(ACTHD); instdone = I915_READ(INSTDONE); instdone1 = 0; @@ -1347,9 +1338,8 @@ void i915_hangcheck_elapsed(unsigned long data) /* If all work is done then ACTHD clearly hasn't advanced. */ if (list_empty(&dev_priv->render_ring.request_list) || - i915_seqno_passed(i915_get_gem_seqno(dev, - &dev_priv->render_ring), - i915_get_tail_request(dev)->seqno)) { + i915_seqno_passed(dev_priv->render_ring.get_seqno(dev, &dev_priv->render_ring), + i915_get_tail_request(dev)->seqno)) { bool missed_wakeup = false; dev_priv->hangcheck_count = 0; @@ -1357,13 +1347,13 @@ void i915_hangcheck_elapsed(unsigned long data) /* Issue a wake-up to catch stuck h/w. */ if (dev_priv->render_ring.waiting_gem_seqno && waitqueue_active(&dev_priv->render_ring.irq_queue)) { - DRM_WAKEUP(&dev_priv->render_ring.irq_queue); + wake_up_all(&dev_priv->render_ring.irq_queue); missed_wakeup = true; } if (dev_priv->bsd_ring.waiting_gem_seqno && waitqueue_active(&dev_priv->bsd_ring.irq_queue)) { - DRM_WAKEUP(&dev_priv->bsd_ring.irq_queue); + wake_up_all(&dev_priv->bsd_ring.irq_queue); missed_wakeup = true; } @@ -1377,6 +1367,21 @@ void i915_hangcheck_elapsed(unsigned long data) dev_priv->last_instdone1 == instdone1) { if (dev_priv->hangcheck_count++ > 1) { DRM_ERROR("Hangcheck timer elapsed... GPU hung\n"); + + if (!IS_GEN2(dev)) { + /* Is the chip hanging on a WAIT_FOR_EVENT? + * If so we can simply poke the RB_WAIT bit + * and break the hang. This should work on + * all but the second generation chipsets. + */ + u32 tmp = I915_READ(PRB0_CTL); + if (tmp & RING_WAIT) { + I915_WRITE(PRB0_CTL, tmp); + POSTING_READ(PRB0_CTL); + goto out; + } + } + i915_handle_error(dev, true); return; } @@ -1388,8 +1393,10 @@ void i915_hangcheck_elapsed(unsigned long data) dev_priv->last_instdone1 = instdone1; } +out: /* Reset timer case chip hangs without another request being added */ - mod_timer(&dev_priv->hangcheck_timer, jiffies + DRM_I915_HANGCHECK_PERIOD); + mod_timer(&dev_priv->hangcheck_timer, + jiffies + msecs_to_jiffies(DRM_I915_HANGCHECK_PERIOD)); } /* drm_dma.h hooks @@ -1436,17 +1443,19 @@ static int ironlake_irq_postinstall(struct drm_device *dev) I915_WRITE(DEIER, dev_priv->de_irq_enable_reg); (void) I915_READ(DEIER); - /* Gen6 only needs render pipe_control now */ if (IS_GEN6(dev)) - render_mask = GT_PIPE_NOTIFY; + render_mask = GT_PIPE_NOTIFY | GT_GEN6_BSD_USER_INTERRUPT; dev_priv->gt_irq_mask_reg = ~render_mask; dev_priv->gt_irq_enable_reg = render_mask; I915_WRITE(GTIIR, I915_READ(GTIIR)); I915_WRITE(GTIMR, dev_priv->gt_irq_mask_reg); - if (IS_GEN6(dev)) + if (IS_GEN6(dev)) { I915_WRITE(GEN6_RENDER_IMR, ~GEN6_RENDER_PIPE_CONTROL_NOTIFY_INTERRUPT); + I915_WRITE(GEN6_BSD_IMR, ~GEN6_BSD_IMR_USER_INTERRUPT); + } + I915_WRITE(GTIER, dev_priv->gt_irq_enable_reg); (void) I915_READ(GTIER); @@ -1578,7 +1587,7 @@ int i915_driver_irq_postinstall(struct drm_device *dev) I915_WRITE(PORT_HOTPLUG_EN, hotplug_en); } - opregion_enable_asle(dev); + intel_opregion_enable_asle(dev); return 0; } diff --git a/drivers/gpu/drm/i915/i915_reg.h b/drivers/gpu/drm/i915/i915_reg.h index 4f5e15577e8..d02de212e6a 100644 --- a/drivers/gpu/drm/i915/i915_reg.h +++ b/drivers/gpu/drm/i915/i915_reg.h @@ -25,52 +25,16 @@ #ifndef _I915_REG_H_ #define _I915_REG_H_ +#define _PIPE(pipe, a, b) ((a) + (pipe)*((b)-(a))) + /* * The Bridge device's PCI config space has information about the * fb aperture size and the amount of pre-reserved memory. + * This is all handled in the intel-gtt.ko module. i915.ko only + * cares about the vga bit for the vga rbiter. */ #define INTEL_GMCH_CTRL 0x52 #define INTEL_GMCH_VGA_DISABLE (1 << 1) -#define INTEL_GMCH_ENABLED 0x4 -#define INTEL_GMCH_MEM_MASK 0x1 -#define INTEL_GMCH_MEM_64M 0x1 -#define INTEL_GMCH_MEM_128M 0 - -#define INTEL_GMCH_GMS_MASK (0xf << 4) -#define INTEL_855_GMCH_GMS_DISABLED (0x0 << 4) -#define INTEL_855_GMCH_GMS_STOLEN_1M (0x1 << 4) -#define INTEL_855_GMCH_GMS_STOLEN_4M (0x2 << 4) -#define INTEL_855_GMCH_GMS_STOLEN_8M (0x3 << 4) -#define INTEL_855_GMCH_GMS_STOLEN_16M (0x4 << 4) -#define INTEL_855_GMCH_GMS_STOLEN_32M (0x5 << 4) - -#define INTEL_915G_GMCH_GMS_STOLEN_48M (0x6 << 4) -#define INTEL_915G_GMCH_GMS_STOLEN_64M (0x7 << 4) -#define INTEL_GMCH_GMS_STOLEN_128M (0x8 << 4) -#define INTEL_GMCH_GMS_STOLEN_256M (0x9 << 4) -#define INTEL_GMCH_GMS_STOLEN_96M (0xa << 4) -#define INTEL_GMCH_GMS_STOLEN_160M (0xb << 4) -#define INTEL_GMCH_GMS_STOLEN_224M (0xc << 4) -#define INTEL_GMCH_GMS_STOLEN_352M (0xd << 4) - -#define SNB_GMCH_CTRL 0x50 -#define SNB_GMCH_GMS_STOLEN_MASK 0xF8 -#define SNB_GMCH_GMS_STOLEN_32M (1 << 3) -#define SNB_GMCH_GMS_STOLEN_64M (2 << 3) -#define SNB_GMCH_GMS_STOLEN_96M (3 << 3) -#define SNB_GMCH_GMS_STOLEN_128M (4 << 3) -#define SNB_GMCH_GMS_STOLEN_160M (5 << 3) -#define SNB_GMCH_GMS_STOLEN_192M (6 << 3) -#define SNB_GMCH_GMS_STOLEN_224M (7 << 3) -#define SNB_GMCH_GMS_STOLEN_256M (8 << 3) -#define SNB_GMCH_GMS_STOLEN_288M (9 << 3) -#define SNB_GMCH_GMS_STOLEN_320M (0xa << 3) -#define SNB_GMCH_GMS_STOLEN_352M (0xb << 3) -#define SNB_GMCH_GMS_STOLEN_384M (0xc << 3) -#define SNB_GMCH_GMS_STOLEN_416M (0xd << 3) -#define SNB_GMCH_GMS_STOLEN_448M (0xe << 3) -#define SNB_GMCH_GMS_STOLEN_480M (0xf << 3) -#define SNB_GMCH_GMS_STOLEN_512M (0x10 << 3) /* PCI config space */ @@ -106,10 +70,13 @@ #define I915_GC_RENDER_CLOCK_200_MHZ (1 << 0) #define I915_GC_RENDER_CLOCK_333_MHZ (4 << 0) #define LBB 0xf4 -#define GDRST 0xc0 -#define GDRST_FULL (0<<2) -#define GDRST_RENDER (1<<2) -#define GDRST_MEDIA (3<<2) + +/* Graphics reset regs */ +#define I965_GDRST 0xc0 /* PCI config register */ +#define ILK_GDSR 0x2ca4 /* MCHBAR offset */ +#define GRDOM_FULL (0<<2) +#define GRDOM_RENDER (1<<2) +#define GRDOM_MEDIA (3<<2) /* VGA stuff */ @@ -192,11 +159,11 @@ #define MI_STORE_DWORD_INDEX MI_INSTR(0x21, 1) #define MI_STORE_DWORD_INDEX_SHIFT 2 #define MI_LOAD_REGISTER_IMM MI_INSTR(0x22, 1) +#define MI_FLUSH_DW MI_INSTR(0x26, 2) /* for GEN6 */ #define MI_BATCH_BUFFER MI_INSTR(0x30, 1) #define MI_BATCH_NON_SECURE (1) #define MI_BATCH_NON_SECURE_I965 (1<<8) #define MI_BATCH_BUFFER_START MI_INSTR(0x31, 0) - /* * 3D instructions used by the kernel */ @@ -249,6 +216,16 @@ #define PIPE_CONTROL_GLOBAL_GTT (1<<2) /* in addr dword */ #define PIPE_CONTROL_STALL_EN (1<<1) /* in addr word, Ironlake+ only */ + +/* + * Reset registers + */ +#define DEBUG_RESET_I830 0x6070 +#define DEBUG_RESET_FULL (1<<7) +#define DEBUG_RESET_RENDER (1<<8) +#define DEBUG_RESET_DISPLAY (1<<9) + + /* * Fence registers */ @@ -283,6 +260,16 @@ #define PRB0_HEAD 0x02034 #define PRB0_START 0x02038 #define PRB0_CTL 0x0203c +#define RENDER_RING_BASE 0x02000 +#define BSD_RING_BASE 0x04000 +#define GEN6_BSD_RING_BASE 0x12000 +#define RING_TAIL(base) ((base)+0x30) +#define RING_HEAD(base) ((base)+0x34) +#define RING_START(base) ((base)+0x38) +#define RING_CTL(base) ((base)+0x3c) +#define RING_HWS_PGA(base) ((base)+0x80) +#define RING_HWS_PGA_GEN6(base) ((base)+0x2080) +#define RING_ACTHD(base) ((base)+0x74) #define TAIL_ADDR 0x001FFFF8 #define HEAD_WRAP_COUNT 0xFFE00000 #define HEAD_WRAP_ONE 0x00200000 @@ -295,6 +282,8 @@ #define RING_VALID_MASK 0x00000001 #define RING_VALID 0x00000001 #define RING_INVALID 0x00000000 +#define RING_WAIT_I8XX (1<<0) /* gen2, PRBx_HEAD */ +#define RING_WAIT (1<<11) /* gen3+, PRBx_CTL */ #define PRB1_TAIL 0x02040 /* 915+ only */ #define PRB1_HEAD 0x02044 /* 915+ only */ #define PRB1_START 0x02048 /* 915+ only */ @@ -306,7 +295,6 @@ #define INSTDONE1 0x0207c /* 965+ only */ #define ACTHD_I965 0x02074 #define HWS_PGA 0x02080 -#define HWS_PGA_GEN6 0x04080 #define HWS_ADDRESS_MASK 0xfffff000 #define HWS_START_ADDRESS_SHIFT 4 #define PWRCTXA 0x2088 /* 965GM+ only */ @@ -464,17 +452,17 @@ #define GEN6_BLITTER_COMMAND_PARSER_MASTER_ERROR (1 << 25) #define GEN6_BLITTER_SYNC_STATUS (1 << 24) #define GEN6_BLITTER_USER_INTERRUPT (1 << 22) -/* - * BSD (bit stream decoder instruction and interrupt control register defines - * (G4X and Ironlake only) - */ -#define BSD_RING_TAIL 0x04030 -#define BSD_RING_HEAD 0x04034 -#define BSD_RING_START 0x04038 -#define BSD_RING_CTL 0x0403c -#define BSD_RING_ACTHD 0x04074 -#define BSD_HWS_PGA 0x04080 +#define GEN6_BSD_SLEEP_PSMI_CONTROL 0x12050 +#define GEN6_BSD_SLEEP_PSMI_CONTROL_RC_ILDL_MESSAGE_MODIFY_MASK (1 << 16) +#define GEN6_BSD_SLEEP_PSMI_CONTROL_RC_ILDL_MESSAGE_DISABLE (1 << 0) +#define GEN6_BSD_SLEEP_PSMI_CONTROL_RC_ILDL_MESSAGE_ENABLE 0 +#define GEN6_BSD_SLEEP_PSMI_CONTROL_IDLE_INDICATOR (1 << 3) + +#define GEN6_BSD_IMR 0x120a8 +#define GEN6_BSD_IMR_USER_INTERRUPT (1 << 12) + +#define GEN6_BSD_RNCID 0x12198 /* * Framebuffer compression (915+ only) @@ -579,12 +567,51 @@ # define GPIO_DATA_VAL_IN (1 << 12) # define GPIO_DATA_PULLUP_DISABLE (1 << 13) -#define GMBUS0 0x5100 -#define GMBUS1 0x5104 -#define GMBUS2 0x5108 -#define GMBUS3 0x510c -#define GMBUS4 0x5110 -#define GMBUS5 0x5120 +#define GMBUS0 0x5100 /* clock/port select */ +#define GMBUS_RATE_100KHZ (0<<8) +#define GMBUS_RATE_50KHZ (1<<8) +#define GMBUS_RATE_400KHZ (2<<8) /* reserved on Pineview */ +#define GMBUS_RATE_1MHZ (3<<8) /* reserved on Pineview */ +#define GMBUS_HOLD_EXT (1<<7) /* 300ns hold time, rsvd on Pineview */ +#define GMBUS_PORT_DISABLED 0 +#define GMBUS_PORT_SSC 1 +#define GMBUS_PORT_VGADDC 2 +#define GMBUS_PORT_PANEL 3 +#define GMBUS_PORT_DPC 4 /* HDMIC */ +#define GMBUS_PORT_DPB 5 /* SDVO, HDMIB */ + /* 6 reserved */ +#define GMBUS_PORT_DPD 7 /* HDMID */ +#define GMBUS_NUM_PORTS 8 +#define GMBUS1 0x5104 /* command/status */ +#define GMBUS_SW_CLR_INT (1<<31) +#define GMBUS_SW_RDY (1<<30) +#define GMBUS_ENT (1<<29) /* enable timeout */ +#define GMBUS_CYCLE_NONE (0<<25) +#define GMBUS_CYCLE_WAIT (1<<25) +#define GMBUS_CYCLE_INDEX (2<<25) +#define GMBUS_CYCLE_STOP (4<<25) +#define GMBUS_BYTE_COUNT_SHIFT 16 +#define GMBUS_SLAVE_INDEX_SHIFT 8 +#define GMBUS_SLAVE_ADDR_SHIFT 1 +#define GMBUS_SLAVE_READ (1<<0) +#define GMBUS_SLAVE_WRITE (0<<0) +#define GMBUS2 0x5108 /* status */ +#define GMBUS_INUSE (1<<15) +#define GMBUS_HW_WAIT_PHASE (1<<14) +#define GMBUS_STALL_TIMEOUT (1<<13) +#define GMBUS_INT (1<<12) +#define GMBUS_HW_RDY (1<<11) +#define GMBUS_SATOER (1<<10) +#define GMBUS_ACTIVE (1<<9) +#define GMBUS3 0x510c /* data buffer bytes 3-0 */ +#define GMBUS4 0x5110 /* interrupt mask (Pineview+) */ +#define GMBUS_SLAVE_TIMEOUT_EN (1<<4) +#define GMBUS_NAK_EN (1<<3) +#define GMBUS_IDLE_EN (1<<2) +#define GMBUS_HW_WAIT_EN (1<<1) +#define GMBUS_HW_RDY_EN (1<<0) +#define GMBUS5 0x5120 /* byte index */ +#define GMBUS_2BYTE_INDEX_EN (1<<31) /* * Clock control & power management @@ -603,6 +630,7 @@ #define VGA1_PD_P1_MASK (0x1f << 8) #define DPLL_A 0x06014 #define DPLL_B 0x06018 +#define DPLL(pipe) _PIPE(pipe, DPLL_A, DPLL_B) #define DPLL_VCO_ENABLE (1 << 31) #define DPLL_DVO_HIGH_SPEED (1 << 30) #define DPLL_SYNCLOCK_ENABLE (1 << 29) @@ -640,24 +668,6 @@ #define ADPA_DPMS_STANDBY (2<<10) #define ADPA_DPMS_OFF (3<<10) -#define RING_TAIL 0x00 -#define TAIL_ADDR 0x001FFFF8 -#define RING_HEAD 0x04 -#define HEAD_WRAP_COUNT 0xFFE00000 -#define HEAD_WRAP_ONE 0x00200000 -#define HEAD_ADDR 0x001FFFFC -#define RING_START 0x08 -#define START_ADDR 0xFFFFF000 -#define RING_LEN 0x0C -#define RING_NR_PAGES 0x001FF000 -#define RING_REPORT_MASK 0x00000006 -#define RING_REPORT_64K 0x00000002 -#define RING_REPORT_128K 0x00000004 -#define RING_NO_REPORT 0x00000000 -#define RING_VALID_MASK 0x00000001 -#define RING_VALID 0x00000001 -#define RING_INVALID 0x00000000 - /* Scratch pad debug 0 reg: */ #define DPLL_FPA01_P1_POST_DIV_MASK_I830 0x001f0000 @@ -736,10 +746,13 @@ #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_MD(pipe) _PIPE(pipe, DPLL_A_MD, DPLL_B_MD) #define FPA0 0x06040 #define FPA1 0x06044 #define FPB0 0x06048 #define FPB1 0x0604c +#define FP0(pipe) _PIPE(pipe, FPA0, FPB0) +#define FP1(pipe) _PIPE(pipe, FPA1, FPB1) #define FP_N_DIV_MASK 0x003f0000 #define FP_N_PINEVIEW_DIV_MASK 0x00ff0000 #define FP_N_DIV_SHIFT 16 @@ -760,6 +773,7 @@ #define DPLLA_TEST_M_BYPASS (1 << 2) #define DPLLA_INPUT_BUFFER_ENABLE (1 << 0) #define D_STATE 0x6104 +#define DSTATE_GFX_RESET_I830 (1<<6) #define DSTATE_PLL_D3_OFF (1<<3) #define DSTATE_GFX_CLOCK_GATING (1<<1) #define DSTATE_DOT_CLOCK_GATING (1<<0) @@ -926,6 +940,8 @@ #define CLKCFG_MEM_800 (3 << 4) #define CLKCFG_MEM_MASK (7 << 4) +#define TSC1 0x11001 +#define TSE (1<<0) #define TR1 0x11006 #define TSFS 0x11020 #define TSFS_SLOPE_MASK 0x0000ff00 @@ -1070,6 +1086,8 @@ #define MEMSTAT_SRC_CTL_STDBY 3 #define RCPREVBSYTUPAVG 0x113b8 #define RCPREVBSYTDNAVG 0x113bc +#define PMMISC 0x11214 +#define MCPPCE_EN (1<<0) /* enable PM_MSG from PCH->MPC */ #define SDEW 0x1124c #define CSIEW0 0x11250 #define CSIEW1 0x11254 @@ -1150,6 +1168,15 @@ #define PIPEBSRC 0x6101c #define BCLRPAT_B 0x61020 +#define HTOTAL(pipe) _PIPE(pipe, HTOTAL_A, HTOTAL_B) +#define HBLANK(pipe) _PIPE(pipe, HBLANK_A, HBLANK_B) +#define HSYNC(pipe) _PIPE(pipe, HSYNC_A, HSYNC_B) +#define VTOTAL(pipe) _PIPE(pipe, VTOTAL_A, VTOTAL_B) +#define VBLANK(pipe) _PIPE(pipe, VBLANK_A, VBLANK_B) +#define VSYNC(pipe) _PIPE(pipe, VSYNC_A, VSYNC_B) +#define PIPESRC(pipe) _PIPE(pipe, PIPEASRC, PIPEBSRC) +#define BCLRPAT(pipe) _PIPE(pipe, BCLRPAT_A, BCLRPAT_B) + /* VGA port control */ #define ADPA 0x61100 #define ADPA_DAC_ENABLE (1<<31) @@ -1481,6 +1508,7 @@ # define TV_TEST_MODE_MASK (7 << 0) #define TV_DAC 0x68004 +# define TV_DAC_SAVE 0x00ffff00 /** * Reports that DAC state change logic has reported change (RO). * @@ -2075,29 +2103,35 @@ /* Display & cursor control */ -/* dithering flag on Ironlake */ -#define PIPE_ENABLE_DITHER (1 << 4) -#define PIPE_DITHER_TYPE_MASK (3 << 2) -#define PIPE_DITHER_TYPE_SPATIAL (0 << 2) -#define PIPE_DITHER_TYPE_ST01 (1 << 2) /* Pipe A */ #define PIPEADSL 0x70000 -#define DSL_LINEMASK 0x00000fff +#define DSL_LINEMASK 0x00000fff #define PIPEACONF 0x70008 -#define PIPEACONF_ENABLE (1<<31) -#define PIPEACONF_DISABLE 0 -#define PIPEACONF_DOUBLE_WIDE (1<<30) +#define PIPECONF_ENABLE (1<<31) +#define PIPECONF_DISABLE 0 +#define PIPECONF_DOUBLE_WIDE (1<<30) #define I965_PIPECONF_ACTIVE (1<<30) -#define PIPEACONF_SINGLE_WIDE 0 -#define PIPEACONF_PIPE_UNLOCKED 0 -#define PIPEACONF_PIPE_LOCKED (1<<25) -#define PIPEACONF_PALETTE 0 -#define PIPEACONF_GAMMA (1<<24) +#define PIPECONF_SINGLE_WIDE 0 +#define PIPECONF_PIPE_UNLOCKED 0 +#define PIPECONF_PIPE_LOCKED (1<<25) +#define PIPECONF_PALETTE 0 +#define PIPECONF_GAMMA (1<<24) #define PIPECONF_FORCE_BORDER (1<<25) #define PIPECONF_PROGRESSIVE (0 << 21) #define PIPECONF_INTERLACE_W_FIELD_INDICATION (6 << 21) #define PIPECONF_INTERLACE_FIELD_0_ONLY (7 << 21) #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_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 PIPE_FIFO_UNDERRUN_STATUS (1UL<<31) #define PIPE_CRC_ERROR_ENABLE (1UL<<29) @@ -2128,12 +2162,15 @@ #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_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 PIPECONF(pipe) _PIPE(pipe, PIPEACONF, PIPEBCONF) +#define PIPEDSL(pipe) _PIPE(pipe, PIPEADSL, PIPEBDSL) + #define DSPARB 0x70030 #define DSPARB_CSTART_MASK (0x7f << 7) #define DSPARB_CSTART_SHIFT 7 @@ -2206,8 +2243,8 @@ #define WM1_LP_SR_EN (1<<31) #define WM1_LP_LATENCY_SHIFT 24 #define WM1_LP_LATENCY_MASK (0x7f<<24) -#define WM1_LP_FBC_LP1_MASK (0xf<<20) -#define WM1_LP_FBC_LP1_SHIFT 20 +#define WM1_LP_FBC_MASK (0xf<<20) +#define WM1_LP_FBC_SHIFT 20 #define WM1_LP_SR_MASK (0x1ff<<8) #define WM1_LP_SR_SHIFT 8 #define WM1_LP_CURSOR_MASK (0x3f) @@ -2333,6 +2370,14 @@ #define DSPASURF 0x7019C /* 965+ only */ #define DSPATILEOFF 0x701A4 /* 965+ only */ +#define DSPCNTR(plane) _PIPE(plane, DSPACNTR, DSPBCNTR) +#define DSPADDR(plane) _PIPE(plane, DSPAADDR, DSPBADDR) +#define DSPSTRIDE(plane) _PIPE(plane, DSPASTRIDE, DSPBSTRIDE) +#define DSPPOS(plane) _PIPE(plane, DSPAPOS, DSPBPOS) +#define DSPSIZE(plane) _PIPE(plane, DSPASIZE, DSPBSIZE) +#define DSPSURF(plane) _PIPE(plane, DSPASURF, DSPBSURF) +#define DSPTILEOFF(plane) _PIPE(plane, DSPATILEOFF, DSPBTILEOFF) + /* VBIOS flags */ #define SWF00 0x71410 #define SWF01 0x71414 @@ -2397,6 +2442,7 @@ #define RR_HW_HIGH_POWER_FRAMES_MASK 0xff00 #define FDI_PLL_BIOS_0 0x46000 +#define FDI_PLL_FB_CLOCK_MASK 0xff #define FDI_PLL_BIOS_1 0x46004 #define FDI_PLL_BIOS_2 0x46008 #define DISPLAY_PORT_PLL_BIOS_0 0x4600c @@ -2420,46 +2466,47 @@ #define PIPEA_DATA_M1 0x60030 #define TU_SIZE(x) (((x)-1) << 25) /* default size 64 */ #define TU_SIZE_MASK 0x7e000000 -#define PIPEA_DATA_M1_OFFSET 0 +#define PIPE_DATA_M1_OFFSET 0 #define PIPEA_DATA_N1 0x60034 -#define PIPEA_DATA_N1_OFFSET 0 +#define PIPE_DATA_N1_OFFSET 0 #define PIPEA_DATA_M2 0x60038 -#define PIPEA_DATA_M2_OFFSET 0 +#define PIPE_DATA_M2_OFFSET 0 #define PIPEA_DATA_N2 0x6003c -#define PIPEA_DATA_N2_OFFSET 0 +#define PIPE_DATA_N2_OFFSET 0 #define PIPEA_LINK_M1 0x60040 -#define PIPEA_LINK_M1_OFFSET 0 +#define PIPE_LINK_M1_OFFSET 0 #define PIPEA_LINK_N1 0x60044 -#define PIPEA_LINK_N1_OFFSET 0 +#define PIPE_LINK_N1_OFFSET 0 #define PIPEA_LINK_M2 0x60048 -#define PIPEA_LINK_M2_OFFSET 0 +#define PIPE_LINK_M2_OFFSET 0 #define PIPEA_LINK_N2 0x6004c -#define PIPEA_LINK_N2_OFFSET 0 +#define PIPE_LINK_N2_OFFSET 0 /* PIPEB timing regs are same start from 0x61000 */ #define PIPEB_DATA_M1 0x61030 -#define PIPEB_DATA_M1_OFFSET 0 #define PIPEB_DATA_N1 0x61034 -#define PIPEB_DATA_N1_OFFSET 0 #define PIPEB_DATA_M2 0x61038 -#define PIPEB_DATA_M2_OFFSET 0 #define PIPEB_DATA_N2 0x6103c -#define PIPEB_DATA_N2_OFFSET 0 #define PIPEB_LINK_M1 0x61040 -#define PIPEB_LINK_M1_OFFSET 0 #define PIPEB_LINK_N1 0x61044 -#define PIPEB_LINK_N1_OFFSET 0 #define PIPEB_LINK_M2 0x61048 -#define PIPEB_LINK_M2_OFFSET 0 #define PIPEB_LINK_N2 0x6104c -#define PIPEB_LINK_N2_OFFSET 0 + +#define PIPE_DATA_M1(pipe) _PIPE(pipe, PIPEA_DATA_M1, PIPEB_DATA_M1) +#define PIPE_DATA_N1(pipe) _PIPE(pipe, PIPEA_DATA_N1, PIPEB_DATA_N1) +#define PIPE_DATA_M2(pipe) _PIPE(pipe, PIPEA_DATA_M2, PIPEB_DATA_M2) +#define PIPE_DATA_N2(pipe) _PIPE(pipe, PIPEA_DATA_N2, PIPEB_DATA_N2) +#define PIPE_LINK_M1(pipe) _PIPE(pipe, PIPEA_LINK_M1, PIPEB_LINK_M1) +#define PIPE_LINK_N1(pipe) _PIPE(pipe, PIPEA_LINK_N1, PIPEB_LINK_N1) +#define PIPE_LINK_M2(pipe) _PIPE(pipe, PIPEA_LINK_M2, PIPEB_LINK_M2) +#define PIPE_LINK_N2(pipe) _PIPE(pipe, PIPEA_LINK_N2, PIPEB_LINK_N2) /* CPU panel fitter */ #define PFA_CTL_1 0x68080 @@ -2516,7 +2563,7 @@ #define GT_SYNC_STATUS (1 << 2) #define GT_USER_INTERRUPT (1 << 0) #define GT_BSD_USER_INTERRUPT (1 << 5) - +#define GT_GEN6_BSD_USER_INTERRUPT (1 << 12) #define GTISR 0x44010 #define GTIMR 0x44014 @@ -2600,11 +2647,14 @@ #define PCH_DPLL_A 0xc6014 #define PCH_DPLL_B 0xc6018 +#define PCH_DPLL(pipe) _PIPE(pipe, PCH_DPLL_A, PCH_DPLL_B) #define PCH_FPA0 0xc6040 #define PCH_FPA1 0xc6044 #define PCH_FPB0 0xc6048 #define PCH_FPB1 0xc604c +#define PCH_FP0(pipe) _PIPE(pipe, PCH_FPA0, PCH_FPB0) +#define PCH_FP1(pipe) _PIPE(pipe, PCH_FPA1, PCH_FPB1) #define PCH_DPLL_TEST 0xc606c @@ -2690,6 +2740,13 @@ #define TRANS_VBLANK_B 0xe1010 #define TRANS_VSYNC_B 0xe1014 +#define TRANS_HTOTAL(pipe) _PIPE(pipe, TRANS_HTOTAL_A, TRANS_HTOTAL_B) +#define TRANS_HBLANK(pipe) _PIPE(pipe, TRANS_HBLANK_A, TRANS_HBLANK_B) +#define TRANS_HSYNC(pipe) _PIPE(pipe, TRANS_HSYNC_A, TRANS_HSYNC_B) +#define TRANS_VTOTAL(pipe) _PIPE(pipe, TRANS_VTOTAL_A, TRANS_VTOTAL_B) +#define TRANS_VBLANK(pipe) _PIPE(pipe, TRANS_VBLANK_A, TRANS_VBLANK_B) +#define TRANS_VSYNC(pipe) _PIPE(pipe, TRANS_VSYNC_A, TRANS_VSYNC_B) + #define TRANSB_DATA_M1 0xe1030 #define TRANSB_DATA_N1 0xe1034 #define TRANSB_DATA_M2 0xe1038 @@ -2701,6 +2758,7 @@ #define TRANSACONF 0xf0008 #define TRANSBCONF 0xf1008 +#define TRANSCONF(plane) _PIPE(plane, TRANSACONF, TRANSBCONF) #define TRANS_DISABLE (0<<31) #define TRANS_ENABLE (1<<31) #define TRANS_STATE_MASK (1<<30) @@ -2725,6 +2783,7 @@ /* CPU: FDI_TX */ #define FDI_TXA_CTL 0x60100 #define FDI_TXB_CTL 0x61100 +#define FDI_TX_CTL(pipe) _PIPE(pipe, FDI_TXA_CTL, FDI_TXB_CTL) #define FDI_TX_DISABLE (0<<31) #define FDI_TX_ENABLE (1<<31) #define FDI_LINK_TRAIN_PATTERN_1 (0<<28) @@ -2766,8 +2825,8 @@ /* FDI_RX, FDI_X is hard-wired to Transcoder_X */ #define FDI_RXA_CTL 0xf000c #define FDI_RXB_CTL 0xf100c +#define FDI_RX_CTL(pipe) _PIPE(pipe, FDI_RXA_CTL, FDI_RXB_CTL) #define FDI_RX_ENABLE (1<<31) -#define FDI_RX_DISABLE (0<<31) /* train, dp width same as FDI_TX */ #define FDI_DP_PORT_WIDTH_X8 (7<<19) #define FDI_8BPC (0<<16) @@ -2782,8 +2841,7 @@ #define FDI_FS_ERR_REPORT_ENABLE (1<<9) #define FDI_FE_ERR_REPORT_ENABLE (1<<8) #define FDI_RX_ENHANCE_FRAME_ENABLE (1<<6) -#define FDI_SEL_RAWCLK (0<<4) -#define FDI_SEL_PCDCLK (1<<4) +#define FDI_PCDCLK (1<<4) /* CPT */ #define FDI_AUTO_TRAINING (1<<10) #define FDI_LINK_TRAIN_PATTERN_1_CPT (0<<8) @@ -2798,6 +2856,9 @@ #define FDI_RXA_TUSIZE2 0xf0038 #define FDI_RXB_TUSIZE1 0xf1030 #define FDI_RXB_TUSIZE2 0xf1038 +#define FDI_RX_MISC(pipe) _PIPE(pipe, FDI_RXA_MISC, FDI_RXB_MISC) +#define FDI_RX_TUSIZE1(pipe) _PIPE(pipe, FDI_RXA_TUSIZE1, FDI_RXB_TUSIZE1) +#define FDI_RX_TUSIZE2(pipe) _PIPE(pipe, FDI_RXA_TUSIZE2, FDI_RXB_TUSIZE2) /* FDI_RX interrupt register format */ #define FDI_RX_INTER_LANE_ALIGN (1<<10) @@ -2816,6 +2877,8 @@ #define FDI_RXA_IMR 0xf0018 #define FDI_RXB_IIR 0xf1014 #define FDI_RXB_IMR 0xf1018 +#define FDI_RX_IIR(pipe) _PIPE(pipe, FDI_RXA_IIR, FDI_RXB_IIR) +#define FDI_RX_IMR(pipe) _PIPE(pipe, FDI_RXA_IMR, FDI_RXB_IMR) #define FDI_PLL_CTL_1 0xfe000 #define FDI_PLL_CTL_2 0xfe004 @@ -2935,6 +2998,7 @@ #define TRANS_DP_CTL_A 0xe0300 #define TRANS_DP_CTL_B 0xe1300 #define TRANS_DP_CTL_C 0xe2300 +#define TRANS_DP_CTL(pipe) (TRANS_DP_CTL_A + (pipe) * 0x01000) #define TRANS_DP_OUTPUT_ENABLE (1<<31) #define TRANS_DP_PORT_SEL_B (0<<29) #define TRANS_DP_PORT_SEL_C (1<<29) diff --git a/drivers/gpu/drm/i915/i915_suspend.c b/drivers/gpu/drm/i915/i915_suspend.c index 31f08581e93..989c19d2d95 100644 --- a/drivers/gpu/drm/i915/i915_suspend.c +++ b/drivers/gpu/drm/i915/i915_suspend.c @@ -256,7 +256,7 @@ static void i915_save_modeset_reg(struct drm_device *dev) dev_priv->saveFPA1 = I915_READ(FPA1); dev_priv->saveDPLL_A = I915_READ(DPLL_A); } - if (IS_I965G(dev) && !HAS_PCH_SPLIT(dev)) + if (INTEL_INFO(dev)->gen >= 4 && !HAS_PCH_SPLIT(dev)) dev_priv->saveDPLL_A_MD = I915_READ(DPLL_A_MD); dev_priv->saveHTOTAL_A = I915_READ(HTOTAL_A); dev_priv->saveHBLANK_A = I915_READ(HBLANK_A); @@ -294,7 +294,7 @@ static void i915_save_modeset_reg(struct drm_device *dev) dev_priv->saveDSPASIZE = I915_READ(DSPASIZE); dev_priv->saveDSPAPOS = I915_READ(DSPAPOS); dev_priv->saveDSPAADDR = I915_READ(DSPAADDR); - if (IS_I965G(dev)) { + if (INTEL_INFO(dev)->gen >= 4) { dev_priv->saveDSPASURF = I915_READ(DSPASURF); dev_priv->saveDSPATILEOFF = I915_READ(DSPATILEOFF); } @@ -313,7 +313,7 @@ static void i915_save_modeset_reg(struct drm_device *dev) dev_priv->saveFPB1 = I915_READ(FPB1); dev_priv->saveDPLL_B = I915_READ(DPLL_B); } - if (IS_I965G(dev) && !HAS_PCH_SPLIT(dev)) + if (INTEL_INFO(dev)->gen >= 4 && !HAS_PCH_SPLIT(dev)) dev_priv->saveDPLL_B_MD = I915_READ(DPLL_B_MD); dev_priv->saveHTOTAL_B = I915_READ(HTOTAL_B); dev_priv->saveHBLANK_B = I915_READ(HBLANK_B); @@ -351,7 +351,7 @@ static void i915_save_modeset_reg(struct drm_device *dev) dev_priv->saveDSPBSIZE = I915_READ(DSPBSIZE); dev_priv->saveDSPBPOS = I915_READ(DSPBPOS); dev_priv->saveDSPBADDR = I915_READ(DSPBADDR); - if (IS_I965GM(dev) || IS_GM45(dev)) { + if (INTEL_INFO(dev)->gen >= 4) { dev_priv->saveDSPBSURF = I915_READ(DSPBSURF); dev_priv->saveDSPBTILEOFF = I915_READ(DSPBTILEOFF); } @@ -404,7 +404,7 @@ static void i915_restore_modeset_reg(struct drm_device *dev) I915_WRITE(dpll_a_reg, dev_priv->saveDPLL_A); POSTING_READ(dpll_a_reg); udelay(150); - if (IS_I965G(dev) && !HAS_PCH_SPLIT(dev)) { + if (INTEL_INFO(dev)->gen >= 4 && !HAS_PCH_SPLIT(dev)) { I915_WRITE(DPLL_A_MD, dev_priv->saveDPLL_A_MD); POSTING_READ(DPLL_A_MD); } @@ -448,7 +448,7 @@ static void i915_restore_modeset_reg(struct drm_device *dev) I915_WRITE(PIPEASRC, dev_priv->savePIPEASRC); I915_WRITE(DSPAADDR, dev_priv->saveDSPAADDR); I915_WRITE(DSPASTRIDE, dev_priv->saveDSPASTRIDE); - if (IS_I965G(dev)) { + if (INTEL_INFO(dev)->gen >= 4) { I915_WRITE(DSPASURF, dev_priv->saveDSPASURF); I915_WRITE(DSPATILEOFF, dev_priv->saveDSPATILEOFF); } @@ -473,7 +473,7 @@ static void i915_restore_modeset_reg(struct drm_device *dev) I915_WRITE(dpll_b_reg, dev_priv->saveDPLL_B); POSTING_READ(dpll_b_reg); udelay(150); - if (IS_I965G(dev) && !HAS_PCH_SPLIT(dev)) { + if (INTEL_INFO(dev)->gen >= 4 && !HAS_PCH_SPLIT(dev)) { I915_WRITE(DPLL_B_MD, dev_priv->saveDPLL_B_MD); POSTING_READ(DPLL_B_MD); } @@ -517,7 +517,7 @@ static void i915_restore_modeset_reg(struct drm_device *dev) I915_WRITE(PIPEBSRC, dev_priv->savePIPEBSRC); I915_WRITE(DSPBADDR, dev_priv->saveDSPBADDR); I915_WRITE(DSPBSTRIDE, dev_priv->saveDSPBSTRIDE); - if (IS_I965G(dev)) { + if (INTEL_INFO(dev)->gen >= 4) { I915_WRITE(DSPBSURF, dev_priv->saveDSPBSURF); I915_WRITE(DSPBTILEOFF, dev_priv->saveDSPBTILEOFF); } @@ -550,7 +550,7 @@ void i915_save_display(struct drm_device *dev) dev_priv->saveCURBCNTR = I915_READ(CURBCNTR); dev_priv->saveCURBPOS = I915_READ(CURBPOS); dev_priv->saveCURBBASE = I915_READ(CURBBASE); - if (!IS_I9XX(dev)) + if (IS_GEN2(dev)) dev_priv->saveCURSIZE = I915_READ(CURSIZE); /* CRT state */ @@ -573,7 +573,7 @@ void i915_save_display(struct drm_device *dev) dev_priv->savePFIT_PGM_RATIOS = I915_READ(PFIT_PGM_RATIOS); dev_priv->saveBLC_PWM_CTL = I915_READ(BLC_PWM_CTL); dev_priv->saveBLC_HIST_CTL = I915_READ(BLC_HIST_CTL); - if (IS_I965G(dev)) + if (INTEL_INFO(dev)->gen >= 4) dev_priv->saveBLC_PWM_CTL2 = I915_READ(BLC_PWM_CTL2); if (IS_MOBILE(dev) && !IS_I830(dev)) dev_priv->saveLVDS = I915_READ(LVDS); @@ -664,7 +664,7 @@ void i915_restore_display(struct drm_device *dev) I915_WRITE(CURBPOS, dev_priv->saveCURBPOS); I915_WRITE(CURBCNTR, dev_priv->saveCURBCNTR); I915_WRITE(CURBBASE, dev_priv->saveCURBBASE); - if (!IS_I9XX(dev)) + if (IS_GEN2(dev)) I915_WRITE(CURSIZE, dev_priv->saveCURSIZE); /* CRT state */ @@ -674,7 +674,7 @@ void i915_restore_display(struct drm_device *dev) I915_WRITE(ADPA, dev_priv->saveADPA); /* LVDS state */ - if (IS_I965G(dev) && !HAS_PCH_SPLIT(dev)) + if (INTEL_INFO(dev)->gen >= 4 && !HAS_PCH_SPLIT(dev)) I915_WRITE(BLC_PWM_CTL2, dev_priv->saveBLC_PWM_CTL2); if (HAS_PCH_SPLIT(dev)) { @@ -878,9 +878,7 @@ int i915_restore_state(struct drm_device *dev) for (i = 0; i < 3; i++) I915_WRITE(SWF30 + (i << 2), dev_priv->saveSWF2[i]); - /* I2C state */ - intel_i2c_reset_gmbus(dev); + intel_i2c_reset(dev); return 0; } - diff --git a/drivers/gpu/drm/i915/intel_bios.c b/drivers/gpu/drm/i915/intel_bios.c index 96f75d7f663..b1f73ac0f3f 100644 --- a/drivers/gpu/drm/i915/intel_bios.c +++ b/drivers/gpu/drm/i915/intel_bios.c @@ -129,10 +129,6 @@ parse_lfp_panel_data(struct drm_i915_private *dev_priv, int i, temp_downclock; struct drm_display_mode *temp_mode; - /* Defaults if we can't find VBT info */ - dev_priv->lvds_dither = 0; - dev_priv->lvds_vbt = 0; - lvds_options = find_section(bdb, BDB_LVDS_OPTIONS); if (!lvds_options) return; @@ -140,6 +136,7 @@ parse_lfp_panel_data(struct drm_i915_private *dev_priv, dev_priv->lvds_dither = lvds_options->pixel_dither; if (lvds_options->panel_type == 0xff) return; + panel_type = lvds_options->panel_type; lvds_lfp_data = find_section(bdb, BDB_LVDS_LFP_DATA); @@ -169,6 +166,8 @@ parse_lfp_panel_data(struct drm_i915_private *dev_priv, ((unsigned char *)entry + dvo_timing_offset); panel_fixed_mode = kzalloc(sizeof(*panel_fixed_mode), GFP_KERNEL); + if (!panel_fixed_mode) + return; fill_detail_timing_data(panel_fixed_mode, dvo_timing); @@ -230,8 +229,6 @@ parse_sdvo_panel_data(struct drm_i915_private *dev_priv, struct lvds_dvo_timing *dvo_timing; struct drm_display_mode *panel_fixed_mode; - dev_priv->sdvo_lvds_vbt_mode = NULL; - sdvo_lvds_options = find_section(bdb, BDB_SDVO_LVDS_OPTIONS); if (!sdvo_lvds_options) return; @@ -260,10 +257,6 @@ parse_general_features(struct drm_i915_private *dev_priv, struct drm_device *dev = dev_priv->dev; struct bdb_general_features *general; - /* Set sensible defaults in case we can't find the general block */ - dev_priv->int_tv_support = 1; - dev_priv->int_crt_support = 1; - general = find_section(bdb, BDB_GENERAL_FEATURES); if (general) { dev_priv->int_tv_support = general->int_tv_support; @@ -289,14 +282,6 @@ parse_general_definitions(struct drm_i915_private *dev_priv, struct bdb_header *bdb) { struct bdb_general_definitions *general; - const int crt_bus_map_table[] = { - GPIOB, - GPIOA, - GPIOC, - GPIOD, - GPIOE, - GPIOF, - }; general = find_section(bdb, BDB_GENERAL_DEFINITIONS); if (general) { @@ -304,10 +289,8 @@ parse_general_definitions(struct drm_i915_private *dev_priv, if (block_size >= sizeof(*general)) { int bus_pin = general->crt_ddc_gmbus_pin; DRM_DEBUG_KMS("crt_ddc_bus_pin: %d\n", bus_pin); - if ((bus_pin >= 1) && (bus_pin <= 6)) { - dev_priv->crt_ddc_bus = - crt_bus_map_table[bus_pin-1]; - } + if (bus_pin >= 1 && bus_pin <= 6) + dev_priv->crt_ddc_pin = bus_pin; } else { DRM_DEBUG_KMS("BDB_GD too small (%d). Invalid.\n", block_size); @@ -317,7 +300,7 @@ parse_general_definitions(struct drm_i915_private *dev_priv, static void parse_sdvo_device_mapping(struct drm_i915_private *dev_priv, - struct bdb_header *bdb) + struct bdb_header *bdb) { struct sdvo_device_mapping *p_mapping; struct bdb_general_definitions *p_defs; @@ -327,7 +310,7 @@ parse_sdvo_device_mapping(struct drm_i915_private *dev_priv, p_defs = find_section(bdb, BDB_GENERAL_DEFINITIONS); if (!p_defs) { - DRM_DEBUG_KMS("No general definition block is found\n"); + DRM_DEBUG_KMS("No general definition block is found, unable to construct sdvo mapping.\n"); return; } /* judge whether the size of child device meets the requirements. @@ -377,7 +360,16 @@ parse_sdvo_device_mapping(struct drm_i915_private *dev_priv, p_mapping->slave_addr = p_child->slave_addr; p_mapping->dvo_wiring = p_child->dvo_wiring; p_mapping->ddc_pin = p_child->ddc_pin; + p_mapping->i2c_pin = p_child->i2c_pin; + p_mapping->i2c_speed = p_child->i2c_speed; p_mapping->initialized = 1; + DRM_DEBUG_KMS("SDVO device: dvo=%x, addr=%x, wiring=%d, ddc_pin=%d, i2c_pin=%d, i2c_speed=%d\n", + p_mapping->dvo_port, + p_mapping->slave_addr, + p_mapping->dvo_wiring, + p_mapping->ddc_pin, + p_mapping->i2c_pin, + p_mapping->i2c_speed); } else { DRM_DEBUG_KMS("Maybe one SDVO port is shared by " "two SDVO device.\n"); @@ -409,14 +401,11 @@ parse_driver_features(struct drm_i915_private *dev_priv, if (!driver) return; - if (driver && SUPPORTS_EDP(dev) && - driver->lvds_config == BDB_DRIVER_FEATURE_EDP) { - dev_priv->edp_support = 1; - } else { - dev_priv->edp_support = 0; - } + if (SUPPORTS_EDP(dev) && + driver->lvds_config == BDB_DRIVER_FEATURE_EDP) + dev_priv->edp.support = 1; - if (driver && driver->dual_frequency) + if (driver->dual_frequency) dev_priv->render_reclock_avail = true; } @@ -427,26 +416,40 @@ parse_edp(struct drm_i915_private *dev_priv, struct bdb_header *bdb) edp = find_section(bdb, BDB_EDP); if (!edp) { - if (SUPPORTS_EDP(dev_priv->dev) && dev_priv->edp_support) { + if (SUPPORTS_EDP(dev_priv->dev) && dev_priv->edp.support) { DRM_DEBUG_KMS("No eDP BDB found but eDP panel " - "supported, assume 18bpp panel color " - "depth.\n"); - dev_priv->edp_bpp = 18; + "supported, assume %dbpp panel color " + "depth.\n", + dev_priv->edp.bpp); } return; } switch ((edp->color_depth >> (panel_type * 2)) & 3) { case EDP_18BPP: - dev_priv->edp_bpp = 18; + dev_priv->edp.bpp = 18; break; case EDP_24BPP: - dev_priv->edp_bpp = 24; + dev_priv->edp.bpp = 24; break; case EDP_30BPP: - dev_priv->edp_bpp = 30; + dev_priv->edp.bpp = 30; break; } + + dev_priv->edp.rate = edp->link_params[panel_type].rate; + dev_priv->edp.lanes = edp->link_params[panel_type].lanes; + dev_priv->edp.preemphasis = edp->link_params[panel_type].preemphasis; + dev_priv->edp.vswing = edp->link_params[panel_type].vswing; + + DRM_DEBUG_KMS("eDP vBIOS settings: bpp=%d, rate=%d, lanes=%d, preemphasis=%d, vswing=%d\n", + dev_priv->edp.bpp, + dev_priv->edp.rate, + dev_priv->edp.lanes, + dev_priv->edp.preemphasis, + dev_priv->edp.vswing); + + dev_priv->edp.initialized = true; } static void @@ -460,7 +463,7 @@ parse_device_mapping(struct drm_i915_private *dev_priv, p_defs = find_section(bdb, BDB_GENERAL_DEFINITIONS); if (!p_defs) { - DRM_DEBUG_KMS("No general definition block is found\n"); + DRM_DEBUG_KMS("No general definition block is found, no devices defined.\n"); return; } /* judge whether the size of child device meets the requirements. @@ -513,6 +516,28 @@ parse_device_mapping(struct drm_i915_private *dev_priv, } return; } + +static void +init_vbt_defaults(struct drm_i915_private *dev_priv) +{ + dev_priv->crt_ddc_pin = GMBUS_PORT_VGADDC; + + /* LFP panel data */ + dev_priv->lvds_dither = 1; + dev_priv->lvds_vbt = 0; + + /* SDVO panel data */ + dev_priv->sdvo_lvds_vbt_mode = NULL; + + /* general features */ + dev_priv->int_tv_support = 1; + dev_priv->int_crt_support = 1; + dev_priv->lvds_use_ssc = 0; + + /* eDP data */ + dev_priv->edp.bpp = 18; +} + /** * intel_init_bios - initialize VBIOS settings & find VBT * @dev: DRM device @@ -520,11 +545,6 @@ parse_device_mapping(struct drm_i915_private *dev_priv, * Loads the Video BIOS and checks that the VBT exists. Sets scratch registers * to appropriate values. * - * VBT existence is a sanity check that is relied on by other i830_bios.c code. - * Note that it would be better to use a BIOS call to get the VBT, as BIOSes may - * feed an updated VBT back through that, compared to what we'll fetch using - * this method of groping around in the BIOS data. - * * Returns 0 on success, nonzero on failure. */ bool @@ -532,31 +552,47 @@ intel_init_bios(struct drm_device *dev) { struct drm_i915_private *dev_priv = dev->dev_private; struct pci_dev *pdev = dev->pdev; - struct vbt_header *vbt = NULL; - struct bdb_header *bdb; - u8 __iomem *bios; - size_t size; - int i; - - bios = pci_map_rom(pdev, &size); - if (!bios) - return -1; - - /* Scour memory looking for the VBT signature */ - for (i = 0; i + 4 < size; i++) { - if (!memcmp(bios + i, "$VBT", 4)) { - vbt = (struct vbt_header *)(bios + i); - break; - } + struct bdb_header *bdb = NULL; + u8 __iomem *bios = NULL; + + init_vbt_defaults(dev_priv); + + /* XXX Should this validation be moved to intel_opregion.c? */ + if (dev_priv->opregion.vbt) { + struct vbt_header *vbt = dev_priv->opregion.vbt; + if (memcmp(vbt->signature, "$VBT", 4) == 0) { + DRM_DEBUG_DRIVER("Using VBT from OpRegion: %20s\n", + vbt->signature); + bdb = (struct bdb_header *)((char *)vbt + vbt->bdb_offset); + } else + dev_priv->opregion.vbt = NULL; } - if (!vbt) { - DRM_ERROR("VBT signature missing\n"); - pci_unmap_rom(pdev, bios); - return -1; - } + if (bdb == NULL) { + struct vbt_header *vbt = NULL; + size_t size; + int i; + + bios = pci_map_rom(pdev, &size); + if (!bios) + return -1; + + /* Scour memory looking for the VBT signature */ + for (i = 0; i + 4 < size; i++) { + if (!memcmp(bios + i, "$VBT", 4)) { + vbt = (struct vbt_header *)(bios + i); + break; + } + } - bdb = (struct bdb_header *)(bios + i + vbt->bdb_offset); + if (!vbt) { + DRM_ERROR("VBT signature missing\n"); + pci_unmap_rom(pdev, bios); + return -1; + } + + bdb = (struct bdb_header *)(bios + i + vbt->bdb_offset); + } /* Grab useful general definitions */ parse_general_features(dev_priv, bdb); @@ -568,7 +604,8 @@ intel_init_bios(struct drm_device *dev) parse_driver_features(dev_priv, bdb); parse_edp(dev_priv, bdb); - pci_unmap_rom(pdev, bios); + if (bios) + pci_unmap_rom(pdev, bios); return 0; } diff --git a/drivers/gpu/drm/i915/intel_bios.h b/drivers/gpu/drm/i915/intel_bios.h index 4c18514f6f8..e1a598f2a96 100644 --- a/drivers/gpu/drm/i915/intel_bios.h +++ b/drivers/gpu/drm/i915/intel_bios.h @@ -197,7 +197,8 @@ struct bdb_general_features { struct child_device_config { u16 handle; u16 device_type; - u8 device_id[10]; /* See DEVICE_TYPE_* above */ + u8 i2c_speed; + u8 rsvd[9]; u16 addin_offset; u8 dvo_port; /* See Device_PORT_* above */ u8 i2c_pin; diff --git a/drivers/gpu/drm/i915/intel_crt.c b/drivers/gpu/drm/i915/intel_crt.c index 197d4f32585..389fcd2aea1 100644 --- a/drivers/gpu/drm/i915/intel_crt.c +++ b/drivers/gpu/drm/i915/intel_crt.c @@ -79,7 +79,7 @@ static int intel_crt_mode_valid(struct drm_connector *connector, if (mode->clock < 25000) return MODE_CLOCK_LOW; - if (!IS_I9XX(dev)) + if (IS_GEN2(dev)) max_clock = 350000; else max_clock = 400000; @@ -123,7 +123,7 @@ static void intel_crt_mode_set(struct drm_encoder *encoder, * Disable separate mode multiplier used when cloning SDVO to CRT * XXX this needs to be adjusted when we really are cloning */ - if (IS_I965G(dev) && !HAS_PCH_SPLIT(dev)) { + if (INTEL_INFO(dev)->gen >= 4 && !HAS_PCH_SPLIT(dev)) { dpll_md = I915_READ(dpll_md_reg); I915_WRITE(dpll_md_reg, dpll_md & ~DPLL_MD_UDI_MULTIPLIER_MASK); @@ -187,7 +187,7 @@ static bool intel_ironlake_crt_detect_hotplug(struct drm_connector *connector) I915_WRITE(PCH_ADPA, adpa); if (wait_for((I915_READ(PCH_ADPA) & ADPA_CRT_HOTPLUG_FORCE_TRIGGER) == 0, - 1000, 1)) + 1000)) DRM_DEBUG_KMS("timed out waiting for FORCE_TRIGGER"); if (turn_off_dac) { @@ -244,7 +244,7 @@ static bool intel_crt_detect_hotplug(struct drm_connector *connector) /* wait for FORCE_DETECT to go off */ if (wait_for((I915_READ(PORT_HOTPLUG_EN) & CRT_HOTPLUG_FORCE_DETECT) == 0, - 1000, 1)) + 1000)) DRM_DEBUG_KMS("timed out waiting for FORCE_DETECT to go off"); } @@ -261,21 +261,47 @@ static bool intel_crt_detect_hotplug(struct drm_connector *connector) return ret; } +static bool intel_crt_ddc_probe(struct drm_i915_private *dev_priv, int ddc_bus) +{ + u8 buf; + struct i2c_msg msgs[] = { + { + .addr = 0xA0, + .flags = 0, + .len = 1, + .buf = &buf, + }, + }; + /* DDC monitor detect: Does it ACK a write to 0xA0? */ + return i2c_transfer(&dev_priv->gmbus[ddc_bus].adapter, msgs, 1) == 1; +} + static bool intel_crt_detect_ddc(struct drm_encoder *encoder) { - struct intel_encoder *intel_encoder = enc_to_intel_encoder(encoder); + struct intel_encoder *intel_encoder = to_intel_encoder(encoder); + struct drm_i915_private *dev_priv = encoder->dev->dev_private; /* CRT should always be at 0, but check anyway */ if (intel_encoder->type != INTEL_OUTPUT_ANALOG) return false; - return intel_ddc_probe(intel_encoder); + if (intel_crt_ddc_probe(dev_priv, dev_priv->crt_ddc_pin)) { + DRM_DEBUG_KMS("CRT detected via DDC:0xa0\n"); + return true; + } + + if (intel_ddc_probe(intel_encoder, dev_priv->crt_ddc_pin)) { + DRM_DEBUG_KMS("CRT detected via DDC:0x50 [EDID]\n"); + return true; + } + + return false; } static enum drm_connector_status intel_crt_load_detect(struct drm_crtc *crtc, struct intel_encoder *intel_encoder) { - struct drm_encoder *encoder = &intel_encoder->enc; + struct drm_encoder *encoder = &intel_encoder->base; struct drm_device *dev = encoder->dev; struct drm_i915_private *dev_priv = dev->dev_private; struct intel_crtc *intel_crtc = to_intel_crtc(crtc); @@ -295,6 +321,8 @@ intel_crt_load_detect(struct drm_crtc *crtc, struct intel_encoder *intel_encoder uint8_t st00; enum drm_connector_status status; + DRM_DEBUG_KMS("starting load-detect on CRT\n"); + if (pipe == 0) { bclrpat_reg = BCLRPAT_A; vtotal_reg = VTOTAL_A; @@ -324,9 +352,10 @@ intel_crt_load_detect(struct drm_crtc *crtc, struct intel_encoder *intel_encoder /* Set the border color to purple. */ I915_WRITE(bclrpat_reg, 0x500050); - if (IS_I9XX(dev)) { + if (!IS_GEN2(dev)) { uint32_t pipeconf = I915_READ(pipeconf_reg); I915_WRITE(pipeconf_reg, pipeconf | PIPECONF_FORCE_BORDER); + POSTING_READ(pipeconf_reg); /* Wait for next Vblank to substitue * border color for Color info */ intel_wait_for_vblank(dev, pipe); @@ -404,34 +433,37 @@ static enum drm_connector_status intel_crt_detect(struct drm_connector *connector, bool force) { struct drm_device *dev = connector->dev; - struct drm_encoder *encoder = intel_attached_encoder(connector); - struct intel_encoder *intel_encoder = enc_to_intel_encoder(encoder); + struct intel_encoder *encoder = intel_attached_encoder(connector); struct drm_crtc *crtc; int dpms_mode; enum drm_connector_status status; - if (IS_I9XX(dev) && !IS_I915G(dev) && !IS_I915GM(dev)) { - if (intel_crt_detect_hotplug(connector)) + if (I915_HAS_HOTPLUG(dev)) { + if (intel_crt_detect_hotplug(connector)) { + DRM_DEBUG_KMS("CRT detected via hotplug\n"); return connector_status_connected; - else + } else return connector_status_disconnected; } - if (intel_crt_detect_ddc(encoder)) + if (intel_crt_detect_ddc(&encoder->base)) return connector_status_connected; if (!force) return connector->status; /* for pre-945g platforms use load detect */ - if (encoder->crtc && encoder->crtc->enabled) { - status = intel_crt_load_detect(encoder->crtc, intel_encoder); + if (encoder->base.crtc && encoder->base.crtc->enabled) { + status = intel_crt_load_detect(encoder->base.crtc, encoder); } else { - crtc = intel_get_load_detect_pipe(intel_encoder, connector, + crtc = intel_get_load_detect_pipe(encoder, connector, NULL, &dpms_mode); if (crtc) { - status = intel_crt_load_detect(crtc, intel_encoder); - intel_release_load_detect_pipe(intel_encoder, + if (intel_crt_detect_ddc(&encoder->base)) + status = connector_status_connected; + else + status = intel_crt_load_detect(crtc, encoder); + intel_release_load_detect_pipe(encoder, connector, dpms_mode); } else status = connector_status_unknown; @@ -449,32 +481,18 @@ static void intel_crt_destroy(struct drm_connector *connector) static int intel_crt_get_modes(struct drm_connector *connector) { - int ret; - struct drm_encoder *encoder = intel_attached_encoder(connector); - struct intel_encoder *intel_encoder = enc_to_intel_encoder(encoder); - struct i2c_adapter *ddc_bus; struct drm_device *dev = connector->dev; + struct drm_i915_private *dev_priv = dev->dev_private; + int ret; - - ret = intel_ddc_get_modes(connector, intel_encoder->ddc_bus); + ret = intel_ddc_get_modes(connector, + &dev_priv->gmbus[dev_priv->crt_ddc_pin].adapter); if (ret || !IS_G4X(dev)) - goto end; + return ret; /* Try to probe digital port for output in DVI-I -> VGA mode. */ - ddc_bus = intel_i2c_create(connector->dev, GPIOD, "CRTDDC_D"); - - if (!ddc_bus) { - dev_printk(KERN_ERR, &connector->dev->pdev->dev, - "DDC bus registration failed for CRTDDC_D.\n"); - goto end; - } - /* Try to get modes by GPIOD port */ - ret = intel_ddc_get_modes(connector, ddc_bus); - intel_i2c_destroy(ddc_bus); - -end: - return ret; - + return intel_ddc_get_modes(connector, + &dev_priv->gmbus[GMBUS_PORT_DPB].adapter); } static int intel_crt_set_property(struct drm_connector *connector, @@ -507,7 +525,7 @@ static const struct drm_connector_funcs intel_crt_connector_funcs = { static const struct drm_connector_helper_funcs intel_crt_connector_helper_funcs = { .mode_valid = intel_crt_mode_valid, .get_modes = intel_crt_get_modes, - .best_encoder = intel_attached_encoder, + .best_encoder = intel_best_encoder, }; static const struct drm_encoder_funcs intel_crt_enc_funcs = { @@ -520,7 +538,6 @@ void intel_crt_init(struct drm_device *dev) struct intel_encoder *intel_encoder; struct intel_connector *intel_connector; struct drm_i915_private *dev_priv = dev->dev_private; - u32 i2c_reg; intel_encoder = kzalloc(sizeof(struct intel_encoder), GFP_KERNEL); if (!intel_encoder) @@ -536,27 +553,10 @@ void intel_crt_init(struct drm_device *dev) drm_connector_init(dev, &intel_connector->base, &intel_crt_connector_funcs, DRM_MODE_CONNECTOR_VGA); - drm_encoder_init(dev, &intel_encoder->enc, &intel_crt_enc_funcs, + drm_encoder_init(dev, &intel_encoder->base, &intel_crt_enc_funcs, DRM_MODE_ENCODER_DAC); - drm_mode_connector_attach_encoder(&intel_connector->base, - &intel_encoder->enc); - - /* Set up the DDC bus. */ - if (HAS_PCH_SPLIT(dev)) - i2c_reg = PCH_GPIOA; - else { - i2c_reg = GPIOA; - /* Use VBT information for CRT DDC if available */ - if (dev_priv->crt_ddc_bus != 0) - i2c_reg = dev_priv->crt_ddc_bus; - } - intel_encoder->ddc_bus = intel_i2c_create(dev, i2c_reg, "CRTDDC_A"); - if (!intel_encoder->ddc_bus) { - dev_printk(KERN_ERR, &dev->pdev->dev, "DDC bus registration " - "failed.\n"); - return; - } + intel_connector_attach_encoder(intel_connector, intel_encoder); intel_encoder->type = INTEL_OUTPUT_ANALOG; intel_encoder->clone_mask = (1 << INTEL_SDVO_NON_TV_CLONE_BIT) | @@ -566,7 +566,7 @@ void intel_crt_init(struct drm_device *dev) connector->interlace_allowed = 1; connector->doublescan_allowed = 0; - drm_encoder_helper_add(&intel_encoder->enc, &intel_crt_helper_funcs); + drm_encoder_helper_add(&intel_encoder->base, &intel_crt_helper_funcs); drm_connector_helper_add(connector, &intel_crt_connector_helper_funcs); drm_sysfs_connector_add(connector); diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index b5bf51a4502..69c54c5a425 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -43,8 +43,8 @@ bool intel_pipe_has_type (struct drm_crtc *crtc, int type); static void intel_update_watermarks(struct drm_device *dev); -static void intel_increase_pllclock(struct drm_crtc *crtc, bool schedule); -static void intel_crtc_update_cursor(struct drm_crtc *crtc); +static void intel_increase_pllclock(struct drm_crtc *crtc); +static void intel_crtc_update_cursor(struct drm_crtc *crtc, bool on); typedef struct { /* given values */ @@ -342,6 +342,13 @@ static bool intel_find_pll_ironlake_dp(const intel_limit_t *, struct drm_crtc *crtc, int target, int refclk, intel_clock_t *best_clock); +static inline u32 /* units of 100MHz */ +intel_fdi_link_freq(struct drm_device *dev) +{ + struct drm_i915_private *dev_priv = dev->dev_private; + return (I915_READ(FDI_PLL_BIOS_0) & FDI_PLL_FB_CLOCK_MASK) + 2; +} + static const intel_limit_t intel_limits_i8xx_dvo = { .dot = { .min = I8XX_DOT_MIN, .max = I8XX_DOT_MAX }, .vco = { .min = I8XX_VCO_MIN, .max = I8XX_VCO_MAX }, @@ -701,16 +708,16 @@ static const intel_limit_t *intel_limit(struct drm_crtc *crtc) limit = intel_ironlake_limit(crtc); else if (IS_G4X(dev)) { limit = intel_g4x_limit(crtc); - } else if (IS_I9XX(dev) && !IS_PINEVIEW(dev)) { - if (intel_pipe_has_type(crtc, INTEL_OUTPUT_LVDS)) - limit = &intel_limits_i9xx_lvds; - else - limit = &intel_limits_i9xx_sdvo; } else if (IS_PINEVIEW(dev)) { if (intel_pipe_has_type(crtc, INTEL_OUTPUT_LVDS)) limit = &intel_limits_pineview_lvds; else limit = &intel_limits_pineview_sdvo; + } else if (!IS_GEN2(dev)) { + if (intel_pipe_has_type(crtc, INTEL_OUTPUT_LVDS)) + limit = &intel_limits_i9xx_lvds; + else + limit = &intel_limits_i9xx_sdvo; } else { if (intel_pipe_has_type(crtc, INTEL_OUTPUT_LVDS)) limit = &intel_limits_i8xx_lvds; @@ -744,20 +751,17 @@ static void intel_clock(struct drm_device *dev, int refclk, intel_clock_t *clock /** * Returns whether any output on the specified pipe is of the specified type */ -bool intel_pipe_has_type (struct drm_crtc *crtc, int type) +bool intel_pipe_has_type(struct drm_crtc *crtc, int type) { - struct drm_device *dev = crtc->dev; - struct drm_mode_config *mode_config = &dev->mode_config; - struct drm_encoder *l_entry; + struct drm_device *dev = crtc->dev; + struct drm_mode_config *mode_config = &dev->mode_config; + struct intel_encoder *encoder; + + list_for_each_entry(encoder, &mode_config->encoder_list, base.head) + if (encoder->base.crtc == crtc && encoder->type == type) + return true; - list_for_each_entry(l_entry, &mode_config->encoder_list, head) { - if (l_entry && l_entry->crtc == crtc) { - struct intel_encoder *intel_encoder = enc_to_intel_encoder(l_entry); - if (intel_encoder->type == type) - return true; - } - } - return false; + return false; } #define INTELPllInvalid(s) do { /* DRM_DEBUG(s); */ return false; } while (0) @@ -955,26 +959,26 @@ static bool intel_find_pll_g4x_dp(const intel_limit_t *limit, struct drm_crtc *crtc, int target, int refclk, intel_clock_t *best_clock) { - intel_clock_t clock; - if (target < 200000) { - clock.p1 = 2; - clock.p2 = 10; - clock.n = 2; - clock.m1 = 23; - clock.m2 = 8; - } else { - clock.p1 = 1; - clock.p2 = 10; - clock.n = 1; - clock.m1 = 14; - clock.m2 = 2; - } - clock.m = 5 * (clock.m1 + 2) + (clock.m2 + 2); - clock.p = (clock.p1 * clock.p2); - clock.dot = 96000 * clock.m / (clock.n + 2) / clock.p; - clock.vco = 0; - memcpy(best_clock, &clock, sizeof(intel_clock_t)); - return true; + intel_clock_t clock; + if (target < 200000) { + clock.p1 = 2; + clock.p2 = 10; + clock.n = 2; + clock.m1 = 23; + clock.m2 = 8; + } else { + clock.p1 = 1; + clock.p2 = 10; + clock.n = 1; + clock.m1 = 14; + clock.m2 = 2; + } + clock.m = 5 * (clock.m1 + 2) + (clock.m2 + 2); + clock.p = (clock.p1 * clock.p2); + clock.dot = 96000 * clock.m / (clock.n + 2) / clock.p; + clock.vco = 0; + memcpy(best_clock, &clock, sizeof(intel_clock_t)); + return true; } /** @@ -1007,14 +1011,14 @@ void intel_wait_for_vblank(struct drm_device *dev, int pipe) I915_READ(pipestat_reg) | PIPE_VBLANK_INTERRUPT_STATUS); /* Wait for vblank interrupt bit to set */ - if (wait_for((I915_READ(pipestat_reg) & - PIPE_VBLANK_INTERRUPT_STATUS), - 50, 0)) + if (wait_for(I915_READ(pipestat_reg) & + PIPE_VBLANK_INTERRUPT_STATUS, + 50)) DRM_DEBUG_KMS("vblank wait timed out\n"); } -/** - * intel_wait_for_vblank_off - wait for vblank after disabling a pipe +/* + * intel_wait_for_pipe_off - wait for pipe to turn off * @dev: drm device * @pipe: pipe to wait for * @@ -1022,28 +1026,41 @@ void intel_wait_for_vblank(struct drm_device *dev, int pipe) * spinning on the vblank interrupt status bit, since we won't actually * see an interrupt when the pipe is disabled. * - * So this function waits for the display line value to settle (it - * usually ends up stopping at the start of the next frame). + * On Gen4 and above: + * wait for the pipe register state bit to turn off + * + * Otherwise: + * wait for the display line value to settle (it usually + * ends up stopping at the start of the next frame). + * */ -void intel_wait_for_vblank_off(struct drm_device *dev, int pipe) +void intel_wait_for_pipe_off(struct drm_device *dev, int pipe) { struct drm_i915_private *dev_priv = dev->dev_private; - int pipedsl_reg = (pipe == 0 ? PIPEADSL : PIPEBDSL); - unsigned long timeout = jiffies + msecs_to_jiffies(100); - u32 last_line; - - /* Wait for the display line to settle */ - do { - last_line = I915_READ(pipedsl_reg) & DSL_LINEMASK; - mdelay(5); - } while (((I915_READ(pipedsl_reg) & DSL_LINEMASK) != last_line) && - time_after(timeout, jiffies)); - - if (time_after(jiffies, timeout)) - DRM_DEBUG_KMS("vblank wait timed out\n"); + + if (INTEL_INFO(dev)->gen >= 4) { + int reg = PIPECONF(pipe); + + /* Wait for the Pipe State to go off */ + if (wait_for((I915_READ(reg) & I965_PIPECONF_ACTIVE) == 0, + 100)) + DRM_DEBUG_KMS("pipe_off wait timed out\n"); + } else { + u32 last_line; + int reg = PIPEDSL(pipe); + unsigned long timeout = jiffies + msecs_to_jiffies(100); + + /* Wait for the display line to settle */ + do { + last_line = I915_READ(reg) & DSL_LINEMASK; + mdelay(5); + } while (((I915_READ(reg) & DSL_LINEMASK) != last_line) && + time_after(timeout, jiffies)); + if (time_after(jiffies, timeout)) + DRM_DEBUG_KMS("pipe_off wait timed out\n"); + } } -/* Parameters have changed, update FBC info */ static void i8xx_enable_fbc(struct drm_crtc *crtc, unsigned long interval) { struct drm_device *dev = crtc->dev; @@ -1055,6 +1072,14 @@ static void i8xx_enable_fbc(struct drm_crtc *crtc, unsigned long interval) int plane, i; u32 fbc_ctl, fbc_ctl2; + if (fb->pitch == dev_priv->cfb_pitch && + obj_priv->fence_reg == dev_priv->cfb_fence && + intel_crtc->plane == dev_priv->cfb_plane && + I915_READ(FBC_CONTROL) & FBC_CTL_EN) + return; + + i8xx_disable_fbc(dev); + dev_priv->cfb_pitch = dev_priv->cfb_size / FBC_LL_SIZE; if (fb->pitch < dev_priv->cfb_pitch) @@ -1088,7 +1113,7 @@ static void i8xx_enable_fbc(struct drm_crtc *crtc, unsigned long interval) I915_WRITE(FBC_CONTROL, fbc_ctl); DRM_DEBUG_KMS("enabled FBC, pitch %ld, yoff %d, plane %d, ", - dev_priv->cfb_pitch, crtc->y, dev_priv->cfb_plane); + dev_priv->cfb_pitch, crtc->y, dev_priv->cfb_plane); } void i8xx_disable_fbc(struct drm_device *dev) @@ -1096,19 +1121,16 @@ void i8xx_disable_fbc(struct drm_device *dev) struct drm_i915_private *dev_priv = dev->dev_private; u32 fbc_ctl; - if (!I915_HAS_FBC(dev)) - return; - - if (!(I915_READ(FBC_CONTROL) & FBC_CTL_EN)) - return; /* Already off, just return */ - /* Disable compression */ fbc_ctl = I915_READ(FBC_CONTROL); + if ((fbc_ctl & FBC_CTL_EN) == 0) + return; + fbc_ctl &= ~FBC_CTL_EN; I915_WRITE(FBC_CONTROL, fbc_ctl); /* Wait for compressing bit to clear */ - if (wait_for((I915_READ(FBC_STATUS) & FBC_STAT_COMPRESSING) == 0, 10, 0)) { + if (wait_for((I915_READ(FBC_STATUS) & FBC_STAT_COMPRESSING) == 0, 10)) { DRM_DEBUG_KMS("FBC idle timed out\n"); return; } @@ -1131,14 +1153,27 @@ static void g4x_enable_fbc(struct drm_crtc *crtc, unsigned long interval) struct intel_framebuffer *intel_fb = to_intel_framebuffer(fb); struct drm_i915_gem_object *obj_priv = to_intel_bo(intel_fb->obj); struct intel_crtc *intel_crtc = to_intel_crtc(crtc); - int plane = (intel_crtc->plane == 0 ? DPFC_CTL_PLANEA : - DPFC_CTL_PLANEB); + int plane = intel_crtc->plane == 0 ? DPFC_CTL_PLANEA : DPFC_CTL_PLANEB; unsigned long stall_watermark = 200; u32 dpfc_ctl; + dpfc_ctl = I915_READ(DPFC_CONTROL); + if (dpfc_ctl & DPFC_CTL_EN) { + if (dev_priv->cfb_pitch == dev_priv->cfb_pitch / 64 - 1 && + dev_priv->cfb_fence == obj_priv->fence_reg && + dev_priv->cfb_plane == intel_crtc->plane && + dev_priv->cfb_y == crtc->y) + return; + + I915_WRITE(DPFC_CONTROL, dpfc_ctl & ~DPFC_CTL_EN); + POSTING_READ(DPFC_CONTROL); + intel_wait_for_vblank(dev, intel_crtc->pipe); + } + dev_priv->cfb_pitch = (dev_priv->cfb_pitch / 64) - 1; dev_priv->cfb_fence = obj_priv->fence_reg; dev_priv->cfb_plane = intel_crtc->plane; + dev_priv->cfb_y = crtc->y; dpfc_ctl = plane | DPFC_SR_EN | DPFC_CTL_LIMIT_1X; if (obj_priv->tiling_mode != I915_TILING_NONE) { @@ -1148,7 +1183,6 @@ static void g4x_enable_fbc(struct drm_crtc *crtc, unsigned long interval) I915_WRITE(DPFC_CHICKEN, ~DPFC_HT_MODIFY); } - I915_WRITE(DPFC_CONTROL, dpfc_ctl); I915_WRITE(DPFC_RECOMP_CTL, DPFC_RECOMP_STALL_EN | (stall_watermark << DPFC_RECOMP_STALL_WM_SHIFT) | (interval << DPFC_RECOMP_TIMER_COUNT_SHIFT)); @@ -1167,10 +1201,12 @@ void g4x_disable_fbc(struct drm_device *dev) /* Disable compression */ dpfc_ctl = I915_READ(DPFC_CONTROL); - dpfc_ctl &= ~DPFC_CTL_EN; - I915_WRITE(DPFC_CONTROL, dpfc_ctl); + if (dpfc_ctl & DPFC_CTL_EN) { + dpfc_ctl &= ~DPFC_CTL_EN; + I915_WRITE(DPFC_CONTROL, dpfc_ctl); - DRM_DEBUG_KMS("disabled FBC\n"); + DRM_DEBUG_KMS("disabled FBC\n"); + } } static bool g4x_fbc_enabled(struct drm_device *dev) @@ -1188,16 +1224,30 @@ static void ironlake_enable_fbc(struct drm_crtc *crtc, unsigned long interval) struct intel_framebuffer *intel_fb = to_intel_framebuffer(fb); struct drm_i915_gem_object *obj_priv = to_intel_bo(intel_fb->obj); struct intel_crtc *intel_crtc = to_intel_crtc(crtc); - int plane = (intel_crtc->plane == 0) ? DPFC_CTL_PLANEA : - DPFC_CTL_PLANEB; + int plane = intel_crtc->plane == 0 ? DPFC_CTL_PLANEA : DPFC_CTL_PLANEB; unsigned long stall_watermark = 200; u32 dpfc_ctl; + dpfc_ctl = I915_READ(ILK_DPFC_CONTROL); + if (dpfc_ctl & DPFC_CTL_EN) { + if (dev_priv->cfb_pitch == dev_priv->cfb_pitch / 64 - 1 && + dev_priv->cfb_fence == obj_priv->fence_reg && + dev_priv->cfb_plane == intel_crtc->plane && + dev_priv->cfb_offset == obj_priv->gtt_offset && + dev_priv->cfb_y == crtc->y) + return; + + I915_WRITE(ILK_DPFC_CONTROL, dpfc_ctl & ~DPFC_CTL_EN); + POSTING_READ(ILK_DPFC_CONTROL); + intel_wait_for_vblank(dev, intel_crtc->pipe); + } + dev_priv->cfb_pitch = (dev_priv->cfb_pitch / 64) - 1; dev_priv->cfb_fence = obj_priv->fence_reg; dev_priv->cfb_plane = intel_crtc->plane; + dev_priv->cfb_offset = obj_priv->gtt_offset; + dev_priv->cfb_y = crtc->y; - dpfc_ctl = I915_READ(ILK_DPFC_CONTROL); dpfc_ctl &= DPFC_RESERVED; dpfc_ctl |= (plane | DPFC_CTL_LIMIT_1X); if (obj_priv->tiling_mode != I915_TILING_NONE) { @@ -1207,15 +1257,13 @@ static void ironlake_enable_fbc(struct drm_crtc *crtc, unsigned long interval) I915_WRITE(ILK_DPFC_CHICKEN, ~DPFC_HT_MODIFY); } - I915_WRITE(ILK_DPFC_CONTROL, dpfc_ctl); I915_WRITE(ILK_DPFC_RECOMP_CTL, DPFC_RECOMP_STALL_EN | (stall_watermark << DPFC_RECOMP_STALL_WM_SHIFT) | (interval << DPFC_RECOMP_TIMER_COUNT_SHIFT)); I915_WRITE(ILK_DPFC_FENCE_YOFF, crtc->y); I915_WRITE(ILK_FBC_RT_BASE, obj_priv->gtt_offset | ILK_FBC_RT_VALID); /* enable it... */ - I915_WRITE(ILK_DPFC_CONTROL, I915_READ(ILK_DPFC_CONTROL) | - DPFC_CTL_EN); + I915_WRITE(ILK_DPFC_CONTROL, dpfc_ctl | DPFC_CTL_EN); DRM_DEBUG_KMS("enabled fbc on plane %d\n", intel_crtc->plane); } @@ -1227,10 +1275,12 @@ void ironlake_disable_fbc(struct drm_device *dev) /* Disable compression */ dpfc_ctl = I915_READ(ILK_DPFC_CONTROL); - dpfc_ctl &= ~DPFC_CTL_EN; - I915_WRITE(ILK_DPFC_CONTROL, dpfc_ctl); + if (dpfc_ctl & DPFC_CTL_EN) { + dpfc_ctl &= ~DPFC_CTL_EN; + I915_WRITE(ILK_DPFC_CONTROL, dpfc_ctl); - DRM_DEBUG_KMS("disabled FBC\n"); + DRM_DEBUG_KMS("disabled FBC\n"); + } } static bool ironlake_fbc_enabled(struct drm_device *dev) @@ -1272,8 +1322,7 @@ void intel_disable_fbc(struct drm_device *dev) /** * intel_update_fbc - enable/disable FBC as needed - * @crtc: CRTC to point the compressor at - * @mode: mode in use + * @dev: the drm_device * * Set up the framebuffer compression hardware at mode set time. We * enable it if possible: @@ -1290,18 +1339,14 @@ void intel_disable_fbc(struct drm_device *dev) * * We need to enable/disable FBC on a global basis. */ -static void intel_update_fbc(struct drm_crtc *crtc, - struct drm_display_mode *mode) +static void intel_update_fbc(struct drm_device *dev) { - struct drm_device *dev = crtc->dev; struct drm_i915_private *dev_priv = dev->dev_private; - struct drm_framebuffer *fb = crtc->fb; + struct drm_crtc *crtc = NULL, *tmp_crtc; + struct intel_crtc *intel_crtc; + struct drm_framebuffer *fb; struct intel_framebuffer *intel_fb; struct drm_i915_gem_object *obj_priv; - struct drm_crtc *tmp_crtc; - struct intel_crtc *intel_crtc = to_intel_crtc(crtc); - int plane = intel_crtc->plane; - int crtcs_enabled = 0; DRM_DEBUG_KMS("\n"); @@ -1311,12 +1356,6 @@ static void intel_update_fbc(struct drm_crtc *crtc, if (!I915_HAS_FBC(dev)) return; - if (!crtc->fb) - return; - - intel_fb = to_intel_framebuffer(fb); - obj_priv = to_intel_bo(intel_fb->obj); - /* * If FBC is already on, we just have to verify that we can * keep it that way... @@ -1327,35 +1366,47 @@ static void intel_update_fbc(struct drm_crtc *crtc, * - going to an unsupported config (interlace, pixel multiply, etc.) */ list_for_each_entry(tmp_crtc, &dev->mode_config.crtc_list, head) { - if (tmp_crtc->enabled) - crtcs_enabled++; + if (tmp_crtc->enabled) { + if (crtc) { + DRM_DEBUG_KMS("more than one pipe active, disabling compression\n"); + dev_priv->no_fbc_reason = FBC_MULTIPLE_PIPES; + goto out_disable; + } + crtc = tmp_crtc; + } } - DRM_DEBUG_KMS("%d pipes active\n", crtcs_enabled); - if (crtcs_enabled > 1) { - DRM_DEBUG_KMS("more than one pipe active, disabling compression\n"); - dev_priv->no_fbc_reason = FBC_MULTIPLE_PIPES; + + if (!crtc || crtc->fb == NULL) { + DRM_DEBUG_KMS("no output, disabling\n"); + dev_priv->no_fbc_reason = FBC_NO_OUTPUT; goto out_disable; } + + intel_crtc = to_intel_crtc(crtc); + fb = crtc->fb; + intel_fb = to_intel_framebuffer(fb); + obj_priv = to_intel_bo(intel_fb->obj); + if (intel_fb->obj->size > dev_priv->cfb_size) { DRM_DEBUG_KMS("framebuffer too large, disabling " - "compression\n"); + "compression\n"); dev_priv->no_fbc_reason = FBC_STOLEN_TOO_SMALL; goto out_disable; } - if ((mode->flags & DRM_MODE_FLAG_INTERLACE) || - (mode->flags & DRM_MODE_FLAG_DBLSCAN)) { + if ((crtc->mode.flags & DRM_MODE_FLAG_INTERLACE) || + (crtc->mode.flags & DRM_MODE_FLAG_DBLSCAN)) { DRM_DEBUG_KMS("mode incompatible with compression, " - "disabling\n"); + "disabling\n"); dev_priv->no_fbc_reason = FBC_UNSUPPORTED_MODE; goto out_disable; } - if ((mode->hdisplay > 2048) || - (mode->vdisplay > 1536)) { + if ((crtc->mode.hdisplay > 2048) || + (crtc->mode.vdisplay > 1536)) { DRM_DEBUG_KMS("mode too large for compression, disabling\n"); dev_priv->no_fbc_reason = FBC_MODE_TOO_LARGE; goto out_disable; } - if ((IS_I915GM(dev) || IS_I945GM(dev)) && plane != 0) { + if ((IS_I915GM(dev) || IS_I945GM(dev)) && intel_crtc->plane != 0) { DRM_DEBUG_KMS("plane not 0, disabling compression\n"); dev_priv->no_fbc_reason = FBC_BAD_PLANE; goto out_disable; @@ -1370,18 +1421,7 @@ static void intel_update_fbc(struct drm_crtc *crtc, if (in_dbg_master()) goto out_disable; - if (intel_fbc_enabled(dev)) { - /* We can re-enable it in this case, but need to update pitch */ - if ((fb->pitch > dev_priv->cfb_pitch) || - (obj_priv->fence_reg != dev_priv->cfb_fence) || - (plane != dev_priv->cfb_plane)) - intel_disable_fbc(dev); - } - - /* Now try to turn it back on if possible */ - if (!intel_fbc_enabled(dev)) - intel_enable_fbc(crtc, 500); - + intel_enable_fbc(crtc, 500); return; out_disable: @@ -1393,7 +1433,9 @@ out_disable: } int -intel_pin_and_fence_fb_obj(struct drm_device *dev, struct drm_gem_object *obj) +intel_pin_and_fence_fb_obj(struct drm_device *dev, + struct drm_gem_object *obj, + bool pipelined) { struct drm_i915_gem_object *obj_priv = to_intel_bo(obj); u32 alignment; @@ -1403,7 +1445,7 @@ intel_pin_and_fence_fb_obj(struct drm_device *dev, struct drm_gem_object *obj) case I915_TILING_NONE: if (IS_BROADWATER(dev) || IS_CRESTLINE(dev)) alignment = 128 * 1024; - else if (IS_I965G(dev)) + else if (INTEL_INFO(dev)->gen >= 4) alignment = 4 * 1024; else alignment = 64 * 1024; @@ -1421,9 +1463,13 @@ intel_pin_and_fence_fb_obj(struct drm_device *dev, struct drm_gem_object *obj) } ret = i915_gem_object_pin(obj, alignment); - if (ret != 0) + if (ret) return ret; + ret = i915_gem_object_set_to_display_plane(obj, pipelined); + if (ret) + goto err_unpin; + /* Install a fence for tiled scan-out. Pre-i965 always needs a * fence, whereas 965+ only requires a fence if using * framebuffer compression. For simplicity, we always install @@ -1431,14 +1477,16 @@ intel_pin_and_fence_fb_obj(struct drm_device *dev, struct drm_gem_object *obj) */ if (obj_priv->fence_reg == I915_FENCE_REG_NONE && obj_priv->tiling_mode != I915_TILING_NONE) { - ret = i915_gem_object_get_fence_reg(obj); - if (ret != 0) { - i915_gem_object_unpin(obj); - return ret; - } + ret = i915_gem_object_get_fence_reg(obj, false); + if (ret) + goto err_unpin; } return 0; + +err_unpin: + i915_gem_object_unpin(obj); + return ret; } /* Assume fb object is pinned & idle & fenced and just update base pointers */ @@ -1454,12 +1502,8 @@ intel_pipe_set_base_atomic(struct drm_crtc *crtc, struct drm_framebuffer *fb, struct drm_gem_object *obj; int plane = intel_crtc->plane; unsigned long Start, Offset; - int dspbase = (plane == 0 ? DSPAADDR : DSPBADDR); - int dspsurf = (plane == 0 ? DSPASURF : DSPBSURF); - int dspstride = (plane == 0) ? DSPASTRIDE : DSPBSTRIDE; - int dsptileoff = (plane == 0 ? DSPATILEOFF : DSPBTILEOFF); - int dspcntr_reg = (plane == 0) ? DSPACNTR : DSPBCNTR; u32 dspcntr; + u32 reg; switch (plane) { case 0: @@ -1474,7 +1518,8 @@ intel_pipe_set_base_atomic(struct drm_crtc *crtc, struct drm_framebuffer *fb, obj = intel_fb->obj; obj_priv = to_intel_bo(obj); - dspcntr = I915_READ(dspcntr_reg); + reg = DSPCNTR(plane); + dspcntr = I915_READ(reg); /* Mask out pixel format bits in case we change it */ dspcntr &= ~DISPPLANE_PIXFORMAT_MASK; switch (fb->bits_per_pixel) { @@ -1495,7 +1540,7 @@ intel_pipe_set_base_atomic(struct drm_crtc *crtc, struct drm_framebuffer *fb, DRM_ERROR("Unknown color depth\n"); return -EINVAL; } - if (IS_I965G(dev)) { + if (INTEL_INFO(dev)->gen >= 4) { if (obj_priv->tiling_mode != I915_TILING_NONE) dspcntr |= DISPPLANE_TILED; else @@ -1506,28 +1551,24 @@ intel_pipe_set_base_atomic(struct drm_crtc *crtc, struct drm_framebuffer *fb, /* must disable */ dspcntr |= DISPPLANE_TRICKLE_FEED_DISABLE; - I915_WRITE(dspcntr_reg, dspcntr); + I915_WRITE(reg, dspcntr); Start = obj_priv->gtt_offset; Offset = y * fb->pitch + x * (fb->bits_per_pixel / 8); DRM_DEBUG_KMS("Writing base %08lX %08lX %d %d %d\n", Start, Offset, x, y, fb->pitch); - I915_WRITE(dspstride, fb->pitch); - if (IS_I965G(dev)) { - I915_WRITE(dspsurf, Start); - I915_WRITE(dsptileoff, (y << 16) | x); - I915_WRITE(dspbase, Offset); - } else { - I915_WRITE(dspbase, Start + Offset); - } - POSTING_READ(dspbase); + I915_WRITE(DSPSTRIDE(plane), fb->pitch); + if (INTEL_INFO(dev)->gen >= 4) { + I915_WRITE(DSPSURF(plane), Start); + I915_WRITE(DSPTILEOFF(plane), (y << 16) | x); + I915_WRITE(DSPADDR(plane), Offset); + } else + I915_WRITE(DSPADDR(plane), Start + Offset); + POSTING_READ(reg); - if (IS_I965G(dev) || plane == 0) - intel_update_fbc(crtc, &crtc->mode); - - intel_wait_for_vblank(dev, intel_crtc->pipe); - intel_increase_pllclock(crtc, true); + intel_update_fbc(dev); + intel_increase_pllclock(crtc); return 0; } @@ -1539,11 +1580,6 @@ intel_pipe_set_base(struct drm_crtc *crtc, int x, int y, struct drm_device *dev = crtc->dev; struct drm_i915_master_private *master_priv; struct intel_crtc *intel_crtc = to_intel_crtc(crtc); - struct intel_framebuffer *intel_fb; - struct drm_i915_gem_object *obj_priv; - struct drm_gem_object *obj; - int pipe = intel_crtc->pipe; - int plane = intel_crtc->plane; int ret; /* no fb bound */ @@ -1552,45 +1588,41 @@ intel_pipe_set_base(struct drm_crtc *crtc, int x, int y, return 0; } - switch (plane) { + switch (intel_crtc->plane) { case 0: case 1: break; default: - DRM_ERROR("Can't update plane %d in SAREA\n", plane); return -EINVAL; } - intel_fb = to_intel_framebuffer(crtc->fb); - obj = intel_fb->obj; - obj_priv = to_intel_bo(obj); - mutex_lock(&dev->struct_mutex); - ret = intel_pin_and_fence_fb_obj(dev, obj); + ret = intel_pin_and_fence_fb_obj(dev, + to_intel_framebuffer(crtc->fb)->obj, + false); if (ret != 0) { mutex_unlock(&dev->struct_mutex); return ret; } - ret = i915_gem_object_set_to_display_plane(obj); - if (ret != 0) { - i915_gem_object_unpin(obj); - mutex_unlock(&dev->struct_mutex); - return ret; + if (old_fb) { + struct drm_i915_private *dev_priv = dev->dev_private; + struct drm_gem_object *obj = to_intel_framebuffer(old_fb)->obj; + struct drm_i915_gem_object *obj_priv = to_intel_bo(obj); + + wait_event(dev_priv->pending_flip_queue, + atomic_read(&obj_priv->pending_flip) == 0); } ret = intel_pipe_set_base_atomic(crtc, crtc->fb, x, y); if (ret) { - i915_gem_object_unpin(obj); + i915_gem_object_unpin(to_intel_framebuffer(crtc->fb)->obj); mutex_unlock(&dev->struct_mutex); return ret; } - if (old_fb) { - intel_fb = to_intel_framebuffer(old_fb); - obj_priv = to_intel_bo(intel_fb->obj); - i915_gem_object_unpin(intel_fb->obj); - } + if (old_fb) + i915_gem_object_unpin(to_intel_framebuffer(old_fb)->obj); mutex_unlock(&dev->struct_mutex); @@ -1601,7 +1633,7 @@ intel_pipe_set_base(struct drm_crtc *crtc, int x, int y, if (!master_priv->sarea_priv) return 0; - if (pipe) { + if (intel_crtc->pipe) { master_priv->sarea_priv->pipeB_x = x; master_priv->sarea_priv->pipeB_y = y; } else { @@ -1612,7 +1644,7 @@ 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) +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; @@ -1645,6 +1677,7 @@ static void ironlake_set_pll_edp (struct drm_crtc *crtc, int clock) } I915_WRITE(DP_A, dpa_ctl); + POSTING_READ(DP_A); udelay(500); } @@ -1655,84 +1688,84 @@ static void ironlake_fdi_link_train(struct drm_crtc *crtc) struct drm_i915_private *dev_priv = dev->dev_private; struct intel_crtc *intel_crtc = to_intel_crtc(crtc); int pipe = intel_crtc->pipe; - int fdi_tx_reg = (pipe == 0) ? FDI_TXA_CTL : FDI_TXB_CTL; - int fdi_rx_reg = (pipe == 0) ? FDI_RXA_CTL : FDI_RXB_CTL; - int fdi_rx_iir_reg = (pipe == 0) ? FDI_RXA_IIR : FDI_RXB_IIR; - int fdi_rx_imr_reg = (pipe == 0) ? FDI_RXA_IMR : FDI_RXB_IMR; - u32 temp, tries = 0; + u32 reg, temp, tries; /* Train 1: umask FDI RX Interrupt symbol_lock and bit_lock bit for train result */ - temp = I915_READ(fdi_rx_imr_reg); + reg = FDI_RX_IMR(pipe); + temp = I915_READ(reg); temp &= ~FDI_RX_SYMBOL_LOCK; temp &= ~FDI_RX_BIT_LOCK; - I915_WRITE(fdi_rx_imr_reg, temp); - I915_READ(fdi_rx_imr_reg); + I915_WRITE(reg, temp); + I915_READ(reg); udelay(150); /* enable CPU FDI TX and PCH FDI RX */ - temp = I915_READ(fdi_tx_reg); - temp |= FDI_TX_ENABLE; + reg = FDI_TX_CTL(pipe); + temp = I915_READ(reg); temp &= ~(7 << 19); temp |= (intel_crtc->fdi_lanes - 1) << 19; temp &= ~FDI_LINK_TRAIN_NONE; temp |= FDI_LINK_TRAIN_PATTERN_1; - I915_WRITE(fdi_tx_reg, temp); - I915_READ(fdi_tx_reg); + I915_WRITE(reg, temp | FDI_TX_ENABLE); - temp = I915_READ(fdi_rx_reg); + reg = FDI_RX_CTL(pipe); + temp = I915_READ(reg); temp &= ~FDI_LINK_TRAIN_NONE; temp |= FDI_LINK_TRAIN_PATTERN_1; - I915_WRITE(fdi_rx_reg, temp | FDI_RX_ENABLE); - I915_READ(fdi_rx_reg); + I915_WRITE(reg, temp | FDI_RX_ENABLE); + + POSTING_READ(reg); udelay(150); + reg = FDI_RX_IIR(pipe); for (tries = 0; tries < 5; tries++) { - temp = I915_READ(fdi_rx_iir_reg); + temp = I915_READ(reg); DRM_DEBUG_KMS("FDI_RX_IIR 0x%x\n", temp); if ((temp & FDI_RX_BIT_LOCK)) { DRM_DEBUG_KMS("FDI train 1 done.\n"); - I915_WRITE(fdi_rx_iir_reg, - temp | FDI_RX_BIT_LOCK); + I915_WRITE(reg, temp | FDI_RX_BIT_LOCK); break; } } if (tries == 5) - DRM_DEBUG_KMS("FDI train 1 fail!\n"); + DRM_ERROR("FDI train 1 fail!\n"); /* Train 2 */ - temp = I915_READ(fdi_tx_reg); + reg = FDI_TX_CTL(pipe); + temp = I915_READ(reg); temp &= ~FDI_LINK_TRAIN_NONE; temp |= FDI_LINK_TRAIN_PATTERN_2; - I915_WRITE(fdi_tx_reg, temp); + I915_WRITE(reg, temp); - temp = I915_READ(fdi_rx_reg); + reg = FDI_RX_CTL(pipe); + temp = I915_READ(reg); temp &= ~FDI_LINK_TRAIN_NONE; temp |= FDI_LINK_TRAIN_PATTERN_2; - I915_WRITE(fdi_rx_reg, temp); - udelay(150); + I915_WRITE(reg, temp); - tries = 0; + POSTING_READ(reg); + udelay(150); + reg = FDI_RX_IIR(pipe); for (tries = 0; tries < 5; tries++) { - temp = I915_READ(fdi_rx_iir_reg); + temp = I915_READ(reg); DRM_DEBUG_KMS("FDI_RX_IIR 0x%x\n", temp); if (temp & FDI_RX_SYMBOL_LOCK) { - I915_WRITE(fdi_rx_iir_reg, - temp | FDI_RX_SYMBOL_LOCK); + I915_WRITE(reg, temp | FDI_RX_SYMBOL_LOCK); DRM_DEBUG_KMS("FDI train 2 done.\n"); break; } } if (tries == 5) - DRM_DEBUG_KMS("FDI train 2 fail!\n"); + DRM_ERROR("FDI train 2 fail!\n"); DRM_DEBUG_KMS("FDI train done\n"); } -static int snb_b_fdi_train_param [] = { +static const int const snb_b_fdi_train_param [] = { FDI_LINK_TRAIN_400MV_0DB_SNB_B, FDI_LINK_TRAIN_400MV_6DB_SNB_B, FDI_LINK_TRAIN_600MV_3_5DB_SNB_B, @@ -1746,24 +1779,22 @@ static void gen6_fdi_link_train(struct drm_crtc *crtc) struct drm_i915_private *dev_priv = dev->dev_private; struct intel_crtc *intel_crtc = to_intel_crtc(crtc); int pipe = intel_crtc->pipe; - int fdi_tx_reg = (pipe == 0) ? FDI_TXA_CTL : FDI_TXB_CTL; - int fdi_rx_reg = (pipe == 0) ? FDI_RXA_CTL : FDI_RXB_CTL; - int fdi_rx_iir_reg = (pipe == 0) ? FDI_RXA_IIR : FDI_RXB_IIR; - int fdi_rx_imr_reg = (pipe == 0) ? FDI_RXA_IMR : FDI_RXB_IMR; - u32 temp, i; + u32 reg, temp, i; /* Train 1: umask FDI RX Interrupt symbol_lock and bit_lock bit for train result */ - temp = I915_READ(fdi_rx_imr_reg); + reg = FDI_RX_IMR(pipe); + temp = I915_READ(reg); temp &= ~FDI_RX_SYMBOL_LOCK; temp &= ~FDI_RX_BIT_LOCK; - I915_WRITE(fdi_rx_imr_reg, temp); - I915_READ(fdi_rx_imr_reg); + I915_WRITE(reg, temp); + + POSTING_READ(reg); udelay(150); /* enable CPU FDI TX and PCH FDI RX */ - temp = I915_READ(fdi_tx_reg); - temp |= FDI_TX_ENABLE; + reg = FDI_TX_CTL(pipe); + temp = I915_READ(reg); temp &= ~(7 << 19); temp |= (intel_crtc->fdi_lanes - 1) << 19; temp &= ~FDI_LINK_TRAIN_NONE; @@ -1771,10 +1802,10 @@ static void gen6_fdi_link_train(struct drm_crtc *crtc) temp &= ~FDI_LINK_TRAIN_VOL_EMP_MASK; /* SNB-B */ temp |= FDI_LINK_TRAIN_400MV_0DB_SNB_B; - I915_WRITE(fdi_tx_reg, temp); - I915_READ(fdi_tx_reg); + I915_WRITE(reg, temp | FDI_TX_ENABLE); - temp = I915_READ(fdi_rx_reg); + reg = FDI_RX_CTL(pipe); + temp = I915_READ(reg); if (HAS_PCH_CPT(dev)) { temp &= ~FDI_LINK_TRAIN_PATTERN_MASK_CPT; temp |= FDI_LINK_TRAIN_PATTERN_1_CPT; @@ -1782,32 +1813,37 @@ static void gen6_fdi_link_train(struct drm_crtc *crtc) temp &= ~FDI_LINK_TRAIN_NONE; temp |= FDI_LINK_TRAIN_PATTERN_1; } - I915_WRITE(fdi_rx_reg, temp | FDI_RX_ENABLE); - I915_READ(fdi_rx_reg); + I915_WRITE(reg, temp | FDI_RX_ENABLE); + + POSTING_READ(reg); udelay(150); for (i = 0; i < 4; i++ ) { - temp = I915_READ(fdi_tx_reg); + reg = FDI_TX_CTL(pipe); + temp = I915_READ(reg); temp &= ~FDI_LINK_TRAIN_VOL_EMP_MASK; temp |= snb_b_fdi_train_param[i]; - I915_WRITE(fdi_tx_reg, temp); + I915_WRITE(reg, temp); + + POSTING_READ(reg); udelay(500); - temp = I915_READ(fdi_rx_iir_reg); + reg = FDI_RX_IIR(pipe); + temp = I915_READ(reg); DRM_DEBUG_KMS("FDI_RX_IIR 0x%x\n", temp); if (temp & FDI_RX_BIT_LOCK) { - I915_WRITE(fdi_rx_iir_reg, - temp | FDI_RX_BIT_LOCK); + I915_WRITE(reg, temp | FDI_RX_BIT_LOCK); DRM_DEBUG_KMS("FDI train 1 done.\n"); break; } } if (i == 4) - DRM_DEBUG_KMS("FDI train 1 fail!\n"); + DRM_ERROR("FDI train 1 fail!\n"); /* Train 2 */ - temp = I915_READ(fdi_tx_reg); + reg = FDI_TX_CTL(pipe); + temp = I915_READ(reg); temp &= ~FDI_LINK_TRAIN_NONE; temp |= FDI_LINK_TRAIN_PATTERN_2; if (IS_GEN6(dev)) { @@ -1815,9 +1851,10 @@ static void gen6_fdi_link_train(struct drm_crtc *crtc) /* SNB-B */ temp |= FDI_LINK_TRAIN_400MV_0DB_SNB_B; } - I915_WRITE(fdi_tx_reg, temp); + I915_WRITE(reg, temp); - temp = I915_READ(fdi_rx_reg); + reg = FDI_RX_CTL(pipe); + temp = I915_READ(reg); if (HAS_PCH_CPT(dev)) { temp &= ~FDI_LINK_TRAIN_PATTERN_MASK_CPT; temp |= FDI_LINK_TRAIN_PATTERN_2_CPT; @@ -1825,535 +1862,611 @@ static void gen6_fdi_link_train(struct drm_crtc *crtc) temp &= ~FDI_LINK_TRAIN_NONE; temp |= FDI_LINK_TRAIN_PATTERN_2; } - I915_WRITE(fdi_rx_reg, temp); + I915_WRITE(reg, temp); + + POSTING_READ(reg); udelay(150); for (i = 0; i < 4; i++ ) { - temp = I915_READ(fdi_tx_reg); + reg = FDI_TX_CTL(pipe); + temp = I915_READ(reg); temp &= ~FDI_LINK_TRAIN_VOL_EMP_MASK; temp |= snb_b_fdi_train_param[i]; - I915_WRITE(fdi_tx_reg, temp); + I915_WRITE(reg, temp); + + POSTING_READ(reg); udelay(500); - temp = I915_READ(fdi_rx_iir_reg); + reg = FDI_RX_IIR(pipe); + temp = I915_READ(reg); DRM_DEBUG_KMS("FDI_RX_IIR 0x%x\n", temp); if (temp & FDI_RX_SYMBOL_LOCK) { - I915_WRITE(fdi_rx_iir_reg, - temp | FDI_RX_SYMBOL_LOCK); + I915_WRITE(reg, temp | FDI_RX_SYMBOL_LOCK); DRM_DEBUG_KMS("FDI train 2 done.\n"); break; } } if (i == 4) - DRM_DEBUG_KMS("FDI train 2 fail!\n"); + DRM_ERROR("FDI train 2 fail!\n"); DRM_DEBUG_KMS("FDI train done.\n"); } -static void ironlake_crtc_dpms(struct drm_crtc *crtc, int mode) +static void ironlake_fdi_enable(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); int pipe = intel_crtc->pipe; - int plane = intel_crtc->plane; - int pch_dpll_reg = (pipe == 0) ? PCH_DPLL_A : PCH_DPLL_B; - int pipeconf_reg = (pipe == 0) ? PIPEACONF : PIPEBCONF; - int dspcntr_reg = (plane == 0) ? DSPACNTR : DSPBCNTR; - int dspbase_reg = (plane == 0) ? DSPAADDR : DSPBADDR; - int fdi_tx_reg = (pipe == 0) ? FDI_TXA_CTL : FDI_TXB_CTL; - int fdi_rx_reg = (pipe == 0) ? FDI_RXA_CTL : FDI_RXB_CTL; - int transconf_reg = (pipe == 0) ? TRANSACONF : TRANSBCONF; - int cpu_htot_reg = (pipe == 0) ? HTOTAL_A : HTOTAL_B; - int cpu_hblank_reg = (pipe == 0) ? HBLANK_A : HBLANK_B; - int cpu_hsync_reg = (pipe == 0) ? HSYNC_A : HSYNC_B; - int cpu_vtot_reg = (pipe == 0) ? VTOTAL_A : VTOTAL_B; - int cpu_vblank_reg = (pipe == 0) ? VBLANK_A : VBLANK_B; - int cpu_vsync_reg = (pipe == 0) ? VSYNC_A : VSYNC_B; - int trans_htot_reg = (pipe == 0) ? TRANS_HTOTAL_A : TRANS_HTOTAL_B; - int trans_hblank_reg = (pipe == 0) ? TRANS_HBLANK_A : TRANS_HBLANK_B; - int trans_hsync_reg = (pipe == 0) ? TRANS_HSYNC_A : TRANS_HSYNC_B; - int trans_vtot_reg = (pipe == 0) ? TRANS_VTOTAL_A : TRANS_VTOTAL_B; - int trans_vblank_reg = (pipe == 0) ? TRANS_VBLANK_A : TRANS_VBLANK_B; - int trans_vsync_reg = (pipe == 0) ? TRANS_VSYNC_A : TRANS_VSYNC_B; - int trans_dpll_sel = (pipe == 0) ? 0 : 1; - u32 temp; - u32 pipe_bpc; - - temp = I915_READ(pipeconf_reg); - pipe_bpc = temp & PIPE_BPC_MASK; + u32 reg, temp; - /* XXX: When our outputs are all unaware of DPMS modes other than off - * and on, we should map those modes to DRM_MODE_DPMS_OFF in the CRTC. - */ - switch (mode) { - case DRM_MODE_DPMS_ON: - case DRM_MODE_DPMS_STANDBY: - case DRM_MODE_DPMS_SUSPEND: - DRM_DEBUG_KMS("crtc %d/%d dpms on\n", pipe, plane); + /* Write the TU size bits so error detection works */ + I915_WRITE(FDI_RX_TUSIZE1(pipe), + I915_READ(PIPE_DATA_M1(pipe)) & TU_SIZE_MASK); - if (intel_pipe_has_type(crtc, INTEL_OUTPUT_LVDS)) { - temp = I915_READ(PCH_LVDS); - if ((temp & LVDS_PORT_EN) == 0) { - I915_WRITE(PCH_LVDS, temp | LVDS_PORT_EN); - POSTING_READ(PCH_LVDS); - } - } + /* enable PCH FDI RX PLL, wait warmup plus DMI latency */ + reg = FDI_RX_CTL(pipe); + 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; + I915_WRITE(reg, temp | FDI_RX_PLL_ENABLE); - if (!HAS_eDP) { + POSTING_READ(reg); + udelay(200); - /* enable PCH FDI RX PLL, wait warmup plus DMI latency */ - temp = I915_READ(fdi_rx_reg); - /* - * make the BPC in FDI Rx be consistent with that in - * pipeconf reg. - */ - temp &= ~(0x7 << 16); - temp |= (pipe_bpc << 11); - temp &= ~(7 << 19); - temp |= (intel_crtc->fdi_lanes - 1) << 19; - I915_WRITE(fdi_rx_reg, temp | FDI_RX_PLL_ENABLE); - I915_READ(fdi_rx_reg); - udelay(200); + /* Switch from Rawclk to PCDclk */ + temp = I915_READ(reg); + I915_WRITE(reg, temp | FDI_PCDCLK); - /* Switch from Rawclk to PCDclk */ - temp = I915_READ(fdi_rx_reg); - I915_WRITE(fdi_rx_reg, temp | FDI_SEL_PCDCLK); - I915_READ(fdi_rx_reg); - udelay(200); + POSTING_READ(reg); + udelay(200); - /* Enable CPU FDI TX PLL, always on for Ironlake */ - temp = I915_READ(fdi_tx_reg); - if ((temp & FDI_TX_PLL_ENABLE) == 0) { - I915_WRITE(fdi_tx_reg, temp | FDI_TX_PLL_ENABLE); - I915_READ(fdi_tx_reg); - udelay(100); - } - } + /* 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 panel fitting for LVDS */ - if (dev_priv->pch_pf_size && - (intel_pipe_has_type(crtc, INTEL_OUTPUT_LVDS) - || HAS_eDP || intel_pch_has_edp(crtc))) { - /* Force use of hard-coded filter coefficients - * as some pre-programmed values are broken, - * e.g. x201. - */ - I915_WRITE(pipe ? PFB_CTL_1 : PFA_CTL_1, - PF_ENABLE | PF_FILTER_MED_3x3); - I915_WRITE(pipe ? PFB_WIN_POS : PFA_WIN_POS, - dev_priv->pch_pf_pos); - I915_WRITE(pipe ? PFB_WIN_SZ : PFA_WIN_SZ, - dev_priv->pch_pf_size); - } + POSTING_READ(reg); + udelay(100); + } +} - /* Enable CPU pipe */ - temp = I915_READ(pipeconf_reg); - if ((temp & PIPEACONF_ENABLE) == 0) { - I915_WRITE(pipeconf_reg, temp | PIPEACONF_ENABLE); - I915_READ(pipeconf_reg); - udelay(100); - } +static void intel_flush_display_plane(struct drm_device *dev, + int plane) +{ + struct drm_i915_private *dev_priv = dev->dev_private; + u32 reg = DSPADDR(plane); + I915_WRITE(reg, I915_READ(reg)); +} - /* configure and enable CPU plane */ - temp = I915_READ(dspcntr_reg); - if ((temp & DISPLAY_PLANE_ENABLE) == 0) { - I915_WRITE(dspcntr_reg, temp | DISPLAY_PLANE_ENABLE); - /* Flush the plane changes */ - I915_WRITE(dspbase_reg, I915_READ(dspbase_reg)); - } +/* + * When we disable a pipe, we need to clear any pending scanline wait events + * to avoid hanging the ring, which we assume we are waiting on. + */ +static void intel_clear_scanline_wait(struct drm_device *dev) +{ + struct drm_i915_private *dev_priv = dev->dev_private; + u32 tmp; - if (!HAS_eDP) { - /* For PCH output, training FDI link */ - if (IS_GEN6(dev)) - gen6_fdi_link_train(crtc); - else - ironlake_fdi_link_train(crtc); + if (IS_GEN2(dev)) + /* Can't break the hang on i8xx */ + return; - /* enable PCH DPLL */ - temp = I915_READ(pch_dpll_reg); - if ((temp & DPLL_VCO_ENABLE) == 0) { - I915_WRITE(pch_dpll_reg, temp | DPLL_VCO_ENABLE); - I915_READ(pch_dpll_reg); - } - udelay(200); + tmp = I915_READ(PRB0_CTL); + if (tmp & RING_WAIT) { + I915_WRITE(PRB0_CTL, tmp); + POSTING_READ(PRB0_CTL); + } +} - if (HAS_PCH_CPT(dev)) { - /* Be sure PCH DPLL SEL is set */ - temp = I915_READ(PCH_DPLL_SEL); - if (trans_dpll_sel == 0 && - (temp & TRANSA_DPLL_ENABLE) == 0) - temp |= (TRANSA_DPLL_ENABLE | TRANSA_DPLLA_SEL); - else if (trans_dpll_sel == 1 && - (temp & TRANSB_DPLL_ENABLE) == 0) - temp |= (TRANSB_DPLL_ENABLE | TRANSB_DPLLB_SEL); - I915_WRITE(PCH_DPLL_SEL, temp); - I915_READ(PCH_DPLL_SEL); - } +static void intel_crtc_wait_for_pending_flips(struct drm_crtc *crtc) +{ + struct drm_i915_gem_object *obj_priv; + struct drm_i915_private *dev_priv; - /* set transcoder timing */ - I915_WRITE(trans_htot_reg, I915_READ(cpu_htot_reg)); - I915_WRITE(trans_hblank_reg, I915_READ(cpu_hblank_reg)); - I915_WRITE(trans_hsync_reg, I915_READ(cpu_hsync_reg)); - - I915_WRITE(trans_vtot_reg, I915_READ(cpu_vtot_reg)); - I915_WRITE(trans_vblank_reg, I915_READ(cpu_vblank_reg)); - I915_WRITE(trans_vsync_reg, I915_READ(cpu_vsync_reg)); - - /* enable normal train */ - temp = I915_READ(fdi_tx_reg); - temp &= ~FDI_LINK_TRAIN_NONE; - I915_WRITE(fdi_tx_reg, temp | FDI_LINK_TRAIN_NONE | - FDI_TX_ENHANCE_FRAME_ENABLE); - I915_READ(fdi_tx_reg); - - temp = I915_READ(fdi_rx_reg); - if (HAS_PCH_CPT(dev)) { - temp &= ~FDI_LINK_TRAIN_PATTERN_MASK_CPT; - temp |= FDI_LINK_TRAIN_NORMAL_CPT; - } else { - temp &= ~FDI_LINK_TRAIN_NONE; - temp |= FDI_LINK_TRAIN_NONE; - } - I915_WRITE(fdi_rx_reg, temp | FDI_RX_ENHANCE_FRAME_ENABLE); - I915_READ(fdi_rx_reg); + if (crtc->fb == NULL) + return; - /* wait one idle pattern time */ - udelay(100); + obj_priv = to_intel_bo(to_intel_framebuffer(crtc->fb)->obj); + dev_priv = crtc->dev->dev_private; + wait_event(dev_priv->pending_flip_queue, + atomic_read(&obj_priv->pending_flip) == 0); +} - /* For PCH DP, enable TRANS_DP_CTL */ - if (HAS_PCH_CPT(dev) && - intel_pipe_has_type(crtc, INTEL_OUTPUT_DISPLAYPORT)) { - int trans_dp_ctl = (pipe == 0) ? TRANS_DP_CTL_A : TRANS_DP_CTL_B; - int reg; - - reg = I915_READ(trans_dp_ctl); - reg &= ~(TRANS_DP_PORT_SEL_MASK | - TRANS_DP_SYNC_MASK); - reg |= (TRANS_DP_OUTPUT_ENABLE | - TRANS_DP_ENH_FRAMING); - - if (crtc->mode.flags & DRM_MODE_FLAG_PHSYNC) - reg |= TRANS_DP_HSYNC_ACTIVE_HIGH; - if (crtc->mode.flags & DRM_MODE_FLAG_PVSYNC) - reg |= TRANS_DP_VSYNC_ACTIVE_HIGH; - - switch (intel_trans_dp_port_sel(crtc)) { - case PCH_DP_B: - reg |= TRANS_DP_PORT_SEL_B; - break; - case PCH_DP_C: - reg |= TRANS_DP_PORT_SEL_C; - break; - case PCH_DP_D: - reg |= TRANS_DP_PORT_SEL_D; - break; - default: - DRM_DEBUG_KMS("Wrong PCH DP port return. Guess port B\n"); - reg |= TRANS_DP_PORT_SEL_B; - break; - } +static void ironlake_crtc_enable(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); + int pipe = intel_crtc->pipe; + int plane = intel_crtc->plane; + u32 reg, temp; - I915_WRITE(trans_dp_ctl, reg); - POSTING_READ(trans_dp_ctl); - } + if (intel_crtc->active) + return; - /* enable PCH transcoder */ - temp = I915_READ(transconf_reg); - /* - * make the BPC in transcoder be consistent with - * that in pipeconf reg. - */ - temp &= ~PIPE_BPC_MASK; - temp |= pipe_bpc; - I915_WRITE(transconf_reg, temp | TRANS_ENABLE); - I915_READ(transconf_reg); + intel_crtc->active = true; + intel_update_watermarks(dev); - if (wait_for(I915_READ(transconf_reg) & TRANS_STATE_ENABLE, 100, 1)) - DRM_ERROR("failed to enable transcoder\n"); - } + if (intel_pipe_has_type(crtc, INTEL_OUTPUT_LVDS)) { + temp = I915_READ(PCH_LVDS); + if ((temp & LVDS_PORT_EN) == 0) + I915_WRITE(PCH_LVDS, temp | LVDS_PORT_EN); + } - intel_crtc_load_lut(crtc); + ironlake_fdi_enable(crtc); - intel_update_fbc(crtc, &crtc->mode); - break; + /* Enable panel fitting for LVDS */ + if (dev_priv->pch_pf_size && + (intel_pipe_has_type(crtc, INTEL_OUTPUT_LVDS) + || HAS_eDP || intel_pch_has_edp(crtc))) { + /* Force use of hard-coded filter coefficients + * as some pre-programmed values are broken, + * e.g. x201. + */ + I915_WRITE(pipe ? PFB_CTL_1 : PFA_CTL_1, + PF_ENABLE | PF_FILTER_MED_3x3); + I915_WRITE(pipe ? PFB_WIN_POS : PFA_WIN_POS, + dev_priv->pch_pf_pos); + I915_WRITE(pipe ? PFB_WIN_SZ : PFA_WIN_SZ, + dev_priv->pch_pf_size); + } + + /* Enable CPU pipe */ + reg = PIPECONF(pipe); + temp = I915_READ(reg); + if ((temp & PIPECONF_ENABLE) == 0) { + I915_WRITE(reg, temp | PIPECONF_ENABLE); + POSTING_READ(reg); + udelay(100); + } - case DRM_MODE_DPMS_OFF: - DRM_DEBUG_KMS("crtc %d/%d dpms off\n", pipe, plane); + /* configure and enable CPU plane */ + reg = DSPCNTR(plane); + temp = I915_READ(reg); + if ((temp & DISPLAY_PLANE_ENABLE) == 0) { + I915_WRITE(reg, temp | DISPLAY_PLANE_ENABLE); + intel_flush_display_plane(dev, plane); + } + + /* For PCH output, training FDI link */ + if (IS_GEN6(dev)) + gen6_fdi_link_train(crtc); + else + ironlake_fdi_link_train(crtc); + + /* enable PCH DPLL */ + reg = PCH_DPLL(pipe); + temp = I915_READ(reg); + if ((temp & DPLL_VCO_ENABLE) == 0) { + I915_WRITE(reg, temp | DPLL_VCO_ENABLE); + POSTING_READ(reg); + udelay(200); + } + + if (HAS_PCH_CPT(dev)) { + /* Be sure PCH DPLL SEL is set */ + temp = I915_READ(PCH_DPLL_SEL); + if (pipe == 0 && (temp & TRANSA_DPLL_ENABLE) == 0) + temp |= (TRANSA_DPLL_ENABLE | TRANSA_DPLLA_SEL); + else if (pipe == 1 && (temp & TRANSB_DPLL_ENABLE) == 0) + temp |= (TRANSB_DPLL_ENABLE | TRANSB_DPLLB_SEL); + I915_WRITE(PCH_DPLL_SEL, temp); + } + + /* set transcoder timing */ + I915_WRITE(TRANS_HTOTAL(pipe), I915_READ(HTOTAL(pipe))); + I915_WRITE(TRANS_HBLANK(pipe), I915_READ(HBLANK(pipe))); + I915_WRITE(TRANS_HSYNC(pipe), I915_READ(HSYNC(pipe))); + + I915_WRITE(TRANS_VTOTAL(pipe), I915_READ(VTOTAL(pipe))); + I915_WRITE(TRANS_VBLANK(pipe), I915_READ(VBLANK(pipe))); + I915_WRITE(TRANS_VSYNC(pipe), I915_READ(VSYNC(pipe))); + + /* enable normal train */ + reg = FDI_TX_CTL(pipe); + temp = I915_READ(reg); + temp &= ~FDI_LINK_TRAIN_NONE; + temp |= FDI_LINK_TRAIN_NONE | FDI_TX_ENHANCE_FRAME_ENABLE; + I915_WRITE(reg, temp); - drm_vblank_off(dev, pipe); - /* Disable display plane */ - temp = I915_READ(dspcntr_reg); - if ((temp & DISPLAY_PLANE_ENABLE) != 0) { - I915_WRITE(dspcntr_reg, temp & ~DISPLAY_PLANE_ENABLE); - /* Flush the plane changes */ - I915_WRITE(dspbase_reg, I915_READ(dspbase_reg)); - I915_READ(dspbase_reg); + reg = FDI_RX_CTL(pipe); + temp = I915_READ(reg); + if (HAS_PCH_CPT(dev)) { + temp &= ~FDI_LINK_TRAIN_PATTERN_MASK_CPT; + temp |= FDI_LINK_TRAIN_NORMAL_CPT; + } else { + temp &= ~FDI_LINK_TRAIN_NONE; + temp |= FDI_LINK_TRAIN_NONE; + } + I915_WRITE(reg, temp | FDI_RX_ENHANCE_FRAME_ENABLE); + + /* wait one idle pattern time */ + POSTING_READ(reg); + udelay(100); + + /* For PCH DP, enable TRANS_DP_CTL */ + if (HAS_PCH_CPT(dev) && + intel_pipe_has_type(crtc, INTEL_OUTPUT_DISPLAYPORT)) { + reg = TRANS_DP_CTL(pipe); + temp = I915_READ(reg); + temp &= ~(TRANS_DP_PORT_SEL_MASK | + TRANS_DP_SYNC_MASK); + temp |= (TRANS_DP_OUTPUT_ENABLE | + TRANS_DP_ENH_FRAMING); + + if (crtc->mode.flags & DRM_MODE_FLAG_PHSYNC) + temp |= TRANS_DP_HSYNC_ACTIVE_HIGH; + if (crtc->mode.flags & DRM_MODE_FLAG_PVSYNC) + temp |= TRANS_DP_VSYNC_ACTIVE_HIGH; + + switch (intel_trans_dp_port_sel(crtc)) { + case PCH_DP_B: + temp |= TRANS_DP_PORT_SEL_B; + break; + case PCH_DP_C: + temp |= TRANS_DP_PORT_SEL_C; + break; + case PCH_DP_D: + temp |= TRANS_DP_PORT_SEL_D; + break; + default: + DRM_DEBUG_KMS("Wrong PCH DP port return. Guess port B\n"); + temp |= TRANS_DP_PORT_SEL_B; + break; } - if (dev_priv->cfb_plane == plane && - dev_priv->display.disable_fbc) - dev_priv->display.disable_fbc(dev); + I915_WRITE(reg, temp); + } - /* disable cpu pipe, disable after all planes disabled */ - temp = I915_READ(pipeconf_reg); - if ((temp & PIPEACONF_ENABLE) != 0) { - I915_WRITE(pipeconf_reg, temp & ~PIPEACONF_ENABLE); + /* enable PCH transcoder */ + reg = TRANSCONF(pipe); + temp = I915_READ(reg); + /* + * make the BPC in transcoder be consistent with + * that in pipeconf reg. + */ + temp &= ~PIPE_BPC_MASK; + temp |= I915_READ(PIPECONF(pipe)) & PIPE_BPC_MASK; + I915_WRITE(reg, temp | TRANS_ENABLE); + if (wait_for(I915_READ(reg) & TRANS_STATE_ENABLE, 100)) + DRM_ERROR("failed to enable transcoder\n"); - /* wait for cpu pipe off, pipe state */ - if (wait_for((I915_READ(pipeconf_reg) & I965_PIPECONF_ACTIVE) == 0, 50, 1)) - DRM_ERROR("failed to turn off cpu pipe\n"); - } else - DRM_DEBUG_KMS("crtc %d is disabled\n", pipe); + intel_crtc_load_lut(crtc); + intel_update_fbc(dev); + intel_crtc_update_cursor(crtc, true); +} - udelay(100); +static void ironlake_crtc_disable(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); + int pipe = intel_crtc->pipe; + int plane = intel_crtc->plane; + u32 reg, temp; - /* Disable PF */ - I915_WRITE(pipe ? PFB_CTL_1 : PFA_CTL_1, 0); - I915_WRITE(pipe ? PFB_WIN_SZ : PFA_WIN_SZ, 0); + if (!intel_crtc->active) + return; - /* disable CPU FDI tx and PCH FDI rx */ - temp = I915_READ(fdi_tx_reg); - I915_WRITE(fdi_tx_reg, temp & ~FDI_TX_ENABLE); - I915_READ(fdi_tx_reg); + intel_crtc_wait_for_pending_flips(crtc); + drm_vblank_off(dev, pipe); + intel_crtc_update_cursor(crtc, false); - temp = I915_READ(fdi_rx_reg); - /* BPC in FDI rx is consistent with that in pipeconf */ - temp &= ~(0x07 << 16); - temp |= (pipe_bpc << 11); - I915_WRITE(fdi_rx_reg, temp & ~FDI_RX_ENABLE); - I915_READ(fdi_rx_reg); + /* Disable display plane */ + reg = DSPCNTR(plane); + temp = I915_READ(reg); + if (temp & DISPLAY_PLANE_ENABLE) { + I915_WRITE(reg, temp & ~DISPLAY_PLANE_ENABLE); + intel_flush_display_plane(dev, plane); + } - udelay(100); + if (dev_priv->cfb_plane == plane && + dev_priv->display.disable_fbc) + dev_priv->display.disable_fbc(dev); - /* still set train pattern 1 */ - temp = I915_READ(fdi_tx_reg); + /* disable cpu pipe, disable after all planes disabled */ + reg = PIPECONF(pipe); + temp = I915_READ(reg); + if (temp & PIPECONF_ENABLE) { + I915_WRITE(reg, temp & ~PIPECONF_ENABLE); + /* wait for cpu pipe off, pipe state */ + if (wait_for((I915_READ(reg) & I965_PIPECONF_ACTIVE) == 0, 50)) + DRM_ERROR("failed to turn off cpu pipe\n"); + } + + /* Disable PF */ + I915_WRITE(pipe ? PFB_CTL_1 : PFA_CTL_1, 0); + I915_WRITE(pipe ? PFB_WIN_SZ : PFA_WIN_SZ, 0); + + /* disable CPU FDI tx and PCH FDI rx */ + reg = FDI_TX_CTL(pipe); + temp = I915_READ(reg); + I915_WRITE(reg, temp & ~FDI_TX_ENABLE); + POSTING_READ(reg); + + reg = FDI_RX_CTL(pipe); + temp = I915_READ(reg); + temp &= ~(0x7 << 16); + temp |= (I915_READ(PIPECONF(pipe)) & PIPE_BPC_MASK) << 11; + I915_WRITE(reg, temp & ~FDI_RX_ENABLE); + + POSTING_READ(reg); + udelay(100); + + /* still set train pattern 1 */ + reg = FDI_TX_CTL(pipe); + temp = I915_READ(reg); + temp &= ~FDI_LINK_TRAIN_NONE; + temp |= FDI_LINK_TRAIN_PATTERN_1; + I915_WRITE(reg, temp); + + reg = FDI_RX_CTL(pipe); + temp = I915_READ(reg); + if (HAS_PCH_CPT(dev)) { + temp &= ~FDI_LINK_TRAIN_PATTERN_MASK_CPT; + temp |= FDI_LINK_TRAIN_PATTERN_1_CPT; + } else { temp &= ~FDI_LINK_TRAIN_NONE; temp |= FDI_LINK_TRAIN_PATTERN_1; - I915_WRITE(fdi_tx_reg, temp); - POSTING_READ(fdi_tx_reg); - - temp = I915_READ(fdi_rx_reg); - if (HAS_PCH_CPT(dev)) { - temp &= ~FDI_LINK_TRAIN_PATTERN_MASK_CPT; - temp |= FDI_LINK_TRAIN_PATTERN_1_CPT; - } else { - temp &= ~FDI_LINK_TRAIN_NONE; - temp |= FDI_LINK_TRAIN_PATTERN_1; - } - I915_WRITE(fdi_rx_reg, temp); - POSTING_READ(fdi_rx_reg); + } + /* BPC in FDI rx is consistent with that in PIPECONF */ + temp &= ~(0x07 << 16); + temp |= (I915_READ(PIPECONF(pipe)) & PIPE_BPC_MASK) << 11; + I915_WRITE(reg, temp); - udelay(100); + POSTING_READ(reg); + udelay(100); - if (intel_pipe_has_type(crtc, INTEL_OUTPUT_LVDS)) { - temp = I915_READ(PCH_LVDS); + if (intel_pipe_has_type(crtc, INTEL_OUTPUT_LVDS)) { + temp = I915_READ(PCH_LVDS); + if (temp & LVDS_PORT_EN) { I915_WRITE(PCH_LVDS, temp & ~LVDS_PORT_EN); - I915_READ(PCH_LVDS); + POSTING_READ(PCH_LVDS); udelay(100); } + } - /* disable PCH transcoder */ - temp = I915_READ(transconf_reg); - if ((temp & TRANS_ENABLE) != 0) { - I915_WRITE(transconf_reg, temp & ~TRANS_ENABLE); + /* disable PCH transcoder */ + reg = TRANSCONF(plane); + temp = I915_READ(reg); + if (temp & TRANS_ENABLE) { + I915_WRITE(reg, temp & ~TRANS_ENABLE); + /* wait for PCH transcoder off, transcoder state */ + if (wait_for((I915_READ(reg) & TRANS_STATE_ENABLE) == 0, 50)) + DRM_ERROR("failed to disable transcoder\n"); + } - /* wait for PCH transcoder off, transcoder state */ - if (wait_for((I915_READ(transconf_reg) & TRANS_STATE_ENABLE) == 0, 50, 1)) - DRM_ERROR("failed to disable transcoder\n"); - } + if (HAS_PCH_CPT(dev)) { + /* disable TRANS_DP_CTL */ + reg = TRANS_DP_CTL(pipe); + temp = I915_READ(reg); + temp &= ~(TRANS_DP_OUTPUT_ENABLE | TRANS_DP_PORT_SEL_MASK); + I915_WRITE(reg, temp); - temp = I915_READ(transconf_reg); - /* BPC in transcoder is consistent with that in pipeconf */ - temp &= ~PIPE_BPC_MASK; - temp |= pipe_bpc; - I915_WRITE(transconf_reg, temp); - I915_READ(transconf_reg); - udelay(100); + /* disable DPLL_SEL */ + temp = I915_READ(PCH_DPLL_SEL); + if (pipe == 0) + temp &= ~(TRANSA_DPLL_ENABLE | TRANSA_DPLLB_SEL); + else + temp &= ~(TRANSB_DPLL_ENABLE | TRANSB_DPLLB_SEL); + I915_WRITE(PCH_DPLL_SEL, temp); + } - if (HAS_PCH_CPT(dev)) { - /* disable TRANS_DP_CTL */ - int trans_dp_ctl = (pipe == 0) ? TRANS_DP_CTL_A : TRANS_DP_CTL_B; - int reg; + /* disable PCH DPLL */ + reg = PCH_DPLL(pipe); + temp = I915_READ(reg); + I915_WRITE(reg, temp & ~DPLL_VCO_ENABLE); - reg = I915_READ(trans_dp_ctl); - reg &= ~(TRANS_DP_OUTPUT_ENABLE | TRANS_DP_PORT_SEL_MASK); - I915_WRITE(trans_dp_ctl, reg); - POSTING_READ(trans_dp_ctl); + /* Switch from PCDclk to Rawclk */ + reg = FDI_RX_CTL(pipe); + temp = I915_READ(reg); + I915_WRITE(reg, temp & ~FDI_PCDCLK); - /* disable DPLL_SEL */ - temp = I915_READ(PCH_DPLL_SEL); - if (trans_dpll_sel == 0) - temp &= ~(TRANSA_DPLL_ENABLE | TRANSA_DPLLB_SEL); - else - temp &= ~(TRANSB_DPLL_ENABLE | TRANSB_DPLLB_SEL); - I915_WRITE(PCH_DPLL_SEL, temp); - I915_READ(PCH_DPLL_SEL); + /* Disable CPU FDI TX PLL */ + reg = FDI_TX_CTL(pipe); + temp = I915_READ(reg); + I915_WRITE(reg, temp & ~FDI_TX_PLL_ENABLE); - } + POSTING_READ(reg); + udelay(100); - /* disable PCH DPLL */ - temp = I915_READ(pch_dpll_reg); - I915_WRITE(pch_dpll_reg, temp & ~DPLL_VCO_ENABLE); - I915_READ(pch_dpll_reg); - - /* Switch from PCDclk to Rawclk */ - temp = I915_READ(fdi_rx_reg); - temp &= ~FDI_SEL_PCDCLK; - I915_WRITE(fdi_rx_reg, temp); - I915_READ(fdi_rx_reg); - - /* Disable CPU FDI TX PLL */ - temp = I915_READ(fdi_tx_reg); - I915_WRITE(fdi_tx_reg, temp & ~FDI_TX_PLL_ENABLE); - I915_READ(fdi_tx_reg); - udelay(100); + reg = FDI_RX_CTL(pipe); + temp = I915_READ(reg); + I915_WRITE(reg, temp & ~FDI_RX_PLL_ENABLE); - temp = I915_READ(fdi_rx_reg); - temp &= ~FDI_RX_PLL_ENABLE; - I915_WRITE(fdi_rx_reg, temp); - I915_READ(fdi_rx_reg); + /* Wait for the clocks to turn off. */ + POSTING_READ(reg); + udelay(100); - /* Wait for the clocks to turn off. */ - udelay(100); + intel_crtc->active = false; + intel_update_watermarks(dev); + intel_update_fbc(dev); + intel_clear_scanline_wait(dev); +} + +static void ironlake_crtc_dpms(struct drm_crtc *crtc, int mode) +{ + struct intel_crtc *intel_crtc = to_intel_crtc(crtc); + int pipe = intel_crtc->pipe; + int plane = intel_crtc->plane; + + /* XXX: When our outputs are all unaware of DPMS modes other than off + * and on, we should map those modes to DRM_MODE_DPMS_OFF in the CRTC. + */ + switch (mode) { + case DRM_MODE_DPMS_ON: + case DRM_MODE_DPMS_STANDBY: + case DRM_MODE_DPMS_SUSPEND: + DRM_DEBUG_KMS("crtc %d/%d dpms on\n", pipe, plane); + ironlake_crtc_enable(crtc); + break; + + case DRM_MODE_DPMS_OFF: + DRM_DEBUG_KMS("crtc %d/%d dpms off\n", pipe, plane); + ironlake_crtc_disable(crtc); break; } } static void intel_crtc_dpms_overlay(struct intel_crtc *intel_crtc, bool enable) { - struct intel_overlay *overlay; - int ret; - if (!enable && intel_crtc->overlay) { - overlay = intel_crtc->overlay; - mutex_lock(&overlay->dev->struct_mutex); - for (;;) { - ret = intel_overlay_switch_off(overlay); - if (ret == 0) - break; + struct drm_device *dev = intel_crtc->base.dev; - ret = intel_overlay_recover_from_interrupt(overlay, 0); - if (ret != 0) { - /* overlay doesn't react anymore. Usually - * results in a black screen and an unkillable - * X server. */ - BUG(); - overlay->hw_wedged = HW_WEDGED; - break; - } - } - mutex_unlock(&overlay->dev->struct_mutex); + mutex_lock(&dev->struct_mutex); + (void) intel_overlay_switch_off(intel_crtc->overlay, false); + mutex_unlock(&dev->struct_mutex); } - /* Let userspace switch the overlay on again. In most cases userspace - * has to recompute where to put it anyway. */ - return; + /* Let userspace switch the overlay on again. In most cases userspace + * has to recompute where to put it anyway. + */ } -static void i9xx_crtc_dpms(struct drm_crtc *crtc, int mode) +static void i9xx_crtc_enable(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); int pipe = intel_crtc->pipe; int plane = intel_crtc->plane; - int dpll_reg = (pipe == 0) ? DPLL_A : DPLL_B; - int dspcntr_reg = (plane == 0) ? DSPACNTR : DSPBCNTR; - int dspbase_reg = (plane == 0) ? DSPAADDR : DSPBADDR; - int pipeconf_reg = (pipe == 0) ? PIPEACONF : PIPEBCONF; - u32 temp; + u32 reg, temp; - /* XXX: When our outputs are all unaware of DPMS modes other than off - * and on, we should map those modes to DRM_MODE_DPMS_OFF in the CRTC. - */ - switch (mode) { - case DRM_MODE_DPMS_ON: - case DRM_MODE_DPMS_STANDBY: - case DRM_MODE_DPMS_SUSPEND: - /* Enable the DPLL */ - temp = I915_READ(dpll_reg); - if ((temp & DPLL_VCO_ENABLE) == 0) { - I915_WRITE(dpll_reg, temp); - I915_READ(dpll_reg); - /* Wait for the clocks to stabilize. */ - udelay(150); - I915_WRITE(dpll_reg, temp | DPLL_VCO_ENABLE); - I915_READ(dpll_reg); - /* Wait for the clocks to stabilize. */ - udelay(150); - I915_WRITE(dpll_reg, temp | DPLL_VCO_ENABLE); - I915_READ(dpll_reg); - /* Wait for the clocks to stabilize. */ - udelay(150); - } + if (intel_crtc->active) + return; - /* Enable the pipe */ - temp = I915_READ(pipeconf_reg); - if ((temp & PIPEACONF_ENABLE) == 0) - I915_WRITE(pipeconf_reg, temp | PIPEACONF_ENABLE); - - /* Enable the plane */ - temp = I915_READ(dspcntr_reg); - if ((temp & DISPLAY_PLANE_ENABLE) == 0) { - I915_WRITE(dspcntr_reg, temp | DISPLAY_PLANE_ENABLE); - /* Flush the plane changes */ - I915_WRITE(dspbase_reg, I915_READ(dspbase_reg)); - } + intel_crtc->active = true; + intel_update_watermarks(dev); - intel_crtc_load_lut(crtc); + /* Enable the DPLL */ + reg = DPLL(pipe); + temp = I915_READ(reg); + if ((temp & DPLL_VCO_ENABLE) == 0) { + I915_WRITE(reg, temp); - if ((IS_I965G(dev) || plane == 0)) - intel_update_fbc(crtc, &crtc->mode); + /* Wait for the clocks to stabilize. */ + POSTING_READ(reg); + udelay(150); - /* Give the overlay scaler a chance to enable if it's on this pipe */ - intel_crtc_dpms_overlay(intel_crtc, true); - break; - case DRM_MODE_DPMS_OFF: - /* Give the overlay scaler a chance to disable if it's on this pipe */ - intel_crtc_dpms_overlay(intel_crtc, false); - drm_vblank_off(dev, pipe); - - if (dev_priv->cfb_plane == plane && - dev_priv->display.disable_fbc) - dev_priv->display.disable_fbc(dev); - - /* Disable display plane */ - temp = I915_READ(dspcntr_reg); - if ((temp & DISPLAY_PLANE_ENABLE) != 0) { - I915_WRITE(dspcntr_reg, temp & ~DISPLAY_PLANE_ENABLE); - /* Flush the plane changes */ - I915_WRITE(dspbase_reg, I915_READ(dspbase_reg)); - I915_READ(dspbase_reg); - } + I915_WRITE(reg, temp | DPLL_VCO_ENABLE); + + /* Wait for the clocks to stabilize. */ + POSTING_READ(reg); + udelay(150); + + I915_WRITE(reg, temp | DPLL_VCO_ENABLE); + + /* Wait for the clocks to stabilize. */ + POSTING_READ(reg); + udelay(150); + } + + /* Enable the pipe */ + reg = PIPECONF(pipe); + temp = I915_READ(reg); + if ((temp & PIPECONF_ENABLE) == 0) + I915_WRITE(reg, temp | PIPECONF_ENABLE); + + /* Enable the plane */ + reg = DSPCNTR(plane); + temp = I915_READ(reg); + if ((temp & DISPLAY_PLANE_ENABLE) == 0) { + I915_WRITE(reg, temp | DISPLAY_PLANE_ENABLE); + intel_flush_display_plane(dev, plane); + } + + intel_crtc_load_lut(crtc); + intel_update_fbc(dev); + + /* Give the overlay scaler a chance to enable if it's on this pipe */ + intel_crtc_dpms_overlay(intel_crtc, true); + intel_crtc_update_cursor(crtc, true); +} + +static void i9xx_crtc_disable(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); + int pipe = intel_crtc->pipe; + int plane = intel_crtc->plane; + u32 reg, temp; + + if (!intel_crtc->active) + return; + + /* Give the overlay scaler a chance to disable if it's on this pipe */ + intel_crtc_wait_for_pending_flips(crtc); + drm_vblank_off(dev, pipe); + intel_crtc_dpms_overlay(intel_crtc, false); + intel_crtc_update_cursor(crtc, false); + + if (dev_priv->cfb_plane == plane && + dev_priv->display.disable_fbc) + dev_priv->display.disable_fbc(dev); + + /* Disable display plane */ + reg = DSPCNTR(plane); + temp = I915_READ(reg); + if (temp & DISPLAY_PLANE_ENABLE) { + I915_WRITE(reg, temp & ~DISPLAY_PLANE_ENABLE); + /* Flush the plane changes */ + intel_flush_display_plane(dev, plane); /* Wait for vblank for the disable to take effect */ - intel_wait_for_vblank_off(dev, pipe); - - /* Don't disable pipe A or pipe A PLLs if needed */ - if (pipeconf_reg == PIPEACONF && - (dev_priv->quirks & QUIRK_PIPEA_FORCE)) - goto skip_pipe_off; - - /* Next, disable display pipes */ - temp = I915_READ(pipeconf_reg); - if ((temp & PIPEACONF_ENABLE) != 0) { - I915_WRITE(pipeconf_reg, temp & ~PIPEACONF_ENABLE); - I915_READ(pipeconf_reg); - } + if (IS_GEN2(dev)) + intel_wait_for_vblank(dev, pipe); + } - /* Wait for vblank for the disable to take effect. */ - intel_wait_for_vblank_off(dev, pipe); + /* Don't disable pipe A or pipe A PLLs if needed */ + if (pipe == 0 && (dev_priv->quirks & QUIRK_PIPEA_FORCE)) + goto done; + + /* Next, disable display pipes */ + reg = PIPECONF(pipe); + temp = I915_READ(reg); + if (temp & PIPECONF_ENABLE) { + I915_WRITE(reg, temp & ~PIPECONF_ENABLE); + + /* Wait for the pipe to turn off */ + POSTING_READ(reg); + intel_wait_for_pipe_off(dev, pipe); + } + + reg = DPLL(pipe); + temp = I915_READ(reg); + if (temp & DPLL_VCO_ENABLE) { + I915_WRITE(reg, temp & ~DPLL_VCO_ENABLE); - temp = I915_READ(dpll_reg); - if ((temp & DPLL_VCO_ENABLE) != 0) { - I915_WRITE(dpll_reg, temp & ~DPLL_VCO_ENABLE); - I915_READ(dpll_reg); - } - skip_pipe_off: /* Wait for the clocks to turn off. */ + POSTING_READ(reg); udelay(150); + } + +done: + intel_crtc->active = false; + intel_update_fbc(dev); + intel_update_watermarks(dev); + intel_clear_scanline_wait(dev); +} + +static void i9xx_crtc_dpms(struct drm_crtc *crtc, int mode) +{ + /* XXX: When our outputs are all unaware of DPMS modes other than off + * and on, we should map those modes to DRM_MODE_DPMS_OFF in the CRTC. + */ + switch (mode) { + case DRM_MODE_DPMS_ON: + case DRM_MODE_DPMS_STANDBY: + case DRM_MODE_DPMS_SUSPEND: + i9xx_crtc_enable(crtc); + break; + case DRM_MODE_DPMS_OFF: + i9xx_crtc_disable(crtc); break; } } @@ -2374,26 +2487,9 @@ static void intel_crtc_dpms(struct drm_crtc *crtc, int mode) return; intel_crtc->dpms_mode = mode; - intel_crtc->cursor_on = mode == DRM_MODE_DPMS_ON; - - /* When switching on the display, ensure that SR is disabled - * with multiple pipes prior to enabling to new pipe. - * - * When switching off the display, make sure the cursor is - * properly hidden prior to disabling the pipe. - */ - if (mode == DRM_MODE_DPMS_ON) - intel_update_watermarks(dev); - else - intel_crtc_update_cursor(crtc); dev_priv->display.dpms(crtc, mode); - if (mode == DRM_MODE_DPMS_ON) - intel_crtc_update_cursor(crtc); - else - intel_update_watermarks(dev); - if (!dev->primary->master) return; @@ -2418,16 +2514,46 @@ static void intel_crtc_dpms(struct drm_crtc *crtc, int mode) } } -static void intel_crtc_prepare (struct drm_crtc *crtc) +static void intel_crtc_disable(struct drm_crtc *crtc) { struct drm_crtc_helper_funcs *crtc_funcs = crtc->helper_private; + struct drm_device *dev = crtc->dev; + crtc_funcs->dpms(crtc, DRM_MODE_DPMS_OFF); + + if (crtc->fb) { + mutex_lock(&dev->struct_mutex); + i915_gem_object_unpin(to_intel_framebuffer(crtc->fb)->obj); + mutex_unlock(&dev->struct_mutex); + } } -static void intel_crtc_commit (struct drm_crtc *crtc) +/* Prepare for a mode set. + * + * Note we could be a lot smarter here. We need to figure out which outputs + * will be enabled, which disabled (in short, how the config will changes) + * and perform the minimum necessary steps to accomplish that, e.g. updating + * watermarks, FBC configuration, making sure PLLs are programmed correctly, + * panel fitting is in the proper state, etc. + */ +static void i9xx_crtc_prepare(struct drm_crtc *crtc) { - struct drm_crtc_helper_funcs *crtc_funcs = crtc->helper_private; - crtc_funcs->dpms(crtc, DRM_MODE_DPMS_ON); + i9xx_crtc_disable(crtc); +} + +static void i9xx_crtc_commit(struct drm_crtc *crtc) +{ + i9xx_crtc_enable(crtc); +} + +static void ironlake_crtc_prepare(struct drm_crtc *crtc) +{ + ironlake_crtc_disable(crtc); +} + +static void ironlake_crtc_commit(struct drm_crtc *crtc) +{ + ironlake_crtc_enable(crtc); } void intel_encoder_prepare (struct drm_encoder *encoder) @@ -2446,13 +2572,7 @@ void intel_encoder_commit (struct drm_encoder *encoder) void intel_encoder_destroy(struct drm_encoder *encoder) { - struct intel_encoder *intel_encoder = enc_to_intel_encoder(encoder); - - if (intel_encoder->ddc_bus) - intel_i2c_destroy(intel_encoder->ddc_bus); - - if (intel_encoder->i2c_bus) - intel_i2c_destroy(intel_encoder->i2c_bus); + struct intel_encoder *intel_encoder = to_intel_encoder(encoder); drm_encoder_cleanup(encoder); kfree(intel_encoder); @@ -2543,33 +2663,6 @@ static int i830_get_display_clock_speed(struct drm_device *dev) return 133000; } -/** - * Return the pipe currently connected to the panel fitter, - * or -1 if the panel fitter is not present or not in use - */ -int intel_panel_fitter_pipe (struct drm_device *dev) -{ - struct drm_i915_private *dev_priv = dev->dev_private; - u32 pfit_control; - - /* i830 doesn't have a panel fitter */ - if (IS_I830(dev)) - return -1; - - pfit_control = I915_READ(PFIT_CONTROL); - - /* See if the panel fitter is in use */ - if ((pfit_control & PFIT_ENABLE) == 0) - return -1; - - /* 965 can place panel fitter on either pipe */ - if (IS_I965G(dev)) - return (pfit_control >> 29) & 0x3; - - /* older chips can only use pipe 1 */ - return 1; -} - struct fdi_m_n { u32 tu; u32 gmch_m; @@ -2888,7 +2981,7 @@ static int i9xx_get_fifo_size(struct drm_device *dev, int plane) size = ((dsparb >> DSPARB_CSTART_SHIFT) & 0x7f) - size; DRM_DEBUG_KMS("FIFO size - (0x%08x) %s: %d\n", dsparb, - plane ? "B" : "A", size); + plane ? "B" : "A", size); return size; } @@ -2905,7 +2998,7 @@ static int i85x_get_fifo_size(struct drm_device *dev, int plane) size >>= 1; /* Convert to cachelines */ DRM_DEBUG_KMS("FIFO size - (0x%08x) %s: %d\n", dsparb, - plane ? "B" : "A", size); + plane ? "B" : "A", size); return size; } @@ -2920,8 +3013,8 @@ static int i845_get_fifo_size(struct drm_device *dev, int plane) size >>= 2; /* Convert to cachelines */ DRM_DEBUG_KMS("FIFO size - (0x%08x) %s: %d\n", dsparb, - plane ? "B" : "A", - size); + plane ? "B" : "A", + size); return size; } @@ -2936,14 +3029,14 @@ static int i830_get_fifo_size(struct drm_device *dev, int plane) size >>= 1; /* Convert to cachelines */ DRM_DEBUG_KMS("FIFO size - (0x%08x) %s: %d\n", dsparb, - plane ? "B" : "A", size); + plane ? "B" : "A", size); return size; } static void pineview_update_wm(struct drm_device *dev, int planea_clock, - int planeb_clock, int sr_hdisplay, int unused, - int pixel_size) + int planeb_clock, int sr_hdisplay, int unused, + int pixel_size) { struct drm_i915_private *dev_priv = dev->dev_private; const struct cxsr_latency *latency; @@ -3055,13 +3148,13 @@ static void g4x_update_wm(struct drm_device *dev, int planea_clock, /* Use ns/us then divide to preserve precision */ sr_entries = (((sr_latency_ns / line_time_us) + 1000) / 1000) * - pixel_size * sr_hdisplay; + pixel_size * sr_hdisplay; sr_entries = DIV_ROUND_UP(sr_entries, cacheline_size); entries_required = (((sr_latency_ns / line_time_us) + 1000) / 1000) * pixel_size * 64; entries_required = DIV_ROUND_UP(entries_required, - g4x_cursor_wm_info.cacheline_size); + g4x_cursor_wm_info.cacheline_size); cursor_sr = entries_required + g4x_cursor_wm_info.guard_size; if (cursor_sr > g4x_cursor_wm_info.max_wm) @@ -3073,7 +3166,7 @@ static void g4x_update_wm(struct drm_device *dev, int planea_clock, } else { /* Turn off self refresh if both pipes are enabled */ I915_WRITE(FW_BLC_SELF, I915_READ(FW_BLC_SELF) - & ~FW_BLC_SELF_EN); + & ~FW_BLC_SELF_EN); } DRM_DEBUG("Setting FIFO watermarks - A: %d, B: %d, SR %d\n", @@ -3111,7 +3204,7 @@ static void i965_update_wm(struct drm_device *dev, int planea_clock, /* Use ns/us then divide to preserve precision */ sr_entries = (((sr_latency_ns / line_time_us) + 1000) / 1000) * - pixel_size * sr_hdisplay; + pixel_size * sr_hdisplay; sr_entries = DIV_ROUND_UP(sr_entries, I915_FIFO_LINE_SIZE); DRM_DEBUG("self-refresh entries: %d\n", sr_entries); srwm = I965_FIFO_SIZE - sr_entries; @@ -3120,11 +3213,11 @@ static void i965_update_wm(struct drm_device *dev, int planea_clock, srwm &= 0x1ff; sr_entries = (((sr_latency_ns / line_time_us) + 1000) / 1000) * - pixel_size * 64; + pixel_size * 64; sr_entries = DIV_ROUND_UP(sr_entries, i965_cursor_wm_info.cacheline_size); cursor_sr = i965_cursor_wm_info.fifo_size - - (sr_entries + i965_cursor_wm_info.guard_size); + (sr_entries + i965_cursor_wm_info.guard_size); if (cursor_sr > i965_cursor_wm_info.max_wm) cursor_sr = i965_cursor_wm_info.max_wm; @@ -3132,11 +3225,11 @@ static void i965_update_wm(struct drm_device *dev, int planea_clock, DRM_DEBUG_KMS("self-refresh watermark: display plane %d " "cursor %d\n", srwm, cursor_sr); - if (IS_I965GM(dev)) + if (IS_CRESTLINE(dev)) I915_WRITE(FW_BLC_SELF, FW_BLC_SELF_EN); } else { /* Turn off self refresh if both pipes are enabled */ - if (IS_I965GM(dev)) + if (IS_CRESTLINE(dev)) I915_WRITE(FW_BLC_SELF, I915_READ(FW_BLC_SELF) & ~FW_BLC_SELF_EN); } @@ -3166,9 +3259,9 @@ static void i9xx_update_wm(struct drm_device *dev, int planea_clock, int sr_clock, sr_entries = 0; /* Create copies of the base settings for each pipe */ - if (IS_I965GM(dev) || IS_I945GM(dev)) + if (IS_CRESTLINE(dev) || IS_I945GM(dev)) planea_params = planeb_params = i945_wm_info; - else if (IS_I9XX(dev)) + else if (!IS_GEN2(dev)) planea_params = planeb_params = i915_wm_info; else planea_params = planeb_params = i855_wm_info; @@ -3203,7 +3296,7 @@ static void i9xx_update_wm(struct drm_device *dev, int planea_clock, /* Use ns/us then divide to preserve precision */ sr_entries = (((sr_latency_ns / line_time_us) + 1000) / 1000) * - pixel_size * sr_hdisplay; + pixel_size * sr_hdisplay; sr_entries = DIV_ROUND_UP(sr_entries, cacheline_size); DRM_DEBUG_KMS("self-refresh entries: %d\n", sr_entries); srwm = total_size - sr_entries; @@ -3228,7 +3321,7 @@ static void i9xx_update_wm(struct drm_device *dev, int planea_clock, } DRM_DEBUG_KMS("Setting FIFO watermarks - A: %d, B: %d, C: %d, SR %d\n", - planea_wm, planeb_wm, cwm, srwm); + planea_wm, planeb_wm, cwm, srwm); fwater_lo = ((planeb_wm & 0x3f) << 16) | (planea_wm & 0x3f); fwater_hi = (cwm & 0x1f); @@ -3262,146 +3355,130 @@ static void i830_update_wm(struct drm_device *dev, int planea_clock, int unused, #define ILK_LP0_PLANE_LATENCY 700 #define ILK_LP0_CURSOR_LATENCY 1300 -static void ironlake_update_wm(struct drm_device *dev, int planea_clock, - int planeb_clock, int sr_hdisplay, int sr_htotal, - int pixel_size) +static bool ironlake_compute_wm0(struct drm_device *dev, + int pipe, + int *plane_wm, + int *cursor_wm) { - struct drm_i915_private *dev_priv = dev->dev_private; - int planea_wm, planeb_wm, cursora_wm, cursorb_wm; - int sr_wm, cursor_wm; - unsigned long line_time_us; - int sr_clock, entries_required; - u32 reg_value; - int line_count; - int planea_htotal = 0, planeb_htotal = 0; struct drm_crtc *crtc; + int htotal, hdisplay, clock, pixel_size = 0; + int line_time_us, line_count, entries; - /* Need htotal for all active display plane */ - list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { - struct intel_crtc *intel_crtc = to_intel_crtc(crtc); - if (intel_crtc->dpms_mode == DRM_MODE_DPMS_ON) { - if (intel_crtc->plane == 0) - planea_htotal = crtc->mode.htotal; - else - planeb_htotal = crtc->mode.htotal; - } - } - - /* Calculate and update the watermark for plane A */ - if (planea_clock) { - entries_required = ((planea_clock / 1000) * pixel_size * - ILK_LP0_PLANE_LATENCY) / 1000; - entries_required = DIV_ROUND_UP(entries_required, - ironlake_display_wm_info.cacheline_size); - planea_wm = entries_required + - ironlake_display_wm_info.guard_size; - - if (planea_wm > (int)ironlake_display_wm_info.max_wm) - planea_wm = ironlake_display_wm_info.max_wm; - - /* Use the large buffer method to calculate cursor watermark */ - line_time_us = (planea_htotal * 1000) / planea_clock; - - /* Use ns/us then divide to preserve precision */ - line_count = (ILK_LP0_CURSOR_LATENCY / line_time_us + 1000) / 1000; - - /* calculate the cursor watermark for cursor A */ - entries_required = line_count * 64 * pixel_size; - entries_required = DIV_ROUND_UP(entries_required, - ironlake_cursor_wm_info.cacheline_size); - cursora_wm = entries_required + ironlake_cursor_wm_info.guard_size; - if (cursora_wm > ironlake_cursor_wm_info.max_wm) - cursora_wm = ironlake_cursor_wm_info.max_wm; - - reg_value = I915_READ(WM0_PIPEA_ILK); - reg_value &= ~(WM0_PIPE_PLANE_MASK | WM0_PIPE_CURSOR_MASK); - reg_value |= (planea_wm << WM0_PIPE_PLANE_SHIFT) | - (cursora_wm & WM0_PIPE_CURSOR_MASK); - I915_WRITE(WM0_PIPEA_ILK, reg_value); - DRM_DEBUG_KMS("FIFO watermarks For pipe A - plane %d, " - "cursor: %d\n", planea_wm, cursora_wm); - } - /* Calculate and update the watermark for plane B */ - if (planeb_clock) { - entries_required = ((planeb_clock / 1000) * pixel_size * - ILK_LP0_PLANE_LATENCY) / 1000; - entries_required = DIV_ROUND_UP(entries_required, - ironlake_display_wm_info.cacheline_size); - planeb_wm = entries_required + - ironlake_display_wm_info.guard_size; - - if (planeb_wm > (int)ironlake_display_wm_info.max_wm) - planeb_wm = ironlake_display_wm_info.max_wm; + crtc = intel_get_crtc_for_pipe(dev, pipe); + if (crtc->fb == NULL || !crtc->enabled) + return false; - /* Use the large buffer method to calculate cursor watermark */ - line_time_us = (planeb_htotal * 1000) / planeb_clock; + htotal = crtc->mode.htotal; + hdisplay = crtc->mode.hdisplay; + clock = crtc->mode.clock; + pixel_size = crtc->fb->bits_per_pixel / 8; + + /* Use the small buffer method to calculate plane watermark */ + entries = ((clock * pixel_size / 1000) * ILK_LP0_PLANE_LATENCY) / 1000; + entries = DIV_ROUND_UP(entries, + ironlake_display_wm_info.cacheline_size); + *plane_wm = entries + ironlake_display_wm_info.guard_size; + if (*plane_wm > (int)ironlake_display_wm_info.max_wm) + *plane_wm = ironlake_display_wm_info.max_wm; + + /* Use the large buffer method to calculate cursor watermark */ + line_time_us = ((htotal * 1000) / clock); + line_count = (ILK_LP0_CURSOR_LATENCY / line_time_us + 1000) / 1000; + entries = line_count * 64 * pixel_size; + entries = DIV_ROUND_UP(entries, + ironlake_cursor_wm_info.cacheline_size); + *cursor_wm = entries + ironlake_cursor_wm_info.guard_size; + if (*cursor_wm > ironlake_cursor_wm_info.max_wm) + *cursor_wm = ironlake_cursor_wm_info.max_wm; - /* Use ns/us then divide to preserve precision */ - line_count = (ILK_LP0_CURSOR_LATENCY / line_time_us + 1000) / 1000; + return true; +} - /* calculate the cursor watermark for cursor B */ - entries_required = line_count * 64 * pixel_size; - entries_required = DIV_ROUND_UP(entries_required, - ironlake_cursor_wm_info.cacheline_size); - cursorb_wm = entries_required + ironlake_cursor_wm_info.guard_size; - if (cursorb_wm > ironlake_cursor_wm_info.max_wm) - cursorb_wm = ironlake_cursor_wm_info.max_wm; +static void ironlake_update_wm(struct drm_device *dev, + int planea_clock, int planeb_clock, + int sr_hdisplay, int sr_htotal, + int pixel_size) +{ + struct drm_i915_private *dev_priv = dev->dev_private; + int plane_wm, cursor_wm, enabled; + int tmp; + + enabled = 0; + if (ironlake_compute_wm0(dev, 0, &plane_wm, &cursor_wm)) { + I915_WRITE(WM0_PIPEA_ILK, + (plane_wm << WM0_PIPE_PLANE_SHIFT) | cursor_wm); + DRM_DEBUG_KMS("FIFO watermarks For pipe A -" + " plane %d, " "cursor: %d\n", + plane_wm, cursor_wm); + enabled++; + } - reg_value = I915_READ(WM0_PIPEB_ILK); - reg_value &= ~(WM0_PIPE_PLANE_MASK | WM0_PIPE_CURSOR_MASK); - reg_value |= (planeb_wm << WM0_PIPE_PLANE_SHIFT) | - (cursorb_wm & WM0_PIPE_CURSOR_MASK); - I915_WRITE(WM0_PIPEB_ILK, reg_value); - DRM_DEBUG_KMS("FIFO watermarks For pipe B - plane %d, " - "cursor: %d\n", planeb_wm, cursorb_wm); + if (ironlake_compute_wm0(dev, 1, &plane_wm, &cursor_wm)) { + I915_WRITE(WM0_PIPEB_ILK, + (plane_wm << WM0_PIPE_PLANE_SHIFT) | cursor_wm); + DRM_DEBUG_KMS("FIFO watermarks For pipe B -" + " plane %d, cursor: %d\n", + plane_wm, cursor_wm); + enabled++; } /* * Calculate and update the self-refresh watermark only when one * display plane is used. */ - if (!planea_clock || !planeb_clock) { - + tmp = 0; + if (enabled == 1 && /* XXX disabled due to buggy implmentation? */ 0) { + unsigned long line_time_us; + int small, large, plane_fbc; + int sr_clock, entries; + int line_count, line_size; /* Read the self-refresh latency. The unit is 0.5us */ int ilk_sr_latency = I915_READ(MLTR_ILK) & ILK_SRLT_MASK; sr_clock = planea_clock ? planea_clock : planeb_clock; - line_time_us = ((sr_htotal * 1000) / sr_clock); + line_time_us = (sr_htotal * 1000) / sr_clock; /* Use ns/us then divide to preserve precision */ line_count = ((ilk_sr_latency * 500) / line_time_us + 1000) - / 1000; + / 1000; + line_size = sr_hdisplay * pixel_size; - /* calculate the self-refresh watermark for display plane */ - entries_required = line_count * sr_hdisplay * pixel_size; - entries_required = DIV_ROUND_UP(entries_required, - ironlake_display_srwm_info.cacheline_size); - sr_wm = entries_required + - ironlake_display_srwm_info.guard_size; + /* Use the minimum of the small and large buffer method for primary */ + small = ((sr_clock * pixel_size / 1000) * (ilk_sr_latency * 500)) / 1000; + large = line_count * line_size; - /* calculate the self-refresh watermark for display cursor */ - entries_required = line_count * pixel_size * 64; - entries_required = DIV_ROUND_UP(entries_required, - ironlake_cursor_srwm_info.cacheline_size); - cursor_wm = entries_required + - ironlake_cursor_srwm_info.guard_size; + entries = DIV_ROUND_UP(min(small, large), + ironlake_display_srwm_info.cacheline_size); - /* configure watermark and enable self-refresh */ - reg_value = I915_READ(WM1_LP_ILK); - reg_value &= ~(WM1_LP_LATENCY_MASK | WM1_LP_SR_MASK | - WM1_LP_CURSOR_MASK); - reg_value |= (ilk_sr_latency << WM1_LP_LATENCY_SHIFT) | - (sr_wm << WM1_LP_SR_SHIFT) | cursor_wm; + plane_fbc = entries * 64; + plane_fbc = DIV_ROUND_UP(plane_fbc, line_size); - I915_WRITE(WM1_LP_ILK, reg_value); - DRM_DEBUG_KMS("self-refresh watermark: display plane %d " - "cursor %d\n", sr_wm, cursor_wm); + plane_wm = entries + ironlake_display_srwm_info.guard_size; + if (plane_wm > (int)ironlake_display_srwm_info.max_wm) + plane_wm = ironlake_display_srwm_info.max_wm; - } else { - /* Turn off self refresh if both pipes are enabled */ - I915_WRITE(WM1_LP_ILK, I915_READ(WM1_LP_ILK) & ~WM1_LP_SR_EN); - } + /* calculate the self-refresh watermark for display cursor */ + entries = line_count * pixel_size * 64; + entries = DIV_ROUND_UP(entries, + ironlake_cursor_srwm_info.cacheline_size); + + cursor_wm = entries + ironlake_cursor_srwm_info.guard_size; + if (cursor_wm > (int)ironlake_cursor_srwm_info.max_wm) + cursor_wm = ironlake_cursor_srwm_info.max_wm; + + /* configure watermark and enable self-refresh */ + tmp = (WM1_LP_SR_EN | + (ilk_sr_latency << WM1_LP_LATENCY_SHIFT) | + (plane_fbc << WM1_LP_FBC_SHIFT) | + (plane_wm << WM1_LP_SR_SHIFT) | + cursor_wm); + DRM_DEBUG_KMS("self-refresh watermark: display plane %d, fbc lines %d," + " cursor %d\n", plane_wm, plane_fbc, cursor_wm); + } + I915_WRITE(WM1_LP_ILK, tmp); + /* XXX setup WM2 and WM3 */ } + /** * intel_update_watermarks - update FIFO watermark values based on current modes * @@ -3433,7 +3510,7 @@ static void ironlake_update_wm(struct drm_device *dev, int planea_clock, * * We don't use the sprite, so we can ignore that. And on Crestline we have * to set the non-SR watermarks to 8. - */ + */ static void intel_update_watermarks(struct drm_device *dev) { struct drm_i915_private *dev_priv = dev->dev_private; @@ -3449,15 +3526,15 @@ static void intel_update_watermarks(struct drm_device *dev) /* Get the clock config from both planes */ list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { struct intel_crtc *intel_crtc = to_intel_crtc(crtc); - if (intel_crtc->dpms_mode == DRM_MODE_DPMS_ON) { + if (intel_crtc->active) { enabled++; if (intel_crtc->plane == 0) { DRM_DEBUG_KMS("plane A (pipe %d) clock: %d\n", - intel_crtc->pipe, crtc->mode.clock); + intel_crtc->pipe, crtc->mode.clock); planea_clock = crtc->mode.clock; } else { DRM_DEBUG_KMS("plane B (pipe %d) clock: %d\n", - intel_crtc->pipe, crtc->mode.clock); + intel_crtc->pipe, crtc->mode.clock); planeb_clock = crtc->mode.clock; } sr_hdisplay = crtc->mode.hdisplay; @@ -3488,62 +3565,35 @@ static int intel_crtc_mode_set(struct drm_crtc *crtc, struct intel_crtc *intel_crtc = to_intel_crtc(crtc); int pipe = intel_crtc->pipe; int plane = intel_crtc->plane; - int fp_reg = (pipe == 0) ? FPA0 : FPB0; - int dpll_reg = (pipe == 0) ? DPLL_A : DPLL_B; - int dpll_md_reg = (intel_crtc->pipe == 0) ? DPLL_A_MD : DPLL_B_MD; - int dspcntr_reg = (plane == 0) ? DSPACNTR : DSPBCNTR; - int pipeconf_reg = (pipe == 0) ? PIPEACONF : PIPEBCONF; - int htot_reg = (pipe == 0) ? HTOTAL_A : HTOTAL_B; - int hblank_reg = (pipe == 0) ? HBLANK_A : HBLANK_B; - int hsync_reg = (pipe == 0) ? HSYNC_A : HSYNC_B; - int vtot_reg = (pipe == 0) ? VTOTAL_A : VTOTAL_B; - int vblank_reg = (pipe == 0) ? VBLANK_A : VBLANK_B; - int vsync_reg = (pipe == 0) ? VSYNC_A : VSYNC_B; - int dspsize_reg = (plane == 0) ? DSPASIZE : DSPBSIZE; - int dsppos_reg = (plane == 0) ? DSPAPOS : DSPBPOS; - int pipesrc_reg = (pipe == 0) ? PIPEASRC : PIPEBSRC; + u32 fp_reg, dpll_reg; int refclk, num_connectors = 0; intel_clock_t clock, reduced_clock; - u32 dpll = 0, fp = 0, fp2 = 0, dspcntr, pipeconf; + u32 dpll, fp = 0, fp2 = 0, dspcntr, pipeconf; bool ok, has_reduced_clock = false, is_sdvo = false, is_dvo = false; bool is_crt = false, is_lvds = false, is_tv = false, is_dp = false; struct intel_encoder *has_edp_encoder = NULL; struct drm_mode_config *mode_config = &dev->mode_config; - struct drm_encoder *encoder; + struct intel_encoder *encoder; const intel_limit_t *limit; int ret; struct fdi_m_n m_n = {0}; - int data_m1_reg = (pipe == 0) ? PIPEA_DATA_M1 : PIPEB_DATA_M1; - int data_n1_reg = (pipe == 0) ? PIPEA_DATA_N1 : PIPEB_DATA_N1; - int link_m1_reg = (pipe == 0) ? PIPEA_LINK_M1 : PIPEB_LINK_M1; - int link_n1_reg = (pipe == 0) ? PIPEA_LINK_N1 : PIPEB_LINK_N1; - int pch_fp_reg = (pipe == 0) ? PCH_FPA0 : PCH_FPB0; - int pch_dpll_reg = (pipe == 0) ? PCH_DPLL_A : PCH_DPLL_B; - int fdi_rx_reg = (pipe == 0) ? FDI_RXA_CTL : FDI_RXB_CTL; - int fdi_tx_reg = (pipe == 0) ? FDI_TXA_CTL : FDI_TXB_CTL; - int trans_dpll_sel = (pipe == 0) ? 0 : 1; - int lvds_reg = LVDS; - u32 temp; - int sdvo_pixel_multiply; + u32 reg, temp; int target_clock; drm_vblank_pre_modeset(dev, pipe); - list_for_each_entry(encoder, &mode_config->encoder_list, head) { - struct intel_encoder *intel_encoder; - - if (encoder->crtc != crtc) + list_for_each_entry(encoder, &mode_config->encoder_list, base.head) { + if (encoder->base.crtc != crtc) continue; - intel_encoder = enc_to_intel_encoder(encoder); - switch (intel_encoder->type) { + switch (encoder->type) { case INTEL_OUTPUT_LVDS: is_lvds = true; break; case INTEL_OUTPUT_SDVO: case INTEL_OUTPUT_HDMI: is_sdvo = true; - if (intel_encoder->needs_tv_clock) + if (encoder->needs_tv_clock) is_tv = true; break; case INTEL_OUTPUT_DVO: @@ -3559,7 +3609,7 @@ static int intel_crtc_mode_set(struct drm_crtc *crtc, is_dp = true; break; case INTEL_OUTPUT_EDP: - has_edp_encoder = intel_encoder; + has_edp_encoder = encoder; break; } @@ -3569,15 +3619,14 @@ static int intel_crtc_mode_set(struct drm_crtc *crtc, if (is_lvds && dev_priv->lvds_use_ssc && num_connectors < 2) { refclk = dev_priv->lvds_ssc_freq * 1000; DRM_DEBUG_KMS("using SSC reference clock of %d MHz\n", - refclk / 1000); - } else if (IS_I9XX(dev)) { + refclk / 1000); + } else if (!IS_GEN2(dev)) { refclk = 96000; if (HAS_PCH_SPLIT(dev)) refclk = 120000; /* 120Mhz refclk */ } else { refclk = 48000; } - /* * Returns a set of divisors for the desired target clock with the given @@ -3593,13 +3642,13 @@ static int intel_crtc_mode_set(struct drm_crtc *crtc, } /* Ensure that the cursor is valid for the new mode before changing... */ - intel_crtc_update_cursor(crtc); + intel_crtc_update_cursor(crtc, true); if (is_lvds && dev_priv->lvds_downclock_avail) { has_reduced_clock = limit->find_pll(limit, crtc, - dev_priv->lvds_downclock, - refclk, - &reduced_clock); + dev_priv->lvds_downclock, + refclk, + &reduced_clock); if (has_reduced_clock && (clock.p != reduced_clock.p)) { /* * If the different P is found, it means that we can't @@ -3608,7 +3657,7 @@ static int intel_crtc_mode_set(struct drm_crtc *crtc, * feature. */ DRM_DEBUG_KMS("Different P is found for " - "LVDS clock/downclock\n"); + "LVDS clock/downclock\n"); has_reduced_clock = 0; } } @@ -3616,14 +3665,14 @@ static int intel_crtc_mode_set(struct drm_crtc *crtc, this mirrors vbios setting. */ if (is_sdvo && is_tv) { if (adjusted_mode->clock >= 100000 - && adjusted_mode->clock < 140500) { + && adjusted_mode->clock < 140500) { clock.p1 = 2; clock.p2 = 10; clock.n = 3; clock.m1 = 16; clock.m2 = 8; } else if (adjusted_mode->clock >= 140500 - && adjusted_mode->clock <= 200000) { + && adjusted_mode->clock <= 200000) { clock.p1 = 1; clock.p2 = 10; clock.n = 6; @@ -3648,21 +3697,28 @@ static int intel_crtc_mode_set(struct drm_crtc *crtc, target_clock = mode->clock; else target_clock = adjusted_mode->clock; - link_bw = 270000; + + /* FDI is a binary signal running at ~2.7GHz, encoding + * each output octet as 10 bits. The actual frequency + * is stored as a divider into a 100MHz clock, and the + * mode pixel clock is stored in units of 1KHz. + * Hence the bw of each lane in terms of the mode signal + * is: + */ + link_bw = intel_fdi_link_freq(dev) * MHz(100)/KHz(1)/10; } /* determine panel color depth */ - temp = I915_READ(pipeconf_reg); + temp = I915_READ(PIPECONF(pipe)); temp &= ~PIPE_BPC_MASK; if (is_lvds) { - int lvds_reg = I915_READ(PCH_LVDS); /* the BPC will be 6 if it is 18-bit LVDS panel */ - if ((lvds_reg & LVDS_A3_POWER_MASK) == LVDS_A3_POWER_UP) + if ((I915_READ(PCH_LVDS) & LVDS_A3_POWER_MASK) == LVDS_A3_POWER_UP) temp |= PIPE_8BPC; else temp |= PIPE_6BPC; } else if (has_edp_encoder || (is_dp && intel_pch_has_edp(crtc))) { - switch (dev_priv->edp_bpp/3) { + switch (dev_priv->edp.bpp/3) { case 8: temp |= PIPE_8BPC; break; @@ -3678,8 +3734,7 @@ static int intel_crtc_mode_set(struct drm_crtc *crtc, } } else temp |= PIPE_8BPC; - I915_WRITE(pipeconf_reg, temp); - I915_READ(pipeconf_reg); + I915_WRITE(PIPECONF(pipe), temp); switch (temp & PIPE_BPC_MASK) { case PIPE_8BPC: @@ -3724,33 +3779,27 @@ static int intel_crtc_mode_set(struct drm_crtc *crtc, /* Always enable nonspread source */ temp &= ~DREF_NONSPREAD_SOURCE_MASK; temp |= DREF_NONSPREAD_SOURCE_ENABLE; - I915_WRITE(PCH_DREF_CONTROL, temp); - POSTING_READ(PCH_DREF_CONTROL); - temp &= ~DREF_SSC_SOURCE_MASK; temp |= DREF_SSC_SOURCE_ENABLE; I915_WRITE(PCH_DREF_CONTROL, temp); - POSTING_READ(PCH_DREF_CONTROL); + POSTING_READ(PCH_DREF_CONTROL); udelay(200); if (has_edp_encoder) { if (dev_priv->lvds_use_ssc) { temp |= DREF_SSC1_ENABLE; I915_WRITE(PCH_DREF_CONTROL, temp); - POSTING_READ(PCH_DREF_CONTROL); + POSTING_READ(PCH_DREF_CONTROL); udelay(200); temp &= ~DREF_CPU_SOURCE_OUTPUT_MASK; temp |= DREF_CPU_SOURCE_OUTPUT_DOWNSPREAD; - I915_WRITE(PCH_DREF_CONTROL, temp); - POSTING_READ(PCH_DREF_CONTROL); } else { temp |= DREF_CPU_SOURCE_OUTPUT_NONSPREAD; - I915_WRITE(PCH_DREF_CONTROL, temp); - POSTING_READ(PCH_DREF_CONTROL); } + I915_WRITE(PCH_DREF_CONTROL, temp); } } @@ -3766,21 +3815,24 @@ static int intel_crtc_mode_set(struct drm_crtc *crtc, reduced_clock.m2; } + dpll = 0; if (!HAS_PCH_SPLIT(dev)) dpll = DPLL_VGA_MODE_DIS; - if (IS_I9XX(dev)) { + if (!IS_GEN2(dev)) { if (is_lvds) dpll |= DPLLB_MODE_LVDS; else dpll |= DPLLB_MODE_DAC_SERIAL; if (is_sdvo) { + int pixel_multiplier = intel_mode_get_pixel_multiplier(adjusted_mode); + if (pixel_multiplier > 1) { + if (IS_I945G(dev) || IS_I945GM(dev) || IS_G33(dev)) + dpll |= (pixel_multiplier - 1) << SDVO_MULTIPLIER_SHIFT_HIRES; + else if (HAS_PCH_SPLIT(dev)) + dpll |= (pixel_multiplier - 1) << PLL_REF_SDVO_HDMI_MULTIPLIER_SHIFT; + } dpll |= DPLL_DVO_HIGH_SPEED; - sdvo_pixel_multiply = adjusted_mode->clock / mode->clock; - if (IS_I945G(dev) || IS_I945GM(dev) || IS_G33(dev)) - dpll |= (sdvo_pixel_multiply - 1) << SDVO_MULTIPLIER_SHIFT_HIRES; - else if (HAS_PCH_SPLIT(dev)) - dpll |= (sdvo_pixel_multiply - 1) << PLL_REF_SDVO_HDMI_MULTIPLIER_SHIFT; } if (is_dp) dpll |= DPLL_DVO_HIGH_SPEED; @@ -3810,7 +3862,7 @@ static int intel_crtc_mode_set(struct drm_crtc *crtc, dpll |= DPLLB_LVDS_P2_CLOCK_DIV_14; break; } - if (IS_I965G(dev) && !HAS_PCH_SPLIT(dev)) + if (INTEL_INFO(dev)->gen >= 4 && !HAS_PCH_SPLIT(dev)) dpll |= (6 << PLL_LOAD_PULSE_PHASE_SHIFT); } else { if (is_lvds) { @@ -3837,7 +3889,7 @@ static int intel_crtc_mode_set(struct drm_crtc *crtc, dpll |= PLL_REF_INPUT_DREFCLK; /* setup pipeconf */ - pipeconf = I915_READ(pipeconf_reg); + pipeconf = I915_READ(PIPECONF(pipe)); /* Set up the display plane register */ dspcntr = DISPPLANE_GAMMA_ENABLE; @@ -3851,7 +3903,7 @@ static int intel_crtc_mode_set(struct drm_crtc *crtc, dspcntr |= DISPPLANE_SEL_PIPE_B; } - if (pipe == 0 && !IS_I965G(dev)) { + if (pipe == 0 && INTEL_INFO(dev)->gen < 4) { /* Enable pixel doubling when the dot clock is > 90% of the (display) * core speed. * @@ -3860,51 +3912,46 @@ static int intel_crtc_mode_set(struct drm_crtc *crtc, */ if (mode->clock > dev_priv->display.get_display_clock_speed(dev) * 9 / 10) - pipeconf |= PIPEACONF_DOUBLE_WIDE; + pipeconf |= PIPECONF_DOUBLE_WIDE; else - pipeconf &= ~PIPEACONF_DOUBLE_WIDE; + pipeconf &= ~PIPECONF_DOUBLE_WIDE; } dspcntr |= DISPLAY_PLANE_ENABLE; - pipeconf |= PIPEACONF_ENABLE; + pipeconf |= PIPECONF_ENABLE; dpll |= DPLL_VCO_ENABLE; - - /* Disable the panel fitter if it was on our pipe */ - if (!HAS_PCH_SPLIT(dev) && intel_panel_fitter_pipe(dev) == pipe) - I915_WRITE(PFIT_CONTROL, 0); - DRM_DEBUG_KMS("Mode for pipe %c:\n", pipe == 0 ? 'A' : 'B'); drm_mode_debug_printmodeline(mode); /* assign to Ironlake registers */ if (HAS_PCH_SPLIT(dev)) { - fp_reg = pch_fp_reg; - dpll_reg = pch_dpll_reg; + fp_reg = PCH_FP0(pipe); + dpll_reg = PCH_DPLL(pipe); + } else { + fp_reg = FP0(pipe); + dpll_reg = DPLL(pipe); } if (!has_edp_encoder) { I915_WRITE(fp_reg, fp); I915_WRITE(dpll_reg, dpll & ~DPLL_VCO_ENABLE); - I915_READ(dpll_reg); + + POSTING_READ(dpll_reg); udelay(150); } /* enable transcoder DPLL */ if (HAS_PCH_CPT(dev)) { temp = I915_READ(PCH_DPLL_SEL); - if (trans_dpll_sel == 0) - temp |= (TRANSA_DPLL_ENABLE | TRANSA_DPLLA_SEL); + if (pipe == 0) + temp |= TRANSA_DPLL_ENABLE | TRANSA_DPLLA_SEL; else - temp |= (TRANSB_DPLL_ENABLE | TRANSB_DPLLB_SEL); + temp |= TRANSB_DPLL_ENABLE | TRANSB_DPLLB_SEL; I915_WRITE(PCH_DPLL_SEL, temp); - I915_READ(PCH_DPLL_SEL); - udelay(150); - } - if (HAS_PCH_SPLIT(dev)) { - pipeconf &= ~PIPE_ENABLE_DITHER; - pipeconf &= ~PIPE_DITHER_TYPE_MASK; + POSTING_READ(PCH_DPLL_SEL); + udelay(150); } /* The LVDS pin pair needs to be on before the DPLLs are enabled. @@ -3912,55 +3959,57 @@ static int intel_crtc_mode_set(struct drm_crtc *crtc, * things on. */ if (is_lvds) { - u32 lvds; - + reg = LVDS; if (HAS_PCH_SPLIT(dev)) - lvds_reg = PCH_LVDS; + reg = PCH_LVDS; - lvds = I915_READ(lvds_reg); - lvds |= LVDS_PORT_EN | LVDS_A0A2_CLKA_POWER_UP; + temp = I915_READ(reg); + temp |= LVDS_PORT_EN | LVDS_A0A2_CLKA_POWER_UP; if (pipe == 1) { if (HAS_PCH_CPT(dev)) - lvds |= PORT_TRANS_B_SEL_CPT; + temp |= PORT_TRANS_B_SEL_CPT; else - lvds |= LVDS_PIPEB_SELECT; + temp |= LVDS_PIPEB_SELECT; } else { if (HAS_PCH_CPT(dev)) - lvds &= ~PORT_TRANS_SEL_MASK; + temp &= ~PORT_TRANS_SEL_MASK; else - lvds &= ~LVDS_PIPEB_SELECT; + temp &= ~LVDS_PIPEB_SELECT; } /* set the corresponsding LVDS_BORDER bit */ - lvds |= dev_priv->lvds_border_bits; + 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) - lvds |= LVDS_B0B3_POWER_UP | LVDS_CLKB_POWER_UP; + temp |= LVDS_B0B3_POWER_UP | LVDS_CLKB_POWER_UP; else - lvds &= ~(LVDS_B0B3_POWER_UP | LVDS_CLKB_POWER_UP); + 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 */ - if (IS_I965G(dev)) { - if (dev_priv->lvds_dither) { - if (HAS_PCH_SPLIT(dev)) { - pipeconf |= PIPE_ENABLE_DITHER; - pipeconf |= PIPE_DITHER_TYPE_ST01; - } else - lvds |= LVDS_ENABLE_DITHER; - } else { - if (!HAS_PCH_SPLIT(dev)) { - lvds &= ~LVDS_ENABLE_DITHER; - } - } + /* set the dithering flag on non-PCH LVDS as needed */ + if (INTEL_INFO(dev)->gen >= 4 && !HAS_PCH_SPLIT(dev)) { + if (dev_priv->lvds_dither) + temp |= LVDS_ENABLE_DITHER; + else + temp &= ~LVDS_ENABLE_DITHER; } - I915_WRITE(lvds_reg, lvds); - I915_READ(lvds_reg); + I915_WRITE(reg, temp); } + + /* set the dithering flag and clear for anything other than a panel. */ + if (HAS_PCH_SPLIT(dev)) { + pipeconf &= ~PIPECONF_DITHER_EN; + pipeconf &= ~PIPECONF_DITHER_TYPE_MASK; + if (dev_priv->lvds_dither && (is_lvds || has_edp_encoder)) { + pipeconf |= PIPECONF_DITHER_EN; + pipeconf |= PIPECONF_DITHER_TYPE_ST1; + } + } + if (is_dp) intel_dp_set_m_n(crtc, mode, adjusted_mode); else if (HAS_PCH_SPLIT(dev)) { @@ -3981,26 +4030,32 @@ static int intel_crtc_mode_set(struct drm_crtc *crtc, if (!has_edp_encoder) { I915_WRITE(fp_reg, fp); I915_WRITE(dpll_reg, dpll); - I915_READ(dpll_reg); + /* Wait for the clocks to stabilize. */ + POSTING_READ(dpll_reg); udelay(150); - if (IS_I965G(dev) && !HAS_PCH_SPLIT(dev)) { + if (INTEL_INFO(dev)->gen >= 4 && !HAS_PCH_SPLIT(dev)) { + temp = 0; if (is_sdvo) { - sdvo_pixel_multiply = adjusted_mode->clock / mode->clock; - I915_WRITE(dpll_md_reg, (0 << DPLL_MD_UDI_DIVIDER_SHIFT) | - ((sdvo_pixel_multiply - 1) << DPLL_MD_UDI_MULTIPLIER_SHIFT)); - } else - I915_WRITE(dpll_md_reg, 0); + temp = intel_mode_get_pixel_multiplier(adjusted_mode); + if (temp > 1) + temp = (temp - 1) << DPLL_MD_UDI_MULTIPLIER_SHIFT; + else + temp = 0; + } + I915_WRITE(DPLL_MD(pipe), temp); } else { /* write it again -- the BIOS does, after all */ I915_WRITE(dpll_reg, dpll); } - I915_READ(dpll_reg); + /* Wait for the clocks to stabilize. */ + POSTING_READ(dpll_reg); udelay(150); } + intel_crtc->lowfreq_avail = false; if (is_lvds && has_reduced_clock && i915_powersave) { I915_WRITE(fp_reg + 4, fp2); intel_crtc->lowfreq_avail = true; @@ -4010,7 +4065,6 @@ static int intel_crtc_mode_set(struct drm_crtc *crtc, } } else { I915_WRITE(fp_reg + 4, fp); - intel_crtc->lowfreq_avail = false; if (HAS_PIPE_CXSR(dev)) { DRM_DEBUG_KMS("disabling CxSR downclocking\n"); pipeconf &= ~PIPECONF_CXSR_DOWNCLOCK; @@ -4029,58 +4083,72 @@ static int intel_crtc_mode_set(struct drm_crtc *crtc, } else pipeconf &= ~PIPECONF_INTERLACE_W_FIELD_INDICATION; /* progressive */ - I915_WRITE(htot_reg, (adjusted_mode->crtc_hdisplay - 1) | + I915_WRITE(HTOTAL(pipe), + (adjusted_mode->crtc_hdisplay - 1) | ((adjusted_mode->crtc_htotal - 1) << 16)); - I915_WRITE(hblank_reg, (adjusted_mode->crtc_hblank_start - 1) | + I915_WRITE(HBLANK(pipe), + (adjusted_mode->crtc_hblank_start - 1) | ((adjusted_mode->crtc_hblank_end - 1) << 16)); - I915_WRITE(hsync_reg, (adjusted_mode->crtc_hsync_start - 1) | + I915_WRITE(HSYNC(pipe), + (adjusted_mode->crtc_hsync_start - 1) | ((adjusted_mode->crtc_hsync_end - 1) << 16)); - I915_WRITE(vtot_reg, (adjusted_mode->crtc_vdisplay - 1) | + + I915_WRITE(VTOTAL(pipe), + (adjusted_mode->crtc_vdisplay - 1) | ((adjusted_mode->crtc_vtotal - 1) << 16)); - I915_WRITE(vblank_reg, (adjusted_mode->crtc_vblank_start - 1) | + I915_WRITE(VBLANK(pipe), + (adjusted_mode->crtc_vblank_start - 1) | ((adjusted_mode->crtc_vblank_end - 1) << 16)); - I915_WRITE(vsync_reg, (adjusted_mode->crtc_vsync_start - 1) | + I915_WRITE(VSYNC(pipe), + (adjusted_mode->crtc_vsync_start - 1) | ((adjusted_mode->crtc_vsync_end - 1) << 16)); - /* pipesrc and dspsize control the size that is scaled from, which should - * always be the user's requested size. + + /* pipesrc and dspsize control the size that is scaled from, + * which should always be the user's requested size. */ if (!HAS_PCH_SPLIT(dev)) { - I915_WRITE(dspsize_reg, ((mode->vdisplay - 1) << 16) | - (mode->hdisplay - 1)); - I915_WRITE(dsppos_reg, 0); + I915_WRITE(DSPSIZE(plane), + ((mode->vdisplay - 1) << 16) | + (mode->hdisplay - 1)); + I915_WRITE(DSPPOS(plane), 0); } - I915_WRITE(pipesrc_reg, ((mode->hdisplay - 1) << 16) | (mode->vdisplay - 1)); + I915_WRITE(PIPESRC(pipe), + ((mode->hdisplay - 1) << 16) | (mode->vdisplay - 1)); if (HAS_PCH_SPLIT(dev)) { - I915_WRITE(data_m1_reg, TU_SIZE(m_n.tu) | m_n.gmch_m); - I915_WRITE(data_n1_reg, TU_SIZE(m_n.tu) | m_n.gmch_n); - I915_WRITE(link_m1_reg, m_n.link_m); - I915_WRITE(link_n1_reg, m_n.link_n); + I915_WRITE(PIPE_DATA_M1(pipe), TU_SIZE(m_n.tu) | m_n.gmch_m); + I915_WRITE(PIPE_DATA_N1(pipe), m_n.gmch_n); + I915_WRITE(PIPE_LINK_M1(pipe), m_n.link_m); + I915_WRITE(PIPE_LINK_N1(pipe), m_n.link_n); if (has_edp_encoder) { ironlake_set_pll_edp(crtc, adjusted_mode->clock); } else { /* enable FDI RX PLL too */ - temp = I915_READ(fdi_rx_reg); - I915_WRITE(fdi_rx_reg, temp | FDI_RX_PLL_ENABLE); - I915_READ(fdi_rx_reg); + reg = FDI_RX_CTL(pipe); + temp = I915_READ(reg); + I915_WRITE(reg, temp | FDI_RX_PLL_ENABLE); + + POSTING_READ(reg); udelay(200); /* enable FDI TX PLL too */ - temp = I915_READ(fdi_tx_reg); - I915_WRITE(fdi_tx_reg, temp | FDI_TX_PLL_ENABLE); - I915_READ(fdi_tx_reg); + reg = FDI_TX_CTL(pipe); + temp = I915_READ(reg); + I915_WRITE(reg, temp | FDI_TX_PLL_ENABLE); /* enable FDI RX PCDCLK */ - temp = I915_READ(fdi_rx_reg); - I915_WRITE(fdi_rx_reg, temp | FDI_SEL_PCDCLK); - I915_READ(fdi_rx_reg); + reg = FDI_RX_CTL(pipe); + temp = I915_READ(reg); + I915_WRITE(reg, temp | FDI_PCDCLK); + + POSTING_READ(reg); udelay(200); } } - I915_WRITE(pipeconf_reg, pipeconf); - I915_READ(pipeconf_reg); + I915_WRITE(PIPECONF(pipe), pipeconf); + POSTING_READ(PIPECONF(pipe)); intel_wait_for_vblank(dev, pipe); @@ -4090,9 +4158,8 @@ static int intel_crtc_mode_set(struct drm_crtc *crtc, I915_WRITE(DISP_ARB_CTL, temp | DISP_TILE_SURFACE_SWIZZLING); } - I915_WRITE(dspcntr_reg, dspcntr); + I915_WRITE(DSPCNTR(plane), dspcntr); - /* Flush the plane changes */ ret = intel_pipe_set_base(crtc, x, y, old_fb); intel_update_watermarks(dev); @@ -4185,7 +4252,8 @@ static void i9xx_update_cursor(struct drm_crtc *crtc, u32 base) } /* If no-part of the cursor is visible on the framebuffer, then the GPU may hang... */ -static void intel_crtc_update_cursor(struct drm_crtc *crtc) +static void intel_crtc_update_cursor(struct drm_crtc *crtc, + bool on) { struct drm_device *dev = crtc->dev; struct drm_i915_private *dev_priv = dev->dev_private; @@ -4198,7 +4266,7 @@ static void intel_crtc_update_cursor(struct drm_crtc *crtc) pos = 0; - if (intel_crtc->cursor_on && crtc->fb) { + if (on && crtc->enabled && crtc->fb) { base = intel_crtc->cursor_addr; if (x > (int) crtc->fb->width) base = 0; @@ -4310,7 +4378,7 @@ static int intel_crtc_cursor_set(struct drm_crtc *crtc, addr = obj_priv->phys_obj->handle->busaddr; } - if (!IS_I9XX(dev)) + if (IS_GEN2(dev)) I915_WRITE(CURSIZE, (height << 12) | width); finish: @@ -4330,7 +4398,7 @@ static int intel_crtc_cursor_set(struct drm_crtc *crtc, intel_crtc->cursor_width = width; intel_crtc->cursor_height = height; - intel_crtc_update_cursor(crtc); + intel_crtc_update_cursor(crtc, true); return 0; fail_unpin: @@ -4349,7 +4417,7 @@ static int intel_crtc_cursor_move(struct drm_crtc *crtc, int x, int y) intel_crtc->cursor_x = x; intel_crtc->cursor_y = y; - intel_crtc_update_cursor(crtc); + intel_crtc_update_cursor(crtc, true); return 0; } @@ -4418,7 +4486,7 @@ struct drm_crtc *intel_get_load_detect_pipe(struct intel_encoder *intel_encoder, struct intel_crtc *intel_crtc; struct drm_crtc *possible_crtc; struct drm_crtc *supported_crtc =NULL; - struct drm_encoder *encoder = &intel_encoder->enc; + struct drm_encoder *encoder = &intel_encoder->base; struct drm_crtc *crtc = NULL; struct drm_device *dev = encoder->dev; struct drm_encoder_helper_funcs *encoder_funcs = encoder->helper_private; @@ -4499,7 +4567,7 @@ struct drm_crtc *intel_get_load_detect_pipe(struct intel_encoder *intel_encoder, void intel_release_load_detect_pipe(struct intel_encoder *intel_encoder, struct drm_connector *connector, int dpms_mode) { - struct drm_encoder *encoder = &intel_encoder->enc; + struct drm_encoder *encoder = &intel_encoder->base; struct drm_device *dev = encoder->dev; struct drm_crtc *crtc = encoder->crtc; struct drm_encoder_helper_funcs *encoder_funcs = encoder->helper_private; @@ -4545,7 +4613,7 @@ static int intel_crtc_clock_get(struct drm_device *dev, struct drm_crtc *crtc) clock.m2 = (fp & FP_M2_DIV_MASK) >> FP_M2_DIV_SHIFT; } - if (IS_I9XX(dev)) { + if (!IS_GEN2(dev)) { if (IS_PINEVIEW(dev)) clock.p1 = ffs((dpll & DPLL_FPA01_P1_POST_DIV_MASK_PINEVIEW) >> DPLL_FPA01_P1_POST_DIV_SHIFT_PINEVIEW); @@ -4649,8 +4717,6 @@ static void intel_gpu_idle_timer(unsigned long arg) struct drm_device *dev = (struct drm_device *)arg; drm_i915_private_t *dev_priv = dev->dev_private; - DRM_DEBUG_DRIVER("idle timer fired, downclocking\n"); - dev_priv->busy = false; queue_work(dev_priv->wq, &dev_priv->idle_work); @@ -4664,14 +4730,12 @@ static void intel_crtc_idle_timer(unsigned long arg) struct drm_crtc *crtc = &intel_crtc->base; drm_i915_private_t *dev_priv = crtc->dev->dev_private; - DRM_DEBUG_DRIVER("idle timer fired, downclocking\n"); - intel_crtc->busy = false; queue_work(dev_priv->wq, &dev_priv->idle_work); } -static void intel_increase_pllclock(struct drm_crtc *crtc, bool schedule) +static void intel_increase_pllclock(struct drm_crtc *crtc) { struct drm_device *dev = crtc->dev; drm_i915_private_t *dev_priv = dev->dev_private; @@ -4706,9 +4770,8 @@ static void intel_increase_pllclock(struct drm_crtc *crtc, bool schedule) } /* Schedule downclock */ - if (schedule) - mod_timer(&intel_crtc->idle_timer, jiffies + - msecs_to_jiffies(CRTC_IDLE_TIMEOUT)); + mod_timer(&intel_crtc->idle_timer, jiffies + + msecs_to_jiffies(CRTC_IDLE_TIMEOUT)); } static void intel_decrease_pllclock(struct drm_crtc *crtc) @@ -4844,7 +4907,7 @@ void intel_mark_busy(struct drm_device *dev, struct drm_gem_object *obj) I915_WRITE(FW_BLC_SELF, fw_blc_self | FW_BLC_SELF_EN_MASK); } /* Non-busy -> busy, upclock */ - intel_increase_pllclock(crtc, true); + intel_increase_pllclock(crtc); intel_crtc->busy = true; } else { /* Busy -> busy, put off timer */ @@ -4858,8 +4921,22 @@ void intel_mark_busy(struct drm_device *dev, struct drm_gem_object *obj) static void intel_crtc_destroy(struct drm_crtc *crtc) { struct intel_crtc *intel_crtc = to_intel_crtc(crtc); + struct drm_device *dev = crtc->dev; + struct intel_unpin_work *work; + unsigned long flags; + + spin_lock_irqsave(&dev->event_lock, flags); + work = intel_crtc->unpin_work; + intel_crtc->unpin_work = NULL; + spin_unlock_irqrestore(&dev->event_lock, flags); + + if (work) { + cancel_work_sync(&work->work); + kfree(work); + } drm_crtc_cleanup(crtc); + kfree(intel_crtc); } @@ -4919,7 +4996,7 @@ static void do_intel_finish_page_flip(struct drm_device *dev, /* Initial scanout buffer will have a 0 pending flip count */ if ((atomic_read(&obj_priv->pending_flip) == 0) || atomic_dec_and_test(&obj_priv->pending_flip)) - DRM_WAKEUP(&dev_priv->pending_flip_queue); + wake_up(&dev_priv->pending_flip_queue); schedule_work(&work->work); trace_i915_flip_complete(intel_crtc->plane, work->pending_flip_obj); @@ -5000,7 +5077,7 @@ static int intel_crtc_page_flip(struct drm_crtc *crtc, obj = intel_fb->obj; mutex_lock(&dev->struct_mutex); - ret = intel_pin_and_fence_fb_obj(dev, obj); + ret = intel_pin_and_fence_fb_obj(dev, obj, true); if (ret) goto cleanup_work; @@ -5009,9 +5086,6 @@ static int intel_crtc_page_flip(struct drm_crtc *crtc, drm_gem_object_reference(obj); crtc->fb = fb; - ret = i915_gem_object_flush_write_domain(obj); - if (ret) - goto cleanup_objs; ret = drm_vblank_get(dev, intel_crtc->pipe); if (ret) @@ -5024,14 +5098,16 @@ static int intel_crtc_page_flip(struct drm_crtc *crtc, if (IS_GEN3(dev) || IS_GEN2(dev)) { u32 flip_mask; + /* Can't queue multiple flips, so wait for the previous + * one to finish before executing the next. + */ + BEGIN_LP_RING(2); if (intel_crtc->plane) flip_mask = MI_WAIT_FOR_PLANE_B_FLIP; else flip_mask = MI_WAIT_FOR_PLANE_A_FLIP; - - BEGIN_LP_RING(2); OUT_RING(MI_WAIT_FOR_EVENT | flip_mask); - OUT_RING(0); + OUT_RING(MI_NOOP); ADVANCE_LP_RING(); } @@ -5112,15 +5188,14 @@ cleanup_work: return ret; } -static const struct drm_crtc_helper_funcs intel_helper_funcs = { +static struct drm_crtc_helper_funcs intel_helper_funcs = { .dpms = intel_crtc_dpms, .mode_fixup = intel_crtc_mode_fixup, .mode_set = intel_crtc_mode_set, .mode_set_base = intel_pipe_set_base, .mode_set_base_atomic = intel_pipe_set_base_atomic, - .prepare = intel_crtc_prepare, - .commit = intel_crtc_commit, .load_lut = intel_crtc_load_lut, + .disable = intel_crtc_disable, }; static const struct drm_crtc_funcs intel_crtc_funcs = { @@ -5146,8 +5221,6 @@ static void intel_crtc_init(struct drm_device *dev, int pipe) drm_crtc_init(dev, &intel_crtc->base, &intel_crtc_funcs); drm_mode_crtc_set_gamma_size(&intel_crtc->base, 256); - intel_crtc->pipe = pipe; - intel_crtc->plane = pipe; for (i = 0; i < 256; i++) { intel_crtc->lut_r[i] = i; intel_crtc->lut_g[i] = i; @@ -5157,9 +5230,9 @@ static void intel_crtc_init(struct drm_device *dev, int pipe) /* Swap pipes & planes for FBC on pre-965 */ intel_crtc->pipe = pipe; intel_crtc->plane = pipe; - if (IS_MOBILE(dev) && (IS_I9XX(dev) && !IS_I965G(dev))) { + if (IS_MOBILE(dev) && IS_GEN3(dev)) { DRM_DEBUG_KMS("swapping pipes & planes for FBC\n"); - intel_crtc->plane = ((pipe == 0) ? 1 : 0); + intel_crtc->plane = !pipe; } BUG_ON(pipe >= ARRAY_SIZE(dev_priv->plane_to_crtc_mapping) || @@ -5169,6 +5242,16 @@ static void intel_crtc_init(struct drm_device *dev, int pipe) intel_crtc->cursor_addr = 0; intel_crtc->dpms_mode = -1; + intel_crtc->active = true; /* force the pipe off on setup_init_config */ + + if (HAS_PCH_SPLIT(dev)) { + intel_helper_funcs.prepare = ironlake_crtc_prepare; + intel_helper_funcs.commit = ironlake_crtc_commit; + } else { + intel_helper_funcs.prepare = i9xx_crtc_prepare; + intel_helper_funcs.commit = i9xx_crtc_commit; + } + drm_crtc_helper_add(&intel_crtc->base, &intel_helper_funcs); intel_crtc->busy = false; @@ -5204,38 +5287,25 @@ int intel_get_pipe_from_crtc_id(struct drm_device *dev, void *data, return 0; } -struct drm_crtc *intel_get_crtc_from_pipe(struct drm_device *dev, int pipe) -{ - struct drm_crtc *crtc = NULL; - - list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { - struct intel_crtc *intel_crtc = to_intel_crtc(crtc); - if (intel_crtc->pipe == pipe) - break; - } - return crtc; -} - static int intel_encoder_clones(struct drm_device *dev, int type_mask) { + struct intel_encoder *encoder; int index_mask = 0; - struct drm_encoder *encoder; int entry = 0; - list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) { - struct intel_encoder *intel_encoder = enc_to_intel_encoder(encoder); - if (type_mask & intel_encoder->clone_mask) + list_for_each_entry(encoder, &dev->mode_config.encoder_list, base.head) { + if (type_mask & encoder->clone_mask) index_mask |= (1 << entry); entry++; } + return index_mask; } - static void intel_setup_outputs(struct drm_device *dev) { struct drm_i915_private *dev_priv = dev->dev_private; - struct drm_encoder *encoder; + struct intel_encoder *encoder; bool dpd_is_edp = false; if (IS_MOBILE(dev) && !IS_I830(dev)) @@ -5324,12 +5394,10 @@ static void intel_setup_outputs(struct drm_device *dev) if (SUPPORTS_TV(dev)) intel_tv_init(dev); - list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) { - struct intel_encoder *intel_encoder = enc_to_intel_encoder(encoder); - - encoder->possible_crtcs = intel_encoder->crtc_mask; - encoder->possible_clones = intel_encoder_clones(dev, - intel_encoder->clone_mask); + list_for_each_entry(encoder, &dev->mode_config.encoder_list, base.head) { + encoder->base.possible_crtcs = encoder->crtc_mask; + encoder->base.possible_clones = + intel_encoder_clones(dev, encoder->clone_mask); } } @@ -5363,8 +5431,25 @@ int intel_framebuffer_init(struct drm_device *dev, struct drm_mode_fb_cmd *mode_cmd, struct drm_gem_object *obj) { + struct drm_i915_gem_object *obj_priv = to_intel_bo(obj); int ret; + if (obj_priv->tiling_mode == I915_TILING_Y) + return -EINVAL; + + if (mode_cmd->pitch & 63) + return -EINVAL; + + switch (mode_cmd->bpp) { + case 8: + case 16: + case 24: + case 32: + break; + default: + return -EINVAL; + } + ret = drm_framebuffer_init(dev, &intel_fb->base, &intel_fb_funcs); if (ret) { DRM_ERROR("framebuffer init failed %d\n", ret); @@ -5473,6 +5558,10 @@ void ironlake_enable_drps(struct drm_device *dev) u32 rgvmodectl = I915_READ(MEMMODECTL); u8 fmax, fmin, fstart, vstart; + /* Enable temp reporting */ + I915_WRITE16(PMMISC, I915_READ(PMMISC) | MCPPCE_EN); + I915_WRITE16(TSC1, I915_READ(TSC1) | TSE); + /* 100ms RC evaluation intervals */ I915_WRITE(RCUPEI, 100000); I915_WRITE(RCDNEI, 100000); @@ -5515,7 +5604,7 @@ void ironlake_enable_drps(struct drm_device *dev) rgvmodectl |= MEMMODE_SWMODE_EN; I915_WRITE(MEMMODECTL, rgvmodectl); - if (wait_for((I915_READ(MEMSWCTL) & MEMCTL_CMD_STS) == 0, 1, 0)) + if (wait_for((I915_READ(MEMSWCTL) & MEMCTL_CMD_STS) == 0, 10)) DRM_ERROR("stuck trying to change perf mode\n"); msleep(1); @@ -5714,20 +5803,20 @@ void intel_init_clock_gating(struct drm_device *dev) if (IS_GM45(dev)) dspclk_gate |= DSSUNIT_CLOCK_GATE_DISABLE; I915_WRITE(DSPCLK_GATE_D, dspclk_gate); - } else if (IS_I965GM(dev)) { + } else if (IS_CRESTLINE(dev)) { I915_WRITE(RENCLK_GATE_D1, I965_RCC_CLOCK_GATE_DISABLE); I915_WRITE(RENCLK_GATE_D2, 0); I915_WRITE(DSPCLK_GATE_D, 0); I915_WRITE(RAMCLK_GATE_D, 0); I915_WRITE16(DEUC, 0); - } else if (IS_I965G(dev)) { + } else if (IS_BROADWATER(dev)) { I915_WRITE(RENCLK_GATE_D1, I965_RCZ_CLOCK_GATE_DISABLE | I965_RCC_CLOCK_GATE_DISABLE | I965_RCPB_CLOCK_GATE_DISABLE | I965_ISC_CLOCK_GATE_DISABLE | I965_FBC_CLOCK_GATE_DISABLE); I915_WRITE(RENCLK_GATE_D2, 0); - } else if (IS_I9XX(dev)) { + } else if (IS_GEN3(dev)) { u32 dstate = I915_READ(D_STATE); dstate |= DSTATE_PLL_D3_OFF | DSTATE_GFX_CLOCK_GATING | @@ -5809,7 +5898,7 @@ static void intel_init_display(struct drm_device *dev) dev_priv->display.fbc_enabled = g4x_fbc_enabled; dev_priv->display.enable_fbc = g4x_enable_fbc; dev_priv->display.disable_fbc = g4x_disable_fbc; - } else if (IS_I965GM(dev)) { + } else if (IS_CRESTLINE(dev)) { dev_priv->display.fbc_enabled = i8xx_fbc_enabled; dev_priv->display.enable_fbc = i8xx_enable_fbc; dev_priv->display.disable_fbc = i8xx_disable_fbc; @@ -5869,9 +5958,9 @@ static void intel_init_display(struct drm_device *dev) dev_priv->display.update_wm = pineview_update_wm; } else if (IS_G4X(dev)) dev_priv->display.update_wm = g4x_update_wm; - else if (IS_I965G(dev)) + else if (IS_GEN4(dev)) dev_priv->display.update_wm = i965_update_wm; - else if (IS_I9XX(dev)) { + else if (IS_GEN3(dev)) { dev_priv->display.update_wm = i9xx_update_wm; dev_priv->display.get_fifo_size = i9xx_get_fifo_size; } else if (IS_I85X(dev)) { @@ -5985,24 +6074,24 @@ void intel_modeset_init(struct drm_device *dev) intel_init_display(dev); - if (IS_I965G(dev)) { - dev->mode_config.max_width = 8192; - dev->mode_config.max_height = 8192; - } else if (IS_I9XX(dev)) { + if (IS_GEN2(dev)) { + dev->mode_config.max_width = 2048; + dev->mode_config.max_height = 2048; + } else if (IS_GEN3(dev)) { dev->mode_config.max_width = 4096; dev->mode_config.max_height = 4096; } else { - dev->mode_config.max_width = 2048; - dev->mode_config.max_height = 2048; + dev->mode_config.max_width = 8192; + dev->mode_config.max_height = 8192; } /* set memory base */ - if (IS_I9XX(dev)) - dev->mode_config.fb_base = pci_resource_start(dev->pdev, 2); - else + if (IS_GEN2(dev)) dev->mode_config.fb_base = pci_resource_start(dev->pdev, 0); + else + dev->mode_config.fb_base = pci_resource_start(dev->pdev, 2); - if (IS_MOBILE(dev) || IS_I9XX(dev)) + if (IS_MOBILE(dev) || !IS_GEN2(dev)) dev_priv->num_pipe = 2; else dev_priv->num_pipe = 1; @@ -6038,10 +6127,8 @@ void intel_modeset_cleanup(struct drm_device *dev) struct drm_crtc *crtc; struct intel_crtc *intel_crtc; - mutex_lock(&dev->struct_mutex); - drm_kms_helper_poll_fini(dev); - intel_fbdev_fini(dev); + mutex_lock(&dev->struct_mutex); list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { /* Skip inactive CRTCs */ @@ -6049,12 +6136,9 @@ void intel_modeset_cleanup(struct drm_device *dev) continue; intel_crtc = to_intel_crtc(crtc); - intel_increase_pllclock(crtc, false); - del_timer_sync(&intel_crtc->idle_timer); + intel_increase_pllclock(crtc); } - del_timer_sync(&dev_priv->idle_timer); - if (dev_priv->display.disable_fbc) dev_priv->display.disable_fbc(dev); @@ -6083,33 +6167,36 @@ void intel_modeset_cleanup(struct drm_device *dev) mutex_unlock(&dev->struct_mutex); + /* Disable the irq before mode object teardown, for the irq might + * enqueue unpin/hotplug work. */ + drm_irq_uninstall(dev); + cancel_work_sync(&dev_priv->hotplug_work); + + /* Shut off idle work before the crtcs get freed. */ + list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { + intel_crtc = to_intel_crtc(crtc); + del_timer_sync(&intel_crtc->idle_timer); + } + del_timer_sync(&dev_priv->idle_timer); + cancel_work_sync(&dev_priv->idle_work); + drm_mode_config_cleanup(dev); } - /* * Return which encoder is currently attached for connector. */ -struct drm_encoder *intel_attached_encoder (struct drm_connector *connector) +struct drm_encoder *intel_best_encoder(struct drm_connector *connector) { - struct drm_mode_object *obj; - struct drm_encoder *encoder; - int i; - - for (i = 0; i < DRM_CONNECTOR_MAX_ENCODER; i++) { - if (connector->encoder_ids[i] == 0) - break; - - obj = drm_mode_object_find(connector->dev, - connector->encoder_ids[i], - DRM_MODE_OBJECT_ENCODER); - if (!obj) - continue; + return &intel_attached_encoder(connector)->base; +} - encoder = obj_to_encoder(obj); - return encoder; - } - return NULL; +void intel_connector_attach_encoder(struct intel_connector *connector, + struct intel_encoder *encoder) +{ + connector->encoder = encoder; + drm_mode_connector_attach_encoder(&connector->base, + &encoder->base); } /* diff --git a/drivers/gpu/drm/i915/intel_dp.c b/drivers/gpu/drm/i915/intel_dp.c index 1a51ee07de3..152d94507b7 100644 --- a/drivers/gpu/drm/i915/intel_dp.c +++ b/drivers/gpu/drm/i915/intel_dp.c @@ -58,14 +58,23 @@ struct intel_dp { struct i2c_adapter adapter; struct i2c_algo_dp_aux_data algo; bool is_pch_edp; + uint8_t train_set[4]; + uint8_t link_status[DP_LINK_STATUS_SIZE]; }; static struct intel_dp *enc_to_intel_dp(struct drm_encoder *encoder) { - return container_of(enc_to_intel_encoder(encoder), struct intel_dp, base); + return container_of(encoder, struct intel_dp, base.base); +} + +static struct intel_dp *intel_attached_dp(struct drm_connector *connector) +{ + return container_of(intel_attached_encoder(connector), + struct intel_dp, base); } -static void intel_dp_link_train(struct intel_dp *intel_dp); +static void intel_dp_start_link_train(struct intel_dp *intel_dp); +static void intel_dp_complete_link_train(struct intel_dp *intel_dp); static void intel_dp_link_down(struct intel_dp *intel_dp); void @@ -130,7 +139,7 @@ intel_dp_link_required(struct drm_device *dev, struct intel_dp *intel_dp, int pi struct drm_i915_private *dev_priv = dev->dev_private; if (IS_eDP(intel_dp) || IS_PCH_eDP(intel_dp)) - return (pixel_clock * dev_priv->edp_bpp) / 8; + return (pixel_clock * dev_priv->edp.bpp + 7) / 8; else return pixel_clock * 3; } @@ -145,8 +154,7 @@ static int intel_dp_mode_valid(struct drm_connector *connector, struct drm_display_mode *mode) { - struct drm_encoder *encoder = intel_attached_encoder(connector); - struct intel_dp *intel_dp = enc_to_intel_dp(encoder); + struct intel_dp *intel_dp = intel_attached_dp(connector); struct drm_device *dev = connector->dev; struct drm_i915_private *dev_priv = dev->dev_private; int max_link_clock = intel_dp_link_clock(intel_dp_max_link_bw(intel_dp)); @@ -233,7 +241,7 @@ intel_dp_aux_ch(struct intel_dp *intel_dp, uint8_t *recv, int recv_size) { uint32_t output_reg = intel_dp->output_reg; - struct drm_device *dev = intel_dp->base.enc.dev; + struct drm_device *dev = intel_dp->base.base.dev; struct drm_i915_private *dev_priv = dev->dev_private; uint32_t ch_ctl = output_reg + 0x10; uint32_t ch_data = ch_ctl + 4; @@ -246,8 +254,11 @@ intel_dp_aux_ch(struct intel_dp *intel_dp, /* The clock divider is based off the hrawclk, * and would like to run at 2MHz. So, take the * hrawclk value and divide by 2 and use that + * + * Note that PCH attached eDP panels should use a 125MHz input + * clock divider. */ - if (IS_eDP(intel_dp)) { + if (IS_eDP(intel_dp) && !IS_PCH_eDP(intel_dp)) { if (IS_GEN6(dev)) aux_clock_divider = 200; /* SNB eDP input clock at 400Mhz */ else @@ -642,7 +653,7 @@ intel_dp_set_m_n(struct drm_crtc *crtc, struct drm_display_mode *mode, if (intel_dp->base.type == INTEL_OUTPUT_DISPLAYPORT) { lane_count = intel_dp->lane_count; if (IS_PCH_eDP(intel_dp)) - bpp = dev_priv->edp_bpp; + bpp = dev_priv->edp.bpp; break; } } @@ -698,7 +709,7 @@ intel_dp_mode_set(struct drm_encoder *encoder, struct drm_display_mode *mode, { struct drm_device *dev = encoder->dev; struct intel_dp *intel_dp = enc_to_intel_dp(encoder); - struct drm_crtc *crtc = intel_dp->base.enc.crtc; + struct drm_crtc *crtc = intel_dp->base.base.crtc; struct intel_crtc *intel_crtc = to_intel_crtc(crtc); intel_dp->DP = (DP_VOLTAGE_0_4 | @@ -754,13 +765,14 @@ intel_dp_mode_set(struct drm_encoder *encoder, struct drm_display_mode *mode, } } -static void ironlake_edp_panel_on (struct drm_device *dev) +/* Returns true if the panel was already on when called */ +static bool ironlake_edp_panel_on (struct drm_device *dev) { struct drm_i915_private *dev_priv = dev->dev_private; u32 pp; if (I915_READ(PCH_PP_STATUS) & PP_ON) - return; + return true; pp = I915_READ(PCH_PP_CONTROL); @@ -769,17 +781,24 @@ static void ironlake_edp_panel_on (struct drm_device *dev) I915_WRITE(PCH_PP_CONTROL, pp); POSTING_READ(PCH_PP_CONTROL); - pp |= PANEL_UNLOCK_REGS | POWER_TARGET_ON; + pp |= POWER_TARGET_ON; I915_WRITE(PCH_PP_CONTROL, pp); - if (wait_for(I915_READ(PCH_PP_STATUS) & PP_ON, 5000, 10)) + /* Ouch. We need to wait here for some panels, like Dell e6510 + * https://bugs.freedesktop.org/show_bug.cgi?id=29278i + */ + msleep(300); + + if (wait_for(I915_READ(PCH_PP_STATUS) & PP_ON, 5000)) DRM_ERROR("panel on wait timed out: 0x%08x\n", I915_READ(PCH_PP_STATUS)); - pp &= ~(PANEL_UNLOCK_REGS | EDP_FORCE_VDD); + pp &= ~(PANEL_UNLOCK_REGS); pp |= PANEL_POWER_RESET; /* restore panel reset bit */ I915_WRITE(PCH_PP_CONTROL, pp); POSTING_READ(PCH_PP_CONTROL); + + return false; } static void ironlake_edp_panel_off (struct drm_device *dev) @@ -797,14 +816,43 @@ static void ironlake_edp_panel_off (struct drm_device *dev) pp &= ~POWER_TARGET_ON; I915_WRITE(PCH_PP_CONTROL, pp); - if (wait_for((I915_READ(PCH_PP_STATUS) & PP_ON) == 0, 5000, 10)) + if (wait_for((I915_READ(PCH_PP_STATUS) & PP_ON) == 0, 5000)) DRM_ERROR("panel off wait timed out: 0x%08x\n", I915_READ(PCH_PP_STATUS)); /* Make sure VDD is enabled so DP AUX will work */ - pp |= EDP_FORCE_VDD | PANEL_POWER_RESET; /* restore panel reset bit */ + pp |= PANEL_POWER_RESET; /* restore panel reset bit */ I915_WRITE(PCH_PP_CONTROL, pp); POSTING_READ(PCH_PP_CONTROL); + + /* Ouch. We need to wait here for some panels, like Dell e6510 + * https://bugs.freedesktop.org/show_bug.cgi?id=29278i + */ + msleep(300); +} + +static void ironlake_edp_panel_vdd_on(struct drm_device *dev) +{ + struct drm_i915_private *dev_priv = dev->dev_private; + u32 pp; + + pp = I915_READ(PCH_PP_CONTROL); + pp |= EDP_FORCE_VDD; + I915_WRITE(PCH_PP_CONTROL, pp); + POSTING_READ(PCH_PP_CONTROL); + msleep(300); +} + +static void ironlake_edp_panel_vdd_off(struct drm_device *dev) +{ + struct drm_i915_private *dev_priv = dev->dev_private; + u32 pp; + + pp = I915_READ(PCH_PP_CONTROL); + pp &= ~EDP_FORCE_VDD; + I915_WRITE(PCH_PP_CONTROL, pp); + POSTING_READ(PCH_PP_CONTROL); + msleep(300); } static void ironlake_edp_backlight_on (struct drm_device *dev) @@ -850,6 +898,7 @@ static void ironlake_edp_pll_off(struct drm_encoder *encoder) dpa_ctl = I915_READ(DP_A); dpa_ctl |= DP_PLL_ENABLE; I915_WRITE(DP_A, dpa_ctl); + POSTING_READ(DP_A); udelay(200); } @@ -860,9 +909,10 @@ static void intel_dp_prepare(struct drm_encoder *encoder) struct drm_i915_private *dev_priv = dev->dev_private; uint32_t dp_reg = I915_READ(intel_dp->output_reg); - if (IS_eDP(intel_dp)) { + if (IS_eDP(intel_dp) || IS_PCH_eDP(intel_dp)) { + ironlake_edp_panel_off(dev); ironlake_edp_backlight_off(dev); - ironlake_edp_panel_on(dev); + ironlake_edp_panel_vdd_on(dev); ironlake_edp_pll_on(encoder); } if (dp_reg & DP_PORT_EN) @@ -873,14 +923,17 @@ static void intel_dp_commit(struct drm_encoder *encoder) { struct intel_dp *intel_dp = enc_to_intel_dp(encoder); struct drm_device *dev = encoder->dev; - struct drm_i915_private *dev_priv = dev->dev_private; - uint32_t dp_reg = I915_READ(intel_dp->output_reg); - if (!(dp_reg & DP_PORT_EN)) { - intel_dp_link_train(intel_dp); - } + intel_dp_start_link_train(intel_dp); + + if (IS_eDP(intel_dp) || IS_PCH_eDP(intel_dp)) + ironlake_edp_panel_on(dev); + + intel_dp_complete_link_train(intel_dp); + if (IS_eDP(intel_dp) || IS_PCH_eDP(intel_dp)) ironlake_edp_backlight_on(dev); + intel_dp->dpms_mode = DRM_MODE_DPMS_ON; } static void @@ -902,9 +955,10 @@ intel_dp_dpms(struct drm_encoder *encoder, int mode) ironlake_edp_pll_off(encoder); } else { if (!(dp_reg & DP_PORT_EN)) { + intel_dp_start_link_train(intel_dp); if (IS_eDP(intel_dp) || IS_PCH_eDP(intel_dp)) ironlake_edp_panel_on(dev); - intel_dp_link_train(intel_dp); + intel_dp_complete_link_train(intel_dp); if (IS_eDP(intel_dp) || IS_PCH_eDP(intel_dp)) ironlake_edp_backlight_on(dev); } @@ -917,14 +971,13 @@ intel_dp_dpms(struct drm_encoder *encoder, int mode) * link status information */ static bool -intel_dp_get_link_status(struct intel_dp *intel_dp, - uint8_t link_status[DP_LINK_STATUS_SIZE]) +intel_dp_get_link_status(struct intel_dp *intel_dp) { int ret; ret = intel_dp_aux_native_read(intel_dp, DP_LANE0_1_STATUS, - link_status, DP_LINK_STATUS_SIZE); + intel_dp->link_status, DP_LINK_STATUS_SIZE); if (ret != DP_LINK_STATUS_SIZE) return false; return true; @@ -999,18 +1052,15 @@ intel_dp_pre_emphasis_max(uint8_t voltage_swing) } static void -intel_get_adjust_train(struct intel_dp *intel_dp, - uint8_t link_status[DP_LINK_STATUS_SIZE], - int lane_count, - uint8_t train_set[4]) +intel_get_adjust_train(struct intel_dp *intel_dp) { uint8_t v = 0; uint8_t p = 0; int lane; - for (lane = 0; lane < lane_count; lane++) { - uint8_t this_v = intel_get_adjust_request_voltage(link_status, lane); - uint8_t this_p = intel_get_adjust_request_pre_emphasis(link_status, lane); + for (lane = 0; lane < intel_dp->lane_count; lane++) { + uint8_t this_v = intel_get_adjust_request_voltage(intel_dp->link_status, lane); + uint8_t this_p = intel_get_adjust_request_pre_emphasis(intel_dp->link_status, lane); if (this_v > v) v = this_v; @@ -1025,7 +1075,7 @@ intel_get_adjust_train(struct intel_dp *intel_dp, p = intel_dp_pre_emphasis_max(v) | DP_TRAIN_MAX_PRE_EMPHASIS_REACHED; for (lane = 0; lane < 4; lane++) - train_set[lane] = v | p; + intel_dp->train_set[lane] = v | p; } static uint32_t @@ -1116,18 +1166,18 @@ intel_clock_recovery_ok(uint8_t link_status[DP_LINK_STATUS_SIZE], int lane_count DP_LANE_CHANNEL_EQ_DONE|\ DP_LANE_SYMBOL_LOCKED) static bool -intel_channel_eq_ok(uint8_t link_status[DP_LINK_STATUS_SIZE], int lane_count) +intel_channel_eq_ok(struct intel_dp *intel_dp) { uint8_t lane_align; uint8_t lane_status; int lane; - lane_align = intel_dp_link_status(link_status, + lane_align = intel_dp_link_status(intel_dp->link_status, DP_LANE_ALIGN_STATUS_UPDATED); if ((lane_align & DP_INTERLANE_ALIGN_DONE) == 0) return false; - for (lane = 0; lane < lane_count; lane++) { - lane_status = intel_get_lane_status(link_status, lane); + for (lane = 0; lane < intel_dp->lane_count; lane++) { + lane_status = intel_get_lane_status(intel_dp->link_status, lane); if ((lane_status & CHANNEL_EQ_BITS) != CHANNEL_EQ_BITS) return false; } @@ -1137,48 +1187,47 @@ intel_channel_eq_ok(uint8_t link_status[DP_LINK_STATUS_SIZE], int lane_count) static bool intel_dp_set_link_train(struct intel_dp *intel_dp, uint32_t dp_reg_value, - uint8_t dp_train_pat, - uint8_t train_set[4], - bool first) + uint8_t dp_train_pat) { - struct drm_device *dev = intel_dp->base.enc.dev; + struct drm_device *dev = intel_dp->base.base.dev; struct drm_i915_private *dev_priv = dev->dev_private; - struct intel_crtc *intel_crtc = to_intel_crtc(intel_dp->base.enc.crtc); int ret; I915_WRITE(intel_dp->output_reg, dp_reg_value); POSTING_READ(intel_dp->output_reg); - if (first) - intel_wait_for_vblank(dev, intel_crtc->pipe); intel_dp_aux_native_write_1(intel_dp, DP_TRAINING_PATTERN_SET, dp_train_pat); ret = intel_dp_aux_native_write(intel_dp, - DP_TRAINING_LANE0_SET, train_set, 4); + DP_TRAINING_LANE0_SET, + intel_dp->train_set, 4); if (ret != 4) return false; return true; } +/* Enable corresponding port and start training pattern 1 */ static void -intel_dp_link_train(struct intel_dp *intel_dp) +intel_dp_start_link_train(struct intel_dp *intel_dp) { - struct drm_device *dev = intel_dp->base.enc.dev; + struct drm_device *dev = intel_dp->base.base.dev; struct drm_i915_private *dev_priv = dev->dev_private; - uint8_t train_set[4]; - uint8_t link_status[DP_LINK_STATUS_SIZE]; + struct intel_crtc *intel_crtc = to_intel_crtc(intel_dp->base.base.crtc); int i; uint8_t voltage; bool clock_recovery = false; - bool channel_eq = false; - bool first = true; int tries; u32 reg; uint32_t DP = intel_dp->DP; + /* Enable output, wait for it to become active */ + I915_WRITE(intel_dp->output_reg, intel_dp->DP); + POSTING_READ(intel_dp->output_reg); + intel_wait_for_vblank(dev, intel_crtc->pipe); + /* Write the link configuration data */ intel_dp_aux_native_write(intel_dp, DP_LINK_BW_SET, intel_dp->link_configuration, @@ -1189,18 +1238,18 @@ intel_dp_link_train(struct intel_dp *intel_dp) DP &= ~DP_LINK_TRAIN_MASK_CPT; else DP &= ~DP_LINK_TRAIN_MASK; - memset(train_set, 0, 4); + memset(intel_dp->train_set, 0, 4); voltage = 0xff; tries = 0; clock_recovery = false; for (;;) { - /* Use train_set[0] to set the voltage and pre emphasis values */ + /* Use intel_dp->train_set[0] to set the voltage and pre emphasis values */ uint32_t signal_levels; if (IS_GEN6(dev) && IS_eDP(intel_dp)) { - signal_levels = intel_gen6_edp_signal_levels(train_set[0]); + 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(train_set[0], intel_dp->lane_count); + signal_levels = intel_dp_signal_levels(intel_dp->train_set[0], intel_dp->lane_count); DP = (DP & ~(DP_VOLTAGE_MASK|DP_PRE_EMPHASIS_MASK)) | signal_levels; } @@ -1210,52 +1259,64 @@ intel_dp_link_train(struct intel_dp *intel_dp) reg = DP | DP_LINK_TRAIN_PAT_1; if (!intel_dp_set_link_train(intel_dp, reg, - DP_TRAINING_PATTERN_1, train_set, first)) + DP_TRAINING_PATTERN_1)) break; - first = false; /* Set training pattern 1 */ udelay(100); - if (!intel_dp_get_link_status(intel_dp, link_status)) + if (!intel_dp_get_link_status(intel_dp)) break; - if (intel_clock_recovery_ok(link_status, intel_dp->lane_count)) { + if (intel_clock_recovery_ok(intel_dp->link_status, intel_dp->lane_count)) { clock_recovery = true; break; } /* Check to see if we've tried the max voltage */ for (i = 0; i < intel_dp->lane_count; i++) - if ((train_set[i] & DP_TRAIN_MAX_SWING_REACHED) == 0) + if ((intel_dp->train_set[i] & DP_TRAIN_MAX_SWING_REACHED) == 0) break; if (i == intel_dp->lane_count) break; /* Check to see if we've tried the same voltage 5 times */ - if ((train_set[0] & DP_TRAIN_VOLTAGE_SWING_MASK) == voltage) { + if ((intel_dp->train_set[0] & DP_TRAIN_VOLTAGE_SWING_MASK) == voltage) { ++tries; if (tries == 5) break; } else tries = 0; - voltage = train_set[0] & DP_TRAIN_VOLTAGE_SWING_MASK; + voltage = intel_dp->train_set[0] & DP_TRAIN_VOLTAGE_SWING_MASK; - /* Compute new train_set as requested by target */ - intel_get_adjust_train(intel_dp, link_status, intel_dp->lane_count, train_set); + /* Compute new intel_dp->train_set as requested by target */ + intel_get_adjust_train(intel_dp); } + intel_dp->DP = DP; +} + +static void +intel_dp_complete_link_train(struct intel_dp *intel_dp) +{ + struct drm_device *dev = intel_dp->base.base.dev; + struct drm_i915_private *dev_priv = dev->dev_private; + bool channel_eq = false; + int tries; + u32 reg; + uint32_t DP = intel_dp->DP; + /* channel equalization */ tries = 0; channel_eq = false; for (;;) { - /* Use train_set[0] to set the voltage and pre emphasis values */ + /* Use intel_dp->train_set[0] to set the voltage and pre emphasis values */ uint32_t signal_levels; if (IS_GEN6(dev) && IS_eDP(intel_dp)) { - signal_levels = intel_gen6_edp_signal_levels(train_set[0]); + 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(train_set[0], intel_dp->lane_count); + signal_levels = intel_dp_signal_levels(intel_dp->train_set[0], intel_dp->lane_count); DP = (DP & ~(DP_VOLTAGE_MASK|DP_PRE_EMPHASIS_MASK)) | signal_levels; } @@ -1266,15 +1327,14 @@ intel_dp_link_train(struct intel_dp *intel_dp) /* channel eq pattern */ if (!intel_dp_set_link_train(intel_dp, reg, - DP_TRAINING_PATTERN_2, train_set, - false)) + DP_TRAINING_PATTERN_2)) break; udelay(400); - if (!intel_dp_get_link_status(intel_dp, link_status)) + if (!intel_dp_get_link_status(intel_dp)) break; - if (intel_channel_eq_ok(link_status, intel_dp->lane_count)) { + if (intel_channel_eq_ok(intel_dp)) { channel_eq = true; break; } @@ -1283,8 +1343,8 @@ intel_dp_link_train(struct intel_dp *intel_dp) if (tries > 5) break; - /* Compute new train_set as requested by target */ - intel_get_adjust_train(intel_dp, link_status, intel_dp->lane_count, train_set); + /* Compute new intel_dp->train_set as requested by target */ + intel_get_adjust_train(intel_dp); ++tries; } @@ -1302,7 +1362,7 @@ intel_dp_link_train(struct intel_dp *intel_dp) static void intel_dp_link_down(struct intel_dp *intel_dp) { - struct drm_device *dev = intel_dp->base.enc.dev; + struct drm_device *dev = intel_dp->base.base.dev; struct drm_i915_private *dev_priv = dev->dev_private; uint32_t DP = intel_dp->DP; @@ -1318,14 +1378,13 @@ intel_dp_link_down(struct intel_dp *intel_dp) if (HAS_PCH_CPT(dev) && !IS_eDP(intel_dp)) { DP &= ~DP_LINK_TRAIN_MASK_CPT; I915_WRITE(intel_dp->output_reg, DP | DP_LINK_TRAIN_PAT_IDLE_CPT); - POSTING_READ(intel_dp->output_reg); } else { DP &= ~DP_LINK_TRAIN_MASK; I915_WRITE(intel_dp->output_reg, DP | DP_LINK_TRAIN_PAT_IDLE); - POSTING_READ(intel_dp->output_reg); } + POSTING_READ(intel_dp->output_reg); - udelay(17000); + msleep(17); if (IS_eDP(intel_dp)) DP |= DP_LINK_TRAIN_OFF; @@ -1345,27 +1404,29 @@ intel_dp_link_down(struct intel_dp *intel_dp) static void intel_dp_check_link_status(struct intel_dp *intel_dp) { - uint8_t link_status[DP_LINK_STATUS_SIZE]; - - if (!intel_dp->base.enc.crtc) + if (!intel_dp->base.base.crtc) return; - if (!intel_dp_get_link_status(intel_dp, link_status)) { + if (!intel_dp_get_link_status(intel_dp)) { intel_dp_link_down(intel_dp); return; } - if (!intel_channel_eq_ok(link_status, intel_dp->lane_count)) - intel_dp_link_train(intel_dp); + if (!intel_channel_eq_ok(intel_dp)) { + intel_dp_start_link_train(intel_dp); + intel_dp_complete_link_train(intel_dp); + } } static enum drm_connector_status ironlake_dp_detect(struct drm_connector *connector) { - struct drm_encoder *encoder = intel_attached_encoder(connector); - struct intel_dp *intel_dp = enc_to_intel_dp(encoder); + struct intel_dp *intel_dp = intel_attached_dp(connector); enum drm_connector_status status; + /* Panel needs power for AUX to work */ + if (IS_eDP(intel_dp) || IS_PCH_eDP(intel_dp)) + ironlake_edp_panel_vdd_on(connector->dev); status = connector_status_disconnected; if (intel_dp_aux_native_read(intel_dp, 0x000, intel_dp->dpcd, @@ -1376,6 +1437,8 @@ ironlake_dp_detect(struct drm_connector *connector) } DRM_DEBUG_KMS("DPCD: %hx%hx%hx%hx\n", intel_dp->dpcd[0], intel_dp->dpcd[1], intel_dp->dpcd[2], intel_dp->dpcd[3]); + if (IS_eDP(intel_dp) || IS_PCH_eDP(intel_dp)) + ironlake_edp_panel_vdd_off(connector->dev); return status; } @@ -1388,9 +1451,8 @@ ironlake_dp_detect(struct drm_connector *connector) static enum drm_connector_status intel_dp_detect(struct drm_connector *connector, bool force) { - struct drm_encoder *encoder = intel_attached_encoder(connector); - struct intel_dp *intel_dp = enc_to_intel_dp(encoder); - struct drm_device *dev = intel_dp->base.enc.dev; + struct intel_dp *intel_dp = intel_attached_dp(connector); + struct drm_device *dev = intel_dp->base.base.dev; struct drm_i915_private *dev_priv = dev->dev_private; uint32_t temp, bit; enum drm_connector_status status; @@ -1432,16 +1494,15 @@ intel_dp_detect(struct drm_connector *connector, bool force) static int intel_dp_get_modes(struct drm_connector *connector) { - struct drm_encoder *encoder = intel_attached_encoder(connector); - struct intel_dp *intel_dp = enc_to_intel_dp(encoder); - struct drm_device *dev = intel_dp->base.enc.dev; + struct intel_dp *intel_dp = intel_attached_dp(connector); + struct drm_device *dev = intel_dp->base.base.dev; struct drm_i915_private *dev_priv = dev->dev_private; int ret; /* We should parse the EDID data and find out if it has an audio sink */ - ret = intel_ddc_get_modes(connector, intel_dp->base.ddc_bus); + ret = intel_ddc_get_modes(connector, &intel_dp->adapter); if (ret) { if ((IS_eDP(intel_dp) || IS_PCH_eDP(intel_dp)) && !dev_priv->panel_fixed_mode) { @@ -1479,6 +1540,15 @@ intel_dp_destroy (struct drm_connector *connector) kfree(connector); } +static void intel_dp_encoder_destroy(struct drm_encoder *encoder) +{ + struct intel_dp *intel_dp = enc_to_intel_dp(encoder); + + i2c_del_adapter(&intel_dp->adapter); + drm_encoder_cleanup(encoder); + kfree(intel_dp); +} + static const struct drm_encoder_helper_funcs intel_dp_helper_funcs = { .dpms = intel_dp_dpms, .mode_fixup = intel_dp_mode_fixup, @@ -1497,14 +1567,14 @@ static const struct drm_connector_funcs intel_dp_connector_funcs = { static const struct drm_connector_helper_funcs intel_dp_connector_helper_funcs = { .get_modes = intel_dp_get_modes, .mode_valid = intel_dp_mode_valid, - .best_encoder = intel_attached_encoder, + .best_encoder = intel_best_encoder, }; static const struct drm_encoder_funcs intel_dp_enc_funcs = { - .destroy = intel_encoder_destroy, + .destroy = intel_dp_encoder_destroy, }; -void +static void intel_dp_hot_plug(struct intel_encoder *intel_encoder) { struct intel_dp *intel_dp = container_of(intel_encoder, struct intel_dp, base); @@ -1613,12 +1683,11 @@ intel_dp_init(struct drm_device *dev, int output_reg) intel_dp->has_audio = false; intel_dp->dpms_mode = DRM_MODE_DPMS_ON; - drm_encoder_init(dev, &intel_encoder->enc, &intel_dp_enc_funcs, + drm_encoder_init(dev, &intel_encoder->base, &intel_dp_enc_funcs, DRM_MODE_ENCODER_TMDS); - drm_encoder_helper_add(&intel_encoder->enc, &intel_dp_helper_funcs); + drm_encoder_helper_add(&intel_encoder->base, &intel_dp_helper_funcs); - drm_mode_connector_attach_encoder(&intel_connector->base, - &intel_encoder->enc); + intel_connector_attach_encoder(intel_connector, intel_encoder); drm_sysfs_connector_add(connector); /* Set up the DDC bus. */ @@ -1648,7 +1717,6 @@ intel_dp_init(struct drm_device *dev, int output_reg) intel_dp_i2c_init(intel_dp, intel_connector, name); - intel_encoder->ddc_bus = &intel_dp->adapter; intel_encoder->hot_plug = intel_dp_hot_plug; if (output_reg == DP_A || IS_PCH_eDP(intel_dp)) { diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h index ad312ca6b3e..40e99bf27ff 100644 --- a/drivers/gpu/drm/i915/intel_drv.h +++ b/drivers/gpu/drm/i915/intel_drv.h @@ -26,14 +26,12 @@ #define __INTEL_DRV_H__ #include <linux/i2c.h> -#include <linux/i2c-id.h> -#include <linux/i2c-algo-bit.h> #include "i915_drv.h" #include "drm_crtc.h" - #include "drm_crtc_helper.h" +#include "drm_fb_helper.h" -#define wait_for(COND, MS, W) ({ \ +#define _wait_for(COND, MS, W) ({ \ unsigned long timeout__ = jiffies + msecs_to_jiffies(MS); \ int ret__ = 0; \ while (! (COND)) { \ @@ -41,11 +39,24 @@ ret__ = -ETIMEDOUT; \ break; \ } \ - if (W) msleep(W); \ + if (W && !in_dbg_master()) msleep(W); \ } \ ret__; \ }) +#define wait_for(COND, MS) _wait_for(COND, MS, 1) +#define wait_for_atomic(COND, MS) _wait_for(COND, MS, 0) + +#define MSLEEP(x) do { \ + if (in_dbg_master()) \ + mdelay(x); \ + else \ + msleep(x); \ +} while(0) + +#define KHz(x) (1000*x) +#define MHz(x) KHz(1000*x) + /* * Display related stuff */ @@ -96,24 +107,39 @@ #define INTEL_DVO_CHIP_TMDS 2 #define INTEL_DVO_CHIP_TVOUT 4 -struct intel_i2c_chan { - struct drm_device *drm_dev; /* for getting at dev. private (mmio etc.) */ - u32 reg; /* GPIO reg */ - struct i2c_adapter adapter; - struct i2c_algo_bit_data algo; -}; +/* drm_display_mode->private_flags */ +#define INTEL_MODE_PIXEL_MULTIPLIER_SHIFT (0x0) +#define INTEL_MODE_PIXEL_MULTIPLIER_MASK (0xf << INTEL_MODE_PIXEL_MULTIPLIER_SHIFT) + +static inline void +intel_mode_set_pixel_multiplier(struct drm_display_mode *mode, + int multiplier) +{ + mode->clock *= multiplier; + mode->private_flags |= multiplier; +} + +static inline int +intel_mode_get_pixel_multiplier(const struct drm_display_mode *mode) +{ + return (mode->private_flags & INTEL_MODE_PIXEL_MULTIPLIER_MASK) >> INTEL_MODE_PIXEL_MULTIPLIER_SHIFT; +} struct intel_framebuffer { struct drm_framebuffer base; struct drm_gem_object *obj; }; +struct intel_fbdev { + struct drm_fb_helper helper; + struct intel_framebuffer ifb; + struct list_head fbdev_list; + struct drm_display_mode *our_mode; +}; struct intel_encoder { - struct drm_encoder enc; + struct drm_encoder base; int type; - struct i2c_adapter *i2c_bus; - struct i2c_adapter *ddc_bus; bool load_detect_temp; bool needs_tv_clock; void (*hot_plug)(struct intel_encoder *); @@ -123,32 +149,7 @@ struct intel_encoder { struct intel_connector { struct drm_connector base; -}; - -struct intel_crtc; -struct intel_overlay { - struct drm_device *dev; - struct intel_crtc *crtc; - struct drm_i915_gem_object *vid_bo; - struct drm_i915_gem_object *old_vid_bo; - int active; - int pfit_active; - u32 pfit_vscale_ratio; /* shifted-point number, (1<<12) == 1.0 */ - u32 color_key; - u32 brightness, contrast, saturation; - u32 old_xscale, old_yscale; - /* register access */ - u32 flip_addr; - struct drm_i915_gem_object *reg_bo; - void *virt_addr; - /* flip handling */ - uint32_t last_flip_req; - int hw_wedged; -#define HW_WEDGED 1 -#define NEEDS_WAIT_FOR_FLIP 2 -#define RELEASE_OLD_VID 3 -#define SWITCH_OFF_STAGE_1 4 -#define SWITCH_OFF_STAGE_2 5 + struct intel_encoder *encoder; }; struct intel_crtc { @@ -157,6 +158,7 @@ struct intel_crtc { enum plane plane; u8 lut_r[256], lut_g[256], lut_b[256]; int dpms_mode; + bool active; /* is the crtc on? independent of the dpms mode */ bool busy; /* is scanout buffer being updated frequently? */ struct timer_list idle_timer; bool lowfreq_avail; @@ -168,14 +170,21 @@ struct intel_crtc { uint32_t cursor_addr; int16_t cursor_x, cursor_y; int16_t cursor_width, cursor_height; - bool cursor_visible, cursor_on; + bool cursor_visible; }; #define to_intel_crtc(x) container_of(x, struct intel_crtc, base) #define to_intel_connector(x) container_of(x, struct intel_connector, base) -#define enc_to_intel_encoder(x) container_of(x, struct intel_encoder, enc) +#define to_intel_encoder(x) container_of(x, struct intel_encoder, base) #define to_intel_framebuffer(x) container_of(x, struct intel_framebuffer, base) +static inline struct drm_crtc * +intel_get_crtc_for_pipe(struct drm_device *dev, int pipe) +{ + struct drm_i915_private *dev_priv = dev->dev_private; + return dev_priv->pipe_to_crtc_mapping[pipe]; +} + struct intel_unpin_work { struct work_struct work; struct drm_device *dev; @@ -186,13 +195,8 @@ struct intel_unpin_work { bool enable_stall_check; }; -struct i2c_adapter *intel_i2c_create(struct drm_device *dev, const u32 reg, - const char *name); -void intel_i2c_destroy(struct i2c_adapter *adapter); int intel_ddc_get_modes(struct drm_connector *c, struct i2c_adapter *adapter); -extern bool intel_ddc_probe(struct intel_encoder *intel_encoder); -void intel_i2c_quirk_set(struct drm_device *dev, bool enable); -void intel_i2c_reset_gmbus(struct drm_device *dev); +extern bool intel_ddc_probe(struct intel_encoder *intel_encoder, int ddc_bus); extern void intel_crt_init(struct drm_device *dev); extern void intel_hdmi_init(struct drm_device *dev, int sdvox_reg); @@ -209,29 +213,37 @@ extern bool intel_pch_has_edp(struct drm_crtc *crtc); extern bool intel_dpd_is_edp(struct drm_device *dev); extern void intel_edp_link_config (struct intel_encoder *, int *, int *); - +/* intel_panel.c */ extern void intel_fixed_panel_mode(struct drm_display_mode *fixed_mode, struct drm_display_mode *adjusted_mode); extern void intel_pch_panel_fitting(struct drm_device *dev, int fitting_mode, struct drm_display_mode *mode, struct drm_display_mode *adjusted_mode); +extern u32 intel_panel_get_max_backlight(struct drm_device *dev); +extern u32 intel_panel_get_backlight(struct drm_device *dev); +extern void intel_panel_set_backlight(struct drm_device *dev, u32 level); -extern int intel_panel_fitter_pipe (struct drm_device *dev); extern void intel_crtc_load_lut(struct drm_crtc *crtc); extern void intel_encoder_prepare (struct drm_encoder *encoder); extern void intel_encoder_commit (struct drm_encoder *encoder); extern void intel_encoder_destroy(struct drm_encoder *encoder); -extern struct drm_encoder *intel_attached_encoder(struct drm_connector *connector); +static inline struct intel_encoder *intel_attached_encoder(struct drm_connector *connector) +{ + return to_intel_connector(connector)->encoder; +} + +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); extern struct drm_display_mode *intel_crtc_mode_get(struct drm_device *dev, struct drm_crtc *crtc); int intel_get_pipe_from_crtc_id(struct drm_device *dev, void *data, struct drm_file *file_priv); -extern void intel_wait_for_vblank_off(struct drm_device *dev, int pipe); extern void intel_wait_for_vblank(struct drm_device *dev, int pipe); -extern struct drm_crtc *intel_get_crtc_from_pipe(struct drm_device *dev, int pipe); +extern void intel_wait_for_pipe_off(struct drm_device *dev, int pipe); extern struct drm_crtc *intel_get_load_detect_pipe(struct intel_encoder *intel_encoder, struct drm_connector *connector, struct drm_display_mode *mode, @@ -253,7 +265,8 @@ extern void ironlake_enable_drps(struct drm_device *dev); extern void ironlake_disable_drps(struct drm_device *dev); extern int intel_pin_and_fence_fb_obj(struct drm_device *dev, - struct drm_gem_object *obj); + struct drm_gem_object *obj, + bool pipelined); extern int intel_framebuffer_init(struct drm_device *dev, struct intel_framebuffer *ifb, @@ -268,9 +281,8 @@ extern void intel_finish_page_flip_plane(struct drm_device *dev, int plane); extern void intel_setup_overlay(struct drm_device *dev); extern void intel_cleanup_overlay(struct drm_device *dev); -extern int intel_overlay_switch_off(struct intel_overlay *overlay); -extern int intel_overlay_recover_from_interrupt(struct intel_overlay *overlay, - int interruptible); +extern int intel_overlay_switch_off(struct intel_overlay *overlay, + bool interruptible); extern int intel_overlay_put_image(struct drm_device *dev, void *data, struct drm_file *file_priv); extern int intel_overlay_attrs(struct drm_device *dev, void *data, diff --git a/drivers/gpu/drm/i915/intel_dvo.c b/drivers/gpu/drm/i915/intel_dvo.c index 7c9ec1472d4..ea373283c93 100644 --- a/drivers/gpu/drm/i915/intel_dvo.c +++ b/drivers/gpu/drm/i915/intel_dvo.c @@ -72,7 +72,7 @@ static const struct intel_dvo_device intel_dvo_devices[] = { .name = "ch7017", .dvo_reg = DVOC, .slave_addr = 0x75, - .gpio = GPIOE, + .gpio = GMBUS_PORT_DPB, .dev_ops = &ch7017_ops, } }; @@ -88,7 +88,13 @@ struct intel_dvo { static struct intel_dvo *enc_to_intel_dvo(struct drm_encoder *encoder) { - return container_of(enc_to_intel_encoder(encoder), struct intel_dvo, base); + return container_of(encoder, struct intel_dvo, base.base); +} + +static struct intel_dvo *intel_attached_dvo(struct drm_connector *connector) +{ + return container_of(intel_attached_encoder(connector), + struct intel_dvo, base); } static void intel_dvo_dpms(struct drm_encoder *encoder, int mode) @@ -112,8 +118,7 @@ static void intel_dvo_dpms(struct drm_encoder *encoder, int mode) static int intel_dvo_mode_valid(struct drm_connector *connector, struct drm_display_mode *mode) { - struct drm_encoder *encoder = intel_attached_encoder(connector); - struct intel_dvo *intel_dvo = enc_to_intel_dvo(encoder); + struct intel_dvo *intel_dvo = intel_attached_dvo(connector); if (mode->flags & DRM_MODE_FLAG_DBLSCAN) return MODE_NO_DBLESCAN; @@ -224,23 +229,22 @@ static void intel_dvo_mode_set(struct drm_encoder *encoder, static enum drm_connector_status intel_dvo_detect(struct drm_connector *connector, bool force) { - struct drm_encoder *encoder = intel_attached_encoder(connector); - struct intel_dvo *intel_dvo = enc_to_intel_dvo(encoder); - + struct intel_dvo *intel_dvo = intel_attached_dvo(connector); return intel_dvo->dev.dev_ops->detect(&intel_dvo->dev); } static int intel_dvo_get_modes(struct drm_connector *connector) { - struct drm_encoder *encoder = intel_attached_encoder(connector); - struct intel_dvo *intel_dvo = enc_to_intel_dvo(encoder); + struct intel_dvo *intel_dvo = intel_attached_dvo(connector); + struct drm_i915_private *dev_priv = connector->dev->dev_private; /* We should probably have an i2c driver get_modes function for those * devices which will have a fixed set of modes determined by the chip * (TV-out, for example), but for now with just TMDS and LVDS, * that's not the case. */ - intel_ddc_get_modes(connector, intel_dvo->base.ddc_bus); + intel_ddc_get_modes(connector, + &dev_priv->gmbus[GMBUS_PORT_DPC].adapter); if (!list_empty(&connector->probed_modes)) return 1; @@ -281,7 +285,7 @@ static const struct drm_connector_funcs intel_dvo_connector_funcs = { static const struct drm_connector_helper_funcs intel_dvo_connector_helper_funcs = { .mode_valid = intel_dvo_mode_valid, .get_modes = intel_dvo_get_modes, - .best_encoder = intel_attached_encoder, + .best_encoder = intel_best_encoder, }; static void intel_dvo_enc_destroy(struct drm_encoder *encoder) @@ -311,8 +315,7 @@ intel_dvo_get_current_mode(struct drm_connector *connector) { struct drm_device *dev = connector->dev; struct drm_i915_private *dev_priv = dev->dev_private; - struct drm_encoder *encoder = intel_attached_encoder(connector); - struct intel_dvo *intel_dvo = enc_to_intel_dvo(encoder); + struct intel_dvo *intel_dvo = intel_attached_dvo(connector); uint32_t dvo_val = I915_READ(intel_dvo->dev.dvo_reg); struct drm_display_mode *mode = NULL; @@ -323,7 +326,7 @@ intel_dvo_get_current_mode(struct drm_connector *connector) struct drm_crtc *crtc; int pipe = (dvo_val & DVO_PIPE_B_SELECT) ? 1 : 0; - crtc = intel_get_crtc_from_pipe(dev, pipe); + crtc = intel_get_crtc_for_pipe(dev, pipe); if (crtc) { mode = intel_crtc_mode_get(dev, crtc); if (mode) { @@ -341,11 +344,10 @@ intel_dvo_get_current_mode(struct drm_connector *connector) void intel_dvo_init(struct drm_device *dev) { + struct drm_i915_private *dev_priv = dev->dev_private; struct intel_encoder *intel_encoder; struct intel_dvo *intel_dvo; struct intel_connector *intel_connector; - struct i2c_adapter *i2cbus = NULL; - int ret = 0; int i; int encoder_type = DRM_MODE_ENCODER_NONE; @@ -360,16 +362,14 @@ void intel_dvo_init(struct drm_device *dev) } intel_encoder = &intel_dvo->base; - - /* Set up the DDC bus */ - intel_encoder->ddc_bus = intel_i2c_create(dev, GPIOD, "DVODDC_D"); - if (!intel_encoder->ddc_bus) - goto free_intel; + drm_encoder_init(dev, &intel_encoder->base, + &intel_dvo_enc_funcs, encoder_type); /* Now, try to find a controller */ for (i = 0; i < ARRAY_SIZE(intel_dvo_devices); i++) { struct drm_connector *connector = &intel_connector->base; const struct intel_dvo_device *dvo = &intel_dvo_devices[i]; + struct i2c_adapter *i2c; int gpio; /* Allow the I2C driver info to specify the GPIO to be used in @@ -379,24 +379,18 @@ void intel_dvo_init(struct drm_device *dev) if (dvo->gpio != 0) gpio = dvo->gpio; else if (dvo->type == INTEL_DVO_CHIP_LVDS) - gpio = GPIOB; + gpio = GMBUS_PORT_SSC; else - gpio = GPIOE; + gpio = GMBUS_PORT_DPB; /* Set up the I2C bus necessary for the chip we're probing. * It appears that everything is on GPIOE except for panels * on i830 laptops, which are on GPIOB (DVOA). */ - if (i2cbus != NULL) - intel_i2c_destroy(i2cbus); - if (!(i2cbus = intel_i2c_create(dev, gpio, - gpio == GPIOB ? "DVOI2C_B" : "DVOI2C_E"))) { - continue; - } + i2c = &dev_priv->gmbus[gpio].adapter; intel_dvo->dev = *dvo; - ret = dvo->dev_ops->init(&intel_dvo->dev, i2cbus); - if (!ret) + if (!dvo->dev_ops->init(&intel_dvo->dev, i2c)) continue; intel_encoder->type = INTEL_OUTPUT_DVO; @@ -427,13 +421,10 @@ void intel_dvo_init(struct drm_device *dev) connector->interlace_allowed = false; connector->doublescan_allowed = false; - drm_encoder_init(dev, &intel_encoder->enc, - &intel_dvo_enc_funcs, encoder_type); - drm_encoder_helper_add(&intel_encoder->enc, + drm_encoder_helper_add(&intel_encoder->base, &intel_dvo_helper_funcs); - drm_mode_connector_attach_encoder(&intel_connector->base, - &intel_encoder->enc); + intel_connector_attach_encoder(intel_connector, intel_encoder); if (dvo->type == INTEL_DVO_CHIP_LVDS) { /* For our LVDS chipsets, we should hopefully be able * to dig the fixed panel mode out of the BIOS data. @@ -451,11 +442,7 @@ void intel_dvo_init(struct drm_device *dev) return; } - intel_i2c_destroy(intel_encoder->ddc_bus); - /* Didn't find a chip, so tear down. */ - if (i2cbus != NULL) - intel_i2c_destroy(i2cbus); -free_intel: + drm_encoder_cleanup(&intel_encoder->base); kfree(intel_dvo); kfree(intel_connector); } diff --git a/drivers/gpu/drm/i915/intel_fb.c b/drivers/gpu/drm/i915/intel_fb.c index 7bdc96256bf..b937ccfa7be 100644 --- a/drivers/gpu/drm/i915/intel_fb.c +++ b/drivers/gpu/drm/i915/intel_fb.c @@ -44,13 +44,6 @@ #include "i915_drm.h" #include "i915_drv.h" -struct intel_fbdev { - struct drm_fb_helper helper; - struct intel_framebuffer ifb; - struct list_head fbdev_list; - struct drm_display_mode *our_mode; -}; - static struct fb_ops intelfb_ops = { .owner = THIS_MODULE, .fb_check_var = drm_fb_helper_check_var, @@ -75,7 +68,7 @@ static int intelfb_create(struct intel_fbdev *ifbdev, struct drm_gem_object *fbo = NULL; struct drm_i915_gem_object *obj_priv; struct device *device = &dev->pdev->dev; - int size, ret, mmio_bar = IS_I9XX(dev) ? 0 : 1; + int size, ret, mmio_bar = IS_GEN2(dev) ? 1 : 0; /* we don't do packed 24bpp */ if (sizes->surface_bpp == 24) @@ -100,19 +93,13 @@ static int intelfb_create(struct intel_fbdev *ifbdev, mutex_lock(&dev->struct_mutex); - ret = intel_pin_and_fence_fb_obj(dev, fbo); + /* Flush everything out, we'll be doing GTT only from now on */ + ret = intel_pin_and_fence_fb_obj(dev, fbo, false); if (ret) { DRM_ERROR("failed to pin fb: %d\n", ret); goto out_unref; } - /* Flush everything out, we'll be doing GTT only from now on */ - ret = i915_gem_object_set_to_gtt_domain(fbo, 1); - if (ret) { - DRM_ERROR("failed to bind fb: %d.\n", ret); - goto out_unpin; - } - info = framebuffer_alloc(0, device); if (!info) { ret = -ENOMEM; @@ -142,7 +129,7 @@ static int intelfb_create(struct intel_fbdev *ifbdev, goto out_unpin; } info->apertures->ranges[0].base = dev->mode_config.fb_base; - if (IS_I9XX(dev)) + if (!IS_GEN2(dev)) info->apertures->ranges[0].size = pci_resource_len(dev->pdev, 2); else info->apertures->ranges[0].size = pci_resource_len(dev->pdev, 0); @@ -219,8 +206,8 @@ static struct drm_fb_helper_funcs intel_fb_helper_funcs = { .fb_probe = intel_fb_find_or_create_single, }; -int intel_fbdev_destroy(struct drm_device *dev, - struct intel_fbdev *ifbdev) +static void intel_fbdev_destroy(struct drm_device *dev, + struct intel_fbdev *ifbdev) { struct fb_info *info; struct intel_framebuffer *ifb = &ifbdev->ifb; @@ -237,10 +224,10 @@ int intel_fbdev_destroy(struct drm_device *dev, drm_fb_helper_fini(&ifbdev->helper); drm_framebuffer_cleanup(&ifb->base); - if (ifb->obj) - drm_gem_object_unreference(ifb->obj); - - return 0; + if (ifb->obj) { + drm_gem_object_handle_unreference_unlocked(ifb->obj); + drm_gem_object_unreference_unlocked(ifb->obj); + } } int intel_fbdev_init(struct drm_device *dev) diff --git a/drivers/gpu/drm/i915/intel_hdmi.c b/drivers/gpu/drm/i915/intel_hdmi.c index 926934a482e..9fb9501f2d0 100644 --- a/drivers/gpu/drm/i915/intel_hdmi.c +++ b/drivers/gpu/drm/i915/intel_hdmi.c @@ -40,12 +40,19 @@ struct intel_hdmi { struct intel_encoder base; u32 sdvox_reg; + int ddc_bus; bool has_hdmi_sink; }; static struct intel_hdmi *enc_to_intel_hdmi(struct drm_encoder *encoder) { - return container_of(enc_to_intel_encoder(encoder), struct intel_hdmi, base); + return container_of(encoder, struct intel_hdmi, base.base); +} + +static struct intel_hdmi *intel_attached_hdmi(struct drm_connector *connector) +{ + return container_of(intel_attached_encoder(connector), + struct intel_hdmi, base); } static void intel_hdmi_mode_set(struct drm_encoder *encoder, @@ -141,13 +148,14 @@ static bool intel_hdmi_mode_fixup(struct drm_encoder *encoder, static enum drm_connector_status intel_hdmi_detect(struct drm_connector *connector, bool force) { - struct drm_encoder *encoder = intel_attached_encoder(connector); - struct intel_hdmi *intel_hdmi = enc_to_intel_hdmi(encoder); - struct edid *edid = NULL; + struct intel_hdmi *intel_hdmi = intel_attached_hdmi(connector); + struct drm_i915_private *dev_priv = connector->dev->dev_private; + struct edid *edid; enum drm_connector_status status = connector_status_disconnected; intel_hdmi->has_hdmi_sink = false; - edid = drm_get_edid(connector, intel_hdmi->base.ddc_bus); + edid = drm_get_edid(connector, + &dev_priv->gmbus[intel_hdmi->ddc_bus].adapter); if (edid) { if (edid->input & DRM_EDID_INPUT_DIGITAL) { @@ -163,14 +171,15 @@ intel_hdmi_detect(struct drm_connector *connector, bool force) static int intel_hdmi_get_modes(struct drm_connector *connector) { - struct drm_encoder *encoder = intel_attached_encoder(connector); - struct intel_hdmi *intel_hdmi = enc_to_intel_hdmi(encoder); + struct intel_hdmi *intel_hdmi = intel_attached_hdmi(connector); + struct drm_i915_private *dev_priv = connector->dev->dev_private; /* We should parse the EDID data and find out if it's an HDMI sink so * we can send audio to it. */ - return intel_ddc_get_modes(connector, intel_hdmi->base.ddc_bus); + return intel_ddc_get_modes(connector, + &dev_priv->gmbus[intel_hdmi->ddc_bus].adapter); } static void intel_hdmi_destroy(struct drm_connector *connector) @@ -198,7 +207,7 @@ static const struct drm_connector_funcs intel_hdmi_connector_funcs = { static const struct drm_connector_helper_funcs intel_hdmi_connector_helper_funcs = { .get_modes = intel_hdmi_get_modes, .mode_valid = intel_hdmi_mode_valid, - .best_encoder = intel_attached_encoder, + .best_encoder = intel_best_encoder, }; static const struct drm_encoder_funcs intel_hdmi_enc_funcs = { @@ -224,6 +233,9 @@ void intel_hdmi_init(struct drm_device *dev, int sdvox_reg) } intel_encoder = &intel_hdmi->base; + drm_encoder_init(dev, &intel_encoder->base, &intel_hdmi_enc_funcs, + DRM_MODE_ENCODER_TMDS); + connector = &intel_connector->base; drm_connector_init(dev, connector, &intel_hdmi_connector_funcs, DRM_MODE_CONNECTOR_HDMIA); @@ -239,39 +251,31 @@ void intel_hdmi_init(struct drm_device *dev, int sdvox_reg) /* Set up the DDC bus. */ if (sdvox_reg == SDVOB) { intel_encoder->clone_mask = (1 << INTEL_HDMIB_CLONE_BIT); - intel_encoder->ddc_bus = intel_i2c_create(dev, GPIOE, "HDMIB"); + intel_hdmi->ddc_bus = GMBUS_PORT_DPB; dev_priv->hotplug_supported_mask |= HDMIB_HOTPLUG_INT_STATUS; } else if (sdvox_reg == SDVOC) { intel_encoder->clone_mask = (1 << INTEL_HDMIC_CLONE_BIT); - intel_encoder->ddc_bus = intel_i2c_create(dev, GPIOD, "HDMIC"); + intel_hdmi->ddc_bus = GMBUS_PORT_DPC; dev_priv->hotplug_supported_mask |= HDMIC_HOTPLUG_INT_STATUS; } else if (sdvox_reg == HDMIB) { intel_encoder->clone_mask = (1 << INTEL_HDMID_CLONE_BIT); - intel_encoder->ddc_bus = intel_i2c_create(dev, PCH_GPIOE, - "HDMIB"); + intel_hdmi->ddc_bus = GMBUS_PORT_DPB; dev_priv->hotplug_supported_mask |= HDMIB_HOTPLUG_INT_STATUS; } else if (sdvox_reg == HDMIC) { intel_encoder->clone_mask = (1 << INTEL_HDMIE_CLONE_BIT); - intel_encoder->ddc_bus = intel_i2c_create(dev, PCH_GPIOD, - "HDMIC"); + intel_hdmi->ddc_bus = GMBUS_PORT_DPC; dev_priv->hotplug_supported_mask |= HDMIC_HOTPLUG_INT_STATUS; } else if (sdvox_reg == HDMID) { intel_encoder->clone_mask = (1 << INTEL_HDMIF_CLONE_BIT); - intel_encoder->ddc_bus = intel_i2c_create(dev, PCH_GPIOF, - "HDMID"); + intel_hdmi->ddc_bus = GMBUS_PORT_DPD; dev_priv->hotplug_supported_mask |= HDMID_HOTPLUG_INT_STATUS; } - if (!intel_encoder->ddc_bus) - goto err_connector; intel_hdmi->sdvox_reg = sdvox_reg; - drm_encoder_init(dev, &intel_encoder->enc, &intel_hdmi_enc_funcs, - DRM_MODE_ENCODER_TMDS); - drm_encoder_helper_add(&intel_encoder->enc, &intel_hdmi_helper_funcs); + drm_encoder_helper_add(&intel_encoder->base, &intel_hdmi_helper_funcs); - drm_mode_connector_attach_encoder(&intel_connector->base, - &intel_encoder->enc); + intel_connector_attach_encoder(intel_connector, intel_encoder); drm_sysfs_connector_add(connector); /* For G4X desktop chip, PEG_BAND_GAP_DATA 3:0 must first be written @@ -282,13 +286,4 @@ void intel_hdmi_init(struct drm_device *dev, int sdvox_reg) u32 temp = I915_READ(PEG_BAND_GAP_DATA); I915_WRITE(PEG_BAND_GAP_DATA, (temp & ~0xf) | 0xd); } - - return; - -err_connector: - drm_connector_cleanup(connector); - kfree(intel_hdmi); - kfree(intel_connector); - - return; } diff --git a/drivers/gpu/drm/i915/intel_i2c.c b/drivers/gpu/drm/i915/intel_i2c.c index c2649c7df14..2449a74d4d8 100644 --- a/drivers/gpu/drm/i915/intel_i2c.c +++ b/drivers/gpu/drm/i915/intel_i2c.c @@ -1,6 +1,6 @@ /* * Copyright (c) 2006 Dave Airlie <airlied@linux.ie> - * Copyright © 2006-2008 Intel Corporation + * Copyright © 2006-2008,2010 Intel Corporation * Jesse Barnes <jesse.barnes@intel.com> * * Permission is hereby granted, free of charge, to any person obtaining a @@ -24,10 +24,9 @@ * * Authors: * Eric Anholt <eric@anholt.net> + * Chris Wilson <chris@chris-wilson.co.uk> */ #include <linux/i2c.h> -#include <linux/slab.h> -#include <linux/i2c-id.h> #include <linux/i2c-algo-bit.h> #include "drmP.h" #include "drm.h" @@ -35,79 +34,106 @@ #include "i915_drm.h" #include "i915_drv.h" -void intel_i2c_quirk_set(struct drm_device *dev, bool enable) +/* Intel GPIO access functions */ + +#define I2C_RISEFALL_TIME 20 + +static inline struct intel_gmbus * +to_intel_gmbus(struct i2c_adapter *i2c) +{ + return container_of(i2c, struct intel_gmbus, adapter); +} + +struct intel_gpio { + struct i2c_adapter adapter; + struct i2c_algo_bit_data algo; + struct drm_i915_private *dev_priv; + u32 reg; +}; + +void +intel_i2c_reset(struct drm_device *dev) { struct drm_i915_private *dev_priv = dev->dev_private; + if (HAS_PCH_SPLIT(dev)) + I915_WRITE(PCH_GMBUS0, 0); + else + I915_WRITE(GMBUS0, 0); +} + +static void intel_i2c_quirk_set(struct drm_i915_private *dev_priv, bool enable) +{ + u32 val; /* When using bit bashing for I2C, this bit needs to be set to 1 */ - if (!IS_PINEVIEW(dev)) + if (!IS_PINEVIEW(dev_priv->dev)) return; + + val = I915_READ(DSPCLK_GATE_D); if (enable) - I915_WRITE(DSPCLK_GATE_D, - I915_READ(DSPCLK_GATE_D) | DPCUNIT_CLOCK_GATE_DISABLE); + val |= DPCUNIT_CLOCK_GATE_DISABLE; else - I915_WRITE(DSPCLK_GATE_D, - I915_READ(DSPCLK_GATE_D) & (~DPCUNIT_CLOCK_GATE_DISABLE)); + val &= ~DPCUNIT_CLOCK_GATE_DISABLE; + I915_WRITE(DSPCLK_GATE_D, val); } -/* - * Intel GPIO access functions - */ +static u32 get_reserved(struct intel_gpio *gpio) +{ + struct drm_i915_private *dev_priv = gpio->dev_priv; + struct drm_device *dev = dev_priv->dev; + u32 reserved = 0; -#define I2C_RISEFALL_TIME 20 + /* On most chips, these bits must be preserved in software. */ + if (!IS_I830(dev) && !IS_845G(dev)) + reserved = I915_READ(gpio->reg) & (GPIO_DATA_PULLUP_DISABLE | + GPIO_CLOCK_PULLUP_DISABLE); + + return reserved; +} static int get_clock(void *data) { - struct intel_i2c_chan *chan = data; - struct drm_i915_private *dev_priv = chan->drm_dev->dev_private; - u32 val; - - val = I915_READ(chan->reg); - return ((val & GPIO_CLOCK_VAL_IN) != 0); + struct intel_gpio *gpio = data; + struct drm_i915_private *dev_priv = gpio->dev_priv; + u32 reserved = get_reserved(gpio); + I915_WRITE(gpio->reg, reserved | GPIO_CLOCK_DIR_MASK); + I915_WRITE(gpio->reg, reserved); + return (I915_READ(gpio->reg) & GPIO_CLOCK_VAL_IN) != 0; } static int get_data(void *data) { - struct intel_i2c_chan *chan = data; - struct drm_i915_private *dev_priv = chan->drm_dev->dev_private; - u32 val; - - val = I915_READ(chan->reg); - return ((val & GPIO_DATA_VAL_IN) != 0); + struct intel_gpio *gpio = data; + struct drm_i915_private *dev_priv = gpio->dev_priv; + u32 reserved = get_reserved(gpio); + I915_WRITE(gpio->reg, reserved | GPIO_DATA_DIR_MASK); + I915_WRITE(gpio->reg, reserved); + return (I915_READ(gpio->reg) & GPIO_DATA_VAL_IN) != 0; } static void set_clock(void *data, int state_high) { - struct intel_i2c_chan *chan = data; - struct drm_device *dev = chan->drm_dev; - struct drm_i915_private *dev_priv = chan->drm_dev->dev_private; - u32 reserved = 0, clock_bits; - - /* On most chips, these bits must be preserved in software. */ - if (!IS_I830(dev) && !IS_845G(dev)) - reserved = I915_READ(chan->reg) & (GPIO_DATA_PULLUP_DISABLE | - GPIO_CLOCK_PULLUP_DISABLE); + struct intel_gpio *gpio = data; + struct drm_i915_private *dev_priv = gpio->dev_priv; + u32 reserved = get_reserved(gpio); + u32 clock_bits; if (state_high) clock_bits = GPIO_CLOCK_DIR_IN | GPIO_CLOCK_DIR_MASK; else clock_bits = GPIO_CLOCK_DIR_OUT | GPIO_CLOCK_DIR_MASK | GPIO_CLOCK_VAL_MASK; - I915_WRITE(chan->reg, reserved | clock_bits); - udelay(I2C_RISEFALL_TIME); /* wait for the line to change state */ + + I915_WRITE(gpio->reg, reserved | clock_bits); + POSTING_READ(gpio->reg); } static void set_data(void *data, int state_high) { - struct intel_i2c_chan *chan = data; - struct drm_device *dev = chan->drm_dev; - struct drm_i915_private *dev_priv = chan->drm_dev->dev_private; - u32 reserved = 0, data_bits; - - /* On most chips, these bits must be preserved in software. */ - if (!IS_I830(dev) && !IS_845G(dev)) - reserved = I915_READ(chan->reg) & (GPIO_DATA_PULLUP_DISABLE | - GPIO_CLOCK_PULLUP_DISABLE); + struct intel_gpio *gpio = data; + struct drm_i915_private *dev_priv = gpio->dev_priv; + u32 reserved = get_reserved(gpio); + u32 data_bits; if (state_high) data_bits = GPIO_DATA_DIR_IN | GPIO_DATA_DIR_MASK; @@ -115,109 +141,312 @@ static void set_data(void *data, int state_high) data_bits = GPIO_DATA_DIR_OUT | GPIO_DATA_DIR_MASK | GPIO_DATA_VAL_MASK; - I915_WRITE(chan->reg, reserved | data_bits); - udelay(I2C_RISEFALL_TIME); /* wait for the line to change state */ + I915_WRITE(gpio->reg, reserved | data_bits); + POSTING_READ(gpio->reg); } -/* Clears the GMBUS setup. Our driver doesn't make use of the GMBUS I2C - * engine, but if the BIOS leaves it enabled, then that can break our use - * of the bit-banging I2C interfaces. This is notably the case with the - * Mac Mini in EFI mode. - */ -void -intel_i2c_reset_gmbus(struct drm_device *dev) +static struct i2c_adapter * +intel_gpio_create(struct drm_i915_private *dev_priv, u32 pin) { - struct drm_i915_private *dev_priv = dev->dev_private; + static const int map_pin_to_reg[] = { + 0, + GPIOB, + GPIOA, + GPIOC, + GPIOD, + GPIOE, + GPIOF, + }; + struct intel_gpio *gpio; - if (HAS_PCH_SPLIT(dev)) { - I915_WRITE(PCH_GMBUS0, 0); - } else { - I915_WRITE(GMBUS0, 0); + if (pin < 1 || pin > 7) + return NULL; + + gpio = kzalloc(sizeof(struct intel_gpio), GFP_KERNEL); + if (gpio == NULL) + return NULL; + + gpio->reg = map_pin_to_reg[pin]; + if (HAS_PCH_SPLIT(dev_priv->dev)) + gpio->reg += PCH_GPIOA - GPIOA; + gpio->dev_priv = dev_priv; + + snprintf(gpio->adapter.name, I2C_NAME_SIZE, "GPIO%c", "?BACDEF?"[pin]); + gpio->adapter.owner = THIS_MODULE; + gpio->adapter.algo_data = &gpio->algo; + gpio->adapter.dev.parent = &dev_priv->dev->pdev->dev; + gpio->algo.setsda = set_data; + gpio->algo.setscl = set_clock; + gpio->algo.getsda = get_data; + gpio->algo.getscl = get_clock; + gpio->algo.udelay = I2C_RISEFALL_TIME; + gpio->algo.timeout = usecs_to_jiffies(2200); + gpio->algo.data = gpio; + + if (i2c_bit_add_bus(&gpio->adapter)) + goto out_free; + + return &gpio->adapter; + +out_free: + kfree(gpio); + return NULL; +} + +static int +intel_i2c_quirk_xfer(struct drm_i915_private *dev_priv, + struct i2c_adapter *adapter, + struct i2c_msg *msgs, + int num) +{ + struct intel_gpio *gpio = container_of(adapter, + struct intel_gpio, + adapter); + int ret; + + intel_i2c_reset(dev_priv->dev); + + intel_i2c_quirk_set(dev_priv, true); + set_data(gpio, 1); + set_clock(gpio, 1); + udelay(I2C_RISEFALL_TIME); + + ret = adapter->algo->master_xfer(adapter, msgs, num); + + set_data(gpio, 1); + set_clock(gpio, 1); + intel_i2c_quirk_set(dev_priv, false); + + return ret; +} + +static int +gmbus_xfer(struct i2c_adapter *adapter, + struct i2c_msg *msgs, + int num) +{ + struct intel_gmbus *bus = container_of(adapter, + struct intel_gmbus, + adapter); + struct drm_i915_private *dev_priv = adapter->algo_data; + int i, reg_offset; + + if (bus->force_bit) + return intel_i2c_quirk_xfer(dev_priv, + bus->force_bit, msgs, num); + + reg_offset = HAS_PCH_SPLIT(dev_priv->dev) ? PCH_GMBUS0 - GMBUS0 : 0; + + I915_WRITE(GMBUS0 + reg_offset, bus->reg0); + + for (i = 0; i < num; i++) { + u16 len = msgs[i].len; + u8 *buf = msgs[i].buf; + + if (msgs[i].flags & I2C_M_RD) { + I915_WRITE(GMBUS1 + reg_offset, + GMBUS_CYCLE_WAIT | (i + 1 == num ? GMBUS_CYCLE_STOP : 0) | + (len << GMBUS_BYTE_COUNT_SHIFT) | + (msgs[i].addr << GMBUS_SLAVE_ADDR_SHIFT) | + GMBUS_SLAVE_READ | GMBUS_SW_RDY); + POSTING_READ(GMBUS2+reg_offset); + do { + u32 val, loop = 0; + + if (wait_for(I915_READ(GMBUS2 + reg_offset) & (GMBUS_SATOER | GMBUS_HW_RDY), 50)) + goto timeout; + if (I915_READ(GMBUS2 + reg_offset) & GMBUS_SATOER) + return 0; + + val = I915_READ(GMBUS3 + reg_offset); + do { + *buf++ = val & 0xff; + val >>= 8; + } while (--len && ++loop < 4); + } while (len); + } else { + u32 val, loop; + + val = loop = 0; + do { + val |= *buf++ << (8 * loop); + } while (--len && ++loop < 4); + + I915_WRITE(GMBUS3 + reg_offset, val); + I915_WRITE(GMBUS1 + reg_offset, + (i + 1 == num ? GMBUS_CYCLE_STOP : GMBUS_CYCLE_WAIT) | + (msgs[i].len << GMBUS_BYTE_COUNT_SHIFT) | + (msgs[i].addr << GMBUS_SLAVE_ADDR_SHIFT) | + GMBUS_SLAVE_WRITE | GMBUS_SW_RDY); + POSTING_READ(GMBUS2+reg_offset); + + while (len) { + if (wait_for(I915_READ(GMBUS2 + reg_offset) & (GMBUS_SATOER | GMBUS_HW_RDY), 50)) + goto timeout; + if (I915_READ(GMBUS2 + reg_offset) & GMBUS_SATOER) + return 0; + + val = loop = 0; + do { + val |= *buf++ << (8 * loop); + } while (--len && ++loop < 4); + + I915_WRITE(GMBUS3 + reg_offset, val); + POSTING_READ(GMBUS2+reg_offset); + } + } + + if (i + 1 < num && wait_for(I915_READ(GMBUS2 + reg_offset) & (GMBUS_SATOER | GMBUS_HW_WAIT_PHASE), 50)) + goto timeout; + if (I915_READ(GMBUS2 + reg_offset) & GMBUS_SATOER) + return 0; } + + return num; + +timeout: + DRM_INFO("GMBUS timed out, falling back to bit banging on pin %d [%s]\n", + bus->reg0 & 0xff, bus->adapter.name); + /* Hardware may not support GMBUS over these pins? Try GPIO bitbanging instead. */ + bus->force_bit = intel_gpio_create(dev_priv, bus->reg0 & 0xff); + if (!bus->force_bit) + return -ENOMEM; + + return intel_i2c_quirk_xfer(dev_priv, bus->force_bit, msgs, num); } +static u32 gmbus_func(struct i2c_adapter *adapter) +{ + struct intel_gmbus *bus = container_of(adapter, + struct intel_gmbus, + adapter); + + if (bus->force_bit) + bus->force_bit->algo->functionality(bus->force_bit); + + return (I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL | + /* I2C_FUNC_10BIT_ADDR | */ + I2C_FUNC_SMBUS_READ_BLOCK_DATA | + I2C_FUNC_SMBUS_BLOCK_PROC_CALL); +} + +static const struct i2c_algorithm gmbus_algorithm = { + .master_xfer = gmbus_xfer, + .functionality = gmbus_func +}; + /** - * intel_i2c_create - instantiate an Intel i2c bus using the specified GPIO reg + * intel_gmbus_setup - instantiate all Intel i2c GMBuses * @dev: DRM device - * @output: driver specific output device - * @reg: GPIO reg to use - * @name: name for this bus - * @slave_addr: slave address (if fixed) - * - * Creates and registers a new i2c bus with the Linux i2c layer, for use - * in output probing and control (e.g. DDC or SDVO control functions). - * - * Possible values for @reg include: - * %GPIOA - * %GPIOB - * %GPIOC - * %GPIOD - * %GPIOE - * %GPIOF - * %GPIOG - * %GPIOH - * see PRM for details on how these different busses are used. */ -struct i2c_adapter *intel_i2c_create(struct drm_device *dev, const u32 reg, - const char *name) +int intel_setup_gmbus(struct drm_device *dev) { - struct intel_i2c_chan *chan; + static const char *names[GMBUS_NUM_PORTS] = { + "disabled", + "ssc", + "vga", + "panel", + "dpc", + "dpb", + "reserved" + "dpd", + }; + struct drm_i915_private *dev_priv = dev->dev_private; + int ret, i; - chan = kzalloc(sizeof(struct intel_i2c_chan), GFP_KERNEL); - if (!chan) - goto out_free; + dev_priv->gmbus = kcalloc(sizeof(struct intel_gmbus), GMBUS_NUM_PORTS, + GFP_KERNEL); + if (dev_priv->gmbus == NULL) + return -ENOMEM; - chan->drm_dev = dev; - chan->reg = reg; - snprintf(chan->adapter.name, I2C_NAME_SIZE, "intel drm %s", name); - chan->adapter.owner = THIS_MODULE; - chan->adapter.algo_data = &chan->algo; - chan->adapter.dev.parent = &dev->pdev->dev; - chan->algo.setsda = set_data; - chan->algo.setscl = set_clock; - chan->algo.getsda = get_data; - chan->algo.getscl = get_clock; - chan->algo.udelay = 20; - chan->algo.timeout = usecs_to_jiffies(2200); - chan->algo.data = chan; - - i2c_set_adapdata(&chan->adapter, chan); - - if(i2c_bit_add_bus(&chan->adapter)) - goto out_free; + for (i = 0; i < GMBUS_NUM_PORTS; i++) { + struct intel_gmbus *bus = &dev_priv->gmbus[i]; - intel_i2c_reset_gmbus(dev); + bus->adapter.owner = THIS_MODULE; + bus->adapter.class = I2C_CLASS_DDC; + snprintf(bus->adapter.name, + I2C_NAME_SIZE, + "gmbus %s", + names[i]); - /* JJJ: raise SCL and SDA? */ - intel_i2c_quirk_set(dev, true); - set_data(chan, 1); - set_clock(chan, 1); - intel_i2c_quirk_set(dev, false); - udelay(20); + bus->adapter.dev.parent = &dev->pdev->dev; + bus->adapter.algo_data = dev_priv; - return &chan->adapter; + bus->adapter.algo = &gmbus_algorithm; + ret = i2c_add_adapter(&bus->adapter); + if (ret) + goto err; -out_free: - kfree(chan); - return NULL; + /* By default use a conservative clock rate */ + bus->reg0 = i | GMBUS_RATE_100KHZ; + + /* XXX force bit banging until GMBUS is fully debugged */ + bus->force_bit = intel_gpio_create(dev_priv, i); + } + + intel_i2c_reset(dev_priv->dev); + + return 0; + +err: + while (--i) { + struct intel_gmbus *bus = &dev_priv->gmbus[i]; + i2c_del_adapter(&bus->adapter); + } + kfree(dev_priv->gmbus); + dev_priv->gmbus = NULL; + return ret; } -/** - * intel_i2c_destroy - unregister and free i2c bus resources - * @output: channel to free - * - * Unregister the adapter from the i2c layer, then free the structure. - */ -void intel_i2c_destroy(struct i2c_adapter *adapter) +void intel_gmbus_set_speed(struct i2c_adapter *adapter, int speed) +{ + struct intel_gmbus *bus = to_intel_gmbus(adapter); + + /* speed: + * 0x0 = 100 KHz + * 0x1 = 50 KHz + * 0x2 = 400 KHz + * 0x3 = 1000 Khz + */ + bus->reg0 = (bus->reg0 & ~(0x3 << 8)) | (speed << 8); +} + +void intel_gmbus_force_bit(struct i2c_adapter *adapter, bool force_bit) +{ + struct intel_gmbus *bus = to_intel_gmbus(adapter); + + if (force_bit) { + if (bus->force_bit == NULL) { + struct drm_i915_private *dev_priv = adapter->algo_data; + bus->force_bit = intel_gpio_create(dev_priv, + bus->reg0 & 0xff); + } + } else { + if (bus->force_bit) { + i2c_del_adapter(bus->force_bit); + kfree(bus->force_bit); + bus->force_bit = NULL; + } + } +} + +void intel_teardown_gmbus(struct drm_device *dev) { - struct intel_i2c_chan *chan; + struct drm_i915_private *dev_priv = dev->dev_private; + int i; - if (!adapter) + if (dev_priv->gmbus == NULL) return; - chan = container_of(adapter, - struct intel_i2c_chan, - adapter); - i2c_del_adapter(&chan->adapter); - kfree(chan); + for (i = 0; i < GMBUS_NUM_PORTS; i++) { + struct intel_gmbus *bus = &dev_priv->gmbus[i]; + if (bus->force_bit) { + i2c_del_adapter(bus->force_bit); + kfree(bus->force_bit); + } + i2c_del_adapter(&bus->adapter); + } + + kfree(dev_priv->gmbus); + dev_priv->gmbus = NULL; } diff --git a/drivers/gpu/drm/i915/intel_lvds.c b/drivers/gpu/drm/i915/intel_lvds.c index 6ec39a86ed0..f1a649990ea 100644 --- a/drivers/gpu/drm/i915/intel_lvds.c +++ b/drivers/gpu/drm/i915/intel_lvds.c @@ -43,102 +43,76 @@ /* Private structure for the integrated LVDS support */ struct intel_lvds { struct intel_encoder base; + + struct edid *edid; + int fitting_mode; u32 pfit_control; u32 pfit_pgm_ratios; + bool pfit_dirty; + + struct drm_display_mode *fixed_mode; }; -static struct intel_lvds *enc_to_intel_lvds(struct drm_encoder *encoder) +static struct intel_lvds *to_intel_lvds(struct drm_encoder *encoder) { - return container_of(enc_to_intel_encoder(encoder), struct intel_lvds, base); -} - -/** - * Sets the backlight level. - * - * \param level backlight level, from 0 to intel_lvds_get_max_backlight(). - */ -static void intel_lvds_set_backlight(struct drm_device *dev, int level) -{ - struct drm_i915_private *dev_priv = dev->dev_private; - u32 blc_pwm_ctl, reg; - - if (HAS_PCH_SPLIT(dev)) - reg = BLC_PWM_CPU_CTL; - else - reg = BLC_PWM_CTL; - - blc_pwm_ctl = I915_READ(reg) & ~BACKLIGHT_DUTY_CYCLE_MASK; - I915_WRITE(reg, (blc_pwm_ctl | - (level << BACKLIGHT_DUTY_CYCLE_SHIFT))); + return container_of(encoder, struct intel_lvds, base.base); } -/** - * Returns the maximum level of the backlight duty cycle field. - */ -static u32 intel_lvds_get_max_backlight(struct drm_device *dev) +static struct intel_lvds *intel_attached_lvds(struct drm_connector *connector) { - struct drm_i915_private *dev_priv = dev->dev_private; - u32 reg; - - if (HAS_PCH_SPLIT(dev)) - reg = BLC_PWM_PCH_CTL2; - else - reg = BLC_PWM_CTL; - - return ((I915_READ(reg) & BACKLIGHT_MODULATION_FREQ_MASK) >> - BACKLIGHT_MODULATION_FREQ_SHIFT) * 2; + return container_of(intel_attached_encoder(connector), + struct intel_lvds, base); } /** * Sets the power state for the panel. */ -static void intel_lvds_set_power(struct drm_device *dev, bool on) +static void intel_lvds_set_power(struct intel_lvds *intel_lvds, bool on) { + struct drm_device *dev = intel_lvds->base.base.dev; struct drm_i915_private *dev_priv = dev->dev_private; - u32 ctl_reg, status_reg, lvds_reg; + u32 ctl_reg, lvds_reg; if (HAS_PCH_SPLIT(dev)) { ctl_reg = PCH_PP_CONTROL; - status_reg = PCH_PP_STATUS; lvds_reg = PCH_LVDS; } else { ctl_reg = PP_CONTROL; - status_reg = PP_STATUS; lvds_reg = LVDS; } if (on) { I915_WRITE(lvds_reg, I915_READ(lvds_reg) | LVDS_PORT_EN); - POSTING_READ(lvds_reg); - - I915_WRITE(ctl_reg, I915_READ(ctl_reg) | - POWER_TARGET_ON); - if (wait_for(I915_READ(status_reg) & PP_ON, 1000, 0)) - DRM_ERROR("timed out waiting to enable LVDS pipe"); - - intel_lvds_set_backlight(dev, dev_priv->backlight_duty_cycle); + I915_WRITE(ctl_reg, I915_READ(ctl_reg) | POWER_TARGET_ON); + intel_panel_set_backlight(dev, dev_priv->backlight_level); } else { - intel_lvds_set_backlight(dev, 0); + dev_priv->backlight_level = intel_panel_get_backlight(dev); + + intel_panel_set_backlight(dev, 0); + I915_WRITE(ctl_reg, I915_READ(ctl_reg) & ~POWER_TARGET_ON); - I915_WRITE(ctl_reg, I915_READ(ctl_reg) & - ~POWER_TARGET_ON); - if (wait_for((I915_READ(status_reg) & PP_ON) == 0, 1000, 0)) - DRM_ERROR("timed out waiting for LVDS pipe to turn off"); + if (intel_lvds->pfit_control) { + if (wait_for((I915_READ(PP_STATUS) & PP_ON) == 0, 1000)) + DRM_ERROR("timed out waiting for panel to power off\n"); + I915_WRITE(PFIT_CONTROL, 0); + intel_lvds->pfit_control = 0; + intel_lvds->pfit_dirty = false; + } I915_WRITE(lvds_reg, I915_READ(lvds_reg) & ~LVDS_PORT_EN); - POSTING_READ(lvds_reg); } + POSTING_READ(lvds_reg); } static void intel_lvds_dpms(struct drm_encoder *encoder, int mode) { - struct drm_device *dev = encoder->dev; + struct intel_lvds *intel_lvds = to_intel_lvds(encoder); if (mode == DRM_MODE_DPMS_ON) - intel_lvds_set_power(dev, true); + intel_lvds_set_power(intel_lvds, true); else - intel_lvds_set_power(dev, false); + intel_lvds_set_power(intel_lvds, false); /* XXX: We never power down the LVDS pairs. */ } @@ -146,16 +120,13 @@ static void intel_lvds_dpms(struct drm_encoder *encoder, int mode) static int intel_lvds_mode_valid(struct drm_connector *connector, struct drm_display_mode *mode) { - struct drm_device *dev = connector->dev; - struct drm_i915_private *dev_priv = dev->dev_private; - struct drm_display_mode *fixed_mode = dev_priv->panel_fixed_mode; + struct intel_lvds *intel_lvds = intel_attached_lvds(connector); + struct drm_display_mode *fixed_mode = intel_lvds->fixed_mode; - if (fixed_mode) { - if (mode->hdisplay > fixed_mode->hdisplay) - return MODE_PANEL; - if (mode->vdisplay > fixed_mode->vdisplay) - return MODE_PANEL; - } + if (mode->hdisplay > fixed_mode->hdisplay) + return MODE_PANEL; + if (mode->vdisplay > fixed_mode->vdisplay) + return MODE_PANEL; return MODE_OK; } @@ -223,12 +194,12 @@ static bool intel_lvds_mode_fixup(struct drm_encoder *encoder, struct drm_device *dev = encoder->dev; struct drm_i915_private *dev_priv = dev->dev_private; struct intel_crtc *intel_crtc = to_intel_crtc(encoder->crtc); - struct intel_lvds *intel_lvds = enc_to_intel_lvds(encoder); + struct intel_lvds *intel_lvds = to_intel_lvds(encoder); struct drm_encoder *tmp_encoder; u32 pfit_control = 0, pfit_pgm_ratios = 0, border = 0; /* Should never happen!! */ - if (!IS_I965G(dev) && intel_crtc->pipe == 0) { + if (INTEL_INFO(dev)->gen < 4 && intel_crtc->pipe == 0) { DRM_ERROR("Can't support LVDS on pipe A\n"); return false; } @@ -241,9 +212,6 @@ static bool intel_lvds_mode_fixup(struct drm_encoder *encoder, return false; } } - /* If we don't have a panel mode, there is nothing we can do */ - if (dev_priv->panel_fixed_mode == NULL) - return true; /* * We have timings from the BIOS for the panel, put them in @@ -251,7 +219,7 @@ static bool intel_lvds_mode_fixup(struct drm_encoder *encoder, * with the panel scaling set up to source from the H/VDisplay * of the original mode. */ - intel_fixed_panel_mode(dev_priv->panel_fixed_mode, adjusted_mode); + intel_fixed_panel_mode(intel_lvds->fixed_mode, adjusted_mode); if (HAS_PCH_SPLIT(dev)) { intel_pch_panel_fitting(dev, intel_lvds->fitting_mode, @@ -260,8 +228,8 @@ static bool intel_lvds_mode_fixup(struct drm_encoder *encoder, } /* Make sure pre-965s set dither correctly */ - if (!IS_I965G(dev)) { - if (dev_priv->panel_wants_dither || dev_priv->lvds_dither) + if (INTEL_INFO(dev)->gen < 4) { + if (dev_priv->lvds_dither) pfit_control |= PANEL_8TO6_DITHER_ENABLE; } @@ -271,7 +239,7 @@ static bool intel_lvds_mode_fixup(struct drm_encoder *encoder, goto out; /* 965+ wants fuzzy fitting */ - if (IS_I965G(dev)) + if (INTEL_INFO(dev)->gen >= 4) pfit_control |= ((intel_crtc->pipe << PFIT_PIPE_SHIFT) | PFIT_FILTER_FUZZY); @@ -297,7 +265,7 @@ static bool intel_lvds_mode_fixup(struct drm_encoder *encoder, case DRM_MODE_SCALE_ASPECT: /* Scale but preserve the aspect ratio */ - if (IS_I965G(dev)) { + if (INTEL_INFO(dev)->gen >= 4) { u32 scaled_width = adjusted_mode->hdisplay * mode->vdisplay; u32 scaled_height = mode->hdisplay * adjusted_mode->vdisplay; @@ -356,7 +324,7 @@ static bool intel_lvds_mode_fixup(struct drm_encoder *encoder, * Fortunately this is all done for us in hw. */ pfit_control |= PFIT_ENABLE; - if (IS_I965G(dev)) + if (INTEL_INFO(dev)->gen >= 4) pfit_control |= PFIT_SCALING_AUTO; else pfit_control |= (VERT_AUTO_SCALE | HORIZ_AUTO_SCALE | @@ -369,8 +337,12 @@ static bool intel_lvds_mode_fixup(struct drm_encoder *encoder, } out: - intel_lvds->pfit_control = pfit_control; - intel_lvds->pfit_pgm_ratios = pfit_pgm_ratios; + if (pfit_control != intel_lvds->pfit_control || + pfit_pgm_ratios != intel_lvds->pfit_pgm_ratios) { + intel_lvds->pfit_control = pfit_control; + intel_lvds->pfit_pgm_ratios = pfit_pgm_ratios; + intel_lvds->pfit_dirty = true; + } dev_priv->lvds_border_bits = border; /* @@ -386,30 +358,60 @@ static void intel_lvds_prepare(struct drm_encoder *encoder) { struct drm_device *dev = encoder->dev; struct drm_i915_private *dev_priv = dev->dev_private; - u32 reg; - - if (HAS_PCH_SPLIT(dev)) - reg = BLC_PWM_CPU_CTL; - else - reg = BLC_PWM_CTL; - - dev_priv->saveBLC_PWM_CTL = I915_READ(reg); - dev_priv->backlight_duty_cycle = (dev_priv->saveBLC_PWM_CTL & - BACKLIGHT_DUTY_CYCLE_MASK); + struct intel_lvds *intel_lvds = to_intel_lvds(encoder); + + dev_priv->backlight_level = intel_panel_get_backlight(dev); + + /* We try to do the minimum that is necessary in order to unlock + * the registers for mode setting. + * + * On Ironlake, this is quite simple as we just set the unlock key + * and ignore all subtleties. (This may cause some issues...) + * + * Prior to Ironlake, we must disable the pipe if we want to adjust + * the panel fitter. However at all other times we can just reset + * the registers regardless. + */ - intel_lvds_set_power(dev, false); + if (HAS_PCH_SPLIT(dev)) { + I915_WRITE(PCH_PP_CONTROL, + I915_READ(PCH_PP_CONTROL) | PANEL_UNLOCK_REGS); + } else if (intel_lvds->pfit_dirty) { + I915_WRITE(PP_CONTROL, + (I915_READ(PP_CONTROL) | PANEL_UNLOCK_REGS) + & ~POWER_TARGET_ON); + } else { + I915_WRITE(PP_CONTROL, + I915_READ(PP_CONTROL) | PANEL_UNLOCK_REGS); + } } -static void intel_lvds_commit( struct drm_encoder *encoder) +static void intel_lvds_commit(struct drm_encoder *encoder) { struct drm_device *dev = encoder->dev; struct drm_i915_private *dev_priv = dev->dev_private; + struct intel_lvds *intel_lvds = to_intel_lvds(encoder); - if (dev_priv->backlight_duty_cycle == 0) - dev_priv->backlight_duty_cycle = - intel_lvds_get_max_backlight(dev); + if (dev_priv->backlight_level == 0) + dev_priv->backlight_level = intel_panel_get_max_backlight(dev); + + /* Undo any unlocking done in prepare to prevent accidental + * adjustment of the registers. + */ + if (HAS_PCH_SPLIT(dev)) { + u32 val = I915_READ(PCH_PP_CONTROL); + if ((val & PANEL_UNLOCK_REGS) == PANEL_UNLOCK_REGS) + I915_WRITE(PCH_PP_CONTROL, val & 0x3); + } else { + u32 val = I915_READ(PP_CONTROL); + if ((val & PANEL_UNLOCK_REGS) == PANEL_UNLOCK_REGS) + I915_WRITE(PP_CONTROL, val & 0x3); + } - intel_lvds_set_power(dev, true); + /* Always do a full power on as we do not know what state + * we were left in. + */ + intel_lvds_set_power(intel_lvds, true); } static void intel_lvds_mode_set(struct drm_encoder *encoder, @@ -418,7 +420,7 @@ static void intel_lvds_mode_set(struct drm_encoder *encoder, { struct drm_device *dev = encoder->dev; struct drm_i915_private *dev_priv = dev->dev_private; - struct intel_lvds *intel_lvds = enc_to_intel_lvds(encoder); + struct intel_lvds *intel_lvds = to_intel_lvds(encoder); /* * The LVDS pin pair will already have been turned on in the @@ -429,13 +431,23 @@ static void intel_lvds_mode_set(struct drm_encoder *encoder, if (HAS_PCH_SPLIT(dev)) return; + if (!intel_lvds->pfit_dirty) + return; + /* * Enable automatic panel scaling so that non-native modes fill the * screen. Should be enabled before the pipe is enabled, according to * register description and PRM. */ + DRM_DEBUG_KMS("applying panel-fitter: %x, %x\n", + intel_lvds->pfit_control, + intel_lvds->pfit_pgm_ratios); + if (wait_for((I915_READ(PP_STATUS) & PP_ON) == 0, 1000)) + DRM_ERROR("timed out waiting for panel to power off\n"); + I915_WRITE(PFIT_PGM_RATIOS, intel_lvds->pfit_pgm_ratios); I915_WRITE(PFIT_CONTROL, intel_lvds->pfit_control); + intel_lvds->pfit_dirty = false; } /** @@ -465,38 +477,22 @@ intel_lvds_detect(struct drm_connector *connector, bool force) */ static int intel_lvds_get_modes(struct drm_connector *connector) { + struct intel_lvds *intel_lvds = intel_attached_lvds(connector); struct drm_device *dev = connector->dev; - struct drm_encoder *encoder = intel_attached_encoder(connector); - struct intel_encoder *intel_encoder = enc_to_intel_encoder(encoder); - struct drm_i915_private *dev_priv = dev->dev_private; - int ret = 0; - - if (dev_priv->lvds_edid_good) { - ret = intel_ddc_get_modes(connector, intel_encoder->ddc_bus); + struct drm_display_mode *mode; - if (ret) - return ret; + if (intel_lvds->edid) { + drm_mode_connector_update_edid_property(connector, + intel_lvds->edid); + return drm_add_edid_modes(connector, intel_lvds->edid); } - /* Didn't get an EDID, so - * Set wide sync ranges so we get all modes - * handed to valid_mode for checking - */ - connector->display_info.min_vfreq = 0; - connector->display_info.max_vfreq = 200; - connector->display_info.min_hfreq = 0; - connector->display_info.max_hfreq = 200; - - if (dev_priv->panel_fixed_mode != NULL) { - struct drm_display_mode *mode; - - mode = drm_mode_duplicate(dev, dev_priv->panel_fixed_mode); - drm_mode_probed_add(connector, mode); - - return 1; - } + mode = drm_mode_duplicate(dev, intel_lvds->fixed_mode); + if (mode == 0) + return 0; - return 0; + drm_mode_probed_add(connector, mode); + return 1; } static int intel_no_modeset_on_lid_dmi_callback(const struct dmi_system_id *id) @@ -587,18 +583,17 @@ static int intel_lvds_set_property(struct drm_connector *connector, struct drm_property *property, uint64_t value) { + struct intel_lvds *intel_lvds = intel_attached_lvds(connector); struct drm_device *dev = connector->dev; - if (property == dev->mode_config.scaling_mode_property && - connector->encoder) { - struct drm_crtc *crtc = connector->encoder->crtc; - struct drm_encoder *encoder = connector->encoder; - struct intel_lvds *intel_lvds = enc_to_intel_lvds(encoder); + if (property == dev->mode_config.scaling_mode_property) { + struct drm_crtc *crtc = intel_lvds->base.base.crtc; if (value == DRM_MODE_SCALE_NONE) { DRM_DEBUG_KMS("no scaling not supported\n"); - return 0; + return -EINVAL; } + if (intel_lvds->fitting_mode == value) { /* the LVDS scaling property is not changed */ return 0; @@ -628,7 +623,7 @@ static const struct drm_encoder_helper_funcs intel_lvds_helper_funcs = { static const struct drm_connector_helper_funcs intel_lvds_connector_helper_funcs = { .get_modes = intel_lvds_get_modes, .mode_valid = intel_lvds_mode_valid, - .best_encoder = intel_attached_encoder, + .best_encoder = intel_best_encoder, }; static const struct drm_connector_funcs intel_lvds_connector_funcs = { @@ -726,16 +721,14 @@ static const struct dmi_system_id intel_no_lvds[] = { * Find the reduced downclock for LVDS in EDID. */ static void intel_find_lvds_downclock(struct drm_device *dev, - struct drm_connector *connector) + struct drm_display_mode *fixed_mode, + struct drm_connector *connector) { struct drm_i915_private *dev_priv = dev->dev_private; - struct drm_display_mode *scan, *panel_fixed_mode; + struct drm_display_mode *scan; int temp_downclock; - panel_fixed_mode = dev_priv->panel_fixed_mode; - temp_downclock = panel_fixed_mode->clock; - - mutex_lock(&dev->mode_config.mutex); + temp_downclock = fixed_mode->clock; list_for_each_entry(scan, &connector->probed_modes, head) { /* * If one mode has the same resolution with the fixed_panel @@ -744,14 +737,14 @@ static void intel_find_lvds_downclock(struct drm_device *dev, * case we can set the different FPx0/1 to dynamically select * between low and high frequency. */ - if (scan->hdisplay == panel_fixed_mode->hdisplay && - scan->hsync_start == panel_fixed_mode->hsync_start && - scan->hsync_end == panel_fixed_mode->hsync_end && - scan->htotal == panel_fixed_mode->htotal && - scan->vdisplay == panel_fixed_mode->vdisplay && - scan->vsync_start == panel_fixed_mode->vsync_start && - scan->vsync_end == panel_fixed_mode->vsync_end && - scan->vtotal == panel_fixed_mode->vtotal) { + if (scan->hdisplay == fixed_mode->hdisplay && + scan->hsync_start == fixed_mode->hsync_start && + scan->hsync_end == fixed_mode->hsync_end && + scan->htotal == fixed_mode->htotal && + scan->vdisplay == fixed_mode->vdisplay && + scan->vsync_start == fixed_mode->vsync_start && + scan->vsync_end == fixed_mode->vsync_end && + scan->vtotal == fixed_mode->vtotal) { if (scan->clock < temp_downclock) { /* * The downclock is already found. But we @@ -761,17 +754,14 @@ static void intel_find_lvds_downclock(struct drm_device *dev, } } } - mutex_unlock(&dev->mode_config.mutex); - if (temp_downclock < panel_fixed_mode->clock && - i915_lvds_downclock) { + if (temp_downclock < fixed_mode->clock && i915_lvds_downclock) { /* We found the downclock for LVDS. */ dev_priv->lvds_downclock_avail = 1; dev_priv->lvds_downclock = temp_downclock; DRM_DEBUG_KMS("LVDS downclock is found in EDID. " - "Normal clock %dKhz, downclock %dKhz\n", - panel_fixed_mode->clock, temp_downclock); + "Normal clock %dKhz, downclock %dKhz\n", + fixed_mode->clock, temp_downclock); } - return; } /* @@ -780,38 +770,67 @@ static void intel_find_lvds_downclock(struct drm_device *dev, * If it is present, return 1. * If it is not present, return false. * If no child dev is parsed from VBT, it assumes that the LVDS is present. - * Note: The addin_offset should also be checked for LVDS panel. - * Only when it is non-zero, it is assumed that it is present. */ -static int lvds_is_present_in_vbt(struct drm_device *dev) +static bool lvds_is_present_in_vbt(struct drm_device *dev, + u8 *i2c_pin) { struct drm_i915_private *dev_priv = dev->dev_private; - struct child_device_config *p_child; - int i, ret; + int i; if (!dev_priv->child_dev_num) - return 1; + return true; - ret = 0; for (i = 0; i < dev_priv->child_dev_num; i++) { - p_child = dev_priv->child_dev + i; - /* - * If the device type is not LFP, continue. - * If the device type is 0x22, it is also regarded as LFP. + struct child_device_config *child = dev_priv->child_dev + i; + + /* If the device type is not LFP, continue. + * We have to check both the new identifiers as well as the + * old for compatibility with some BIOSes. */ - if (p_child->device_type != DEVICE_TYPE_INT_LFP && - p_child->device_type != DEVICE_TYPE_LFP) + if (child->device_type != DEVICE_TYPE_INT_LFP && + child->device_type != DEVICE_TYPE_LFP) continue; - /* The addin_offset should be checked. Only when it is - * non-zero, it is regarded as present. + if (child->i2c_pin) + *i2c_pin = child->i2c_pin; + + /* However, we cannot trust the BIOS writers to populate + * the VBT correctly. Since LVDS requires additional + * information from AIM blocks, a non-zero addin offset is + * a good indicator that the LVDS is actually present. */ - if (p_child->addin_offset) { - ret = 1; - break; - } + if (child->addin_offset) + return true; + + /* But even then some BIOS writers perform some black magic + * and instantiate the device without reference to any + * additional data. Trust that if the VBT was written into + * the OpRegion then they have validated the LVDS's existence. + */ + if (dev_priv->opregion.vbt) + return true; } - return ret; + + return false; +} + +static bool intel_lvds_ddc_probe(struct drm_device *dev, u8 pin) +{ + struct drm_i915_private *dev_priv = dev->dev_private; + u8 buf = 0; + struct i2c_msg msgs[] = { + { + .addr = 0xA0, + .flags = 0, + .len = 1, + .buf = &buf, + }, + }; + struct i2c_adapter *i2c = &dev_priv->gmbus[pin].adapter; + /* XXX this only appears to work when using GMBUS */ + if (intel_gmbus_is_forced_bit(i2c)) + return true; + return i2c_transfer(i2c, msgs, 1) == 1; } /** @@ -832,13 +851,15 @@ void intel_lvds_init(struct drm_device *dev) struct drm_display_mode *scan; /* *modes, *bios_mode; */ struct drm_crtc *crtc; u32 lvds; - int pipe, gpio = GPIOC; + int pipe; + u8 pin; /* Skip init on machines we know falsely report LVDS */ if (dmi_check_system(intel_no_lvds)) return; - if (!lvds_is_present_in_vbt(dev)) { + pin = GMBUS_PORT_PANEL; + if (!lvds_is_present_in_vbt(dev, &pin)) { DRM_DEBUG_KMS("LVDS is not present in VBT\n"); return; } @@ -846,11 +867,15 @@ void intel_lvds_init(struct drm_device *dev) if (HAS_PCH_SPLIT(dev)) { if ((I915_READ(PCH_LVDS) & LVDS_DETECTED) == 0) return; - if (dev_priv->edp_support) { + if (dev_priv->edp.support) { DRM_DEBUG_KMS("disable LVDS for eDP support\n"); return; } - gpio = PCH_GPIOC; + } + + if (!intel_lvds_ddc_probe(dev, pin)) { + DRM_DEBUG_KMS("LVDS did not respond to DDC probe\n"); + return; } intel_lvds = kzalloc(sizeof(struct intel_lvds), GFP_KERNEL); @@ -864,16 +889,20 @@ void intel_lvds_init(struct drm_device *dev) return; } + if (!HAS_PCH_SPLIT(dev)) { + intel_lvds->pfit_control = I915_READ(PFIT_CONTROL); + } + intel_encoder = &intel_lvds->base; - encoder = &intel_encoder->enc; + encoder = &intel_encoder->base; connector = &intel_connector->base; drm_connector_init(dev, &intel_connector->base, &intel_lvds_connector_funcs, DRM_MODE_CONNECTOR_LVDS); - drm_encoder_init(dev, &intel_encoder->enc, &intel_lvds_enc_funcs, + drm_encoder_init(dev, &intel_encoder->base, &intel_lvds_enc_funcs, DRM_MODE_ENCODER_LVDS); - drm_mode_connector_attach_encoder(&intel_connector->base, &intel_encoder->enc); + intel_connector_attach_encoder(intel_connector, intel_encoder); intel_encoder->type = INTEL_OUTPUT_LVDS; intel_encoder->clone_mask = (1 << INTEL_LVDS_CLONE_BIT); @@ -904,43 +933,41 @@ void intel_lvds_init(struct drm_device *dev) * if closed, act like it's not there for now */ - /* Set up the DDC bus. */ - intel_encoder->ddc_bus = intel_i2c_create(dev, gpio, "LVDSDDC_C"); - if (!intel_encoder->ddc_bus) { - dev_printk(KERN_ERR, &dev->pdev->dev, "DDC bus registration " - "failed.\n"); - goto failed; - } - /* * Attempt to get the fixed panel mode from DDC. Assume that the * preferred mode is the right one. */ - dev_priv->lvds_edid_good = true; + intel_lvds->edid = drm_get_edid(connector, + &dev_priv->gmbus[pin].adapter); - if (!intel_ddc_get_modes(connector, intel_encoder->ddc_bus)) - dev_priv->lvds_edid_good = false; + if (!intel_lvds->edid) { + /* Didn't get an EDID, so + * Set wide sync ranges so we get all modes + * handed to valid_mode for checking + */ + connector->display_info.min_vfreq = 0; + connector->display_info.max_vfreq = 200; + connector->display_info.min_hfreq = 0; + connector->display_info.max_hfreq = 200; + } list_for_each_entry(scan, &connector->probed_modes, head) { - mutex_lock(&dev->mode_config.mutex); if (scan->type & DRM_MODE_TYPE_PREFERRED) { - dev_priv->panel_fixed_mode = + intel_lvds->fixed_mode = drm_mode_duplicate(dev, scan); - mutex_unlock(&dev->mode_config.mutex); - intel_find_lvds_downclock(dev, connector); + intel_find_lvds_downclock(dev, + intel_lvds->fixed_mode, + connector); goto out; } - mutex_unlock(&dev->mode_config.mutex); } /* Failed to get EDID, what about VBT? */ if (dev_priv->lfp_lvds_vbt_mode) { - mutex_lock(&dev->mode_config.mutex); - dev_priv->panel_fixed_mode = + intel_lvds->fixed_mode = drm_mode_duplicate(dev, dev_priv->lfp_lvds_vbt_mode); - mutex_unlock(&dev->mode_config.mutex); - if (dev_priv->panel_fixed_mode) { - dev_priv->panel_fixed_mode->type |= + if (intel_lvds->fixed_mode) { + intel_lvds->fixed_mode->type |= DRM_MODE_TYPE_PREFERRED; goto out; } @@ -958,19 +985,19 @@ void intel_lvds_init(struct drm_device *dev) lvds = I915_READ(LVDS); pipe = (lvds & LVDS_PIPEB_SELECT) ? 1 : 0; - crtc = intel_get_crtc_from_pipe(dev, pipe); + crtc = intel_get_crtc_for_pipe(dev, pipe); if (crtc && (lvds & LVDS_PORT_EN)) { - dev_priv->panel_fixed_mode = intel_crtc_mode_get(dev, crtc); - if (dev_priv->panel_fixed_mode) { - dev_priv->panel_fixed_mode->type |= + intel_lvds->fixed_mode = intel_crtc_mode_get(dev, crtc); + if (intel_lvds->fixed_mode) { + intel_lvds->fixed_mode->type |= DRM_MODE_TYPE_PREFERRED; goto out; } } /* If we still don't have a mode after all that, give up. */ - if (!dev_priv->panel_fixed_mode) + if (!intel_lvds->fixed_mode) goto failed; out: @@ -997,8 +1024,6 @@ out: failed: DRM_DEBUG_KMS("No LVDS modes found, disabling.\n"); - if (intel_encoder->ddc_bus) - intel_i2c_destroy(intel_encoder->ddc_bus); drm_connector_cleanup(connector); drm_encoder_cleanup(encoder); kfree(intel_lvds); diff --git a/drivers/gpu/drm/i915/intel_modes.c b/drivers/gpu/drm/i915/intel_modes.c index 4b1fd3d9c73..f70b7cf32bf 100644 --- a/drivers/gpu/drm/i915/intel_modes.c +++ b/drivers/gpu/drm/i915/intel_modes.c @@ -1,6 +1,6 @@ /* * Copyright (c) 2007 Dave Airlie <airlied@linux.ie> - * Copyright (c) 2007 Intel Corporation + * Copyright (c) 2007, 2010 Intel Corporation * Jesse Barnes <jesse.barnes@intel.com> * * Permission is hereby granted, free of charge, to any person obtaining a @@ -34,11 +34,11 @@ * intel_ddc_probe * */ -bool intel_ddc_probe(struct intel_encoder *intel_encoder) +bool intel_ddc_probe(struct intel_encoder *intel_encoder, int ddc_bus) { + struct drm_i915_private *dev_priv = intel_encoder->base.dev->dev_private; u8 out_buf[] = { 0x0, 0x0}; u8 buf[2]; - int ret; struct i2c_msg msgs[] = { { .addr = 0x50, @@ -54,13 +54,7 @@ bool intel_ddc_probe(struct intel_encoder *intel_encoder) } }; - intel_i2c_quirk_set(intel_encoder->enc.dev, true); - ret = i2c_transfer(intel_encoder->ddc_bus, msgs, 2); - intel_i2c_quirk_set(intel_encoder->enc.dev, false); - if (ret == 2) - return true; - - return false; + return i2c_transfer(&dev_priv->gmbus[ddc_bus].adapter, msgs, 2) == 2; } /** @@ -76,9 +70,7 @@ int intel_ddc_get_modes(struct drm_connector *connector, struct edid *edid; int ret = 0; - intel_i2c_quirk_set(connector->dev, true); edid = drm_get_edid(connector, adapter); - intel_i2c_quirk_set(connector->dev, false); if (edid) { drm_mode_connector_update_edid_property(connector, edid); ret = drm_add_edid_modes(connector, edid); diff --git a/drivers/gpu/drm/i915/i915_opregion.c b/drivers/gpu/drm/i915/intel_opregion.c index ea5d3fea4b6..917c7dc3cd6 100644 --- a/drivers/gpu/drm/i915/i915_opregion.c +++ b/drivers/gpu/drm/i915/intel_opregion.c @@ -31,17 +31,16 @@ #include "drmP.h" #include "i915_drm.h" #include "i915_drv.h" +#include "intel_drv.h" #define PCI_ASLE 0xe4 -#define PCI_LBPC 0xf4 #define PCI_ASLS 0xfc -#define OPREGION_SZ (8*1024) #define OPREGION_HEADER_OFFSET 0 #define OPREGION_ACPI_OFFSET 0x100 #define OPREGION_SWSCI_OFFSET 0x200 #define OPREGION_ASLE_OFFSET 0x300 -#define OPREGION_VBT_OFFSET 0x1000 +#define OPREGION_VBT_OFFSET 0x400 #define OPREGION_SIGNATURE "IntelGraphicsMem" #define MBOX_ACPI (1<<0) @@ -143,40 +142,22 @@ struct opregion_asle { #define ACPI_DIGITAL_OUTPUT (3<<8) #define ACPI_LVDS_OUTPUT (4<<8) +#ifdef CONFIG_ACPI static u32 asle_set_backlight(struct drm_device *dev, u32 bclp) { struct drm_i915_private *dev_priv = dev->dev_private; struct opregion_asle *asle = dev_priv->opregion.asle; - u32 blc_pwm_ctl, blc_pwm_ctl2; - u32 max_backlight, level, shift; + u32 max; if (!(bclp & ASLE_BCLP_VALID)) return ASLE_BACKLIGHT_FAILED; bclp &= ASLE_BCLP_MSK; - if (bclp < 0 || bclp > 255) + if (bclp > 255) return ASLE_BACKLIGHT_FAILED; - blc_pwm_ctl = I915_READ(BLC_PWM_CTL); - blc_pwm_ctl2 = I915_READ(BLC_PWM_CTL2); - - if (IS_I965G(dev) && (blc_pwm_ctl2 & BLM_COMBINATION_MODE)) - pci_write_config_dword(dev->pdev, PCI_LBPC, bclp); - else { - if (IS_PINEVIEW(dev)) { - blc_pwm_ctl &= ~(BACKLIGHT_DUTY_CYCLE_MASK - 1); - max_backlight = (blc_pwm_ctl & BACKLIGHT_MODULATION_FREQ_MASK) >> - BACKLIGHT_MODULATION_FREQ_SHIFT; - shift = BACKLIGHT_DUTY_CYCLE_SHIFT + 1; - } else { - blc_pwm_ctl &= ~BACKLIGHT_DUTY_CYCLE_MASK; - max_backlight = ((blc_pwm_ctl & BACKLIGHT_MODULATION_FREQ_MASK) >> - BACKLIGHT_MODULATION_FREQ_SHIFT) * 2; - shift = BACKLIGHT_DUTY_CYCLE_SHIFT; - } - level = (bclp * max_backlight) / 255; - I915_WRITE(BLC_PWM_CTL, blc_pwm_ctl | (level << shift)); - } + max = intel_panel_get_max_backlight(dev); + intel_panel_set_backlight(dev, bclp * max / 255); asle->cblv = (bclp*0x64)/0xff | ASLE_CBLV_VALID; return 0; @@ -211,7 +192,7 @@ static u32 asle_set_pfit(struct drm_device *dev, u32 pfit) return 0; } -void opregion_asle_intr(struct drm_device *dev) +void intel_opregion_asle_intr(struct drm_device *dev) { struct drm_i915_private *dev_priv = dev->dev_private; struct opregion_asle *asle = dev_priv->opregion.asle; @@ -243,37 +224,8 @@ void opregion_asle_intr(struct drm_device *dev) asle->aslc = asle_stat; } -static u32 asle_set_backlight_ironlake(struct drm_device *dev, u32 bclp) -{ - struct drm_i915_private *dev_priv = dev->dev_private; - struct opregion_asle *asle = dev_priv->opregion.asle; - u32 cpu_pwm_ctl, pch_pwm_ctl2; - u32 max_backlight, level; - - if (!(bclp & ASLE_BCLP_VALID)) - return ASLE_BACKLIGHT_FAILED; - - bclp &= ASLE_BCLP_MSK; - if (bclp < 0 || bclp > 255) - return ASLE_BACKLIGHT_FAILED; - - cpu_pwm_ctl = I915_READ(BLC_PWM_CPU_CTL); - pch_pwm_ctl2 = I915_READ(BLC_PWM_PCH_CTL2); - /* get the max PWM frequency */ - max_backlight = (pch_pwm_ctl2 >> 16) & BACKLIGHT_DUTY_CYCLE_MASK; - /* calculate the expected PMW frequency */ - level = (bclp * max_backlight) / 255; - /* reserve the high 16 bits */ - cpu_pwm_ctl &= ~(BACKLIGHT_DUTY_CYCLE_MASK); - /* write the updated PWM frequency */ - I915_WRITE(BLC_PWM_CPU_CTL, cpu_pwm_ctl | level); - - asle->cblv = (bclp*0x64)/0xff | ASLE_CBLV_VALID; - - return 0; -} - -void ironlake_opregion_gse_intr(struct drm_device *dev) +/* Only present on Ironlake+ */ +void intel_opregion_gse_intr(struct drm_device *dev) { struct drm_i915_private *dev_priv = dev->dev_private; struct opregion_asle *asle = dev_priv->opregion.asle; @@ -296,7 +248,7 @@ void ironlake_opregion_gse_intr(struct drm_device *dev) } if (asle_req & ASLE_SET_BACKLIGHT) - asle_stat |= asle_set_backlight_ironlake(dev, asle->bclp); + asle_stat |= asle_set_backlight(dev, asle->bclp); if (asle_req & ASLE_SET_PFIT) { DRM_DEBUG_DRIVER("Pfit is not supported\n"); @@ -315,7 +267,7 @@ void ironlake_opregion_gse_intr(struct drm_device *dev) #define ASLE_PFIT_EN (1<<2) #define ASLE_PFMB_EN (1<<3) -void opregion_enable_asle(struct drm_device *dev) +void intel_opregion_enable_asle(struct drm_device *dev) { struct drm_i915_private *dev_priv = dev->dev_private; struct opregion_asle *asle = dev_priv->opregion.asle; @@ -464,7 +416,58 @@ blind_set: goto end; } -int intel_opregion_init(struct drm_device *dev, int resume) +void intel_opregion_init(struct drm_device *dev) +{ + struct drm_i915_private *dev_priv = dev->dev_private; + struct intel_opregion *opregion = &dev_priv->opregion; + + if (!opregion->header) + return; + + if (opregion->acpi) { + if (drm_core_check_feature(dev, DRIVER_MODESET)) + intel_didl_outputs(dev); + + /* Notify BIOS we are ready to handle ACPI video ext notifs. + * Right now, all the events are handled by the ACPI video module. + * We don't actually need to do anything with them. */ + opregion->acpi->csts = 0; + opregion->acpi->drdy = 1; + + system_opregion = opregion; + register_acpi_notifier(&intel_opregion_notifier); + } + + if (opregion->asle) + intel_opregion_enable_asle(dev); +} + +void intel_opregion_fini(struct drm_device *dev) +{ + struct drm_i915_private *dev_priv = dev->dev_private; + struct intel_opregion *opregion = &dev_priv->opregion; + + if (!opregion->header) + return; + + if (opregion->acpi) { + opregion->acpi->drdy = 0; + + system_opregion = NULL; + unregister_acpi_notifier(&intel_opregion_notifier); + } + + /* just clear all opregion memory pointers now */ + iounmap(opregion->header); + opregion->header = NULL; + opregion->acpi = NULL; + opregion->swsci = NULL; + opregion->asle = NULL; + opregion->vbt = NULL; +} +#endif + +int intel_opregion_setup(struct drm_device *dev) { struct drm_i915_private *dev_priv = dev->dev_private; struct intel_opregion *opregion = &dev_priv->opregion; @@ -479,29 +482,23 @@ int intel_opregion_init(struct drm_device *dev, int resume) return -ENOTSUPP; } - base = ioremap(asls, OPREGION_SZ); + base = ioremap(asls, OPREGION_SIZE); if (!base) return -ENOMEM; - opregion->header = base; - if (memcmp(opregion->header->signature, OPREGION_SIGNATURE, 16)) { + if (memcmp(base, OPREGION_SIGNATURE, 16)) { DRM_DEBUG_DRIVER("opregion signature mismatch\n"); err = -EINVAL; goto err_out; } + opregion->header = base; + opregion->vbt = base + OPREGION_VBT_OFFSET; mboxes = opregion->header->mboxes; if (mboxes & MBOX_ACPI) { DRM_DEBUG_DRIVER("Public ACPI methods supported\n"); opregion->acpi = base + OPREGION_ACPI_OFFSET; - if (drm_core_check_feature(dev, DRIVER_MODESET)) - intel_didl_outputs(dev); - } else { - DRM_DEBUG_DRIVER("Public ACPI methods not supported\n"); - err = -ENOTSUPP; - goto err_out; } - opregion->enabled = 1; if (mboxes & MBOX_SWSCI) { DRM_DEBUG_DRIVER("SWSCI supported\n"); @@ -510,53 +507,11 @@ int intel_opregion_init(struct drm_device *dev, int resume) if (mboxes & MBOX_ASLE) { DRM_DEBUG_DRIVER("ASLE supported\n"); opregion->asle = base + OPREGION_ASLE_OFFSET; - opregion_enable_asle(dev); } - if (!resume) - acpi_video_register(); - - - /* Notify BIOS we are ready to handle ACPI video ext notifs. - * Right now, all the events are handled by the ACPI video module. - * We don't actually need to do anything with them. */ - opregion->acpi->csts = 0; - opregion->acpi->drdy = 1; - - system_opregion = opregion; - register_acpi_notifier(&intel_opregion_notifier); - return 0; err_out: iounmap(opregion->header); - opregion->header = NULL; - acpi_video_register(); return err; } - -void intel_opregion_free(struct drm_device *dev, int suspend) -{ - struct drm_i915_private *dev_priv = dev->dev_private; - struct intel_opregion *opregion = &dev_priv->opregion; - - if (!opregion->enabled) - return; - - if (!suspend) - acpi_video_unregister(); - - opregion->acpi->drdy = 0; - - system_opregion = NULL; - unregister_acpi_notifier(&intel_opregion_notifier); - - /* just clear all opregion memory pointers now */ - iounmap(opregion->header); - opregion->header = NULL; - opregion->acpi = NULL; - opregion->swsci = NULL; - opregion->asle = NULL; - - opregion->enabled = 0; -} diff --git a/drivers/gpu/drm/i915/intel_overlay.c b/drivers/gpu/drm/i915/intel_overlay.c index 1d306a458be..375316a8420 100644 --- a/drivers/gpu/drm/i915/intel_overlay.c +++ b/drivers/gpu/drm/i915/intel_overlay.c @@ -170,57 +170,143 @@ struct overlay_registers { u16 RESERVEDG[0x100 / 2 - N_HORIZ_UV_TAPS * N_PHASES]; }; -/* overlay flip addr flag */ -#define OFC_UPDATE 0x1 - -#define OVERLAY_NONPHYSICAL(dev) (IS_G33(dev) || IS_I965G(dev)) -#define OVERLAY_EXISTS(dev) (!IS_G4X(dev) && !IS_IRONLAKE(dev) && !IS_GEN6(dev)) - +struct intel_overlay { + struct drm_device *dev; + struct intel_crtc *crtc; + struct drm_i915_gem_object *vid_bo; + struct drm_i915_gem_object *old_vid_bo; + int active; + int pfit_active; + u32 pfit_vscale_ratio; /* shifted-point number, (1<<12) == 1.0 */ + u32 color_key; + u32 brightness, contrast, saturation; + u32 old_xscale, old_yscale; + /* register access */ + u32 flip_addr; + struct drm_i915_gem_object *reg_bo; + /* flip handling */ + uint32_t last_flip_req; + void (*flip_tail)(struct intel_overlay *); +}; -static struct overlay_registers *intel_overlay_map_regs_atomic(struct intel_overlay *overlay) +static struct overlay_registers * +intel_overlay_map_regs(struct intel_overlay *overlay) { drm_i915_private_t *dev_priv = overlay->dev->dev_private; struct overlay_registers *regs; - /* no recursive mappings */ - BUG_ON(overlay->virt_addr); + if (OVERLAY_NEEDS_PHYSICAL(overlay->dev)) + regs = overlay->reg_bo->phys_obj->handle->vaddr; + else + regs = io_mapping_map_wc(dev_priv->mm.gtt_mapping, + overlay->reg_bo->gtt_offset); - if (OVERLAY_NONPHYSICAL(overlay->dev)) { - regs = io_mapping_map_atomic_wc(dev_priv->mm.gtt_mapping, - overlay->reg_bo->gtt_offset, - KM_USER0); + return regs; +} - if (!regs) { - DRM_ERROR("failed to map overlay regs in GTT\n"); - return NULL; - } - } else - regs = overlay->reg_bo->phys_obj->handle->vaddr; +static void intel_overlay_unmap_regs(struct intel_overlay *overlay, + struct overlay_registers *regs) +{ + if (!OVERLAY_NEEDS_PHYSICAL(overlay->dev)) + io_mapping_unmap(regs); +} + +static int intel_overlay_do_wait_request(struct intel_overlay *overlay, + struct drm_i915_gem_request *request, + bool interruptible, + void (*tail)(struct intel_overlay *)) +{ + struct drm_device *dev = overlay->dev; + drm_i915_private_t *dev_priv = dev->dev_private; + int ret; + + BUG_ON(overlay->last_flip_req); + overlay->last_flip_req = + i915_add_request(dev, NULL, request, &dev_priv->render_ring); + if (overlay->last_flip_req == 0) + return -ENOMEM; - return overlay->virt_addr = regs; + overlay->flip_tail = tail; + ret = i915_do_wait_request(dev, + overlay->last_flip_req, true, + &dev_priv->render_ring); + if (ret) + return ret; + + overlay->last_flip_req = 0; + return 0; } -static void intel_overlay_unmap_regs_atomic(struct intel_overlay *overlay) +/* Workaround for i830 bug where pipe a must be enable to change control regs */ +static int +i830_activate_pipe_a(struct drm_device *dev) { - if (OVERLAY_NONPHYSICAL(overlay->dev)) - io_mapping_unmap_atomic(overlay->virt_addr, KM_USER0); + drm_i915_private_t *dev_priv = dev->dev_private; + struct intel_crtc *crtc; + struct drm_crtc_helper_funcs *crtc_funcs; + struct drm_display_mode vesa_640x480 = { + DRM_MODE("640x480", DRM_MODE_TYPE_DRIVER, 25175, 640, 656, + 752, 800, 0, 480, 489, 492, 525, 0, + DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC) + }, *mode; + + crtc = to_intel_crtc(dev_priv->pipe_to_crtc_mapping[0]); + if (crtc->dpms_mode == DRM_MODE_DPMS_ON) + return 0; - overlay->virt_addr = NULL; + /* most i8xx have pipe a forced on, so don't trust dpms mode */ + if (I915_READ(PIPEACONF) & PIPECONF_ENABLE) + return 0; - return; + crtc_funcs = crtc->base.helper_private; + if (crtc_funcs->dpms == NULL) + return 0; + + DRM_DEBUG_DRIVER("Enabling pipe A in order to enable overlay\n"); + + mode = drm_mode_duplicate(dev, &vesa_640x480); + drm_mode_set_crtcinfo(mode, CRTC_INTERLACE_HALVE_V); + if(!drm_crtc_helper_set_mode(&crtc->base, mode, + crtc->base.x, crtc->base.y, + crtc->base.fb)) + return 0; + + crtc_funcs->dpms(&crtc->base, DRM_MODE_DPMS_ON); + return 1; +} + +static void +i830_deactivate_pipe_a(struct drm_device *dev) +{ + drm_i915_private_t *dev_priv = dev->dev_private; + struct drm_crtc *crtc = dev_priv->pipe_to_crtc_mapping[0]; + struct drm_crtc_helper_funcs *crtc_funcs = crtc->helper_private; + + crtc_funcs->dpms(crtc, DRM_MODE_DPMS_OFF); } /* overlay needs to be disable in OCMD reg */ static int intel_overlay_on(struct intel_overlay *overlay) { struct drm_device *dev = overlay->dev; + struct drm_i915_gem_request *request; + int pipe_a_quirk = 0; int ret; - drm_i915_private_t *dev_priv = dev->dev_private; BUG_ON(overlay->active); - overlay->active = 1; - overlay->hw_wedged = NEEDS_WAIT_FOR_FLIP; + + if (IS_I830(dev)) { + pipe_a_quirk = i830_activate_pipe_a(dev); + if (pipe_a_quirk < 0) + return pipe_a_quirk; + } + + request = kzalloc(sizeof(*request), GFP_KERNEL); + if (request == NULL) { + ret = -ENOMEM; + goto out; + } BEGIN_LP_RING(4); OUT_RING(MI_OVERLAY_FLIP | MI_OVERLAY_ON); @@ -229,32 +315,30 @@ static int intel_overlay_on(struct intel_overlay *overlay) OUT_RING(MI_NOOP); ADVANCE_LP_RING(); - overlay->last_flip_req = - i915_add_request(dev, NULL, 0, &dev_priv->render_ring); - if (overlay->last_flip_req == 0) - return -ENOMEM; - - ret = i915_do_wait_request(dev, - overlay->last_flip_req, 1, &dev_priv->render_ring); - if (ret != 0) - return ret; + ret = intel_overlay_do_wait_request(overlay, request, true, NULL); +out: + if (pipe_a_quirk) + i830_deactivate_pipe_a(dev); - overlay->hw_wedged = 0; - overlay->last_flip_req = 0; - return 0; + return ret; } /* overlay needs to be enabled in OCMD reg */ -static void intel_overlay_continue(struct intel_overlay *overlay, - bool load_polyphase_filter) +static int intel_overlay_continue(struct intel_overlay *overlay, + bool load_polyphase_filter) { struct drm_device *dev = overlay->dev; drm_i915_private_t *dev_priv = dev->dev_private; + struct drm_i915_gem_request *request; u32 flip_addr = overlay->flip_addr; u32 tmp; BUG_ON(!overlay->active); + request = kzalloc(sizeof(*request), GFP_KERNEL); + if (request == NULL) + return -ENOMEM; + if (load_polyphase_filter) flip_addr |= OFC_UPDATE; @@ -269,220 +353,132 @@ static void intel_overlay_continue(struct intel_overlay *overlay, ADVANCE_LP_RING(); overlay->last_flip_req = - i915_add_request(dev, NULL, 0, &dev_priv->render_ring); + i915_add_request(dev, NULL, request, &dev_priv->render_ring); + return 0; } -static int intel_overlay_wait_flip(struct intel_overlay *overlay) +static void intel_overlay_release_old_vid_tail(struct intel_overlay *overlay) { - struct drm_device *dev = overlay->dev; - drm_i915_private_t *dev_priv = dev->dev_private; - int ret; - u32 tmp; - - if (overlay->last_flip_req != 0) { - ret = i915_do_wait_request(dev, overlay->last_flip_req, - 1, &dev_priv->render_ring); - if (ret == 0) { - overlay->last_flip_req = 0; + struct drm_gem_object *obj = &overlay->old_vid_bo->base; - tmp = I915_READ(ISR); - - if (!(tmp & I915_OVERLAY_PLANE_FLIP_PENDING_INTERRUPT)) - return 0; - } - } + i915_gem_object_unpin(obj); + drm_gem_object_unreference(obj); - /* synchronous slowpath */ - overlay->hw_wedged = RELEASE_OLD_VID; + overlay->old_vid_bo = NULL; +} - BEGIN_LP_RING(2); - OUT_RING(MI_WAIT_FOR_EVENT | MI_WAIT_FOR_OVERLAY_FLIP); - OUT_RING(MI_NOOP); - ADVANCE_LP_RING(); +static void intel_overlay_off_tail(struct intel_overlay *overlay) +{ + struct drm_gem_object *obj; - overlay->last_flip_req = - i915_add_request(dev, NULL, 0, &dev_priv->render_ring); - if (overlay->last_flip_req == 0) - return -ENOMEM; + /* never have the overlay hw on without showing a frame */ + BUG_ON(!overlay->vid_bo); + obj = &overlay->vid_bo->base; - ret = i915_do_wait_request(dev, overlay->last_flip_req, - 1, &dev_priv->render_ring); - if (ret != 0) - return ret; + i915_gem_object_unpin(obj); + drm_gem_object_unreference(obj); + overlay->vid_bo = NULL; - overlay->hw_wedged = 0; - overlay->last_flip_req = 0; - return 0; + overlay->crtc->overlay = NULL; + overlay->crtc = NULL; + overlay->active = 0; } /* overlay needs to be disabled in OCMD reg */ -static int intel_overlay_off(struct intel_overlay *overlay) +static int intel_overlay_off(struct intel_overlay *overlay, + bool interruptible) { - u32 flip_addr = overlay->flip_addr; struct drm_device *dev = overlay->dev; - drm_i915_private_t *dev_priv = dev->dev_private; - int ret; + u32 flip_addr = overlay->flip_addr; + struct drm_i915_gem_request *request; BUG_ON(!overlay->active); + request = kzalloc(sizeof(*request), GFP_KERNEL); + if (request == NULL) + return -ENOMEM; + /* According to intel docs the overlay hw may hang (when switching * off) without loading the filter coeffs. It is however unclear whether * this applies to the disabling of the overlay or to the switching off * of the hw. Do it in both cases */ flip_addr |= OFC_UPDATE; + BEGIN_LP_RING(6); /* wait for overlay to go idle */ - overlay->hw_wedged = SWITCH_OFF_STAGE_1; - - BEGIN_LP_RING(4); OUT_RING(MI_OVERLAY_FLIP | MI_OVERLAY_CONTINUE); OUT_RING(flip_addr); - OUT_RING(MI_WAIT_FOR_EVENT | MI_WAIT_FOR_OVERLAY_FLIP); - OUT_RING(MI_NOOP); - ADVANCE_LP_RING(); - - overlay->last_flip_req = - i915_add_request(dev, NULL, 0, &dev_priv->render_ring); - if (overlay->last_flip_req == 0) - return -ENOMEM; - - ret = i915_do_wait_request(dev, overlay->last_flip_req, - 1, &dev_priv->render_ring); - if (ret != 0) - return ret; - + OUT_RING(MI_WAIT_FOR_EVENT | MI_WAIT_FOR_OVERLAY_FLIP); /* turn overlay off */ - overlay->hw_wedged = SWITCH_OFF_STAGE_2; - - BEGIN_LP_RING(4); - OUT_RING(MI_OVERLAY_FLIP | MI_OVERLAY_OFF); + OUT_RING(MI_OVERLAY_FLIP | MI_OVERLAY_OFF); OUT_RING(flip_addr); - OUT_RING(MI_WAIT_FOR_EVENT | MI_WAIT_FOR_OVERLAY_FLIP); - OUT_RING(MI_NOOP); + OUT_RING(MI_WAIT_FOR_EVENT | MI_WAIT_FOR_OVERLAY_FLIP); ADVANCE_LP_RING(); - overlay->last_flip_req = - i915_add_request(dev, NULL, 0, &dev_priv->render_ring); - if (overlay->last_flip_req == 0) - return -ENOMEM; - - ret = i915_do_wait_request(dev, overlay->last_flip_req, - 1, &dev_priv->render_ring); - if (ret != 0) - return ret; - - overlay->hw_wedged = 0; - overlay->last_flip_req = 0; - return ret; -} - -static void intel_overlay_off_tail(struct intel_overlay *overlay) -{ - struct drm_gem_object *obj; - - /* never have the overlay hw on without showing a frame */ - BUG_ON(!overlay->vid_bo); - obj = &overlay->vid_bo->base; - - i915_gem_object_unpin(obj); - drm_gem_object_unreference(obj); - overlay->vid_bo = NULL; - - overlay->crtc->overlay = NULL; - overlay->crtc = NULL; - overlay->active = 0; + return intel_overlay_do_wait_request(overlay, request, interruptible, + intel_overlay_off_tail); } /* recover from an interruption due to a signal * We have to be careful not to repeat work forever an make forward progess. */ -int intel_overlay_recover_from_interrupt(struct intel_overlay *overlay, - int interruptible) +static int intel_overlay_recover_from_interrupt(struct intel_overlay *overlay, + bool interruptible) { struct drm_device *dev = overlay->dev; - struct drm_gem_object *obj; drm_i915_private_t *dev_priv = dev->dev_private; - u32 flip_addr; int ret; - if (overlay->hw_wedged == HW_WEDGED) - return -EIO; - - if (overlay->last_flip_req == 0) { - overlay->last_flip_req = - i915_add_request(dev, NULL, 0, &dev_priv->render_ring); - if (overlay->last_flip_req == 0) - return -ENOMEM; - } + if (overlay->last_flip_req == 0) + return 0; ret = i915_do_wait_request(dev, overlay->last_flip_req, - interruptible, &dev_priv->render_ring); - if (ret != 0) + interruptible, &dev_priv->render_ring); + if (ret) return ret; - switch (overlay->hw_wedged) { - case RELEASE_OLD_VID: - obj = &overlay->old_vid_bo->base; - i915_gem_object_unpin(obj); - drm_gem_object_unreference(obj); - overlay->old_vid_bo = NULL; - break; - case SWITCH_OFF_STAGE_1: - flip_addr = overlay->flip_addr; - flip_addr |= OFC_UPDATE; - - overlay->hw_wedged = SWITCH_OFF_STAGE_2; - - BEGIN_LP_RING(4); - OUT_RING(MI_OVERLAY_FLIP | MI_OVERLAY_OFF); - OUT_RING(flip_addr); - OUT_RING(MI_WAIT_FOR_EVENT | MI_WAIT_FOR_OVERLAY_FLIP); - OUT_RING(MI_NOOP); - ADVANCE_LP_RING(); - - overlay->last_flip_req = i915_add_request(dev, NULL, - 0, &dev_priv->render_ring); - if (overlay->last_flip_req == 0) - return -ENOMEM; - - ret = i915_do_wait_request(dev, overlay->last_flip_req, - interruptible, &dev_priv->render_ring); - if (ret != 0) - return ret; - - case SWITCH_OFF_STAGE_2: - intel_overlay_off_tail(overlay); - break; - default: - BUG_ON(overlay->hw_wedged != NEEDS_WAIT_FOR_FLIP); - } + if (overlay->flip_tail) + overlay->flip_tail(overlay); - overlay->hw_wedged = 0; overlay->last_flip_req = 0; return 0; } /* Wait for pending overlay flip and release old frame. * Needs to be called before the overlay register are changed - * via intel_overlay_(un)map_regs_atomic */ + * via intel_overlay_(un)map_regs + */ static int intel_overlay_release_old_vid(struct intel_overlay *overlay) { + struct drm_device *dev = overlay->dev; + drm_i915_private_t *dev_priv = dev->dev_private; int ret; - struct drm_gem_object *obj; - /* only wait if there is actually an old frame to release to - * guarantee forward progress */ + /* Only wait if there is actually an old frame to release to + * guarantee forward progress. + */ if (!overlay->old_vid_bo) return 0; - ret = intel_overlay_wait_flip(overlay); - if (ret != 0) - return ret; + if (I915_READ(ISR) & I915_OVERLAY_PLANE_FLIP_PENDING_INTERRUPT) { + struct drm_i915_gem_request *request; - obj = &overlay->old_vid_bo->base; - i915_gem_object_unpin(obj); - drm_gem_object_unreference(obj); - overlay->old_vid_bo = NULL; + /* synchronous slowpath */ + request = kzalloc(sizeof(*request), GFP_KERNEL); + if (request == NULL) + return -ENOMEM; + + BEGIN_LP_RING(2); + OUT_RING(MI_WAIT_FOR_EVENT | MI_WAIT_FOR_OVERLAY_FLIP); + OUT_RING(MI_NOOP); + ADVANCE_LP_RING(); + ret = intel_overlay_do_wait_request(overlay, request, true, + intel_overlay_release_old_vid_tail); + if (ret) + return ret; + } + + intel_overlay_release_old_vid_tail(overlay); return 0; } @@ -506,65 +502,65 @@ struct put_image_params { static int packed_depth_bytes(u32 format) { switch (format & I915_OVERLAY_DEPTH_MASK) { - case I915_OVERLAY_YUV422: - return 4; - case I915_OVERLAY_YUV411: - /* return 6; not implemented */ - default: - return -EINVAL; + case I915_OVERLAY_YUV422: + return 4; + case I915_OVERLAY_YUV411: + /* return 6; not implemented */ + default: + return -EINVAL; } } static int packed_width_bytes(u32 format, short width) { switch (format & I915_OVERLAY_DEPTH_MASK) { - case I915_OVERLAY_YUV422: - return width << 1; - default: - return -EINVAL; + case I915_OVERLAY_YUV422: + return width << 1; + default: + return -EINVAL; } } static int uv_hsubsampling(u32 format) { switch (format & I915_OVERLAY_DEPTH_MASK) { - case I915_OVERLAY_YUV422: - case I915_OVERLAY_YUV420: - return 2; - case I915_OVERLAY_YUV411: - case I915_OVERLAY_YUV410: - return 4; - default: - return -EINVAL; + case I915_OVERLAY_YUV422: + case I915_OVERLAY_YUV420: + return 2; + case I915_OVERLAY_YUV411: + case I915_OVERLAY_YUV410: + return 4; + default: + return -EINVAL; } } static int uv_vsubsampling(u32 format) { switch (format & I915_OVERLAY_DEPTH_MASK) { - case I915_OVERLAY_YUV420: - case I915_OVERLAY_YUV410: - return 2; - case I915_OVERLAY_YUV422: - case I915_OVERLAY_YUV411: - return 1; - default: - return -EINVAL; + case I915_OVERLAY_YUV420: + case I915_OVERLAY_YUV410: + return 2; + case I915_OVERLAY_YUV422: + case I915_OVERLAY_YUV411: + return 1; + default: + return -EINVAL; } } static u32 calc_swidthsw(struct drm_device *dev, u32 offset, u32 width) { u32 mask, shift, ret; - if (IS_I9XX(dev)) { - mask = 0x3f; - shift = 6; - } else { + if (IS_GEN2(dev)) { mask = 0x1f; shift = 5; + } else { + mask = 0x3f; + shift = 6; } ret = ((offset + width + mask) >> shift) - (offset >> shift); - if (IS_I9XX(dev)) + if (!IS_GEN2(dev)) ret <<= 1; ret -=1; return ret << 2; @@ -587,7 +583,9 @@ static const u16 y_static_hcoeffs[N_HORIZ_Y_TAPS * N_PHASES] = { 0x3020, 0xb340, 0x1fb8, 0x34a0, 0xb060, 0x3020, 0xb240, 0x1fe0, 0x32e0, 0xb040, 0x3020, 0xb140, 0x1ff8, 0x3160, 0xb020, - 0xb000, 0x3000, 0x0800, 0x3000, 0xb000}; + 0xb000, 0x3000, 0x0800, 0x3000, 0xb000 +}; + static const u16 uv_static_hcoeffs[N_HORIZ_UV_TAPS * N_PHASES] = { 0x3000, 0x1800, 0x1800, 0xb000, 0x18d0, 0x2e60, 0xb000, 0x1990, 0x2ce0, 0xb020, 0x1a68, 0x2b40, @@ -597,7 +595,8 @@ static const u16 uv_static_hcoeffs[N_HORIZ_UV_TAPS * N_PHASES] = { 0xb100, 0x1eb8, 0x3620, 0xb100, 0x1f18, 0x34a0, 0xb100, 0x1f68, 0x3360, 0xb0e0, 0x1fa8, 0x3240, 0xb0c0, 0x1fe0, 0x3140, 0xb060, 0x1ff0, 0x30a0, - 0x3000, 0x0800, 0x3000}; + 0x3000, 0x0800, 0x3000 +}; static void update_polyphase_filter(struct overlay_registers *regs) { @@ -630,29 +629,31 @@ static bool update_scaling_factors(struct intel_overlay *overlay, yscale = 1 << FP_SHIFT; /*if (params->format & I915_OVERLAY_YUV_PLANAR) {*/ - xscale_UV = xscale/uv_hscale; - yscale_UV = yscale/uv_vscale; - /* make the Y scale to UV scale ratio an exact multiply */ - xscale = xscale_UV * uv_hscale; - yscale = yscale_UV * uv_vscale; + xscale_UV = xscale/uv_hscale; + yscale_UV = yscale/uv_vscale; + /* make the Y scale to UV scale ratio an exact multiply */ + xscale = xscale_UV * uv_hscale; + yscale = yscale_UV * uv_vscale; /*} else { - xscale_UV = 0; - yscale_UV = 0; - }*/ + xscale_UV = 0; + yscale_UV = 0; + }*/ if (xscale != overlay->old_xscale || yscale != overlay->old_yscale) scale_changed = true; overlay->old_xscale = xscale; overlay->old_yscale = yscale; - regs->YRGBSCALE = ((yscale & FRACT_MASK) << 20) - | ((xscale >> FP_SHIFT) << 16) - | ((xscale & FRACT_MASK) << 3); - regs->UVSCALE = ((yscale_UV & FRACT_MASK) << 20) - | ((xscale_UV >> FP_SHIFT) << 16) - | ((xscale_UV & FRACT_MASK) << 3); - regs->UVSCALEV = ((yscale >> FP_SHIFT) << 16) - | ((yscale_UV >> FP_SHIFT) << 0); + regs->YRGBSCALE = (((yscale & FRACT_MASK) << 20) | + ((xscale >> FP_SHIFT) << 16) | + ((xscale & FRACT_MASK) << 3)); + + regs->UVSCALE = (((yscale_UV & FRACT_MASK) << 20) | + ((xscale_UV >> FP_SHIFT) << 16) | + ((xscale_UV & FRACT_MASK) << 3)); + + regs->UVSCALEV = ((((yscale >> FP_SHIFT) << 16) | + ((yscale_UV >> FP_SHIFT) << 0))); if (scale_changed) update_polyphase_filter(regs); @@ -664,22 +665,28 @@ static void update_colorkey(struct intel_overlay *overlay, struct overlay_registers *regs) { u32 key = overlay->color_key; + switch (overlay->crtc->base.fb->bits_per_pixel) { - case 8: - regs->DCLRKV = 0; - regs->DCLRKM = CLK_RGB8I_MASK | DST_KEY_ENABLE; - case 16: - if (overlay->crtc->base.fb->depth == 15) { - regs->DCLRKV = RGB15_TO_COLORKEY(key); - regs->DCLRKM = CLK_RGB15_MASK | DST_KEY_ENABLE; - } else { - regs->DCLRKV = RGB16_TO_COLORKEY(key); - regs->DCLRKM = CLK_RGB16_MASK | DST_KEY_ENABLE; - } - case 24: - case 32: - regs->DCLRKV = key; - regs->DCLRKM = CLK_RGB24_MASK | DST_KEY_ENABLE; + case 8: + regs->DCLRKV = 0; + regs->DCLRKM = CLK_RGB8I_MASK | DST_KEY_ENABLE; + break; + + case 16: + if (overlay->crtc->base.fb->depth == 15) { + regs->DCLRKV = RGB15_TO_COLORKEY(key); + regs->DCLRKM = CLK_RGB15_MASK | DST_KEY_ENABLE; + } else { + regs->DCLRKV = RGB16_TO_COLORKEY(key); + regs->DCLRKM = CLK_RGB16_MASK | DST_KEY_ENABLE; + } + break; + + case 24: + case 32: + regs->DCLRKV = key; + regs->DCLRKM = CLK_RGB24_MASK | DST_KEY_ENABLE; + break; } } @@ -689,48 +696,48 @@ static u32 overlay_cmd_reg(struct put_image_params *params) if (params->format & I915_OVERLAY_YUV_PLANAR) { switch (params->format & I915_OVERLAY_DEPTH_MASK) { - case I915_OVERLAY_YUV422: - cmd |= OCMD_YUV_422_PLANAR; - break; - case I915_OVERLAY_YUV420: - cmd |= OCMD_YUV_420_PLANAR; - break; - case I915_OVERLAY_YUV411: - case I915_OVERLAY_YUV410: - cmd |= OCMD_YUV_410_PLANAR; - break; + case I915_OVERLAY_YUV422: + cmd |= OCMD_YUV_422_PLANAR; + break; + case I915_OVERLAY_YUV420: + cmd |= OCMD_YUV_420_PLANAR; + break; + case I915_OVERLAY_YUV411: + case I915_OVERLAY_YUV410: + cmd |= OCMD_YUV_410_PLANAR; + break; } } else { /* YUV packed */ switch (params->format & I915_OVERLAY_DEPTH_MASK) { - case I915_OVERLAY_YUV422: - cmd |= OCMD_YUV_422_PACKED; - break; - case I915_OVERLAY_YUV411: - cmd |= OCMD_YUV_411_PACKED; - break; + case I915_OVERLAY_YUV422: + cmd |= OCMD_YUV_422_PACKED; + break; + case I915_OVERLAY_YUV411: + cmd |= OCMD_YUV_411_PACKED; + break; } switch (params->format & I915_OVERLAY_SWAP_MASK) { - case I915_OVERLAY_NO_SWAP: - break; - case I915_OVERLAY_UV_SWAP: - cmd |= OCMD_UV_SWAP; - break; - case I915_OVERLAY_Y_SWAP: - cmd |= OCMD_Y_SWAP; - break; - case I915_OVERLAY_Y_AND_UV_SWAP: - cmd |= OCMD_Y_AND_UV_SWAP; - break; + case I915_OVERLAY_NO_SWAP: + break; + case I915_OVERLAY_UV_SWAP: + cmd |= OCMD_UV_SWAP; + break; + case I915_OVERLAY_Y_SWAP: + cmd |= OCMD_Y_SWAP; + break; + case I915_OVERLAY_Y_AND_UV_SWAP: + cmd |= OCMD_Y_AND_UV_SWAP; + break; } } return cmd; } -int intel_overlay_do_put_image(struct intel_overlay *overlay, - struct drm_gem_object *new_bo, - struct put_image_params *params) +static int intel_overlay_do_put_image(struct intel_overlay *overlay, + struct drm_gem_object *new_bo, + struct put_image_params *params) { int ret, tmp_width; struct overlay_registers *regs; @@ -755,24 +762,24 @@ int intel_overlay_do_put_image(struct intel_overlay *overlay, goto out_unpin; if (!overlay->active) { - regs = intel_overlay_map_regs_atomic(overlay); + regs = intel_overlay_map_regs(overlay); if (!regs) { ret = -ENOMEM; goto out_unpin; } regs->OCONFIG = OCONF_CC_OUT_8BIT; - if (IS_I965GM(overlay->dev)) + if (IS_GEN4(overlay->dev)) regs->OCONFIG |= OCONF_CSC_MODE_BT709; regs->OCONFIG |= overlay->crtc->pipe == 0 ? OCONF_PIPE_A : OCONF_PIPE_B; - intel_overlay_unmap_regs_atomic(overlay); + intel_overlay_unmap_regs(overlay, regs); ret = intel_overlay_on(overlay); if (ret != 0) goto out_unpin; } - regs = intel_overlay_map_regs_atomic(overlay); + regs = intel_overlay_map_regs(overlay); if (!regs) { ret = -ENOMEM; goto out_unpin; @@ -788,7 +795,7 @@ int intel_overlay_do_put_image(struct intel_overlay *overlay, regs->SWIDTH = params->src_w; regs->SWIDTHSW = calc_swidthsw(overlay->dev, - params->offset_Y, tmp_width); + params->offset_Y, tmp_width); regs->SHEIGHT = params->src_h; regs->OBUF_0Y = bo_priv->gtt_offset + params-> offset_Y; regs->OSTRIDE = params->stride_Y; @@ -799,9 +806,9 @@ int intel_overlay_do_put_image(struct intel_overlay *overlay, u32 tmp_U, tmp_V; regs->SWIDTH |= (params->src_w/uv_hscale) << 16; tmp_U = calc_swidthsw(overlay->dev, params->offset_U, - params->src_w/uv_hscale); + params->src_w/uv_hscale); tmp_V = calc_swidthsw(overlay->dev, params->offset_V, - params->src_w/uv_hscale); + params->src_w/uv_hscale); regs->SWIDTHSW |= max_t(u32, tmp_U, tmp_V) << 16; regs->SHEIGHT |= (params->src_h/uv_vscale) << 16; regs->OBUF_0U = bo_priv->gtt_offset + params->offset_U; @@ -815,9 +822,11 @@ int intel_overlay_do_put_image(struct intel_overlay *overlay, regs->OCMD = overlay_cmd_reg(params); - intel_overlay_unmap_regs_atomic(overlay); + intel_overlay_unmap_regs(overlay, regs); - intel_overlay_continue(overlay, scale_changed); + ret = intel_overlay_continue(overlay, scale_changed); + if (ret) + goto out_unpin; overlay->old_vid_bo = overlay->vid_bo; overlay->vid_bo = to_intel_bo(new_bo); @@ -829,20 +838,19 @@ out_unpin: return ret; } -int intel_overlay_switch_off(struct intel_overlay *overlay) +int intel_overlay_switch_off(struct intel_overlay *overlay, + bool interruptible) { - int ret; struct overlay_registers *regs; struct drm_device *dev = overlay->dev; + int ret; BUG_ON(!mutex_is_locked(&dev->struct_mutex)); BUG_ON(!mutex_is_locked(&dev->mode_config.mutex)); - if (overlay->hw_wedged) { - ret = intel_overlay_recover_from_interrupt(overlay, 1); - if (ret != 0) - return ret; - } + ret = intel_overlay_recover_from_interrupt(overlay, interruptible); + if (ret != 0) + return ret; if (!overlay->active) return 0; @@ -851,33 +859,29 @@ int intel_overlay_switch_off(struct intel_overlay *overlay) if (ret != 0) return ret; - regs = intel_overlay_map_regs_atomic(overlay); + regs = intel_overlay_map_regs(overlay); regs->OCMD = 0; - intel_overlay_unmap_regs_atomic(overlay); + intel_overlay_unmap_regs(overlay, regs); - ret = intel_overlay_off(overlay); + ret = intel_overlay_off(overlay, interruptible); if (ret != 0) return ret; intel_overlay_off_tail(overlay); - return 0; } static int check_overlay_possible_on_crtc(struct intel_overlay *overlay, struct intel_crtc *crtc) { - drm_i915_private_t *dev_priv = overlay->dev->dev_private; - u32 pipeconf; - int pipeconf_reg = (crtc->pipe == 0) ? PIPEACONF : PIPEBCONF; + drm_i915_private_t *dev_priv = overlay->dev->dev_private; - if (!crtc->base.enabled || crtc->dpms_mode != DRM_MODE_DPMS_ON) + if (!crtc->active) return -EINVAL; - pipeconf = I915_READ(pipeconf_reg); - /* can't use the overlay with double wide pipe */ - if (!IS_I965G(overlay->dev) && pipeconf & PIPEACONF_DOUBLE_WIDE) + if (INTEL_INFO(overlay->dev)->gen < 4 && + (I915_READ(PIPECONF(crtc->pipe)) & (PIPECONF_DOUBLE_WIDE | PIPECONF_ENABLE)) != PIPECONF_ENABLE) return -EINVAL; return 0; @@ -886,20 +890,22 @@ static int check_overlay_possible_on_crtc(struct intel_overlay *overlay, static void update_pfit_vscale_ratio(struct intel_overlay *overlay) { struct drm_device *dev = overlay->dev; - drm_i915_private_t *dev_priv = dev->dev_private; - u32 ratio; + drm_i915_private_t *dev_priv = dev->dev_private; u32 pfit_control = I915_READ(PFIT_CONTROL); + u32 ratio; /* XXX: This is not the same logic as in the xorg driver, but more in - * line with the intel documentation for the i965 */ - if (!IS_I965G(dev) && (pfit_control & VERT_AUTO_SCALE)) { - ratio = I915_READ(PFIT_AUTO_RATIOS) >> PFIT_VERT_SCALE_SHIFT; - } else { /* on i965 use the PGM reg to read out the autoscaler values */ - ratio = I915_READ(PFIT_PGM_RATIOS); - if (IS_I965G(dev)) - ratio >>= PFIT_VERT_SCALE_SHIFT_965; + * line with the intel documentation for the i965 + */ + if (INTEL_INFO(dev)->gen >= 4) { + /* on i965 use the PGM reg to read out the autoscaler values */ + ratio = I915_READ(PFIT_PGM_RATIOS) >> PFIT_VERT_SCALE_SHIFT_965; + } else { + if (pfit_control & VERT_AUTO_SCALE) + ratio = I915_READ(PFIT_AUTO_RATIOS); else - ratio >>= PFIT_VERT_SCALE_SHIFT; + ratio = I915_READ(PFIT_PGM_RATIOS); + ratio >>= PFIT_VERT_SCALE_SHIFT; } overlay->pfit_vscale_ratio = ratio; @@ -910,12 +916,10 @@ static int check_overlay_dst(struct intel_overlay *overlay, { struct drm_display_mode *mode = &overlay->crtc->base.mode; - if ((rec->dst_x < mode->crtc_hdisplay) - && (rec->dst_x + rec->dst_width - <= mode->crtc_hdisplay) - && (rec->dst_y < mode->crtc_vdisplay) - && (rec->dst_y + rec->dst_height - <= mode->crtc_vdisplay)) + if (rec->dst_x < mode->crtc_hdisplay && + rec->dst_x + rec->dst_width <= mode->crtc_hdisplay && + rec->dst_y < mode->crtc_vdisplay && + rec->dst_y + rec->dst_height <= mode->crtc_vdisplay) return 0; else return -EINVAL; @@ -940,53 +944,57 @@ static int check_overlay_src(struct drm_device *dev, struct drm_intel_overlay_put_image *rec, struct drm_gem_object *new_bo) { - u32 stride_mask; - int depth; int uv_hscale = uv_hsubsampling(rec->flags); int uv_vscale = uv_vsubsampling(rec->flags); - size_t tmp; + u32 stride_mask, depth, tmp; /* check src dimensions */ if (IS_845G(dev) || IS_I830(dev)) { - if (rec->src_height > IMAGE_MAX_HEIGHT_LEGACY - || rec->src_width > IMAGE_MAX_WIDTH_LEGACY) + if (rec->src_height > IMAGE_MAX_HEIGHT_LEGACY || + rec->src_width > IMAGE_MAX_WIDTH_LEGACY) return -EINVAL; } else { - if (rec->src_height > IMAGE_MAX_HEIGHT - || rec->src_width > IMAGE_MAX_WIDTH) + if (rec->src_height > IMAGE_MAX_HEIGHT || + rec->src_width > IMAGE_MAX_WIDTH) return -EINVAL; } + /* better safe than sorry, use 4 as the maximal subsampling ratio */ - if (rec->src_height < N_VERT_Y_TAPS*4 - || rec->src_width < N_HORIZ_Y_TAPS*4) + if (rec->src_height < N_VERT_Y_TAPS*4 || + rec->src_width < N_HORIZ_Y_TAPS*4) return -EINVAL; /* check alignment constraints */ switch (rec->flags & I915_OVERLAY_TYPE_MASK) { - case I915_OVERLAY_RGB: - /* not implemented */ + case I915_OVERLAY_RGB: + /* not implemented */ + return -EINVAL; + + case I915_OVERLAY_YUV_PACKED: + if (uv_vscale != 1) return -EINVAL; - case I915_OVERLAY_YUV_PACKED: - depth = packed_depth_bytes(rec->flags); - if (uv_vscale != 1) - return -EINVAL; - if (depth < 0) - return depth; - /* ignore UV planes */ - rec->stride_UV = 0; - rec->offset_U = 0; - rec->offset_V = 0; - /* check pixel alignment */ - if (rec->offset_Y % depth) - return -EINVAL; - break; - case I915_OVERLAY_YUV_PLANAR: - if (uv_vscale < 0 || uv_hscale < 0) - return -EINVAL; - /* no offset restrictions for planar formats */ - break; - default: + + depth = packed_depth_bytes(rec->flags); + if (depth < 0) + return depth; + + /* ignore UV planes */ + rec->stride_UV = 0; + rec->offset_U = 0; + rec->offset_V = 0; + /* check pixel alignment */ + if (rec->offset_Y % depth) + return -EINVAL; + break; + + case I915_OVERLAY_YUV_PLANAR: + if (uv_vscale < 0 || uv_hscale < 0) return -EINVAL; + /* no offset restrictions for planar formats */ + break; + + default: + return -EINVAL; } if (rec->src_width % uv_hscale) @@ -1000,47 +1008,74 @@ static int check_overlay_src(struct drm_device *dev, if (rec->stride_Y & stride_mask || rec->stride_UV & stride_mask) return -EINVAL; - if (IS_I965G(dev) && rec->stride_Y < 512) + if (IS_GEN4(dev) && rec->stride_Y < 512) return -EINVAL; tmp = (rec->flags & I915_OVERLAY_TYPE_MASK) == I915_OVERLAY_YUV_PLANAR ? - 4 : 8; - if (rec->stride_Y > tmp*1024 || rec->stride_UV > 2*1024) + 4096 : 8192; + if (rec->stride_Y > tmp || rec->stride_UV > 2*1024) return -EINVAL; /* check buffer dimensions */ switch (rec->flags & I915_OVERLAY_TYPE_MASK) { - case I915_OVERLAY_RGB: - case I915_OVERLAY_YUV_PACKED: - /* always 4 Y values per depth pixels */ - if (packed_width_bytes(rec->flags, rec->src_width) - > rec->stride_Y) - return -EINVAL; - - tmp = rec->stride_Y*rec->src_height; - if (rec->offset_Y + tmp > new_bo->size) - return -EINVAL; - break; - case I915_OVERLAY_YUV_PLANAR: - if (rec->src_width > rec->stride_Y) - return -EINVAL; - if (rec->src_width/uv_hscale > rec->stride_UV) - return -EINVAL; - - tmp = rec->stride_Y*rec->src_height; - if (rec->offset_Y + tmp > new_bo->size) - return -EINVAL; - tmp = rec->stride_UV*rec->src_height; - tmp /= uv_vscale; - if (rec->offset_U + tmp > new_bo->size - || rec->offset_V + tmp > new_bo->size) - return -EINVAL; - break; + case I915_OVERLAY_RGB: + case I915_OVERLAY_YUV_PACKED: + /* always 4 Y values per depth pixels */ + if (packed_width_bytes(rec->flags, rec->src_width) > rec->stride_Y) + return -EINVAL; + + tmp = rec->stride_Y*rec->src_height; + if (rec->offset_Y + tmp > new_bo->size) + return -EINVAL; + break; + + case I915_OVERLAY_YUV_PLANAR: + if (rec->src_width > rec->stride_Y) + return -EINVAL; + if (rec->src_width/uv_hscale > rec->stride_UV) + return -EINVAL; + + tmp = rec->stride_Y * rec->src_height; + if (rec->offset_Y + tmp > new_bo->size) + return -EINVAL; + + tmp = rec->stride_UV * (rec->src_height / uv_vscale); + if (rec->offset_U + tmp > new_bo->size || + rec->offset_V + tmp > new_bo->size) + return -EINVAL; + break; } return 0; } +/** + * Return the pipe currently connected to the panel fitter, + * or -1 if the panel fitter is not present or not in use + */ +static int intel_panel_fitter_pipe(struct drm_device *dev) +{ + struct drm_i915_private *dev_priv = dev->dev_private; + u32 pfit_control; + + /* i830 doesn't have a panel fitter */ + if (IS_I830(dev)) + return -1; + + pfit_control = I915_READ(PFIT_CONTROL); + + /* See if the panel fitter is in use */ + if ((pfit_control & PFIT_ENABLE) == 0) + return -1; + + /* 965 can place panel fitter on either pipe */ + if (IS_GEN4(dev)) + return (pfit_control >> 29) & 0x3; + + /* older chips can only use pipe 1 */ + return 1; +} + int intel_overlay_put_image(struct drm_device *dev, void *data, struct drm_file *file_priv) { @@ -1068,7 +1103,7 @@ int intel_overlay_put_image(struct drm_device *dev, void *data, mutex_lock(&dev->mode_config.mutex); mutex_lock(&dev->struct_mutex); - ret = intel_overlay_switch_off(overlay); + ret = intel_overlay_switch_off(overlay, true); mutex_unlock(&dev->struct_mutex); mutex_unlock(&dev->mode_config.mutex); @@ -1081,7 +1116,7 @@ int intel_overlay_put_image(struct drm_device *dev, void *data, return -ENOMEM; drmmode_obj = drm_mode_object_find(dev, put_image_rec->crtc_id, - DRM_MODE_OBJECT_CRTC); + DRM_MODE_OBJECT_CRTC); if (!drmmode_obj) { ret = -ENOENT; goto out_free; @@ -1089,7 +1124,7 @@ int intel_overlay_put_image(struct drm_device *dev, void *data, crtc = to_intel_crtc(obj_to_crtc(drmmode_obj)); new_bo = drm_gem_object_lookup(dev, file_priv, - put_image_rec->bo_handle); + put_image_rec->bo_handle); if (!new_bo) { ret = -ENOENT; goto out_free; @@ -1098,15 +1133,13 @@ int intel_overlay_put_image(struct drm_device *dev, void *data, mutex_lock(&dev->mode_config.mutex); mutex_lock(&dev->struct_mutex); - if (overlay->hw_wedged) { - ret = intel_overlay_recover_from_interrupt(overlay, 1); - if (ret != 0) - goto out_unlock; - } + ret = intel_overlay_recover_from_interrupt(overlay, true); + if (ret != 0) + goto out_unlock; if (overlay->crtc != crtc) { struct drm_display_mode *mode = &crtc->base.mode; - ret = intel_overlay_switch_off(overlay); + ret = intel_overlay_switch_off(overlay, true); if (ret != 0) goto out_unlock; @@ -1117,9 +1150,9 @@ int intel_overlay_put_image(struct drm_device *dev, void *data, overlay->crtc = crtc; crtc->overlay = overlay; - if (intel_panel_fitter_pipe(dev) == crtc->pipe - /* and line to wide, i.e. one-line-mode */ - && mode->hdisplay > 1024) { + /* line too wide, i.e. one-line-mode */ + if (mode->hdisplay > 1024 && + intel_panel_fitter_pipe(dev) == crtc->pipe) { overlay->pfit_active = 1; update_pfit_vscale_ratio(overlay); } else @@ -1132,10 +1165,10 @@ int intel_overlay_put_image(struct drm_device *dev, void *data, if (overlay->pfit_active) { params->dst_y = ((((u32)put_image_rec->dst_y) << 12) / - overlay->pfit_vscale_ratio); + overlay->pfit_vscale_ratio); /* shifting right rounds downwards, so add 1 */ params->dst_h = ((((u32)put_image_rec->dst_height) << 12) / - overlay->pfit_vscale_ratio) + 1; + overlay->pfit_vscale_ratio) + 1; } else { params->dst_y = put_image_rec->dst_y; params->dst_h = put_image_rec->dst_height; @@ -1147,8 +1180,8 @@ int intel_overlay_put_image(struct drm_device *dev, void *data, params->src_h = put_image_rec->src_height; params->src_scan_w = put_image_rec->src_scan_width; params->src_scan_h = put_image_rec->src_scan_height; - if (params->src_scan_h > params->src_h - || params->src_scan_w > params->src_w) { + if (params->src_scan_h > params->src_h || + params->src_scan_w > params->src_w) { ret = -EINVAL; goto out_unlock; } @@ -1204,7 +1237,7 @@ static bool check_gamma_bounds(u32 gamma1, u32 gamma2) return false; for (i = 0; i < 3; i++) { - if (((gamma1 >> i * 8) & 0xff) >= ((gamma2 >> i*8) & 0xff)) + if (((gamma1 >> i*8) & 0xff) >= ((gamma2 >> i*8) & 0xff)) return false; } @@ -1225,16 +1258,18 @@ static bool check_gamma5_errata(u32 gamma5) static int check_gamma(struct drm_intel_overlay_attrs *attrs) { - if (!check_gamma_bounds(0, attrs->gamma0) - || !check_gamma_bounds(attrs->gamma0, attrs->gamma1) - || !check_gamma_bounds(attrs->gamma1, attrs->gamma2) - || !check_gamma_bounds(attrs->gamma2, attrs->gamma3) - || !check_gamma_bounds(attrs->gamma3, attrs->gamma4) - || !check_gamma_bounds(attrs->gamma4, attrs->gamma5) - || !check_gamma_bounds(attrs->gamma5, 0x00ffffff)) + if (!check_gamma_bounds(0, attrs->gamma0) || + !check_gamma_bounds(attrs->gamma0, attrs->gamma1) || + !check_gamma_bounds(attrs->gamma1, attrs->gamma2) || + !check_gamma_bounds(attrs->gamma2, attrs->gamma3) || + !check_gamma_bounds(attrs->gamma3, attrs->gamma4) || + !check_gamma_bounds(attrs->gamma4, attrs->gamma5) || + !check_gamma_bounds(attrs->gamma5, 0x00ffffff)) return -EINVAL; + if (!check_gamma5_errata(attrs->gamma5)) return -EINVAL; + return 0; } @@ -1261,13 +1296,14 @@ int intel_overlay_attrs(struct drm_device *dev, void *data, mutex_lock(&dev->mode_config.mutex); mutex_lock(&dev->struct_mutex); + ret = -EINVAL; if (!(attrs->flags & I915_OVERLAY_UPDATE_ATTRS)) { - attrs->color_key = overlay->color_key; + attrs->color_key = overlay->color_key; attrs->brightness = overlay->brightness; - attrs->contrast = overlay->contrast; + attrs->contrast = overlay->contrast; attrs->saturation = overlay->saturation; - if (IS_I9XX(dev)) { + if (!IS_GEN2(dev)) { attrs->gamma0 = I915_READ(OGAMC0); attrs->gamma1 = I915_READ(OGAMC1); attrs->gamma2 = I915_READ(OGAMC2); @@ -1275,29 +1311,20 @@ int intel_overlay_attrs(struct drm_device *dev, void *data, attrs->gamma4 = I915_READ(OGAMC4); attrs->gamma5 = I915_READ(OGAMC5); } - ret = 0; } else { - overlay->color_key = attrs->color_key; - if (attrs->brightness >= -128 && attrs->brightness <= 127) { - overlay->brightness = attrs->brightness; - } else { - ret = -EINVAL; + if (attrs->brightness < -128 || attrs->brightness > 127) goto out_unlock; - } - if (attrs->contrast <= 255) { - overlay->contrast = attrs->contrast; - } else { - ret = -EINVAL; + if (attrs->contrast > 255) goto out_unlock; - } - if (attrs->saturation <= 1023) { - overlay->saturation = attrs->saturation; - } else { - ret = -EINVAL; + if (attrs->saturation > 1023) goto out_unlock; - } - regs = intel_overlay_map_regs_atomic(overlay); + overlay->color_key = attrs->color_key; + overlay->brightness = attrs->brightness; + overlay->contrast = attrs->contrast; + overlay->saturation = attrs->saturation; + + regs = intel_overlay_map_regs(overlay); if (!regs) { ret = -ENOMEM; goto out_unlock; @@ -1305,13 +1332,11 @@ int intel_overlay_attrs(struct drm_device *dev, void *data, update_reg_attrs(overlay, regs); - intel_overlay_unmap_regs_atomic(overlay); + intel_overlay_unmap_regs(overlay, regs); if (attrs->flags & I915_OVERLAY_UPDATE_GAMMA) { - if (!IS_I9XX(dev)) { - ret = -EINVAL; + if (IS_GEN2(dev)) goto out_unlock; - } if (overlay->active) { ret = -EBUSY; @@ -1319,7 +1344,7 @@ int intel_overlay_attrs(struct drm_device *dev, void *data, } ret = check_gamma(attrs); - if (ret != 0) + if (ret) goto out_unlock; I915_WRITE(OGAMC0, attrs->gamma0); @@ -1329,9 +1354,9 @@ int intel_overlay_attrs(struct drm_device *dev, void *data, I915_WRITE(OGAMC4, attrs->gamma4); I915_WRITE(OGAMC5, attrs->gamma5); } - ret = 0; } + ret = 0; out_unlock: mutex_unlock(&dev->struct_mutex); mutex_unlock(&dev->mode_config.mutex); @@ -1347,7 +1372,7 @@ void intel_setup_overlay(struct drm_device *dev) struct overlay_registers *regs; int ret; - if (!OVERLAY_EXISTS(dev)) + if (!HAS_OVERLAY(dev)) return; overlay = kzalloc(sizeof(struct intel_overlay), GFP_KERNEL); @@ -1360,22 +1385,28 @@ void intel_setup_overlay(struct drm_device *dev) goto out_free; overlay->reg_bo = to_intel_bo(reg_bo); - if (OVERLAY_NONPHYSICAL(dev)) { - ret = i915_gem_object_pin(reg_bo, PAGE_SIZE); - if (ret) { - DRM_ERROR("failed to pin overlay register bo\n"); - goto out_free_bo; - } - overlay->flip_addr = overlay->reg_bo->gtt_offset; - } else { + if (OVERLAY_NEEDS_PHYSICAL(dev)) { ret = i915_gem_attach_phys_object(dev, reg_bo, I915_GEM_PHYS_OVERLAY_REGS, - 0); + PAGE_SIZE); if (ret) { DRM_ERROR("failed to attach phys overlay regs\n"); goto out_free_bo; } overlay->flip_addr = overlay->reg_bo->phys_obj->handle->busaddr; + } else { + ret = i915_gem_object_pin(reg_bo, PAGE_SIZE); + if (ret) { + DRM_ERROR("failed to pin overlay register bo\n"); + goto out_free_bo; + } + overlay->flip_addr = overlay->reg_bo->gtt_offset; + + ret = i915_gem_object_set_to_gtt_domain(reg_bo, true); + if (ret) { + DRM_ERROR("failed to move overlay register bo into the GTT\n"); + goto out_unpin_bo; + } } /* init all values */ @@ -1384,21 +1415,22 @@ void intel_setup_overlay(struct drm_device *dev) overlay->contrast = 75; overlay->saturation = 146; - regs = intel_overlay_map_regs_atomic(overlay); + regs = intel_overlay_map_regs(overlay); if (!regs) goto out_free_bo; memset(regs, 0, sizeof(struct overlay_registers)); update_polyphase_filter(regs); - update_reg_attrs(overlay, regs); - intel_overlay_unmap_regs_atomic(overlay); + intel_overlay_unmap_regs(overlay, regs); dev_priv->overlay = overlay; DRM_INFO("initialized overlay support\n"); return; +out_unpin_bo: + i915_gem_object_unpin(reg_bo); out_free_bo: drm_gem_object_unreference(reg_bo); out_free: @@ -1408,18 +1440,23 @@ out_free: void intel_cleanup_overlay(struct drm_device *dev) { - drm_i915_private_t *dev_priv = dev->dev_private; + drm_i915_private_t *dev_priv = dev->dev_private; - if (dev_priv->overlay) { - /* The bo's should be free'd by the generic code already. - * Furthermore modesetting teardown happens beforehand so the - * hardware should be off already */ - BUG_ON(dev_priv->overlay->active); + if (!dev_priv->overlay) + return; - kfree(dev_priv->overlay); - } + /* The bo's should be free'd by the generic code already. + * Furthermore modesetting teardown happens beforehand so the + * hardware should be off already */ + BUG_ON(dev_priv->overlay->active); + + drm_gem_object_unreference_unlocked(&dev_priv->overlay->reg_bo->base); + kfree(dev_priv->overlay); } +#ifdef CONFIG_DEBUG_FS +#include <linux/seq_file.h> + struct intel_overlay_error_state { struct overlay_registers regs; unsigned long base; @@ -1427,6 +1464,32 @@ struct intel_overlay_error_state { u32 isr; }; +static struct overlay_registers * +intel_overlay_map_regs_atomic(struct intel_overlay *overlay, + int slot) +{ + drm_i915_private_t *dev_priv = overlay->dev->dev_private; + struct overlay_registers *regs; + + if (OVERLAY_NEEDS_PHYSICAL(overlay->dev)) + regs = overlay->reg_bo->phys_obj->handle->vaddr; + else + regs = io_mapping_map_atomic_wc(dev_priv->mm.gtt_mapping, + overlay->reg_bo->gtt_offset, + slot); + + return regs; +} + +static void intel_overlay_unmap_regs_atomic(struct intel_overlay *overlay, + int slot, + struct overlay_registers *regs) +{ + if (!OVERLAY_NEEDS_PHYSICAL(overlay->dev)) + io_mapping_unmap_atomic(regs, slot); +} + + struct intel_overlay_error_state * intel_overlay_capture_error_state(struct drm_device *dev) { @@ -1444,17 +1507,17 @@ intel_overlay_capture_error_state(struct drm_device *dev) error->dovsta = I915_READ(DOVSTA); error->isr = I915_READ(ISR); - if (OVERLAY_NONPHYSICAL(overlay->dev)) - error->base = (long) overlay->reg_bo->gtt_offset; - else + if (OVERLAY_NEEDS_PHYSICAL(overlay->dev)) error->base = (long) overlay->reg_bo->phys_obj->handle->vaddr; + else + error->base = (long) overlay->reg_bo->gtt_offset; - regs = intel_overlay_map_regs_atomic(overlay); + regs = intel_overlay_map_regs_atomic(overlay, KM_IRQ0); if (!regs) goto err; memcpy_fromio(&error->regs, regs, sizeof(struct overlay_registers)); - intel_overlay_unmap_regs_atomic(overlay); + intel_overlay_unmap_regs_atomic(overlay, KM_IRQ0, regs); return error; @@ -1515,3 +1578,4 @@ intel_overlay_print_error_state(struct seq_file *m, struct intel_overlay_error_s P(UVSCALEV); #undef P } +#endif diff --git a/drivers/gpu/drm/i915/intel_panel.c b/drivers/gpu/drm/i915/intel_panel.c index e7f5299d9d5..92ff8f38527 100644 --- a/drivers/gpu/drm/i915/intel_panel.c +++ b/drivers/gpu/drm/i915/intel_panel.c @@ -30,6 +30,8 @@ #include "intel_drv.h" +#define PCI_LBPC 0xf4 /* legacy/combination backlight modes */ + void intel_fixed_panel_mode(struct drm_display_mode *fixed_mode, struct drm_display_mode *adjusted_mode) @@ -109,3 +111,110 @@ done: dev_priv->pch_pf_pos = (x << 16) | y; dev_priv->pch_pf_size = (width << 16) | height; } + +static int is_backlight_combination_mode(struct drm_device *dev) +{ + struct drm_i915_private *dev_priv = dev->dev_private; + + if (INTEL_INFO(dev)->gen >= 4) + return I915_READ(BLC_PWM_CTL2) & BLM_COMBINATION_MODE; + + if (IS_GEN2(dev)) + return I915_READ(BLC_PWM_CTL) & BLM_LEGACY_MODE; + + return 0; +} + +u32 intel_panel_get_max_backlight(struct drm_device *dev) +{ + struct drm_i915_private *dev_priv = dev->dev_private; + u32 max; + + if (HAS_PCH_SPLIT(dev)) { + max = I915_READ(BLC_PWM_PCH_CTL2) >> 16; + } else { + max = I915_READ(BLC_PWM_CTL); + if (IS_PINEVIEW(dev)) { + max >>= 17; + } else { + max >>= 16; + if (INTEL_INFO(dev)->gen < 4) + max &= ~1; + } + + if (is_backlight_combination_mode(dev)) + max *= 0xff; + } + + if (max == 0) { + /* XXX add code here to query mode clock or hardware clock + * and program max PWM appropriately. + */ + DRM_ERROR("fixme: max PWM is zero.\n"); + max = 1; + } + + DRM_DEBUG_DRIVER("max backlight PWM = %d\n", max); + return max; +} + +u32 intel_panel_get_backlight(struct drm_device *dev) +{ + struct drm_i915_private *dev_priv = dev->dev_private; + u32 val; + + if (HAS_PCH_SPLIT(dev)) { + val = I915_READ(BLC_PWM_CPU_CTL) & BACKLIGHT_DUTY_CYCLE_MASK; + } else { + val = I915_READ(BLC_PWM_CTL) & BACKLIGHT_DUTY_CYCLE_MASK; + if (IS_PINEVIEW(dev)) + val >>= 1; + + if (is_backlight_combination_mode(dev)){ + u8 lbpc; + + val &= ~1; + pci_read_config_byte(dev->pdev, PCI_LBPC, &lbpc); + val *= lbpc; + val >>= 1; + } + } + + DRM_DEBUG_DRIVER("get backlight PWM = %d\n", val); + return val; +} + +static void intel_pch_panel_set_backlight(struct drm_device *dev, u32 level) +{ + struct drm_i915_private *dev_priv = dev->dev_private; + u32 val = I915_READ(BLC_PWM_CPU_CTL) & ~BACKLIGHT_DUTY_CYCLE_MASK; + I915_WRITE(BLC_PWM_CPU_CTL, val | level); +} + +void intel_panel_set_backlight(struct drm_device *dev, u32 level) +{ + struct drm_i915_private *dev_priv = dev->dev_private; + u32 tmp; + + DRM_DEBUG_DRIVER("set backlight PWM = %d\n", level); + + if (HAS_PCH_SPLIT(dev)) + return intel_pch_panel_set_backlight(dev, level); + + if (is_backlight_combination_mode(dev)){ + u32 max = intel_panel_get_max_backlight(dev); + u8 lpbc; + + lpbc = level * 0xfe / max + 1; + level /= lpbc; + pci_write_config_byte(dev->pdev, PCI_LBPC, lpbc); + } + + tmp = I915_READ(BLC_PWM_CTL); + if (IS_PINEVIEW(dev)) { + tmp &= ~(BACKLIGHT_DUTY_CYCLE_MASK - 1); + level <<= 1; + } else + tmp &= ~BACKLIGHT_DUTY_CYCLE_MASK; + I915_WRITE(BLC_PWM_CTL, tmp | level); +} diff --git a/drivers/gpu/drm/i915/intel_ringbuffer.c b/drivers/gpu/drm/i915/intel_ringbuffer.c index cb3508f78bc..d89b88791aa 100644 --- a/drivers/gpu/drm/i915/intel_ringbuffer.c +++ b/drivers/gpu/drm/i915/intel_ringbuffer.c @@ -32,6 +32,7 @@ #include "i915_drv.h" #include "i915_drm.h" #include "i915_trace.h" +#include "intel_drv.h" static u32 i915_gem_get_seqno(struct drm_device *dev) { @@ -49,9 +50,9 @@ static u32 i915_gem_get_seqno(struct drm_device *dev) static void render_ring_flush(struct drm_device *dev, - struct intel_ring_buffer *ring, - u32 invalidate_domains, - u32 flush_domains) + struct intel_ring_buffer *ring, + u32 invalidate_domains, + u32 flush_domains) { drm_i915_private_t *dev_priv = dev->dev_private; u32 cmd; @@ -97,7 +98,7 @@ render_ring_flush(struct drm_device *dev, if ((invalidate_domains|flush_domains) & I915_GEM_DOMAIN_RENDER) cmd &= ~MI_NO_WRITE_FLUSH; - if (!IS_I965G(dev)) { + if (INTEL_INFO(dev)->gen < 4) { /* * On the 965, the sampler cache always gets flushed * and this bit is reserved. @@ -118,38 +119,26 @@ render_ring_flush(struct drm_device *dev, } } -static unsigned int render_ring_get_head(struct drm_device *dev, - struct intel_ring_buffer *ring) -{ - drm_i915_private_t *dev_priv = dev->dev_private; - return I915_READ(PRB0_HEAD) & HEAD_ADDR; -} - -static unsigned int render_ring_get_tail(struct drm_device *dev, - struct intel_ring_buffer *ring) +static void ring_set_tail(struct drm_device *dev, + struct intel_ring_buffer *ring, + u32 value) { drm_i915_private_t *dev_priv = dev->dev_private; - return I915_READ(PRB0_TAIL) & TAIL_ADDR; + I915_WRITE_TAIL(ring, ring->tail); } -static unsigned int render_ring_get_active_head(struct drm_device *dev, - struct intel_ring_buffer *ring) +u32 intel_ring_get_active_head(struct drm_device *dev, + struct intel_ring_buffer *ring) { drm_i915_private_t *dev_priv = dev->dev_private; - u32 acthd_reg = IS_I965G(dev) ? ACTHD_I965 : ACTHD; + u32 acthd_reg = INTEL_INFO(dev)->gen >= 4 ? + RING_ACTHD(ring->mmio_base) : ACTHD; return I915_READ(acthd_reg); } -static void render_ring_advance_ring(struct drm_device *dev, - struct intel_ring_buffer *ring) -{ - drm_i915_private_t *dev_priv = dev->dev_private; - I915_WRITE(PRB0_TAIL, ring->tail); -} - static int init_ring_common(struct drm_device *dev, - struct intel_ring_buffer *ring) + struct intel_ring_buffer *ring) { u32 head; drm_i915_private_t *dev_priv = dev->dev_private; @@ -157,57 +146,57 @@ static int init_ring_common(struct drm_device *dev, obj_priv = to_intel_bo(ring->gem_object); /* Stop the ring if it's running. */ - I915_WRITE(ring->regs.ctl, 0); - I915_WRITE(ring->regs.head, 0); - I915_WRITE(ring->regs.tail, 0); + I915_WRITE_CTL(ring, 0); + I915_WRITE_HEAD(ring, 0); + ring->set_tail(dev, ring, 0); /* Initialize the ring. */ - I915_WRITE(ring->regs.start, obj_priv->gtt_offset); - head = ring->get_head(dev, ring); + I915_WRITE_START(ring, obj_priv->gtt_offset); + head = I915_READ_HEAD(ring) & HEAD_ADDR; /* G45 ring initialization fails to reset head to zero */ if (head != 0) { DRM_ERROR("%s head not reset to zero " "ctl %08x head %08x tail %08x start %08x\n", ring->name, - I915_READ(ring->regs.ctl), - I915_READ(ring->regs.head), - I915_READ(ring->regs.tail), - I915_READ(ring->regs.start)); + I915_READ_CTL(ring), + I915_READ_HEAD(ring), + I915_READ_TAIL(ring), + I915_READ_START(ring)); - I915_WRITE(ring->regs.head, 0); + I915_WRITE_HEAD(ring, 0); DRM_ERROR("%s head forced to zero " "ctl %08x head %08x tail %08x start %08x\n", ring->name, - I915_READ(ring->regs.ctl), - I915_READ(ring->regs.head), - I915_READ(ring->regs.tail), - I915_READ(ring->regs.start)); + I915_READ_CTL(ring), + I915_READ_HEAD(ring), + I915_READ_TAIL(ring), + I915_READ_START(ring)); } - I915_WRITE(ring->regs.ctl, + I915_WRITE_CTL(ring, ((ring->gem_object->size - PAGE_SIZE) & RING_NR_PAGES) | RING_NO_REPORT | RING_VALID); - head = I915_READ(ring->regs.head) & HEAD_ADDR; + head = I915_READ_HEAD(ring) & HEAD_ADDR; /* If the head is still not zero, the ring is dead */ if (head != 0) { DRM_ERROR("%s initialization failed " "ctl %08x head %08x tail %08x start %08x\n", ring->name, - I915_READ(ring->regs.ctl), - I915_READ(ring->regs.head), - I915_READ(ring->regs.tail), - I915_READ(ring->regs.start)); + I915_READ_CTL(ring), + I915_READ_HEAD(ring), + I915_READ_TAIL(ring), + I915_READ_START(ring)); return -EIO; } if (!drm_core_check_feature(dev, DRIVER_MODESET)) i915_kernel_lost_context(dev); else { - ring->head = ring->get_head(dev, ring); - ring->tail = ring->get_tail(dev, ring); + ring->head = I915_READ_HEAD(ring) & HEAD_ADDR; + ring->tail = I915_READ_TAIL(ring) & TAIL_ADDR; ring->space = ring->head - (ring->tail + 8); if (ring->space < 0) ring->space += ring->size; @@ -216,13 +205,13 @@ static int init_ring_common(struct drm_device *dev, } static int init_render_ring(struct drm_device *dev, - struct intel_ring_buffer *ring) + struct intel_ring_buffer *ring) { drm_i915_private_t *dev_priv = dev->dev_private; int ret = init_ring_common(dev, ring); int mode; - if (IS_I9XX(dev) && !IS_GEN3(dev)) { + if (INTEL_INFO(dev)->gen > 3) { mode = VS_TIMER_DISPATCH << 16 | VS_TIMER_DISPATCH; if (IS_GEN6(dev)) mode |= MI_FLUSH_ENABLE << 16 | MI_FLUSH_ENABLE; @@ -250,9 +239,8 @@ do { \ */ static u32 render_ring_add_request(struct drm_device *dev, - struct intel_ring_buffer *ring, - struct drm_file *file_priv, - u32 flush_domains) + struct intel_ring_buffer *ring, + u32 flush_domains) { drm_i915_private_t *dev_priv = dev->dev_private; u32 seqno; @@ -315,8 +303,8 @@ render_ring_add_request(struct drm_device *dev, } static u32 -render_ring_get_gem_seqno(struct drm_device *dev, - struct intel_ring_buffer *ring) +render_ring_get_seqno(struct drm_device *dev, + struct intel_ring_buffer *ring) { drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private; if (HAS_PIPE_CONTROL(dev)) @@ -327,7 +315,7 @@ render_ring_get_gem_seqno(struct drm_device *dev, static void render_ring_get_user_irq(struct drm_device *dev, - struct intel_ring_buffer *ring) + struct intel_ring_buffer *ring) { drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private; unsigned long irqflags; @@ -344,7 +332,7 @@ render_ring_get_user_irq(struct drm_device *dev, static void render_ring_put_user_irq(struct drm_device *dev, - struct intel_ring_buffer *ring) + struct intel_ring_buffer *ring) { drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private; unsigned long irqflags; @@ -360,21 +348,23 @@ render_ring_put_user_irq(struct drm_device *dev, spin_unlock_irqrestore(&dev_priv->user_irq_lock, irqflags); } -static void render_setup_status_page(struct drm_device *dev, - struct intel_ring_buffer *ring) +void intel_ring_setup_status_page(struct drm_device *dev, + struct intel_ring_buffer *ring) { drm_i915_private_t *dev_priv = dev->dev_private; if (IS_GEN6(dev)) { - I915_WRITE(HWS_PGA_GEN6, ring->status_page.gfx_addr); - I915_READ(HWS_PGA_GEN6); /* posting read */ + I915_WRITE(RING_HWS_PGA_GEN6(ring->mmio_base), + ring->status_page.gfx_addr); + I915_READ(RING_HWS_PGA_GEN6(ring->mmio_base)); /* posting read */ } else { - I915_WRITE(HWS_PGA, ring->status_page.gfx_addr); - I915_READ(HWS_PGA); /* posting read */ + I915_WRITE(RING_HWS_PGA(ring->mmio_base), + ring->status_page.gfx_addr); + I915_READ(RING_HWS_PGA(ring->mmio_base)); /* posting read */ } } -void +static void bsd_ring_flush(struct drm_device *dev, struct intel_ring_buffer *ring, u32 invalidate_domains, @@ -386,45 +376,16 @@ bsd_ring_flush(struct drm_device *dev, intel_ring_advance(dev, ring); } -static inline unsigned int bsd_ring_get_head(struct drm_device *dev, - struct intel_ring_buffer *ring) -{ - drm_i915_private_t *dev_priv = dev->dev_private; - return I915_READ(BSD_RING_HEAD) & HEAD_ADDR; -} - -static inline unsigned int bsd_ring_get_tail(struct drm_device *dev, - struct intel_ring_buffer *ring) -{ - drm_i915_private_t *dev_priv = dev->dev_private; - return I915_READ(BSD_RING_TAIL) & TAIL_ADDR; -} - -static inline unsigned int bsd_ring_get_active_head(struct drm_device *dev, - struct intel_ring_buffer *ring) -{ - drm_i915_private_t *dev_priv = dev->dev_private; - return I915_READ(BSD_RING_ACTHD); -} - -static inline void bsd_ring_advance_ring(struct drm_device *dev, - struct intel_ring_buffer *ring) -{ - drm_i915_private_t *dev_priv = dev->dev_private; - I915_WRITE(BSD_RING_TAIL, ring->tail); -} - static int init_bsd_ring(struct drm_device *dev, - struct intel_ring_buffer *ring) + struct intel_ring_buffer *ring) { return init_ring_common(dev, ring); } static u32 bsd_ring_add_request(struct drm_device *dev, - struct intel_ring_buffer *ring, - struct drm_file *file_priv, - u32 flush_domains) + struct intel_ring_buffer *ring, + u32 flush_domains) { u32 seqno; @@ -443,40 +404,32 @@ bsd_ring_add_request(struct drm_device *dev, return seqno; } -static void bsd_setup_status_page(struct drm_device *dev, - struct intel_ring_buffer *ring) -{ - drm_i915_private_t *dev_priv = dev->dev_private; - I915_WRITE(BSD_HWS_PGA, ring->status_page.gfx_addr); - I915_READ(BSD_HWS_PGA); -} - static void bsd_ring_get_user_irq(struct drm_device *dev, - struct intel_ring_buffer *ring) + struct intel_ring_buffer *ring) { /* do nothing */ } static void bsd_ring_put_user_irq(struct drm_device *dev, - struct intel_ring_buffer *ring) + struct intel_ring_buffer *ring) { /* do nothing */ } static u32 -bsd_ring_get_gem_seqno(struct drm_device *dev, - struct intel_ring_buffer *ring) +bsd_ring_get_seqno(struct drm_device *dev, + struct intel_ring_buffer *ring) { return intel_read_status_page(ring, I915_GEM_HWS_INDEX); } static int bsd_ring_dispatch_gem_execbuffer(struct drm_device *dev, - struct intel_ring_buffer *ring, - struct drm_i915_gem_execbuffer2 *exec, - struct drm_clip_rect *cliprects, - uint64_t exec_offset) + struct intel_ring_buffer *ring, + struct drm_i915_gem_execbuffer2 *exec, + struct drm_clip_rect *cliprects, + uint64_t exec_offset) { uint32_t exec_start; exec_start = (uint32_t) exec_offset + exec->batch_start_offset; @@ -491,10 +444,10 @@ bsd_ring_dispatch_gem_execbuffer(struct drm_device *dev, static int render_ring_dispatch_gem_execbuffer(struct drm_device *dev, - struct intel_ring_buffer *ring, - struct drm_i915_gem_execbuffer2 *exec, - struct drm_clip_rect *cliprects, - uint64_t exec_offset) + struct intel_ring_buffer *ring, + struct drm_i915_gem_execbuffer2 *exec, + struct drm_clip_rect *cliprects, + uint64_t exec_offset) { drm_i915_private_t *dev_priv = dev->dev_private; int nbox = exec->num_cliprects; @@ -524,7 +477,7 @@ render_ring_dispatch_gem_execbuffer(struct drm_device *dev, intel_ring_emit(dev, ring, 0); } else { intel_ring_begin(dev, ring, 4); - if (IS_I965G(dev)) { + if (INTEL_INFO(dev)->gen >= 4) { intel_ring_emit(dev, ring, MI_BATCH_BUFFER_START | (2 << 6) | MI_BATCH_NON_SECURE_I965); @@ -553,7 +506,7 @@ render_ring_dispatch_gem_execbuffer(struct drm_device *dev, } static void cleanup_status_page(struct drm_device *dev, - struct intel_ring_buffer *ring) + struct intel_ring_buffer *ring) { drm_i915_private_t *dev_priv = dev->dev_private; struct drm_gem_object *obj; @@ -573,7 +526,7 @@ static void cleanup_status_page(struct drm_device *dev, } static int init_status_page(struct drm_device *dev, - struct intel_ring_buffer *ring) + struct intel_ring_buffer *ring) { drm_i915_private_t *dev_priv = dev->dev_private; struct drm_gem_object *obj; @@ -603,7 +556,7 @@ static int init_status_page(struct drm_device *dev, ring->status_page.obj = obj; memset(ring->status_page.page_addr, 0, PAGE_SIZE); - ring->setup_status_page(dev, ring); + intel_ring_setup_status_page(dev, ring); DRM_DEBUG_DRIVER("%s hws offset: 0x%08x\n", ring->name, ring->status_page.gfx_addr); @@ -617,15 +570,17 @@ err: return ret; } - int intel_init_ring_buffer(struct drm_device *dev, - struct intel_ring_buffer *ring) + struct intel_ring_buffer *ring) { + struct drm_i915_private *dev_priv = dev->dev_private; struct drm_i915_gem_object *obj_priv; struct drm_gem_object *obj; int ret; ring->dev = dev; + INIT_LIST_HEAD(&ring->active_list); + INIT_LIST_HEAD(&ring->request_list); if (I915_NEED_GFX_HWS(dev)) { ret = init_status_page(dev, ring); @@ -642,7 +597,7 @@ int intel_init_ring_buffer(struct drm_device *dev, ring->gem_object = obj; - ret = i915_gem_object_pin(obj, ring->alignment); + ret = i915_gem_object_pin(obj, PAGE_SIZE); if (ret) goto err_unref; @@ -668,14 +623,12 @@ int intel_init_ring_buffer(struct drm_device *dev, if (!drm_core_check_feature(dev, DRIVER_MODESET)) i915_kernel_lost_context(dev); else { - ring->head = ring->get_head(dev, ring); - ring->tail = ring->get_tail(dev, ring); + ring->head = I915_READ_HEAD(ring) & HEAD_ADDR; + ring->tail = I915_READ_TAIL(ring) & TAIL_ADDR; ring->space = ring->head - (ring->tail + 8); if (ring->space < 0) ring->space += ring->size; } - INIT_LIST_HEAD(&ring->active_list); - INIT_LIST_HEAD(&ring->request_list); return ret; err_unmap: @@ -691,7 +644,7 @@ err_hws: } void intel_cleanup_ring_buffer(struct drm_device *dev, - struct intel_ring_buffer *ring) + struct intel_ring_buffer *ring) { if (ring->gem_object == NULL) return; @@ -704,8 +657,8 @@ void intel_cleanup_ring_buffer(struct drm_device *dev, cleanup_status_page(dev, ring); } -int intel_wrap_ring_buffer(struct drm_device *dev, - struct intel_ring_buffer *ring) +static int intel_wrap_ring_buffer(struct drm_device *dev, + struct intel_ring_buffer *ring) { unsigned int *virt; int rem; @@ -731,14 +684,15 @@ int intel_wrap_ring_buffer(struct drm_device *dev, } int intel_wait_ring_buffer(struct drm_device *dev, - struct intel_ring_buffer *ring, int n) + struct intel_ring_buffer *ring, int n) { unsigned long end; + drm_i915_private_t *dev_priv = dev->dev_private; trace_i915_ring_wait_begin (dev); end = jiffies + 3 * HZ; do { - ring->head = ring->get_head(dev, ring); + ring->head = I915_READ_HEAD(ring) & HEAD_ADDR; ring->space = ring->head - (ring->tail + 8); if (ring->space < 0) ring->space += ring->size; @@ -760,7 +714,8 @@ int intel_wait_ring_buffer(struct drm_device *dev, } void intel_ring_begin(struct drm_device *dev, - struct intel_ring_buffer *ring, int num_dwords) + struct intel_ring_buffer *ring, + int num_dwords) { int n = 4*num_dwords; if (unlikely(ring->tail + n > ring->size)) @@ -772,16 +727,16 @@ void intel_ring_begin(struct drm_device *dev, } void intel_ring_advance(struct drm_device *dev, - struct intel_ring_buffer *ring) + struct intel_ring_buffer *ring) { ring->tail &= ring->size - 1; - ring->advance_ring(dev, ring); + ring->set_tail(dev, ring, ring->tail); } void intel_fill_struct(struct drm_device *dev, - struct intel_ring_buffer *ring, - void *data, - unsigned int len) + struct intel_ring_buffer *ring, + void *data, + unsigned int len) { unsigned int *virt = ring->virtual_start + ring->tail; BUG_ON((len&~(4-1)) != 0); @@ -793,76 +748,136 @@ void intel_fill_struct(struct drm_device *dev, intel_ring_advance(dev, ring); } -struct intel_ring_buffer render_ring = { +static const struct intel_ring_buffer render_ring = { .name = "render ring", - .regs = { - .ctl = PRB0_CTL, - .head = PRB0_HEAD, - .tail = PRB0_TAIL, - .start = PRB0_START - }, - .ring_flag = I915_EXEC_RENDER, + .id = RING_RENDER, + .mmio_base = RENDER_RING_BASE, .size = 32 * PAGE_SIZE, - .alignment = PAGE_SIZE, - .virtual_start = NULL, - .dev = NULL, - .gem_object = NULL, - .head = 0, - .tail = 0, - .space = 0, - .user_irq_refcount = 0, - .irq_gem_seqno = 0, - .waiting_gem_seqno = 0, - .setup_status_page = render_setup_status_page, .init = init_render_ring, - .get_head = render_ring_get_head, - .get_tail = render_ring_get_tail, - .get_active_head = render_ring_get_active_head, - .advance_ring = render_ring_advance_ring, + .set_tail = ring_set_tail, .flush = render_ring_flush, .add_request = render_ring_add_request, - .get_gem_seqno = render_ring_get_gem_seqno, + .get_seqno = render_ring_get_seqno, .user_irq_get = render_ring_get_user_irq, .user_irq_put = render_ring_put_user_irq, .dispatch_gem_execbuffer = render_ring_dispatch_gem_execbuffer, - .status_page = {NULL, 0, NULL}, - .map = {0,} }; /* ring buffer for bit-stream decoder */ -struct intel_ring_buffer bsd_ring = { +static const struct intel_ring_buffer bsd_ring = { .name = "bsd ring", - .regs = { - .ctl = BSD_RING_CTL, - .head = BSD_RING_HEAD, - .tail = BSD_RING_TAIL, - .start = BSD_RING_START - }, - .ring_flag = I915_EXEC_BSD, + .id = RING_BSD, + .mmio_base = BSD_RING_BASE, .size = 32 * PAGE_SIZE, - .alignment = PAGE_SIZE, - .virtual_start = NULL, - .dev = NULL, - .gem_object = NULL, - .head = 0, - .tail = 0, - .space = 0, - .user_irq_refcount = 0, - .irq_gem_seqno = 0, - .waiting_gem_seqno = 0, - .setup_status_page = bsd_setup_status_page, .init = init_bsd_ring, - .get_head = bsd_ring_get_head, - .get_tail = bsd_ring_get_tail, - .get_active_head = bsd_ring_get_active_head, - .advance_ring = bsd_ring_advance_ring, + .set_tail = ring_set_tail, .flush = bsd_ring_flush, .add_request = bsd_ring_add_request, - .get_gem_seqno = bsd_ring_get_gem_seqno, + .get_seqno = bsd_ring_get_seqno, .user_irq_get = bsd_ring_get_user_irq, .user_irq_put = bsd_ring_put_user_irq, .dispatch_gem_execbuffer = bsd_ring_dispatch_gem_execbuffer, - .status_page = {NULL, 0, NULL}, - .map = {0,} }; + + +static void gen6_bsd_ring_set_tail(struct drm_device *dev, + struct intel_ring_buffer *ring, + u32 value) +{ + drm_i915_private_t *dev_priv = dev->dev_private; + + /* Every tail move must follow the sequence below */ + I915_WRITE(GEN6_BSD_SLEEP_PSMI_CONTROL, + GEN6_BSD_SLEEP_PSMI_CONTROL_RC_ILDL_MESSAGE_MODIFY_MASK | + GEN6_BSD_SLEEP_PSMI_CONTROL_RC_ILDL_MESSAGE_DISABLE); + I915_WRITE(GEN6_BSD_RNCID, 0x0); + + if (wait_for((I915_READ(GEN6_BSD_SLEEP_PSMI_CONTROL) & + GEN6_BSD_SLEEP_PSMI_CONTROL_IDLE_INDICATOR) == 0, + 50)) + DRM_ERROR("timed out waiting for IDLE Indicator\n"); + + I915_WRITE_TAIL(ring, value); + I915_WRITE(GEN6_BSD_SLEEP_PSMI_CONTROL, + GEN6_BSD_SLEEP_PSMI_CONTROL_RC_ILDL_MESSAGE_MODIFY_MASK | + GEN6_BSD_SLEEP_PSMI_CONTROL_RC_ILDL_MESSAGE_ENABLE); +} + +static void gen6_bsd_ring_flush(struct drm_device *dev, + struct intel_ring_buffer *ring, + u32 invalidate_domains, + u32 flush_domains) +{ + intel_ring_begin(dev, ring, 4); + intel_ring_emit(dev, ring, MI_FLUSH_DW); + intel_ring_emit(dev, ring, 0); + intel_ring_emit(dev, ring, 0); + intel_ring_emit(dev, ring, 0); + intel_ring_advance(dev, ring); +} + +static int +gen6_bsd_ring_dispatch_gem_execbuffer(struct drm_device *dev, + struct intel_ring_buffer *ring, + struct drm_i915_gem_execbuffer2 *exec, + struct drm_clip_rect *cliprects, + uint64_t exec_offset) +{ + uint32_t exec_start; + + exec_start = (uint32_t) exec_offset + exec->batch_start_offset; + + intel_ring_begin(dev, ring, 2); + intel_ring_emit(dev, ring, + MI_BATCH_BUFFER_START | MI_BATCH_NON_SECURE_I965); + /* bit0-7 is the length on GEN6+ */ + intel_ring_emit(dev, ring, exec_start); + intel_ring_advance(dev, ring); + + return 0; +} + +/* ring buffer for Video Codec for Gen6+ */ +static const struct intel_ring_buffer gen6_bsd_ring = { + .name = "gen6 bsd ring", + .id = RING_BSD, + .mmio_base = GEN6_BSD_RING_BASE, + .size = 32 * PAGE_SIZE, + .init = init_bsd_ring, + .set_tail = gen6_bsd_ring_set_tail, + .flush = gen6_bsd_ring_flush, + .add_request = bsd_ring_add_request, + .get_seqno = bsd_ring_get_seqno, + .user_irq_get = bsd_ring_get_user_irq, + .user_irq_put = bsd_ring_put_user_irq, + .dispatch_gem_execbuffer = gen6_bsd_ring_dispatch_gem_execbuffer, +}; + +int intel_init_render_ring_buffer(struct drm_device *dev) +{ + drm_i915_private_t *dev_priv = dev->dev_private; + + dev_priv->render_ring = render_ring; + + if (!I915_NEED_GFX_HWS(dev)) { + dev_priv->render_ring.status_page.page_addr + = dev_priv->status_page_dmah->vaddr; + memset(dev_priv->render_ring.status_page.page_addr, + 0, PAGE_SIZE); + } + + return intel_init_ring_buffer(dev, &dev_priv->render_ring); +} + +int intel_init_bsd_ring_buffer(struct drm_device *dev) +{ + drm_i915_private_t *dev_priv = dev->dev_private; + + if (IS_GEN6(dev)) + dev_priv->bsd_ring = gen6_bsd_ring; + else + dev_priv->bsd_ring = bsd_ring; + + return intel_init_ring_buffer(dev, &dev_priv->bsd_ring); +} diff --git a/drivers/gpu/drm/i915/intel_ringbuffer.h b/drivers/gpu/drm/i915/intel_ringbuffer.h index 525e7d3edda..9725f783db2 100644 --- a/drivers/gpu/drm/i915/intel_ringbuffer.h +++ b/drivers/gpu/drm/i915/intel_ringbuffer.h @@ -7,25 +7,31 @@ struct intel_hw_status_page { struct drm_gem_object *obj; }; +#define I915_READ_TAIL(ring) I915_READ(RING_TAIL(ring->mmio_base)) +#define I915_WRITE_TAIL(ring, val) I915_WRITE(RING_TAIL(ring->mmio_base), val) +#define I915_READ_START(ring) I915_READ(RING_START(ring->mmio_base)) +#define I915_WRITE_START(ring, val) I915_WRITE(RING_START(ring->mmio_base), val) +#define I915_READ_HEAD(ring) I915_READ(RING_HEAD(ring->mmio_base)) +#define I915_WRITE_HEAD(ring, val) I915_WRITE(RING_HEAD(ring->mmio_base), val) +#define I915_READ_CTL(ring) I915_READ(RING_CTL(ring->mmio_base)) +#define I915_WRITE_CTL(ring, val) I915_WRITE(RING_CTL(ring->mmio_base), val) + struct drm_i915_gem_execbuffer2; struct intel_ring_buffer { const char *name; - struct ring_regs { - u32 ctl; - u32 head; - u32 tail; - u32 start; - } regs; - unsigned int ring_flag; + enum intel_ring_id { + RING_RENDER = 0x1, + RING_BSD = 0x2, + } id; + u32 mmio_base; unsigned long size; - unsigned int alignment; void *virtual_start; struct drm_device *dev; struct drm_gem_object *gem_object; unsigned int head; unsigned int tail; - unsigned int space; + int space; struct intel_hw_status_page status_page; u32 irq_gem_seqno; /* last seq seem at irq time */ @@ -35,30 +41,22 @@ struct intel_ring_buffer { struct intel_ring_buffer *ring); void (*user_irq_put)(struct drm_device *dev, struct intel_ring_buffer *ring); - void (*setup_status_page)(struct drm_device *dev, - struct intel_ring_buffer *ring); int (*init)(struct drm_device *dev, struct intel_ring_buffer *ring); - unsigned int (*get_head)(struct drm_device *dev, - struct intel_ring_buffer *ring); - unsigned int (*get_tail)(struct drm_device *dev, - struct intel_ring_buffer *ring); - unsigned int (*get_active_head)(struct drm_device *dev, - struct intel_ring_buffer *ring); - void (*advance_ring)(struct drm_device *dev, - struct intel_ring_buffer *ring); + void (*set_tail)(struct drm_device *dev, + struct intel_ring_buffer *ring, + u32 value); void (*flush)(struct drm_device *dev, struct intel_ring_buffer *ring, u32 invalidate_domains, u32 flush_domains); u32 (*add_request)(struct drm_device *dev, struct intel_ring_buffer *ring, - struct drm_file *file_priv, u32 flush_domains); - u32 (*get_gem_seqno)(struct drm_device *dev, - struct intel_ring_buffer *ring); + u32 (*get_seqno)(struct drm_device *dev, + struct intel_ring_buffer *ring); int (*dispatch_gem_execbuffer)(struct drm_device *dev, struct intel_ring_buffer *ring, struct drm_i915_gem_execbuffer2 *exec, @@ -83,6 +81,11 @@ struct intel_ring_buffer { */ struct list_head request_list; + /** + * Do we have some not yet emitted requests outstanding? + */ + bool outstanding_lazy_request; + wait_queue_head_t irq_queue; drm_local_map_t map; }; @@ -96,15 +99,13 @@ intel_read_status_page(struct intel_ring_buffer *ring, } int intel_init_ring_buffer(struct drm_device *dev, - struct intel_ring_buffer *ring); + struct intel_ring_buffer *ring); void intel_cleanup_ring_buffer(struct drm_device *dev, - struct intel_ring_buffer *ring); + struct intel_ring_buffer *ring); int intel_wait_ring_buffer(struct drm_device *dev, - struct intel_ring_buffer *ring, int n); -int intel_wrap_ring_buffer(struct drm_device *dev, - struct intel_ring_buffer *ring); + struct intel_ring_buffer *ring, int n); void intel_ring_begin(struct drm_device *dev, - struct intel_ring_buffer *ring, int n); + struct intel_ring_buffer *ring, int n); static inline void intel_ring_emit(struct drm_device *dev, struct intel_ring_buffer *ring, @@ -125,7 +126,12 @@ void intel_ring_advance(struct drm_device *dev, u32 intel_ring_get_seqno(struct drm_device *dev, struct intel_ring_buffer *ring); -extern struct intel_ring_buffer render_ring; -extern struct intel_ring_buffer bsd_ring; +int intel_init_render_ring_buffer(struct drm_device *dev); +int intel_init_bsd_ring_buffer(struct drm_device *dev); + +u32 intel_ring_get_active_head(struct drm_device *dev, + struct intel_ring_buffer *ring); +void intel_ring_setup_status_page(struct drm_device *dev, + struct intel_ring_buffer *ring); #endif /* _INTEL_RINGBUFFER_H_ */ diff --git a/drivers/gpu/drm/i915/intel_sdvo.c b/drivers/gpu/drm/i915/intel_sdvo.c index e8e902d614e..a84224f3760 100644 --- a/drivers/gpu/drm/i915/intel_sdvo.c +++ b/drivers/gpu/drm/i915/intel_sdvo.c @@ -65,8 +65,11 @@ static const char *tv_format_names[] = { struct intel_sdvo { struct intel_encoder base; + struct i2c_adapter *i2c; u8 slave_addr; + struct i2c_adapter ddc; + /* Register for the SDVO device: SDVOB or SDVOC */ int sdvo_reg; @@ -106,16 +109,12 @@ struct intel_sdvo { bool is_hdmi; /** - * This is set if we detect output of sdvo device as LVDS. + * This is set if we detect output of sdvo device as LVDS and + * have a valid fixed mode to use with the panel. */ bool is_lvds; /** - * This is sdvo flags for input timing. - */ - uint8_t sdvo_flags; - - /** * This is sdvo fixed pannel mode pointer */ struct drm_display_mode *sdvo_lvds_fixed_mode; @@ -129,9 +128,8 @@ struct intel_sdvo { /* DDC bus used by this SDVO encoder */ uint8_t ddc_bus; - /* Mac mini hack -- use the same DDC as the analog connector */ - struct i2c_adapter *analog_ddc_bus; - + /* Input timings for adjusted_mode */ + struct intel_sdvo_dtd input_dtd; }; struct intel_sdvo_connector { @@ -186,9 +184,15 @@ struct intel_sdvo_connector { u32 cur_dot_crawl, max_dot_crawl; }; -static struct intel_sdvo *enc_to_intel_sdvo(struct drm_encoder *encoder) +static struct intel_sdvo *to_intel_sdvo(struct drm_encoder *encoder) +{ + return container_of(encoder, struct intel_sdvo, base.base); +} + +static struct intel_sdvo *intel_attached_sdvo(struct drm_connector *connector) { - return container_of(enc_to_intel_encoder(encoder), struct intel_sdvo, base); + return container_of(intel_attached_encoder(connector), + struct intel_sdvo, base); } static struct intel_sdvo_connector *to_intel_sdvo_connector(struct drm_connector *connector) @@ -213,7 +217,7 @@ intel_sdvo_create_enhance_property(struct intel_sdvo *intel_sdvo, */ static void intel_sdvo_write_sdvox(struct intel_sdvo *intel_sdvo, u32 val) { - struct drm_device *dev = intel_sdvo->base.enc.dev; + struct drm_device *dev = intel_sdvo->base.base.dev; struct drm_i915_private *dev_priv = dev->dev_private; u32 bval = val, cval = val; int i; @@ -245,49 +249,29 @@ static void intel_sdvo_write_sdvox(struct intel_sdvo *intel_sdvo, u32 val) static bool intel_sdvo_read_byte(struct intel_sdvo *intel_sdvo, u8 addr, u8 *ch) { - u8 out_buf[2] = { addr, 0 }; - u8 buf[2]; struct i2c_msg msgs[] = { { - .addr = intel_sdvo->slave_addr >> 1, + .addr = intel_sdvo->slave_addr, .flags = 0, .len = 1, - .buf = out_buf, + .buf = &addr, }, { - .addr = intel_sdvo->slave_addr >> 1, + .addr = intel_sdvo->slave_addr, .flags = I2C_M_RD, .len = 1, - .buf = buf, + .buf = ch, } }; int ret; - if ((ret = i2c_transfer(intel_sdvo->base.i2c_bus, msgs, 2)) == 2) - { - *ch = buf[0]; + if ((ret = i2c_transfer(intel_sdvo->i2c, msgs, 2)) == 2) return true; - } DRM_DEBUG_KMS("i2c transfer returned %d\n", ret); return false; } -static bool intel_sdvo_write_byte(struct intel_sdvo *intel_sdvo, int addr, u8 ch) -{ - u8 out_buf[2] = { addr, ch }; - struct i2c_msg msgs[] = { - { - .addr = intel_sdvo->slave_addr >> 1, - .flags = 0, - .len = 2, - .buf = out_buf, - } - }; - - return i2c_transfer(intel_sdvo->base.i2c_bus, msgs, 1) == 1; -} - #define SDVO_CMD_NAME_ENTRY(cmd) {cmd, #cmd} /** Mapping of command numbers to names, for debug output */ static const struct _sdvo_cmd_name { @@ -432,22 +416,6 @@ static void intel_sdvo_debug_write(struct intel_sdvo *intel_sdvo, u8 cmd, DRM_LOG_KMS("\n"); } -static bool intel_sdvo_write_cmd(struct intel_sdvo *intel_sdvo, u8 cmd, - const void *args, int args_len) -{ - int i; - - intel_sdvo_debug_write(intel_sdvo, cmd, args, args_len); - - for (i = 0; i < args_len; i++) { - if (!intel_sdvo_write_byte(intel_sdvo, SDVO_I2C_ARG_0 - i, - ((u8*)args)[i])) - return false; - } - - return intel_sdvo_write_byte(intel_sdvo, SDVO_I2C_OPCODE, cmd); -} - static const char *cmd_status_names[] = { "Power on", "Success", @@ -458,54 +426,115 @@ static const char *cmd_status_names[] = { "Scaling not supported" }; -static void intel_sdvo_debug_response(struct intel_sdvo *intel_sdvo, - void *response, int response_len, - u8 status) +static bool intel_sdvo_write_cmd(struct intel_sdvo *intel_sdvo, u8 cmd, + const void *args, int args_len) { - int i; + u8 buf[args_len*2 + 2], status; + struct i2c_msg msgs[args_len + 3]; + int i, ret; - DRM_DEBUG_KMS("%s: R: ", SDVO_NAME(intel_sdvo)); - for (i = 0; i < response_len; i++) - DRM_LOG_KMS("%02X ", ((u8 *)response)[i]); - for (; i < 8; i++) - DRM_LOG_KMS(" "); - if (status <= SDVO_CMD_STATUS_SCALING_NOT_SUPP) - DRM_LOG_KMS("(%s)", cmd_status_names[status]); - else - DRM_LOG_KMS("(??? %d)", status); - DRM_LOG_KMS("\n"); + intel_sdvo_debug_write(intel_sdvo, cmd, args, args_len); + + for (i = 0; i < args_len; i++) { + msgs[i].addr = intel_sdvo->slave_addr; + msgs[i].flags = 0; + msgs[i].len = 2; + msgs[i].buf = buf + 2 *i; + buf[2*i + 0] = SDVO_I2C_ARG_0 - i; + buf[2*i + 1] = ((u8*)args)[i]; + } + msgs[i].addr = intel_sdvo->slave_addr; + msgs[i].flags = 0; + msgs[i].len = 2; + msgs[i].buf = buf + 2*i; + buf[2*i + 0] = SDVO_I2C_OPCODE; + buf[2*i + 1] = cmd; + + /* the following two are to read the response */ + status = SDVO_I2C_CMD_STATUS; + msgs[i+1].addr = intel_sdvo->slave_addr; + msgs[i+1].flags = 0; + msgs[i+1].len = 1; + msgs[i+1].buf = &status; + + msgs[i+2].addr = intel_sdvo->slave_addr; + msgs[i+2].flags = I2C_M_RD; + msgs[i+2].len = 1; + msgs[i+2].buf = &status; + + ret = i2c_transfer(intel_sdvo->i2c, msgs, i+3); + if (ret < 0) { + DRM_DEBUG_KMS("I2c transfer returned %d\n", ret); + return false; + } + if (ret != i+3) { + /* failure in I2C transfer */ + DRM_DEBUG_KMS("I2c transfer returned %d/%d\n", ret, i+3); + return false; + } + + i = 3; + while (status == SDVO_CMD_STATUS_PENDING && i--) { + if (!intel_sdvo_read_byte(intel_sdvo, + SDVO_I2C_CMD_STATUS, + &status)) + return false; + } + if (status != SDVO_CMD_STATUS_SUCCESS) { + DRM_DEBUG_KMS("command returns response %s [%d]\n", + status <= SDVO_CMD_STATUS_SCALING_NOT_SUPP ? cmd_status_names[status] : "???", + status); + return false; + } + + return true; } static bool intel_sdvo_read_response(struct intel_sdvo *intel_sdvo, void *response, int response_len) { - int i; + u8 retry = 5; u8 status; - u8 retry = 50; - - while (retry--) { - /* Read the command response */ - for (i = 0; i < response_len; i++) { - if (!intel_sdvo_read_byte(intel_sdvo, - SDVO_I2C_RETURN_0 + i, - &((u8 *)response)[i])) - return false; - } + int i; - /* read the return status */ - if (!intel_sdvo_read_byte(intel_sdvo, SDVO_I2C_CMD_STATUS, + /* + * The documentation states that all commands will be + * processed within 15µs, and that we need only poll + * the status byte a maximum of 3 times in order for the + * command to be complete. + * + * Check 5 times in case the hardware failed to read the docs. + */ + do { + if (!intel_sdvo_read_byte(intel_sdvo, + SDVO_I2C_CMD_STATUS, &status)) return false; + } while (status == SDVO_CMD_STATUS_PENDING && --retry); - intel_sdvo_debug_response(intel_sdvo, response, response_len, - status); - if (status != SDVO_CMD_STATUS_PENDING) - break; + DRM_DEBUG_KMS("%s: R: ", SDVO_NAME(intel_sdvo)); + if (status <= SDVO_CMD_STATUS_SCALING_NOT_SUPP) + DRM_LOG_KMS("(%s)", cmd_status_names[status]); + else + DRM_LOG_KMS("(??? %d)", status); + + if (status != SDVO_CMD_STATUS_SUCCESS) + goto log_fail; - mdelay(50); + /* Read the command response */ + for (i = 0; i < response_len; i++) { + if (!intel_sdvo_read_byte(intel_sdvo, + SDVO_I2C_RETURN_0 + i, + &((u8 *)response)[i])) + goto log_fail; + DRM_LOG_KMS(" %02X", ((u8 *)response)[i]); } + DRM_LOG_KMS("\n"); + return true; - return status == SDVO_CMD_STATUS_SUCCESS; +log_fail: + DRM_LOG_KMS("\n"); + return false; } static int intel_sdvo_get_pixel_multiplier(struct drm_display_mode *mode) @@ -518,71 +547,17 @@ static int intel_sdvo_get_pixel_multiplier(struct drm_display_mode *mode) return 4; } -/** - * Try to read the response after issuie the DDC switch command. But it - * is noted that we must do the action of reading response and issuing DDC - * switch command in one I2C transaction. Otherwise when we try to start - * another I2C transaction after issuing the DDC bus switch, it will be - * switched to the internal SDVO register. - */ -static void intel_sdvo_set_control_bus_switch(struct intel_sdvo *intel_sdvo, - u8 target) +static bool intel_sdvo_set_control_bus_switch(struct intel_sdvo *intel_sdvo, + u8 ddc_bus) { - u8 out_buf[2], cmd_buf[2], ret_value[2], ret; - struct i2c_msg msgs[] = { - { - .addr = intel_sdvo->slave_addr >> 1, - .flags = 0, - .len = 2, - .buf = out_buf, - }, - /* the following two are to read the response */ - { - .addr = intel_sdvo->slave_addr >> 1, - .flags = 0, - .len = 1, - .buf = cmd_buf, - }, - { - .addr = intel_sdvo->slave_addr >> 1, - .flags = I2C_M_RD, - .len = 1, - .buf = ret_value, - }, - }; - - intel_sdvo_debug_write(intel_sdvo, SDVO_CMD_SET_CONTROL_BUS_SWITCH, - &target, 1); - /* write the DDC switch command argument */ - intel_sdvo_write_byte(intel_sdvo, SDVO_I2C_ARG_0, target); - - out_buf[0] = SDVO_I2C_OPCODE; - out_buf[1] = SDVO_CMD_SET_CONTROL_BUS_SWITCH; - cmd_buf[0] = SDVO_I2C_CMD_STATUS; - cmd_buf[1] = 0; - ret_value[0] = 0; - ret_value[1] = 0; - - ret = i2c_transfer(intel_sdvo->base.i2c_bus, msgs, 3); - if (ret != 3) { - /* failure in I2C transfer */ - DRM_DEBUG_KMS("I2c transfer returned %d\n", ret); - return; - } - if (ret_value[0] != SDVO_CMD_STATUS_SUCCESS) { - DRM_DEBUG_KMS("DDC switch command returns response %d\n", - ret_value[0]); - return; - } - return; + return intel_sdvo_write_cmd(intel_sdvo, + SDVO_CMD_SET_CONTROL_BUS_SWITCH, + &ddc_bus, 1); } static bool intel_sdvo_set_value(struct intel_sdvo *intel_sdvo, u8 cmd, const void *data, int len) { - if (!intel_sdvo_write_cmd(intel_sdvo, cmd, data, len)) - return false; - - return intel_sdvo_read_response(intel_sdvo, NULL, 0); + return intel_sdvo_write_cmd(intel_sdvo, cmd, data, len); } static bool @@ -1022,8 +997,6 @@ intel_sdvo_set_input_timings_for_mode(struct intel_sdvo *intel_sdvo, struct drm_display_mode *mode, struct drm_display_mode *adjusted_mode) { - struct intel_sdvo_dtd input_dtd; - /* Reset the input timing to the screen. Assume always input 0. */ if (!intel_sdvo_set_target_input(intel_sdvo)) return false; @@ -1035,14 +1008,12 @@ intel_sdvo_set_input_timings_for_mode(struct intel_sdvo *intel_sdvo, return false; if (!intel_sdvo_get_preferred_input_timing(intel_sdvo, - &input_dtd)) + &intel_sdvo->input_dtd)) return false; - intel_sdvo_get_mode_from_dtd(adjusted_mode, &input_dtd); - intel_sdvo->sdvo_flags = input_dtd.part2.sdvo_flags; + intel_sdvo_get_mode_from_dtd(adjusted_mode, &intel_sdvo->input_dtd); drm_mode_set_crtcinfo(adjusted_mode, 0); - mode->clock = adjusted_mode->clock; return true; } @@ -1050,7 +1021,8 @@ static bool intel_sdvo_mode_fixup(struct drm_encoder *encoder, struct drm_display_mode *mode, struct drm_display_mode *adjusted_mode) { - struct intel_sdvo *intel_sdvo = enc_to_intel_sdvo(encoder); + struct intel_sdvo *intel_sdvo = to_intel_sdvo(encoder); + int multiplier; /* We need to construct preferred input timings based on our * output timings. To do that, we have to set the output @@ -1065,10 +1037,8 @@ static bool intel_sdvo_mode_fixup(struct drm_encoder *encoder, mode, adjusted_mode); } else if (intel_sdvo->is_lvds) { - drm_mode_set_crtcinfo(intel_sdvo->sdvo_lvds_fixed_mode, 0); - if (!intel_sdvo_set_output_timings_from_mode(intel_sdvo, - intel_sdvo->sdvo_lvds_fixed_mode)) + intel_sdvo->sdvo_lvds_fixed_mode)) return false; (void) intel_sdvo_set_input_timings_for_mode(intel_sdvo, @@ -1077,9 +1047,10 @@ static bool intel_sdvo_mode_fixup(struct drm_encoder *encoder, } /* Make the CRTC code factor in the SDVO pixel multiplier. The - * SDVO device will be told of the multiplier during mode_set. + * SDVO device will factor out the multiplier during mode_set. */ - adjusted_mode->clock *= intel_sdvo_get_pixel_multiplier(mode); + multiplier = intel_sdvo_get_pixel_multiplier(adjusted_mode); + intel_mode_set_pixel_multiplier(adjusted_mode, multiplier); return true; } @@ -1092,11 +1063,12 @@ static void intel_sdvo_mode_set(struct drm_encoder *encoder, struct drm_i915_private *dev_priv = dev->dev_private; struct drm_crtc *crtc = encoder->crtc; struct intel_crtc *intel_crtc = to_intel_crtc(crtc); - struct intel_sdvo *intel_sdvo = enc_to_intel_sdvo(encoder); - u32 sdvox = 0; - int sdvo_pixel_multiply, rate; + struct intel_sdvo *intel_sdvo = to_intel_sdvo(encoder); + u32 sdvox; struct intel_sdvo_in_out_map in_out; struct intel_sdvo_dtd input_dtd; + int pixel_multiplier = intel_mode_get_pixel_multiplier(adjusted_mode); + int rate; if (!mode) return; @@ -1114,28 +1086,23 @@ static void intel_sdvo_mode_set(struct drm_encoder *encoder, SDVO_CMD_SET_IN_OUT_MAP, &in_out, sizeof(in_out)); - if (intel_sdvo->is_hdmi) { - if (!intel_sdvo_set_avi_infoframe(intel_sdvo, mode)) - return; - - sdvox |= SDVO_AUDIO_ENABLE; - } + /* Set the output timings to the screen */ + if (!intel_sdvo_set_target_output(intel_sdvo, + intel_sdvo->attached_output)) + return; /* We have tried to get input timing in mode_fixup, and filled into - adjusted_mode */ - intel_sdvo_get_dtd_from_mode(&input_dtd, adjusted_mode); - if (intel_sdvo->is_tv || intel_sdvo->is_lvds) - input_dtd.part2.sdvo_flags = intel_sdvo->sdvo_flags; - - /* If it's a TV, we already set the output timing in mode_fixup. - * Otherwise, the output timing is equal to the input timing. + * adjusted_mode. */ - if (!intel_sdvo->is_tv && !intel_sdvo->is_lvds) { + if (intel_sdvo->is_tv || intel_sdvo->is_lvds) { + input_dtd = intel_sdvo->input_dtd; + } else { /* Set the output timing to the screen */ if (!intel_sdvo_set_target_output(intel_sdvo, intel_sdvo->attached_output)) return; + intel_sdvo_get_dtd_from_mode(&input_dtd, adjusted_mode); (void) intel_sdvo_set_output_timing(intel_sdvo, &input_dtd); } @@ -1143,31 +1110,18 @@ static void intel_sdvo_mode_set(struct drm_encoder *encoder, if (!intel_sdvo_set_target_input(intel_sdvo)) return; - if (intel_sdvo->is_tv) { - if (!intel_sdvo_set_tv_format(intel_sdvo)) - return; - } + if (intel_sdvo->is_hdmi && + !intel_sdvo_set_avi_infoframe(intel_sdvo, mode)) + return; - /* We would like to use intel_sdvo_create_preferred_input_timing() to - * provide the device with a timing it can support, if it supports that - * feature. However, presumably we would need to adjust the CRTC to - * output the preferred timing, and we don't support that currently. - */ -#if 0 - success = intel_sdvo_create_preferred_input_timing(encoder, clock, - width, height); - if (success) { - struct intel_sdvo_dtd *input_dtd; + if (intel_sdvo->is_tv && + !intel_sdvo_set_tv_format(intel_sdvo)) + return; - intel_sdvo_get_preferred_input_timing(encoder, &input_dtd); - intel_sdvo_set_input_timing(encoder, &input_dtd); - } -#else (void) intel_sdvo_set_input_timing(intel_sdvo, &input_dtd); -#endif - sdvo_pixel_multiply = intel_sdvo_get_pixel_multiplier(mode); - switch (sdvo_pixel_multiply) { + switch (pixel_multiplier) { + default: case 1: rate = SDVO_CLOCK_RATE_MULT_1X; break; case 2: rate = SDVO_CLOCK_RATE_MULT_2X; break; case 4: rate = SDVO_CLOCK_RATE_MULT_4X; break; @@ -1176,14 +1130,14 @@ static void intel_sdvo_mode_set(struct drm_encoder *encoder, return; /* Set the SDVO control regs. */ - if (IS_I965G(dev)) { - sdvox |= SDVO_BORDER_ENABLE; + if (INTEL_INFO(dev)->gen >= 4) { + sdvox = SDVO_BORDER_ENABLE; if (adjusted_mode->flags & DRM_MODE_FLAG_PVSYNC) sdvox |= SDVO_VSYNC_ACTIVE_HIGH; if (adjusted_mode->flags & DRM_MODE_FLAG_PHSYNC) sdvox |= SDVO_HSYNC_ACTIVE_HIGH; } else { - sdvox |= I915_READ(intel_sdvo->sdvo_reg); + sdvox = I915_READ(intel_sdvo->sdvo_reg); switch (intel_sdvo->sdvo_reg) { case SDVOB: sdvox &= SDVOB_PRESERVE_MASK; @@ -1196,16 +1150,18 @@ static void intel_sdvo_mode_set(struct drm_encoder *encoder, } if (intel_crtc->pipe == 1) sdvox |= SDVO_PIPE_B_SELECT; + if (intel_sdvo->is_hdmi) + sdvox |= SDVO_AUDIO_ENABLE; - if (IS_I965G(dev)) { + if (INTEL_INFO(dev)->gen >= 4) { /* done in crtc_mode_set as the dpll_md reg must be written early */ } else if (IS_I945G(dev) || IS_I945GM(dev) || IS_G33(dev)) { /* done in crtc_mode_set as it lives inside the dpll register */ } else { - sdvox |= (sdvo_pixel_multiply - 1) << SDVO_PORT_MULTIPLY_SHIFT; + sdvox |= (pixel_multiplier - 1) << SDVO_PORT_MULTIPLY_SHIFT; } - if (intel_sdvo->sdvo_flags & SDVO_NEED_TO_STALL) + if (input_dtd.part2.sdvo_flags & SDVO_NEED_TO_STALL) sdvox |= SDVO_STALL_SELECT; intel_sdvo_write_sdvox(intel_sdvo, sdvox); } @@ -1214,7 +1170,7 @@ static void intel_sdvo_dpms(struct drm_encoder *encoder, int mode) { struct drm_device *dev = encoder->dev; struct drm_i915_private *dev_priv = dev->dev_private; - struct intel_sdvo *intel_sdvo = enc_to_intel_sdvo(encoder); + struct intel_sdvo *intel_sdvo = to_intel_sdvo(encoder); struct intel_crtc *intel_crtc = to_intel_crtc(encoder->crtc); u32 temp; @@ -1260,8 +1216,7 @@ static void intel_sdvo_dpms(struct drm_encoder *encoder, int mode) static int intel_sdvo_mode_valid(struct drm_connector *connector, struct drm_display_mode *mode) { - struct drm_encoder *encoder = intel_attached_encoder(connector); - struct intel_sdvo *intel_sdvo = enc_to_intel_sdvo(encoder); + struct intel_sdvo *intel_sdvo = intel_attached_sdvo(connector); if (mode->flags & DRM_MODE_FLAG_DBLSCAN) return MODE_NO_DBLESCAN; @@ -1285,7 +1240,38 @@ static int intel_sdvo_mode_valid(struct drm_connector *connector, static bool intel_sdvo_get_capabilities(struct intel_sdvo *intel_sdvo, struct intel_sdvo_caps *caps) { - return intel_sdvo_get_value(intel_sdvo, SDVO_CMD_GET_DEVICE_CAPS, caps, sizeof(*caps)); + if (!intel_sdvo_get_value(intel_sdvo, + SDVO_CMD_GET_DEVICE_CAPS, + caps, sizeof(*caps))) + return false; + + DRM_DEBUG_KMS("SDVO capabilities:\n" + " vendor_id: %d\n" + " device_id: %d\n" + " device_rev_id: %d\n" + " sdvo_version_major: %d\n" + " sdvo_version_minor: %d\n" + " sdvo_inputs_mask: %d\n" + " smooth_scaling: %d\n" + " sharp_scaling: %d\n" + " up_scaling: %d\n" + " down_scaling: %d\n" + " stall_support: %d\n" + " output_flags: %d\n", + caps->vendor_id, + caps->device_id, + caps->device_rev_id, + caps->sdvo_version_major, + caps->sdvo_version_minor, + caps->sdvo_inputs_mask, + caps->smooth_scaling, + caps->sharp_scaling, + caps->up_scaling, + caps->down_scaling, + caps->stall_support, + caps->output_flags); + + return true; } /* No use! */ @@ -1389,22 +1375,33 @@ intel_sdvo_multifunc_encoder(struct intel_sdvo *intel_sdvo) return (caps > 1); } +static struct edid * +intel_sdvo_get_edid(struct drm_connector *connector) +{ + struct intel_sdvo *sdvo = intel_attached_sdvo(connector); + return drm_get_edid(connector, &sdvo->ddc); +} + static struct drm_connector * intel_find_analog_connector(struct drm_device *dev) { struct drm_connector *connector; - struct drm_encoder *encoder; - struct intel_sdvo *intel_sdvo; - - list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) { - intel_sdvo = enc_to_intel_sdvo(encoder); - if (intel_sdvo->base.type == INTEL_OUTPUT_ANALOG) { - list_for_each_entry(connector, &dev->mode_config.connector_list, head) { - if (encoder == intel_attached_encoder(connector)) + struct intel_sdvo *encoder; + + list_for_each_entry(encoder, + &dev->mode_config.encoder_list, + base.base.head) { + if (encoder->base.type == INTEL_OUTPUT_ANALOG) { + list_for_each_entry(connector, + &dev->mode_config.connector_list, + head) { + if (&encoder->base == + intel_attached_encoder(connector)) return connector; } } } + return NULL; } @@ -1424,65 +1421,66 @@ intel_analog_is_connected(struct drm_device *dev) return true; } +/* Mac mini hack -- use the same DDC as the analog connector */ +static struct edid * +intel_sdvo_get_analog_edid(struct drm_connector *connector) +{ + struct drm_i915_private *dev_priv = connector->dev->dev_private; + + if (!intel_analog_is_connected(connector->dev)) + return NULL; + + return drm_get_edid(connector, &dev_priv->gmbus[dev_priv->crt_ddc_pin].adapter); +} + enum drm_connector_status intel_sdvo_hdmi_sink_detect(struct drm_connector *connector) { - struct drm_encoder *encoder = intel_attached_encoder(connector); - struct intel_sdvo *intel_sdvo = enc_to_intel_sdvo(encoder); - struct intel_sdvo_connector *intel_sdvo_connector = to_intel_sdvo_connector(connector); - enum drm_connector_status status = connector_status_connected; - struct edid *edid = NULL; + struct intel_sdvo *intel_sdvo = intel_attached_sdvo(connector); + enum drm_connector_status status; + struct edid *edid; - edid = drm_get_edid(connector, intel_sdvo->base.ddc_bus); + edid = intel_sdvo_get_edid(connector); - /* This is only applied to SDVO cards with multiple outputs */ if (edid == NULL && intel_sdvo_multifunc_encoder(intel_sdvo)) { - uint8_t saved_ddc, temp_ddc; - saved_ddc = intel_sdvo->ddc_bus; - temp_ddc = intel_sdvo->ddc_bus >> 1; + u8 ddc, saved_ddc = intel_sdvo->ddc_bus; + /* * Don't use the 1 as the argument of DDC bus switch to get * the EDID. It is used for SDVO SPD ROM. */ - while(temp_ddc > 1) { - intel_sdvo->ddc_bus = temp_ddc; - edid = drm_get_edid(connector, intel_sdvo->base.ddc_bus); - if (edid) { - /* - * When we can get the EDID, maybe it is the - * correct DDC bus. Update it. - */ - intel_sdvo->ddc_bus = temp_ddc; + for (ddc = intel_sdvo->ddc_bus >> 1; ddc > 1; ddc >>= 1) { + intel_sdvo->ddc_bus = ddc; + edid = intel_sdvo_get_edid(connector); + if (edid) break; - } - temp_ddc >>= 1; } + /* + * If we found the EDID on the other bus, + * assume that is the correct DDC bus. + */ if (edid == NULL) intel_sdvo->ddc_bus = saved_ddc; } - /* when there is no edid and no monitor is connected with VGA - * port, try to use the CRT ddc to read the EDID for DVI-connector + + /* + * When there is no edid and no monitor is connected with VGA + * port, try to use the CRT ddc to read the EDID for DVI-connector. */ - if (edid == NULL && intel_sdvo->analog_ddc_bus && - !intel_analog_is_connected(connector->dev)) - edid = drm_get_edid(connector, intel_sdvo->analog_ddc_bus); + if (edid == NULL) + edid = intel_sdvo_get_analog_edid(connector); + status = connector_status_unknown; if (edid != NULL) { - bool is_digital = !!(edid->input & DRM_EDID_INPUT_DIGITAL); - bool need_digital = !!(intel_sdvo_connector->output_flag & SDVO_TMDS_MASK); - /* DDC bus is shared, match EDID to connector type */ - if (is_digital && need_digital) + if (edid->input & DRM_EDID_INPUT_DIGITAL) { + status = connector_status_connected; intel_sdvo->is_hdmi = drm_detect_hdmi_monitor(edid); - else if (is_digital != need_digital) - status = connector_status_disconnected; - + } connector->display_info.raw_edid = NULL; - } else - status = connector_status_disconnected; + kfree(edid); + } - kfree(edid); - return status; } @@ -1490,13 +1488,12 @@ static enum drm_connector_status intel_sdvo_detect(struct drm_connector *connector, bool force) { uint16_t response; - struct drm_encoder *encoder = intel_attached_encoder(connector); - struct intel_sdvo *intel_sdvo = enc_to_intel_sdvo(encoder); + struct intel_sdvo *intel_sdvo = intel_attached_sdvo(connector); struct intel_sdvo_connector *intel_sdvo_connector = to_intel_sdvo_connector(connector); enum drm_connector_status ret; if (!intel_sdvo_write_cmd(intel_sdvo, - SDVO_CMD_GET_ATTACHED_DISPLAYS, NULL, 0)) + SDVO_CMD_GET_ATTACHED_DISPLAYS, NULL, 0)) return connector_status_unknown; if (intel_sdvo->is_tv) { /* add 30ms delay when the output type is SDVO-TV */ @@ -1505,7 +1502,9 @@ intel_sdvo_detect(struct drm_connector *connector, bool force) if (!intel_sdvo_read_response(intel_sdvo, &response, 2)) return connector_status_unknown; - DRM_DEBUG_KMS("SDVO response %d %d\n", response & 0xff, response >> 8); + DRM_DEBUG_KMS("SDVO response %d %d [%x]\n", + response & 0xff, response >> 8, + intel_sdvo_connector->output_flag); if (response == 0) return connector_status_disconnected; @@ -1538,12 +1537,10 @@ intel_sdvo_detect(struct drm_connector *connector, bool force) static void intel_sdvo_get_ddc_modes(struct drm_connector *connector) { - struct drm_encoder *encoder = intel_attached_encoder(connector); - struct intel_sdvo *intel_sdvo = enc_to_intel_sdvo(encoder); - int num_modes; + struct edid *edid; /* set the bus switch and get the modes */ - num_modes = intel_ddc_get_modes(connector, intel_sdvo->base.ddc_bus); + edid = intel_sdvo_get_edid(connector); /* * Mac mini hack. On this device, the DVI-I connector shares one DDC @@ -1551,12 +1548,14 @@ static void intel_sdvo_get_ddc_modes(struct drm_connector *connector) * DDC fails, check to see if the analog output is disconnected, in * which case we'll look there for the digital DDC data. */ - if (num_modes == 0 && - intel_sdvo->analog_ddc_bus && - !intel_analog_is_connected(connector->dev)) { - /* Switch to the analog ddc bus and try that - */ - (void) intel_ddc_get_modes(connector, intel_sdvo->analog_ddc_bus); + if (edid == NULL) + edid = intel_sdvo_get_analog_edid(connector); + + if (edid != NULL) { + drm_mode_connector_update_edid_property(connector, edid); + drm_add_edid_modes(connector, edid); + connector->display_info.raw_edid = NULL; + kfree(edid); } } @@ -1627,8 +1626,7 @@ struct drm_display_mode sdvo_tv_modes[] = { static void intel_sdvo_get_tv_modes(struct drm_connector *connector) { - struct drm_encoder *encoder = intel_attached_encoder(connector); - struct intel_sdvo *intel_sdvo = enc_to_intel_sdvo(encoder); + struct intel_sdvo *intel_sdvo = intel_attached_sdvo(connector); struct intel_sdvo_sdtv_resolution_request tv_res; uint32_t reply = 0, format_map = 0; int i; @@ -1644,7 +1642,8 @@ static void intel_sdvo_get_tv_modes(struct drm_connector *connector) return; BUILD_BUG_ON(sizeof(tv_res) != 3); - if (!intel_sdvo_write_cmd(intel_sdvo, SDVO_CMD_GET_SDTV_RESOLUTION_SUPPORT, + if (!intel_sdvo_write_cmd(intel_sdvo, + SDVO_CMD_GET_SDTV_RESOLUTION_SUPPORT, &tv_res, sizeof(tv_res))) return; if (!intel_sdvo_read_response(intel_sdvo, &reply, 3)) @@ -1662,8 +1661,7 @@ static void intel_sdvo_get_tv_modes(struct drm_connector *connector) static void intel_sdvo_get_lvds_modes(struct drm_connector *connector) { - struct drm_encoder *encoder = intel_attached_encoder(connector); - struct intel_sdvo *intel_sdvo = enc_to_intel_sdvo(encoder); + struct intel_sdvo *intel_sdvo = intel_attached_sdvo(connector); struct drm_i915_private *dev_priv = connector->dev->dev_private; struct drm_display_mode *newmode; @@ -1672,7 +1670,7 @@ static void intel_sdvo_get_lvds_modes(struct drm_connector *connector) * Assume that the preferred modes are * arranged in priority order. */ - intel_ddc_get_modes(connector, intel_sdvo->base.ddc_bus); + intel_ddc_get_modes(connector, intel_sdvo->i2c); if (list_empty(&connector->probed_modes) == false) goto end; @@ -1693,6 +1691,10 @@ end: if (newmode->type & DRM_MODE_TYPE_PREFERRED) { intel_sdvo->sdvo_lvds_fixed_mode = drm_mode_duplicate(connector->dev, newmode); + + drm_mode_set_crtcinfo(intel_sdvo->sdvo_lvds_fixed_mode, + 0); + intel_sdvo->is_lvds = true; break; } @@ -1775,8 +1777,7 @@ intel_sdvo_set_property(struct drm_connector *connector, struct drm_property *property, uint64_t val) { - struct drm_encoder *encoder = intel_attached_encoder(connector); - struct intel_sdvo *intel_sdvo = enc_to_intel_sdvo(encoder); + struct intel_sdvo *intel_sdvo = intel_attached_sdvo(connector); struct intel_sdvo_connector *intel_sdvo_connector = to_intel_sdvo_connector(connector); uint16_t temp_value; uint8_t cmd; @@ -1879,9 +1880,8 @@ set_value: done: - if (encoder->crtc) { - struct drm_crtc *crtc = encoder->crtc; - + if (intel_sdvo->base.base.crtc) { + struct drm_crtc *crtc = intel_sdvo->base.base.crtc; drm_crtc_helper_set_mode(crtc, &crtc->mode, crtc->x, crtc->y, crtc->fb); } @@ -1909,20 +1909,18 @@ static const struct drm_connector_funcs intel_sdvo_connector_funcs = { static const struct drm_connector_helper_funcs intel_sdvo_connector_helper_funcs = { .get_modes = intel_sdvo_get_modes, .mode_valid = intel_sdvo_mode_valid, - .best_encoder = intel_attached_encoder, + .best_encoder = intel_best_encoder, }; static void intel_sdvo_enc_destroy(struct drm_encoder *encoder) { - struct intel_sdvo *intel_sdvo = enc_to_intel_sdvo(encoder); - - if (intel_sdvo->analog_ddc_bus) - intel_i2c_destroy(intel_sdvo->analog_ddc_bus); + struct intel_sdvo *intel_sdvo = to_intel_sdvo(encoder); if (intel_sdvo->sdvo_lvds_fixed_mode != NULL) drm_mode_destroy(encoder->dev, intel_sdvo->sdvo_lvds_fixed_mode); + i2c_del_adapter(&intel_sdvo->ddc); intel_encoder_destroy(encoder); } @@ -1990,54 +1988,39 @@ intel_sdvo_select_ddc_bus(struct drm_i915_private *dev_priv, intel_sdvo_guess_ddc_bus(sdvo); } -static bool -intel_sdvo_get_digital_encoding_mode(struct intel_sdvo *intel_sdvo, int device) +static void +intel_sdvo_select_i2c_bus(struct drm_i915_private *dev_priv, + struct intel_sdvo *sdvo, u32 reg) { - return intel_sdvo_set_target_output(intel_sdvo, - device == 0 ? SDVO_OUTPUT_TMDS0 : SDVO_OUTPUT_TMDS1) && - intel_sdvo_get_value(intel_sdvo, SDVO_CMD_GET_ENCODE, - &intel_sdvo->is_hdmi, 1); -} + struct sdvo_device_mapping *mapping; + u8 pin, speed; -static struct intel_sdvo * -intel_sdvo_chan_to_intel_sdvo(struct intel_i2c_chan *chan) -{ - struct drm_device *dev = chan->drm_dev; - struct drm_encoder *encoder; + if (IS_SDVOB(reg)) + mapping = &dev_priv->sdvo_mappings[0]; + else + mapping = &dev_priv->sdvo_mappings[1]; - list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) { - struct intel_sdvo *intel_sdvo = enc_to_intel_sdvo(encoder); - if (intel_sdvo->base.ddc_bus == &chan->adapter) - return intel_sdvo; + pin = GMBUS_PORT_DPB; + speed = GMBUS_RATE_1MHZ >> 8; + if (mapping->initialized) { + pin = mapping->i2c_pin; + speed = mapping->i2c_speed; } - return NULL; + sdvo->i2c = &dev_priv->gmbus[pin].adapter; + intel_gmbus_set_speed(sdvo->i2c, speed); + intel_gmbus_force_bit(sdvo->i2c, true); } -static int intel_sdvo_master_xfer(struct i2c_adapter *i2c_adap, - struct i2c_msg msgs[], int num) +static bool +intel_sdvo_get_digital_encoding_mode(struct intel_sdvo *intel_sdvo, int device) { - struct intel_sdvo *intel_sdvo; - struct i2c_algo_bit_data *algo_data; - const struct i2c_algorithm *algo; - - algo_data = (struct i2c_algo_bit_data *)i2c_adap->algo_data; - intel_sdvo = - intel_sdvo_chan_to_intel_sdvo((struct intel_i2c_chan *) - (algo_data->data)); - if (intel_sdvo == NULL) - return -EINVAL; - - algo = intel_sdvo->base.i2c_bus->algo; - - intel_sdvo_set_control_bus_switch(intel_sdvo, intel_sdvo->ddc_bus); - return algo->master_xfer(i2c_adap, msgs, num); + return intel_sdvo_set_target_output(intel_sdvo, + device == 0 ? SDVO_OUTPUT_TMDS0 : SDVO_OUTPUT_TMDS1) && + intel_sdvo_get_value(intel_sdvo, SDVO_CMD_GET_ENCODE, + &intel_sdvo->is_hdmi, 1); } -static struct i2c_algorithm intel_sdvo_i2c_bit_algo = { - .master_xfer = intel_sdvo_master_xfer, -}; - static u8 intel_sdvo_get_slave_addr(struct drm_device *dev, int sdvo_reg) { @@ -2076,26 +2059,29 @@ intel_sdvo_get_slave_addr(struct drm_device *dev, int sdvo_reg) } static void -intel_sdvo_connector_init(struct drm_encoder *encoder, - struct drm_connector *connector) +intel_sdvo_connector_init(struct intel_sdvo_connector *connector, + struct intel_sdvo *encoder) { - drm_connector_init(encoder->dev, connector, &intel_sdvo_connector_funcs, - connector->connector_type); + drm_connector_init(encoder->base.base.dev, + &connector->base.base, + &intel_sdvo_connector_funcs, + connector->base.base.connector_type); - drm_connector_helper_add(connector, &intel_sdvo_connector_helper_funcs); + drm_connector_helper_add(&connector->base.base, + &intel_sdvo_connector_helper_funcs); - connector->interlace_allowed = 0; - connector->doublescan_allowed = 0; - connector->display_info.subpixel_order = SubPixelHorizontalRGB; + connector->base.base.interlace_allowed = 0; + connector->base.base.doublescan_allowed = 0; + connector->base.base.display_info.subpixel_order = SubPixelHorizontalRGB; - drm_mode_connector_attach_encoder(connector, encoder); - drm_sysfs_connector_add(connector); + intel_connector_attach_encoder(&connector->base, &encoder->base); + drm_sysfs_connector_add(&connector->base.base); } static bool intel_sdvo_dvi_init(struct intel_sdvo *intel_sdvo, int device) { - struct drm_encoder *encoder = &intel_sdvo->base.enc; + struct drm_encoder *encoder = &intel_sdvo->base.base; struct drm_connector *connector; struct intel_connector *intel_connector; struct intel_sdvo_connector *intel_sdvo_connector; @@ -2130,7 +2116,7 @@ intel_sdvo_dvi_init(struct intel_sdvo *intel_sdvo, int device) intel_sdvo->base.clone_mask = ((1 << INTEL_SDVO_NON_TV_CLONE_BIT) | (1 << INTEL_ANALOG_CLONE_BIT)); - intel_sdvo_connector_init(encoder, connector); + intel_sdvo_connector_init(intel_sdvo_connector, intel_sdvo); return true; } @@ -2138,83 +2124,83 @@ intel_sdvo_dvi_init(struct intel_sdvo *intel_sdvo, int device) static bool intel_sdvo_tv_init(struct intel_sdvo *intel_sdvo, int type) { - struct drm_encoder *encoder = &intel_sdvo->base.enc; - struct drm_connector *connector; - struct intel_connector *intel_connector; - struct intel_sdvo_connector *intel_sdvo_connector; + struct drm_encoder *encoder = &intel_sdvo->base.base; + struct drm_connector *connector; + struct intel_connector *intel_connector; + struct intel_sdvo_connector *intel_sdvo_connector; intel_sdvo_connector = kzalloc(sizeof(struct intel_sdvo_connector), GFP_KERNEL); if (!intel_sdvo_connector) return false; intel_connector = &intel_sdvo_connector->base; - connector = &intel_connector->base; - encoder->encoder_type = DRM_MODE_ENCODER_TVDAC; - connector->connector_type = DRM_MODE_CONNECTOR_SVIDEO; + connector = &intel_connector->base; + encoder->encoder_type = DRM_MODE_ENCODER_TVDAC; + connector->connector_type = DRM_MODE_CONNECTOR_SVIDEO; - intel_sdvo->controlled_output |= type; - intel_sdvo_connector->output_flag = type; + intel_sdvo->controlled_output |= type; + intel_sdvo_connector->output_flag = type; - intel_sdvo->is_tv = true; - intel_sdvo->base.needs_tv_clock = true; - intel_sdvo->base.clone_mask = 1 << INTEL_SDVO_TV_CLONE_BIT; + intel_sdvo->is_tv = true; + intel_sdvo->base.needs_tv_clock = true; + intel_sdvo->base.clone_mask = 1 << INTEL_SDVO_TV_CLONE_BIT; - intel_sdvo_connector_init(encoder, connector); + intel_sdvo_connector_init(intel_sdvo_connector, intel_sdvo); - if (!intel_sdvo_tv_create_property(intel_sdvo, intel_sdvo_connector, type)) + if (!intel_sdvo_tv_create_property(intel_sdvo, intel_sdvo_connector, type)) goto err; - if (!intel_sdvo_create_enhance_property(intel_sdvo, intel_sdvo_connector)) + if (!intel_sdvo_create_enhance_property(intel_sdvo, intel_sdvo_connector)) goto err; - return true; + return true; err: - intel_sdvo_destroy_enhance_property(connector); - kfree(intel_sdvo_connector); + intel_sdvo_destroy(connector); return false; } static bool intel_sdvo_analog_init(struct intel_sdvo *intel_sdvo, int device) { - struct drm_encoder *encoder = &intel_sdvo->base.enc; - struct drm_connector *connector; - struct intel_connector *intel_connector; - struct intel_sdvo_connector *intel_sdvo_connector; + struct drm_encoder *encoder = &intel_sdvo->base.base; + struct drm_connector *connector; + struct intel_connector *intel_connector; + struct intel_sdvo_connector *intel_sdvo_connector; intel_sdvo_connector = kzalloc(sizeof(struct intel_sdvo_connector), GFP_KERNEL); if (!intel_sdvo_connector) return false; intel_connector = &intel_sdvo_connector->base; - connector = &intel_connector->base; + connector = &intel_connector->base; connector->polled = DRM_CONNECTOR_POLL_CONNECT; - encoder->encoder_type = DRM_MODE_ENCODER_DAC; - connector->connector_type = DRM_MODE_CONNECTOR_VGA; - - if (device == 0) { - intel_sdvo->controlled_output |= SDVO_OUTPUT_RGB0; - intel_sdvo_connector->output_flag = SDVO_OUTPUT_RGB0; - } else if (device == 1) { - intel_sdvo->controlled_output |= SDVO_OUTPUT_RGB1; - intel_sdvo_connector->output_flag = SDVO_OUTPUT_RGB1; - } - - intel_sdvo->base.clone_mask = ((1 << INTEL_SDVO_NON_TV_CLONE_BIT) | + encoder->encoder_type = DRM_MODE_ENCODER_DAC; + connector->connector_type = DRM_MODE_CONNECTOR_VGA; + + if (device == 0) { + intel_sdvo->controlled_output |= SDVO_OUTPUT_RGB0; + intel_sdvo_connector->output_flag = SDVO_OUTPUT_RGB0; + } else if (device == 1) { + intel_sdvo->controlled_output |= SDVO_OUTPUT_RGB1; + intel_sdvo_connector->output_flag = SDVO_OUTPUT_RGB1; + } + + intel_sdvo->base.clone_mask = ((1 << INTEL_SDVO_NON_TV_CLONE_BIT) | (1 << INTEL_ANALOG_CLONE_BIT)); - intel_sdvo_connector_init(encoder, connector); - return true; + intel_sdvo_connector_init(intel_sdvo_connector, + intel_sdvo); + return true; } static bool intel_sdvo_lvds_init(struct intel_sdvo *intel_sdvo, int device) { - struct drm_encoder *encoder = &intel_sdvo->base.enc; - struct drm_connector *connector; - struct intel_connector *intel_connector; - struct intel_sdvo_connector *intel_sdvo_connector; + struct drm_encoder *encoder = &intel_sdvo->base.base; + struct drm_connector *connector; + struct intel_connector *intel_connector; + struct intel_sdvo_connector *intel_sdvo_connector; intel_sdvo_connector = kzalloc(sizeof(struct intel_sdvo_connector), GFP_KERNEL); if (!intel_sdvo_connector) @@ -2222,29 +2208,28 @@ intel_sdvo_lvds_init(struct intel_sdvo *intel_sdvo, int device) intel_connector = &intel_sdvo_connector->base; connector = &intel_connector->base; - encoder->encoder_type = DRM_MODE_ENCODER_LVDS; - connector->connector_type = DRM_MODE_CONNECTOR_LVDS; - - if (device == 0) { - intel_sdvo->controlled_output |= SDVO_OUTPUT_LVDS0; - intel_sdvo_connector->output_flag = SDVO_OUTPUT_LVDS0; - } else if (device == 1) { - intel_sdvo->controlled_output |= SDVO_OUTPUT_LVDS1; - intel_sdvo_connector->output_flag = SDVO_OUTPUT_LVDS1; - } - - intel_sdvo->base.clone_mask = ((1 << INTEL_ANALOG_CLONE_BIT) | + encoder->encoder_type = DRM_MODE_ENCODER_LVDS; + connector->connector_type = DRM_MODE_CONNECTOR_LVDS; + + if (device == 0) { + intel_sdvo->controlled_output |= SDVO_OUTPUT_LVDS0; + intel_sdvo_connector->output_flag = SDVO_OUTPUT_LVDS0; + } else if (device == 1) { + intel_sdvo->controlled_output |= SDVO_OUTPUT_LVDS1; + intel_sdvo_connector->output_flag = SDVO_OUTPUT_LVDS1; + } + + intel_sdvo->base.clone_mask = ((1 << INTEL_ANALOG_CLONE_BIT) | (1 << INTEL_SDVO_LVDS_CLONE_BIT)); - intel_sdvo_connector_init(encoder, connector); - if (!intel_sdvo_create_enhance_property(intel_sdvo, intel_sdvo_connector)) + intel_sdvo_connector_init(intel_sdvo_connector, intel_sdvo); + if (!intel_sdvo_create_enhance_property(intel_sdvo, intel_sdvo_connector)) goto err; return true; err: - intel_sdvo_destroy_enhance_property(connector); - kfree(intel_sdvo_connector); + intel_sdvo_destroy(connector); return false; } @@ -2309,7 +2294,7 @@ static bool intel_sdvo_tv_create_property(struct intel_sdvo *intel_sdvo, struct intel_sdvo_connector *intel_sdvo_connector, int type) { - struct drm_device *dev = intel_sdvo->base.enc.dev; + struct drm_device *dev = intel_sdvo->base.base.dev; struct intel_sdvo_tv_format format; uint32_t format_map, i; @@ -2375,7 +2360,7 @@ intel_sdvo_create_enhance_property_tv(struct intel_sdvo *intel_sdvo, struct intel_sdvo_connector *intel_sdvo_connector, struct intel_sdvo_enhancements_reply enhancements) { - struct drm_device *dev = intel_sdvo->base.enc.dev; + struct drm_device *dev = intel_sdvo->base.base.dev; struct drm_connector *connector = &intel_sdvo_connector->base.base; uint16_t response, data_value[2]; @@ -2504,7 +2489,7 @@ intel_sdvo_create_enhance_property_lvds(struct intel_sdvo *intel_sdvo, struct intel_sdvo_connector *intel_sdvo_connector, struct intel_sdvo_enhancements_reply enhancements) { - struct drm_device *dev = intel_sdvo->base.enc.dev; + struct drm_device *dev = intel_sdvo->base.base.dev; struct drm_connector *connector = &intel_sdvo_connector->base.base; uint16_t response, data_value[2]; @@ -2522,11 +2507,10 @@ static bool intel_sdvo_create_enhance_property(struct intel_sdvo *intel_sdvo, uint16_t response; } enhancements; - if (!intel_sdvo_get_value(intel_sdvo, - SDVO_CMD_GET_SUPPORTED_ENHANCEMENTS, - &enhancements, sizeof(enhancements))) - return false; - + enhancements.response = 0; + intel_sdvo_get_value(intel_sdvo, + SDVO_CMD_GET_SUPPORTED_ENHANCEMENTS, + &enhancements, sizeof(enhancements)); if (enhancements.response == 0) { DRM_DEBUG_KMS("No enhancement is supported\n"); return true; @@ -2538,7 +2522,43 @@ static bool intel_sdvo_create_enhance_property(struct intel_sdvo *intel_sdvo, return intel_sdvo_create_enhance_property_lvds(intel_sdvo, intel_sdvo_connector, enhancements.reply); else return true; +} + +static int intel_sdvo_ddc_proxy_xfer(struct i2c_adapter *adapter, + struct i2c_msg *msgs, + int num) +{ + struct intel_sdvo *sdvo = adapter->algo_data; + + if (!intel_sdvo_set_control_bus_switch(sdvo, sdvo->ddc_bus)) + return -EIO; + + return sdvo->i2c->algo->master_xfer(sdvo->i2c, msgs, num); +} +static u32 intel_sdvo_ddc_proxy_func(struct i2c_adapter *adapter) +{ + struct intel_sdvo *sdvo = adapter->algo_data; + return sdvo->i2c->algo->functionality(sdvo->i2c); +} + +static const struct i2c_algorithm intel_sdvo_ddc_proxy = { + .master_xfer = intel_sdvo_ddc_proxy_xfer, + .functionality = intel_sdvo_ddc_proxy_func +}; + +static bool +intel_sdvo_init_ddc_proxy(struct intel_sdvo *sdvo, + struct drm_device *dev) +{ + sdvo->ddc.owner = THIS_MODULE; + sdvo->ddc.class = I2C_CLASS_DDC; + snprintf(sdvo->ddc.name, I2C_NAME_SIZE, "SDVO DDC proxy"); + sdvo->ddc.dev.parent = &dev->pdev->dev; + sdvo->ddc.algo_data = sdvo; + sdvo->ddc.algo = &intel_sdvo_ddc_proxy; + + return i2c_add_adapter(&sdvo->ddc) == 0; } bool intel_sdvo_init(struct drm_device *dev, int sdvo_reg) @@ -2546,95 +2566,66 @@ bool intel_sdvo_init(struct drm_device *dev, int sdvo_reg) struct drm_i915_private *dev_priv = dev->dev_private; struct intel_encoder *intel_encoder; struct intel_sdvo *intel_sdvo; - u8 ch[0x40]; int i; - u32 i2c_reg, ddc_reg, analog_ddc_reg; intel_sdvo = kzalloc(sizeof(struct intel_sdvo), GFP_KERNEL); if (!intel_sdvo) return false; + if (!intel_sdvo_init_ddc_proxy(intel_sdvo, dev)) { + kfree(intel_sdvo); + return false; + } + intel_sdvo->sdvo_reg = sdvo_reg; intel_encoder = &intel_sdvo->base; intel_encoder->type = INTEL_OUTPUT_SDVO; + /* encoder type will be decided later */ + drm_encoder_init(dev, &intel_encoder->base, &intel_sdvo_enc_funcs, 0); - if (HAS_PCH_SPLIT(dev)) { - i2c_reg = PCH_GPIOE; - ddc_reg = PCH_GPIOE; - analog_ddc_reg = PCH_GPIOA; - } else { - i2c_reg = GPIOE; - ddc_reg = GPIOE; - analog_ddc_reg = GPIOA; - } - - /* setup the DDC bus. */ - if (IS_SDVOB(sdvo_reg)) - intel_encoder->i2c_bus = intel_i2c_create(dev, i2c_reg, "SDVOCTRL_E for SDVOB"); - else - intel_encoder->i2c_bus = intel_i2c_create(dev, i2c_reg, "SDVOCTRL_E for SDVOC"); - - if (!intel_encoder->i2c_bus) - goto err_inteloutput; - - intel_sdvo->slave_addr = intel_sdvo_get_slave_addr(dev, sdvo_reg); - - /* Save the bit-banging i2c functionality for use by the DDC wrapper */ - intel_sdvo_i2c_bit_algo.functionality = intel_encoder->i2c_bus->algo->functionality; + intel_sdvo->slave_addr = intel_sdvo_get_slave_addr(dev, sdvo_reg) >> 1; + intel_sdvo_select_i2c_bus(dev_priv, intel_sdvo, sdvo_reg); /* Read the regs to test if we can talk to the device */ for (i = 0; i < 0x40; i++) { - if (!intel_sdvo_read_byte(intel_sdvo, i, &ch[i])) { + u8 byte; + + if (!intel_sdvo_read_byte(intel_sdvo, i, &byte)) { DRM_DEBUG_KMS("No SDVO device found on SDVO%c\n", IS_SDVOB(sdvo_reg) ? 'B' : 'C'); - goto err_i2c; + goto err; } } - /* setup the DDC bus. */ - if (IS_SDVOB(sdvo_reg)) { - intel_encoder->ddc_bus = intel_i2c_create(dev, ddc_reg, "SDVOB DDC BUS"); - intel_sdvo->analog_ddc_bus = intel_i2c_create(dev, analog_ddc_reg, - "SDVOB/VGA DDC BUS"); + if (IS_SDVOB(sdvo_reg)) dev_priv->hotplug_supported_mask |= SDVOB_HOTPLUG_INT_STATUS; - } else { - intel_encoder->ddc_bus = intel_i2c_create(dev, ddc_reg, "SDVOC DDC BUS"); - intel_sdvo->analog_ddc_bus = intel_i2c_create(dev, analog_ddc_reg, - "SDVOC/VGA DDC BUS"); + else dev_priv->hotplug_supported_mask |= SDVOC_HOTPLUG_INT_STATUS; - } - if (intel_encoder->ddc_bus == NULL || intel_sdvo->analog_ddc_bus == NULL) - goto err_i2c; - - /* Wrap with our custom algo which switches to DDC mode */ - intel_encoder->ddc_bus->algo = &intel_sdvo_i2c_bit_algo; - /* encoder type will be decided later */ - drm_encoder_init(dev, &intel_encoder->enc, &intel_sdvo_enc_funcs, 0); - drm_encoder_helper_add(&intel_encoder->enc, &intel_sdvo_helper_funcs); + drm_encoder_helper_add(&intel_encoder->base, &intel_sdvo_helper_funcs); /* In default case sdvo lvds is false */ if (!intel_sdvo_get_capabilities(intel_sdvo, &intel_sdvo->caps)) - goto err_enc; + goto err; if (intel_sdvo_output_setup(intel_sdvo, intel_sdvo->caps.output_flags) != true) { DRM_DEBUG_KMS("SDVO output failed to setup on SDVO%c\n", IS_SDVOB(sdvo_reg) ? 'B' : 'C'); - goto err_enc; + goto err; } intel_sdvo_select_ddc_bus(dev_priv, intel_sdvo, sdvo_reg); /* Set the input timing to the screen. Assume always input 0. */ if (!intel_sdvo_set_target_input(intel_sdvo)) - goto err_enc; + goto err; if (!intel_sdvo_get_input_pixel_clock_range(intel_sdvo, &intel_sdvo->pixel_clock_min, &intel_sdvo->pixel_clock_max)) - goto err_enc; + goto err; DRM_DEBUG_KMS("%s device VID/DID: %02X:%02X.%02X, " "clock range %dMHz - %dMHz, " @@ -2654,16 +2645,9 @@ bool intel_sdvo_init(struct drm_device *dev, int sdvo_reg) (SDVO_OUTPUT_TMDS1 | SDVO_OUTPUT_RGB1) ? 'Y' : 'N'); return true; -err_enc: - drm_encoder_cleanup(&intel_encoder->enc); -err_i2c: - if (intel_sdvo->analog_ddc_bus != NULL) - intel_i2c_destroy(intel_sdvo->analog_ddc_bus); - if (intel_encoder->ddc_bus != NULL) - intel_i2c_destroy(intel_encoder->ddc_bus); - if (intel_encoder->i2c_bus != NULL) - intel_i2c_destroy(intel_encoder->i2c_bus); -err_inteloutput: +err: + drm_encoder_cleanup(&intel_encoder->base); + i2c_del_adapter(&intel_sdvo->ddc); kfree(intel_sdvo); return false; diff --git a/drivers/gpu/drm/i915/intel_tv.c b/drivers/gpu/drm/i915/intel_tv.c index 4a117e318a7..2f768198931 100644 --- a/drivers/gpu/drm/i915/intel_tv.c +++ b/drivers/gpu/drm/i915/intel_tv.c @@ -48,7 +48,7 @@ struct intel_tv { struct intel_encoder base; int type; - char *tv_format; + const char *tv_format; int margin[4]; u32 save_TV_H_CTL_1; u32 save_TV_H_CTL_2; @@ -350,7 +350,7 @@ static const struct video_levels component_levels = { struct tv_mode { - char *name; + const char *name; int clock; int refresh; /* in millihertz (for precision) */ u32 oversample; @@ -900,7 +900,14 @@ static const struct tv_mode tv_modes[] = { static struct intel_tv *enc_to_intel_tv(struct drm_encoder *encoder) { - return container_of(enc_to_intel_encoder(encoder), struct intel_tv, base); + return container_of(encoder, struct intel_tv, base.base); +} + +static struct intel_tv *intel_attached_tv(struct drm_connector *connector) +{ + return container_of(intel_attached_encoder(connector), + struct intel_tv, + base); } static void @@ -922,7 +929,7 @@ intel_tv_dpms(struct drm_encoder *encoder, int mode) } static const struct tv_mode * -intel_tv_mode_lookup (char *tv_format) +intel_tv_mode_lookup(const char *tv_format) { int i; @@ -936,22 +943,23 @@ intel_tv_mode_lookup (char *tv_format) } static const struct tv_mode * -intel_tv_mode_find (struct intel_tv *intel_tv) +intel_tv_mode_find(struct intel_tv *intel_tv) { return intel_tv_mode_lookup(intel_tv->tv_format); } static enum drm_mode_status -intel_tv_mode_valid(struct drm_connector *connector, struct drm_display_mode *mode) +intel_tv_mode_valid(struct drm_connector *connector, + struct drm_display_mode *mode) { - struct drm_encoder *encoder = intel_attached_encoder(connector); - struct intel_tv *intel_tv = enc_to_intel_tv(encoder); + struct intel_tv *intel_tv = intel_attached_tv(connector); const struct tv_mode *tv_mode = intel_tv_mode_find(intel_tv); /* Ensure TV refresh is close to desired refresh */ if (tv_mode && abs(tv_mode->refresh - drm_mode_vrefresh(mode) * 1000) < 1000) return MODE_OK; + return MODE_CLOCK_RANGE; } @@ -1131,7 +1139,7 @@ intel_tv_mode_set(struct drm_encoder *encoder, struct drm_display_mode *mode, color_conversion->av); } - if (IS_I965G(dev)) + if (INTEL_INFO(dev)->gen >= 4) I915_WRITE(TV_CLR_KNOBS, 0x00404000); else I915_WRITE(TV_CLR_KNOBS, 0x00606000); @@ -1157,12 +1165,12 @@ intel_tv_mode_set(struct drm_encoder *encoder, struct drm_display_mode *mode, I915_WRITE(dspbase_reg, I915_READ(dspbase_reg)); /* Wait for vblank for the disable to take effect */ - if (!IS_I9XX(dev)) + if (IS_GEN2(dev)) intel_wait_for_vblank(dev, intel_crtc->pipe); - I915_WRITE(pipeconf_reg, pipeconf & ~PIPEACONF_ENABLE); + I915_WRITE(pipeconf_reg, pipeconf & ~PIPECONF_ENABLE); /* Wait for vblank for the disable to take effect. */ - intel_wait_for_vblank(dev, intel_crtc->pipe); + intel_wait_for_pipe_off(dev, intel_crtc->pipe); /* Filter ctl must be set before TV_WIN_SIZE */ I915_WRITE(TV_FILTER_CTL_1, TV_AUTO_SCALE); @@ -1196,7 +1204,7 @@ intel_tv_mode_set(struct drm_encoder *encoder, struct drm_display_mode *mode, I915_WRITE(TV_V_LUMA_0 + (i<<2), tv_mode->filter_table[j++]); for (i = 0; i < 43; i++) I915_WRITE(TV_V_CHROMA_0 + (i<<2), tv_mode->filter_table[j++]); - I915_WRITE(TV_DAC, 0); + I915_WRITE(TV_DAC, I915_READ(TV_DAC) & TV_DAC_SAVE); I915_WRITE(TV_CTL, tv_ctl); } @@ -1228,15 +1236,13 @@ static const struct drm_display_mode reported_modes[] = { static int intel_tv_detect_type (struct intel_tv *intel_tv) { - struct drm_encoder *encoder = &intel_tv->base.enc; + struct drm_encoder *encoder = &intel_tv->base.base; struct drm_device *dev = encoder->dev; struct drm_i915_private *dev_priv = dev->dev_private; unsigned long irqflags; u32 tv_ctl, save_tv_ctl; u32 tv_dac, save_tv_dac; - int type = DRM_MODE_CONNECTOR_Unknown; - - tv_dac = I915_READ(TV_DAC); + int type; /* Disable TV interrupts around load detect or we'll recurse */ spin_lock_irqsave(&dev_priv->user_irq_lock, irqflags); @@ -1244,19 +1250,14 @@ intel_tv_detect_type (struct intel_tv *intel_tv) PIPE_HOTPLUG_TV_INTERRUPT_ENABLE); spin_unlock_irqrestore(&dev_priv->user_irq_lock, irqflags); - /* - * Detect TV by polling) - */ - save_tv_dac = tv_dac; - tv_ctl = I915_READ(TV_CTL); - save_tv_ctl = tv_ctl; - tv_ctl &= ~TV_ENC_ENABLE; - tv_ctl &= ~TV_TEST_MODE_MASK; + save_tv_dac = tv_dac = I915_READ(TV_DAC); + save_tv_ctl = tv_ctl = I915_READ(TV_CTL); + + /* Poll for TV detection */ + tv_ctl &= ~(TV_ENC_ENABLE | TV_TEST_MODE_MASK); tv_ctl |= TV_TEST_MODE_MONITOR_DETECT; - tv_dac &= ~TVDAC_SENSE_MASK; - tv_dac &= ~DAC_A_MASK; - tv_dac &= ~DAC_B_MASK; - tv_dac &= ~DAC_C_MASK; + + tv_dac &= ~(TVDAC_SENSE_MASK | DAC_A_MASK | DAC_B_MASK | DAC_C_MASK); tv_dac |= (TVDAC_STATE_CHG_EN | TVDAC_A_SENSE_CTL | TVDAC_B_SENSE_CTL | @@ -1265,37 +1266,40 @@ intel_tv_detect_type (struct intel_tv *intel_tv) DAC_A_0_7_V | DAC_B_0_7_V | DAC_C_0_7_V); + I915_WRITE(TV_CTL, tv_ctl); I915_WRITE(TV_DAC, tv_dac); POSTING_READ(TV_DAC); - msleep(20); - tv_dac = I915_READ(TV_DAC); - I915_WRITE(TV_DAC, save_tv_dac); - I915_WRITE(TV_CTL, save_tv_ctl); - POSTING_READ(TV_CTL); - msleep(20); + intel_wait_for_vblank(intel_tv->base.base.dev, + to_intel_crtc(intel_tv->base.base.crtc)->pipe); - /* - * A B C - * 0 1 1 Composite - * 1 0 X svideo - * 0 0 0 Component - */ - if ((tv_dac & TVDAC_SENSE_MASK) == (TVDAC_B_SENSE | TVDAC_C_SENSE)) { - DRM_DEBUG_KMS("Detected Composite TV connection\n"); - type = DRM_MODE_CONNECTOR_Composite; - } else if ((tv_dac & (TVDAC_A_SENSE|TVDAC_B_SENSE)) == TVDAC_A_SENSE) { - DRM_DEBUG_KMS("Detected S-Video TV connection\n"); - type = DRM_MODE_CONNECTOR_SVIDEO; - } else if ((tv_dac & TVDAC_SENSE_MASK) == 0) { - DRM_DEBUG_KMS("Detected Component TV connection\n"); - type = DRM_MODE_CONNECTOR_Component; - } else { - DRM_DEBUG_KMS("No TV connection detected\n"); - type = -1; + type = -1; + if (wait_for((tv_dac = I915_READ(TV_DAC)) & TVDAC_STATE_CHG, 20) == 0) { + DRM_DEBUG_KMS("TV detected: %x, %x\n", tv_ctl, tv_dac); + /* + * A B C + * 0 1 1 Composite + * 1 0 X svideo + * 0 0 0 Component + */ + if ((tv_dac & TVDAC_SENSE_MASK) == (TVDAC_B_SENSE | TVDAC_C_SENSE)) { + DRM_DEBUG_KMS("Detected Composite TV connection\n"); + type = DRM_MODE_CONNECTOR_Composite; + } else if ((tv_dac & (TVDAC_A_SENSE|TVDAC_B_SENSE)) == TVDAC_A_SENSE) { + DRM_DEBUG_KMS("Detected S-Video TV connection\n"); + type = DRM_MODE_CONNECTOR_SVIDEO; + } else if ((tv_dac & TVDAC_SENSE_MASK) == 0) { + DRM_DEBUG_KMS("Detected Component TV connection\n"); + type = DRM_MODE_CONNECTOR_Component; + } else { + DRM_DEBUG_KMS("Unrecognised TV connection\n"); + } } + I915_WRITE(TV_DAC, save_tv_dac & ~TVDAC_STATE_CHG_EN); + I915_WRITE(TV_CTL, save_tv_ctl); + /* Restore interrupt config */ spin_lock_irqsave(&dev_priv->user_irq_lock, irqflags); i915_enable_pipestat(dev_priv, 0, PIPE_HOTPLUG_INTERRUPT_ENABLE | @@ -1311,8 +1315,7 @@ intel_tv_detect_type (struct intel_tv *intel_tv) */ static void intel_tv_find_better_format(struct drm_connector *connector) { - struct drm_encoder *encoder = intel_attached_encoder(connector); - struct intel_tv *intel_tv = enc_to_intel_tv(encoder); + struct intel_tv *intel_tv = intel_attached_tv(connector); const struct tv_mode *tv_mode = intel_tv_mode_find(intel_tv); int i; @@ -1344,14 +1347,13 @@ static enum drm_connector_status intel_tv_detect(struct drm_connector *connector, bool force) { struct drm_display_mode mode; - struct drm_encoder *encoder = intel_attached_encoder(connector); - struct intel_tv *intel_tv = enc_to_intel_tv(encoder); + struct intel_tv *intel_tv = intel_attached_tv(connector); int type; mode = reported_modes[0]; drm_mode_set_crtcinfo(&mode, CRTC_INTERLACE_HALVE_V); - if (encoder->crtc && encoder->crtc->enabled) { + if (intel_tv->base.base.crtc && intel_tv->base.base.crtc->enabled) { type = intel_tv_detect_type(intel_tv); } else if (force) { struct drm_crtc *crtc; @@ -1375,11 +1377,10 @@ intel_tv_detect(struct drm_connector *connector, bool force) return connector_status_connected; } -static struct input_res { - char *name; +static const struct input_res { + const char *name; int w, h; -} input_res_table[] = -{ +} input_res_table[] = { {"640x480", 640, 480}, {"800x600", 800, 600}, {"1024x768", 1024, 768}, @@ -1396,8 +1397,7 @@ static void intel_tv_chose_preferred_modes(struct drm_connector *connector, struct drm_display_mode *mode_ptr) { - struct drm_encoder *encoder = intel_attached_encoder(connector); - struct intel_tv *intel_tv = enc_to_intel_tv(encoder); + struct intel_tv *intel_tv = intel_attached_tv(connector); const struct tv_mode *tv_mode = intel_tv_mode_find(intel_tv); if (tv_mode->nbr_end < 480 && mode_ptr->vdisplay == 480) @@ -1422,15 +1422,14 @@ static int intel_tv_get_modes(struct drm_connector *connector) { struct drm_display_mode *mode_ptr; - struct drm_encoder *encoder = intel_attached_encoder(connector); - struct intel_tv *intel_tv = enc_to_intel_tv(encoder); + struct intel_tv *intel_tv = intel_attached_tv(connector); const struct tv_mode *tv_mode = intel_tv_mode_find(intel_tv); int j, count = 0; u64 tmp; for (j = 0; j < ARRAY_SIZE(input_res_table); j++) { - struct input_res *input = &input_res_table[j]; + const struct input_res *input = &input_res_table[j]; unsigned int hactive_s = input->w; unsigned int vactive_s = input->h; @@ -1488,9 +1487,8 @@ intel_tv_set_property(struct drm_connector *connector, struct drm_property *prop uint64_t val) { struct drm_device *dev = connector->dev; - struct drm_encoder *encoder = intel_attached_encoder(connector); - struct intel_tv *intel_tv = enc_to_intel_tv(encoder); - struct drm_crtc *crtc = encoder->crtc; + struct intel_tv *intel_tv = intel_attached_tv(connector); + struct drm_crtc *crtc = intel_tv->base.base.crtc; int ret = 0; bool changed = false; @@ -1555,7 +1553,7 @@ static const struct drm_connector_funcs intel_tv_connector_funcs = { static const struct drm_connector_helper_funcs intel_tv_connector_helper_funcs = { .mode_valid = intel_tv_mode_valid, .get_modes = intel_tv_get_modes, - .best_encoder = intel_attached_encoder, + .best_encoder = intel_best_encoder, }; static const struct drm_encoder_funcs intel_tv_enc_funcs = { @@ -1607,7 +1605,7 @@ intel_tv_init(struct drm_device *dev) struct intel_encoder *intel_encoder; struct intel_connector *intel_connector; u32 tv_dac_on, tv_dac_off, save_tv_dac; - char **tv_format_names; + char *tv_format_names[ARRAY_SIZE(tv_modes)]; int i, initial_mode = 0; if ((I915_READ(TV_CTL) & TV_FUSE_STATE_MASK) == TV_FUSE_STATE_DISABLED) @@ -1661,15 +1659,15 @@ intel_tv_init(struct drm_device *dev) drm_connector_init(dev, connector, &intel_tv_connector_funcs, DRM_MODE_CONNECTOR_SVIDEO); - drm_encoder_init(dev, &intel_encoder->enc, &intel_tv_enc_funcs, + drm_encoder_init(dev, &intel_encoder->base, &intel_tv_enc_funcs, DRM_MODE_ENCODER_TVDAC); - drm_mode_connector_attach_encoder(&intel_connector->base, &intel_encoder->enc); + intel_connector_attach_encoder(intel_connector, intel_encoder); intel_encoder->type = INTEL_OUTPUT_TVOUT; intel_encoder->crtc_mask = (1 << 0) | (1 << 1); intel_encoder->clone_mask = (1 << INTEL_TV_CLONE_BIT); - intel_encoder->enc.possible_crtcs = ((1 << 0) | (1 << 1)); - intel_encoder->enc.possible_clones = (1 << INTEL_OUTPUT_TVOUT); + intel_encoder->base.possible_crtcs = ((1 << 0) | (1 << 1)); + intel_encoder->base.possible_clones = (1 << INTEL_OUTPUT_TVOUT); intel_tv->type = DRM_MODE_CONNECTOR_Unknown; /* BIOS margin values */ @@ -1678,21 +1676,19 @@ intel_tv_init(struct drm_device *dev) intel_tv->margin[TV_MARGIN_RIGHT] = 46; intel_tv->margin[TV_MARGIN_BOTTOM] = 37; - intel_tv->tv_format = kstrdup(tv_modes[initial_mode].name, GFP_KERNEL); + intel_tv->tv_format = tv_modes[initial_mode].name; - drm_encoder_helper_add(&intel_encoder->enc, &intel_tv_helper_funcs); + drm_encoder_helper_add(&intel_encoder->base, &intel_tv_helper_funcs); drm_connector_helper_add(connector, &intel_tv_connector_helper_funcs); connector->interlace_allowed = false; connector->doublescan_allowed = false; /* Create TV properties then attach current values */ - tv_format_names = kmalloc(sizeof(char *) * ARRAY_SIZE(tv_modes), - GFP_KERNEL); - if (!tv_format_names) - goto out; for (i = 0; i < ARRAY_SIZE(tv_modes); i++) - tv_format_names[i] = tv_modes[i].name; - drm_mode_create_tv_properties(dev, ARRAY_SIZE(tv_modes), tv_format_names); + tv_format_names[i] = (char *)tv_modes[i].name; + drm_mode_create_tv_properties(dev, + ARRAY_SIZE(tv_modes), + tv_format_names); drm_connector_attach_property(connector, dev->mode_config.tv_mode_property, initial_mode); @@ -1708,6 +1704,5 @@ intel_tv_init(struct drm_device *dev) drm_connector_attach_property(connector, dev->mode_config.tv_bottom_margin_property, intel_tv->margin[TV_MARGIN_BOTTOM]); -out: drm_sysfs_connector_add(connector); } diff --git a/drivers/gpu/drm/nouveau/nouveau_fbcon.c b/drivers/gpu/drm/nouveau/nouveau_fbcon.c index dbd30b2e43f..d2047713dc5 100644 --- a/drivers/gpu/drm/nouveau/nouveau_fbcon.c +++ b/drivers/gpu/drm/nouveau/nouveau_fbcon.c @@ -352,6 +352,7 @@ nouveau_fbcon_destroy(struct drm_device *dev, struct nouveau_fbdev *nfbdev) if (nouveau_fb->nvbo) { nouveau_bo_unmap(nouveau_fb->nvbo); + drm_gem_object_handle_unreference_unlocked(nouveau_fb->nvbo->gem); drm_gem_object_unreference_unlocked(nouveau_fb->nvbo->gem); nouveau_fb->nvbo = NULL; } diff --git a/drivers/gpu/drm/nouveau/nouveau_gem.c b/drivers/gpu/drm/nouveau/nouveau_gem.c index 1e630987543..5c4c929d7f7 100644 --- a/drivers/gpu/drm/nouveau/nouveau_gem.c +++ b/drivers/gpu/drm/nouveau/nouveau_gem.c @@ -167,11 +167,9 @@ nouveau_gem_ioctl_new(struct drm_device *dev, void *data, goto out; ret = drm_gem_handle_create(file_priv, nvbo->gem, &req->info.handle); + /* drop reference from allocate - handle holds it now */ + drm_gem_object_unreference_unlocked(nvbo->gem); out: - drm_gem_object_handle_unreference_unlocked(nvbo->gem); - - if (ret) - drm_gem_object_unreference_unlocked(nvbo->gem); return ret; } diff --git a/drivers/gpu/drm/nouveau/nouveau_notifier.c b/drivers/gpu/drm/nouveau/nouveau_notifier.c index 2cc59f8c658..d670839cb34 100644 --- a/drivers/gpu/drm/nouveau/nouveau_notifier.c +++ b/drivers/gpu/drm/nouveau/nouveau_notifier.c @@ -80,6 +80,7 @@ nouveau_notifier_takedown_channel(struct nouveau_channel *chan) mutex_lock(&dev->struct_mutex); nouveau_bo_unpin(chan->notifier_bo); mutex_unlock(&dev->struct_mutex); + drm_gem_object_handle_unreference_unlocked(chan->notifier_bo->gem); drm_gem_object_unreference_unlocked(chan->notifier_bo->gem); drm_mm_takedown(&chan->notifier_heap); } diff --git a/drivers/gpu/drm/radeon/atombios.h b/drivers/gpu/drm/radeon/atombios.h index 1bc72c3190a..fe359a239df 100644 --- a/drivers/gpu/drm/radeon/atombios.h +++ b/drivers/gpu/drm/radeon/atombios.h @@ -4999,7 +4999,7 @@ typedef struct _SW_I2C_IO_DATA_PARAMETERS #define SW_I2C_CNTL_WRITE1BIT 6 //==============================VESA definition Portion=============================== -#define VESA_OEM_PRODUCT_REV '01.00' +#define VESA_OEM_PRODUCT_REV "01.00" #define VESA_MODE_ATTRIBUTE_MODE_SUPPORT 0xBB //refer to VBE spec p.32, no TTY support #define VESA_MODE_WIN_ATTRIBUTE 7 #define VESA_WIN_SIZE 64 diff --git a/drivers/gpu/drm/radeon/r600.c b/drivers/gpu/drm/radeon/r600.c index afc18d87fdc..7a04959ba0e 100644 --- a/drivers/gpu/drm/radeon/r600.c +++ b/drivers/gpu/drm/radeon/r600.c @@ -2729,7 +2729,7 @@ int r600_ib_test(struct radeon_device *rdev) if (i < rdev->usec_timeout) { DRM_INFO("ib test succeeded in %u usecs\n", i); } else { - DRM_ERROR("radeon: ib test failed (sracth(0x%04X)=0x%08X)\n", + DRM_ERROR("radeon: ib test failed (scratch(0x%04X)=0x%08X)\n", scratch, tmp); r = -EINVAL; } @@ -3528,7 +3528,8 @@ void r600_ioctl_wait_idle(struct radeon_device *rdev, struct radeon_bo *bo) /* r7xx hw bug. write to HDP_DEBUG1 followed by fb read * rather than write to HDP_REG_COHERENCY_FLUSH_CNTL */ - if ((rdev->family >= CHIP_RV770) && (rdev->family <= CHIP_RV740)) { + if ((rdev->family >= CHIP_RV770) && (rdev->family <= CHIP_RV740) && + rdev->vram_scratch.ptr) { void __iomem *ptr = (void *)rdev->vram_scratch.ptr; u32 tmp; diff --git a/drivers/gpu/drm/radeon/radeon_atombios.c b/drivers/gpu/drm/radeon/radeon_atombios.c index ebae14c4b76..68932ba7b8a 100644 --- a/drivers/gpu/drm/radeon/radeon_atombios.c +++ b/drivers/gpu/drm/radeon/radeon_atombios.c @@ -317,6 +317,15 @@ static bool radeon_atom_apply_quirks(struct drm_device *dev, *connector_type = DRM_MODE_CONNECTOR_DVID; } + /* MSI K9A2GM V2/V3 board has no HDMI or DVI */ + if ((dev->pdev->device == 0x796e) && + (dev->pdev->subsystem_vendor == 0x1462) && + (dev->pdev->subsystem_device == 0x7302)) { + if ((supported_device == ATOM_DEVICE_DFP2_SUPPORT) || + (supported_device == ATOM_DEVICE_DFP3_SUPPORT)) + return false; + } + /* a-bit f-i90hd - ciaranm on #radeonhd - this board has no DVI */ if ((dev->pdev->device == 0x7941) && (dev->pdev->subsystem_vendor == 0x147b) && diff --git a/drivers/gpu/drm/radeon/radeon_display.c b/drivers/gpu/drm/radeon/radeon_display.c index 127a395f70f..b92d2f2fcbe 100644 --- a/drivers/gpu/drm/radeon/radeon_display.c +++ b/drivers/gpu/drm/radeon/radeon_display.c @@ -349,6 +349,8 @@ static void radeon_print_display_setup(struct drm_device *dev) DRM_INFO(" DFP4: %s\n", encoder_names[radeon_encoder->encoder_id]); if (devices & ATOM_DEVICE_DFP5_SUPPORT) DRM_INFO(" DFP5: %s\n", encoder_names[radeon_encoder->encoder_id]); + if (devices & ATOM_DEVICE_DFP6_SUPPORT) + DRM_INFO(" DFP6: %s\n", encoder_names[radeon_encoder->encoder_id]); if (devices & ATOM_DEVICE_TV1_SUPPORT) DRM_INFO(" TV1: %s\n", encoder_names[radeon_encoder->encoder_id]); if (devices & ATOM_DEVICE_CV_SUPPORT) @@ -841,8 +843,9 @@ static void radeon_user_framebuffer_destroy(struct drm_framebuffer *fb) { struct radeon_framebuffer *radeon_fb = to_radeon_framebuffer(fb); - if (radeon_fb->obj) + if (radeon_fb->obj) { drm_gem_object_unreference_unlocked(radeon_fb->obj); + } drm_framebuffer_cleanup(fb); kfree(radeon_fb); } diff --git a/drivers/gpu/drm/radeon/radeon_fb.c b/drivers/gpu/drm/radeon/radeon_fb.c index c74a8b20d94..9cdf6a35bc2 100644 --- a/drivers/gpu/drm/radeon/radeon_fb.c +++ b/drivers/gpu/drm/radeon/radeon_fb.c @@ -94,8 +94,10 @@ static void radeonfb_destroy_pinned_object(struct drm_gem_object *gobj) ret = radeon_bo_reserve(rbo, false); if (likely(ret == 0)) { radeon_bo_kunmap(rbo); + radeon_bo_unpin(rbo); radeon_bo_unreserve(rbo); } + drm_gem_object_handle_unreference(gobj); drm_gem_object_unreference_unlocked(gobj); } @@ -325,8 +327,6 @@ static int radeon_fbdev_destroy(struct drm_device *dev, struct radeon_fbdev *rfb { struct fb_info *info; struct radeon_framebuffer *rfb = &rfbdev->rfb; - struct radeon_bo *rbo; - int r; if (rfbdev->helper.fbdev) { info = rfbdev->helper.fbdev; @@ -338,14 +338,8 @@ static int radeon_fbdev_destroy(struct drm_device *dev, struct radeon_fbdev *rfb } if (rfb->obj) { - rbo = rfb->obj->driver_private; - r = radeon_bo_reserve(rbo, false); - if (likely(r == 0)) { - radeon_bo_kunmap(rbo); - radeon_bo_unpin(rbo); - radeon_bo_unreserve(rbo); - } - drm_gem_object_unreference_unlocked(rfb->obj); + radeonfb_destroy_pinned_object(rfb->obj); + rfb->obj = NULL; } drm_fb_helper_fini(&rfbdev->helper); drm_framebuffer_cleanup(&rfb->base); diff --git a/drivers/gpu/drm/radeon/radeon_gem.c b/drivers/gpu/drm/radeon/radeon_gem.c index c578f265b24..d1e595d9172 100644 --- a/drivers/gpu/drm/radeon/radeon_gem.c +++ b/drivers/gpu/drm/radeon/radeon_gem.c @@ -201,11 +201,11 @@ int radeon_gem_create_ioctl(struct drm_device *dev, void *data, return r; } r = drm_gem_handle_create(filp, gobj, &handle); + /* drop reference from allocate - handle holds it now */ + drm_gem_object_unreference_unlocked(gobj); if (r) { - drm_gem_object_unreference_unlocked(gobj); return r; } - drm_gem_object_handle_unreference_unlocked(gobj); args->handle = handle; return 0; } diff --git a/drivers/gpu/drm/radeon/radeon_kms.c b/drivers/gpu/drm/radeon/radeon_kms.c index 5eee3c41d12..8fbbe1c6ebb 100644 --- a/drivers/gpu/drm/radeon/radeon_kms.c +++ b/drivers/gpu/drm/radeon/radeon_kms.c @@ -203,6 +203,10 @@ int radeon_info_ioctl(struct drm_device *dev, void *data, struct drm_file *filp) */ int radeon_driver_firstopen_kms(struct drm_device *dev) { + struct radeon_device *rdev = dev->dev_private; + + if (rdev->powered_down) + return -EINVAL; return 0; } diff --git a/drivers/gpu/drm/ttm/ttm_bo_util.c b/drivers/gpu/drm/ttm/ttm_bo_util.c index c9d2d4d8d06..ff358ad45aa 100644 --- a/drivers/gpu/drm/ttm/ttm_bo_util.c +++ b/drivers/gpu/drm/ttm/ttm_bo_util.c @@ -343,6 +343,7 @@ static int ttm_buffer_object_transfer(struct ttm_buffer_object *bo, INIT_LIST_HEAD(&fbo->lru); INIT_LIST_HEAD(&fbo->swap); fbo->vm_node = NULL; + atomic_set(&fbo->cpu_writers, 0); fbo->sync_obj = driver->sync_obj_ref(bo->sync_obj); kref_init(&fbo->list_kref); diff --git a/drivers/gpu/drm/ttm/ttm_page_alloc.c b/drivers/gpu/drm/ttm/ttm_page_alloc.c index ca904799f01..b1e02fffd3c 100644 --- a/drivers/gpu/drm/ttm/ttm_page_alloc.c +++ b/drivers/gpu/drm/ttm/ttm_page_alloc.c @@ -69,7 +69,7 @@ struct ttm_page_pool { spinlock_t lock; bool fill_lock; struct list_head list; - int gfp_flags; + gfp_t gfp_flags; unsigned npages; char *name; unsigned long nfrees; @@ -475,7 +475,7 @@ static void ttm_handle_caching_state_failure(struct list_head *pages, * This function is reentrant if caller updates count depending on number of * pages returned in pages array. */ -static int ttm_alloc_new_pages(struct list_head *pages, int gfp_flags, +static int ttm_alloc_new_pages(struct list_head *pages, gfp_t gfp_flags, int ttm_flags, enum ttm_caching_state cstate, unsigned count) { struct page **caching_array; @@ -666,7 +666,7 @@ int ttm_get_pages(struct list_head *pages, int flags, { struct ttm_page_pool *pool = ttm_get_pool(flags, cstate); struct page *p = NULL; - int gfp_flags = GFP_USER; + gfp_t gfp_flags = GFP_USER; int r; /* set zero flag for page allocation if required */ @@ -818,7 +818,7 @@ int ttm_page_alloc_init(struct ttm_mem_global *glob, unsigned max_pages) return 0; } -void ttm_page_alloc_fini() +void ttm_page_alloc_fini(void) { int i; diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_drv.c b/drivers/gpu/drm/vmwgfx/vmwgfx_drv.c index e645f44e430..f2942b3c59c 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_drv.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_drv.c @@ -148,13 +148,16 @@ static struct pci_device_id vmw_pci_id_list[] = { {0, 0, 0} }; -static char *vmw_devname = "vmwgfx"; +static int enable_fbdev; static int vmw_probe(struct pci_dev *, const struct pci_device_id *); static void vmw_master_init(struct vmw_master *); static int vmwgfx_pm_notifier(struct notifier_block *nb, unsigned long val, void *ptr); +MODULE_PARM_DESC(enable_fbdev, "Enable vmwgfx fbdev"); +module_param_named(enable_fbdev, enable_fbdev, int, 0600); + static void vmw_print_capabilities(uint32_t capabilities) { DRM_INFO("Capabilities:\n"); @@ -192,8 +195,6 @@ static int vmw_request_device(struct vmw_private *dev_priv) { int ret; - vmw_kms_save_vga(dev_priv); - ret = vmw_fifo_init(dev_priv, &dev_priv->fifo); if (unlikely(ret != 0)) { DRM_ERROR("Unable to initialize FIFO.\n"); @@ -206,10 +207,36 @@ static int vmw_request_device(struct vmw_private *dev_priv) static void vmw_release_device(struct vmw_private *dev_priv) { vmw_fifo_release(dev_priv, &dev_priv->fifo); - vmw_kms_restore_vga(dev_priv); +} + +int vmw_3d_resource_inc(struct vmw_private *dev_priv) +{ + int ret = 0; + + mutex_lock(&dev_priv->release_mutex); + if (unlikely(dev_priv->num_3d_resources++ == 0)) { + ret = vmw_request_device(dev_priv); + if (unlikely(ret != 0)) + --dev_priv->num_3d_resources; + } + mutex_unlock(&dev_priv->release_mutex); + return ret; } +void vmw_3d_resource_dec(struct vmw_private *dev_priv) +{ + int32_t n3d; + + mutex_lock(&dev_priv->release_mutex); + if (unlikely(--dev_priv->num_3d_resources == 0)) + vmw_release_device(dev_priv); + n3d = (int32_t) dev_priv->num_3d_resources; + mutex_unlock(&dev_priv->release_mutex); + + BUG_ON(n3d < 0); +} + static int vmw_driver_load(struct drm_device *dev, unsigned long chipset) { struct vmw_private *dev_priv; @@ -228,6 +255,7 @@ static int vmw_driver_load(struct drm_device *dev, unsigned long chipset) dev_priv->last_read_sequence = (uint32_t) -100; mutex_init(&dev_priv->hw_mutex); mutex_init(&dev_priv->cmdbuf_mutex); + mutex_init(&dev_priv->release_mutex); rwlock_init(&dev_priv->resource_lock); idr_init(&dev_priv->context_idr); idr_init(&dev_priv->surface_idr); @@ -244,6 +272,8 @@ static int vmw_driver_load(struct drm_device *dev, unsigned long chipset) dev_priv->vram_start = pci_resource_start(dev->pdev, 1); dev_priv->mmio_start = pci_resource_start(dev->pdev, 2); + dev_priv->enable_fb = enable_fbdev; + mutex_lock(&dev_priv->hw_mutex); vmw_write(dev_priv, SVGA_REG_ID, SVGA_ID_2); @@ -343,17 +373,6 @@ static int vmw_driver_load(struct drm_device *dev, unsigned long chipset) dev->dev_private = dev_priv; - if (!dev->devname) - dev->devname = vmw_devname; - - if (dev_priv->capabilities & SVGA_CAP_IRQMASK) { - ret = drm_irq_install(dev); - if (unlikely(ret != 0)) { - DRM_ERROR("Failed installing irq: %d\n", ret); - goto out_no_irq; - } - } - ret = pci_request_regions(dev->pdev, "vmwgfx probe"); dev_priv->stealth = (ret != 0); if (dev_priv->stealth) { @@ -369,26 +388,52 @@ static int vmw_driver_load(struct drm_device *dev, unsigned long chipset) goto out_no_device; } } - ret = vmw_request_device(dev_priv); + ret = vmw_kms_init(dev_priv); if (unlikely(ret != 0)) - goto out_no_device; - vmw_kms_init(dev_priv); + goto out_no_kms; vmw_overlay_init(dev_priv); - vmw_fb_init(dev_priv); + if (dev_priv->enable_fb) { + ret = vmw_3d_resource_inc(dev_priv); + if (unlikely(ret != 0)) + goto out_no_fifo; + vmw_kms_save_vga(dev_priv); + vmw_fb_init(dev_priv); + DRM_INFO("%s", vmw_fifo_have_3d(dev_priv) ? + "Detected device 3D availability.\n" : + "Detected no device 3D availability.\n"); + } else { + DRM_INFO("Delayed 3D detection since we're not " + "running the device in SVGA mode yet.\n"); + } + + if (dev_priv->capabilities & SVGA_CAP_IRQMASK) { + ret = drm_irq_install(dev); + if (unlikely(ret != 0)) { + DRM_ERROR("Failed installing irq: %d\n", ret); + goto out_no_irq; + } + } dev_priv->pm_nb.notifier_call = vmwgfx_pm_notifier; register_pm_notifier(&dev_priv->pm_nb); - DRM_INFO("%s", vmw_fifo_have_3d(dev_priv) ? "Have 3D\n" : "No 3D\n"); - return 0; -out_no_device: - if (dev_priv->capabilities & SVGA_CAP_IRQMASK) - drm_irq_uninstall(dev_priv->dev); - if (dev->devname == vmw_devname) - dev->devname = NULL; out_no_irq: + if (dev_priv->enable_fb) { + vmw_fb_close(dev_priv); + vmw_kms_restore_vga(dev_priv); + vmw_3d_resource_dec(dev_priv); + } +out_no_fifo: + vmw_overlay_close(dev_priv); + vmw_kms_close(dev_priv); +out_no_kms: + if (dev_priv->stealth) + pci_release_region(dev->pdev, 2); + else + pci_release_regions(dev->pdev); +out_no_device: ttm_object_device_release(&dev_priv->tdev); out_err4: iounmap(dev_priv->mmio_virt); @@ -415,19 +460,20 @@ static int vmw_driver_unload(struct drm_device *dev) unregister_pm_notifier(&dev_priv->pm_nb); - vmw_fb_close(dev_priv); + if (dev_priv->capabilities & SVGA_CAP_IRQMASK) + drm_irq_uninstall(dev_priv->dev); + if (dev_priv->enable_fb) { + vmw_fb_close(dev_priv); + vmw_kms_restore_vga(dev_priv); + vmw_3d_resource_dec(dev_priv); + } vmw_kms_close(dev_priv); vmw_overlay_close(dev_priv); - vmw_release_device(dev_priv); if (dev_priv->stealth) pci_release_region(dev->pdev, 2); else pci_release_regions(dev->pdev); - if (dev_priv->capabilities & SVGA_CAP_IRQMASK) - drm_irq_uninstall(dev_priv->dev); - if (dev->devname == vmw_devname) - dev->devname = NULL; ttm_object_device_release(&dev_priv->tdev); iounmap(dev_priv->mmio_virt); drm_mtrr_del(dev_priv->mmio_mtrr, dev_priv->mmio_start, @@ -500,7 +546,7 @@ static long vmw_unlocked_ioctl(struct file *filp, unsigned int cmd, struct drm_ioctl_desc *ioctl = &vmw_ioctls[nr - DRM_COMMAND_BASE]; - if (unlikely(ioctl->cmd != cmd)) { + if (unlikely(ioctl->cmd_drv != cmd)) { DRM_ERROR("Invalid command format, ioctl %d\n", nr - DRM_COMMAND_BASE); return -EINVAL; @@ -551,6 +597,8 @@ static void vmw_lastclose(struct drm_device *dev) static void vmw_master_init(struct vmw_master *vmaster) { ttm_lock_init(&vmaster->lock); + INIT_LIST_HEAD(&vmaster->fb_surf); + mutex_init(&vmaster->fb_surf_mutex); } static int vmw_master_create(struct drm_device *dev, @@ -562,7 +610,7 @@ static int vmw_master_create(struct drm_device *dev, if (unlikely(vmaster == NULL)) return -ENOMEM; - ttm_lock_init(&vmaster->lock); + vmw_master_init(vmaster); ttm_lock_set_kill(&vmaster->lock, true, SIGTERM); master->driver_priv = vmaster; @@ -589,6 +637,16 @@ static int vmw_master_set(struct drm_device *dev, struct vmw_master *vmaster = vmw_master(file_priv->master); int ret = 0; + if (!dev_priv->enable_fb) { + ret = vmw_3d_resource_inc(dev_priv); + if (unlikely(ret != 0)) + return ret; + vmw_kms_save_vga(dev_priv); + mutex_lock(&dev_priv->hw_mutex); + vmw_write(dev_priv, SVGA_REG_TRACES, 0); + mutex_unlock(&dev_priv->hw_mutex); + } + if (active) { BUG_ON(active != &dev_priv->fbdev_master); ret = ttm_vt_lock(&active->lock, false, vmw_fp->tfile); @@ -617,7 +675,13 @@ static int vmw_master_set(struct drm_device *dev, return 0; out_no_active_lock: - vmw_release_device(dev_priv); + if (!dev_priv->enable_fb) { + mutex_lock(&dev_priv->hw_mutex); + vmw_write(dev_priv, SVGA_REG_TRACES, 1); + mutex_unlock(&dev_priv->hw_mutex); + vmw_kms_restore_vga(dev_priv); + vmw_3d_resource_dec(dev_priv); + } return ret; } @@ -637,6 +701,7 @@ static void vmw_master_drop(struct drm_device *dev, vmw_fp->locked_master = drm_master_get(file_priv->master); ret = ttm_vt_lock(&vmaster->lock, false, vmw_fp->tfile); + vmw_kms_idle_workqueues(vmaster); if (unlikely((ret != 0))) { DRM_ERROR("Unable to lock TTM at VT switch.\n"); @@ -645,11 +710,23 @@ static void vmw_master_drop(struct drm_device *dev, ttm_lock_set_kill(&vmaster->lock, true, SIGTERM); + if (!dev_priv->enable_fb) { + ret = ttm_bo_evict_mm(&dev_priv->bdev, TTM_PL_VRAM); + if (unlikely(ret != 0)) + DRM_ERROR("Unable to clean VRAM on master drop.\n"); + mutex_lock(&dev_priv->hw_mutex); + vmw_write(dev_priv, SVGA_REG_TRACES, 1); + mutex_unlock(&dev_priv->hw_mutex); + vmw_kms_restore_vga(dev_priv); + vmw_3d_resource_dec(dev_priv); + } + dev_priv->active_master = &dev_priv->fbdev_master; ttm_lock_set_kill(&dev_priv->fbdev_master.lock, false, SIGTERM); ttm_vt_unlock(&dev_priv->fbdev_master.lock); - vmw_fb_on(dev_priv); + if (dev_priv->enable_fb) + vmw_fb_on(dev_priv); } @@ -677,15 +754,16 @@ static int vmwgfx_pm_notifier(struct notifier_block *nb, unsigned long val, * Buffer contents is moved to swappable memory. */ ttm_bo_swapout_all(&dev_priv->bdev); + break; case PM_POST_HIBERNATION: case PM_POST_SUSPEND: + case PM_POST_RESTORE: ttm_suspend_unlock(&vmaster->lock); + break; case PM_RESTORE_PREPARE: break; - case PM_POST_RESTORE: - break; default: break; } @@ -696,21 +774,98 @@ static int vmwgfx_pm_notifier(struct notifier_block *nb, unsigned long val, * These might not be needed with the virtual SVGA device. */ -int vmw_pci_suspend(struct pci_dev *pdev, pm_message_t state) +static int vmw_pci_suspend(struct pci_dev *pdev, pm_message_t state) { + struct drm_device *dev = pci_get_drvdata(pdev); + struct vmw_private *dev_priv = vmw_priv(dev); + + if (dev_priv->num_3d_resources != 0) { + DRM_INFO("Can't suspend or hibernate " + "while 3D resources are active.\n"); + return -EBUSY; + } + pci_save_state(pdev); pci_disable_device(pdev); pci_set_power_state(pdev, PCI_D3hot); return 0; } -int vmw_pci_resume(struct pci_dev *pdev) +static int vmw_pci_resume(struct pci_dev *pdev) { pci_set_power_state(pdev, PCI_D0); pci_restore_state(pdev); return pci_enable_device(pdev); } +static int vmw_pm_suspend(struct device *kdev) +{ + struct pci_dev *pdev = to_pci_dev(kdev); + struct pm_message dummy; + + dummy.event = 0; + + return vmw_pci_suspend(pdev, dummy); +} + +static int vmw_pm_resume(struct device *kdev) +{ + struct pci_dev *pdev = to_pci_dev(kdev); + + return vmw_pci_resume(pdev); +} + +static int vmw_pm_prepare(struct device *kdev) +{ + struct pci_dev *pdev = to_pci_dev(kdev); + struct drm_device *dev = pci_get_drvdata(pdev); + struct vmw_private *dev_priv = vmw_priv(dev); + + /** + * Release 3d reference held by fbdev and potentially + * stop fifo. + */ + dev_priv->suspended = true; + if (dev_priv->enable_fb) + vmw_3d_resource_dec(dev_priv); + + if (dev_priv->num_3d_resources != 0) { + + DRM_INFO("Can't suspend or hibernate " + "while 3D resources are active.\n"); + + if (dev_priv->enable_fb) + vmw_3d_resource_inc(dev_priv); + dev_priv->suspended = false; + return -EBUSY; + } + + return 0; +} + +static void vmw_pm_complete(struct device *kdev) +{ + struct pci_dev *pdev = to_pci_dev(kdev); + struct drm_device *dev = pci_get_drvdata(pdev); + struct vmw_private *dev_priv = vmw_priv(dev); + + /** + * Reclaim 3d reference held by fbdev and potentially + * start fifo. + */ + if (dev_priv->enable_fb) + vmw_3d_resource_inc(dev_priv); + + dev_priv->suspended = false; +} + +static const struct dev_pm_ops vmw_pm_ops = { + .prepare = vmw_pm_prepare, + .complete = vmw_pm_complete, + .suspend = vmw_pm_suspend, + .resume = vmw_pm_resume, +}; + static struct drm_driver driver = { .driver_features = DRIVER_HAVE_IRQ | DRIVER_IRQ_SHARED | DRIVER_MODESET, @@ -722,6 +877,7 @@ static struct drm_driver driver = { .irq_postinstall = vmw_irq_postinstall, .irq_uninstall = vmw_irq_uninstall, .irq_handler = vmw_irq_handler, + .get_vblank_counter = vmw_get_vblank_counter, .reclaim_buffers_locked = NULL, .ioctls = vmw_ioctls, .num_ioctls = DRM_ARRAY_SIZE(vmw_ioctls), @@ -743,15 +899,16 @@ static struct drm_driver driver = { #if defined(CONFIG_COMPAT) .compat_ioctl = drm_compat_ioctl, #endif - }, + }, .pci_driver = { - .name = VMWGFX_DRIVER_NAME, - .id_table = vmw_pci_id_list, - .probe = vmw_probe, - .remove = vmw_remove, - .suspend = vmw_pci_suspend, - .resume = vmw_pci_resume - }, + .name = VMWGFX_DRIVER_NAME, + .id_table = vmw_pci_id_list, + .probe = vmw_probe, + .remove = vmw_remove, + .driver = { + .pm = &vmw_pm_ops + } + }, .name = VMWGFX_DRIVER_NAME, .desc = VMWGFX_DRIVER_DESC, .date = VMWGFX_DRIVER_DATE, @@ -785,3 +942,7 @@ module_exit(vmwgfx_exit); MODULE_AUTHOR("VMware Inc. and others"); MODULE_DESCRIPTION("Standalone drm driver for the VMware SVGA device"); MODULE_LICENSE("GPL and additional rights"); +MODULE_VERSION(__stringify(VMWGFX_DRIVER_MAJOR) "." + __stringify(VMWGFX_DRIVER_MINOR) "." + __stringify(VMWGFX_DRIVER_PATCHLEVEL) "." + "0"); diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_drv.h b/drivers/gpu/drm/vmwgfx/vmwgfx_drv.h index 429f917b60b..9d55fa8cd0f 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_drv.h +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_drv.h @@ -39,9 +39,9 @@ #include "ttm/ttm_execbuf_util.h" #include "ttm/ttm_module.h" -#define VMWGFX_DRIVER_DATE "20100209" +#define VMWGFX_DRIVER_DATE "20100927" #define VMWGFX_DRIVER_MAJOR 1 -#define VMWGFX_DRIVER_MINOR 2 +#define VMWGFX_DRIVER_MINOR 4 #define VMWGFX_DRIVER_PATCHLEVEL 0 #define VMWGFX_FILE_PAGE_OFFSET 0x00100000 #define VMWGFX_FIFO_STATIC_SIZE (1024*1024) @@ -151,6 +151,8 @@ struct vmw_overlay; struct vmw_master { struct ttm_lock lock; + struct mutex fb_surf_mutex; + struct list_head fb_surf; }; struct vmw_vga_topology_state { @@ -277,6 +279,7 @@ struct vmw_private { bool stealth; bool is_opened; + bool enable_fb; /** * Master management. @@ -285,6 +288,10 @@ struct vmw_private { struct vmw_master *active_master; struct vmw_master fbdev_master; struct notifier_block pm_nb; + bool suspended; + + struct mutex release_mutex; + uint32_t num_3d_resources; }; static inline struct vmw_private *vmw_priv(struct drm_device *dev) @@ -319,6 +326,9 @@ static inline uint32_t vmw_read(struct vmw_private *dev_priv, return val; } +int vmw_3d_resource_inc(struct vmw_private *dev_priv); +void vmw_3d_resource_dec(struct vmw_private *dev_priv); + /** * GMR utilities - vmwgfx_gmr.c */ @@ -511,6 +521,11 @@ void vmw_kms_write_svga(struct vmw_private *vmw_priv, unsigned bbp, unsigned depth); int vmw_kms_update_layout_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv); +void vmw_kms_idle_workqueues(struct vmw_master *vmaster); +bool vmw_kms_validate_mode_vram(struct vmw_private *dev_priv, + uint32_t pitch, + uint32_t height); +u32 vmw_get_vblank_counter(struct drm_device *dev, int crtc); /** * Overlay control - vmwgfx_overlay.c diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_fb.c b/drivers/gpu/drm/vmwgfx/vmwgfx_fb.c index 870967a97c1..e7304188a78 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_fb.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_fb.c @@ -144,6 +144,13 @@ static int vmw_fb_check_var(struct fb_var_screeninfo *var, return -EINVAL; } + if (!vmw_kms_validate_mode_vram(vmw_priv, + info->fix.line_length, + var->yoffset + var->yres)) { + DRM_ERROR("Requested geom can not fit in framebuffer\n"); + return -EINVAL; + } + return 0; } @@ -205,6 +212,9 @@ static void vmw_fb_dirty_flush(struct vmw_fb_par *par) SVGAFifoCmdUpdate body; } *cmd; + if (vmw_priv->suspended) + return; + spin_lock_irqsave(&par->dirty.lock, flags); if (!par->dirty.active) { spin_unlock_irqrestore(&par->dirty.lock, flags); @@ -602,6 +612,7 @@ int vmw_dmabuf_to_start_of_vram(struct vmw_private *vmw_priv, { struct ttm_buffer_object *bo = &vmw_bo->base; struct ttm_placement ne_placement = vmw_vram_ne_placement; + struct drm_mm_node *mm_node; int ret = 0; ne_placement.lpfn = bo->num_pages; @@ -615,6 +626,12 @@ int vmw_dmabuf_to_start_of_vram(struct vmw_private *vmw_priv, if (unlikely(ret != 0)) goto err_unlock; + mm_node = bo->mem.mm_node; + if (bo->mem.mem_type == TTM_PL_VRAM && + mm_node->start < bo->num_pages) + (void) ttm_bo_validate(bo, &vmw_sys_placement, false, + false, false); + ret = ttm_bo_validate(bo, &ne_placement, false, false, false); /* Could probably bug on */ diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_fifo.c b/drivers/gpu/drm/vmwgfx/vmwgfx_fifo.c index e6a1eb7ea95..0fe31766e4c 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_fifo.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_fifo.c @@ -106,6 +106,7 @@ int vmw_fifo_init(struct vmw_private *dev_priv, struct vmw_fifo_state *fifo) mutex_lock(&dev_priv->hw_mutex); dev_priv->enable_state = vmw_read(dev_priv, SVGA_REG_ENABLE); dev_priv->config_done_state = vmw_read(dev_priv, SVGA_REG_CONFIG_DONE); + dev_priv->traces_state = vmw_read(dev_priv, SVGA_REG_TRACES); vmw_write(dev_priv, SVGA_REG_ENABLE, 1); min = 4; @@ -175,6 +176,8 @@ void vmw_fifo_release(struct vmw_private *dev_priv, struct vmw_fifo_state *fifo) dev_priv->config_done_state); vmw_write(dev_priv, SVGA_REG_ENABLE, dev_priv->enable_state); + vmw_write(dev_priv, SVGA_REG_TRACES, + dev_priv->traces_state); mutex_unlock(&dev_priv->hw_mutex); vmw_fence_queue_takedown(&fifo->fence_queue); diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_ioctl.c b/drivers/gpu/drm/vmwgfx/vmwgfx_ioctl.c index 1c7a316454d..570d57775a5 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_ioctl.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_ioctl.c @@ -54,6 +54,9 @@ int vmw_getparam_ioctl(struct drm_device *dev, void *data, case DRM_VMW_PARAM_FIFO_CAPS: param->value = dev_priv->fifo.capabilities; break; + case DRM_VMW_PARAM_MAX_FB_SIZE: + param->value = dev_priv->vram_size; + break; default: DRM_ERROR("Illegal vmwgfx get param request: %d\n", param->param); diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c b/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c index 64d7f47df86..87c6e6156d7 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c @@ -332,18 +332,55 @@ struct vmw_framebuffer_surface { struct delayed_work d_work; struct mutex work_lock; bool present_fs; + struct list_head head; + struct drm_master *master; }; +/** + * vmw_kms_idle_workqueues - Flush workqueues on this master + * + * @vmaster - Pointer identifying the master, for the surfaces of which + * we idle the dirty work queues. + * + * This function should be called with the ttm lock held in exclusive mode + * to idle all dirty work queues before the fifo is taken down. + * + * The work task may actually requeue itself, but after the flush returns we're + * sure that there's nothing to present, since the ttm lock is held in + * exclusive mode, so the fifo will never get used. + */ + +void vmw_kms_idle_workqueues(struct vmw_master *vmaster) +{ + struct vmw_framebuffer_surface *entry; + + mutex_lock(&vmaster->fb_surf_mutex); + list_for_each_entry(entry, &vmaster->fb_surf, head) { + if (cancel_delayed_work_sync(&entry->d_work)) + (void) entry->d_work.work.func(&entry->d_work.work); + + (void) cancel_delayed_work_sync(&entry->d_work); + } + mutex_unlock(&vmaster->fb_surf_mutex); +} + void vmw_framebuffer_surface_destroy(struct drm_framebuffer *framebuffer) { - struct vmw_framebuffer_surface *vfb = + struct vmw_framebuffer_surface *vfbs = vmw_framebuffer_to_vfbs(framebuffer); + struct vmw_master *vmaster = vmw_master(vfbs->master); + - cancel_delayed_work_sync(&vfb->d_work); + mutex_lock(&vmaster->fb_surf_mutex); + list_del(&vfbs->head); + mutex_unlock(&vmaster->fb_surf_mutex); + + cancel_delayed_work_sync(&vfbs->d_work); + drm_master_put(&vfbs->master); drm_framebuffer_cleanup(framebuffer); - vmw_surface_unreference(&vfb->surface); + vmw_surface_unreference(&vfbs->surface); - kfree(framebuffer); + kfree(vfbs); } static void vmw_framebuffer_present_fs_callback(struct work_struct *work) @@ -362,6 +399,12 @@ static void vmw_framebuffer_present_fs_callback(struct work_struct *work) SVGA3dCopyRect cr; } *cmd; + /** + * Strictly we should take the ttm_lock in read mode before accessing + * the fifo, to make sure the fifo is present and up. However, + * instead we flush all workqueues under the ttm lock in exclusive mode + * before taking down the fifo. + */ mutex_lock(&vfbs->work_lock); if (!vfbs->present_fs) goto out_unlock; @@ -392,17 +435,20 @@ out_unlock: int vmw_framebuffer_surface_dirty(struct drm_framebuffer *framebuffer, + struct drm_file *file_priv, unsigned flags, unsigned color, struct drm_clip_rect *clips, unsigned num_clips) { struct vmw_private *dev_priv = vmw_priv(framebuffer->dev); + struct vmw_master *vmaster = vmw_master(file_priv->master); struct vmw_framebuffer_surface *vfbs = vmw_framebuffer_to_vfbs(framebuffer); struct vmw_surface *surf = vfbs->surface; struct drm_clip_rect norect; SVGA3dCopyRect *cr; int i, inc = 1; + int ret; struct { SVGA3dCmdHeader header; @@ -410,6 +456,13 @@ int vmw_framebuffer_surface_dirty(struct drm_framebuffer *framebuffer, SVGA3dCopyRect cr; } *cmd; + if (unlikely(vfbs->master != file_priv->master)) + return -EINVAL; + + ret = ttm_read_lock(&vmaster->lock, true); + if (unlikely(ret != 0)) + return ret; + if (!num_clips || !(dev_priv->fifo.capabilities & SVGA_FIFO_CAP_SCREEN_OBJECT)) { @@ -425,6 +478,7 @@ int vmw_framebuffer_surface_dirty(struct drm_framebuffer *framebuffer, */ vmw_framebuffer_present_fs_callback(&vfbs->d_work.work); } + ttm_read_unlock(&vmaster->lock); return 0; } @@ -442,6 +496,7 @@ int vmw_framebuffer_surface_dirty(struct drm_framebuffer *framebuffer, cmd = vmw_fifo_reserve(dev_priv, sizeof(*cmd) + (num_clips - 1) * sizeof(cmd->cr)); if (unlikely(cmd == NULL)) { DRM_ERROR("Fifo reserve failed.\n"); + ttm_read_unlock(&vmaster->lock); return -ENOMEM; } @@ -461,7 +516,7 @@ int vmw_framebuffer_surface_dirty(struct drm_framebuffer *framebuffer, } vmw_fifo_commit(dev_priv, sizeof(*cmd) + (num_clips - 1) * sizeof(cmd->cr)); - + ttm_read_unlock(&vmaster->lock); return 0; } @@ -471,16 +526,57 @@ static struct drm_framebuffer_funcs vmw_framebuffer_surface_funcs = { .create_handle = vmw_framebuffer_create_handle, }; -int vmw_kms_new_framebuffer_surface(struct vmw_private *dev_priv, - struct vmw_surface *surface, - struct vmw_framebuffer **out, - unsigned width, unsigned height) +static int vmw_kms_new_framebuffer_surface(struct vmw_private *dev_priv, + struct drm_file *file_priv, + struct vmw_surface *surface, + struct vmw_framebuffer **out, + const struct drm_mode_fb_cmd + *mode_cmd) { struct drm_device *dev = dev_priv->dev; struct vmw_framebuffer_surface *vfbs; + enum SVGA3dSurfaceFormat format; + struct vmw_master *vmaster = vmw_master(file_priv->master); int ret; + /* + * Sanity checks. + */ + + if (unlikely(surface->mip_levels[0] != 1 || + surface->num_sizes != 1 || + surface->sizes[0].width < mode_cmd->width || + surface->sizes[0].height < mode_cmd->height || + surface->sizes[0].depth != 1)) { + DRM_ERROR("Incompatible surface dimensions " + "for requested mode.\n"); + return -EINVAL; + } + + switch (mode_cmd->depth) { + case 32: + format = SVGA3D_A8R8G8B8; + break; + case 24: + format = SVGA3D_X8R8G8B8; + break; + case 16: + format = SVGA3D_R5G6B5; + break; + case 15: + format = SVGA3D_A1R5G5B5; + break; + default: + DRM_ERROR("Invalid color depth: %d\n", mode_cmd->depth); + return -EINVAL; + } + + if (unlikely(format != surface->format)) { + DRM_ERROR("Invalid surface format for requested mode.\n"); + return -EINVAL; + } + vfbs = kzalloc(sizeof(*vfbs), GFP_KERNEL); if (!vfbs) { ret = -ENOMEM; @@ -498,16 +594,22 @@ int vmw_kms_new_framebuffer_surface(struct vmw_private *dev_priv, } /* XXX get the first 3 from the surface info */ - vfbs->base.base.bits_per_pixel = 32; - vfbs->base.base.pitch = width * 32 / 4; - vfbs->base.base.depth = 24; - vfbs->base.base.width = width; - vfbs->base.base.height = height; + vfbs->base.base.bits_per_pixel = mode_cmd->bpp; + vfbs->base.base.pitch = mode_cmd->pitch; + vfbs->base.base.depth = mode_cmd->depth; + vfbs->base.base.width = mode_cmd->width; + vfbs->base.base.height = mode_cmd->height; vfbs->base.pin = &vmw_surface_dmabuf_pin; vfbs->base.unpin = &vmw_surface_dmabuf_unpin; vfbs->surface = surface; + vfbs->master = drm_master_get(file_priv->master); mutex_init(&vfbs->work_lock); + + mutex_lock(&vmaster->fb_surf_mutex); INIT_DELAYED_WORK(&vfbs->d_work, &vmw_framebuffer_present_fs_callback); + list_add_tail(&vfbs->head, &vmaster->fb_surf); + mutex_unlock(&vmaster->fb_surf_mutex); + *out = &vfbs->base; return 0; @@ -544,18 +646,25 @@ void vmw_framebuffer_dmabuf_destroy(struct drm_framebuffer *framebuffer) } int vmw_framebuffer_dmabuf_dirty(struct drm_framebuffer *framebuffer, + struct drm_file *file_priv, unsigned flags, unsigned color, struct drm_clip_rect *clips, unsigned num_clips) { struct vmw_private *dev_priv = vmw_priv(framebuffer->dev); + struct vmw_master *vmaster = vmw_master(file_priv->master); struct drm_clip_rect norect; + int ret; struct { uint32_t header; SVGAFifoCmdUpdate body; } *cmd; int i, increment = 1; + ret = ttm_read_lock(&vmaster->lock, true); + if (unlikely(ret != 0)) + return ret; + if (!num_clips) { num_clips = 1; clips = &norect; @@ -570,6 +679,7 @@ int vmw_framebuffer_dmabuf_dirty(struct drm_framebuffer *framebuffer, cmd = vmw_fifo_reserve(dev_priv, sizeof(*cmd) * num_clips); if (unlikely(cmd == NULL)) { DRM_ERROR("Fifo reserve failed.\n"); + ttm_read_unlock(&vmaster->lock); return -ENOMEM; } @@ -582,6 +692,7 @@ int vmw_framebuffer_dmabuf_dirty(struct drm_framebuffer *framebuffer, } vmw_fifo_commit(dev_priv, sizeof(*cmd) * num_clips); + ttm_read_unlock(&vmaster->lock); return 0; } @@ -659,16 +770,25 @@ static int vmw_framebuffer_dmabuf_unpin(struct vmw_framebuffer *vfb) return vmw_dmabuf_from_vram(dev_priv, vfbd->buffer); } -int vmw_kms_new_framebuffer_dmabuf(struct vmw_private *dev_priv, - struct vmw_dma_buffer *dmabuf, - struct vmw_framebuffer **out, - unsigned width, unsigned height) +static int vmw_kms_new_framebuffer_dmabuf(struct vmw_private *dev_priv, + struct vmw_dma_buffer *dmabuf, + struct vmw_framebuffer **out, + const struct drm_mode_fb_cmd + *mode_cmd) { struct drm_device *dev = dev_priv->dev; struct vmw_framebuffer_dmabuf *vfbd; + unsigned int requested_size; int ret; + requested_size = mode_cmd->height * mode_cmd->pitch; + if (unlikely(requested_size > dmabuf->base.num_pages * PAGE_SIZE)) { + DRM_ERROR("Screen buffer object size is too small " + "for requested mode.\n"); + return -EINVAL; + } + vfbd = kzalloc(sizeof(*vfbd), GFP_KERNEL); if (!vfbd) { ret = -ENOMEM; @@ -685,12 +805,11 @@ int vmw_kms_new_framebuffer_dmabuf(struct vmw_private *dev_priv, goto out_err3; } - /* XXX get the first 3 from the surface info */ - vfbd->base.base.bits_per_pixel = 32; - vfbd->base.base.pitch = width * vfbd->base.base.bits_per_pixel / 8; - vfbd->base.base.depth = 24; - vfbd->base.base.width = width; - vfbd->base.base.height = height; + vfbd->base.base.bits_per_pixel = mode_cmd->bpp; + vfbd->base.base.pitch = mode_cmd->pitch; + vfbd->base.base.depth = mode_cmd->depth; + vfbd->base.base.width = mode_cmd->width; + vfbd->base.base.height = mode_cmd->height; vfbd->base.pin = vmw_framebuffer_dmabuf_pin; vfbd->base.unpin = vmw_framebuffer_dmabuf_unpin; vfbd->buffer = dmabuf; @@ -719,8 +838,25 @@ static struct drm_framebuffer *vmw_kms_fb_create(struct drm_device *dev, struct vmw_framebuffer *vfb = NULL; struct vmw_surface *surface = NULL; struct vmw_dma_buffer *bo = NULL; + u64 required_size; int ret; + /** + * This code should be conditioned on Screen Objects not being used. + * If screen objects are used, we can allocate a GMR to hold the + * requested framebuffer. + */ + + required_size = mode_cmd->pitch * mode_cmd->height; + if (unlikely(required_size > (u64) dev_priv->vram_size)) { + DRM_ERROR("VRAM size is too small for requested mode.\n"); + return NULL; + } + + /** + * End conditioned code. + */ + ret = vmw_user_surface_lookup_handle(dev_priv, tfile, mode_cmd->handle, &surface); if (ret) @@ -729,8 +865,8 @@ static struct drm_framebuffer *vmw_kms_fb_create(struct drm_device *dev, if (!surface->scanout) goto err_not_scanout; - ret = vmw_kms_new_framebuffer_surface(dev_priv, surface, &vfb, - mode_cmd->width, mode_cmd->height); + ret = vmw_kms_new_framebuffer_surface(dev_priv, file_priv, surface, + &vfb, mode_cmd); /* vmw_user_surface_lookup takes one ref so does new_fb */ vmw_surface_unreference(&surface); @@ -751,7 +887,7 @@ try_dmabuf: } ret = vmw_kms_new_framebuffer_dmabuf(dev_priv, bo, &vfb, - mode_cmd->width, mode_cmd->height); + mode_cmd); /* vmw_user_dmabuf_lookup takes one ref so does new_fb */ vmw_dmabuf_unreference(&bo); @@ -889,6 +1025,9 @@ int vmw_kms_save_vga(struct vmw_private *vmw_priv) vmw_priv->num_displays = vmw_read(vmw_priv, SVGA_REG_NUM_GUEST_DISPLAYS); + if (vmw_priv->num_displays == 0) + vmw_priv->num_displays = 1; + for (i = 0; i < vmw_priv->num_displays; ++i) { save = &vmw_priv->vga_save[i]; vmw_write(vmw_priv, SVGA_REG_DISPLAY_ID, i); @@ -898,7 +1037,19 @@ int vmw_kms_save_vga(struct vmw_private *vmw_priv) save->width = vmw_read(vmw_priv, SVGA_REG_DISPLAY_WIDTH); save->height = vmw_read(vmw_priv, SVGA_REG_DISPLAY_HEIGHT); vmw_write(vmw_priv, SVGA_REG_DISPLAY_ID, SVGA_ID_INVALID); + if (i == 0 && vmw_priv->num_displays == 1 && + save->width == 0 && save->height == 0) { + + /* + * It should be fairly safe to assume that these + * values are uninitialized. + */ + + save->width = vmw_priv->vga_width - save->pos_x; + save->height = vmw_priv->vga_height - save->pos_y; + } } + return 0; } @@ -984,3 +1135,15 @@ out_unlock: ttm_read_unlock(&vmaster->lock); return ret; } + +bool vmw_kms_validate_mode_vram(struct vmw_private *dev_priv, + uint32_t pitch, + uint32_t height) +{ + return ((u64) pitch * (u64) height) < (u64) dev_priv->vram_size; +} + +u32 vmw_get_vblank_counter(struct drm_device *dev, int crtc) +{ + return 0; +} diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_ldu.c b/drivers/gpu/drm/vmwgfx/vmwgfx_ldu.c index 7083b1a24df..a01c47ddb5b 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_ldu.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_ldu.c @@ -27,6 +27,8 @@ #include "vmwgfx_kms.h" +#define VMWGFX_LDU_NUM_DU 8 + #define vmw_crtc_to_ldu(x) \ container_of(x, struct vmw_legacy_display_unit, base.crtc) #define vmw_encoder_to_ldu(x) \ @@ -425,7 +427,9 @@ static int vmw_ldu_connector_fill_modes(struct drm_connector *connector, { struct vmw_legacy_display_unit *ldu = vmw_connector_to_ldu(connector); struct drm_device *dev = connector->dev; + struct vmw_private *dev_priv = vmw_priv(dev); struct drm_display_mode *mode = NULL; + struct drm_display_mode *bmode; struct drm_display_mode prefmode = { DRM_MODE("preferred", DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, @@ -441,22 +445,30 @@ static int vmw_ldu_connector_fill_modes(struct drm_connector *connector, mode->hdisplay = ldu->pref_width; mode->vdisplay = ldu->pref_height; mode->vrefresh = drm_mode_vrefresh(mode); - drm_mode_probed_add(connector, mode); + if (vmw_kms_validate_mode_vram(dev_priv, mode->hdisplay * 2, + mode->vdisplay)) { + drm_mode_probed_add(connector, mode); - if (ldu->pref_mode) { - list_del_init(&ldu->pref_mode->head); - drm_mode_destroy(dev, ldu->pref_mode); - } + if (ldu->pref_mode) { + list_del_init(&ldu->pref_mode->head); + drm_mode_destroy(dev, ldu->pref_mode); + } - ldu->pref_mode = mode; + ldu->pref_mode = mode; + } } for (i = 0; vmw_ldu_connector_builtin[i].type != 0; i++) { - if (vmw_ldu_connector_builtin[i].hdisplay > max_width || - vmw_ldu_connector_builtin[i].vdisplay > max_height) + bmode = &vmw_ldu_connector_builtin[i]; + if (bmode->hdisplay > max_width || + bmode->vdisplay > max_height) + continue; + + if (!vmw_kms_validate_mode_vram(dev_priv, bmode->hdisplay * 2, + bmode->vdisplay)) continue; - mode = drm_mode_duplicate(dev, &vmw_ldu_connector_builtin[i]); + mode = drm_mode_duplicate(dev, bmode); if (!mode) return 0; mode->vrefresh = drm_mode_vrefresh(mode); @@ -536,6 +548,10 @@ static int vmw_ldu_init(struct vmw_private *dev_priv, unsigned unit) int vmw_kms_init_legacy_display_system(struct vmw_private *dev_priv) { + struct drm_device *dev = dev_priv->dev; + int i; + int ret; + if (dev_priv->ldu_priv) { DRM_INFO("ldu system already on\n"); return -EINVAL; @@ -553,23 +569,24 @@ int vmw_kms_init_legacy_display_system(struct vmw_private *dev_priv) drm_mode_create_dirty_info_property(dev_priv->dev); - vmw_ldu_init(dev_priv, 0); - /* for old hardware without multimon only enable one display */ if (dev_priv->capabilities & SVGA_CAP_MULTIMON) { - vmw_ldu_init(dev_priv, 1); - vmw_ldu_init(dev_priv, 2); - vmw_ldu_init(dev_priv, 3); - vmw_ldu_init(dev_priv, 4); - vmw_ldu_init(dev_priv, 5); - vmw_ldu_init(dev_priv, 6); - vmw_ldu_init(dev_priv, 7); + for (i = 0; i < VMWGFX_LDU_NUM_DU; ++i) + vmw_ldu_init(dev_priv, i); + ret = drm_vblank_init(dev, VMWGFX_LDU_NUM_DU); + } else { + /* for old hardware without multimon only enable one display */ + vmw_ldu_init(dev_priv, 0); + ret = drm_vblank_init(dev, 1); } - return 0; + return ret; } int vmw_kms_close_legacy_display_system(struct vmw_private *dev_priv) { + struct drm_device *dev = dev_priv->dev; + + drm_vblank_cleanup(dev); if (!dev_priv->ldu_priv) return -ENOSYS; diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_resource.c b/drivers/gpu/drm/vmwgfx/vmwgfx_resource.c index 5f2d5df01e5..c8c40e9979d 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_resource.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_resource.c @@ -211,6 +211,7 @@ static void vmw_hw_context_destroy(struct vmw_resource *res) cmd->body.cid = cpu_to_le32(res->id); vmw_fifo_commit(dev_priv, sizeof(*cmd)); + vmw_3d_resource_dec(dev_priv); } static int vmw_context_init(struct vmw_private *dev_priv, @@ -247,6 +248,7 @@ static int vmw_context_init(struct vmw_private *dev_priv, cmd->body.cid = cpu_to_le32(res->id); vmw_fifo_commit(dev_priv, sizeof(*cmd)); + (void) vmw_3d_resource_inc(dev_priv); vmw_resource_activate(res, vmw_hw_context_destroy); return 0; } @@ -406,6 +408,7 @@ static void vmw_hw_surface_destroy(struct vmw_resource *res) cmd->body.sid = cpu_to_le32(res->id); vmw_fifo_commit(dev_priv, sizeof(*cmd)); + vmw_3d_resource_dec(dev_priv); } void vmw_surface_res_free(struct vmw_resource *res) @@ -473,6 +476,7 @@ int vmw_surface_init(struct vmw_private *dev_priv, } vmw_fifo_commit(dev_priv, submit_size); + (void) vmw_3d_resource_inc(dev_priv); vmw_resource_activate(res, vmw_hw_surface_destroy); return 0; } diff --git a/drivers/gpu/vga/vgaarb.c b/drivers/gpu/vga/vgaarb.c index b87569e96b1..f366f968155 100644 --- a/drivers/gpu/vga/vgaarb.c +++ b/drivers/gpu/vga/vgaarb.c @@ -598,7 +598,7 @@ static inline void vga_update_device_decodes(struct vga_device *vgadev, pr_debug("vgaarb: decoding count now is: %d\n", vga_decode_count); } -void __vga_set_legacy_decoding(struct pci_dev *pdev, unsigned int decodes, bool userspace) +static void __vga_set_legacy_decoding(struct pci_dev *pdev, unsigned int decodes, bool userspace) { struct vga_device *vgadev; unsigned long flags; diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig index 4d4d09bdec0..97499d00615 100644 --- a/drivers/hwmon/Kconfig +++ b/drivers/hwmon/Kconfig @@ -409,7 +409,7 @@ config SENSORS_CORETEMP config SENSORS_PKGTEMP tristate "Intel processor package temperature sensor" - depends on X86 && PCI && EXPERIMENTAL + depends on X86 && EXPERIMENTAL help If you say yes here you get support for the package level temperature sensor inside your CPU. Check documentation/driver for details. diff --git a/drivers/hwmon/coretemp.c b/drivers/hwmon/coretemp.c index de8111114f4..baa842a80b4 100644 --- a/drivers/hwmon/coretemp.c +++ b/drivers/hwmon/coretemp.c @@ -423,9 +423,18 @@ static int __cpuinit coretemp_device_add(unsigned int cpu) int err; struct platform_device *pdev; struct pdev_entry *pdev_entry; -#ifdef CONFIG_SMP struct cpuinfo_x86 *c = &cpu_data(cpu); -#endif + + /* + * CPUID.06H.EAX[0] indicates whether the CPU has thermal + * sensors. We check this bit only, all the early CPUs + * without thermal sensors will be filtered out. + */ + if (!cpu_has(c, X86_FEATURE_DTS)) { + printk(KERN_INFO DRVNAME ": CPU (model=0x%x)" + " has no thermal sensor.\n", c->x86_model); + return 0; + } mutex_lock(&pdev_list_mutex); @@ -482,14 +491,22 @@ exit: static void coretemp_device_remove(unsigned int cpu) { - struct pdev_entry *p, *n; + struct pdev_entry *p; + unsigned int i; + mutex_lock(&pdev_list_mutex); - list_for_each_entry_safe(p, n, &pdev_list, list) { - if (p->cpu == cpu) { - platform_device_unregister(p->pdev); - list_del(&p->list); - kfree(p); - } + list_for_each_entry(p, &pdev_list, list) { + if (p->cpu != cpu) + continue; + + platform_device_unregister(p->pdev); + list_del(&p->list); + mutex_unlock(&pdev_list_mutex); + kfree(p); + for_each_cpu(i, cpu_sibling_mask(cpu)) + if (i != cpu && !coretemp_device_add(i)) + break; + return; } mutex_unlock(&pdev_list_mutex); } @@ -527,30 +544,21 @@ static int __init coretemp_init(void) if (err) goto exit; - for_each_online_cpu(i) { - struct cpuinfo_x86 *c = &cpu_data(i); - /* - * CPUID.06H.EAX[0] indicates whether the CPU has thermal - * sensors. We check this bit only, all the early CPUs - * without thermal sensors will be filtered out. - */ - if (c->cpuid_level >= 6 && (cpuid_eax(0x06) & 0x01)) - coretemp_device_add(i); - else { - printk(KERN_INFO DRVNAME ": CPU (model=0x%x)" - " has no thermal sensor.\n", c->x86_model); - } - } + for_each_online_cpu(i) + coretemp_device_add(i); + +#ifndef CONFIG_HOTPLUG_CPU if (list_empty(&pdev_list)) { err = -ENODEV; goto exit_driver_unreg; } +#endif register_hotcpu_notifier(&coretemp_cpu_notifier); return 0; -exit_driver_unreg: #ifndef CONFIG_HOTPLUG_CPU +exit_driver_unreg: platform_driver_unregister(&coretemp_driver); #endif exit: diff --git a/drivers/hwmon/lis3lv02d.c b/drivers/hwmon/lis3lv02d.c index 6138f036b15..fc591ae5310 100644 --- a/drivers/hwmon/lis3lv02d.c +++ b/drivers/hwmon/lis3lv02d.c @@ -277,7 +277,7 @@ static irqreturn_t lis302dl_interrupt(int irq, void *dummy) wake_up_interruptible(&lis3_dev.misc_wait); kill_fasync(&lis3_dev.async_queue, SIGIO, POLL_IN); out: - if (lis3_dev.whoami == WAI_8B && lis3_dev.idev && + if (lis3_dev.pdata && lis3_dev.whoami == WAI_8B && lis3_dev.idev && lis3_dev.idev->input->users) return IRQ_WAKE_THREAD; return IRQ_HANDLED; @@ -718,7 +718,7 @@ int lis3lv02d_init_device(struct lis3lv02d *dev) * io-apic is not configurable (and generates a warning) but I keep it * in case of support for other hardware. */ - if (dev->whoami == WAI_8B) + if (dev->pdata && dev->whoami == WAI_8B) thread_fn = lis302dl_interrupt_thread1_8b; else thread_fn = NULL; diff --git a/drivers/hwmon/pkgtemp.c b/drivers/hwmon/pkgtemp.c index 74157fcda6e..f11903936c8 100644 --- a/drivers/hwmon/pkgtemp.c +++ b/drivers/hwmon/pkgtemp.c @@ -33,7 +33,6 @@ #include <linux/list.h> #include <linux/platform_device.h> #include <linux/cpu.h> -#include <linux/pci.h> #include <asm/msr.h> #include <asm/processor.h> @@ -224,7 +223,7 @@ static int __devinit pkgtemp_probe(struct platform_device *pdev) err = sysfs_create_group(&pdev->dev.kobj, &pkgtemp_group); if (err) - goto exit_free; + goto exit_dev; data->hwmon_dev = hwmon_device_register(&pdev->dev); if (IS_ERR(data->hwmon_dev)) { @@ -238,6 +237,8 @@ static int __devinit pkgtemp_probe(struct platform_device *pdev) exit_class: sysfs_remove_group(&pdev->dev.kobj, &pkgtemp_group); +exit_dev: + device_remove_file(&pdev->dev, &sensor_dev_attr_temp1_max.dev_attr); exit_free: kfree(data); exit: @@ -250,6 +251,7 @@ static int __devexit pkgtemp_remove(struct platform_device *pdev) hwmon_device_unregister(data->hwmon_dev); sysfs_remove_group(&pdev->dev.kobj, &pkgtemp_group); + device_remove_file(&pdev->dev, &sensor_dev_attr_temp1_max.dev_attr); platform_set_drvdata(pdev, NULL); kfree(data); return 0; @@ -281,9 +283,10 @@ static int __cpuinit pkgtemp_device_add(unsigned int cpu) int err; struct platform_device *pdev; struct pdev_entry *pdev_entry; -#ifdef CONFIG_SMP struct cpuinfo_x86 *c = &cpu_data(cpu); -#endif + + if (!cpu_has(c, X86_FEATURE_PTS)) + return 0; mutex_lock(&pdev_list_mutex); @@ -339,17 +342,18 @@ exit: #ifdef CONFIG_HOTPLUG_CPU static void pkgtemp_device_remove(unsigned int cpu) { - struct pdev_entry *p, *n; + struct pdev_entry *p; unsigned int i; int err; mutex_lock(&pdev_list_mutex); - list_for_each_entry_safe(p, n, &pdev_list, list) { + list_for_each_entry(p, &pdev_list, list) { if (p->cpu != cpu) continue; platform_device_unregister(p->pdev); list_del(&p->list); + mutex_unlock(&pdev_list_mutex); kfree(p); for_each_cpu(i, cpu_core_mask(cpu)) { if (i != cpu) { @@ -358,7 +362,7 @@ static void pkgtemp_device_remove(unsigned int cpu) break; } } - break; + return; } mutex_unlock(&pdev_list_mutex); } @@ -399,11 +403,6 @@ static int __init pkgtemp_init(void) goto exit; for_each_online_cpu(i) { - struct cpuinfo_x86 *c = &cpu_data(i); - - if (!cpu_has(c, X86_FEATURE_PTS)) - continue; - err = pkgtemp_device_add(i); if (err) goto exit_devices_unreg; diff --git a/drivers/staging/ti-st/st.h b/drivers/staging/ti-st/st.h index 9952579425b..1b3060eb292 100644 --- a/drivers/staging/ti-st/st.h +++ b/drivers/staging/ti-st/st.h @@ -80,5 +80,4 @@ struct st_proto_s { extern long st_register(struct st_proto_s *); extern long st_unregister(enum proto_type); -extern struct platform_device *st_get_plat_device(void); #endif /* ST_H */ diff --git a/drivers/staging/ti-st/st_core.c b/drivers/staging/ti-st/st_core.c index 063c9b1db1a..b85d8bfdf60 100644 --- a/drivers/staging/ti-st/st_core.c +++ b/drivers/staging/ti-st/st_core.c @@ -38,7 +38,6 @@ #include "st_ll.h" #include "st.h" -#define VERBOSE /* strings to be used for rfkill entries and by * ST Core to be used for sysfs debug entry */ @@ -581,7 +580,7 @@ long st_register(struct st_proto_s *new_proto) long err = 0; unsigned long flags = 0; - st_kim_ref(&st_gdata); + st_kim_ref(&st_gdata, 0); pr_info("%s(%d) ", __func__, new_proto->type); if (st_gdata == NULL || new_proto == NULL || new_proto->recv == NULL || new_proto->reg_complete_cb == NULL) { @@ -713,7 +712,7 @@ long st_unregister(enum proto_type type) pr_debug("%s: %d ", __func__, type); - st_kim_ref(&st_gdata); + st_kim_ref(&st_gdata, 0); if (type < ST_BT || type >= ST_MAX) { pr_err(" protocol %d not supported", type); return -EPROTONOSUPPORT; @@ -767,7 +766,7 @@ long st_write(struct sk_buff *skb) #endif long len; - st_kim_ref(&st_gdata); + st_kim_ref(&st_gdata, 0); if (unlikely(skb == NULL || st_gdata == NULL || st_gdata->tty == NULL)) { pr_err("data/tty unavailable to perform write"); @@ -818,7 +817,7 @@ static int st_tty_open(struct tty_struct *tty) struct st_data_s *st_gdata; pr_info("%s ", __func__); - st_kim_ref(&st_gdata); + st_kim_ref(&st_gdata, 0); st_gdata->tty = tty; tty->disc_data = st_gdata; diff --git a/drivers/staging/ti-st/st_core.h b/drivers/staging/ti-st/st_core.h index e0c32d149f5..8601320a679 100644 --- a/drivers/staging/ti-st/st_core.h +++ b/drivers/staging/ti-st/st_core.h @@ -117,7 +117,7 @@ int st_core_init(struct st_data_s **); void st_core_exit(struct st_data_s *); /* ask for reference from KIM */ -void st_kim_ref(struct st_data_s **); +void st_kim_ref(struct st_data_s **, int); #define GPS_STUB_TEST #ifdef GPS_STUB_TEST diff --git a/drivers/staging/ti-st/st_kim.c b/drivers/staging/ti-st/st_kim.c index b4a6c7fdc4e..9e99463f76e 100644 --- a/drivers/staging/ti-st/st_kim.c +++ b/drivers/staging/ti-st/st_kim.c @@ -72,11 +72,26 @@ const unsigned char *protocol_names[] = { PROTO_ENTRY(ST_GPS, "GPS"), }; +#define MAX_ST_DEVICES 3 /* Imagine 1 on each UART for now */ +struct platform_device *st_kim_devices[MAX_ST_DEVICES]; /**********************************************************************/ /* internal functions */ /** + * st_get_plat_device - + * function which returns the reference to the platform device + * requested by id. As of now only 1 such device exists (id=0) + * the context requesting for reference can get the id to be + * requested by a. The protocol driver which is registering or + * b. the tty device which is opened. + */ +static struct platform_device *st_get_plat_device(int id) +{ + return st_kim_devices[id]; +} + +/** * validate_firmware_response - * function to return whether the firmware response was proper * in case of error don't complete so that waiting for proper @@ -353,7 +368,7 @@ void st_kim_chip_toggle(enum proto_type type, enum kim_gpio_state state) struct kim_data_s *kim_gdata; pr_info(" %s ", __func__); - kim_pdev = st_get_plat_device(); + kim_pdev = st_get_plat_device(0); kim_gdata = dev_get_drvdata(&kim_pdev->dev); if (kim_gdata->gpios[type] == -1) { @@ -574,12 +589,12 @@ static int kim_toggle_radio(void *data, bool blocked) * This would enable multiple such platform devices to exist * on a given platform */ -void st_kim_ref(struct st_data_s **core_data) +void st_kim_ref(struct st_data_s **core_data, int id) { struct platform_device *pdev; struct kim_data_s *kim_gdata; /* get kim_gdata reference from platform device */ - pdev = st_get_plat_device(); + pdev = st_get_plat_device(id); kim_gdata = dev_get_drvdata(&pdev->dev); *core_data = kim_gdata->core_data; } @@ -623,6 +638,7 @@ static int kim_probe(struct platform_device *pdev) long *gpios = pdev->dev.platform_data; struct kim_data_s *kim_gdata; + st_kim_devices[pdev->id] = pdev; kim_gdata = kzalloc(sizeof(struct kim_data_s), GFP_ATOMIC); if (!kim_gdata) { pr_err("no mem to allocate"); diff --git a/drivers/usb/core/Kconfig b/drivers/usb/core/Kconfig index 7e594449600..9eed5b52d9d 100644 --- a/drivers/usb/core/Kconfig +++ b/drivers/usb/core/Kconfig @@ -91,12 +91,12 @@ config USB_DYNAMIC_MINORS If you are unsure about this, say N here. config USB_SUSPEND - bool "USB runtime power management (suspend/resume and wakeup)" + bool "USB runtime power management (autosuspend) and wakeup" depends on USB && PM_RUNTIME help If you say Y here, you can use driver calls or the sysfs - "power/level" file to suspend or resume individual USB - peripherals and to enable or disable autosuspend (see + "power/control" file to enable or disable autosuspend for + individual USB peripherals (see Documentation/usb/power-management.txt for more details). Also, USB "remote wakeup" signaling is supported, whereby some diff --git a/drivers/usb/core/file.c b/drivers/usb/core/file.c index f06f5dbc8cd..1e6ccef2cf0 100644 --- a/drivers/usb/core/file.c +++ b/drivers/usb/core/file.c @@ -159,9 +159,9 @@ void usb_major_cleanup(void) int usb_register_dev(struct usb_interface *intf, struct usb_class_driver *class_driver) { - int retval = -EINVAL; + int retval; int minor_base = class_driver->minor_base; - int minor = 0; + int minor; char name[20]; char *temp; @@ -173,12 +173,17 @@ int usb_register_dev(struct usb_interface *intf, */ minor_base = 0; #endif - intf->minor = -1; - - dbg ("looking for a minor, starting at %d", minor_base); if (class_driver->fops == NULL) - goto exit; + return -EINVAL; + if (intf->minor >= 0) + return -EADDRINUSE; + + retval = init_usb_class(); + if (retval) + return retval; + + dev_dbg(&intf->dev, "looking for a minor, starting at %d", minor_base); down_write(&minor_rwsem); for (minor = minor_base; minor < MAX_USB_MINORS; ++minor) { @@ -186,20 +191,12 @@ int usb_register_dev(struct usb_interface *intf, continue; usb_minors[minor] = class_driver->fops; - - retval = 0; + intf->minor = minor; break; } up_write(&minor_rwsem); - - if (retval) - goto exit; - - retval = init_usb_class(); - if (retval) - goto exit; - - intf->minor = minor; + if (intf->minor < 0) + return -EXFULL; /* create a usb class device for this usb interface */ snprintf(name, sizeof(name), class_driver->name, minor - minor_base); @@ -213,11 +210,11 @@ int usb_register_dev(struct usb_interface *intf, "%s", temp); if (IS_ERR(intf->usb_dev)) { down_write(&minor_rwsem); - usb_minors[intf->minor] = NULL; + usb_minors[minor] = NULL; + intf->minor = -1; up_write(&minor_rwsem); retval = PTR_ERR(intf->usb_dev); } -exit: return retval; } EXPORT_SYMBOL_GPL(usb_register_dev); diff --git a/drivers/usb/core/message.c b/drivers/usb/core/message.c index 844683e5038..9f0ce7de0e3 100644 --- a/drivers/usb/core/message.c +++ b/drivers/usb/core/message.c @@ -1802,6 +1802,7 @@ free_interfaces: intf->dev.groups = usb_interface_groups; intf->dev.dma_mask = dev->dev.dma_mask; INIT_WORK(&intf->reset_ws, __usb_queue_reset_device); + intf->minor = -1; device_initialize(&intf->dev); dev_set_name(&intf->dev, "%d-%s:%d.%d", dev->bus->busnum, dev->devpath, diff --git a/drivers/usb/musb/cppi_dma.c b/drivers/usb/musb/cppi_dma.c index 59dc3d351b6..5ab5bb89bae 100644 --- a/drivers/usb/musb/cppi_dma.c +++ b/drivers/usb/musb/cppi_dma.c @@ -322,6 +322,7 @@ cppi_channel_allocate(struct dma_controller *c, index, transmit ? 'T' : 'R', cppi_ch); cppi_ch->hw_ep = ep; cppi_ch->channel.status = MUSB_DMA_STATUS_FREE; + cppi_ch->channel.max_len = 0x7fffffff; DBG(4, "Allocate CPPI%d %cX\n", index, transmit ? 'T' : 'R'); return &cppi_ch->channel; diff --git a/drivers/usb/musb/musb_gadget.c b/drivers/usb/musb/musb_gadget.c index 6fca870e957..d065e23f123 100644 --- a/drivers/usb/musb/musb_gadget.c +++ b/drivers/usb/musb/musb_gadget.c @@ -300,6 +300,11 @@ static void txstate(struct musb *musb, struct musb_request *req) #ifndef CONFIG_MUSB_PIO_ONLY if (is_dma_capable() && musb_ep->dma) { struct dma_controller *c = musb->dma_controller; + size_t request_size; + + /* setup DMA, then program endpoint CSR */ + request_size = min_t(size_t, request->length - request->actual, + musb_ep->dma->max_len); use_dma = (request->dma != DMA_ADDR_INVALID); @@ -307,11 +312,6 @@ static void txstate(struct musb *musb, struct musb_request *req) #ifdef CONFIG_USB_INVENTRA_DMA { - size_t request_size; - - /* setup DMA, then program endpoint CSR */ - request_size = min_t(size_t, request->length, - musb_ep->dma->max_len); if (request_size < musb_ep->packet_sz) musb_ep->dma->desired_mode = 0; else @@ -373,8 +373,8 @@ static void txstate(struct musb *musb, struct musb_request *req) use_dma = use_dma && c->channel_program( musb_ep->dma, musb_ep->packet_sz, 0, - request->dma, - request->length); + request->dma + request->actual, + request_size); if (!use_dma) { c->channel_release(musb_ep->dma); musb_ep->dma = NULL; @@ -386,8 +386,8 @@ static void txstate(struct musb *musb, struct musb_request *req) use_dma = use_dma && c->channel_program( musb_ep->dma, musb_ep->packet_sz, request->zero, - request->dma, - request->length); + request->dma + request->actual, + request_size); #endif } #endif @@ -501,26 +501,14 @@ void musb_g_tx(struct musb *musb, u8 epnum) request->zero = 0; } - /* ... or if not, then complete it. */ - musb_g_giveback(musb_ep, request, 0); - - /* - * Kickstart next transfer if appropriate; - * the packet that just completed might not - * be transmitted for hours or days. - * REVISIT for double buffering... - * FIXME revisit for stalls too... - */ - musb_ep_select(mbase, epnum); - csr = musb_readw(epio, MUSB_TXCSR); - if (csr & MUSB_TXCSR_FIFONOTEMPTY) - return; - - request = musb_ep->desc ? next_request(musb_ep) : NULL; - if (!request) { - DBG(4, "%s idle now\n", - musb_ep->end_point.name); - return; + if (request->actual == request->length) { + musb_g_giveback(musb_ep, request, 0); + request = musb_ep->desc ? next_request(musb_ep) : NULL; + if (!request) { + DBG(4, "%s idle now\n", + musb_ep->end_point.name); + return; + } } } @@ -568,11 +556,19 @@ static void rxstate(struct musb *musb, struct musb_request *req) { const u8 epnum = req->epnum; struct usb_request *request = &req->request; - struct musb_ep *musb_ep = &musb->endpoints[epnum].ep_out; + struct musb_ep *musb_ep; void __iomem *epio = musb->endpoints[epnum].regs; unsigned fifo_count = 0; - u16 len = musb_ep->packet_sz; + u16 len; u16 csr = musb_readw(epio, MUSB_RXCSR); + struct musb_hw_ep *hw_ep = &musb->endpoints[epnum]; + + if (hw_ep->is_shared_fifo) + musb_ep = &hw_ep->ep_in; + else + musb_ep = &hw_ep->ep_out; + + len = musb_ep->packet_sz; /* We shouldn't get here while DMA is active, but we do... */ if (dma_channel_status(musb_ep->dma) == MUSB_DMA_STATUS_BUSY) { @@ -647,8 +643,8 @@ static void rxstate(struct musb *musb, struct musb_request *req) */ csr |= MUSB_RXCSR_DMAENAB; -#ifdef USE_MODE1 csr |= MUSB_RXCSR_AUTOCLEAR; +#ifdef USE_MODE1 /* csr |= MUSB_RXCSR_DMAMODE; */ /* this special sequence (enabling and then @@ -663,10 +659,11 @@ static void rxstate(struct musb *musb, struct musb_request *req) if (request->actual < request->length) { int transfer_size = 0; #ifdef USE_MODE1 - transfer_size = min(request->length, + transfer_size = min(request->length - request->actual, channel->max_len); #else - transfer_size = len; + transfer_size = min(request->length - request->actual, + (unsigned)len); #endif if (transfer_size <= musb_ep->packet_sz) musb_ep->dma->desired_mode = 0; @@ -740,9 +737,15 @@ void musb_g_rx(struct musb *musb, u8 epnum) u16 csr; struct usb_request *request; void __iomem *mbase = musb->mregs; - struct musb_ep *musb_ep = &musb->endpoints[epnum].ep_out; + struct musb_ep *musb_ep; void __iomem *epio = musb->endpoints[epnum].regs; struct dma_channel *dma; + struct musb_hw_ep *hw_ep = &musb->endpoints[epnum]; + + if (hw_ep->is_shared_fifo) + musb_ep = &hw_ep->ep_in; + else + musb_ep = &hw_ep->ep_out; musb_ep_select(mbase, epnum); @@ -1081,7 +1084,7 @@ struct free_record { /* * Context: controller locked, IRQs blocked. */ -static void musb_ep_restart(struct musb *musb, struct musb_request *req) +void musb_ep_restart(struct musb *musb, struct musb_request *req) { DBG(3, "<== %s request %p len %u on hw_ep%d\n", req->tx ? "TX/IN" : "RX/OUT", diff --git a/drivers/usb/musb/musb_gadget.h b/drivers/usb/musb/musb_gadget.h index c8b140325d8..572b1da7f2d 100644 --- a/drivers/usb/musb/musb_gadget.h +++ b/drivers/usb/musb/musb_gadget.h @@ -105,4 +105,6 @@ extern void musb_gadget_cleanup(struct musb *); extern void musb_g_giveback(struct musb_ep *, struct usb_request *, int); +extern void musb_ep_restart(struct musb *, struct musb_request *); + #endif /* __MUSB_GADGET_H */ diff --git a/drivers/usb/musb/musb_gadget_ep0.c b/drivers/usb/musb/musb_gadget_ep0.c index 59bef8f3a35..6dd03f4c5f4 100644 --- a/drivers/usb/musb/musb_gadget_ep0.c +++ b/drivers/usb/musb/musb_gadget_ep0.c @@ -261,6 +261,7 @@ __acquires(musb->lock) ctrlrequest->wIndex & 0x0f; struct musb_ep *musb_ep; struct musb_hw_ep *ep; + struct musb_request *request; void __iomem *regs; int is_in; u16 csr; @@ -302,6 +303,14 @@ __acquires(musb->lock) musb_writew(regs, MUSB_RXCSR, csr); } + /* Maybe start the first request in the queue */ + request = to_musb_request( + next_request(musb_ep)); + if (!musb_ep->busy && request) { + DBG(3, "restarting the request\n"); + musb_ep_restart(musb, request); + } + /* select ep0 again */ musb_ep_select(mbase, 0); } break; diff --git a/drivers/usb/musb/musb_host.c b/drivers/usb/musb/musb_host.c index 877d20b1dff..9e65c47cc98 100644 --- a/drivers/usb/musb/musb_host.c +++ b/drivers/usb/musb/musb_host.c @@ -660,6 +660,12 @@ static bool musb_tx_dma_program(struct dma_controller *dma, qh->segsize = length; + /* + * Ensure the data reaches to main memory before starting + * DMA transfer + */ + wmb(); + if (!dma->channel_program(channel, pkt_size, mode, urb->transfer_dma + offset, length)) { dma->channel_release(channel); |