diff options
155 files changed, 19072 insertions, 5311 deletions
diff --git a/MAINTAINERS b/MAINTAINERS index 7071633..90e7e22 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -2373,10 +2373,10 @@ F: drivers/gpu/drm/ F: include/drm/ INTEL DRM DRIVERS (excluding Poulsbo, Moorestown and derivative chipsets) -M: Keith Packard <keithp@keithp.com> +M: Daniel Vetter <daniel.vetter@ffwll.ch> L: intel-gfx@lists.freedesktop.org (subscribers-only) L: dri-devel@lists.freedesktop.org -T: git git://git.kernel.org/pub/scm/linux/kernel/git/keithp/linux.git +T: git git://people.freedesktop.org/~danvet/drm-intel S: Supported F: drivers/gpu/drm/i915 F: include/drm/i915* diff --git a/arch/x86/pci/fixup.c b/arch/x86/pci/fixup.c index d0e6e40..82487d3 100644 --- a/arch/x86/pci/fixup.c +++ b/arch/x86/pci/fixup.c @@ -6,6 +6,7 @@ #include <linux/dmi.h> #include <linux/pci.h> #include <linux/init.h> +#include <linux/vgaarb.h> #include <asm/pci_x86.h> static void __devinit pci_fixup_i450nx(struct pci_dev *d) @@ -348,6 +349,8 @@ static void __devinit pci_fixup_video(struct pci_dev *pdev) if (config & (PCI_COMMAND_IO | PCI_COMMAND_MEMORY)) { pdev->resource[PCI_ROM_RESOURCE].flags |= IORESOURCE_ROM_SHADOW; dev_printk(KERN_DEBUG, &pdev->dev, "Boot video device\n"); + if (!vga_default_device()) + vga_set_default_device(pdev); } } DECLARE_PCI_FIXUP_CLASS_FINAL(PCI_ANY_ID, PCI_ANY_ID, diff --git a/drivers/char/agp/intel-agp.c b/drivers/char/agp/intel-agp.c index 74c2d92..764f70c 100644 --- a/drivers/char/agp/intel-agp.c +++ b/drivers/char/agp/intel-agp.c @@ -908,6 +908,10 @@ static struct pci_device_id agp_intel_pci_table[] = { ID(PCI_DEVICE_ID_INTEL_IVYBRIDGE_M_HB), ID(PCI_DEVICE_ID_INTEL_IVYBRIDGE_S_HB), ID(PCI_DEVICE_ID_INTEL_VALLEYVIEW_HB), + ID(PCI_DEVICE_ID_INTEL_HASWELL_HB), + ID(PCI_DEVICE_ID_INTEL_HASWELL_M_HB), + ID(PCI_DEVICE_ID_INTEL_HASWELL_S_HB), + ID(PCI_DEVICE_ID_INTEL_HASWELL_E_HB), { } }; diff --git a/drivers/gpu/drm/Kconfig b/drivers/gpu/drm/Kconfig index e354bc0..23120c0 100644 --- a/drivers/gpu/drm/Kconfig +++ b/drivers/gpu/drm/Kconfig @@ -186,3 +186,9 @@ source "drivers/gpu/drm/vmwgfx/Kconfig" source "drivers/gpu/drm/gma500/Kconfig" source "drivers/gpu/drm/udl/Kconfig" + +source "drivers/gpu/drm/ast/Kconfig" + +source "drivers/gpu/drm/mgag200/Kconfig" + +source "drivers/gpu/drm/cirrus/Kconfig" diff --git a/drivers/gpu/drm/Makefile b/drivers/gpu/drm/Makefile index c20da5b..f65f65e 100644 --- a/drivers/gpu/drm/Makefile +++ b/drivers/gpu/drm/Makefile @@ -34,6 +34,8 @@ obj-$(CONFIG_DRM_RADEON)+= radeon/ obj-$(CONFIG_DRM_MGA) += mga/ obj-$(CONFIG_DRM_I810) += i810/ obj-$(CONFIG_DRM_I915) += i915/ +obj-$(CONFIG_DRM_MGAG200) += mgag200/ +obj-$(CONFIG_DRM_CIRRUS_QEMU) += cirrus/ obj-$(CONFIG_DRM_SIS) += sis/ obj-$(CONFIG_DRM_SAVAGE)+= savage/ obj-$(CONFIG_DRM_VMWGFX)+= vmwgfx/ @@ -42,4 +44,5 @@ obj-$(CONFIG_DRM_NOUVEAU) +=nouveau/ obj-$(CONFIG_DRM_EXYNOS) +=exynos/ obj-$(CONFIG_DRM_GMA500) += gma500/ obj-$(CONFIG_DRM_UDL) += udl/ +obj-$(CONFIG_DRM_AST) += ast/ obj-y += i2c/ diff --git a/drivers/gpu/drm/ast/Kconfig b/drivers/gpu/drm/ast/Kconfig new file mode 100644 index 0000000..a277b12 --- /dev/null +++ b/drivers/gpu/drm/ast/Kconfig @@ -0,0 +1,16 @@ +config DRM_AST + tristate "AST server chips" + depends on DRM && PCI && EXPERIMENTAL + select DRM_TTM + select FB_SYS_COPYAREA + select FB_SYS_FILLRECT + select FB_SYS_IMAGEBLIT + select DRM_KMS_HELPER + select DRM_TTM + help + Say yes for experimental AST GPU driver. Do not enable + this driver without having a working -modesetting, + and a version of AST that knows to fail if KMS + is bound to the driver. These GPUs are commonly found + in server chipsets. + diff --git a/drivers/gpu/drm/ast/Makefile b/drivers/gpu/drm/ast/Makefile new file mode 100644 index 0000000..8df4f28 --- /dev/null +++ b/drivers/gpu/drm/ast/Makefile @@ -0,0 +1,9 @@ +# +# Makefile for the drm device driver. This driver provides support for the +# Direct Rendering Infrastructure (DRI) in XFree86 4.1.0 and higher. + +ccflags-y := -Iinclude/drm + +ast-y := ast_drv.o ast_main.o ast_mode.o ast_fb.o ast_ttm.o ast_post.o + +obj-$(CONFIG_DRM_AST) := ast.o
\ No newline at end of file diff --git a/drivers/gpu/drm/ast/ast_dram_tables.h b/drivers/gpu/drm/ast/ast_dram_tables.h new file mode 100644 index 0000000..cc04539 --- /dev/null +++ b/drivers/gpu/drm/ast/ast_dram_tables.h @@ -0,0 +1,144 @@ +#ifndef AST_DRAM_TABLES_H +#define AST_DRAM_TABLES_H + +/* DRAM timing tables */ +struct ast_dramstruct { + u16 index; + u32 data; +}; + +static const struct ast_dramstruct ast2000_dram_table_data[] = { + { 0x0108, 0x00000000 }, + { 0x0120, 0x00004a21 }, + { 0xFF00, 0x00000043 }, + { 0x0000, 0xFFFFFFFF }, + { 0x0004, 0x00000089 }, + { 0x0008, 0x22331353 }, + { 0x000C, 0x0d07000b }, + { 0x0010, 0x11113333 }, + { 0x0020, 0x00110350 }, + { 0x0028, 0x1e0828f0 }, + { 0x0024, 0x00000001 }, + { 0x001C, 0x00000000 }, + { 0x0014, 0x00000003 }, + { 0xFF00, 0x00000043 }, + { 0x0018, 0x00000131 }, + { 0x0014, 0x00000001 }, + { 0xFF00, 0x00000043 }, + { 0x0018, 0x00000031 }, + { 0x0014, 0x00000001 }, + { 0xFF00, 0x00000043 }, + { 0x0028, 0x1e0828f1 }, + { 0x0024, 0x00000003 }, + { 0x002C, 0x1f0f28fb }, + { 0x0030, 0xFFFFFE01 }, + { 0xFFFF, 0xFFFFFFFF } +}; + +static const struct ast_dramstruct ast1100_dram_table_data[] = { + { 0x2000, 0x1688a8a8 }, + { 0x2020, 0x000041f0 }, + { 0xFF00, 0x00000043 }, + { 0x0000, 0xfc600309 }, + { 0x006C, 0x00909090 }, + { 0x0064, 0x00050000 }, + { 0x0004, 0x00000585 }, + { 0x0008, 0x0011030f }, + { 0x0010, 0x22201724 }, + { 0x0018, 0x1e29011a }, + { 0x0020, 0x00c82222 }, + { 0x0014, 0x01001523 }, + { 0x001C, 0x1024010d }, + { 0x0024, 0x00cb2522 }, + { 0x0038, 0xffffff82 }, + { 0x003C, 0x00000000 }, + { 0x0040, 0x00000000 }, + { 0x0044, 0x00000000 }, + { 0x0048, 0x00000000 }, + { 0x004C, 0x00000000 }, + { 0x0050, 0x00000000 }, + { 0x0054, 0x00000000 }, + { 0x0058, 0x00000000 }, + { 0x005C, 0x00000000 }, + { 0x0060, 0x032aa02a }, + { 0x0064, 0x002d3000 }, + { 0x0068, 0x00000000 }, + { 0x0070, 0x00000000 }, + { 0x0074, 0x00000000 }, + { 0x0078, 0x00000000 }, + { 0x007C, 0x00000000 }, + { 0x0034, 0x00000001 }, + { 0xFF00, 0x00000043 }, + { 0x002C, 0x00000732 }, + { 0x0030, 0x00000040 }, + { 0x0028, 0x00000005 }, + { 0x0028, 0x00000007 }, + { 0x0028, 0x00000003 }, + { 0x0028, 0x00000001 }, + { 0x000C, 0x00005a08 }, + { 0x002C, 0x00000632 }, + { 0x0028, 0x00000001 }, + { 0x0030, 0x000003c0 }, + { 0x0028, 0x00000003 }, + { 0x0030, 0x00000040 }, + { 0x0028, 0x00000003 }, + { 0x000C, 0x00005a21 }, + { 0x0034, 0x00007c03 }, + { 0x0120, 0x00004c41 }, + { 0xffff, 0xffffffff }, +}; + +static const struct ast_dramstruct ast2100_dram_table_data[] = { + { 0x2000, 0x1688a8a8 }, + { 0x2020, 0x00004120 }, + { 0xFF00, 0x00000043 }, + { 0x0000, 0xfc600309 }, + { 0x006C, 0x00909090 }, + { 0x0064, 0x00070000 }, + { 0x0004, 0x00000489 }, + { 0x0008, 0x0011030f }, + { 0x0010, 0x32302926 }, + { 0x0018, 0x274c0122 }, + { 0x0020, 0x00ce2222 }, + { 0x0014, 0x01001523 }, + { 0x001C, 0x1024010d }, + { 0x0024, 0x00cb2522 }, + { 0x0038, 0xffffff82 }, + { 0x003C, 0x00000000 }, + { 0x0040, 0x00000000 }, + { 0x0044, 0x00000000 }, + { 0x0048, 0x00000000 }, + { 0x004C, 0x00000000 }, + { 0x0050, 0x00000000 }, + { 0x0054, 0x00000000 }, + { 0x0058, 0x00000000 }, + { 0x005C, 0x00000000 }, + { 0x0060, 0x0f2aa02a }, + { 0x0064, 0x003f3005 }, + { 0x0068, 0x02020202 }, + { 0x0070, 0x00000000 }, + { 0x0074, 0x00000000 }, + { 0x0078, 0x00000000 }, + { 0x007C, 0x00000000 }, + { 0x0034, 0x00000001 }, + { 0xFF00, 0x00000043 }, + { 0x002C, 0x00000942 }, + { 0x0030, 0x00000040 }, + { 0x0028, 0x00000005 }, + { 0x0028, 0x00000007 }, + { 0x0028, 0x00000003 }, + { 0x0028, 0x00000001 }, + { 0x000C, 0x00005a08 }, + { 0x002C, 0x00000842 }, + { 0x0028, 0x00000001 }, + { 0x0030, 0x000003c0 }, + { 0x0028, 0x00000003 }, + { 0x0030, 0x00000040 }, + { 0x0028, 0x00000003 }, + { 0x000C, 0x00005a21 }, + { 0x0034, 0x00007c03 }, + { 0x0120, 0x00005061 }, + { 0xffff, 0xffffffff }, +}; + +#endif diff --git a/drivers/gpu/drm/ast/ast_drv.c b/drivers/gpu/drm/ast/ast_drv.c new file mode 100644 index 0000000..d0c4574 --- /dev/null +++ b/drivers/gpu/drm/ast/ast_drv.c @@ -0,0 +1,244 @@ +/* + * Copyright 2012 Red Hat Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sub license, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDERS, AUTHORS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, + * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR + * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE + * USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * The above copyright notice and this permission notice (including the + * next paragraph) shall be included in all copies or substantial portions + * of the Software. + * + */ +/* + * Authors: Dave Airlie <airlied@redhat.com> + */ +#include <linux/module.h> +#include <linux/console.h> + +#include "drmP.h" +#include "drm.h" +#include "drm_crtc_helper.h" + +#include "ast_drv.h" + +int ast_modeset = -1; + +MODULE_PARM_DESC(modeset, "Disable/Enable modesetting"); +module_param_named(modeset, ast_modeset, int, 0400); + +#define PCI_VENDOR_ASPEED 0x1a03 + +static struct drm_driver driver; + +#define AST_VGA_DEVICE(id, info) { \ + .class = PCI_BASE_CLASS_DISPLAY << 16, \ + .class_mask = 0xff0000, \ + .vendor = PCI_VENDOR_ASPEED, \ + .device = id, \ + .subvendor = PCI_ANY_ID, \ + .subdevice = PCI_ANY_ID, \ + .driver_data = (unsigned long) info } + +static DEFINE_PCI_DEVICE_TABLE(pciidlist) = { + AST_VGA_DEVICE(PCI_CHIP_AST2000, NULL), + AST_VGA_DEVICE(PCI_CHIP_AST2100, NULL), + /* AST_VGA_DEVICE(PCI_CHIP_AST1180, NULL), - don't bind to 1180 for now */ + {0, 0, 0}, +}; + +MODULE_DEVICE_TABLE(pci, pciidlist); + +static int __devinit +ast_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent) +{ + return drm_get_pci_dev(pdev, ent, &driver); +} + +static void +ast_pci_remove(struct pci_dev *pdev) +{ + struct drm_device *dev = pci_get_drvdata(pdev); + + drm_put_dev(dev); +} + + + +static int ast_drm_freeze(struct drm_device *dev) +{ + drm_kms_helper_poll_disable(dev); + + pci_save_state(dev->pdev); + + console_lock(); + ast_fbdev_set_suspend(dev, 1); + console_unlock(); + return 0; +} + +static int ast_drm_thaw(struct drm_device *dev) +{ + int error = 0; + + ast_post_gpu(dev); + + drm_mode_config_reset(dev); + mutex_lock(&dev->mode_config.mutex); + drm_helper_resume_force_mode(dev); + mutex_unlock(&dev->mode_config.mutex); + + console_lock(); + ast_fbdev_set_suspend(dev, 0); + console_unlock(); + return error; +} + +static int ast_drm_resume(struct drm_device *dev) +{ + int ret; + + if (pci_enable_device(dev->pdev)) + return -EIO; + + ret = ast_drm_thaw(dev); + if (ret) + return ret; + + drm_kms_helper_poll_enable(dev); + return 0; +} + +static int ast_pm_suspend(struct device *dev) +{ + struct pci_dev *pdev = to_pci_dev(dev); + struct drm_device *ddev = pci_get_drvdata(pdev); + int error; + + error = ast_drm_freeze(ddev); + if (error) + return error; + + pci_disable_device(pdev); + pci_set_power_state(pdev, PCI_D3hot); + return 0; +} +static int ast_pm_resume(struct device *dev) +{ + struct pci_dev *pdev = to_pci_dev(dev); + struct drm_device *ddev = pci_get_drvdata(pdev); + return ast_drm_resume(ddev); +} + +static int ast_pm_freeze(struct device *dev) +{ + struct pci_dev *pdev = to_pci_dev(dev); + struct drm_device *ddev = pci_get_drvdata(pdev); + + if (!ddev || !ddev->dev_private) + return -ENODEV; + return ast_drm_freeze(ddev); + +} + +static int ast_pm_thaw(struct device *dev) +{ + struct pci_dev *pdev = to_pci_dev(dev); + struct drm_device *ddev = pci_get_drvdata(pdev); + return ast_drm_thaw(ddev); +} + +static int ast_pm_poweroff(struct device *dev) +{ + struct pci_dev *pdev = to_pci_dev(dev); + struct drm_device *ddev = pci_get_drvdata(pdev); + + return ast_drm_freeze(ddev); +} + +static const struct dev_pm_ops ast_pm_ops = { + .suspend = ast_pm_suspend, + .resume = ast_pm_resume, + .freeze = ast_pm_freeze, + .thaw = ast_pm_thaw, + .poweroff = ast_pm_poweroff, + .restore = ast_pm_resume, +}; + +static struct pci_driver ast_pci_driver = { + .name = DRIVER_NAME, + .id_table = pciidlist, + .probe = ast_pci_probe, + .remove = ast_pci_remove, + .driver.pm = &ast_pm_ops, +}; + +static const struct file_operations ast_fops = { + .owner = THIS_MODULE, + .open = drm_open, + .release = drm_release, + .unlocked_ioctl = drm_ioctl, + .mmap = ast_mmap, + .poll = drm_poll, + .fasync = drm_fasync, + .read = drm_read, +}; + +static struct drm_driver driver = { + .driver_features = DRIVER_USE_MTRR | DRIVER_MODESET | DRIVER_GEM, + .dev_priv_size = 0, + + .load = ast_driver_load, + .unload = ast_driver_unload, + + .fops = &ast_fops, + .name = DRIVER_NAME, + .desc = DRIVER_DESC, + .date = DRIVER_DATE, + .major = DRIVER_MAJOR, + .minor = DRIVER_MINOR, + .patchlevel = DRIVER_PATCHLEVEL, + + .gem_init_object = ast_gem_init_object, + .gem_free_object = ast_gem_free_object, + .dumb_create = ast_dumb_create, + .dumb_map_offset = ast_dumb_mmap_offset, + .dumb_destroy = ast_dumb_destroy, + +}; + +static int __init ast_init(void) +{ +#ifdef CONFIG_VGA_CONSOLE + if (vgacon_text_force() && ast_modeset == -1) + return -EINVAL; +#endif + + if (ast_modeset == 0) + return -EINVAL; + return drm_pci_init(&driver, &ast_pci_driver); +} +static void __exit ast_exit(void) +{ + drm_pci_exit(&driver, &ast_pci_driver); +} + +module_init(ast_init); +module_exit(ast_exit); + +MODULE_AUTHOR(DRIVER_AUTHOR); +MODULE_DESCRIPTION(DRIVER_DESC); +MODULE_LICENSE("GPL and additional rights"); + diff --git a/drivers/gpu/drm/ast/ast_drv.h b/drivers/gpu/drm/ast/ast_drv.h new file mode 100644 index 0000000..d4af9ed --- /dev/null +++ b/drivers/gpu/drm/ast/ast_drv.h @@ -0,0 +1,356 @@ +/* + * Copyright 2012 Red Hat Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sub license, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDERS, AUTHORS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, + * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR + * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE + * USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * The above copyright notice and this permission notice (including the + * next paragraph) shall be included in all copies or substantial portions + * of the Software. + * + */ +/* + * Authors: Dave Airlie <airlied@redhat.com> + */ +#ifndef __AST_DRV_H__ +#define __AST_DRV_H__ + +#include "drm_fb_helper.h" + +#include "ttm/ttm_bo_api.h" +#include "ttm/ttm_bo_driver.h" +#include "ttm/ttm_placement.h" +#include "ttm/ttm_memory.h" +#include "ttm/ttm_module.h" + +#include <linux/i2c.h> +#include <linux/i2c-algo-bit.h> + +#define DRIVER_AUTHOR "Dave Airlie" + +#define DRIVER_NAME "ast" +#define DRIVER_DESC "AST" +#define DRIVER_DATE "20120228" + +#define DRIVER_MAJOR 0 +#define DRIVER_MINOR 1 +#define DRIVER_PATCHLEVEL 0 + +#define PCI_CHIP_AST2000 0x2000 +#define PCI_CHIP_AST2100 0x2010 +#define PCI_CHIP_AST1180 0x1180 + + +enum ast_chip { + AST2000, + AST2100, + AST1100, + AST2200, + AST2150, + AST2300, + AST1180, +}; + +#define AST_DRAM_512Mx16 0 +#define AST_DRAM_1Gx16 1 +#define AST_DRAM_512Mx32 2 +#define AST_DRAM_1Gx32 3 +#define AST_DRAM_2Gx16 6 +#define AST_DRAM_4Gx16 7 + +struct ast_fbdev; + +struct ast_private { + struct drm_device *dev; + + void __iomem *regs; + void __iomem *ioregs; + + enum ast_chip chip; + bool vga2_clone; + uint32_t dram_bus_width; + uint32_t dram_type; + uint32_t mclk; + uint32_t vram_size; + + struct ast_fbdev *fbdev; + + int fb_mtrr; + + struct { + struct drm_global_reference mem_global_ref; + struct ttm_bo_global_ref bo_global_ref; + struct ttm_bo_device bdev; + atomic_t validate_sequence; + } ttm; + + struct drm_gem_object *cursor_cache; + uint64_t cursor_cache_gpu_addr; + struct ttm_bo_kmap_obj cache_kmap; + int next_cursor; +}; + +int ast_driver_load(struct drm_device *dev, unsigned long flags); +int ast_driver_unload(struct drm_device *dev); + +struct ast_gem_object; + +#define AST_IO_AR_PORT_WRITE (0x40) +#define AST_IO_MISC_PORT_WRITE (0x42) +#define AST_IO_SEQ_PORT (0x44) +#define AST_DAC_INDEX_READ (0x3c7) +#define AST_IO_DAC_INDEX_WRITE (0x48) +#define AST_IO_DAC_DATA (0x49) +#define AST_IO_GR_PORT (0x4E) +#define AST_IO_CRTC_PORT (0x54) +#define AST_IO_INPUT_STATUS1_READ (0x5A) +#define AST_IO_MISC_PORT_READ (0x4C) + +#define __ast_read(x) \ +static inline u##x ast_read##x(struct ast_private *ast, u32 reg) { \ +u##x val = 0;\ +val = ioread##x(ast->regs + reg); \ +return val;\ +} + +__ast_read(8); +__ast_read(16); +__ast_read(32) + +#define __ast_io_read(x) \ +static inline u##x ast_io_read##x(struct ast_private *ast, u32 reg) { \ +u##x val = 0;\ +val = ioread##x(ast->ioregs + reg); \ +return val;\ +} + +__ast_io_read(8); +__ast_io_read(16); +__ast_io_read(32); + +#define __ast_write(x) \ +static inline void ast_write##x(struct ast_private *ast, u32 reg, u##x val) {\ + iowrite##x(val, ast->regs + reg);\ + } + +__ast_write(8); +__ast_write(16); +__ast_write(32); + +#define __ast_io_write(x) \ +static inline void ast_io_write##x(struct ast_private *ast, u32 reg, u##x val) {\ + iowrite##x(val, ast->ioregs + reg);\ + } + +__ast_io_write(8); +__ast_io_write(16); +#undef __ast_io_write + +static inline void ast_set_index_reg(struct ast_private *ast, + uint32_t base, uint8_t index, + uint8_t val) +{ + ast_io_write16(ast, base, ((u16)val << 8) | index); +} + +void ast_set_index_reg_mask(struct ast_private *ast, + uint32_t base, uint8_t index, + uint8_t mask, uint8_t val); +uint8_t ast_get_index_reg(struct ast_private *ast, + uint32_t base, uint8_t index); +uint8_t ast_get_index_reg_mask(struct ast_private *ast, + uint32_t base, uint8_t index, uint8_t mask); + +static inline void ast_open_key(struct ast_private *ast) +{ + ast_set_index_reg_mask(ast, AST_IO_CRTC_PORT, 0xA1, 0xFF, 0x04); +} + +#define AST_VIDMEM_SIZE_8M 0x00800000 +#define AST_VIDMEM_SIZE_16M 0x01000000 +#define AST_VIDMEM_SIZE_32M 0x02000000 +#define AST_VIDMEM_SIZE_64M 0x04000000 +#define AST_VIDMEM_SIZE_128M 0x08000000 + +#define AST_VIDMEM_DEFAULT_SIZE AST_VIDMEM_SIZE_8M + +#define AST_MAX_HWC_WIDTH 64 +#define AST_MAX_HWC_HEIGHT 64 + +#define AST_HWC_SIZE (AST_MAX_HWC_WIDTH*AST_MAX_HWC_HEIGHT*2) +#define AST_HWC_SIGNATURE_SIZE 32 + +#define AST_DEFAULT_HWC_NUM 2 +/* define for signature structure */ +#define AST_HWC_SIGNATURE_CHECKSUM 0x00 +#define AST_HWC_SIGNATURE_SizeX 0x04 +#define AST_HWC_SIGNATURE_SizeY 0x08 +#define AST_HWC_SIGNATURE_X 0x0C +#define AST_HWC_SIGNATURE_Y 0x10 +#define AST_HWC_SIGNATURE_HOTSPOTX 0x14 +#define AST_HWC_SIGNATURE_HOTSPOTY 0x18 + + +struct ast_i2c_chan { + struct i2c_adapter adapter; + struct drm_device *dev; + struct i2c_algo_bit_data bit; +}; + +struct ast_connector { + struct drm_connector base; + struct ast_i2c_chan *i2c; +}; + +struct ast_crtc { + struct drm_crtc base; + u8 lut_r[256], lut_g[256], lut_b[256]; + struct drm_gem_object *cursor_bo; + uint64_t cursor_addr; + int cursor_width, cursor_height; + u8 offset_x, offset_y; +}; + +struct ast_encoder { + struct drm_encoder base; +}; + +struct ast_framebuffer { + struct drm_framebuffer base; + struct drm_gem_object *obj; +}; + +struct ast_fbdev { + struct drm_fb_helper helper; + struct ast_framebuffer afb; + struct list_head fbdev_list; + void *sysram; + int size; + struct ttm_bo_kmap_obj mapping; +}; + +#define to_ast_crtc(x) container_of(x, struct ast_crtc, base) +#define to_ast_connector(x) container_of(x, struct ast_connector, base) +#define to_ast_encoder(x) container_of(x, struct ast_encoder, base) +#define to_ast_framebuffer(x) container_of(x, struct ast_framebuffer, base) + +struct ast_vbios_stdtable { + u8 misc; + u8 seq[4]; + u8 crtc[25]; + u8 ar[20]; + u8 gr[9]; +}; + +struct ast_vbios_enhtable { + u32 ht; + u32 hde; + u32 hfp; + u32 hsync; + u32 vt; + u32 vde; + u32 vfp; + u32 vsync; + u32 dclk_index; + u32 flags; + u32 refresh_rate; + u32 refresh_rate_index; + u32 mode_id; +}; + +struct ast_vbios_dclk_info { + u8 param1; + u8 param2; + u8 param3; +}; + +struct ast_vbios_mode_info { + struct ast_vbios_stdtable *std_table; + struct ast_vbios_enhtable *enh_table; +}; + +extern int ast_mode_init(struct drm_device *dev); +extern void ast_mode_fini(struct drm_device *dev); + +int ast_framebuffer_init(struct drm_device *dev, + struct ast_framebuffer *ast_fb, + struct drm_mode_fb_cmd2 *mode_cmd, + struct drm_gem_object *obj); + +int ast_fbdev_init(struct drm_device *dev); +void ast_fbdev_fini(struct drm_device *dev); +void ast_fbdev_set_suspend(struct drm_device *dev, int state); + +struct ast_bo { + struct ttm_buffer_object bo; + struct ttm_placement placement; + struct ttm_bo_kmap_obj kmap; + struct drm_gem_object gem; + u32 placements[3]; + int pin_count; +}; +#define gem_to_ast_bo(gobj) container_of((gobj), struct ast_bo, gem) + +static inline struct ast_bo * +ast_bo(struct ttm_buffer_object *bo) +{ + return container_of(bo, struct ast_bo, bo); +} + + +#define to_ast_obj(x) container_of(x, struct ast_gem_object, base) + +#define AST_MM_ALIGN_SHIFT 4 +#define AST_MM_ALIGN_MASK ((1 << AST_MM_ALIGN_SHIFT) - 1) + +extern int ast_dumb_create(struct drm_file *file, + struct drm_device *dev, + struct drm_mode_create_dumb *args); +extern int ast_dumb_destroy(struct drm_file *file, + struct drm_device *dev, + uint32_t handle); + +extern int ast_gem_init_object(struct drm_gem_object *obj); +extern void ast_gem_free_object(struct drm_gem_object *obj); +extern int ast_dumb_mmap_offset(struct drm_file *file, + struct drm_device *dev, + uint32_t handle, + uint64_t *offset); + +#define DRM_FILE_PAGE_OFFSET (0x100000000ULL >> PAGE_SHIFT) + +int ast_mm_init(struct ast_private *ast); +void ast_mm_fini(struct ast_private *ast); + +int ast_bo_create(struct drm_device *dev, int size, int align, + uint32_t flags, struct ast_bo **pastbo); + +int ast_gem_create(struct drm_device *dev, + u32 size, bool iskernel, + struct drm_gem_object **obj); + +int ast_bo_pin(struct ast_bo *bo, u32 pl_flag, u64 *gpu_addr); +int ast_bo_unpin(struct ast_bo *bo); + +int ast_bo_reserve(struct ast_bo *bo, bool no_wait); +void ast_bo_unreserve(struct ast_bo *bo); +void ast_ttm_placement(struct ast_bo *bo, int domain); +int ast_bo_push_sysram(struct ast_bo *bo); +int ast_mmap(struct file *filp, struct vm_area_struct *vma); + +/* ast post */ +void ast_post_gpu(struct drm_device *dev); +#endif diff --git a/drivers/gpu/drm/ast/ast_fb.c b/drivers/gpu/drm/ast/ast_fb.c new file mode 100644 index 0000000..2fc8e9e --- /dev/null +++ b/drivers/gpu/drm/ast/ast_fb.c @@ -0,0 +1,341 @@ +/* + * Copyright 2012 Red Hat Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sub license, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDERS, AUTHORS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, + * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR + * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE + * USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * The above copyright notice and this permission notice (including the + * next paragraph) shall be included in all copies or substantial portions + * of the Software. + * + */ +/* + * Authors: Dave Airlie <airlied@redhat.com> + */ +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/errno.h> +#include <linux/string.h> +#include <linux/mm.h> +#include <linux/tty.h> +#include <linux/sysrq.h> +#include <linux/delay.h> +#include <linux/fb.h> +#include <linux/init.h> + + +#include "drmP.h" +#include "drm.h" +#include "drm_crtc.h" +#include "drm_fb_helper.h" +#include "ast_drv.h" + +static void ast_dirty_update(struct ast_fbdev *afbdev, + int x, int y, int width, int height) +{ + int i; + struct drm_gem_object *obj; + struct ast_bo *bo; + int src_offset, dst_offset; + int bpp = (afbdev->afb.base.bits_per_pixel + 7)/8; + int ret; + bool unmap = false; + + obj = afbdev->afb.obj; + bo = gem_to_ast_bo(obj); + + ret = ast_bo_reserve(bo, true); + if (ret) { + DRM_ERROR("failed to reserve fb bo\n"); + return; + } + + if (!bo->kmap.virtual) { + ret = ttm_bo_kmap(&bo->bo, 0, bo->bo.num_pages, &bo->kmap); + if (ret) { + DRM_ERROR("failed to kmap fb updates\n"); + ast_bo_unreserve(bo); + return; + } + unmap = true; + } + for (i = y; i < y + height; i++) { + /* assume equal stride for now */ + src_offset = dst_offset = i * afbdev->afb.base.pitches[0] + (x * bpp); + memcpy_toio(bo->kmap.virtual + src_offset, afbdev->sysram + src_offset, width * bpp); + + } + if (unmap) + ttm_bo_kunmap(&bo->kmap); + + ast_bo_unreserve(bo); +} + +static void ast_fillrect(struct fb_info *info, + const struct fb_fillrect *rect) +{ + struct ast_fbdev *afbdev = info->par; + sys_fillrect(info, rect); + ast_dirty_update(afbdev, rect->dx, rect->dy, rect->width, + rect->height); +} + +static void ast_copyarea(struct fb_info *info, + const struct fb_copyarea *area) +{ + struct ast_fbdev *afbdev = info->par; + sys_copyarea(info, area); + ast_dirty_update(afbdev, area->dx, area->dy, area->width, + area->height); +} + +static void ast_imageblit(struct fb_info *info, + const struct fb_image *image) +{ + struct ast_fbdev *afbdev = info->par; + sys_imageblit(info, image); + ast_dirty_update(afbdev, image->dx, image->dy, image->width, + image->height); +} + +static struct fb_ops astfb_ops = { + .owner = THIS_MODULE, + .fb_check_var = drm_fb_helper_check_var, + .fb_set_par = drm_fb_helper_set_par, + .fb_fillrect = ast_fillrect, + .fb_copyarea = ast_copyarea, + .fb_imageblit = ast_imageblit, + .fb_pan_display = drm_fb_helper_pan_display, + .fb_blank = drm_fb_helper_blank, + .fb_setcmap = drm_fb_helper_setcmap, + .fb_debug_enter = drm_fb_helper_debug_enter, + .fb_debug_leave = drm_fb_helper_debug_leave, +}; + +static int astfb_create_object(struct ast_fbdev *afbdev, + struct drm_mode_fb_cmd2 *mode_cmd, + struct drm_gem_object **gobj_p) +{ + struct drm_device *dev = afbdev->helper.dev; + u32 bpp, depth; + u32 size; + struct drm_gem_object *gobj; + + int ret = 0; + drm_fb_get_bpp_depth(mode_cmd->pixel_format, &depth, &bpp); + + size = mode_cmd->pitches[0] * mode_cmd->height; + ret = ast_gem_create(dev, size, true, &gobj); + if (ret) + return ret; + + *gobj_p = gobj; + return ret; +} + +static int astfb_create(struct ast_fbdev *afbdev, + struct drm_fb_helper_surface_size *sizes) +{ + struct drm_device *dev = afbdev->helper.dev; + struct drm_mode_fb_cmd2 mode_cmd; + struct drm_framebuffer *fb; + struct fb_info *info; + int size, ret; + struct device *device = &dev->pdev->dev; + void *sysram; + struct drm_gem_object *gobj = NULL; + struct ast_bo *bo = NULL; + mode_cmd.width = sizes->surface_width; + mode_cmd.height = sizes->surface_height; + mode_cmd.pitches[0] = mode_cmd.width * ((sizes->surface_bpp + 7)/8); + + mode_cmd.pixel_format = drm_mode_legacy_fb_format(sizes->surface_bpp, + sizes->surface_depth); + + size = mode_cmd.pitches[0] * mode_cmd.height; + + ret = astfb_create_object(afbdev, &mode_cmd, &gobj); + if (ret) { + DRM_ERROR("failed to create fbcon backing object %d\n", ret); + return ret; + } + bo = gem_to_ast_bo(gobj); + + sysram = vmalloc(size); + if (!sysram) + return -ENOMEM; + + info = framebuffer_alloc(0, device); + if (!info) { + ret = -ENOMEM; + goto out; + } + info->par = afbdev; + + ret = ast_framebuffer_init(dev, &afbdev->afb, &mode_cmd, gobj); + if (ret) + goto out; + + afbdev->sysram = sysram; + afbdev->size = size; + + fb = &afbdev->afb.base; + afbdev->helper.fb = fb; + afbdev->helper.fbdev = info; + + strcpy(info->fix.id, "astdrmfb"); + + info->flags = FBINFO_DEFAULT | FBINFO_CAN_FORCE_OUTPUT; + info->fbops = &astfb_ops; + + ret = fb_alloc_cmap(&info->cmap, 256, 0); + if (ret) { + ret = -ENOMEM; + goto out; + } + + info->apertures = alloc_apertures(1); + if (!info->apertures) { + ret = -ENOMEM; + goto out; + } + info->apertures->ranges[0].base = pci_resource_start(dev->pdev, 0); + info->apertures->ranges[0].size = pci_resource_len(dev->pdev, 0); + + drm_fb_helper_fill_fix(info, fb->pitches[0], fb->depth); + drm_fb_helper_fill_var(info, &afbdev->helper, sizes->fb_width, sizes->fb_height); + + info->screen_base = sysram; + info->screen_size = size; + + info->pixmap.flags = FB_PIXMAP_SYSTEM; + + DRM_DEBUG_KMS("allocated %dx%d\n", + fb->width, fb->height); + + return 0; +out: + return ret; +} + +static void ast_fb_gamma_set(struct drm_crtc *crtc, u16 red, u16 green, + u16 blue, int regno) +{ + struct ast_crtc *ast_crtc = to_ast_crtc(crtc); + ast_crtc->lut_r[regno] = red >> 8; + ast_crtc->lut_g[regno] = green >> 8; + ast_crtc->lut_b[regno] = blue >> 8; +} + +static void ast_fb_gamma_get(struct drm_crtc *crtc, u16 *red, u16 *green, + u16 *blue, int regno) +{ + struct ast_crtc *ast_crtc = to_ast_crtc(crtc); + *red = ast_crtc->lut_r[regno] << 8; + *green = ast_crtc->lut_g[regno] << 8; + *blue = ast_crtc->lut_b[regno] << 8; +} + +static int ast_find_or_create_single(struct drm_fb_helper *helper, + struct drm_fb_helper_surface_size *sizes) +{ + struct ast_fbdev *afbdev = (struct ast_fbdev *)helper; + int new_fb = 0; + int ret; + + if (!helper->fb) { + ret = astfb_create(afbdev, sizes); + if (ret) + return ret; + new_fb = 1; + } + return new_fb; +} + +static struct drm_fb_helper_funcs ast_fb_helper_funcs = { + .gamma_set = ast_fb_gamma_set, + .gamma_get = ast_fb_gamma_get, + .fb_probe = ast_find_or_create_single, +}; + +static void ast_fbdev_destroy(struct drm_device *dev, + struct ast_fbdev *afbdev) +{ + struct fb_info *info; + struct ast_framebuffer *afb = &afbdev->afb; + if (afbdev->helper.fbdev) { + info = afbdev->helper.fbdev; + unregister_framebuffer(info); + if (info->cmap.len) + fb_dealloc_cmap(&info->cmap); + framebuffer_release(info); + } + + if (afb->obj) { + drm_gem_object_unreference_unlocked(afb->obj); + afb->obj = NULL; + } + drm_fb_helper_fini(&afbdev->helper); + + vfree(afbdev->sysram); + drm_framebuffer_cleanup(&afb->base); +} + +int ast_fbdev_init(struct drm_device *dev) +{ + struct ast_private *ast = dev->dev_private; + struct ast_fbdev *afbdev; + int ret; + + afbdev = kzalloc(sizeof(struct ast_fbdev), GFP_KERNEL); + if (!afbdev) + return -ENOMEM; + + ast->fbdev = afbdev; + afbdev->helper.funcs = &ast_fb_helper_funcs; + ret = drm_fb_helper_init(dev, &afbdev->helper, + 1, 1); + if (ret) { + kfree(afbdev); + return ret; + } + + drm_fb_helper_single_add_all_connectors(&afbdev->helper); + drm_fb_helper_initial_config(&afbdev->helper, 32); + return 0; +} + +void ast_fbdev_fini(struct drm_device *dev) +{ + struct ast_private *ast = dev->dev_private; + + if (!ast->fbdev) + return; + + ast_fbdev_destroy(dev, ast->fbdev); + kfree(ast->fbdev); + ast->fbdev = NULL; +} + +void ast_fbdev_set_suspend(struct drm_device *dev, int state) +{ + struct ast_private *ast = dev->dev_private; + + if (!ast->fbdev) + return; + + fb_set_suspend(ast->fbdev->helper.fbdev, state); +} diff --git a/drivers/gpu/drm/ast/ast_main.c b/drivers/gpu/drm/ast/ast_main.c new file mode 100644 index 0000000..95ae55b --- /dev/null +++ b/drivers/gpu/drm/ast/ast_main.c @@ -0,0 +1,527 @@ +/* + * Copyright 2012 Red Hat Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sub license, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDERS, AUTHORS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, + * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR + * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE + * USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * The above copyright notice and this permission notice (including the + * next paragraph) shall be included in all copies or substantial portions + * of the Software. + * + */ +/* + * Authors: Dave Airlie <airlied@redhat.com> + */ +#include "drmP.h" +#include "ast_drv.h" + + +#include "drm_fb_helper.h" +#include "drm_crtc_helper.h" + +#include "ast_dram_tables.h" + +void ast_set_index_reg_mask(struct ast_private *ast, + uint32_t base, uint8_t index, + uint8_t mask, uint8_t val) +{ + u8 tmp; + ast_io_write8(ast, base, index); + tmp = (ast_io_read8(ast, base + 1) & mask) | val; + ast_set_index_reg(ast, base, index, tmp); +} + +uint8_t ast_get_index_reg(struct ast_private *ast, + uint32_t base, uint8_t index) +{ + uint8_t ret; + ast_io_write8(ast, base, index); + ret = ast_io_read8(ast, base + 1); + return ret; +} + +uint8_t ast_get_index_reg_mask(struct ast_private *ast, + uint32_t base, uint8_t index, uint8_t mask) +{ + uint8_t ret; + ast_io_write8(ast, base, index); + ret = ast_io_read8(ast, base + 1) & mask; + return ret; +} + + +static int ast_detect_chip(struct drm_device *dev) +{ + struct ast_private *ast = dev->dev_private; + + if (dev->pdev->device == PCI_CHIP_AST1180) { + ast->chip = AST1100; + DRM_INFO("AST 1180 detected\n"); + } else { + if (dev->pdev->revision >= 0x20) { + ast->chip = AST2300; + DRM_INFO("AST 2300 detected\n"); + } else if (dev->pdev->revision >= 0x10) { + uint32_t data; + ast_write32(ast, 0xf004, 0x1e6e0000); + ast_write32(ast, 0xf000, 0x1); + + data = ast_read32(ast, 0x1207c); + switch (data & 0x0300) { + case 0x0200: + ast->chip = AST1100; + DRM_INFO("AST 1100 detected\n"); + break; + case 0x0100: + ast->chip = AST2200; + DRM_INFO("AST 2200 detected\n"); + break; + case 0x0000: + ast->chip = AST2150; + DRM_INFO("AST 2150 detected\n"); + break; + default: + ast->chip = AST2100; + DRM_INFO("AST 2100 detected\n"); + break; + } + ast->vga2_clone = false; + } else { + ast->chip = 2000; + DRM_INFO("AST 2000 detected\n"); + } + } + return 0; +} + +static int ast_get_dram_info(struct drm_device *dev) +{ + struct ast_private *ast = dev->dev_private; + uint32_t data, data2; + uint32_t denum, num, div, ref_pll; + + ast_write32(ast, 0xf004, 0x1e6e0000); + ast_write32(ast, 0xf000, 0x1); + + + ast_write32(ast, 0x10000, 0xfc600309); + + do { + ; + } while (ast_read32(ast, 0x10000) != 0x01); + data = ast_read32(ast, 0x10004); + + if (data & 0x400) + ast->dram_bus_width = 16; + else + ast->dram_bus_width = 32; + + if (ast->chip == AST2300) { + switch (data & 0x03) { + case 0: + ast->dram_type = AST_DRAM_512Mx16; + break; + default: + case 1: + ast->dram_type = AST_DRAM_1Gx16; + break; + case 2: + ast->dram_type = AST_DRAM_2Gx16; + break; + case 3: + ast->dram_type = AST_DRAM_4Gx16; + break; + } + } else { + switch (data & 0x0c) { + case 0: + case 4: + ast->dram_type = AST_DRAM_512Mx16; + break; + case 8: + if (data & 0x40) + ast->dram_type = AST_DRAM_1Gx16; + else + ast->dram_type = AST_DRAM_512Mx32; + break; + case 0xc: + ast->dram_type = AST_DRAM_1Gx32; + break; + } + } + + data = ast_read32(ast, 0x10120); + data2 = ast_read32(ast, 0x10170); + if (data2 & 0x2000) + ref_pll = 14318; + else + ref_pll = 12000; + + denum = data & 0x1f; + num = (data & 0x3fe0) >> 5; + data = (data & 0xc000) >> 14; + switch (data) { + case 3: + div = 0x4; + break; + case 2: + case 1: + div = 0x2; + break; + default: + div = 0x1; + break; + } + ast->mclk = ref_pll * (num + 2) / (denum + 2) * (div * 1000); + return 0; +} + +uint32_t ast_get_max_dclk(struct drm_device *dev, int bpp) +{ + struct ast_private *ast = dev->dev_private; + uint32_t dclk, jreg; + uint32_t dram_bus_width, mclk, dram_bandwidth, actual_dram_bandwidth, dram_efficency = 500; + + dram_bus_width = ast->dram_bus_width; + mclk = ast->mclk; + + if (ast->chip == AST2100 || + ast->chip == AST1100 || + ast->chip == AST2200 || + ast->chip == AST2150 || + ast->dram_bus_width == 16) + dram_efficency = 600; + else if (ast->chip == AST2300) + dram_efficency = 400; + + dram_bandwidth = mclk * dram_bus_width * 2 / 8; + actual_dram_bandwidth = dram_bandwidth * dram_efficency / 1000; + + if (ast->chip == AST1180) + dclk = actual_dram_bandwidth / ((bpp + 1) / 8); + else { + jreg = ast_get_index_reg_mask(ast, AST_IO_CRTC_PORT, 0xd0, 0xff); + if ((jreg & 0x08) && (ast->chip == AST2000)) + dclk = actual_dram_bandwidth / ((bpp + 1 + 16) / 8); + else if ((jreg & 0x08) && (bpp == 8)) + dclk = actual_dram_bandwidth / ((bpp + 1 + 24) / 8); + else + dclk = actual_dram_bandwidth / ((bpp + 1) / 8); + } + + if (ast->chip == AST2100 || + ast->chip == AST2200 || + ast->chip == AST2300 || + ast->chip == AST1180) { + if (dclk > 200) + dclk = 200; + } else { + if (dclk > 165) + dclk = 165; + } + + return dclk; +} + +static void ast_user_framebuffer_destroy(struct drm_framebuffer *fb) +{ + struct ast_framebuffer *ast_fb = to_ast_framebuffer(fb); + if (ast_fb->obj) + drm_gem_object_unreference_unlocked(ast_fb->obj); + + drm_framebuffer_cleanup(fb); + kfree(fb); +} + +static int ast_user_framebuffer_create_handle(struct drm_framebuffer *fb, + struct drm_file *file, + unsigned int *handle) +{ + return -EINVAL; +} + +static const struct drm_framebuffer_funcs ast_fb_funcs = { + .destroy = ast_user_framebuffer_destroy, + .create_handle = ast_user_framebuffer_create_handle, +}; + + +int ast_framebuffer_init(struct drm_device *dev, + struct ast_framebuffer *ast_fb, + struct drm_mode_fb_cmd2 *mode_cmd, + struct drm_gem_object *obj) +{ + int ret; + + ret = drm_framebuffer_init(dev, &ast_fb->base, &ast_fb_funcs); + if (ret) { + DRM_ERROR("framebuffer init failed %d\n", ret); + return ret; + } + drm_helper_mode_fill_fb_struct(&ast_fb->base, mode_cmd); + ast_fb->obj = obj; + return 0; +} + +static struct drm_framebuffer * +ast_user_framebuffer_create(struct drm_device *dev, + struct drm_file *filp, + struct drm_mode_fb_cmd2 *mode_cmd) +{ + struct drm_gem_object *obj; + struct ast_framebuffer *ast_fb; + int ret; + + obj = drm_gem_object_lookup(dev, filp, mode_cmd->handles[0]); + if (obj == NULL) + return ERR_PTR(-ENOENT); + + ast_fb = kzalloc(sizeof(*ast_fb), GFP_KERNEL); + if (!ast_fb) { + drm_gem_object_unreference_unlocked(obj); + return ERR_PTR(-ENOMEM); + } + + ret = ast_framebuffer_init(dev, ast_fb, mode_cmd, obj); + if (ret) { + drm_gem_object_unreference_unlocked(obj); + kfree(ast_fb); + return ERR_PTR(ret); + } + return &ast_fb->base; +} + +static const struct drm_mode_config_funcs ast_mode_funcs = { + .fb_create = ast_user_framebuffer_create, +}; + +static u32 ast_get_vram_info(struct drm_device *dev) +{ + struct ast_private *ast = dev->dev_private; + u8 jreg; + + ast_open_key(ast); + + jreg = ast_get_index_reg_mask(ast, AST_IO_CRTC_PORT, 0xaa, 0xff); + switch (jreg & 3) { + case 0: return AST_VIDMEM_SIZE_8M; + case 1: return AST_VIDMEM_SIZE_16M; + case 2: return AST_VIDMEM_SIZE_32M; + case 3: return AST_VIDMEM_SIZE_64M; + } + return AST_VIDMEM_DEFAULT_SIZE; +} + +int ast_driver_load(struct drm_device *dev, unsigned long flags) +{ + struct ast_private *ast; + int ret = 0; + + ast = kzalloc(sizeof(struct ast_private), GFP_KERNEL); + if (!ast) + return -ENOMEM; + + dev->dev_private = ast; + ast->dev = dev; + + ast->regs = pci_iomap(dev->pdev, 1, 0); + if (!ast->regs) { + ret = -EIO; + goto out_free; + } + ast->ioregs = pci_iomap(dev->pdev, 2, 0); + if (!ast->ioregs) { + ret = -EIO; + goto out_free; + } + + ast_detect_chip(dev); + + if (ast->chip != AST1180) { + ast_get_dram_info(dev); + ast->vram_size = ast_get_vram_info(dev); + DRM_INFO("dram %d %d %d %08x\n", ast->mclk, ast->dram_type, ast->dram_bus_width, ast->vram_size); + } + + ret = ast_mm_init(ast); + if (ret) + goto out_free; + + drm_mode_config_init(dev); + + dev->mode_config.funcs = (void *)&ast_mode_funcs; + dev->mode_config.min_width = 0; + dev->mode_config.min_height = 0; + dev->mode_config.preferred_depth = 24; + dev->mode_config.prefer_shadow = 1; + + if (ast->chip == AST2100 || + ast->chip == AST2200 || + ast->chip == AST2300 || + ast->chip == AST1180) { + dev->mode_config.max_width = 1920; + dev->mode_config.max_height = 2048; + } else { + dev->mode_config.max_width = 1600; + dev->mode_config.max_height = 1200; + } + + ret = ast_mode_init(dev); + if (ret) + goto out_free; + + ret = ast_fbdev_init(dev); + if (ret) + goto out_free; + + return 0; +out_free: + kfree(ast); + dev->dev_private = NULL; + return ret; +} + +int ast_driver_unload(struct drm_device *dev) +{ + struct ast_private *ast = dev->dev_private; + + ast_mode_fini(dev); + ast_fbdev_fini(dev); + drm_mode_config_cleanup(dev); + + ast_mm_fini(ast); + pci_iounmap(dev->pdev, ast->ioregs); + pci_iounmap(dev->pdev, ast->regs); + kfree(ast); + return 0; +} + +int ast_gem_create(struct drm_device *dev, + u32 size, bool iskernel, + struct drm_gem_object **obj) +{ + struct ast_bo *astbo; + int ret; + + *obj = NULL; + + size = roundup(size, PAGE_SIZE); + if (size == 0) + return -EINVAL; + + ret = ast_bo_create(dev, size, 0, 0, &astbo); + if (ret) { + if (ret != -ERESTARTSYS) + DRM_ERROR("failed to allocate GEM object\n"); + return ret; + } + *obj = &astbo->gem; + return 0; +} + +int ast_dumb_create(struct drm_file *file, + struct drm_device *dev, + struct drm_mode_create_dumb *args) +{ + int ret; + struct drm_gem_object *gobj; + u32 handle; + + args->pitch = args->width * ((args->bpp + 7) / 8); + args->size = args->pitch * args->height; + + ret = ast_gem_create(dev, args->size, false, + &gobj); + if (ret) + return ret; + + ret = drm_gem_handle_create(file, gobj, &handle); + drm_gem_object_unreference_unlocked(gobj); + if (ret) + return ret; + + args->handle = handle; + return 0; +} + +int ast_dumb_destroy(struct drm_file *file, + struct drm_device *dev, + uint32_t handle) +{ + return drm_gem_handle_delete(file, handle); +} + +int ast_gem_init_object(struct drm_gem_object *obj) +{ + BUG(); + return 0; +} + +void ast_bo_unref(struct ast_bo **bo) +{ + struct ttm_buffer_object *tbo; + + if ((*bo) == NULL) + return; + + tbo = &((*bo)->bo); + ttm_bo_unref(&tbo); + if (tbo == NULL) + *bo = NULL; + +} +void ast_gem_free_object(struct drm_gem_object *obj) +{ + struct ast_bo *ast_bo = gem_to_ast_bo(obj); + + if (!ast_bo) + return; + ast_bo_unref(&ast_bo); +} + + +static inline u64 ast_bo_mmap_offset(struct ast_bo *bo) +{ + return bo->bo.addr_space_offset; +} +int +ast_dumb_mmap_offset(struct drm_file *file, + struct drm_device *dev, + uint32_t handle, + uint64_t *offset) +{ + struct drm_gem_object *obj; + int ret; + struct ast_bo *bo; + + mutex_lock(&dev->struct_mutex); + obj = drm_gem_object_lookup(dev, file, handle); + if (obj == NULL) { + ret = -ENOENT; + goto out_unlock; + } + + bo = gem_to_ast_bo(obj); + *offset = ast_bo_mmap_offset(bo); + + drm_gem_object_unreference(obj); + ret = 0; +out_unlock: + mutex_unlock(&dev->struct_mutex); + return ret; + +} + diff --git a/drivers/gpu/drm/ast/ast_mode.c b/drivers/gpu/drm/ast/ast_mode.c new file mode 100644 index 0000000..65f9d23 --- /dev/null +++ b/drivers/gpu/drm/ast/ast_mode.c @@ -0,0 +1,1160 @@ +/* + * Copyright 2012 Red Hat Inc. + * Parts based on xf86-video-ast + * Copyright (c) 2005 ASPEED Technology Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sub license, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDERS, AUTHORS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, + * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR + * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE + * USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * The above copyright notice and this permission notice (including the + * next paragraph) shall be included in all copies or substantial portions + * of the Software. + * + */ +/* + * Authors: Dave Airlie <airlied@redhat.com> + */ +#include <linux/export.h> +#include "drmP.h" +#include "drm_crtc.h" +#include "drm_crtc_helper.h" +#include "ast_drv.h" + +#include "ast_tables.h" + +static struct ast_i2c_chan *ast_i2c_create(struct drm_device *dev); +static void ast_i2c_destroy(struct ast_i2c_chan *i2c); +static int ast_cursor_set(struct drm_crtc *crtc, + struct drm_file *file_priv, + uint32_t handle, + uint32_t width, + uint32_t height); +static int ast_cursor_move(struct drm_crtc *crtc, + int x, int y); + +static inline void ast_load_palette_index(struct ast_private *ast, + u8 index, u8 red, u8 green, + u8 blue) +{ + ast_io_write8(ast, AST_IO_DAC_INDEX_WRITE, index); + ast_io_read8(ast, AST_IO_SEQ_PORT); + ast_io_write8(ast, AST_IO_DAC_DATA, red); + ast_io_read8(ast, AST_IO_SEQ_PORT); + ast_io_write8(ast, AST_IO_DAC_DATA, green); + ast_io_read8(ast, AST_IO_SEQ_PORT); + ast_io_write8(ast, AST_IO_DAC_DATA, blue); + ast_io_read8(ast, AST_IO_SEQ_PORT); +} + +static void ast_crtc_load_lut(struct drm_crtc *crtc) +{ + struct ast_private *ast = crtc->dev->dev_private; + struct ast_crtc *ast_crtc = to_ast_crtc(crtc); + int i; + + if (!crtc->enabled) + return; + + for (i = 0; i < 256; i++) + ast_load_palette_index(ast, i, ast_crtc->lut_r[i], + ast_crtc->lut_g[i], ast_crtc->lut_b[i]); +} + +static bool ast_get_vbios_mode_info(struct drm_crtc *crtc, struct drm_display_mode *mode, + struct drm_display_mode *adjusted_mode, + struct ast_vbios_mode_info *vbios_mode) +{ + struct ast_private *ast = crtc->dev->dev_private; + u32 refresh_rate_index = 0, mode_id, color_index, refresh_rate; + u32 hborder, vborder; + + switch (crtc->fb->bits_per_pixel) { + case 8: + vbios_mode->std_table = &vbios_stdtable[VGAModeIndex]; + color_index = VGAModeIndex - 1; + break; + case 16: + vbios_mode->std_table = &vbios_stdtable[HiCModeIndex]; + color_index = HiCModeIndex; + break; + case 24: + case 32: + vbios_mode->std_table = &vbios_stdtable[TrueCModeIndex]; + color_index = TrueCModeIndex; + break; + default: + return false; + } + + switch (crtc->mode.crtc_hdisplay) { + case 640: + vbios_mode->enh_table = &res_640x480[refresh_rate_index]; + break; + case 800: + vbios_mode->enh_table = &res_800x600[refresh_rate_index]; + break; + case 1024: + vbios_mode->enh_table = &res_1024x768[refresh_rate_index]; + break; + case 1280: + if (crtc->mode.crtc_vdisplay == 800) + vbios_mode->enh_table = &res_1280x800[refresh_rate_index]; + else + vbios_mode->enh_table = &res_1280x1024[refresh_rate_index]; + break; + case 1440: + vbios_mode->enh_table = &res_1440x900[refresh_rate_index]; + break; + case 1600: + vbios_mode->enh_table = &res_1600x1200[refresh_rate_index]; + break; + case 1680: + vbios_mode->enh_table = &res_1680x1050[refresh_rate_index]; + break; + case 1920: + if (crtc->mode.crtc_vdisplay == 1080) + vbios_mode->enh_table = &res_1920x1080[refresh_rate_index]; + else + vbios_mode->enh_table = &res_1920x1200[refresh_rate_index]; + break; + default: + return false; + } + + refresh_rate = drm_mode_vrefresh(mode); + while (vbios_mode->enh_table->refresh_rate < refresh_rate) { + vbios_mode->enh_table++; + if ((vbios_mode->enh_table->refresh_rate > refresh_rate) || + (vbios_mode->enh_table->refresh_rate == 0xff)) { + vbios_mode->enh_table--; + break; + } + } + + hborder = (vbios_mode->enh_table->flags & HBorder) ? 8 : 0; + vborder = (vbios_mode->enh_table->flags & VBorder) ? 8 : 0; + + adjusted_mode->crtc_htotal = vbios_mode->enh_table->ht; + adjusted_mode->crtc_hblank_start = vbios_mode->enh_table->hde + hborder; + adjusted_mode->crtc_hblank_end = vbios_mode->enh_table->ht - hborder; + adjusted_mode->crtc_hsync_start = vbios_mode->enh_table->hde + hborder + + vbios_mode->enh_table->hfp; + adjusted_mode->crtc_hsync_end = (vbios_mode->enh_table->hde + hborder + + vbios_mode->enh_table->hfp + + vbios_mode->enh_table->hsync); + + adjusted_mode->crtc_vtotal = vbios_mode->enh_table->vt; + adjusted_mode->crtc_vblank_start = vbios_mode->enh_table->vde + vborder; + adjusted_mode->crtc_vblank_end = vbios_mode->enh_table->vt - vborder; + adjusted_mode->crtc_vsync_start = vbios_mode->enh_table->vde + vborder + + vbios_mode->enh_table->vfp; + adjusted_mode->crtc_vsync_end = (vbios_mode->enh_table->vde + vborder + + vbios_mode->enh_table->vfp + + vbios_mode->enh_table->vsync); + + refresh_rate_index = vbios_mode->enh_table->refresh_rate_index; + mode_id = vbios_mode->enh_table->mode_id; + + if (ast->chip == AST1180) { + /* TODO 1180 */ + } else { + ast_set_index_reg(ast, AST_IO_CRTC_PORT, 0x8c, (u8)((color_index & 0xf) << 4)); + ast_set_index_reg(ast, AST_IO_CRTC_PORT, 0x8d, refresh_rate_index & 0xff); + ast_set_index_reg(ast, AST_IO_CRTC_PORT, 0x8e, mode_id & 0xff); + + ast_set_index_reg(ast, AST_IO_CRTC_PORT, 0x91, 0xa8); + ast_set_index_reg(ast, AST_IO_CRTC_PORT, 0x92, crtc->fb->bits_per_pixel); + ast_set_index_reg(ast, AST_IO_CRTC_PORT, 0x93, adjusted_mode->clock / 1000); + ast_set_index_reg(ast, AST_IO_CRTC_PORT, 0x94, adjusted_mode->crtc_hdisplay); + ast_set_index_reg(ast, AST_IO_CRTC_PORT, 0x95, adjusted_mode->crtc_hdisplay >> 8); + + ast_set_index_reg(ast, AST_IO_CRTC_PORT, 0x96, adjusted_mode->crtc_vdisplay); + ast_set_index_reg(ast, AST_IO_CRTC_PORT, 0x97, adjusted_mode->crtc_vdisplay >> 8); + } + + return true; + + +} +static void ast_set_std_reg(struct drm_crtc *crtc, struct drm_display_mode *mode, + struct ast_vbios_mode_info *vbios_mode) +{ + struct ast_private *ast = crtc->dev->dev_private; + struct ast_vbios_stdtable *stdtable; + u32 i; + u8 jreg; + + stdtable = vbios_mode->std_table; + + jreg = stdtable->misc; + ast_io_write8(ast, AST_IO_MISC_PORT_WRITE, jreg); + + /* Set SEQ */ + ast_set_index_reg(ast, AST_IO_SEQ_PORT, 0x00, 0x03); + for (i = 0; i < 4; i++) { + jreg = stdtable->seq[i]; + if (!i) + jreg |= 0x20; + ast_set_index_reg(ast, AST_IO_SEQ_PORT, (i + 1) , jreg); + } + + /* Set CRTC */ + ast_set_index_reg_mask(ast, AST_IO_CRTC_PORT, 0x11, 0x7f, 0x00); + for (i = 0; i < 25; i++) + ast_set_index_reg(ast, AST_IO_CRTC_PORT, i, stdtable->crtc[i]); + + /* set AR */ + jreg = ast_io_read8(ast, AST_IO_INPUT_STATUS1_READ); + for (i = 0; i < 20; i++) { + jreg = stdtable->ar[i]; + ast_io_write8(ast, AST_IO_AR_PORT_WRITE, (u8)i); + ast_io_write8(ast, AST_IO_AR_PORT_WRITE, jreg); + } + ast_io_write8(ast, AST_IO_AR_PORT_WRITE, 0x14); + ast_io_write8(ast, AST_IO_AR_PORT_WRITE, 0x00); + + jreg = ast_io_read8(ast, AST_IO_INPUT_STATUS1_READ); + ast_io_write8(ast, AST_IO_AR_PORT_WRITE, 0x20); + + /* Set GR */ + for (i = 0; i < 9; i++) + ast_set_index_reg(ast, AST_IO_GR_PORT, i, stdtable->gr[i]); +} + +static void ast_set_crtc_reg(struct drm_crtc *crtc, struct drm_display_mode *mode, + struct ast_vbios_mode_info *vbios_mode) +{ + struct ast_private *ast = crtc->dev->dev_private; + u8 jreg05 = 0, jreg07 = 0, jreg09 = 0, jregAC = 0, jregAD = 0, jregAE = 0; + u16 temp; + + ast_set_index_reg_mask(ast, AST_IO_CRTC_PORT, 0x11, 0x7f, 0x00); + + temp = (mode->crtc_htotal >> 3) - 5; + if (temp & 0x100) + jregAC |= 0x01; /* HT D[8] */ + ast_set_index_reg_mask(ast, AST_IO_CRTC_PORT, 0x00, 0x00, temp); + + temp = (mode->crtc_hdisplay >> 3) - 1; + if (temp & 0x100) + jregAC |= 0x04; /* HDE D[8] */ + ast_set_index_reg_mask(ast, AST_IO_CRTC_PORT, 0x01, 0x00, temp); + + temp = (mode->crtc_hblank_start >> 3) - 1; + if (temp & 0x100) + jregAC |= 0x10; /* HBS D[8] */ + ast_set_index_reg_mask(ast, AST_IO_CRTC_PORT, 0x02, 0x00, temp); + + temp = ((mode->crtc_hblank_end >> 3) - 1) & 0x7f; + if (temp & 0x20) + jreg05 |= 0x80; /* HBE D[5] */ + if (temp & 0x40) + jregAD |= 0x01; /* HBE D[5] */ + ast_set_index_reg_mask(ast, AST_IO_CRTC_PORT, 0x03, 0xE0, (temp & 0x1f)); + + temp = (mode->crtc_hsync_start >> 3) - 1; + if (temp & 0x100) + jregAC |= 0x40; /* HRS D[5] */ + ast_set_index_reg_mask(ast, AST_IO_CRTC_PORT, 0x04, 0x00, temp); + + temp = ((mode->crtc_hsync_end >> 3) - 1) & 0x3f; + if (temp & 0x20) + jregAD |= 0x04; /* HRE D[5] */ + ast_set_index_reg_mask(ast, AST_IO_CRTC_PORT, 0x05, 0x60, (u8)((temp & 0x1f) | jreg05)); + + ast_set_index_reg_mask(ast, AST_IO_CRTC_PORT, 0xAC, 0x00, jregAC); + ast_set_index_reg_mask(ast, AST_IO_CRTC_PORT, 0xAD, 0x00, jregAD); + + /* vert timings */ + temp = (mode->crtc_vtotal) - 2; + if (temp & 0x100) + jreg07 |= 0x01; + if (temp & 0x200) + jreg07 |= 0x20; + if (temp & 0x400) + jregAE |= 0x01; + ast_set_index_reg_mask(ast, AST_IO_CRTC_PORT, 0x06, 0x00, temp); + + temp = (mode->crtc_vsync_start) - 1; + if (temp & 0x100) + jreg07 |= 0x04; + if (temp & 0x200) + jreg07 |= 0x80; + if (temp & 0x400) + jregAE |= 0x08; + ast_set_index_reg_mask(ast, AST_IO_CRTC_PORT, 0x10, 0x00, temp); + + temp = (mode->crtc_vsync_end - 1) & 0x3f; + if (temp & 0x10) + jregAE |= 0x20; + if (temp & 0x20) + jregAE |= 0x40; + ast_set_index_reg_mask(ast, AST_IO_CRTC_PORT, 0x11, 0x70, temp & 0xf); + + temp = mode->crtc_vdisplay - 1; + if (temp & 0x100) + jreg07 |= 0x02; + if (temp & 0x200) + jreg07 |= 0x40; + if (temp & 0x400) + jregAE |= 0x02; + ast_set_index_reg_mask(ast, AST_IO_CRTC_PORT, 0x12, 0x00, temp); + + temp = mode->crtc_vblank_start - 1; + if (temp & 0x100) + jreg07 |= 0x08; + if (temp & 0x200) + jreg09 |= 0x20; + if (temp & 0x400) + jregAE |= 0x04; + ast_set_index_reg_mask(ast, AST_IO_CRTC_PORT, 0x15, 0x00, temp); + + temp = mode->crtc_vblank_end - 1; + if (temp & 0x100) + jregAE |= 0x10; + ast_set_index_reg_mask(ast, AST_IO_CRTC_PORT, 0x16, 0x00, temp); + + ast_set_index_reg_mask(ast, AST_IO_CRTC_PORT, 0x07, 0x00, jreg07); + ast_set_index_reg_mask(ast, AST_IO_CRTC_PORT, 0x09, 0xdf, jreg09); + ast_set_index_reg_mask(ast, AST_IO_CRTC_PORT, 0xAE, 0x00, (jregAE | 0x80)); + + ast_set_index_reg_mask(ast, AST_IO_CRTC_PORT, 0x11, 0x7f, 0x80); +} + +static void ast_set_offset_reg(struct drm_crtc *crtc) +{ + struct ast_private *ast = crtc->dev->dev_private; + + u16 offset; + + offset = crtc->fb->pitches[0] >> 3; + ast_set_index_reg(ast, AST_IO_CRTC_PORT, 0x13, (offset & 0xff)); + ast_set_index_reg(ast, AST_IO_CRTC_PORT, 0xb0, (offset >> 8) & 0x3f); +} + +static void ast_set_dclk_reg(struct drm_device *dev, struct drm_display_mode *mode, + struct ast_vbios_mode_info *vbios_mode) +{ + struct ast_private *ast = dev->dev_private; + struct ast_vbios_dclk_info *clk_info; + + clk_info = &dclk_table[vbios_mode->enh_table->dclk_index]; + + ast_set_index_reg_mask(ast, AST_IO_CRTC_PORT, 0xc0, 0x00, clk_info->param1); + ast_set_index_reg_mask(ast, AST_IO_CRTC_PORT, 0xc1, 0x00, clk_info->param2); + ast_set_index_reg_mask(ast, AST_IO_CRTC_PORT, 0xbb, 0x0f, + (clk_info->param3 & 0x80) | ((clk_info->param3 & 0x3) << 4)); +} + +static void ast_set_ext_reg(struct drm_crtc *crtc, struct drm_display_mode *mode, + struct ast_vbios_mode_info *vbios_mode) +{ + struct ast_private *ast = crtc->dev->dev_private; + u8 jregA0 = 0, jregA3 = 0, jregA8 = 0; + + switch (crtc->fb->bits_per_pixel) { + case 8: + jregA0 = 0x70; + jregA3 = 0x01; + jregA8 = 0x00; + break; + case 15: + case 16: + jregA0 = 0x70; + jregA3 = 0x04; + jregA8 = 0x02; + break; + case 32: + jregA0 = 0x70; + jregA3 = 0x08; + jregA8 = 0x02; + break; + } + + ast_set_index_reg_mask(ast, AST_IO_CRTC_PORT, 0xa0, 0x8f, jregA0); + ast_set_index_reg_mask(ast, AST_IO_CRTC_PORT, 0xa3, 0xf0, jregA3); + ast_set_index_reg_mask(ast, AST_IO_CRTC_PORT, 0xa8, 0xfd, jregA8); + + /* Set Threshold */ + if (ast->chip == AST2300) { + ast_set_index_reg(ast, AST_IO_CRTC_PORT, 0xa7, 0x78); + ast_set_index_reg(ast, AST_IO_CRTC_PORT, 0xa6, 0x60); + } else if (ast->chip == AST2100 || + ast->chip == AST1100 || + ast->chip == AST2200 || + ast->chip == AST2150) { + ast_set_index_reg(ast, AST_IO_CRTC_PORT, 0xa7, 0x3f); + ast_set_index_reg(ast, AST_IO_CRTC_PORT, 0xa6, 0x2f); + } else { + ast_set_index_reg(ast, AST_IO_CRTC_PORT, 0xa7, 0x2f); + ast_set_index_reg(ast, AST_IO_CRTC_PORT, 0xa6, 0x1f); + } +} + +void ast_set_sync_reg(struct drm_device *dev, struct drm_display_mode *mode, + struct ast_vbios_mode_info *vbios_mode) +{ + struct ast_private *ast = dev->dev_private; + u8 jreg; + + jreg = ast_io_read8(ast, AST_IO_MISC_PORT_READ); + jreg |= (vbios_mode->enh_table->flags & SyncNN); + ast_io_write8(ast, AST_IO_MISC_PORT_WRITE, jreg); +} + +bool ast_set_dac_reg(struct drm_crtc *crtc, struct drm_display_mode *mode, + struct ast_vbios_mode_info *vbios_mode) +{ + switch (crtc->fb->bits_per_pixel) { + case 8: + break; + default: + return false; + } + return true; +} + +void ast_set_start_address_crt1(struct drm_crtc *crtc, unsigned offset) +{ + struct ast_private *ast = crtc->dev->dev_private; + u32 addr; + + addr = offset >> 2; + ast_set_index_reg(ast, AST_IO_CRTC_PORT, 0x0d, (u8)(addr & 0xff)); + ast_set_index_reg(ast, AST_IO_CRTC_PORT, 0x0c, (u8)((addr >> 8) & 0xff)); + ast_set_index_reg(ast, AST_IO_CRTC_PORT, 0xaf, (u8)((addr >> 16) & 0xff)); + +} + +static void ast_crtc_dpms(struct drm_crtc *crtc, int mode) +{ + struct ast_private *ast = crtc->dev->dev_private; + + if (ast->chip == AST1180) + return; + + switch (mode) { + case DRM_MODE_DPMS_ON: + case DRM_MODE_DPMS_STANDBY: + case DRM_MODE_DPMS_SUSPEND: + ast_set_index_reg_mask(ast, AST_IO_SEQ_PORT, 0x1, 0xdf, 0); + ast_crtc_load_lut(crtc); + break; + case DRM_MODE_DPMS_OFF: + ast_set_index_reg_mask(ast, AST_IO_SEQ_PORT, 0x1, 0xdf, 0x20); + break; + } +} + +static bool ast_crtc_mode_fixup(struct drm_crtc *crtc, + struct drm_display_mode *mode, + struct drm_display_mode *adjusted_mode) +{ + return true; +} + +/* ast is different - we will force move buffers out of VRAM */ +static int ast_crtc_do_set_base(struct drm_crtc *crtc, + struct drm_framebuffer *fb, + int x, int y, int atomic) +{ + struct ast_private *ast = crtc->dev->dev_private; + struct drm_gem_object *obj; + struct ast_framebuffer *ast_fb; + struct ast_bo *bo; + int ret; + u64 gpu_addr; + + /* push the previous fb to system ram */ + if (!atomic && fb) { + ast_fb = to_ast_framebuffer(fb); + obj = ast_fb->obj; + bo = gem_to_ast_bo(obj); + ret = ast_bo_reserve(bo, false); + if (ret) + return ret; + ast_bo_push_sysram(bo); + ast_bo_unreserve(bo); + } + + ast_fb = to_ast_framebuffer(crtc->fb); + obj = ast_fb->obj; + bo = gem_to_ast_bo(obj); + + ret = ast_bo_reserve(bo, false); + if (ret) + return ret; + + ret = ast_bo_pin(bo, TTM_PL_FLAG_VRAM, &gpu_addr); + if (ret) { + ast_bo_unreserve(bo); + return ret; + } + + if (&ast->fbdev->afb == ast_fb) { + /* if pushing console in kmap it */ + ret = ttm_bo_kmap(&bo->bo, 0, bo->bo.num_pages, &bo->kmap); + if (ret) + DRM_ERROR("failed to kmap fbcon\n"); + } + ast_bo_unreserve(bo); + + ast_set_start_address_crt1(crtc, (u32)gpu_addr); + + return 0; +} + +static int ast_crtc_mode_set_base(struct drm_crtc *crtc, int x, int y, + struct drm_framebuffer *old_fb) +{ + return ast_crtc_do_set_base(crtc, old_fb, x, y, 0); +} + +static int ast_crtc_mode_set(struct drm_crtc *crtc, + struct drm_display_mode *mode, + struct drm_display_mode *adjusted_mode, + int x, int y, + struct drm_framebuffer *old_fb) +{ + struct drm_device *dev = crtc->dev; + struct ast_private *ast = crtc->dev->dev_private; + struct ast_vbios_mode_info vbios_mode; + bool ret; + if (ast->chip == AST1180) { + DRM_ERROR("AST 1180 modesetting not supported\n"); + return -EINVAL; + } + + ret = ast_get_vbios_mode_info(crtc, mode, adjusted_mode, &vbios_mode); + if (ret == false) + return -EINVAL; + ast_open_key(ast); + + ast_set_index_reg_mask(ast, AST_IO_CRTC_PORT, 0xa1, 0xff, 0x04); + + ast_set_std_reg(crtc, adjusted_mode, &vbios_mode); + ast_set_crtc_reg(crtc, adjusted_mode, &vbios_mode); + ast_set_offset_reg(crtc); + ast_set_dclk_reg(dev, adjusted_mode, &vbios_mode); + ast_set_ext_reg(crtc, adjusted_mode, &vbios_mode); + ast_set_sync_reg(dev, adjusted_mode, &vbios_mode); + ast_set_dac_reg(crtc, adjusted_mode, &vbios_mode); + + ast_crtc_mode_set_base(crtc, x, y, old_fb); + + return 0; +} + +static void ast_crtc_disable(struct drm_crtc *crtc) +{ + +} + +static void ast_crtc_prepare(struct drm_crtc *crtc) +{ + +} + +static void ast_crtc_commit(struct drm_crtc *crtc) +{ + struct ast_private *ast = crtc->dev->dev_private; + ast_set_index_reg_mask(ast, AST_IO_SEQ_PORT, 0x1, 0xdf, 0); +} + + +static const struct drm_crtc_helper_funcs ast_crtc_helper_funcs = { + .dpms = ast_crtc_dpms, + .mode_fixup = ast_crtc_mode_fixup, + .mode_set = ast_crtc_mode_set, + .mode_set_base = ast_crtc_mode_set_base, + .disable = ast_crtc_disable, + .load_lut = ast_crtc_load_lut, + .disable = ast_crtc_disable, + .prepare = ast_crtc_prepare, + .commit = ast_crtc_commit, + +}; + +static void ast_crtc_reset(struct drm_crtc *crtc) +{ + +} + +static void ast_crtc_gamma_set(struct drm_crtc *crtc, u16 *red, u16 *green, + u16 *blue, uint32_t start, uint32_t size) +{ + struct ast_crtc *ast_crtc = to_ast_crtc(crtc); + int end = (start + size > 256) ? 256 : start + size, i; + + /* userspace palettes are always correct as is */ + for (i = start; i < end; i++) { + ast_crtc->lut_r[i] = red[i] >> 8; + ast_crtc->lut_g[i] = green[i] >> 8; + ast_crtc->lut_b[i] = blue[i] >> 8; + } + ast_crtc_load_lut(crtc); +} + + +static void ast_crtc_destroy(struct drm_crtc *crtc) +{ + drm_crtc_cleanup(crtc); + kfree(crtc); +} + +static const struct drm_crtc_funcs ast_crtc_funcs = { + .cursor_set = ast_cursor_set, + .cursor_move = ast_cursor_move, + .reset = ast_crtc_reset, + .set_config = drm_crtc_helper_set_config, + .gamma_set = ast_crtc_gamma_set, + .destroy = ast_crtc_destroy, +}; + +int ast_crtc_init(struct drm_device *dev) +{ + struct ast_crtc *crtc; + int i; + + crtc = kzalloc(sizeof(struct ast_crtc), GFP_KERNEL); + if (!crtc) + return -ENOMEM; + + drm_crtc_init(dev, &crtc->base, &ast_crtc_funcs); + drm_mode_crtc_set_gamma_size(&crtc->base, 256); + drm_crtc_helper_add(&crtc->base, &ast_crtc_helper_funcs); + + for (i = 0; i < 256; i++) { + crtc->lut_r[i] = i; + crtc->lut_g[i] = i; + crtc->lut_b[i] = i; + } + return 0; +} + +static void ast_encoder_destroy(struct drm_encoder *encoder) +{ + drm_encoder_cleanup(encoder); + kfree(encoder); +} + + +static struct drm_encoder *ast_best_single_encoder(struct drm_connector *connector) +{ + int enc_id = connector->encoder_ids[0]; + struct drm_mode_object *obj; + struct drm_encoder *encoder; + + /* pick the encoder ids */ + if (enc_id) { + obj = drm_mode_object_find(connector->dev, enc_id, DRM_MODE_OBJECT_ENCODER); + if (!obj) + return NULL; + encoder = obj_to_encoder(obj); + return encoder; + } + return NULL; +} + + +static const struct drm_encoder_funcs ast_enc_funcs = { + .destroy = ast_encoder_destroy, +}; + +static void ast_encoder_dpms(struct drm_encoder *encoder, int mode) +{ + +} + +static bool ast_mode_fixup(struct drm_encoder *encoder, + struct drm_display_mode *mode, + struct drm_display_mode *adjusted_mode) +{ + return true; +} + +static void ast_encoder_mode_set(struct drm_encoder *encoder, + struct drm_display_mode *mode, + struct drm_display_mode *adjusted_mode) +{ +} + +static void ast_encoder_prepare(struct drm_encoder *encoder) +{ + +} + +static void ast_encoder_commit(struct drm_encoder *encoder) +{ + +} + + +static const struct drm_encoder_helper_funcs ast_enc_helper_funcs = { + .dpms = ast_encoder_dpms, + .mode_fixup = ast_mode_fixup, + .prepare = ast_encoder_prepare, + .commit = ast_encoder_commit, + .mode_set = ast_encoder_mode_set, +}; + +int ast_encoder_init(struct drm_device *dev) +{ + struct ast_encoder *ast_encoder; + + ast_encoder = kzalloc(sizeof(struct ast_encoder), GFP_KERNEL); + if (!ast_encoder) + return -ENOMEM; + + drm_encoder_init(dev, &ast_encoder->base, &ast_enc_funcs, + DRM_MODE_ENCODER_DAC); + drm_encoder_helper_add(&ast_encoder->base, &ast_enc_helper_funcs); + + ast_encoder->base.possible_crtcs = 1; + return 0; +} + +static int ast_get_modes(struct drm_connector *connector) +{ + struct ast_connector *ast_connector = to_ast_connector(connector); + struct edid *edid; + int ret; + + edid = drm_get_edid(connector, &ast_connector->i2c->adapter); + if (edid) { + drm_mode_connector_update_edid_property(&ast_connector->base, edid); + ret = drm_add_edid_modes(connector, edid); + return ret; + } else + drm_mode_connector_update_edid_property(&ast_connector->base, NULL); + return 0; +} + +static int ast_mode_valid(struct drm_connector *connector, + struct drm_display_mode *mode) +{ + return MODE_OK; +} + +static void ast_connector_destroy(struct drm_connector *connector) +{ + struct ast_connector *ast_connector = to_ast_connector(connector); + ast_i2c_destroy(ast_connector->i2c); + drm_sysfs_connector_remove(connector); + drm_connector_cleanup(connector); + kfree(connector); +} + +static enum drm_connector_status +ast_connector_detect(struct drm_connector *connector, bool force) +{ + return connector_status_connected; +} + +static const struct drm_connector_helper_funcs ast_connector_helper_funcs = { + .mode_valid = ast_mode_valid, + .get_modes = ast_get_modes, + .best_encoder = ast_best_single_encoder, +}; + +static const struct drm_connector_funcs ast_connector_funcs = { + .dpms = drm_helper_connector_dpms, + .detect = ast_connector_detect, + .fill_modes = drm_helper_probe_single_connector_modes, + .destroy = ast_connector_destroy, +}; + +int ast_connector_init(struct drm_device *dev) +{ + struct ast_connector *ast_connector; + struct drm_connector *connector; + struct drm_encoder *encoder; + + ast_connector = kzalloc(sizeof(struct ast_connector), GFP_KERNEL); + if (!ast_connector) + return -ENOMEM; + + connector = &ast_connector->base; + drm_connector_init(dev, connector, &ast_connector_funcs, DRM_MODE_CONNECTOR_VGA); + + drm_connector_helper_add(connector, &ast_connector_helper_funcs); + + connector->interlace_allowed = 0; + connector->doublescan_allowed = 0; + + drm_sysfs_connector_add(connector); + + connector->polled = DRM_CONNECTOR_POLL_CONNECT; + + encoder = list_first_entry(&dev->mode_config.encoder_list, struct drm_encoder, head); + drm_mode_connector_attach_encoder(connector, encoder); + + ast_connector->i2c = ast_i2c_create(dev); + if (!ast_connector->i2c) + DRM_ERROR("failed to add ddc bus for connector\n"); + + return 0; +} + +/* allocate cursor cache and pin at start of VRAM */ +int ast_cursor_init(struct drm_device *dev) +{ + struct ast_private *ast = dev->dev_private; + int size; + int ret; + struct drm_gem_object *obj; + struct ast_bo *bo; + uint64_t gpu_addr; + + size = (AST_HWC_SIZE + AST_HWC_SIGNATURE_SIZE) * AST_DEFAULT_HWC_NUM; + + ret = ast_gem_create(dev, size, true, &obj); + if (ret) + return ret; + bo = gem_to_ast_bo(obj); + ret = ast_bo_reserve(bo, false); + if (unlikely(ret != 0)) + goto fail; + + ret = ast_bo_pin(bo, TTM_PL_FLAG_VRAM, &gpu_addr); + ast_bo_unreserve(bo); + if (ret) + goto fail; + + /* kmap the object */ + ret = ttm_bo_kmap(&bo->bo, 0, bo->bo.num_pages, &ast->cache_kmap); + if (ret) + goto fail; + + ast->cursor_cache = obj; + ast->cursor_cache_gpu_addr = gpu_addr; + DRM_ERROR("pinned cursor cache at %llx\n", ast->cursor_cache_gpu_addr); + return 0; +fail: + return ret; +} + +void ast_cursor_fini(struct drm_device *dev) +{ + struct ast_private *ast = dev->dev_private; + ttm_bo_kunmap(&ast->cache_kmap); + drm_gem_object_unreference_unlocked(ast->cursor_cache); +} + +int ast_mode_init(struct drm_device *dev) +{ + ast_cursor_init(dev); + ast_crtc_init(dev); + ast_encoder_init(dev); + ast_connector_init(dev); + return 0; +} + +void ast_mode_fini(struct drm_device *dev) +{ + ast_cursor_fini(dev); +} + +static int get_clock(void *i2c_priv) +{ + struct ast_i2c_chan *i2c = i2c_priv; + struct ast_private *ast = i2c->dev->dev_private; + uint32_t val; + + val = ast_get_index_reg_mask(ast, AST_IO_CRTC_PORT, 0xb7, 0x10) >> 4; + return val & 1 ? 1 : 0; +} + +static int get_data(void *i2c_priv) +{ + struct ast_i2c_chan *i2c = i2c_priv; + struct ast_private *ast = i2c->dev->dev_private; + uint32_t val; + + val = ast_get_index_reg_mask(ast, AST_IO_CRTC_PORT, 0xb7, 0x20) >> 5; + return val & 1 ? 1 : 0; +} + +static void set_clock(void *i2c_priv, int clock) +{ + struct ast_i2c_chan *i2c = i2c_priv; + struct ast_private *ast = i2c->dev->dev_private; + int i; + u8 ujcrb7, jtemp; + + for (i = 0; i < 0x10000; i++) { + ujcrb7 = ((clock & 0x01) ? 0 : 1); + ast_set_index_reg_mask(ast, AST_IO_CRTC_PORT, 0xb7, 0xfe, ujcrb7); + jtemp = ast_get_index_reg_mask(ast, AST_IO_CRTC_PORT, 0xb7, 0x01); + if (ujcrb7 == jtemp) + break; + } +} + +static void set_data(void *i2c_priv, int data) +{ + struct ast_i2c_chan *i2c = i2c_priv; + struct ast_private *ast = i2c->dev->dev_private; + int i; + u8 ujcrb7, jtemp; + + for (i = 0; i < 0x10000; i++) { + ujcrb7 = ((data & 0x01) ? 0 : 1) << 2; + ast_set_index_reg_mask(ast, AST_IO_CRTC_PORT, 0xb7, 0xfb, ujcrb7); + jtemp = ast_get_index_reg_mask(ast, AST_IO_CRTC_PORT, 0xb7, 0x04); + if (ujcrb7 == jtemp) + break; + } +} + +static struct ast_i2c_chan *ast_i2c_create(struct drm_device *dev) +{ + struct ast_i2c_chan *i2c; + int ret; + + i2c = kzalloc(sizeof(struct ast_i2c_chan), GFP_KERNEL); + if (!i2c) + return NULL; + + i2c->adapter.owner = THIS_MODULE; + i2c->adapter.class = I2C_CLASS_DDC; + i2c->adapter.dev.parent = &dev->pdev->dev; + i2c->dev = dev; + i2c_set_adapdata(&i2c->adapter, i2c); + snprintf(i2c->adapter.name, sizeof(i2c->adapter.name), + "AST i2c bit bus"); + i2c->adapter.algo_data = &i2c->bit; + + i2c->bit.udelay = 20; + i2c->bit.timeout = 2; + i2c->bit.data = i2c; + i2c->bit.setsda = set_data; + i2c->bit.setscl = set_clock; + i2c->bit.getsda = get_data; + i2c->bit.getscl = get_clock; + ret = i2c_bit_add_bus(&i2c->adapter); + if (ret) { + DRM_ERROR("Failed to register bit i2c\n"); + goto out_free; + } + + return i2c; +out_free: + kfree(i2c); + return NULL; +} + +static void ast_i2c_destroy(struct ast_i2c_chan *i2c) +{ + if (!i2c) + return; + i2c_del_adapter(&i2c->adapter); + kfree(i2c); +} + +void ast_show_cursor(struct drm_crtc *crtc) +{ + struct ast_private *ast = crtc->dev->dev_private; + u8 jreg; + + jreg = 0x2; + /* enable ARGB cursor */ + jreg |= 1; + ast_set_index_reg_mask(ast, AST_IO_CRTC_PORT, 0xcb, 0xfc, jreg); +} + +void ast_hide_cursor(struct drm_crtc *crtc) +{ + struct ast_private *ast = crtc->dev->dev_private; + ast_set_index_reg_mask(ast, AST_IO_CRTC_PORT, 0xcb, 0xfc, 0x00); +} + +static u32 copy_cursor_image(u8 *src, u8 *dst, int width, int height) +{ + union { + u32 ul; + u8 b[4]; + } srcdata32[2], data32; + union { + u16 us; + u8 b[2]; + } data16; + u32 csum = 0; + s32 alpha_dst_delta, last_alpha_dst_delta; + u8 *srcxor, *dstxor; + int i, j; + u32 per_pixel_copy, two_pixel_copy; + + alpha_dst_delta = AST_MAX_HWC_WIDTH << 1; + last_alpha_dst_delta = alpha_dst_delta - (width << 1); + + srcxor = src; + dstxor = (u8 *)dst + last_alpha_dst_delta + (AST_MAX_HWC_HEIGHT - height) * alpha_dst_delta; + per_pixel_copy = width & 1; + two_pixel_copy = width >> 1; + + for (j = 0; j < height; j++) { + for (i = 0; i < two_pixel_copy; i++) { + srcdata32[0].ul = *((u32 *)srcxor) & 0xf0f0f0f0; + srcdata32[1].ul = *((u32 *)(srcxor + 4)) & 0xf0f0f0f0; + data32.b[0] = srcdata32[0].b[1] | (srcdata32[0].b[0] >> 4); + data32.b[1] = srcdata32[0].b[3] | (srcdata32[0].b[2] >> 4); + data32.b[2] = srcdata32[0].b[1] | (srcdata32[1].b[0] >> 4); + data32.b[3] = srcdata32[0].b[3] | (srcdata32[1].b[2] >> 4); + + writel(data32.ul, dstxor); + csum += data32.ul; + + dstxor += 4; + srcxor += 8; + + } + + for (i = 0; i < per_pixel_copy; i++) { + srcdata32[0].ul = *((u32 *)srcxor) & 0xf0f0f0f0; + data16.b[0] = srcdata32[0].b[1] | (srcdata32[0].b[0] >> 4); + data16.b[1] = srcdata32[0].b[3] | (srcdata32[0].b[2] >> 4); + writew(data16.us, dstxor); + csum += (u32)data16.us; + + dstxor += 2; + srcxor += 4; + } + dstxor += last_alpha_dst_delta; + } + return csum; +} + +static int ast_cursor_set(struct drm_crtc *crtc, + struct drm_file *file_priv, + uint32_t handle, + uint32_t width, + uint32_t height) +{ + struct ast_private *ast = crtc->dev->dev_private; + struct ast_crtc *ast_crtc = to_ast_crtc(crtc); + struct drm_gem_object *obj; + struct ast_bo *bo; + uint64_t gpu_addr; + u32 csum; + int ret; + struct ttm_bo_kmap_obj uobj_map; + u8 *src, *dst; + bool src_isiomem, dst_isiomem; + if (!handle) { + ast_hide_cursor(crtc); + return 0; + } + + if (width > AST_MAX_HWC_WIDTH || height > AST_MAX_HWC_HEIGHT) + return -EINVAL; + + obj = drm_gem_object_lookup(crtc->dev, file_priv, handle); + if (!obj) { + DRM_ERROR("Cannot find cursor object %x for crtc\n", handle); + return -ENOENT; + } + bo = gem_to_ast_bo(obj); + + ret = ast_bo_reserve(bo, false); + if (ret) + goto fail; + + ret = ttm_bo_kmap(&bo->bo, 0, bo->bo.num_pages, &uobj_map); + + src = ttm_kmap_obj_virtual(&uobj_map, &src_isiomem); + dst = ttm_kmap_obj_virtual(&ast->cache_kmap, &dst_isiomem); + + if (src_isiomem == true) + DRM_ERROR("src cursor bo should be in main memory\n"); + if (dst_isiomem == false) + DRM_ERROR("dst bo should be in VRAM\n"); + + dst += (AST_HWC_SIZE + AST_HWC_SIGNATURE_SIZE)*ast->next_cursor; + + /* do data transfer to cursor cache */ + csum = copy_cursor_image(src, dst, width, height); + + /* write checksum + signature */ + ttm_bo_kunmap(&uobj_map); + ast_bo_unreserve(bo); + { + u8 *dst = (u8 *)ast->cache_kmap.virtual + (AST_HWC_SIZE + AST_HWC_SIGNATURE_SIZE)*ast->next_cursor + AST_HWC_SIZE; + writel(csum, dst); + writel(width, dst + AST_HWC_SIGNATURE_SizeX); + writel(height, dst + AST_HWC_SIGNATURE_SizeY); + writel(0, dst + AST_HWC_SIGNATURE_HOTSPOTX); + writel(0, dst + AST_HWC_SIGNATURE_HOTSPOTY); + + /* set pattern offset */ + gpu_addr = ast->cursor_cache_gpu_addr; + gpu_addr += (AST_HWC_SIZE + AST_HWC_SIGNATURE_SIZE)*ast->next_cursor; + gpu_addr >>= 3; + ast_set_index_reg(ast, AST_IO_CRTC_PORT, 0xc8, gpu_addr & 0xff); + ast_set_index_reg(ast, AST_IO_CRTC_PORT, 0xc9, (gpu_addr >> 8) & 0xff); + ast_set_index_reg(ast, AST_IO_CRTC_PORT, 0xca, (gpu_addr >> 16) & 0xff); + } + ast_crtc->cursor_width = width; + ast_crtc->cursor_height = height; + ast_crtc->offset_x = AST_MAX_HWC_WIDTH - width; + ast_crtc->offset_y = AST_MAX_HWC_WIDTH - height; + + ast->next_cursor = (ast->next_cursor + 1) % AST_DEFAULT_HWC_NUM; + + ast_show_cursor(crtc); + + drm_gem_object_unreference_unlocked(obj); + return 0; +fail: + drm_gem_object_unreference_unlocked(obj); + return ret; +} + +static int ast_cursor_move(struct drm_crtc *crtc, + int x, int y) +{ + struct ast_crtc *ast_crtc = to_ast_crtc(crtc); + struct ast_private *ast = crtc->dev->dev_private; + int x_offset, y_offset; + u8 *sig; + + sig = (u8 *)ast->cache_kmap.virtual + (AST_HWC_SIZE + AST_HWC_SIGNATURE_SIZE)*ast->next_cursor + AST_HWC_SIZE; + writel(x, sig + AST_HWC_SIGNATURE_X); + writel(y, sig + AST_HWC_SIGNATURE_Y); + + x_offset = ast_crtc->offset_x; + y_offset = ast_crtc->offset_y; + if (x < 0) { + x_offset = (-x) + ast_crtc->offset_x; + x = 0; + } + + if (y < 0) { + y_offset = (-y) + ast_crtc->offset_y; + y = 0; + } + ast_set_index_reg(ast, AST_IO_CRTC_PORT, 0xc2, x_offset); + ast_set_index_reg(ast, AST_IO_CRTC_PORT, 0xc3, y_offset); + ast_set_index_reg(ast, AST_IO_CRTC_PORT, 0xc4, (x & 0xff)); + ast_set_index_reg(ast, AST_IO_CRTC_PORT, 0xc5, ((x >> 8) & 0x0f)); + ast_set_index_reg(ast, AST_IO_CRTC_PORT, 0xc6, (y & 0xff)); + ast_set_index_reg(ast, AST_IO_CRTC_PORT, 0xc7, ((y >> 8) & 0x07)); + + /* dummy write to fire HWC */ + ast_set_index_reg_mask(ast, AST_IO_CRTC_PORT, 0xCB, 0xFF, 0x00); + + return 0; +} diff --git a/drivers/gpu/drm/ast/ast_post.c b/drivers/gpu/drm/ast/ast_post.c new file mode 100644 index 0000000..6edbee6 --- /dev/null +++ b/drivers/gpu/drm/ast/ast_post.c @@ -0,0 +1,1780 @@ +/* + * Copyright 2012 Red Hat Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sub license, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDERS, AUTHORS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, + * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR + * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE + * USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * The above copyright notice and this permission notice (including the + * next paragraph) shall be included in all copies or substantial portions + * of the Software. + * + */ +/* + * Authors: Dave Airlie <airlied@redhat.com> + */ + +#include "drmP.h" +#include "ast_drv.h" + +#include "ast_dram_tables.h" + +static void ast_init_dram_2300(struct drm_device *dev); + +static void +ast_enable_vga(struct drm_device *dev) +{ + struct ast_private *ast = dev->dev_private; + + ast_io_write8(ast, 0x43, 0x01); + ast_io_write8(ast, 0x42, 0x01); +} + +#if 0 /* will use later */ +static bool +ast_is_vga_enabled(struct drm_device *dev) +{ + struct ast_private *ast = dev->dev_private; + u8 ch; + + if (ast->chip == AST1180) { + /* TODO 1180 */ + } else { + ch = ast_io_read8(ast, 0x43); + if (ch) { + ast_open_key(ast); + ch = ast_get_index_reg_mask(ast, AST_IO_CRTC_PORT, 0xb6, 0xff); + return ch & 0x04; + } + } + return 0; +} +#endif + +static const u8 extreginfo[] = { 0x0f, 0x04, 0x1c, 0xff }; +static const u8 extreginfo_ast2300a0[] = { 0x0f, 0x04, 0x1c, 0xff }; +static const u8 extreginfo_ast2300[] = { 0x0f, 0x04, 0x1f, 0xff }; + +static void +ast_set_def_ext_reg(struct drm_device *dev) +{ + struct ast_private *ast = dev->dev_private; + u8 i, index, reg; + const u8 *ext_reg_info; + + /* reset scratch */ + for (i = 0x81; i <= 0x8f; i++) + ast_set_index_reg(ast, AST_IO_CRTC_PORT, i, 0x00); + + if (ast->chip == AST2300) { + if (dev->pdev->revision >= 0x20) + ext_reg_info = extreginfo_ast2300; + else + ext_reg_info = extreginfo_ast2300a0; + } else + ext_reg_info = extreginfo; + + index = 0xa0; + while (*ext_reg_info != 0xff) { + ast_set_index_reg_mask(ast, AST_IO_CRTC_PORT, index, 0x00, *ext_reg_info); + index++; + ext_reg_info++; + } + + /* disable standard IO/MEM decode if secondary */ + /* ast_set_index_reg-mask(ast, AST_IO_CRTC_PORT, 0xa1, 0xff, 0x3); */ + + /* Set Ext. Default */ + ast_set_index_reg_mask(ast, AST_IO_CRTC_PORT, 0x8c, 0x00, 0x01); + ast_set_index_reg_mask(ast, AST_IO_CRTC_PORT, 0xb7, 0x00, 0x00); + + /* Enable RAMDAC for A1 */ + reg = 0x04; + if (ast->chip == AST2300) + reg |= 0x20; + ast_set_index_reg_mask(ast, AST_IO_CRTC_PORT, 0xb6, 0xff, reg); +} + +static inline u32 mindwm(struct ast_private *ast, u32 r) +{ + ast_write32(ast, 0xf004, r & 0xffff0000); + ast_write32(ast, 0xf000, 0x1); + + return ast_read32(ast, 0x10000 + (r & 0x0000ffff)); +} + +static inline void moutdwm(struct ast_private *ast, u32 r, u32 v) +{ + ast_write32(ast, 0xf004, r & 0xffff0000); + ast_write32(ast, 0xf000, 0x1); + ast_write32(ast, 0x10000 + (r & 0x0000ffff), v); +} + +/* + * AST2100/2150 DLL CBR Setting + */ +#define CBR_SIZE_AST2150 ((16 << 10) - 1) +#define CBR_PASSNUM_AST2150 5 +#define CBR_THRESHOLD_AST2150 10 +#define CBR_THRESHOLD2_AST2150 10 +#define TIMEOUT_AST2150 5000000 + +#define CBR_PATNUM_AST2150 8 + +static const u32 pattern_AST2150[14] = { + 0xFF00FF00, + 0xCC33CC33, + 0xAA55AA55, + 0xFFFE0001, + 0x683501FE, + 0x0F1929B0, + 0x2D0B4346, + 0x60767F02, + 0x6FBE36A6, + 0x3A253035, + 0x3019686D, + 0x41C6167E, + 0x620152BF, + 0x20F050E0 +}; + +static u32 mmctestburst2_ast2150(struct ast_private *ast, u32 datagen) +{ + u32 data, timeout; + + moutdwm(ast, 0x1e6e0070, 0x00000000); + moutdwm(ast, 0x1e6e0070, 0x00000001 | (datagen << 3)); + timeout = 0; + do { + data = mindwm(ast, 0x1e6e0070) & 0x40; + if (++timeout > TIMEOUT_AST2150) { + moutdwm(ast, 0x1e6e0070, 0x00000000); + return 0xffffffff; + } + } while (!data); + moutdwm(ast, 0x1e6e0070, 0x00000000); + moutdwm(ast, 0x1e6e0070, 0x00000003 | (datagen << 3)); + timeout = 0; + do { + data = mindwm(ast, 0x1e6e0070) & 0x40; + if (++timeout > TIMEOUT_AST2150) { + moutdwm(ast, 0x1e6e0070, 0x00000000); + return 0xffffffff; + } + } while (!data); + data = (mindwm(ast, 0x1e6e0070) & 0x80) >> 7; + moutdwm(ast, 0x1e6e0070, 0x00000000); + return data; +} + +#if 0 /* unused in DDX driver - here for completeness */ +static u32 mmctestsingle2_ast2150(struct ast_private *ast, u32 datagen) +{ + u32 data, timeout; + + moutdwm(ast, 0x1e6e0070, 0x00000000); + moutdwm(ast, 0x1e6e0070, 0x00000005 | (datagen << 3)); + timeout = 0; + do { + data = mindwm(ast, 0x1e6e0070) & 0x40; + if (++timeout > TIMEOUT_AST2150) { + moutdwm(ast, 0x1e6e0070, 0x00000000); + return 0xffffffff; + } + } while (!data); + data = (mindwm(ast, 0x1e6e0070) & 0x80) >> 7; + moutdwm(ast, 0x1e6e0070, 0x00000000); + return data; +} +#endif + +static int cbrtest_ast2150(struct ast_private *ast) +{ + int i; + + for (i = 0; i < 8; i++) + if (mmctestburst2_ast2150(ast, i)) + return 0; + return 1; +} + +static int cbrscan_ast2150(struct ast_private *ast, int busw) +{ + u32 patcnt, loop; + + for (patcnt = 0; patcnt < CBR_PATNUM_AST2150; patcnt++) { + moutdwm(ast, 0x1e6e007c, pattern_AST2150[patcnt]); + for (loop = 0; loop < CBR_PASSNUM_AST2150; loop++) { + if (cbrtest_ast2150(ast)) + break; + } + if (loop == CBR_PASSNUM_AST2150) + return 0; + } + return 1; +} + + +static void cbrdlli_ast2150(struct ast_private *ast, int busw) +{ + u32 dll_min[4], dll_max[4], dlli, data, passcnt; + +cbr_start: + dll_min[0] = dll_min[1] = dll_min[2] = dll_min[3] = 0xff; + dll_max[0] = dll_max[1] = dll_max[2] = dll_max[3] = 0x0; + passcnt = 0; + + for (dlli = 0; dlli < 100; dlli++) { + moutdwm(ast, 0x1e6e0068, dlli | (dlli << 8) | (dlli << 16) | (dlli << 24)); + data = cbrscan_ast2150(ast, busw); + if (data != 0) { + if (data & 0x1) { + if (dll_min[0] > dlli) + dll_min[0] = dlli; + if (dll_max[0] < dlli) + dll_max[0] = dlli; + } + passcnt++; + } else if (passcnt >= CBR_THRESHOLD_AST2150) + goto cbr_start; + } + if (dll_max[0] == 0 || (dll_max[0]-dll_min[0]) < CBR_THRESHOLD_AST2150) + goto cbr_start; + + dlli = dll_min[0] + (((dll_max[0] - dll_min[0]) * 7) >> 4); + moutdwm(ast, 0x1e6e0068, dlli | (dlli << 8) | (dlli << 16) | (dlli << 24)); +} + + + +static void ast_init_dram_reg(struct drm_device *dev) +{ + struct ast_private *ast = dev->dev_private; + u8 j; + u32 data, temp, i; + const struct ast_dramstruct *dram_reg_info; + + j = ast_get_index_reg_mask(ast, AST_IO_CRTC_PORT, 0xd0, 0xff); + + if ((j & 0x80) == 0) { /* VGA only */ + if (ast->chip == AST2000) { + dram_reg_info = ast2000_dram_table_data; + ast_write32(ast, 0xf004, 0x1e6e0000); + ast_write32(ast, 0xf000, 0x1); + ast_write32(ast, 0x10100, 0xa8); + + do { + ; + } while (ast_read32(ast, 0x10100) != 0xa8); + } else {/* AST2100/1100 */ + if (ast->chip == AST2100 || ast->chip == 2200) + dram_reg_info = ast2100_dram_table_data; + else + dram_reg_info = ast1100_dram_table_data; + + ast_write32(ast, 0xf004, 0x1e6e0000); + ast_write32(ast, 0xf000, 0x1); + ast_write32(ast, 0x12000, 0x1688A8A8); + do { + ; + } while (ast_read32(ast, 0x12000) != 0x01); + + ast_write32(ast, 0x10000, 0xfc600309); + do { + ; + } while (ast_read32(ast, 0x10000) != 0x01); + } + + while (dram_reg_info->index != 0xffff) { + if (dram_reg_info->index == 0xff00) {/* delay fn */ + for (i = 0; i < 15; i++) + udelay(dram_reg_info->data); + } else if (dram_reg_info->index == 0x4 && ast->chip != AST2000) { + data = dram_reg_info->data; + if (ast->dram_type == AST_DRAM_1Gx16) + data = 0x00000d89; + else if (ast->dram_type == AST_DRAM_1Gx32) + data = 0x00000c8d; + + temp = ast_read32(ast, 0x12070); + temp &= 0xc; + temp <<= 2; + ast_write32(ast, 0x10000 + dram_reg_info->index, data | temp); + } else + ast_write32(ast, 0x10000 + dram_reg_info->index, dram_reg_info->data); + dram_reg_info++; + } + + /* AST 2100/2150 DRAM calibration */ + data = ast_read32(ast, 0x10120); + if (data == 0x5061) { /* 266Mhz */ + data = ast_read32(ast, 0x10004); + if (data & 0x40) + cbrdlli_ast2150(ast, 16); /* 16 bits */ + else + cbrdlli_ast2150(ast, 32); /* 32 bits */ + } + + switch (ast->chip) { + case AST2000: + temp = ast_read32(ast, 0x10140); + ast_write32(ast, 0x10140, temp | 0x40); + break; + case AST1100: + case AST2100: + case AST2200: + case AST2150: + temp = ast_read32(ast, 0x1200c); + ast_write32(ast, 0x1200c, temp & 0xfffffffd); + temp = ast_read32(ast, 0x12040); + ast_write32(ast, 0x12040, temp | 0x40); + break; + default: + break; + } + } + + /* wait ready */ + do { + j = ast_get_index_reg_mask(ast, AST_IO_CRTC_PORT, 0xd0, 0xff); + } while ((j & 0x40) == 0); +} + +void ast_post_gpu(struct drm_device *dev) +{ + u32 reg; + struct ast_private *ast = dev->dev_private; + + pci_read_config_dword(ast->dev->pdev, 0x04, ®); + reg |= 0x3; + pci_write_config_dword(ast->dev->pdev, 0x04, reg); + + ast_enable_vga(dev); + ast_open_key(ast); + ast_set_def_ext_reg(dev); + + if (ast->chip == AST2300) + ast_init_dram_2300(dev); + else + ast_init_dram_reg(dev); +} + +/* AST 2300 DRAM settings */ +#define AST_DDR3 0 +#define AST_DDR2 1 + +struct ast2300_dram_param { + u32 dram_type; + u32 dram_chipid; + u32 dram_freq; + u32 vram_size; + u32 odt; + u32 wodt; + u32 rodt; + u32 dram_config; + u32 reg_PERIOD; + u32 reg_MADJ; + u32 reg_SADJ; + u32 reg_MRS; + u32 reg_EMRS; + u32 reg_AC1; + u32 reg_AC2; + u32 reg_DQSIC; + u32 reg_DRV; + u32 reg_IOZ; + u32 reg_DQIDLY; + u32 reg_FREQ; + u32 madj_max; + u32 dll2_finetune_step; +}; + +/* + * DQSI DLL CBR Setting + */ +#define CBR_SIZE1 ((4 << 10) - 1) +#define CBR_SIZE2 ((64 << 10) - 1) +#define CBR_PASSNUM 5 +#define CBR_PASSNUM2 5 +#define CBR_THRESHOLD 10 +#define CBR_THRESHOLD2 10 +#define TIMEOUT 5000000 +#define CBR_PATNUM 8 + +static const u32 pattern[8] = { + 0xFF00FF00, + 0xCC33CC33, + 0xAA55AA55, + 0x88778877, + 0x92CC4D6E, + 0x543D3CDE, + 0xF1E843C7, + 0x7C61D253 +}; + +#if 0 /* unused in DDX, included for completeness */ +static int mmc_test_burst(struct ast_private *ast, u32 datagen) +{ + u32 data, timeout; + + moutdwm(ast, 0x1e6e0070, 0x00000000); + moutdwm(ast, 0x1e6e0070, 0x000000c1 | (datagen << 3)); + timeout = 0; + do { + data = mindwm(ast, 0x1e6e0070) & 0x3000; + if (data & 0x2000) { + return 0; + } + if (++timeout > TIMEOUT) { + moutdwm(ast, 0x1e6e0070, 0x00000000); + return 0; + } + } while (!data); + moutdwm(ast, 0x1e6e0070, 0x00000000); + return 1; +} +#endif + +static int mmc_test_burst2(struct ast_private *ast, u32 datagen) +{ + u32 data, timeout; + + moutdwm(ast, 0x1e6e0070, 0x00000000); + moutdwm(ast, 0x1e6e0070, 0x00000041 | (datagen << 3)); + timeout = 0; + do { + data = mindwm(ast, 0x1e6e0070) & 0x1000; + if (++timeout > TIMEOUT) { + moutdwm(ast, 0x1e6e0070, 0x0); + return -1; + } + } while (!data); + data = mindwm(ast, 0x1e6e0078); + data = (data | (data >> 16)) & 0xffff; + moutdwm(ast, 0x1e6e0070, 0x0); + return data; +} + +#if 0 /* Unused in DDX here for completeness */ +static int mmc_test_single(struct ast_private *ast, u32 datagen) +{ + u32 data, timeout; + + moutdwm(ast, 0x1e6e0070, 0x00000000); + moutdwm(ast, 0x1e6e0070, 0x000000c5 | (datagen << 3)); + timeout = 0; + do { + data = mindwm(ast, 0x1e6e0070) & 0x3000; + if (data & 0x2000) + return 0; + if (++timeout > TIMEOUT) { + moutdwm(ast, 0x1e6e0070, 0x0); + return 0; + } + } while (!data); + moutdwm(ast, 0x1e6e0070, 0x0); + return 1; +} +#endif + +static int mmc_test_single2(struct ast_private *ast, u32 datagen) +{ + u32 data, timeout; + + moutdwm(ast, 0x1e6e0070, 0x00000000); + moutdwm(ast, 0x1e6e0070, 0x00000005 | (datagen << 3)); + timeout = 0; + do { + data = mindwm(ast, 0x1e6e0070) & 0x1000; + if (++timeout > TIMEOUT) { + moutdwm(ast, 0x1e6e0070, 0x0); + return -1; + } + } while (!data); + data = mindwm(ast, 0x1e6e0078); + data = (data | (data >> 16)) & 0xffff; + moutdwm(ast, 0x1e6e0070, 0x0); + return data; +} + +static int cbr_test(struct ast_private *ast) +{ + u32 data; + int i; + data = mmc_test_single2(ast, 0); + if ((data & 0xff) && (data & 0xff00)) + return 0; + for (i = 0; i < 8; i++) { + data = mmc_test_burst2(ast, i); + if ((data & 0xff) && (data & 0xff00)) + return 0; + } + if (!data) + return 3; + else if (data & 0xff) + return 2; + return 1; +} + +static int cbr_scan(struct ast_private *ast) +{ + u32 data, data2, patcnt, loop; + + data2 = 3; + for (patcnt = 0; patcnt < CBR_PATNUM; patcnt++) { + moutdwm(ast, 0x1e6e007c, pattern[patcnt]); + for (loop = 0; loop < CBR_PASSNUM2; loop++) { + if ((data = cbr_test(ast)) != 0) { + data2 &= data; + if (!data2) + return 0; + break; + } + } + if (loop == CBR_PASSNUM2) + return 0; + } + return data2; +} + +static u32 cbr_test2(struct ast_private *ast) +{ + u32 data; + + data = mmc_test_burst2(ast, 0); + if (data == 0xffff) + return 0; + data |= mmc_test_single2(ast, 0); + if (data == 0xffff) + return 0; + + return ~data & 0xffff; +} + +static u32 cbr_scan2(struct ast_private *ast) +{ + u32 data, data2, patcnt, loop; + + data2 = 0xffff; + for (patcnt = 0; patcnt < CBR_PATNUM; patcnt++) { + moutdwm(ast, 0x1e6e007c, pattern[patcnt]); + for (loop = 0; loop < CBR_PASSNUM2; loop++) { + if ((data = cbr_test2(ast)) != 0) { + data2 &= data; + if (!data) + return 0; + break; + } + } + if (loop == CBR_PASSNUM2) + return 0; + } + return data2; +} + +#if 0 /* unused in DDX - added for completeness */ +static void finetuneDQI(struct ast_private *ast, struct ast2300_dram_param *param) +{ + u32 gold_sadj[2], dllmin[16], dllmax[16], dlli, data, cnt, mask, passcnt; + + gold_sadj[0] = (mindwm(ast, 0x1E6E0024) >> 16) & 0xffff; + gold_sadj[1] = gold_sadj[0] >> 8; + gold_sadj[0] = gold_sadj[0] & 0xff; + gold_sadj[0] = (gold_sadj[0] + gold_sadj[1]) >> 1; + gold_sadj[1] = gold_sadj[0]; + + for (cnt = 0; cnt < 16; cnt++) { + dllmin[cnt] = 0xff; + dllmax[cnt] = 0x0; + } + passcnt = 0; + for (dlli = 0; dlli < 76; dlli++) { + moutdwm(ast, 0x1E6E0068, 0x00001400 | (dlli << 16) | (dlli << 24)); + /* Wait DQSI latch phase calibration */ + moutdwm(ast, 0x1E6E0074, 0x00000010); + moutdwm(ast, 0x1E6E0070, 0x00000003); + do { + data = mindwm(ast, 0x1E6E0070); + } while (!(data & 0x00001000)); + moutdwm(ast, 0x1E6E0070, 0x00000000); + + moutdwm(ast, 0x1E6E0074, CBR_SIZE1); + data = cbr_scan2(ast); + if (data != 0) { + mask = 0x00010001; + for (cnt = 0; cnt < 16; cnt++) { + if (data & mask) { + if (dllmin[cnt] > dlli) { + dllmin[cnt] = dlli; + } + if (dllmax[cnt] < dlli) { + dllmax[cnt] = dlli; + } + } + mask <<= 1; + } + passcnt++; + } else if (passcnt >= CBR_THRESHOLD) { + break; + } + } + data = 0; + for (cnt = 0; cnt < 8; cnt++) { + data >>= 3; + if ((dllmax[cnt] > dllmin[cnt]) && ((dllmax[cnt] - dllmin[cnt]) >= CBR_THRESHOLD)) { + dlli = (dllmin[cnt] + dllmax[cnt]) >> 1; + if (gold_sadj[0] >= dlli) { + dlli = (gold_sadj[0] - dlli) >> 1; + if (dlli > 3) { + dlli = 3; + } + } else { + dlli = (dlli - gold_sadj[0]) >> 1; + if (dlli > 4) { + dlli = 4; + } + dlli = (8 - dlli) & 0x7; + } + data |= dlli << 21; + } + } + moutdwm(ast, 0x1E6E0080, data); + + data = 0; + for (cnt = 8; cnt < 16; cnt++) { + data >>= 3; + if ((dllmax[cnt] > dllmin[cnt]) && ((dllmax[cnt] - dllmin[cnt]) >= CBR_THRESHOLD)) { + dlli = (dllmin[cnt] + dllmax[cnt]) >> 1; + if (gold_sadj[1] >= dlli) { + dlli = (gold_sadj[1] - dlli) >> 1; + if (dlli > 3) { + dlli = 3; + } else { + dlli = (dlli - 1) & 0x7; + } + } else { + dlli = (dlli - gold_sadj[1]) >> 1; + dlli += 1; + if (dlli > 4) { + dlli = 4; + } + dlli = (8 - dlli) & 0x7; + } + data |= dlli << 21; + } + } + moutdwm(ast, 0x1E6E0084, data); + +} /* finetuneDQI */ +#endif + +static void finetuneDQI_L(struct ast_private *ast, struct ast2300_dram_param *param) +{ + u32 gold_sadj[2], dllmin[16], dllmax[16], dlli, data, cnt, mask, passcnt; + +FINETUNE_START: + for (cnt = 0; cnt < 16; cnt++) { + dllmin[cnt] = 0xff; + dllmax[cnt] = 0x0; + } + passcnt = 0; + for (dlli = 0; dlli < 76; dlli++) { + moutdwm(ast, 0x1E6E0068, 0x00001400 | (dlli << 16) | (dlli << 24)); + /* Wait DQSI latch phase calibration */ + moutdwm(ast, 0x1E6E0074, 0x00000010); + moutdwm(ast, 0x1E6E0070, 0x00000003); + do { + data = mindwm(ast, 0x1E6E0070); + } while (!(data & 0x00001000)); + moutdwm(ast, 0x1E6E0070, 0x00000000); + + moutdwm(ast, 0x1E6E0074, CBR_SIZE1); + data = cbr_scan2(ast); + if (data != 0) { + mask = 0x00010001; + for (cnt = 0; cnt < 16; cnt++) { + if (data & mask) { + if (dllmin[cnt] > dlli) { + dllmin[cnt] = dlli; + } + if (dllmax[cnt] < dlli) { + dllmax[cnt] = dlli; + } + } + mask <<= 1; + } + passcnt++; + } else if (passcnt >= CBR_THRESHOLD2) { + break; + } + } + gold_sadj[0] = 0x0; + passcnt = 0; + for (cnt = 0; cnt < 16; cnt++) { + if ((dllmax[cnt] > dllmin[cnt]) && ((dllmax[cnt] - dllmin[cnt]) >= CBR_THRESHOLD2)) { + gold_sadj[0] += dllmin[cnt]; + passcnt++; + } + } + if (passcnt != 16) { + goto FINETUNE_START; + } + gold_sadj[0] = gold_sadj[0] >> 4; + gold_sadj[1] = gold_sadj[0]; + + data = 0; + for (cnt = 0; cnt < 8; cnt++) { + data >>= 3; + if ((dllmax[cnt] > dllmin[cnt]) && ((dllmax[cnt] - dllmin[cnt]) >= CBR_THRESHOLD2)) { + dlli = dllmin[cnt]; + if (gold_sadj[0] >= dlli) { + dlli = ((gold_sadj[0] - dlli) * 19) >> 5; + if (dlli > 3) { + dlli = 3; + } + } else { + dlli = ((dlli - gold_sadj[0]) * 19) >> 5; + if (dlli > 4) { + dlli = 4; + } + dlli = (8 - dlli) & 0x7; + } + data |= dlli << 21; + } + } + moutdwm(ast, 0x1E6E0080, data); + + data = 0; + for (cnt = 8; cnt < 16; cnt++) { + data >>= 3; + if ((dllmax[cnt] > dllmin[cnt]) && ((dllmax[cnt] - dllmin[cnt]) >= CBR_THRESHOLD2)) { + dlli = dllmin[cnt]; + if (gold_sadj[1] >= dlli) { + dlli = ((gold_sadj[1] - dlli) * 19) >> 5; + if (dlli > 3) { + dlli = 3; + } else { + dlli = (dlli - 1) & 0x7; + } + } else { + dlli = ((dlli - gold_sadj[1]) * 19) >> 5; + dlli += 1; + if (dlli > 4) { + dlli = 4; + } + dlli = (8 - dlli) & 0x7; + } + data |= dlli << 21; + } + } + moutdwm(ast, 0x1E6E0084, data); + +} /* finetuneDQI_L */ + +static void finetuneDQI_L2(struct ast_private *ast, struct ast2300_dram_param *param) +{ + u32 gold_sadj[2], dllmin[16], dllmax[16], dlli, data, cnt, mask, passcnt, data2; + + for (cnt = 0; cnt < 16; cnt++) { + dllmin[cnt] = 0xff; + dllmax[cnt] = 0x0; + } + passcnt = 0; + for (dlli = 0; dlli < 76; dlli++) { + moutdwm(ast, 0x1E6E0068, 0x00001400 | (dlli << 16) | (dlli << 24)); + /* Wait DQSI latch phase calibration */ + moutdwm(ast, 0x1E6E0074, 0x00000010); + moutdwm(ast, 0x1E6E0070, 0x00000003); + do { + data = mindwm(ast, 0x1E6E0070); + } while (!(data & 0x00001000)); + moutdwm(ast, 0x1E6E0070, 0x00000000); + + moutdwm(ast, 0x1E6E0074, CBR_SIZE2); + data = cbr_scan2(ast); + if (data != 0) { + mask = 0x00010001; + for (cnt = 0; cnt < 16; cnt++) { + if (data & mask) { + if (dllmin[cnt] > dlli) { + dllmin[cnt] = dlli; + } + if (dllmax[cnt] < dlli) { + dllmax[cnt] = dlli; + } + } + mask <<= 1; + } + passcnt++; + } else if (passcnt >= CBR_THRESHOLD2) { + break; + } + } + gold_sadj[0] = 0x0; + gold_sadj[1] = 0xFF; + for (cnt = 0; cnt < 8; cnt++) { + if ((dllmax[cnt] > dllmin[cnt]) && ((dllmax[cnt] - dllmin[cnt]) >= CBR_THRESHOLD2)) { + if (gold_sadj[0] < dllmin[cnt]) { + gold_sadj[0] = dllmin[cnt]; + } + if (gold_sadj[1] > dllmax[cnt]) { + gold_sadj[1] = dllmax[cnt]; + } + } + } + gold_sadj[0] = (gold_sadj[1] + gold_sadj[0]) >> 1; + gold_sadj[1] = mindwm(ast, 0x1E6E0080); + + data = 0; + for (cnt = 0; cnt < 8; cnt++) { + data >>= 3; + data2 = gold_sadj[1] & 0x7; + gold_sadj[1] >>= 3; + if ((dllmax[cnt] > dllmin[cnt]) && ((dllmax[cnt] - dllmin[cnt]) >= CBR_THRESHOLD2)) { + dlli = (dllmin[cnt] + dllmax[cnt]) >> 1; + if (gold_sadj[0] >= dlli) { + dlli = (gold_sadj[0] - dlli) >> 1; + if (dlli > 0) { + dlli = 1; + } + if (data2 != 3) { + data2 = (data2 + dlli) & 0x7; + } + } else { + dlli = (dlli - gold_sadj[0]) >> 1; + if (dlli > 0) { + dlli = 1; + } + if (data2 != 4) { + data2 = (data2 - dlli) & 0x7; + } + } + } + data |= data2 << 21; + } + moutdwm(ast, 0x1E6E0080, data); + + gold_sadj[0] = 0x0; + gold_sadj[1] = 0xFF; + for (cnt = 8; cnt < 16; cnt++) { + if ((dllmax[cnt] > dllmin[cnt]) && ((dllmax[cnt] - dllmin[cnt]) >= CBR_THRESHOLD2)) { + if (gold_sadj[0] < dllmin[cnt]) { + gold_sadj[0] = dllmin[cnt]; + } + if (gold_sadj[1] > dllmax[cnt]) { + gold_sadj[1] = dllmax[cnt]; + } + } + } + gold_sadj[0] = (gold_sadj[1] + gold_sadj[0]) >> 1; + gold_sadj[1] = mindwm(ast, 0x1E6E0084); + + data = 0; + for (cnt = 8; cnt < 16; cnt++) { + data >>= 3; + data2 = gold_sadj[1] & 0x7; + gold_sadj[1] >>= 3; + if ((dllmax[cnt] > dllmin[cnt]) && ((dllmax[cnt] - dllmin[cnt]) >= CBR_THRESHOLD2)) { + dlli = (dllmin[cnt] + dllmax[cnt]) >> 1; + if (gold_sadj[0] >= dlli) { + dlli = (gold_sadj[0] - dlli) >> 1; + if (dlli > 0) { + dlli = 1; + } + if (data2 != 3) { + data2 = (data2 + dlli) & 0x7; + } + } else { + dlli = (dlli - gold_sadj[0]) >> 1; + if (dlli > 0) { + dlli = 1; + } + if (data2 != 4) { + data2 = (data2 - dlli) & 0x7; + } + } + } + data |= data2 << 21; + } + moutdwm(ast, 0x1E6E0084, data); + +} /* finetuneDQI_L2 */ + +static void cbr_dll2(struct ast_private *ast, struct ast2300_dram_param *param) +{ + u32 dllmin[2], dllmax[2], dlli, data, data2, passcnt; + + + finetuneDQI_L(ast, param); + finetuneDQI_L2(ast, param); + +CBR_START2: + dllmin[0] = dllmin[1] = 0xff; + dllmax[0] = dllmax[1] = 0x0; + passcnt = 0; + for (dlli = 0; dlli < 76; dlli++) { + moutdwm(ast, 0x1E6E0068, 0x00001300 | (dlli << 16) | (dlli << 24)); + /* Wait DQSI latch phase calibration */ + moutdwm(ast, 0x1E6E0074, 0x00000010); + moutdwm(ast, 0x1E6E0070, 0x00000003); + do { + data = mindwm(ast, 0x1E6E0070); + } while (!(data & 0x00001000)); + moutdwm(ast, 0x1E6E0070, 0x00000000); + + moutdwm(ast, 0x1E6E0074, CBR_SIZE2); + data = cbr_scan(ast); + if (data != 0) { + if (data & 0x1) { + if (dllmin[0] > dlli) { + dllmin[0] = dlli; + } + if (dllmax[0] < dlli) { + dllmax[0] = dlli; + } + } + if (data & 0x2) { + if (dllmin[1] > dlli) { + dllmin[1] = dlli; + } + if (dllmax[1] < dlli) { + dllmax[1] = dlli; + } + } + passcnt++; + } else if (passcnt >= CBR_THRESHOLD) { + break; + } + } + if (dllmax[0] == 0 || (dllmax[0]-dllmin[0]) < CBR_THRESHOLD) { + goto CBR_START2; + } + if (dllmax[1] == 0 || (dllmax[1]-dllmin[1]) < CBR_THRESHOLD) { + goto CBR_START2; + } + dlli = (dllmin[1] + dllmax[1]) >> 1; + dlli <<= 8; + dlli += (dllmin[0] + dllmax[0]) >> 1; + moutdwm(ast, 0x1E6E0068, (mindwm(ast, 0x1E6E0068) & 0xFFFF) | (dlli << 16)); + + data = (mindwm(ast, 0x1E6E0080) >> 24) & 0x1F; + data2 = (mindwm(ast, 0x1E6E0018) & 0xff80ffff) | (data << 16); + moutdwm(ast, 0x1E6E0018, data2); + moutdwm(ast, 0x1E6E0024, 0x8001 | (data << 1) | (param->dll2_finetune_step << 8)); + + /* Wait DQSI latch phase calibration */ + moutdwm(ast, 0x1E6E0074, 0x00000010); + moutdwm(ast, 0x1E6E0070, 0x00000003); + do { + data = mindwm(ast, 0x1E6E0070); + } while (!(data & 0x00001000)); + moutdwm(ast, 0x1E6E0070, 0x00000000); + moutdwm(ast, 0x1E6E0070, 0x00000003); + do { + data = mindwm(ast, 0x1E6E0070); + } while (!(data & 0x00001000)); + moutdwm(ast, 0x1E6E0070, 0x00000000); +} /* CBRDLL2 */ + +static void get_ddr3_info(struct ast_private *ast, struct ast2300_dram_param *param) +{ + u32 trap, trap_AC2, trap_MRS; + + moutdwm(ast, 0x1E6E2000, 0x1688A8A8); + + /* Ger trap info */ + trap = (mindwm(ast, 0x1E6E2070) >> 25) & 0x3; + trap_AC2 = 0x00020000 + (trap << 16); + trap_AC2 |= 0x00300000 + ((trap & 0x2) << 19); + trap_MRS = 0x00000010 + (trap << 4); + trap_MRS |= ((trap & 0x2) << 18); + + param->reg_MADJ = 0x00034C4C; + param->reg_SADJ = 0x00001800; + param->reg_DRV = 0x000000F0; + param->reg_PERIOD = param->dram_freq; + param->rodt = 0; + + switch (param->dram_freq) { + case 336: + moutdwm(ast, 0x1E6E2020, 0x0190); + param->wodt = 0; + param->reg_AC1 = 0x22202725; + param->reg_AC2 = 0xAA007613 | trap_AC2; + param->reg_DQSIC = 0x000000BA; + param->reg_MRS = 0x04001400 | trap_MRS; + param->reg_EMRS = 0x00000000; + param->reg_IOZ = 0x00000034; + param->reg_DQIDLY = 0x00000074; + param->reg_FREQ = 0x00004DC0; + param->madj_max = 96; + param->dll2_finetune_step = 3; + break; + default: + case 396: + moutdwm(ast, 0x1E6E2020, 0x03F1); + param->wodt = 1; + param->reg_AC1 = 0x33302825; + param->reg_AC2 = 0xCC009617 | trap_AC2; + param->reg_DQSIC = 0x000000E2; + param->reg_MRS = 0x04001600 | trap_MRS; + param->reg_EMRS = 0x00000000; + param->reg_IOZ = 0x00000034; + param->reg_DRV = 0x000000FA; + param->reg_DQIDLY = 0x00000089; + param->reg_FREQ = 0x000050C0; + param->madj_max = 96; + param->dll2_finetune_step = 4; + + switch (param->dram_chipid) { + default: + case AST_DRAM_512Mx16: + case AST_DRAM_1Gx16: + param->reg_AC2 = 0xCC009617 | trap_AC2; + break; + case AST_DRAM_2Gx16: + param->reg_AC2 = 0xCC009622 | trap_AC2; + break; + case AST_DRAM_4Gx16: + param->reg_AC2 = 0xCC00963F | trap_AC2; + break; + } + break; + + case 408: + moutdwm(ast, 0x1E6E2020, 0x01F0); + param->wodt = 1; + param->reg_AC1 = 0x33302825; + param->reg_AC2 = 0xCC009617 | trap_AC2; + param->reg_DQSIC = 0x000000E2; + param->reg_MRS = 0x04001600 | trap_MRS; + param->reg_EMRS = 0x00000000; + param->reg_IOZ = 0x00000034; + param->reg_DRV = 0x000000FA; + param->reg_DQIDLY = 0x00000089; + param->reg_FREQ = 0x000050C0; + param->madj_max = 96; + param->dll2_finetune_step = 4; + + switch (param->dram_chipid) { + default: + case AST_DRAM_512Mx16: + case AST_DRAM_1Gx16: + param->reg_AC2 = 0xCC009617 | trap_AC2; + break; + case AST_DRAM_2Gx16: + param->reg_AC2 = 0xCC009622 | trap_AC2; + break; + case AST_DRAM_4Gx16: + param->reg_AC2 = 0xCC00963F | trap_AC2; + break; + } + + break; + case 456: + moutdwm(ast, 0x1E6E2020, 0x0230); + param->wodt = 0; + param->reg_AC1 = 0x33302926; + param->reg_AC2 = 0xCD44961A; + param->reg_DQSIC = 0x000000FC; + param->reg_MRS = 0x00081830; + param->reg_EMRS = 0x00000000; + param->reg_IOZ = 0x00000045; + param->reg_DQIDLY = 0x00000097; + param->reg_FREQ = 0x000052C0; + param->madj_max = 88; + param->dll2_finetune_step = 4; + break; + case 504: + moutdwm(ast, 0x1E6E2020, 0x0270); + param->wodt = 1; + param->reg_AC1 = 0x33302926; + param->reg_AC2 = 0xDE44A61D; + param->reg_DQSIC = 0x00000117; + param->reg_MRS = 0x00081A30; + param->reg_EMRS = 0x00000000; + param->reg_IOZ = 0x070000BB; + param->reg_DQIDLY = 0x000000A0; + param->reg_FREQ = 0x000054C0; + param->madj_max = 79; + param->dll2_finetune_step = 4; + break; + case 528: + moutdwm(ast, 0x1E6E2020, 0x0290); + param->wodt = 1; + param->rodt = 1; + param->reg_AC1 = 0x33302926; + param->reg_AC2 = 0xEF44B61E; + param->reg_DQSIC = 0x00000125; + param->reg_MRS = 0x00081A30; + param->reg_EMRS = 0x00000040; + param->reg_DRV = 0x000000F5; + param->reg_IOZ = 0x00000023; + param->reg_DQIDLY = 0x00000088; + param->reg_FREQ = 0x000055C0; + param->madj_max = 76; + param->dll2_finetune_step = 3; + break; + case 576: + moutdwm(ast, 0x1E6E2020, 0x0140); + param->reg_MADJ = 0x00136868; + param->reg_SADJ = 0x00004534; + param->wodt = 1; + param->rodt = 1; + param->reg_AC1 = 0x33302A37; + param->reg_AC2 = 0xEF56B61E; + param->reg_DQSIC = 0x0000013F; + param->reg_MRS = 0x00101A50; + param->reg_EMRS = 0x00000040; + param->reg_DRV = 0x000000FA; + param->reg_IOZ = 0x00000023; + param->reg_DQIDLY = 0x00000078; + param->reg_FREQ = 0x000057C0; + param->madj_max = 136; + param->dll2_finetune_step = 3; + break; + case 600: + moutdwm(ast, 0x1E6E2020, 0x02E1); + param->reg_MADJ = 0x00136868; + param->reg_SADJ = 0x00004534; + param->wodt = 1; + param->rodt = 1; + param->reg_AC1 = 0x32302A37; + param->reg_AC2 = 0xDF56B61F; + param->reg_DQSIC = 0x0000014D; + param->reg_MRS = 0x00101A50; + param->reg_EMRS = 0x00000004; + param->reg_DRV = 0x000000F5; + param->reg_IOZ = 0x00000023; + param->reg_DQIDLY = 0x00000078; + param->reg_FREQ = 0x000058C0; + param->madj_max = 132; + param->dll2_finetune_step = 3; + break; + case 624: + moutdwm(ast, 0x1E6E2020, 0x0160); + param->reg_MADJ = 0x00136868; + param->reg_SADJ = 0x00004534; + param->wodt = 1; + param->rodt = 1; + param->reg_AC1 = 0x32302A37; + param->reg_AC2 = 0xEF56B621; + param->reg_DQSIC = 0x0000015A; + param->reg_MRS = 0x02101A50; + param->reg_EMRS = 0x00000004; + param->reg_DRV = 0x000000F5; + param->reg_IOZ = 0x00000034; + param->reg_DQIDLY = 0x00000078; + param->reg_FREQ = 0x000059C0; + param->madj_max = 128; + param->dll2_finetune_step = 3; + break; + } /* switch freq */ + + switch (param->dram_chipid) { + case AST_DRAM_512Mx16: + param->dram_config = 0x130; + break; + default: + case AST_DRAM_1Gx16: + param->dram_config = 0x131; + break; + case AST_DRAM_2Gx16: + param->dram_config = 0x132; + break; + case AST_DRAM_4Gx16: + param->dram_config = 0x133; + break; + }; /* switch size */ + + switch (param->vram_size) { + default: + case AST_VIDMEM_SIZE_8M: + param->dram_config |= 0x00; + break; + case AST_VIDMEM_SIZE_16M: + param->dram_config |= 0x04; + break; + case AST_VIDMEM_SIZE_32M: + param->dram_config |= 0x08; + break; + case AST_VIDMEM_SIZE_64M: + param->dram_config |= 0x0c; + break; + } + +} + +static void ddr3_init(struct ast_private *ast, struct ast2300_dram_param *param) +{ + u32 data, data2; + + moutdwm(ast, 0x1E6E0000, 0xFC600309); + moutdwm(ast, 0x1E6E0018, 0x00000100); + moutdwm(ast, 0x1E6E0024, 0x00000000); + moutdwm(ast, 0x1E6E0034, 0x00000000); + udelay(10); + moutdwm(ast, 0x1E6E0064, param->reg_MADJ); + moutdwm(ast, 0x1E6E0068, param->reg_SADJ); + udelay(10); + moutdwm(ast, 0x1E6E0064, param->reg_MADJ | 0xC0000); + udelay(10); + + moutdwm(ast, 0x1E6E0004, param->dram_config); + moutdwm(ast, 0x1E6E0008, 0x90040f); + moutdwm(ast, 0x1E6E0010, param->reg_AC1); + moutdwm(ast, 0x1E6E0014, param->reg_AC2); + moutdwm(ast, 0x1E6E0020, param->reg_DQSIC); + moutdwm(ast, 0x1E6E0080, 0x00000000); + moutdwm(ast, 0x1E6E0084, 0x00000000); + moutdwm(ast, 0x1E6E0088, param->reg_DQIDLY); + moutdwm(ast, 0x1E6E0018, 0x4040A170); + moutdwm(ast, 0x1E6E0018, 0x20402370); + moutdwm(ast, 0x1E6E0038, 0x00000000); + moutdwm(ast, 0x1E6E0040, 0xFF444444); + moutdwm(ast, 0x1E6E0044, 0x22222222); + moutdwm(ast, 0x1E6E0048, 0x22222222); + moutdwm(ast, 0x1E6E004C, 0x00000002); + moutdwm(ast, 0x1E6E0050, 0x80000000); + moutdwm(ast, 0x1E6E0050, 0x00000000); + moutdwm(ast, 0x1E6E0054, 0); + moutdwm(ast, 0x1E6E0060, param->reg_DRV); + moutdwm(ast, 0x1E6E006C, param->reg_IOZ); + moutdwm(ast, 0x1E6E0070, 0x00000000); + moutdwm(ast, 0x1E6E0074, 0x00000000); + moutdwm(ast, 0x1E6E0078, 0x00000000); + moutdwm(ast, 0x1E6E007C, 0x00000000); + /* Wait MCLK2X lock to MCLK */ + do { + data = mindwm(ast, 0x1E6E001C); + } while (!(data & 0x08000000)); + moutdwm(ast, 0x1E6E0034, 0x00000001); + moutdwm(ast, 0x1E6E000C, 0x00005C04); + udelay(10); + moutdwm(ast, 0x1E6E000C, 0x00000000); + moutdwm(ast, 0x1E6E0034, 0x00000000); + data = mindwm(ast, 0x1E6E001C); + data = (data >> 8) & 0xff; + while ((data & 0x08) || ((data & 0x7) < 2) || (data < 4)) { + data2 = (mindwm(ast, 0x1E6E0064) & 0xfff3ffff) + 4; + if ((data2 & 0xff) > param->madj_max) { + break; + } + moutdwm(ast, 0x1E6E0064, data2); + if (data2 & 0x00100000) { + data2 = ((data2 & 0xff) >> 3) + 3; + } else { + data2 = ((data2 & 0xff) >> 2) + 5; + } + data = mindwm(ast, 0x1E6E0068) & 0xffff00ff; + data2 += data & 0xff; + data = data | (data2 << 8); + moutdwm(ast, 0x1E6E0068, data); + udelay(10); + moutdwm(ast, 0x1E6E0064, mindwm(ast, 0x1E6E0064) | 0xC0000); + udelay(10); + data = mindwm(ast, 0x1E6E0018) & 0xfffff1ff; + moutdwm(ast, 0x1E6E0018, data); + data = data | 0x200; + moutdwm(ast, 0x1E6E0018, data); + do { + data = mindwm(ast, 0x1E6E001C); + } while (!(data & 0x08000000)); + + moutdwm(ast, 0x1E6E0034, 0x00000001); + moutdwm(ast, 0x1E6E000C, 0x00005C04); + udelay(10); + moutdwm(ast, 0x1E6E000C, 0x00000000); + moutdwm(ast, 0x1E6E0034, 0x00000000); + data = mindwm(ast, 0x1E6E001C); + data = (data >> 8) & 0xff; + } + data = mindwm(ast, 0x1E6E0018) | 0xC00; + moutdwm(ast, 0x1E6E0018, data); + + moutdwm(ast, 0x1E6E0034, 0x00000001); + moutdwm(ast, 0x1E6E000C, 0x00000040); + udelay(50); + /* Mode Register Setting */ + moutdwm(ast, 0x1E6E002C, param->reg_MRS | 0x100); + moutdwm(ast, 0x1E6E0030, param->reg_EMRS); + moutdwm(ast, 0x1E6E0028, 0x00000005); + moutdwm(ast, 0x1E6E0028, 0x00000007); + moutdwm(ast, 0x1E6E0028, 0x00000003); + moutdwm(ast, 0x1E6E0028, 0x00000001); + moutdwm(ast, 0x1E6E002C, param->reg_MRS); + moutdwm(ast, 0x1E6E000C, 0x00005C08); + moutdwm(ast, 0x1E6E0028, 0x00000001); + + moutdwm(ast, 0x1E6E000C, 0x7FFF5C01); + data = 0; + if (param->wodt) { + data = 0x300; + } + if (param->rodt) { + data = data | 0x3000 | ((param->reg_AC2 & 0x60000) >> 3); + } + moutdwm(ast, 0x1E6E0034, data | 0x3); + + /* Wait DQI delay lock */ + do { + data = mindwm(ast, 0x1E6E0080); + } while (!(data & 0x40000000)); + /* Wait DQSI delay lock */ + do { + data = mindwm(ast, 0x1E6E0020); + } while (!(data & 0x00000800)); + /* Calibrate the DQSI delay */ + cbr_dll2(ast, param); + + moutdwm(ast, 0x1E6E0120, param->reg_FREQ); + /* ECC Memory Initialization */ +#ifdef ECC + moutdwm(ast, 0x1E6E007C, 0x00000000); + moutdwm(ast, 0x1E6E0070, 0x221); + do { + data = mindwm(ast, 0x1E6E0070); + } while (!(data & 0x00001000)); + moutdwm(ast, 0x1E6E0070, 0x00000000); + moutdwm(ast, 0x1E6E0050, 0x80000000); + moutdwm(ast, 0x1E6E0050, 0x00000000); +#endif + + +} + +static void get_ddr2_info(struct ast_private *ast, struct ast2300_dram_param *param) +{ + u32 trap, trap_AC2, trap_MRS; + + moutdwm(ast, 0x1E6E2000, 0x1688A8A8); + + /* Ger trap info */ + trap = (mindwm(ast, 0x1E6E2070) >> 25) & 0x3; + trap_AC2 = (trap << 20) | (trap << 16); + trap_AC2 += 0x00110000; + trap_MRS = 0x00000040 | (trap << 4); + + + param->reg_MADJ = 0x00034C4C; + param->reg_SADJ = 0x00001800; + param->reg_DRV = 0x000000F0; + param->reg_PERIOD = param->dram_freq; + param->rodt = 0; + + switch (param->dram_freq) { + case 264: + moutdwm(ast, 0x1E6E2020, 0x0130); + param->wodt = 0; + param->reg_AC1 = 0x11101513; + param->reg_AC2 = 0x78117011; + param->reg_DQSIC = 0x00000092; + param->reg_MRS = 0x00000842; + param->reg_EMRS = 0x00000000; + param->reg_DRV = 0x000000F0; + param->reg_IOZ = 0x00000034; + param->reg_DQIDLY = 0x0000005A; + param->reg_FREQ = 0x00004AC0; + param->madj_max = 138; + param->dll2_finetune_step = 3; + break; + case 336: + moutdwm(ast, 0x1E6E2020, 0x0190); + param->wodt = 1; + param->reg_AC1 = 0x22202613; + param->reg_AC2 = 0xAA009016 | trap_AC2; + param->reg_DQSIC = 0x000000BA; + param->reg_MRS = 0x00000A02 | trap_MRS; + param->reg_EMRS = 0x00000040; + param->reg_DRV = 0x000000FA; + param->reg_IOZ = 0x00000034; + param->reg_DQIDLY = 0x00000074; + param->reg_FREQ = 0x00004DC0; + param->madj_max = 96; + param->dll2_finetune_step = 3; + break; + default: + case 396: + moutdwm(ast, 0x1E6E2020, 0x03F1); + param->wodt = 1; + param->rodt = 0; + param->reg_AC1 = 0x33302714; + param->reg_AC2 = 0xCC00B01B | trap_AC2; + param->reg_DQSIC = 0x000000E2; + param->reg_MRS = 0x00000C02 | trap_MRS; + param->reg_EMRS = 0x00000040; + param->reg_DRV = 0x000000FA; + param->reg_IOZ = 0x00000034; + param->reg_DQIDLY = 0x00000089; + param->reg_FREQ = 0x000050C0; + param->madj_max = 96; + param->dll2_finetune_step = 4; + + switch (param->dram_chipid) { + case AST_DRAM_512Mx16: + param->reg_AC2 = 0xCC00B016 | trap_AC2; + break; + default: + case AST_DRAM_1Gx16: + param->reg_AC2 = 0xCC00B01B | trap_AC2; + break; + case AST_DRAM_2Gx16: + param->reg_AC2 = 0xCC00B02B | trap_AC2; + break; + case AST_DRAM_4Gx16: + param->reg_AC2 = 0xCC00B03F | trap_AC2; + break; + } + + break; + + case 408: + moutdwm(ast, 0x1E6E2020, 0x01F0); + param->wodt = 1; + param->rodt = 0; + param->reg_AC1 = 0x33302714; + param->reg_AC2 = 0xCC00B01B | trap_AC2; + param->reg_DQSIC = 0x000000E2; + param->reg_MRS = 0x00000C02 | trap_MRS; + param->reg_EMRS = 0x00000040; + param->reg_DRV = 0x000000FA; + param->reg_IOZ = 0x00000034; + param->reg_DQIDLY = 0x00000089; + param->reg_FREQ = 0x000050C0; + param->madj_max = 96; + param->dll2_finetune_step = 4; + + switch (param->dram_chipid) { + case AST_DRAM_512Mx16: + param->reg_AC2 = 0xCC00B016 | trap_AC2; + break; + default: + case AST_DRAM_1Gx16: + param->reg_AC2 = 0xCC00B01B | trap_AC2; + break; + case AST_DRAM_2Gx16: + param->reg_AC2 = 0xCC00B02B | trap_AC2; + break; + case AST_DRAM_4Gx16: + param->reg_AC2 = 0xCC00B03F | trap_AC2; + break; + } + + break; + case 456: + moutdwm(ast, 0x1E6E2020, 0x0230); + param->wodt = 0; + param->reg_AC1 = 0x33302815; + param->reg_AC2 = 0xCD44B01E; + param->reg_DQSIC = 0x000000FC; + param->reg_MRS = 0x00000E72; + param->reg_EMRS = 0x00000000; + param->reg_DRV = 0x00000000; + param->reg_IOZ = 0x00000034; + param->reg_DQIDLY = 0x00000097; + param->reg_FREQ = 0x000052C0; + param->madj_max = 88; + param->dll2_finetune_step = 3; + break; + case 504: + moutdwm(ast, 0x1E6E2020, 0x0261); + param->wodt = 1; + param->rodt = 1; + param->reg_AC1 = 0x33302815; + param->reg_AC2 = 0xDE44C022; + param->reg_DQSIC = 0x00000117; + param->reg_MRS = 0x00000E72; + param->reg_EMRS = 0x00000040; + param->reg_DRV = 0x0000000A; + param->reg_IOZ = 0x00000045; + param->reg_DQIDLY = 0x000000A0; + param->reg_FREQ = 0x000054C0; + param->madj_max = 79; + param->dll2_finetune_step = 3; + break; + case 528: + moutdwm(ast, 0x1E6E2020, 0x0120); + param->wodt = 1; + param->rodt = 1; + param->reg_AC1 = 0x33302815; + param->reg_AC2 = 0xEF44D024; + param->reg_DQSIC = 0x00000125; + param->reg_MRS = 0x00000E72; + param->reg_EMRS = 0x00000004; + param->reg_DRV = 0x000000F9; + param->reg_IOZ = 0x00000045; + param->reg_DQIDLY = 0x000000A7; + param->reg_FREQ = 0x000055C0; + param->madj_max = 76; + param->dll2_finetune_step = 3; + break; + case 552: + moutdwm(ast, 0x1E6E2020, 0x02A1); + param->wodt = 1; + param->rodt = 1; + param->reg_AC1 = 0x43402915; + param->reg_AC2 = 0xFF44E025; + param->reg_DQSIC = 0x00000132; + param->reg_MRS = 0x00000E72; + param->reg_EMRS = 0x00000040; + param->reg_DRV = 0x0000000A; + param->reg_IOZ = 0x00000045; + param->reg_DQIDLY = 0x000000AD; + param->reg_FREQ = 0x000056C0; + param->madj_max = 76; + param->dll2_finetune_step = 3; + break; + case 576: + moutdwm(ast, 0x1E6E2020, 0x0140); + param->wodt = 1; + param->rodt = 1; + param->reg_AC1 = 0x43402915; + param->reg_AC2 = 0xFF44E027; + param->reg_DQSIC = 0x0000013F; + param->reg_MRS = 0x00000E72; + param->reg_EMRS = 0x00000004; + param->reg_DRV = 0x000000F5; + param->reg_IOZ = 0x00000045; + param->reg_DQIDLY = 0x000000B3; + param->reg_FREQ = 0x000057C0; + param->madj_max = 76; + param->dll2_finetune_step = 3; + break; + } + + switch (param->dram_chipid) { + case AST_DRAM_512Mx16: + param->dram_config = 0x100; + break; + default: + case AST_DRAM_1Gx16: + param->dram_config = 0x121; + break; + case AST_DRAM_2Gx16: + param->dram_config = 0x122; + break; + case AST_DRAM_4Gx16: + param->dram_config = 0x123; + break; + }; /* switch size */ + + switch (param->vram_size) { + default: + case AST_VIDMEM_SIZE_8M: + param->dram_config |= 0x00; + break; + case AST_VIDMEM_SIZE_16M: + param->dram_config |= 0x04; + break; + case AST_VIDMEM_SIZE_32M: + param->dram_config |= 0x08; + break; + case AST_VIDMEM_SIZE_64M: + param->dram_config |= 0x0c; + break; + } +} + +static void ddr2_init(struct ast_private *ast, struct ast2300_dram_param *param) +{ + u32 data, data2; + + moutdwm(ast, 0x1E6E0000, 0xFC600309); + moutdwm(ast, 0x1E6E0018, 0x00000100); + moutdwm(ast, 0x1E6E0024, 0x00000000); + moutdwm(ast, 0x1E6E0064, param->reg_MADJ); + moutdwm(ast, 0x1E6E0068, param->reg_SADJ); + udelay(10); + moutdwm(ast, 0x1E6E0064, param->reg_MADJ | 0xC0000); + udelay(10); + + moutdwm(ast, 0x1E6E0004, param->dram_config); + moutdwm(ast, 0x1E6E0008, 0x90040f); + moutdwm(ast, 0x1E6E0010, param->reg_AC1); + moutdwm(ast, 0x1E6E0014, param->reg_AC2); + moutdwm(ast, 0x1E6E0020, param->reg_DQSIC); + moutdwm(ast, 0x1E6E0080, 0x00000000); + moutdwm(ast, 0x1E6E0084, 0x00000000); + moutdwm(ast, 0x1E6E0088, param->reg_DQIDLY); + moutdwm(ast, 0x1E6E0018, 0x4040A130); + moutdwm(ast, 0x1E6E0018, 0x20402330); + moutdwm(ast, 0x1E6E0038, 0x00000000); + moutdwm(ast, 0x1E6E0040, 0xFF808000); + moutdwm(ast, 0x1E6E0044, 0x88848466); + moutdwm(ast, 0x1E6E0048, 0x44440008); + moutdwm(ast, 0x1E6E004C, 0x00000000); + moutdwm(ast, 0x1E6E0050, 0x80000000); + moutdwm(ast, 0x1E6E0050, 0x00000000); + moutdwm(ast, 0x1E6E0054, 0); + moutdwm(ast, 0x1E6E0060, param->reg_DRV); + moutdwm(ast, 0x1E6E006C, param->reg_IOZ); + moutdwm(ast, 0x1E6E0070, 0x00000000); + moutdwm(ast, 0x1E6E0074, 0x00000000); + moutdwm(ast, 0x1E6E0078, 0x00000000); + moutdwm(ast, 0x1E6E007C, 0x00000000); + + /* Wait MCLK2X lock to MCLK */ + do { + data = mindwm(ast, 0x1E6E001C); + } while (!(data & 0x08000000)); + moutdwm(ast, 0x1E6E0034, 0x00000001); + moutdwm(ast, 0x1E6E000C, 0x00005C04); + udelay(10); + moutdwm(ast, 0x1E6E000C, 0x00000000); + moutdwm(ast, 0x1E6E0034, 0x00000000); + data = mindwm(ast, 0x1E6E001C); + data = (data >> 8) & 0xff; + while ((data & 0x08) || ((data & 0x7) < 2) || (data < 4)) { + data2 = (mindwm(ast, 0x1E6E0064) & 0xfff3ffff) + 4; + if ((data2 & 0xff) > param->madj_max) { + break; + } + moutdwm(ast, 0x1E6E0064, data2); + if (data2 & 0x00100000) { + data2 = ((data2 & 0xff) >> 3) + 3; + } else { + data2 = ((data2 & 0xff) >> 2) + 5; + } + data = mindwm(ast, 0x1E6E0068) & 0xffff00ff; + data2 += data & 0xff; + data = data | (data2 << 8); + moutdwm(ast, 0x1E6E0068, data); + udelay(10); + moutdwm(ast, 0x1E6E0064, mindwm(ast, 0x1E6E0064) | 0xC0000); + udelay(10); + data = mindwm(ast, 0x1E6E0018) & 0xfffff1ff; + moutdwm(ast, 0x1E6E0018, data); + data = data | 0x200; + moutdwm(ast, 0x1E6E0018, data); + do { + data = mindwm(ast, 0x1E6E001C); + } while (!(data & 0x08000000)); + + moutdwm(ast, 0x1E6E0034, 0x00000001); + moutdwm(ast, 0x1E6E000C, 0x00005C04); + udelay(10); + moutdwm(ast, 0x1E6E000C, 0x00000000); + moutdwm(ast, 0x1E6E0034, 0x00000000); + data = mindwm(ast, 0x1E6E001C); + data = (data >> 8) & 0xff; + } + data = mindwm(ast, 0x1E6E0018) | 0xC00; + moutdwm(ast, 0x1E6E0018, data); + + moutdwm(ast, 0x1E6E0034, 0x00000001); + moutdwm(ast, 0x1E6E000C, 0x00000000); + udelay(50); + /* Mode Register Setting */ + moutdwm(ast, 0x1E6E002C, param->reg_MRS | 0x100); + moutdwm(ast, 0x1E6E0030, param->reg_EMRS); + moutdwm(ast, 0x1E6E0028, 0x00000005); + moutdwm(ast, 0x1E6E0028, 0x00000007); + moutdwm(ast, 0x1E6E0028, 0x00000003); + moutdwm(ast, 0x1E6E0028, 0x00000001); + + moutdwm(ast, 0x1E6E000C, 0x00005C08); + moutdwm(ast, 0x1E6E002C, param->reg_MRS); + moutdwm(ast, 0x1E6E0028, 0x00000001); + moutdwm(ast, 0x1E6E0030, param->reg_EMRS | 0x380); + moutdwm(ast, 0x1E6E0028, 0x00000003); + moutdwm(ast, 0x1E6E0030, param->reg_EMRS); + moutdwm(ast, 0x1E6E0028, 0x00000003); + + moutdwm(ast, 0x1E6E000C, 0x7FFF5C01); + data = 0; + if (param->wodt) { + data = 0x500; + } + if (param->rodt) { + data = data | 0x3000 | ((param->reg_AC2 & 0x60000) >> 3); + } + moutdwm(ast, 0x1E6E0034, data | 0x3); + moutdwm(ast, 0x1E6E0120, param->reg_FREQ); + + /* Wait DQI delay lock */ + do { + data = mindwm(ast, 0x1E6E0080); + } while (!(data & 0x40000000)); + /* Wait DQSI delay lock */ + do { + data = mindwm(ast, 0x1E6E0020); + } while (!(data & 0x00000800)); + /* Calibrate the DQSI delay */ + cbr_dll2(ast, param); + + /* ECC Memory Initialization */ +#ifdef ECC + moutdwm(ast, 0x1E6E007C, 0x00000000); + moutdwm(ast, 0x1E6E0070, 0x221); + do { + data = mindwm(ast, 0x1E6E0070); + } while (!(data & 0x00001000)); + moutdwm(ast, 0x1E6E0070, 0x00000000); + moutdwm(ast, 0x1E6E0050, 0x80000000); + moutdwm(ast, 0x1E6E0050, 0x00000000); +#endif + +} + +static void ast_init_dram_2300(struct drm_device *dev) +{ + struct ast_private *ast = dev->dev_private; + struct ast2300_dram_param param; + u32 temp; + u8 reg; + + reg = ast_get_index_reg_mask(ast, AST_IO_CRTC_PORT, 0xd0, 0xff); + if ((reg & 0x80) == 0) {/* vga only */ + ast_write32(ast, 0xf004, 0x1e6e0000); + ast_write32(ast, 0xf000, 0x1); + ast_write32(ast, 0x12000, 0x1688a8a8); + do { + ; + } while (ast_read32(ast, 0x12000) != 0x1); + + ast_write32(ast, 0x10000, 0xfc600309); + do { + ; + } while (ast_read32(ast, 0x10000) != 0x1); + + /* Slow down CPU/AHB CLK in VGA only mode */ + temp = ast_read32(ast, 0x12008); + temp |= 0x73; + ast_write32(ast, 0x12008, temp); + + param.dram_type = AST_DDR3; + if (temp & 0x01000000) + param.dram_type = AST_DDR2; + param.dram_chipid = ast->dram_type; + param.dram_freq = ast->mclk; + param.vram_size = ast->vram_size; + + if (param.dram_type == AST_DDR3) { + get_ddr3_info(ast, ¶m); + ddr3_init(ast, ¶m); + } else { + get_ddr2_info(ast, ¶m); + ddr2_init(ast, ¶m); + } + + temp = mindwm(ast, 0x1e6e2040); + moutdwm(ast, 0x1e6e2040, temp | 0x40); + } + + /* wait ready */ + do { + reg = ast_get_index_reg_mask(ast, AST_IO_CRTC_PORT, 0xd0, 0xff); + } while ((reg & 0x40) == 0); +} + diff --git a/drivers/gpu/drm/ast/ast_tables.h b/drivers/gpu/drm/ast/ast_tables.h new file mode 100644 index 0000000..95fa6ab --- /dev/null +++ b/drivers/gpu/drm/ast/ast_tables.h @@ -0,0 +1,265 @@ +/* + * Copyright (c) 2005 ASPEED Technology Inc. + * + * Permission to use, copy, modify, distribute, and sell this software and its + * documentation for any purpose is hereby granted without fee, provided that + * the above copyright notice appear in all copies and that both that + * copyright notice and this permission notice appear in supporting + * documentation, and that the name of the authors not be used in + * advertising or publicity pertaining to distribution of the software without + * specific, written prior permission. The authors makes no representations + * about the suitability of this software for any purpose. It is provided + * "as is" without express or implied warranty. + * + * THE AUTHORS DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, + * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO + * EVENT SHALL THE AUTHORS BE LIABLE FOR ANY SPECIAL, INDIRECT OR + * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, + * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ +/* Ported from xf86-video-ast driver */ + +#ifndef AST_TABLES_H +#define AST_TABLES_H + +/* Std. Table Index Definition */ +#define TextModeIndex 0 +#define EGAModeIndex 1 +#define VGAModeIndex 2 +#define HiCModeIndex 3 +#define TrueCModeIndex 4 + +#define Charx8Dot 0x00000001 +#define HalfDCLK 0x00000002 +#define DoubleScanMode 0x00000004 +#define LineCompareOff 0x00000008 +#define SyncPP 0x00000000 +#define SyncPN 0x00000040 +#define SyncNP 0x00000080 +#define SyncNN 0x000000C0 +#define HBorder 0x00000020 +#define VBorder 0x00000010 +#define WideScreenMode 0x00000100 + + +/* DCLK Index */ +#define VCLK25_175 0x00 +#define VCLK28_322 0x01 +#define VCLK31_5 0x02 +#define VCLK36 0x03 +#define VCLK40 0x04 +#define VCLK49_5 0x05 +#define VCLK50 0x06 +#define VCLK56_25 0x07 +#define VCLK65 0x08 +#define VCLK75 0x09 +#define VCLK78_75 0x0A +#define VCLK94_5 0x0B +#define VCLK108 0x0C +#define VCLK135 0x0D +#define VCLK157_5 0x0E +#define VCLK162 0x0F +/* #define VCLK193_25 0x10 */ +#define VCLK154 0x10 +#define VCLK83_5 0x11 +#define VCLK106_5 0x12 +#define VCLK146_25 0x13 +#define VCLK148_5 0x14 + +static struct ast_vbios_dclk_info dclk_table[] = { + {0x2C, 0xE7, 0x03}, /* 00: VCLK25_175 */ + {0x95, 0x62, 0x03}, /* 01: VCLK28_322 */ + {0x67, 0x63, 0x01}, /* 02: VCLK31_5 */ + {0x76, 0x63, 0x01}, /* 03: VCLK36 */ + {0xEE, 0x67, 0x01}, /* 04: VCLK40 */ + {0x82, 0x62, 0x01}, /* 05: VCLK49_5 */ + {0xC6, 0x64, 0x01}, /* 06: VCLK50 */ + {0x94, 0x62, 0x01}, /* 07: VCLK56_25 */ + {0x80, 0x64, 0x00}, /* 08: VCLK65 */ + {0x7B, 0x63, 0x00}, /* 09: VCLK75 */ + {0x67, 0x62, 0x00}, /* 0A: VCLK78_75 */ + {0x7C, 0x62, 0x00}, /* 0B: VCLK94_5 */ + {0x8E, 0x62, 0x00}, /* 0C: VCLK108 */ + {0x85, 0x24, 0x00}, /* 0D: VCLK135 */ + {0x67, 0x22, 0x00}, /* 0E: VCLK157_5 */ + {0x6A, 0x22, 0x00}, /* 0F: VCLK162 */ + {0x4d, 0x4c, 0x80}, /* 10: VCLK154 */ + {0xa7, 0x78, 0x80}, /* 11: VCLK83.5 */ + {0x28, 0x49, 0x80}, /* 12: VCLK106.5 */ + {0x37, 0x49, 0x80}, /* 13: VCLK146.25 */ + {0x1f, 0x45, 0x80}, /* 14: VCLK148.5 */ +}; + +static struct ast_vbios_stdtable vbios_stdtable[] = { + /* MD_2_3_400 */ + { + 0x67, + {0x00,0x03,0x00,0x02}, + {0x5f,0x4f,0x50,0x82,0x55,0x81,0xbf,0x1f, + 0x00,0x4f,0x0d,0x0e,0x00,0x00,0x00,0x00, + 0x9c,0x8e,0x8f,0x28,0x1f,0x96,0xb9,0xa3, + 0xff}, + {0x00,0x01,0x02,0x03,0x04,0x05,0x14,0x07, + 0x38,0x39,0x3a,0x3b,0x3c,0x3d,0x3e,0x3f, + 0x0c,0x00,0x0f,0x08}, + {0x00,0x00,0x00,0x00,0x00,0x10,0x0e,0x00, + 0xff} + }, + /* Mode12/ExtEGATable */ + { + 0xe3, + {0x01,0x0f,0x00,0x06}, + {0x5f,0x4f,0x50,0x82,0x55,0x81,0x0b,0x3e, + 0x00,0x40,0x00,0x00,0x00,0x00,0x00,0x00, + 0xe9,0x8b,0xdf,0x28,0x00,0xe7,0x04,0xe3, + 0xff}, + {0x00,0x01,0x02,0x03,0x04,0x05,0x14,0x07, + 0x38,0x39,0x3a,0x3b,0x3c,0x3d,0x3e,0x3f, + 0x01,0x00,0x0f,0x00}, + {0x00,0x00,0x00,0x00,0x00,0x00,0x05,0x0f, + 0xff} + }, + /* ExtVGATable */ + { + 0x2f, + {0x01,0x0f,0x00,0x0e}, + {0x5f,0x4f,0x50,0x82,0x54,0x80,0x0b,0x3e, + 0x00,0x40,0x00,0x00,0x00,0x00,0x00,0x00, + 0xea,0x8c,0xdf,0x28,0x40,0xe7,0x04,0xa3, + 0xff}, + {0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07, + 0x08,0x09,0x0a,0x0b,0x0c,0x0d,0x0e,0x0f, + 0x01,0x00,0x00,0x00}, + {0x00,0x00,0x00,0x00,0x00,0x40,0x05,0x0f, + 0xff} + }, + /* ExtHiCTable */ + { + 0x2f, + {0x01,0x0f,0x00,0x0e}, + {0x5f,0x4f,0x50,0x82,0x54,0x80,0x0b,0x3e, + 0x00,0x40,0x00,0x00,0x00,0x00,0x00,0x00, + 0xea,0x8c,0xdf,0x28,0x40,0xe7,0x04,0xa3, + 0xff}, + {0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07, + 0x08,0x09,0x0a,0x0b,0x0c,0x0d,0x0e,0x0f, + 0x01,0x00,0x00,0x00}, + {0x00,0x00,0x00,0x00,0x00,0x00,0x05,0x0f, + 0xff} + }, + /* ExtTrueCTable */ + { + 0x2f, + {0x01,0x0f,0x00,0x0e}, + {0x5f,0x4f,0x50,0x82,0x54,0x80,0x0b,0x3e, + 0x00,0x40,0x00,0x00,0x00,0x00,0x00,0x00, + 0xea,0x8c,0xdf,0x28,0x40,0xe7,0x04,0xa3, + 0xff}, + {0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07, + 0x08,0x09,0x0a,0x0b,0x0c,0x0d,0x0e,0x0f, + 0x01,0x00,0x00,0x00}, + {0x00,0x00,0x00,0x00,0x00,0x00,0x05,0x0f, + 0xff} + }, +}; + +static struct ast_vbios_enhtable res_640x480[] = { + { 800, 640, 8, 96, 525, 480, 2, 2, VCLK25_175, /* 60Hz */ + (SyncNN | HBorder | VBorder | Charx8Dot), 60, 1, 0x2E }, + { 832, 640, 16, 40, 520, 480, 1, 3, VCLK31_5, /* 72Hz */ + (SyncNN | HBorder | VBorder | Charx8Dot), 72, 2, 0x2E }, + { 840, 640, 16, 64, 500, 480, 1, 3, VCLK31_5, /* 75Hz */ + (SyncNN | Charx8Dot) , 75, 3, 0x2E }, + { 832, 640, 56, 56, 509, 480, 1, 3, VCLK36, /* 85Hz */ + (SyncNN | Charx8Dot) , 85, 4, 0x2E }, + { 832, 640, 56, 56, 509, 480, 1, 3, VCLK36, /* end */ + (SyncNN | Charx8Dot) , 0xFF, 4, 0x2E }, +}; + +static struct ast_vbios_enhtable res_800x600[] = { + {1024, 800, 24, 72, 625, 600, 1, 2, VCLK36, /* 56Hz */ + (SyncPP | Charx8Dot), 56, 1, 0x30 }, + {1056, 800, 40, 128, 628, 600, 1, 4, VCLK40, /* 60Hz */ + (SyncPP | Charx8Dot), 60, 2, 0x30 }, + {1040, 800, 56, 120, 666, 600, 37, 6, VCLK50, /* 72Hz */ + (SyncPP | Charx8Dot), 72, 3, 0x30 }, + {1056, 800, 16, 80, 625, 600, 1, 3, VCLK49_5, /* 75Hz */ + (SyncPP | Charx8Dot), 75, 4, 0x30 }, + {1048, 800, 32, 64, 631, 600, 1, 3, VCLK56_25, /* 85Hz */ + (SyncPP | Charx8Dot), 84, 5, 0x30 }, + {1048, 800, 32, 64, 631, 600, 1, 3, VCLK56_25, /* end */ + (SyncPP | Charx8Dot), 0xFF, 5, 0x30 }, +}; + + +static struct ast_vbios_enhtable res_1024x768[] = { + {1344, 1024, 24, 136, 806, 768, 3, 6, VCLK65, /* 60Hz */ + (SyncNN | Charx8Dot), 60, 1, 0x31 }, + {1328, 1024, 24, 136, 806, 768, 3, 6, VCLK75, /* 70Hz */ + (SyncNN | Charx8Dot), 70, 2, 0x31 }, + {1312, 1024, 16, 96, 800, 768, 1, 3, VCLK78_75, /* 75Hz */ + (SyncPP | Charx8Dot), 75, 3, 0x31 }, + {1376, 1024, 48, 96, 808, 768, 1, 3, VCLK94_5, /* 85Hz */ + (SyncPP | Charx8Dot), 84, 4, 0x31 }, + {1376, 1024, 48, 96, 808, 768, 1, 3, VCLK94_5, /* end */ + (SyncPP | Charx8Dot), 0xFF, 4, 0x31 }, +}; + +static struct ast_vbios_enhtable res_1280x1024[] = { + {1688, 1280, 48, 112, 1066, 1024, 1, 3, VCLK108, /* 60Hz */ + (SyncPP | Charx8Dot), 60, 1, 0x32 }, + {1688, 1280, 16, 144, 1066, 1024, 1, 3, VCLK135, /* 75Hz */ + (SyncPP | Charx8Dot), 75, 2, 0x32 }, + {1728, 1280, 64, 160, 1072, 1024, 1, 3, VCLK157_5, /* 85Hz */ + (SyncPP | Charx8Dot), 85, 3, 0x32 }, + {1728, 1280, 64, 160, 1072, 1024, 1, 3, VCLK157_5, /* end */ + (SyncPP | Charx8Dot), 0xFF, 3, 0x32 }, +}; + +static struct ast_vbios_enhtable res_1600x1200[] = { + {2160, 1600, 64, 192, 1250, 1200, 1, 3, VCLK162, /* 60Hz */ + (SyncPP | Charx8Dot), 60, 1, 0x33 }, + {2160, 1600, 64, 192, 1250, 1200, 1, 3, VCLK162, /* end */ + (SyncPP | Charx8Dot), 0xFF, 1, 0x33 }, +}; + +static struct ast_vbios_enhtable res_1920x1200[] = { + {2080, 1920, 48, 32, 1235, 1200, 3, 6, VCLK154, /* 60Hz */ + (SyncNP | Charx8Dot), 60, 1, 0x34 }, + {2080, 1920, 48, 32, 1235, 1200, 3, 6, VCLK154, /* 60Hz */ + (SyncNP | Charx8Dot), 0xFF, 1, 0x34 }, +}; + +/* 16:10 */ +static struct ast_vbios_enhtable res_1280x800[] = { + {1680, 1280, 72,128, 831, 800, 3, 6, VCLK83_5, /* 60Hz */ + (SyncPN | Charx8Dot | LineCompareOff | WideScreenMode), 60, 1, 0x35 }, + {1680, 1280, 72,128, 831, 800, 3, 6, VCLK83_5, /* 60Hz */ + (SyncPN | Charx8Dot | LineCompareOff | WideScreenMode), 0xFF, 1, 0x35 }, + +}; + +static struct ast_vbios_enhtable res_1440x900[] = { + {1904, 1440, 80,152, 934, 900, 3, 6, VCLK106_5, /* 60Hz */ + (SyncPN | Charx8Dot | LineCompareOff | WideScreenMode), 60, 1, 0x36 }, + {1904, 1440, 80,152, 934, 900, 3, 6, VCLK106_5, /* 60Hz */ + (SyncPN | Charx8Dot | LineCompareOff | WideScreenMode), 0xFF, 1, 0x36 }, +}; + +static struct ast_vbios_enhtable res_1680x1050[] = { + {2240, 1680,104,176, 1089, 1050, 3, 6, VCLK146_25, /* 60Hz */ + (SyncPN | Charx8Dot | LineCompareOff | WideScreenMode), 60, 1, 0x37 }, + {2240, 1680,104,176, 1089, 1050, 3, 6, VCLK146_25, /* 60Hz */ + (SyncPN | Charx8Dot | LineCompareOff | WideScreenMode), 0xFF, 1, 0x37 }, +}; + +/* HDTV */ +static struct ast_vbios_enhtable res_1920x1080[] = { + {2200, 1920, 88, 44, 1125, 1080, 4, 5, VCLK148_5, /* 60Hz */ + (SyncNP | Charx8Dot | LineCompareOff | WideScreenMode), 60, 1, 0x38 }, + {2200, 1920, 88, 44, 1125, 1080, 4, 5, VCLK148_5, /* 60Hz */ + (SyncNP | Charx8Dot | LineCompareOff | WideScreenMode), 0xFF, 1, 0x38 }, +}; +#endif diff --git a/drivers/gpu/drm/ast/ast_ttm.c b/drivers/gpu/drm/ast/ast_ttm.c new file mode 100644 index 0000000..aad12f7 --- /dev/null +++ b/drivers/gpu/drm/ast/ast_ttm.c @@ -0,0 +1,453 @@ +/* + * Copyright 2012 Red Hat Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sub license, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDERS, AUTHORS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, + * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR + * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE + * USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * The above copyright notice and this permission notice (including the + * next paragraph) shall be included in all copies or substantial portions + * of the Software. + * + */ +/* + * Authors: Dave Airlie <airlied@redhat.com> + */ +#include "drmP.h" +#include "ast_drv.h" +#include <ttm/ttm_page_alloc.h> + +static inline struct ast_private * +ast_bdev(struct ttm_bo_device *bd) +{ + return container_of(bd, struct ast_private, ttm.bdev); +} + +static int +ast_ttm_mem_global_init(struct drm_global_reference *ref) +{ + return ttm_mem_global_init(ref->object); +} + +static void +ast_ttm_mem_global_release(struct drm_global_reference *ref) +{ + ttm_mem_global_release(ref->object); +} + +static int ast_ttm_global_init(struct ast_private *ast) +{ + struct drm_global_reference *global_ref; + int r; + + global_ref = &ast->ttm.mem_global_ref; + global_ref->global_type = DRM_GLOBAL_TTM_MEM; + global_ref->size = sizeof(struct ttm_mem_global); + global_ref->init = &ast_ttm_mem_global_init; + global_ref->release = &ast_ttm_mem_global_release; + r = drm_global_item_ref(global_ref); + if (r != 0) { + DRM_ERROR("Failed setting up TTM memory accounting " + "subsystem.\n"); + return r; + } + + ast->ttm.bo_global_ref.mem_glob = + ast->ttm.mem_global_ref.object; + global_ref = &ast->ttm.bo_global_ref.ref; + global_ref->global_type = DRM_GLOBAL_TTM_BO; + global_ref->size = sizeof(struct ttm_bo_global); + global_ref->init = &ttm_bo_global_init; + global_ref->release = &ttm_bo_global_release; + r = drm_global_item_ref(global_ref); + if (r != 0) { + DRM_ERROR("Failed setting up TTM BO subsystem.\n"); + drm_global_item_unref(&ast->ttm.mem_global_ref); + return r; + } + return 0; +} + +void +ast_ttm_global_release(struct ast_private *ast) +{ + if (ast->ttm.mem_global_ref.release == NULL) + return; + + drm_global_item_unref(&ast->ttm.bo_global_ref.ref); + drm_global_item_unref(&ast->ttm.mem_global_ref); + ast->ttm.mem_global_ref.release = NULL; +} + + +static void ast_bo_ttm_destroy(struct ttm_buffer_object *tbo) +{ + struct ast_bo *bo; + + bo = container_of(tbo, struct ast_bo, bo); + + drm_gem_object_release(&bo->gem); + kfree(bo); +} + +bool ast_ttm_bo_is_ast_bo(struct ttm_buffer_object *bo) +{ + if (bo->destroy == &ast_bo_ttm_destroy) + return true; + return false; +} + +static int +ast_bo_init_mem_type(struct ttm_bo_device *bdev, uint32_t type, + struct ttm_mem_type_manager *man) +{ + switch (type) { + case TTM_PL_SYSTEM: + man->flags = TTM_MEMTYPE_FLAG_MAPPABLE; + man->available_caching = TTM_PL_MASK_CACHING; + man->default_caching = TTM_PL_FLAG_CACHED; + break; + case TTM_PL_VRAM: + man->func = &ttm_bo_manager_func; + man->flags = TTM_MEMTYPE_FLAG_FIXED | + TTM_MEMTYPE_FLAG_MAPPABLE; + man->available_caching = TTM_PL_FLAG_UNCACHED | + TTM_PL_FLAG_WC; + man->default_caching = TTM_PL_FLAG_WC; + break; + default: + DRM_ERROR("Unsupported memory type %u\n", (unsigned)type); + return -EINVAL; + } + return 0; +} + +static void +ast_bo_evict_flags(struct ttm_buffer_object *bo, struct ttm_placement *pl) +{ + struct ast_bo *astbo = ast_bo(bo); + + if (!ast_ttm_bo_is_ast_bo(bo)) + return; + + ast_ttm_placement(astbo, TTM_PL_FLAG_SYSTEM); + *pl = astbo->placement; +} + +static int ast_bo_verify_access(struct ttm_buffer_object *bo, struct file *filp) +{ + return 0; +} + +static int ast_ttm_io_mem_reserve(struct ttm_bo_device *bdev, + struct ttm_mem_reg *mem) +{ + struct ttm_mem_type_manager *man = &bdev->man[mem->mem_type]; + struct ast_private *ast = ast_bdev(bdev); + + mem->bus.addr = NULL; + mem->bus.offset = 0; + mem->bus.size = mem->num_pages << PAGE_SHIFT; + mem->bus.base = 0; + mem->bus.is_iomem = false; + if (!(man->flags & TTM_MEMTYPE_FLAG_MAPPABLE)) + return -EINVAL; + switch (mem->mem_type) { + case TTM_PL_SYSTEM: + /* system memory */ + return 0; + case TTM_PL_VRAM: + mem->bus.offset = mem->start << PAGE_SHIFT; + mem->bus.base = pci_resource_start(ast->dev->pdev, 0); + mem->bus.is_iomem = true; + break; + default: + return -EINVAL; + break; + } + return 0; +} + +static void ast_ttm_io_mem_free(struct ttm_bo_device *bdev, struct ttm_mem_reg *mem) +{ +} + +static int ast_bo_move(struct ttm_buffer_object *bo, + bool evict, bool interruptible, + bool no_wait_reserve, bool no_wait_gpu, + struct ttm_mem_reg *new_mem) +{ + int r; + r = ttm_bo_move_memcpy(bo, evict, no_wait_reserve, no_wait_gpu, new_mem); + return r; +} + + +static void ast_ttm_backend_destroy(struct ttm_tt *tt) +{ + ttm_tt_fini(tt); + kfree(tt); +} + +static struct ttm_backend_func ast_tt_backend_func = { + .destroy = &ast_ttm_backend_destroy, +}; + + +struct ttm_tt *ast_ttm_tt_create(struct ttm_bo_device *bdev, + unsigned long size, uint32_t page_flags, + struct page *dummy_read_page) +{ + struct ttm_tt *tt; + + tt = kzalloc(sizeof(struct ttm_tt), GFP_KERNEL); + if (tt == NULL) + return NULL; + tt->func = &ast_tt_backend_func; + if (ttm_tt_init(tt, bdev, size, page_flags, dummy_read_page)) { + kfree(tt); + return NULL; + } + return tt; +} + +static int ast_ttm_tt_populate(struct ttm_tt *ttm) +{ + return ttm_pool_populate(ttm); +} + +static void ast_ttm_tt_unpopulate(struct ttm_tt *ttm) +{ + ttm_pool_unpopulate(ttm); +} + +struct ttm_bo_driver ast_bo_driver = { + .ttm_tt_create = ast_ttm_tt_create, + .ttm_tt_populate = ast_ttm_tt_populate, + .ttm_tt_unpopulate = ast_ttm_tt_unpopulate, + .init_mem_type = ast_bo_init_mem_type, + .evict_flags = ast_bo_evict_flags, + .move = ast_bo_move, + .verify_access = ast_bo_verify_access, + .io_mem_reserve = &ast_ttm_io_mem_reserve, + .io_mem_free = &ast_ttm_io_mem_free, +}; + +int ast_mm_init(struct ast_private *ast) +{ + int ret; + struct drm_device *dev = ast->dev; + struct ttm_bo_device *bdev = &ast->ttm.bdev; + + ret = ast_ttm_global_init(ast); + if (ret) + return ret; + + ret = ttm_bo_device_init(&ast->ttm.bdev, + ast->ttm.bo_global_ref.ref.object, + &ast_bo_driver, DRM_FILE_PAGE_OFFSET, + true); + if (ret) { + DRM_ERROR("Error initialising bo driver; %d\n", ret); + return ret; + } + + ret = ttm_bo_init_mm(bdev, TTM_PL_VRAM, + ast->vram_size >> PAGE_SHIFT); + if (ret) { + DRM_ERROR("Failed ttm VRAM init: %d\n", ret); + return ret; + } + + ast->fb_mtrr = drm_mtrr_add(pci_resource_start(dev->pdev, 0), + pci_resource_len(dev->pdev, 0), + DRM_MTRR_WC); + + return 0; +} + +void ast_mm_fini(struct ast_private *ast) +{ + struct drm_device *dev = ast->dev; + ttm_bo_device_release(&ast->ttm.bdev); + + ast_ttm_global_release(ast); + + if (ast->fb_mtrr >= 0) { + drm_mtrr_del(ast->fb_mtrr, + pci_resource_start(dev->pdev, 0), + pci_resource_len(dev->pdev, 0), DRM_MTRR_WC); + ast->fb_mtrr = -1; + } +} + +void ast_ttm_placement(struct ast_bo *bo, int domain) +{ + u32 c = 0; + bo->placement.fpfn = 0; + bo->placement.lpfn = 0; + bo->placement.placement = bo->placements; + bo->placement.busy_placement = bo->placements; + if (domain & TTM_PL_FLAG_VRAM) + bo->placements[c++] = TTM_PL_FLAG_WC | TTM_PL_FLAG_UNCACHED | TTM_PL_FLAG_VRAM; + if (domain & TTM_PL_FLAG_SYSTEM) + bo->placements[c++] = TTM_PL_MASK_CACHING | TTM_PL_FLAG_SYSTEM; + if (!c) + bo->placements[c++] = TTM_PL_MASK_CACHING | TTM_PL_FLAG_SYSTEM; + bo->placement.num_placement = c; + bo->placement.num_busy_placement = c; +} + +int ast_bo_reserve(struct ast_bo *bo, bool no_wait) +{ + int ret; + + ret = ttm_bo_reserve(&bo->bo, true, no_wait, false, 0); + if (ret) { + if (ret != -ERESTARTSYS) + DRM_ERROR("reserve failed %p\n", bo); + return ret; + } + return 0; +} + +void ast_bo_unreserve(struct ast_bo *bo) +{ + ttm_bo_unreserve(&bo->bo); +} + +int ast_bo_create(struct drm_device *dev, int size, int align, + uint32_t flags, struct ast_bo **pastbo) +{ + struct ast_private *ast = dev->dev_private; + struct ast_bo *astbo; + size_t acc_size; + int ret; + + astbo = kzalloc(sizeof(struct ast_bo), GFP_KERNEL); + if (!astbo) + return -ENOMEM; + + ret = drm_gem_object_init(dev, &astbo->gem, size); + if (ret) { + kfree(astbo); + return ret; + } + + astbo->gem.driver_private = NULL; + astbo->bo.bdev = &ast->ttm.bdev; + + ast_ttm_placement(astbo, TTM_PL_FLAG_VRAM | TTM_PL_FLAG_SYSTEM); + + acc_size = ttm_bo_dma_acc_size(&ast->ttm.bdev, size, + sizeof(struct ast_bo)); + + ret = ttm_bo_init(&ast->ttm.bdev, &astbo->bo, size, + ttm_bo_type_device, &astbo->placement, + align >> PAGE_SHIFT, 0, false, NULL, acc_size, + ast_bo_ttm_destroy); + if (ret) + return ret; + + *pastbo = astbo; + return 0; +} + +static inline u64 ast_bo_gpu_offset(struct ast_bo *bo) +{ + return bo->bo.offset; +} + +int ast_bo_pin(struct ast_bo *bo, u32 pl_flag, u64 *gpu_addr) +{ + int i, ret; + + if (bo->pin_count) { + bo->pin_count++; + if (gpu_addr) + *gpu_addr = ast_bo_gpu_offset(bo); + } + + ast_ttm_placement(bo, pl_flag); + for (i = 0; i < bo->placement.num_placement; i++) + bo->placements[i] |= TTM_PL_FLAG_NO_EVICT; + ret = ttm_bo_validate(&bo->bo, &bo->placement, false, false, false); + if (ret) + return ret; + + bo->pin_count = 1; + if (gpu_addr) + *gpu_addr = ast_bo_gpu_offset(bo); + return 0; +} + +int ast_bo_unpin(struct ast_bo *bo) +{ + int i, ret; + if (!bo->pin_count) { + DRM_ERROR("unpin bad %p\n", bo); + return 0; + } + bo->pin_count--; + if (bo->pin_count) + return 0; + + for (i = 0; i < bo->placement.num_placement ; i++) + bo->placements[i] &= ~TTM_PL_FLAG_NO_EVICT; + ret = ttm_bo_validate(&bo->bo, &bo->placement, false, false, false); + if (ret) + return ret; + + return 0; +} + +int ast_bo_push_sysram(struct ast_bo *bo) +{ + int i, ret; + if (!bo->pin_count) { + DRM_ERROR("unpin bad %p\n", bo); + return 0; + } + bo->pin_count--; + if (bo->pin_count) + return 0; + + if (bo->kmap.virtual) + ttm_bo_kunmap(&bo->kmap); + + ast_ttm_placement(bo, TTM_PL_FLAG_SYSTEM); + for (i = 0; i < bo->placement.num_placement ; i++) + bo->placements[i] |= TTM_PL_FLAG_NO_EVICT; + + ret = ttm_bo_validate(&bo->bo, &bo->placement, false, false, false); + if (ret) { + DRM_ERROR("pushing to VRAM failed\n"); + return ret; + } + return 0; +} + +int ast_mmap(struct file *filp, struct vm_area_struct *vma) +{ + struct drm_file *file_priv; + struct ast_private *ast; + + if (unlikely(vma->vm_pgoff < DRM_FILE_PAGE_OFFSET)) + return drm_mmap(filp, vma); + + file_priv = filp->private_data; + ast = file_priv->minor->dev->dev_private; + return ttm_bo_mmap(filp, vma, &ast->ttm.bdev); +} diff --git a/drivers/gpu/drm/cirrus/Kconfig b/drivers/gpu/drm/cirrus/Kconfig new file mode 100644 index 0000000..fc154dd --- /dev/null +++ b/drivers/gpu/drm/cirrus/Kconfig @@ -0,0 +1,12 @@ +config DRM_CIRRUS_QEMU + tristate "Cirrus driver for QEMU emulated device" + depends on DRM && PCI && EXPERIMENTAL + select FB_SYS_FILLRECT + select FB_SYS_COPYAREA + select FB_SYS_IMAGEBLIT + select DRM_KMS_HELPER + select DRM_TTM + help + This is a KMS driver for emulated cirrus device in qemu. + It is *NOT* intended for real cirrus devices. This requires + the modesetting userspace X.org driver. diff --git a/drivers/gpu/drm/cirrus/Makefile b/drivers/gpu/drm/cirrus/Makefile new file mode 100644 index 0000000..69ffe70 --- /dev/null +++ b/drivers/gpu/drm/cirrus/Makefile @@ -0,0 +1,5 @@ +ccflags-y := -Iinclude/drm +cirrus-y := cirrus_main.o cirrus_mode.o \ + cirrus_drv.o cirrus_fbdev.o cirrus_ttm.o + +obj-$(CONFIG_DRM_CIRRUS_QEMU) += cirrus.o diff --git a/drivers/gpu/drm/cirrus/cirrus_drv.c b/drivers/gpu/drm/cirrus/cirrus_drv.c new file mode 100644 index 0000000..d703823 --- /dev/null +++ b/drivers/gpu/drm/cirrus/cirrus_drv.c @@ -0,0 +1,108 @@ +/* + * Copyright 2012 Red Hat <mjg@redhat.com> + * + * This file is subject to the terms and conditions of the GNU General + * Public License version 2. See the file COPYING in the main + * directory of this archive for more details. + * + * Authors: Matthew Garrett + * Dave Airlie + */ +#include <linux/module.h> +#include <linux/console.h> +#include "drmP.h" +#include "drm.h" + +#include "cirrus_drv.h" + +int cirrus_modeset = -1; + +MODULE_PARM_DESC(modeset, "Disable/Enable modesetting"); +module_param_named(modeset, cirrus_modeset, int, 0400); + +/* + * This is the generic driver code. This binds the driver to the drm core, + * which then performs further device association and calls our graphics init + * functions + */ + +static struct drm_driver driver; + +/* only bind to the cirrus chip in qemu */ +static DEFINE_PCI_DEVICE_TABLE(pciidlist) = { + { PCI_VENDOR_ID_CIRRUS, PCI_DEVICE_ID_CIRRUS_5446, 0x1af4, 0x1100, 0, + 0, 0 }, + {0,} +}; + +static int __devinit +cirrus_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent) +{ + return drm_get_pci_dev(pdev, ent, &driver); +} + +static void cirrus_pci_remove(struct pci_dev *pdev) +{ + struct drm_device *dev = pci_get_drvdata(pdev); + + drm_put_dev(dev); +} + +static const struct file_operations cirrus_driver_fops = { + .owner = THIS_MODULE, + .open = drm_open, + .release = drm_release, + .unlocked_ioctl = drm_ioctl, + .mmap = cirrus_mmap, + .poll = drm_poll, + .fasync = drm_fasync, +}; +static struct drm_driver driver = { + .driver_features = DRIVER_MODESET | DRIVER_GEM | DRIVER_USE_MTRR, + .load = cirrus_driver_load, + .unload = cirrus_driver_unload, + .fops = &cirrus_driver_fops, + .name = DRIVER_NAME, + .desc = DRIVER_DESC, + .date = DRIVER_DATE, + .major = DRIVER_MAJOR, + .minor = DRIVER_MINOR, + .patchlevel = DRIVER_PATCHLEVEL, + .gem_init_object = cirrus_gem_init_object, + .gem_free_object = cirrus_gem_free_object, + .dumb_create = cirrus_dumb_create, + .dumb_map_offset = cirrus_dumb_mmap_offset, + .dumb_destroy = cirrus_dumb_destroy, +}; + +static struct pci_driver cirrus_pci_driver = { + .name = DRIVER_NAME, + .id_table = pciidlist, + .probe = cirrus_pci_probe, + .remove = cirrus_pci_remove, +}; + +static int __init cirrus_init(void) +{ +#ifdef CONFIG_VGA_CONSOLE + if (vgacon_text_force() && cirrus_modeset == -1) + return -EINVAL; +#endif + + if (cirrus_modeset == 0) + return -EINVAL; + return drm_pci_init(&driver, &cirrus_pci_driver); +} + +static void __exit cirrus_exit(void) +{ + drm_pci_exit(&driver, &cirrus_pci_driver); +} + +module_init(cirrus_init); +module_exit(cirrus_exit); + +MODULE_DEVICE_TABLE(pci, pciidlist); +MODULE_AUTHOR(DRIVER_AUTHOR); +MODULE_DESCRIPTION(DRIVER_DESC); +MODULE_LICENSE("GPL"); diff --git a/drivers/gpu/drm/cirrus/cirrus_drv.h b/drivers/gpu/drm/cirrus/cirrus_drv.h new file mode 100644 index 0000000..21bdfa8 --- /dev/null +++ b/drivers/gpu/drm/cirrus/cirrus_drv.h @@ -0,0 +1,246 @@ +/* + * Copyright 2012 Red Hat + * + * This file is subject to the terms and conditions of the GNU General + * Public License version 2. See the file COPYING in the main + * directory of this archive for more details. + * + * Authors: Matthew Garrett + * Dave Airlie + */ +#ifndef __CIRRUS_DRV_H__ +#define __CIRRUS_DRV_H__ + +#include <video/vga.h> + +#include <drm/drm_fb_helper.h> + +#include "ttm/ttm_bo_api.h" +#include "ttm/ttm_bo_driver.h" +#include "ttm/ttm_placement.h" +#include "ttm/ttm_memory.h" +#include "ttm/ttm_module.h" + +#define DRIVER_AUTHOR "Matthew Garrett" + +#define DRIVER_NAME "cirrus" +#define DRIVER_DESC "qemu Cirrus emulation" +#define DRIVER_DATE "20110418" + +#define DRIVER_MAJOR 1 +#define DRIVER_MINOR 0 +#define DRIVER_PATCHLEVEL 0 + +#define CIRRUSFB_CONN_LIMIT 1 + +#define RREG8(reg) ioread8(((void __iomem *)cdev->rmmio) + (reg)) +#define WREG8(reg, v) iowrite8(v, ((void __iomem *)cdev->rmmio) + (reg)) +#define RREG32(reg) ioread32(((void __iomem *)cdev->rmmio) + (reg)) +#define WREG32(reg, v) iowrite32(v, ((void __iomem *)cdev->rmmio) + (reg)) + +#define SEQ_INDEX 4 +#define SEQ_DATA 5 + +#define WREG_SEQ(reg, v) \ + do { \ + WREG8(SEQ_INDEX, reg); \ + WREG8(SEQ_DATA, v); \ + } while (0) \ + +#define CRT_INDEX 0x14 +#define CRT_DATA 0x15 + +#define WREG_CRT(reg, v) \ + do { \ + WREG8(CRT_INDEX, reg); \ + WREG8(CRT_DATA, v); \ + } while (0) \ + +#define GFX_INDEX 0xe +#define GFX_DATA 0xf + +#define WREG_GFX(reg, v) \ + do { \ + WREG8(GFX_INDEX, reg); \ + WREG8(GFX_DATA, v); \ + } while (0) \ + +/* + * Cirrus has a "hidden" DAC register that can be accessed by writing to + * the pixel mask register to reset the state, then reading from the register + * four times. The next write will then pass to the DAC + */ +#define VGA_DAC_MASK 0x6 + +#define WREG_HDR(v) \ + do { \ + RREG8(VGA_DAC_MASK); \ + RREG8(VGA_DAC_MASK); \ + RREG8(VGA_DAC_MASK); \ + RREG8(VGA_DAC_MASK); \ + WREG8(VGA_DAC_MASK, v); \ + } while (0) \ + + +#define CIRRUS_MAX_FB_HEIGHT 4096 +#define CIRRUS_MAX_FB_WIDTH 4096 + +#define CIRRUS_DPMS_CLEARED (-1) + +#define to_cirrus_crtc(x) container_of(x, struct cirrus_crtc, base) +#define to_cirrus_encoder(x) container_of(x, struct cirrus_encoder, base) +#define to_cirrus_framebuffer(x) container_of(x, struct cirrus_framebuffer, base) + +struct cirrus_crtc { + struct drm_crtc base; + u8 lut_r[256], lut_g[256], lut_b[256]; + int last_dpms; + bool enabled; +}; + +struct cirrus_fbdev; +struct cirrus_mode_info { + bool mode_config_initialized; + struct cirrus_crtc *crtc; + /* pointer to fbdev info structure */ + struct cirrus_fbdev *gfbdev; +}; + +struct cirrus_encoder { + struct drm_encoder base; + int last_dpms; +}; + +struct cirrus_connector { + struct drm_connector base; +}; + +struct cirrus_framebuffer { + struct drm_framebuffer base; + struct drm_gem_object *obj; +}; + +struct cirrus_mc { + resource_size_t vram_size; + resource_size_t vram_base; +}; + +struct cirrus_device { + struct drm_device *dev; + unsigned long flags; + + resource_size_t rmmio_base; + resource_size_t rmmio_size; + void __iomem *rmmio; + + struct cirrus_mc mc; + struct cirrus_mode_info mode_info; + + int num_crtc; + int fb_mtrr; + + struct { + struct drm_global_reference mem_global_ref; + struct ttm_bo_global_ref bo_global_ref; + struct ttm_bo_device bdev; + atomic_t validate_sequence; + } ttm; + +}; + + +struct cirrus_fbdev { + struct drm_fb_helper helper; + struct cirrus_framebuffer gfb; + struct list_head fbdev_list; + void *sysram; + int size; +}; + +struct cirrus_bo { + struct ttm_buffer_object bo; + struct ttm_placement placement; + struct ttm_bo_kmap_obj kmap; + struct drm_gem_object gem; + u32 placements[3]; + int pin_count; +}; +#define gem_to_cirrus_bo(gobj) container_of((gobj), struct cirrus_bo, gem) + +static inline struct cirrus_bo * +cirrus_bo(struct ttm_buffer_object *bo) +{ + return container_of(bo, struct cirrus_bo, bo); +} + + +#define to_cirrus_obj(x) container_of(x, struct cirrus_gem_object, base) +#define DRM_FILE_PAGE_OFFSET (0x100000000ULL >> PAGE_SHIFT) + + /* cirrus_mode.c */ +void cirrus_crtc_fb_gamma_set(struct drm_crtc *crtc, u16 red, u16 green, + u16 blue, int regno); +void cirrus_crtc_fb_gamma_get(struct drm_crtc *crtc, u16 *red, u16 *green, + u16 *blue, int regno); + + + /* cirrus_main.c */ +int cirrus_device_init(struct cirrus_device *cdev, + struct drm_device *ddev, + struct pci_dev *pdev, + uint32_t flags); +void cirrus_device_fini(struct cirrus_device *cdev); +int cirrus_gem_init_object(struct drm_gem_object *obj); +void cirrus_gem_free_object(struct drm_gem_object *obj); +int cirrus_dumb_mmap_offset(struct drm_file *file, + struct drm_device *dev, + uint32_t handle, + uint64_t *offset); +int cirrus_gem_create(struct drm_device *dev, + u32 size, bool iskernel, + struct drm_gem_object **obj); +int cirrus_dumb_create(struct drm_file *file, + struct drm_device *dev, + struct drm_mode_create_dumb *args); +int cirrus_dumb_destroy(struct drm_file *file, + struct drm_device *dev, + uint32_t handle); + +int cirrus_framebuffer_init(struct drm_device *dev, + struct cirrus_framebuffer *gfb, + struct drm_mode_fb_cmd2 *mode_cmd, + struct drm_gem_object *obj); + + /* cirrus_display.c */ +int cirrus_modeset_init(struct cirrus_device *cdev); +void cirrus_modeset_fini(struct cirrus_device *cdev); + + /* cirrus_fbdev.c */ +int cirrus_fbdev_init(struct cirrus_device *cdev); +void cirrus_fbdev_fini(struct cirrus_device *cdev); + + + + /* cirrus_irq.c */ +void cirrus_driver_irq_preinstall(struct drm_device *dev); +int cirrus_driver_irq_postinstall(struct drm_device *dev); +void cirrus_driver_irq_uninstall(struct drm_device *dev); +irqreturn_t cirrus_driver_irq_handler(DRM_IRQ_ARGS); + + /* cirrus_kms.c */ +int cirrus_driver_load(struct drm_device *dev, unsigned long flags); +int cirrus_driver_unload(struct drm_device *dev); +extern struct drm_ioctl_desc cirrus_ioctls[]; +extern int cirrus_max_ioctl; + +int cirrus_mm_init(struct cirrus_device *cirrus); +void cirrus_mm_fini(struct cirrus_device *cirrus); +void cirrus_ttm_placement(struct cirrus_bo *bo, int domain); +int cirrus_bo_create(struct drm_device *dev, int size, int align, + uint32_t flags, struct cirrus_bo **pcirrusbo); +int cirrus_mmap(struct file *filp, struct vm_area_struct *vma); +int cirrus_bo_reserve(struct cirrus_bo *bo, bool no_wait); +void cirrus_bo_unreserve(struct cirrus_bo *bo); +int cirrus_bo_push_sysram(struct cirrus_bo *bo); +int cirrus_bo_pin(struct cirrus_bo *bo, u32 pl_flag, u64 *gpu_addr); +#endif /* __CIRRUS_DRV_H__ */ diff --git a/drivers/gpu/drm/cirrus/cirrus_fbdev.c b/drivers/gpu/drm/cirrus/cirrus_fbdev.c new file mode 100644 index 0000000..9a276a5 --- /dev/null +++ b/drivers/gpu/drm/cirrus/cirrus_fbdev.c @@ -0,0 +1,307 @@ +/* + * Copyright 2012 Red Hat + * + * This file is subject to the terms and conditions of the GNU General + * Public License version 2. See the file COPYING in the main + * directory of this archive for more details. + * + * Authors: Matthew Garrett + * Dave Airlie + */ +#include <linux/module.h> +#include "drmP.h" +#include "drm.h" +#include "drm_fb_helper.h" + +#include <linux/fb.h> + +#include "cirrus_drv.h" + +static void cirrus_dirty_update(struct cirrus_fbdev *afbdev, + int x, int y, int width, int height) +{ + int i; + struct drm_gem_object *obj; + struct cirrus_bo *bo; + int src_offset, dst_offset; + int bpp = (afbdev->gfb.base.bits_per_pixel + 7)/8; + int ret; + bool unmap = false; + + obj = afbdev->gfb.obj; + bo = gem_to_cirrus_bo(obj); + + ret = cirrus_bo_reserve(bo, true); + if (ret) { + DRM_ERROR("failed to reserve fb bo\n"); + return; + } + + if (!bo->kmap.virtual) { + ret = ttm_bo_kmap(&bo->bo, 0, bo->bo.num_pages, &bo->kmap); + if (ret) { + DRM_ERROR("failed to kmap fb updates\n"); + cirrus_bo_unreserve(bo); + return; + } + unmap = true; + } + for (i = y; i < y + height; i++) { + /* assume equal stride for now */ + src_offset = dst_offset = i * afbdev->gfb.base.pitches[0] + (x * bpp); + memcpy_toio(bo->kmap.virtual + src_offset, afbdev->sysram + src_offset, width * bpp); + + } + if (unmap) + ttm_bo_kunmap(&bo->kmap); + + cirrus_bo_unreserve(bo); +} + +static void cirrus_fillrect(struct fb_info *info, + const struct fb_fillrect *rect) +{ + struct cirrus_fbdev *afbdev = info->par; + sys_fillrect(info, rect); + cirrus_dirty_update(afbdev, rect->dx, rect->dy, rect->width, + rect->height); +} + +static void cirrus_copyarea(struct fb_info *info, + const struct fb_copyarea *area) +{ + struct cirrus_fbdev *afbdev = info->par; + sys_copyarea(info, area); + cirrus_dirty_update(afbdev, area->dx, area->dy, area->width, + area->height); +} + +static void cirrus_imageblit(struct fb_info *info, + const struct fb_image *image) +{ + struct cirrus_fbdev *afbdev = info->par; + sys_imageblit(info, image); + cirrus_dirty_update(afbdev, image->dx, image->dy, image->width, + image->height); +} + + +static struct fb_ops cirrusfb_ops = { + .owner = THIS_MODULE, + .fb_check_var = drm_fb_helper_check_var, + .fb_set_par = drm_fb_helper_set_par, + .fb_fillrect = cirrus_fillrect, + .fb_copyarea = cirrus_copyarea, + .fb_imageblit = cirrus_imageblit, + .fb_pan_display = drm_fb_helper_pan_display, + .fb_blank = drm_fb_helper_blank, + .fb_setcmap = drm_fb_helper_setcmap, +}; + +static int cirrusfb_create_object(struct cirrus_fbdev *afbdev, + struct drm_mode_fb_cmd2 *mode_cmd, + struct drm_gem_object **gobj_p) +{ + struct drm_device *dev = afbdev->helper.dev; + u32 bpp, depth; + u32 size; + struct drm_gem_object *gobj; + + int ret = 0; + drm_fb_get_bpp_depth(mode_cmd->pixel_format, &depth, &bpp); + + if (bpp > 24) + return -EINVAL; + size = mode_cmd->pitches[0] * mode_cmd->height; + ret = cirrus_gem_create(dev, size, true, &gobj); + if (ret) + return ret; + + *gobj_p = gobj; + return ret; +} + +static int cirrusfb_create(struct cirrus_fbdev *gfbdev, + struct drm_fb_helper_surface_size *sizes) +{ + struct drm_device *dev = gfbdev->helper.dev; + struct cirrus_device *cdev = gfbdev->helper.dev->dev_private; + struct fb_info *info; + struct drm_framebuffer *fb; + struct drm_mode_fb_cmd2 mode_cmd; + struct device *device = &dev->pdev->dev; + void *sysram; + struct drm_gem_object *gobj = NULL; + struct cirrus_bo *bo = NULL; + int size, ret; + + mode_cmd.width = sizes->surface_width; + mode_cmd.height = sizes->surface_height; + mode_cmd.pitches[0] = mode_cmd.width * ((sizes->surface_bpp + 7) / 8); + mode_cmd.pixel_format = drm_mode_legacy_fb_format(sizes->surface_bpp, + sizes->surface_depth); + size = mode_cmd.pitches[0] * mode_cmd.height; + + ret = cirrusfb_create_object(gfbdev, &mode_cmd, &gobj); + if (ret) { + DRM_ERROR("failed to create fbcon backing object %d\n", ret); + return ret; + } + + bo = gem_to_cirrus_bo(gobj); + + sysram = vmalloc(size); + if (!sysram) + return -ENOMEM; + + info = framebuffer_alloc(0, device); + if (info == NULL) + return -ENOMEM; + + info->par = gfbdev; + + ret = cirrus_framebuffer_init(cdev->dev, &gfbdev->gfb, &mode_cmd, gobj); + if (ret) + return ret; + + gfbdev->sysram = sysram; + gfbdev->size = size; + + fb = &gfbdev->gfb.base; + if (!fb) { + DRM_INFO("fb is NULL\n"); + return -EINVAL; + } + + /* setup helper */ + gfbdev->helper.fb = fb; + gfbdev->helper.fbdev = info; + + strcpy(info->fix.id, "cirrusdrmfb"); + + + info->flags = FBINFO_DEFAULT; + info->fbops = &cirrusfb_ops; + + drm_fb_helper_fill_fix(info, fb->pitches[0], fb->depth); + drm_fb_helper_fill_var(info, &gfbdev->helper, sizes->fb_width, + sizes->fb_height); + + /* setup aperture base/size for vesafb takeover */ + info->apertures = alloc_apertures(1); + if (!info->apertures) { + ret = -ENOMEM; + goto out_iounmap; + } + info->apertures->ranges[0].base = cdev->dev->mode_config.fb_base; + info->apertures->ranges[0].size = cdev->mc.vram_size; + + info->screen_base = sysram; + info->screen_size = size; + + info->fix.mmio_start = 0; + info->fix.mmio_len = 0; + + ret = fb_alloc_cmap(&info->cmap, 256, 0); + if (ret) { + DRM_ERROR("%s: can't allocate color map\n", info->fix.id); + ret = -ENOMEM; + goto out_iounmap; + } + + DRM_INFO("fb mappable at 0x%lX\n", info->fix.smem_start); + DRM_INFO("vram aper at 0x%lX\n", (unsigned long)info->fix.smem_start); + DRM_INFO("size %lu\n", (unsigned long)info->fix.smem_len); + DRM_INFO("fb depth is %d\n", fb->depth); + DRM_INFO(" pitch is %d\n", fb->pitches[0]); + + return 0; +out_iounmap: + return ret; +} + +static int cirrus_fb_find_or_create_single(struct drm_fb_helper *helper, + struct drm_fb_helper_surface_size + *sizes) +{ + struct cirrus_fbdev *gfbdev = (struct cirrus_fbdev *)helper; + int new_fb = 0; + int ret; + + if (!helper->fb) { + ret = cirrusfb_create(gfbdev, sizes); + if (ret) + return ret; + new_fb = 1; + } + return new_fb; +} + +static int cirrus_fbdev_destroy(struct drm_device *dev, + struct cirrus_fbdev *gfbdev) +{ + struct fb_info *info; + struct cirrus_framebuffer *gfb = &gfbdev->gfb; + + if (gfbdev->helper.fbdev) { + info = gfbdev->helper.fbdev; + + unregister_framebuffer(info); + if (info->cmap.len) + fb_dealloc_cmap(&info->cmap); + framebuffer_release(info); + } + + if (gfb->obj) { + drm_gem_object_unreference_unlocked(gfb->obj); + gfb->obj = NULL; + } + + vfree(gfbdev->sysram); + drm_fb_helper_fini(&gfbdev->helper); + drm_framebuffer_cleanup(&gfb->base); + + return 0; +} + +static struct drm_fb_helper_funcs cirrus_fb_helper_funcs = { + .gamma_set = cirrus_crtc_fb_gamma_set, + .gamma_get = cirrus_crtc_fb_gamma_get, + .fb_probe = cirrus_fb_find_or_create_single, +}; + +int cirrus_fbdev_init(struct cirrus_device *cdev) +{ + struct cirrus_fbdev *gfbdev; + int ret; + int bpp_sel = 24; + + /*bpp_sel = 8;*/ + gfbdev = kzalloc(sizeof(struct cirrus_fbdev), GFP_KERNEL); + if (!gfbdev) + return -ENOMEM; + + cdev->mode_info.gfbdev = gfbdev; + gfbdev->helper.funcs = &cirrus_fb_helper_funcs; + + ret = drm_fb_helper_init(cdev->dev, &gfbdev->helper, + cdev->num_crtc, CIRRUSFB_CONN_LIMIT); + if (ret) { + kfree(gfbdev); + return ret; + } + drm_fb_helper_single_add_all_connectors(&gfbdev->helper); + drm_fb_helper_initial_config(&gfbdev->helper, bpp_sel); + + return 0; +} + +void cirrus_fbdev_fini(struct cirrus_device *cdev) +{ + if (!cdev->mode_info.gfbdev) + return; + + cirrus_fbdev_destroy(cdev->dev, cdev->mode_info.gfbdev); + kfree(cdev->mode_info.gfbdev); + cdev->mode_info.gfbdev = NULL; +} diff --git a/drivers/gpu/drm/cirrus/cirrus_main.c b/drivers/gpu/drm/cirrus/cirrus_main.c new file mode 100644 index 0000000..e3c1225 --- /dev/null +++ b/drivers/gpu/drm/cirrus/cirrus_main.c @@ -0,0 +1,335 @@ +/* + * Copyright 2012 Red Hat + * + * This file is subject to the terms and conditions of the GNU General + * Public License version 2. See the file COPYING in the main + * directory of this archive for more details. + * + * Authors: Matthew Garrett + * Dave Airlie + */ +#include "drmP.h" +#include "drm.h" +#include "drm_crtc_helper.h" + +#include "cirrus_drv.h" + + +static void cirrus_user_framebuffer_destroy(struct drm_framebuffer *fb) +{ + struct cirrus_framebuffer *cirrus_fb = to_cirrus_framebuffer(fb); + if (cirrus_fb->obj) + drm_gem_object_unreference_unlocked(cirrus_fb->obj); + drm_framebuffer_cleanup(fb); + kfree(fb); +} + +static int cirrus_user_framebuffer_create_handle(struct drm_framebuffer *fb, + struct drm_file *file_priv, + unsigned int *handle) +{ + return 0; +} + +static const struct drm_framebuffer_funcs cirrus_fb_funcs = { + .destroy = cirrus_user_framebuffer_destroy, + .create_handle = cirrus_user_framebuffer_create_handle, +}; + +int cirrus_framebuffer_init(struct drm_device *dev, + struct cirrus_framebuffer *gfb, + struct drm_mode_fb_cmd2 *mode_cmd, + struct drm_gem_object *obj) +{ + int ret; + + ret = drm_framebuffer_init(dev, &gfb->base, &cirrus_fb_funcs); + if (ret) { + DRM_ERROR("drm_framebuffer_init failed: %d\n", ret); + return ret; + } + drm_helper_mode_fill_fb_struct(&gfb->base, mode_cmd); + gfb->obj = obj; + return 0; +} + +static struct drm_framebuffer * +cirrus_user_framebuffer_create(struct drm_device *dev, + struct drm_file *filp, + struct drm_mode_fb_cmd2 *mode_cmd) +{ + struct drm_gem_object *obj; + struct cirrus_framebuffer *cirrus_fb; + int ret; + u32 bpp, depth; + + drm_fb_get_bpp_depth(mode_cmd->pixel_format, &depth, &bpp); + /* cirrus can't handle > 24bpp framebuffers at all */ + if (bpp > 24) + return ERR_PTR(-EINVAL); + + obj = drm_gem_object_lookup(dev, filp, mode_cmd->handles[0]); + if (obj == NULL) + return ERR_PTR(-ENOENT); + + cirrus_fb = kzalloc(sizeof(*cirrus_fb), GFP_KERNEL); + if (!cirrus_fb) { + drm_gem_object_unreference_unlocked(obj); + return ERR_PTR(-ENOMEM); + } + + ret = cirrus_framebuffer_init(dev, cirrus_fb, mode_cmd, obj); + if (ret) { + drm_gem_object_unreference_unlocked(obj); + kfree(cirrus_fb); + return ERR_PTR(ret); + } + return &cirrus_fb->base; +} + +static const struct drm_mode_config_funcs cirrus_mode_funcs = { + .fb_create = cirrus_user_framebuffer_create, +}; + +/* Unmap the framebuffer from the core and release the memory */ +static void cirrus_vram_fini(struct cirrus_device *cdev) +{ + iounmap(cdev->rmmio); + cdev->rmmio = NULL; + if (cdev->mc.vram_base) + release_mem_region(cdev->mc.vram_base, cdev->mc.vram_size); +} + +/* Map the framebuffer from the card and configure the core */ +static int cirrus_vram_init(struct cirrus_device *cdev) +{ + /* BAR 0 is VRAM */ + cdev->mc.vram_base = pci_resource_start(cdev->dev->pdev, 0); + /* We have 4MB of VRAM */ + cdev->mc.vram_size = 4 * 1024 * 1024; + + if (!request_mem_region(cdev->mc.vram_base, cdev->mc.vram_size, + "cirrusdrmfb_vram")) { + DRM_ERROR("can't reserve VRAM\n"); + return -ENXIO; + } + + return 0; +} + +/* + * Our emulated hardware has two sets of memory. One is video RAM and can + * simply be used as a linear framebuffer - the other provides mmio access + * to the display registers. The latter can also be accessed via IO port + * access, but we map the range and use mmio to program them instead + */ + +int cirrus_device_init(struct cirrus_device *cdev, + struct drm_device *ddev, + struct pci_dev *pdev, uint32_t flags) +{ + int ret; + + cdev->dev = ddev; + cdev->flags = flags; + + /* Hardcode the number of CRTCs to 1 */ + cdev->num_crtc = 1; + + /* BAR 0 is the framebuffer, BAR 1 contains registers */ + cdev->rmmio_base = pci_resource_start(cdev->dev->pdev, 1); + cdev->rmmio_size = pci_resource_len(cdev->dev->pdev, 1); + + if (!request_mem_region(cdev->rmmio_base, cdev->rmmio_size, + "cirrusdrmfb_mmio")) { + DRM_ERROR("can't reserve mmio registers\n"); + return -ENOMEM; + } + + cdev->rmmio = ioremap(cdev->rmmio_base, cdev->rmmio_size); + + if (cdev->rmmio == NULL) + return -ENOMEM; + + ret = cirrus_vram_init(cdev); + if (ret) { + release_mem_region(cdev->rmmio_base, cdev->rmmio_size); + return ret; + } + + return 0; +} + +void cirrus_device_fini(struct cirrus_device *cdev) +{ + release_mem_region(cdev->rmmio_base, cdev->rmmio_size); + cirrus_vram_fini(cdev); +} + +/* + * Functions here will be called by the core once it's bound the driver to + * a PCI device + */ + +int cirrus_driver_load(struct drm_device *dev, unsigned long flags) +{ + struct cirrus_device *cdev; + int r; + + cdev = kzalloc(sizeof(struct cirrus_device), GFP_KERNEL); + if (cdev == NULL) + return -ENOMEM; + dev->dev_private = (void *)cdev; + + r = cirrus_device_init(cdev, dev, dev->pdev, flags); + if (r) { + dev_err(&dev->pdev->dev, "Fatal error during GPU init: %d\n", r); + goto out; + } + + r = cirrus_mm_init(cdev); + if (r) + dev_err(&dev->pdev->dev, "fatal err on mm init\n"); + + r = cirrus_modeset_init(cdev); + if (r) + dev_err(&dev->pdev->dev, "Fatal error during modeset init: %d\n", r); + + dev->mode_config.funcs = (void *)&cirrus_mode_funcs; +out: + if (r) + cirrus_driver_unload(dev); + return r; +} + +int cirrus_driver_unload(struct drm_device *dev) +{ + struct cirrus_device *cdev = dev->dev_private; + + if (cdev == NULL) + return 0; + cirrus_modeset_fini(cdev); + cirrus_mm_fini(cdev); + cirrus_device_fini(cdev); + kfree(cdev); + dev->dev_private = NULL; + return 0; +} + +int cirrus_gem_create(struct drm_device *dev, + u32 size, bool iskernel, + struct drm_gem_object **obj) +{ + struct cirrus_bo *cirrusbo; + int ret; + + *obj = NULL; + + size = roundup(size, PAGE_SIZE); + if (size == 0) + return -EINVAL; + + ret = cirrus_bo_create(dev, size, 0, 0, &cirrusbo); + if (ret) { + if (ret != -ERESTARTSYS) + DRM_ERROR("failed to allocate GEM object\n"); + return ret; + } + *obj = &cirrusbo->gem; + return 0; +} + +int cirrus_dumb_create(struct drm_file *file, + struct drm_device *dev, + struct drm_mode_create_dumb *args) +{ + int ret; + struct drm_gem_object *gobj; + u32 handle; + + args->pitch = args->width * ((args->bpp + 7) / 8); + args->size = args->pitch * args->height; + + ret = cirrus_gem_create(dev, args->size, false, + &gobj); + if (ret) + return ret; + + ret = drm_gem_handle_create(file, gobj, &handle); + drm_gem_object_unreference_unlocked(gobj); + if (ret) + return ret; + + args->handle = handle; + return 0; +} + +int cirrus_dumb_destroy(struct drm_file *file, + struct drm_device *dev, + uint32_t handle) +{ + return drm_gem_handle_delete(file, handle); +} + +int cirrus_gem_init_object(struct drm_gem_object *obj) +{ + BUG(); + return 0; +} + +void cirrus_bo_unref(struct cirrus_bo **bo) +{ + struct ttm_buffer_object *tbo; + + if ((*bo) == NULL) + return; + + tbo = &((*bo)->bo); + ttm_bo_unref(&tbo); + if (tbo == NULL) + *bo = NULL; + +} + +void cirrus_gem_free_object(struct drm_gem_object *obj) +{ + struct cirrus_bo *cirrus_bo = gem_to_cirrus_bo(obj); + + if (!cirrus_bo) + return; + cirrus_bo_unref(&cirrus_bo); +} + + +static inline u64 cirrus_bo_mmap_offset(struct cirrus_bo *bo) +{ + return bo->bo.addr_space_offset; +} + +int +cirrus_dumb_mmap_offset(struct drm_file *file, + struct drm_device *dev, + uint32_t handle, + uint64_t *offset) +{ + struct drm_gem_object *obj; + int ret; + struct cirrus_bo *bo; + + mutex_lock(&dev->struct_mutex); + obj = drm_gem_object_lookup(dev, file, handle); + if (obj == NULL) { + ret = -ENOENT; + goto out_unlock; + } + + bo = gem_to_cirrus_bo(obj); + *offset = cirrus_bo_mmap_offset(bo); + + drm_gem_object_unreference(obj); + ret = 0; +out_unlock: + mutex_unlock(&dev->struct_mutex); + return ret; + +} diff --git a/drivers/gpu/drm/cirrus/cirrus_mode.c b/drivers/gpu/drm/cirrus/cirrus_mode.c new file mode 100644 index 0000000..100f630 --- /dev/null +++ b/drivers/gpu/drm/cirrus/cirrus_mode.c @@ -0,0 +1,629 @@ + +/* + * Copyright 2012 Red Hat + * + * This file is subject to the terms and conditions of the GNU General + * Public License version 2. See the file COPYING in the main + * directory of this archive for more details. + * + * Authors: Matthew Garrett + * Dave Airlie + * + * Portions of this code derived from cirrusfb.c: + * drivers/video/cirrusfb.c - driver for Cirrus Logic chipsets + * + * Copyright 1999-2001 Jeff Garzik <jgarzik@pobox.com> + */ +#include "drmP.h" +#include "drm.h" +#include "drm_crtc_helper.h" + +#include <video/cirrus.h> + +#include "cirrus_drv.h" + +#define CIRRUS_LUT_SIZE 256 + +#define PALETTE_INDEX 0x8 +#define PALETTE_DATA 0x9 + +/* + * This file contains setup code for the CRTC. + */ + +static void cirrus_crtc_load_lut(struct drm_crtc *crtc) +{ + struct cirrus_crtc *cirrus_crtc = to_cirrus_crtc(crtc); + struct drm_device *dev = crtc->dev; + struct cirrus_device *cdev = dev->dev_private; + int i; + + if (!crtc->enabled) + return; + + for (i = 0; i < CIRRUS_LUT_SIZE; i++) { + /* VGA registers */ + WREG8(PALETTE_INDEX, i); + WREG8(PALETTE_DATA, cirrus_crtc->lut_r[i]); + WREG8(PALETTE_DATA, cirrus_crtc->lut_g[i]); + WREG8(PALETTE_DATA, cirrus_crtc->lut_b[i]); + } +} + +/* + * The DRM core requires DPMS functions, but they make little sense in our + * case and so are just stubs + */ + +static void cirrus_crtc_dpms(struct drm_crtc *crtc, int mode) +{ + struct drm_device *dev = crtc->dev; + struct cirrus_device *cdev = dev->dev_private; + u8 sr01, gr0e; + + switch (mode) { + case DRM_MODE_DPMS_ON: + sr01 = 0x00; + gr0e = 0x00; + break; + case DRM_MODE_DPMS_STANDBY: + sr01 = 0x20; + gr0e = 0x02; + break; + case DRM_MODE_DPMS_SUSPEND: + sr01 = 0x20; + gr0e = 0x04; + break; + case DRM_MODE_DPMS_OFF: + sr01 = 0x20; + gr0e = 0x06; + break; + default: + return; + } + + WREG8(SEQ_INDEX, 0x1); + sr01 |= RREG8(SEQ_DATA) & ~0x20; + WREG_SEQ(0x1, sr01); + + WREG8(GFX_INDEX, 0xe); + gr0e |= RREG8(GFX_DATA) & ~0x06; + WREG_GFX(0xe, gr0e); +} + +/* + * The core passes the desired mode to the CRTC code to see whether any + * CRTC-specific modifications need to be made to it. We're in a position + * to just pass that straight through, so this does nothing + */ +static bool cirrus_crtc_mode_fixup(struct drm_crtc *crtc, + struct drm_display_mode *mode, + struct drm_display_mode *adjusted_mode) +{ + return true; +} + +void cirrus_set_start_address(struct drm_crtc *crtc, unsigned offset) +{ + struct cirrus_device *cdev = crtc->dev->dev_private; + u32 addr; + u8 tmp; + + addr = offset >> 2; + WREG_CRT(0x0c, (u8)((addr >> 8) & 0xff)); + WREG_CRT(0x0d, (u8)(addr & 0xff)); + + WREG8(CRT_INDEX, 0x1b); + tmp = RREG8(CRT_DATA); + tmp &= 0xf2; + tmp |= (addr >> 16) & 0x01; + tmp |= (addr >> 15) & 0x0c; + WREG_CRT(0x1b, tmp); + WREG8(CRT_INDEX, 0x1d); + tmp = RREG8(CRT_DATA); + tmp &= 0x7f; + tmp |= (addr >> 12) & 0x80; + WREG_CRT(0x1d, tmp); +} + +/* cirrus is different - we will force move buffers out of VRAM */ +static int cirrus_crtc_do_set_base(struct drm_crtc *crtc, + struct drm_framebuffer *fb, + int x, int y, int atomic) +{ + struct cirrus_device *cdev = crtc->dev->dev_private; + struct drm_gem_object *obj; + struct cirrus_framebuffer *cirrus_fb; + struct cirrus_bo *bo; + int ret; + u64 gpu_addr; + + /* push the previous fb to system ram */ + if (!atomic && fb) { + cirrus_fb = to_cirrus_framebuffer(fb); + obj = cirrus_fb->obj; + bo = gem_to_cirrus_bo(obj); + ret = cirrus_bo_reserve(bo, false); + if (ret) + return ret; + cirrus_bo_push_sysram(bo); + cirrus_bo_unreserve(bo); + } + + cirrus_fb = to_cirrus_framebuffer(crtc->fb); + obj = cirrus_fb->obj; + bo = gem_to_cirrus_bo(obj); + + ret = cirrus_bo_reserve(bo, false); + if (ret) + return ret; + + ret = cirrus_bo_pin(bo, TTM_PL_FLAG_VRAM, &gpu_addr); + if (ret) { + cirrus_bo_unreserve(bo); + return ret; + } + + if (&cdev->mode_info.gfbdev->gfb == cirrus_fb) { + /* if pushing console in kmap it */ + ret = ttm_bo_kmap(&bo->bo, 0, bo->bo.num_pages, &bo->kmap); + if (ret) + DRM_ERROR("failed to kmap fbcon\n"); + } + cirrus_bo_unreserve(bo); + + cirrus_set_start_address(crtc, (u32)gpu_addr); + return 0; +} + +static int cirrus_crtc_mode_set_base(struct drm_crtc *crtc, int x, int y, + struct drm_framebuffer *old_fb) +{ + return cirrus_crtc_do_set_base(crtc, old_fb, x, y, 0); +} + +/* + * The meat of this driver. The core passes us a mode and we have to program + * it. The modesetting here is the bare minimum required to satisfy the qemu + * emulation of this hardware, and running this against a real device is + * likely to result in an inadequately programmed mode. We've already had + * the opportunity to modify the mode, so whatever we receive here should + * be something that can be correctly programmed and displayed + */ +static int cirrus_crtc_mode_set(struct drm_crtc *crtc, + struct drm_display_mode *mode, + struct drm_display_mode *adjusted_mode, + int x, int y, struct drm_framebuffer *old_fb) +{ + struct drm_device *dev = crtc->dev; + struct cirrus_device *cdev = dev->dev_private; + int hsyncstart, hsyncend, htotal, hdispend; + int vtotal, vdispend; + int tmp; + int sr07 = 0, hdr = 0; + + htotal = mode->htotal / 8; + hsyncend = mode->hsync_end / 8; + hsyncstart = mode->hsync_start / 8; + hdispend = mode->hdisplay / 8; + + vtotal = mode->vtotal; + vdispend = mode->vdisplay; + + vdispend -= 1; + vtotal -= 2; + + htotal -= 5; + hdispend -= 1; + hsyncstart += 1; + hsyncend += 1; + + WREG_CRT(VGA_CRTC_V_SYNC_END, 0x20); + WREG_CRT(VGA_CRTC_H_TOTAL, htotal); + WREG_CRT(VGA_CRTC_H_DISP, hdispend); + WREG_CRT(VGA_CRTC_H_SYNC_START, hsyncstart); + WREG_CRT(VGA_CRTC_H_SYNC_END, hsyncend); + WREG_CRT(VGA_CRTC_V_TOTAL, vtotal & 0xff); + WREG_CRT(VGA_CRTC_V_DISP_END, vdispend & 0xff); + + tmp = 0x40; + if ((vdispend + 1) & 512) + tmp |= 0x20; + WREG_CRT(VGA_CRTC_MAX_SCAN, tmp); + + /* + * Overflow bits for values that don't fit in the standard registers + */ + tmp = 16; + if (vtotal & 256) + tmp |= 1; + if (vdispend & 256) + tmp |= 2; + if ((vdispend + 1) & 256) + tmp |= 8; + if (vtotal & 512) + tmp |= 32; + if (vdispend & 512) + tmp |= 64; + WREG_CRT(VGA_CRTC_OVERFLOW, tmp); + + tmp = 0; + + /* More overflow bits */ + + if ((htotal + 5) & 64) + tmp |= 16; + if ((htotal + 5) & 128) + tmp |= 32; + if (vtotal & 256) + tmp |= 64; + if (vtotal & 512) + tmp |= 128; + + WREG_CRT(CL_CRT1A, tmp); + + /* Disable Hercules/CGA compatibility */ + WREG_CRT(VGA_CRTC_MODE, 0x03); + + WREG8(SEQ_INDEX, 0x7); + sr07 = RREG8(SEQ_DATA); + sr07 &= 0xe0; + hdr = 0; + switch (crtc->fb->bits_per_pixel) { + case 8: + sr07 |= 0x11; + break; + case 16: + sr07 |= 0xc1; + hdr = 0xc0; + break; + case 24: + sr07 |= 0x15; + hdr = 0xc5; + break; + case 32: + sr07 |= 0x19; + hdr = 0xc5; + break; + default: + return -1; + } + + WREG_SEQ(0x7, sr07); + + /* Program the pitch */ + tmp = crtc->fb->pitches[0] / 8; + WREG_CRT(VGA_CRTC_OFFSET, tmp); + + /* Enable extended blanking and pitch bits, and enable full memory */ + tmp = 0x22; + tmp |= (crtc->fb->pitches[0] >> 7) & 0x10; + tmp |= (crtc->fb->pitches[0] >> 6) & 0x40; + WREG_CRT(0x1b, tmp); + + /* Enable high-colour modes */ + WREG_GFX(VGA_GFX_MODE, 0x40); + + /* And set graphics mode */ + WREG_GFX(VGA_GFX_MISC, 0x01); + + WREG_HDR(hdr); + cirrus_crtc_do_set_base(crtc, old_fb, x, y, 0); + return 0; +} + +/* + * This is called before a mode is programmed. A typical use might be to + * enable DPMS during the programming to avoid seeing intermediate stages, + * but that's not relevant to us + */ +static void cirrus_crtc_prepare(struct drm_crtc *crtc) +{ +} + +/* + * This is called after a mode is programmed. It should reverse anything done + * by the prepare function + */ +static void cirrus_crtc_commit(struct drm_crtc *crtc) +{ +} + +/* + * The core can pass us a set of gamma values to program. We actually only + * use this for 8-bit mode so can't perform smooth fades on deeper modes, + * but it's a requirement that we provide the function + */ +static void cirrus_crtc_gamma_set(struct drm_crtc *crtc, u16 *red, u16 *green, + u16 *blue, uint32_t start, uint32_t size) +{ + struct cirrus_crtc *cirrus_crtc = to_cirrus_crtc(crtc); + int i; + + if (size != CIRRUS_LUT_SIZE) + return; + + for (i = 0; i < CIRRUS_LUT_SIZE; i++) { + cirrus_crtc->lut_r[i] = red[i]; + cirrus_crtc->lut_g[i] = green[i]; + cirrus_crtc->lut_b[i] = blue[i]; + } + cirrus_crtc_load_lut(crtc); +} + +/* Simple cleanup function */ +static void cirrus_crtc_destroy(struct drm_crtc *crtc) +{ + struct cirrus_crtc *cirrus_crtc = to_cirrus_crtc(crtc); + + drm_crtc_cleanup(crtc); + kfree(cirrus_crtc); +} + +/* These provide the minimum set of functions required to handle a CRTC */ +static const struct drm_crtc_funcs cirrus_crtc_funcs = { + .gamma_set = cirrus_crtc_gamma_set, + .set_config = drm_crtc_helper_set_config, + .destroy = cirrus_crtc_destroy, +}; + +static const struct drm_crtc_helper_funcs cirrus_helper_funcs = { + .dpms = cirrus_crtc_dpms, + .mode_fixup = cirrus_crtc_mode_fixup, + .mode_set = cirrus_crtc_mode_set, + .mode_set_base = cirrus_crtc_mode_set_base, + .prepare = cirrus_crtc_prepare, + .commit = cirrus_crtc_commit, + .load_lut = cirrus_crtc_load_lut, +}; + +/* CRTC setup */ +static void cirrus_crtc_init(struct drm_device *dev) +{ + struct cirrus_device *cdev = dev->dev_private; + struct cirrus_crtc *cirrus_crtc; + int i; + + cirrus_crtc = kzalloc(sizeof(struct cirrus_crtc) + + (CIRRUSFB_CONN_LIMIT * sizeof(struct drm_connector *)), + GFP_KERNEL); + + if (cirrus_crtc == NULL) + return; + + drm_crtc_init(dev, &cirrus_crtc->base, &cirrus_crtc_funcs); + + drm_mode_crtc_set_gamma_size(&cirrus_crtc->base, CIRRUS_LUT_SIZE); + cdev->mode_info.crtc = cirrus_crtc; + + for (i = 0; i < CIRRUS_LUT_SIZE; i++) { + cirrus_crtc->lut_r[i] = i; + cirrus_crtc->lut_g[i] = i; + cirrus_crtc->lut_b[i] = i; + } + + drm_crtc_helper_add(&cirrus_crtc->base, &cirrus_helper_funcs); +} + +/** Sets the color ramps on behalf of fbcon */ +void cirrus_crtc_fb_gamma_set(struct drm_crtc *crtc, u16 red, u16 green, + u16 blue, int regno) +{ + struct cirrus_crtc *cirrus_crtc = to_cirrus_crtc(crtc); + + cirrus_crtc->lut_r[regno] = red; + cirrus_crtc->lut_g[regno] = green; + cirrus_crtc->lut_b[regno] = blue; +} + +/** Gets the color ramps on behalf of fbcon */ +void cirrus_crtc_fb_gamma_get(struct drm_crtc *crtc, u16 *red, u16 *green, + u16 *blue, int regno) +{ + struct cirrus_crtc *cirrus_crtc = to_cirrus_crtc(crtc); + + *red = cirrus_crtc->lut_r[regno]; + *green = cirrus_crtc->lut_g[regno]; + *blue = cirrus_crtc->lut_b[regno]; +} + + +static bool cirrus_encoder_mode_fixup(struct drm_encoder *encoder, + struct drm_display_mode *mode, + struct drm_display_mode *adjusted_mode) +{ + return true; +} + +static void cirrus_encoder_mode_set(struct drm_encoder *encoder, + struct drm_display_mode *mode, + struct drm_display_mode *adjusted_mode) +{ +} + +static void cirrus_encoder_dpms(struct drm_encoder *encoder, int state) +{ + return; +} + +static void cirrus_encoder_prepare(struct drm_encoder *encoder) +{ +} + +static void cirrus_encoder_commit(struct drm_encoder *encoder) +{ +} + +void cirrus_encoder_destroy(struct drm_encoder *encoder) +{ + struct cirrus_encoder *cirrus_encoder = to_cirrus_encoder(encoder); + drm_encoder_cleanup(encoder); + kfree(cirrus_encoder); +} + +static const struct drm_encoder_helper_funcs cirrus_encoder_helper_funcs = { + .dpms = cirrus_encoder_dpms, + .mode_fixup = cirrus_encoder_mode_fixup, + .mode_set = cirrus_encoder_mode_set, + .prepare = cirrus_encoder_prepare, + .commit = cirrus_encoder_commit, +}; + +static const struct drm_encoder_funcs cirrus_encoder_encoder_funcs = { + .destroy = cirrus_encoder_destroy, +}; + +static struct drm_encoder *cirrus_encoder_init(struct drm_device *dev) +{ + struct drm_encoder *encoder; + struct cirrus_encoder *cirrus_encoder; + + cirrus_encoder = kzalloc(sizeof(struct cirrus_encoder), GFP_KERNEL); + if (!cirrus_encoder) + return NULL; + + encoder = &cirrus_encoder->base; + encoder->possible_crtcs = 0x1; + + drm_encoder_init(dev, encoder, &cirrus_encoder_encoder_funcs, + DRM_MODE_ENCODER_DAC); + drm_encoder_helper_add(encoder, &cirrus_encoder_helper_funcs); + + return encoder; +} + + +int cirrus_vga_get_modes(struct drm_connector *connector) +{ + /* Just add a static list of modes */ + drm_add_modes_noedid(connector, 640, 480); + drm_add_modes_noedid(connector, 800, 600); + drm_add_modes_noedid(connector, 1024, 768); + drm_add_modes_noedid(connector, 1280, 1024); + + return 4; +} + +static int cirrus_vga_mode_valid(struct drm_connector *connector, + struct drm_display_mode *mode) +{ + /* Any mode we've added is valid */ + return MODE_OK; +} + +struct drm_encoder *cirrus_connector_best_encoder(struct drm_connector + *connector) +{ + int enc_id = connector->encoder_ids[0]; + struct drm_mode_object *obj; + struct drm_encoder *encoder; + + /* pick the encoder ids */ + if (enc_id) { + obj = + drm_mode_object_find(connector->dev, enc_id, + DRM_MODE_OBJECT_ENCODER); + if (!obj) + return NULL; + encoder = obj_to_encoder(obj); + return encoder; + } + return NULL; +} + +static enum drm_connector_status cirrus_vga_detect(struct drm_connector + *connector, bool force) +{ + return connector_status_connected; +} + +static void cirrus_connector_destroy(struct drm_connector *connector) +{ + drm_connector_cleanup(connector); + kfree(connector); +} + +struct drm_connector_helper_funcs cirrus_vga_connector_helper_funcs = { + .get_modes = cirrus_vga_get_modes, + .mode_valid = cirrus_vga_mode_valid, + .best_encoder = cirrus_connector_best_encoder, +}; + +struct drm_connector_funcs cirrus_vga_connector_funcs = { + .dpms = drm_helper_connector_dpms, + .detect = cirrus_vga_detect, + .fill_modes = drm_helper_probe_single_connector_modes, + .destroy = cirrus_connector_destroy, +}; + +static struct drm_connector *cirrus_vga_init(struct drm_device *dev) +{ + struct drm_connector *connector; + struct cirrus_connector *cirrus_connector; + + cirrus_connector = kzalloc(sizeof(struct cirrus_connector), GFP_KERNEL); + if (!cirrus_connector) + return NULL; + + connector = &cirrus_connector->base; + + drm_connector_init(dev, connector, + &cirrus_vga_connector_funcs, DRM_MODE_CONNECTOR_VGA); + + drm_connector_helper_add(connector, &cirrus_vga_connector_helper_funcs); + + return connector; +} + + +int cirrus_modeset_init(struct cirrus_device *cdev) +{ + struct drm_encoder *encoder; + struct drm_connector *connector; + int ret; + + drm_mode_config_init(cdev->dev); + cdev->mode_info.mode_config_initialized = true; + + cdev->dev->mode_config.max_width = CIRRUS_MAX_FB_WIDTH; + cdev->dev->mode_config.max_height = CIRRUS_MAX_FB_HEIGHT; + + cdev->dev->mode_config.fb_base = cdev->mc.vram_base; + cdev->dev->mode_config.preferred_depth = 24; + /* don't prefer a shadow on virt GPU */ + cdev->dev->mode_config.prefer_shadow = 0; + + cirrus_crtc_init(cdev->dev); + + encoder = cirrus_encoder_init(cdev->dev); + if (!encoder) { + DRM_ERROR("cirrus_encoder_init failed\n"); + return -1; + } + + connector = cirrus_vga_init(cdev->dev); + if (!connector) { + DRM_ERROR("cirrus_vga_init failed\n"); + return -1; + } + + drm_mode_connector_attach_encoder(connector, encoder); + + ret = cirrus_fbdev_init(cdev); + if (ret) { + DRM_ERROR("cirrus_fbdev_init failed\n"); + return ret; + } + + return 0; +} + +void cirrus_modeset_fini(struct cirrus_device *cdev) +{ + cirrus_fbdev_fini(cdev); + + if (cdev->mode_info.mode_config_initialized) { + drm_mode_config_cleanup(cdev->dev); + cdev->mode_info.mode_config_initialized = false; + } +} diff --git a/drivers/gpu/drm/cirrus/cirrus_ttm.c b/drivers/gpu/drm/cirrus/cirrus_ttm.c new file mode 100644 index 0000000..ae2f08f --- /dev/null +++ b/drivers/gpu/drm/cirrus/cirrus_ttm.c @@ -0,0 +1,453 @@ +/* + * Copyright 2012 Red Hat Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sub license, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDERS, AUTHORS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, + * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR + * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE + * USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * The above copyright notice and this permission notice (including the + * next paragraph) shall be included in all copies or substantial portions + * of the Software. + * + */ +/* + * Authors: Dave Airlie <airlied@redhat.com> + */ +#include "drmP.h" +#include "cirrus_drv.h" +#include <ttm/ttm_page_alloc.h> + +static inline struct cirrus_device * +cirrus_bdev(struct ttm_bo_device *bd) +{ + return container_of(bd, struct cirrus_device, ttm.bdev); +} + +static int +cirrus_ttm_mem_global_init(struct drm_global_reference *ref) +{ + return ttm_mem_global_init(ref->object); +} + +static void +cirrus_ttm_mem_global_release(struct drm_global_reference *ref) +{ + ttm_mem_global_release(ref->object); +} + +static int cirrus_ttm_global_init(struct cirrus_device *cirrus) +{ + struct drm_global_reference *global_ref; + int r; + + global_ref = &cirrus->ttm.mem_global_ref; + global_ref->global_type = DRM_GLOBAL_TTM_MEM; + global_ref->size = sizeof(struct ttm_mem_global); + global_ref->init = &cirrus_ttm_mem_global_init; + global_ref->release = &cirrus_ttm_mem_global_release; + r = drm_global_item_ref(global_ref); + if (r != 0) { + DRM_ERROR("Failed setting up TTM memory accounting " + "subsystem.\n"); + return r; + } + + cirrus->ttm.bo_global_ref.mem_glob = + cirrus->ttm.mem_global_ref.object; + global_ref = &cirrus->ttm.bo_global_ref.ref; + global_ref->global_type = DRM_GLOBAL_TTM_BO; + global_ref->size = sizeof(struct ttm_bo_global); + global_ref->init = &ttm_bo_global_init; + global_ref->release = &ttm_bo_global_release; + r = drm_global_item_ref(global_ref); + if (r != 0) { + DRM_ERROR("Failed setting up TTM BO subsystem.\n"); + drm_global_item_unref(&cirrus->ttm.mem_global_ref); + return r; + } + return 0; +} + +void +cirrus_ttm_global_release(struct cirrus_device *cirrus) +{ + if (cirrus->ttm.mem_global_ref.release == NULL) + return; + + drm_global_item_unref(&cirrus->ttm.bo_global_ref.ref); + drm_global_item_unref(&cirrus->ttm.mem_global_ref); + cirrus->ttm.mem_global_ref.release = NULL; +} + + +static void cirrus_bo_ttm_destroy(struct ttm_buffer_object *tbo) +{ + struct cirrus_bo *bo; + + bo = container_of(tbo, struct cirrus_bo, bo); + + drm_gem_object_release(&bo->gem); + kfree(bo); +} + +bool cirrus_ttm_bo_is_cirrus_bo(struct ttm_buffer_object *bo) +{ + if (bo->destroy == &cirrus_bo_ttm_destroy) + return true; + return false; +} + +static int +cirrus_bo_init_mem_type(struct ttm_bo_device *bdev, uint32_t type, + struct ttm_mem_type_manager *man) +{ + switch (type) { + case TTM_PL_SYSTEM: + man->flags = TTM_MEMTYPE_FLAG_MAPPABLE; + man->available_caching = TTM_PL_MASK_CACHING; + man->default_caching = TTM_PL_FLAG_CACHED; + break; + case TTM_PL_VRAM: + man->func = &ttm_bo_manager_func; + man->flags = TTM_MEMTYPE_FLAG_FIXED | + TTM_MEMTYPE_FLAG_MAPPABLE; + man->available_caching = TTM_PL_FLAG_UNCACHED | + TTM_PL_FLAG_WC; + man->default_caching = TTM_PL_FLAG_WC; + break; + default: + DRM_ERROR("Unsupported memory type %u\n", (unsigned)type); + return -EINVAL; + } + return 0; +} + +static void +cirrus_bo_evict_flags(struct ttm_buffer_object *bo, struct ttm_placement *pl) +{ + struct cirrus_bo *cirrusbo = cirrus_bo(bo); + + if (!cirrus_ttm_bo_is_cirrus_bo(bo)) + return; + + cirrus_ttm_placement(cirrusbo, TTM_PL_FLAG_SYSTEM); + *pl = cirrusbo->placement; +} + +static int cirrus_bo_verify_access(struct ttm_buffer_object *bo, struct file *filp) +{ + return 0; +} + +static int cirrus_ttm_io_mem_reserve(struct ttm_bo_device *bdev, + struct ttm_mem_reg *mem) +{ + struct ttm_mem_type_manager *man = &bdev->man[mem->mem_type]; + struct cirrus_device *cirrus = cirrus_bdev(bdev); + + mem->bus.addr = NULL; + mem->bus.offset = 0; + mem->bus.size = mem->num_pages << PAGE_SHIFT; + mem->bus.base = 0; + mem->bus.is_iomem = false; + if (!(man->flags & TTM_MEMTYPE_FLAG_MAPPABLE)) + return -EINVAL; + switch (mem->mem_type) { + case TTM_PL_SYSTEM: + /* system memory */ + return 0; + case TTM_PL_VRAM: + mem->bus.offset = mem->start << PAGE_SHIFT; + mem->bus.base = pci_resource_start(cirrus->dev->pdev, 0); + mem->bus.is_iomem = true; + break; + default: + return -EINVAL; + break; + } + return 0; +} + +static void cirrus_ttm_io_mem_free(struct ttm_bo_device *bdev, struct ttm_mem_reg *mem) +{ +} + +static int cirrus_bo_move(struct ttm_buffer_object *bo, + bool evict, bool interruptible, + bool no_wait_reserve, bool no_wait_gpu, + struct ttm_mem_reg *new_mem) +{ + int r; + r = ttm_bo_move_memcpy(bo, evict, no_wait_reserve, no_wait_gpu, new_mem); + return r; +} + + +static void cirrus_ttm_backend_destroy(struct ttm_tt *tt) +{ + ttm_tt_fini(tt); + kfree(tt); +} + +static struct ttm_backend_func cirrus_tt_backend_func = { + .destroy = &cirrus_ttm_backend_destroy, +}; + + +struct ttm_tt *cirrus_ttm_tt_create(struct ttm_bo_device *bdev, + unsigned long size, uint32_t page_flags, + struct page *dummy_read_page) +{ + struct ttm_tt *tt; + + tt = kzalloc(sizeof(struct ttm_tt), GFP_KERNEL); + if (tt == NULL) + return NULL; + tt->func = &cirrus_tt_backend_func; + if (ttm_tt_init(tt, bdev, size, page_flags, dummy_read_page)) { + kfree(tt); + return NULL; + } + return tt; +} + +static int cirrus_ttm_tt_populate(struct ttm_tt *ttm) +{ + return ttm_pool_populate(ttm); +} + +static void cirrus_ttm_tt_unpopulate(struct ttm_tt *ttm) +{ + ttm_pool_unpopulate(ttm); +} + +struct ttm_bo_driver cirrus_bo_driver = { + .ttm_tt_create = cirrus_ttm_tt_create, + .ttm_tt_populate = cirrus_ttm_tt_populate, + .ttm_tt_unpopulate = cirrus_ttm_tt_unpopulate, + .init_mem_type = cirrus_bo_init_mem_type, + .evict_flags = cirrus_bo_evict_flags, + .move = cirrus_bo_move, + .verify_access = cirrus_bo_verify_access, + .io_mem_reserve = &cirrus_ttm_io_mem_reserve, + .io_mem_free = &cirrus_ttm_io_mem_free, +}; + +int cirrus_mm_init(struct cirrus_device *cirrus) +{ + int ret; + struct drm_device *dev = cirrus->dev; + struct ttm_bo_device *bdev = &cirrus->ttm.bdev; + + ret = cirrus_ttm_global_init(cirrus); + if (ret) + return ret; + + ret = ttm_bo_device_init(&cirrus->ttm.bdev, + cirrus->ttm.bo_global_ref.ref.object, + &cirrus_bo_driver, DRM_FILE_PAGE_OFFSET, + true); + if (ret) { + DRM_ERROR("Error initialising bo driver; %d\n", ret); + return ret; + } + + ret = ttm_bo_init_mm(bdev, TTM_PL_VRAM, + cirrus->mc.vram_size >> PAGE_SHIFT); + if (ret) { + DRM_ERROR("Failed ttm VRAM init: %d\n", ret); + return ret; + } + + cirrus->fb_mtrr = drm_mtrr_add(pci_resource_start(dev->pdev, 0), + pci_resource_len(dev->pdev, 0), + DRM_MTRR_WC); + + return 0; +} + +void cirrus_mm_fini(struct cirrus_device *cirrus) +{ + struct drm_device *dev = cirrus->dev; + ttm_bo_device_release(&cirrus->ttm.bdev); + + cirrus_ttm_global_release(cirrus); + + if (cirrus->fb_mtrr >= 0) { + drm_mtrr_del(cirrus->fb_mtrr, + pci_resource_start(dev->pdev, 0), + pci_resource_len(dev->pdev, 0), DRM_MTRR_WC); + cirrus->fb_mtrr = -1; + } +} + +void cirrus_ttm_placement(struct cirrus_bo *bo, int domain) +{ + u32 c = 0; + bo->placement.fpfn = 0; + bo->placement.lpfn = 0; + bo->placement.placement = bo->placements; + bo->placement.busy_placement = bo->placements; + if (domain & TTM_PL_FLAG_VRAM) + bo->placements[c++] = TTM_PL_FLAG_WC | TTM_PL_FLAG_UNCACHED | TTM_PL_FLAG_VRAM; + if (domain & TTM_PL_FLAG_SYSTEM) + bo->placements[c++] = TTM_PL_MASK_CACHING | TTM_PL_FLAG_SYSTEM; + if (!c) + bo->placements[c++] = TTM_PL_MASK_CACHING | TTM_PL_FLAG_SYSTEM; + bo->placement.num_placement = c; + bo->placement.num_busy_placement = c; +} + +int cirrus_bo_reserve(struct cirrus_bo *bo, bool no_wait) +{ + int ret; + + ret = ttm_bo_reserve(&bo->bo, true, no_wait, false, 0); + if (ret) { + if (ret != -ERESTARTSYS) + DRM_ERROR("reserve failed %p\n", bo); + return ret; + } + return 0; +} + +void cirrus_bo_unreserve(struct cirrus_bo *bo) +{ + ttm_bo_unreserve(&bo->bo); +} + +int cirrus_bo_create(struct drm_device *dev, int size, int align, + uint32_t flags, struct cirrus_bo **pcirrusbo) +{ + struct cirrus_device *cirrus = dev->dev_private; + struct cirrus_bo *cirrusbo; + size_t acc_size; + int ret; + + cirrusbo = kzalloc(sizeof(struct cirrus_bo), GFP_KERNEL); + if (!cirrusbo) + return -ENOMEM; + + ret = drm_gem_object_init(dev, &cirrusbo->gem, size); + if (ret) { + kfree(cirrusbo); + return ret; + } + + cirrusbo->gem.driver_private = NULL; + cirrusbo->bo.bdev = &cirrus->ttm.bdev; + + cirrus_ttm_placement(cirrusbo, TTM_PL_FLAG_VRAM | TTM_PL_FLAG_SYSTEM); + + acc_size = ttm_bo_dma_acc_size(&cirrus->ttm.bdev, size, + sizeof(struct cirrus_bo)); + + ret = ttm_bo_init(&cirrus->ttm.bdev, &cirrusbo->bo, size, + ttm_bo_type_device, &cirrusbo->placement, + align >> PAGE_SHIFT, 0, false, NULL, acc_size, + cirrus_bo_ttm_destroy); + if (ret) + return ret; + + *pcirrusbo = cirrusbo; + return 0; +} + +static inline u64 cirrus_bo_gpu_offset(struct cirrus_bo *bo) +{ + return bo->bo.offset; +} + +int cirrus_bo_pin(struct cirrus_bo *bo, u32 pl_flag, u64 *gpu_addr) +{ + int i, ret; + + if (bo->pin_count) { + bo->pin_count++; + if (gpu_addr) + *gpu_addr = cirrus_bo_gpu_offset(bo); + } + + cirrus_ttm_placement(bo, pl_flag); + for (i = 0; i < bo->placement.num_placement; i++) + bo->placements[i] |= TTM_PL_FLAG_NO_EVICT; + ret = ttm_bo_validate(&bo->bo, &bo->placement, false, false, false); + if (ret) + return ret; + + bo->pin_count = 1; + if (gpu_addr) + *gpu_addr = cirrus_bo_gpu_offset(bo); + return 0; +} + +int cirrus_bo_unpin(struct cirrus_bo *bo) +{ + int i, ret; + if (!bo->pin_count) { + DRM_ERROR("unpin bad %p\n", bo); + return 0; + } + bo->pin_count--; + if (bo->pin_count) + return 0; + + for (i = 0; i < bo->placement.num_placement ; i++) + bo->placements[i] &= ~TTM_PL_FLAG_NO_EVICT; + ret = ttm_bo_validate(&bo->bo, &bo->placement, false, false, false); + if (ret) + return ret; + + return 0; +} + +int cirrus_bo_push_sysram(struct cirrus_bo *bo) +{ + int i, ret; + if (!bo->pin_count) { + DRM_ERROR("unpin bad %p\n", bo); + return 0; + } + bo->pin_count--; + if (bo->pin_count) + return 0; + + if (bo->kmap.virtual) + ttm_bo_kunmap(&bo->kmap); + + cirrus_ttm_placement(bo, TTM_PL_FLAG_SYSTEM); + for (i = 0; i < bo->placement.num_placement ; i++) + bo->placements[i] |= TTM_PL_FLAG_NO_EVICT; + + ret = ttm_bo_validate(&bo->bo, &bo->placement, false, false, false); + if (ret) { + DRM_ERROR("pushing to VRAM failed\n"); + return ret; + } + return 0; +} + +int cirrus_mmap(struct file *filp, struct vm_area_struct *vma) +{ + struct drm_file *file_priv; + struct cirrus_device *cirrus; + + if (unlikely(vma->vm_pgoff < DRM_FILE_PAGE_OFFSET)) + return drm_mmap(filp, vma); + + file_priv = filp->private_data; + cirrus = file_priv->minor->dev->dev_private; + return ttm_bo_mmap(filp, vma, &cirrus->ttm.bdev); +} diff --git a/drivers/gpu/drm/drm_crtc.c b/drivers/gpu/drm/drm_crtc.c index ee63a12..6cb47d9 100644 --- a/drivers/gpu/drm/drm_crtc.c +++ b/drivers/gpu/drm/drm_crtc.c @@ -363,7 +363,7 @@ EXPORT_SYMBOL(drm_framebuffer_cleanup); * @funcs: callbacks for the new CRTC * * LOCKING: - * Caller must hold mode config lock. + * Takes mode_config lock. * * Inits a new object created as base part of an driver crtc object. * @@ -384,6 +384,8 @@ int drm_crtc_init(struct drm_device *dev, struct drm_crtc *crtc, if (ret) goto out; + crtc->base.properties = &crtc->properties; + list_add_tail(&crtc->head, &dev->mode_config.crtc_list); dev->mode_config.num_crtc++; @@ -483,6 +485,7 @@ int drm_connector_init(struct drm_device *dev, if (ret) goto out; + connector->base.properties = &connector->properties; connector->dev = dev; connector->funcs = funcs; connector->connector_type = connector_type; @@ -1424,11 +1427,7 @@ int drm_mode_getconnector(struct drm_device *dev, void *data, } connector = obj_to_connector(obj); - for (i = 0; i < DRM_CONNECTOR_MAX_PROPERTY; i++) { - if (connector->property_ids[i] != 0) { - props_count++; - } - } + props_count = connector->properties.count; for (i = 0; i < DRM_CONNECTOR_MAX_ENCODER; i++) { if (connector->encoder_ids[i] != 0) { @@ -1481,21 +1480,19 @@ int drm_mode_getconnector(struct drm_device *dev, void *data, copied = 0; prop_ptr = (uint32_t __user *)(unsigned long)(out_resp->props_ptr); prop_values = (uint64_t __user *)(unsigned long)(out_resp->prop_values_ptr); - for (i = 0; i < DRM_CONNECTOR_MAX_PROPERTY; i++) { - if (connector->property_ids[i] != 0) { - if (put_user(connector->property_ids[i], - prop_ptr + copied)) { - ret = -EFAULT; - goto out; - } + for (i = 0; i < connector->properties.count; i++) { + if (put_user(connector->properties.ids[i], + prop_ptr + copied)) { + ret = -EFAULT; + goto out; + } - if (put_user(connector->property_values[i], - prop_values + copied)) { - ret = -EFAULT; - goto out; - } - copied++; + if (put_user(connector->properties.values[i], + prop_values + copied)) { + ret = -EFAULT; + goto out; } + copied++; } } out_resp->count_props = props_count; @@ -1832,7 +1829,7 @@ int drm_mode_setcrtc(struct drm_device *dev, void *data, struct drm_display_mode *mode = NULL; struct drm_mode_set set; uint32_t __user *set_connectors_ptr; - int ret = 0; + int ret; int i; if (!drm_core_check_feature(dev, DRIVER_MODESET)) @@ -2251,7 +2248,7 @@ int drm_mode_addfb2(struct drm_device *dev, struct drm_mode_fb_cmd2 *r = data; struct drm_mode_config *config = &dev->mode_config; struct drm_framebuffer *fb; - int ret = 0; + int ret; if (!drm_core_check_feature(dev, DRIVER_MODESET)) return -EINVAL; @@ -2406,7 +2403,7 @@ int drm_mode_dirtyfb_ioctl(struct drm_device *dev, struct drm_framebuffer *fb; unsigned flags; int num_clips; - int ret = 0; + int ret; if (!drm_core_check_feature(dev, DRIVER_MODESET)) return -EINVAL; @@ -2605,7 +2602,7 @@ int drm_mode_attachmode_ioctl(struct drm_device *dev, struct drm_display_mode *mode; struct drm_mode_object *obj; struct drm_mode_modeinfo *umode = &mode_cmd->mode; - int ret = 0; + int ret; if (!drm_core_check_feature(dev, DRIVER_MODESET)) return -EINVAL; @@ -2659,7 +2656,7 @@ int drm_mode_detachmode_ioctl(struct drm_device *dev, struct drm_connector *connector; struct drm_display_mode mode; struct drm_mode_modeinfo *umode = &mode_cmd->mode; - int ret = 0; + int ret; if (!drm_core_check_feature(dev, DRIVER_MODESET)) return -EINVAL; @@ -2819,60 +2816,78 @@ void drm_property_destroy(struct drm_device *dev, struct drm_property *property) } EXPORT_SYMBOL(drm_property_destroy); -int drm_connector_attach_property(struct drm_connector *connector, +void drm_connector_attach_property(struct drm_connector *connector, struct drm_property *property, uint64_t init_val) { - int i; - - for (i = 0; i < DRM_CONNECTOR_MAX_PROPERTY; i++) { - if (connector->property_ids[i] == 0) { - connector->property_ids[i] = property->base.id; - connector->property_values[i] = init_val; - break; - } - } - - if (i == DRM_CONNECTOR_MAX_PROPERTY) - return -EINVAL; - return 0; + drm_object_attach_property(&connector->base, property, init_val); } EXPORT_SYMBOL(drm_connector_attach_property); int drm_connector_property_set_value(struct drm_connector *connector, struct drm_property *property, uint64_t value) { + return drm_object_property_set_value(&connector->base, property, value); +} +EXPORT_SYMBOL(drm_connector_property_set_value); + +int drm_connector_property_get_value(struct drm_connector *connector, + struct drm_property *property, uint64_t *val) +{ + return drm_object_property_get_value(&connector->base, property, val); +} +EXPORT_SYMBOL(drm_connector_property_get_value); + +void drm_object_attach_property(struct drm_mode_object *obj, + struct drm_property *property, + uint64_t init_val) +{ + int count = obj->properties->count; + + if (count == DRM_OBJECT_MAX_PROPERTY) { + WARN(1, "Failed to attach object property (type: 0x%x). Please " + "increase DRM_OBJECT_MAX_PROPERTY by 1 for each time " + "you see this message on the same object type.\n", + obj->type); + return; + } + + obj->properties->ids[count] = property->base.id; + obj->properties->values[count] = init_val; + obj->properties->count++; +} +EXPORT_SYMBOL(drm_object_attach_property); + +int drm_object_property_set_value(struct drm_mode_object *obj, + struct drm_property *property, uint64_t val) +{ int i; - for (i = 0; i < DRM_CONNECTOR_MAX_PROPERTY; i++) { - if (connector->property_ids[i] == property->base.id) { - connector->property_values[i] = value; - break; + for (i = 0; i < obj->properties->count; i++) { + if (obj->properties->ids[i] == property->base.id) { + obj->properties->values[i] = val; + return 0; } } - if (i == DRM_CONNECTOR_MAX_PROPERTY) - return -EINVAL; - return 0; + return -EINVAL; } -EXPORT_SYMBOL(drm_connector_property_set_value); +EXPORT_SYMBOL(drm_object_property_set_value); -int drm_connector_property_get_value(struct drm_connector *connector, +int drm_object_property_get_value(struct drm_mode_object *obj, struct drm_property *property, uint64_t *val) { int i; - for (i = 0; i < DRM_CONNECTOR_MAX_PROPERTY; i++) { - if (connector->property_ids[i] == property->base.id) { - *val = connector->property_values[i]; - break; + for (i = 0; i < obj->properties->count; i++) { + if (obj->properties->ids[i] == property->base.id) { + *val = obj->properties->values[i]; + return 0; } } - if (i == DRM_CONNECTOR_MAX_PROPERTY) - return -EINVAL; - return 0; + return -EINVAL; } -EXPORT_SYMBOL(drm_connector_property_get_value); +EXPORT_SYMBOL(drm_object_property_get_value); int drm_mode_getproperty_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv) @@ -3050,7 +3065,7 @@ int drm_mode_connector_update_edid_property(struct drm_connector *connector, struct edid *edid) { struct drm_device *dev = connector->dev; - int ret = 0, size; + int ret, size; if (connector->edid_blob_ptr) drm_property_destroy_blob(dev, connector->edid_blob_ptr); @@ -3074,75 +3089,178 @@ int drm_mode_connector_update_edid_property(struct drm_connector *connector, } EXPORT_SYMBOL(drm_mode_connector_update_edid_property); +static bool drm_property_change_is_valid(struct drm_property *property, + __u64 value) +{ + if (property->flags & DRM_MODE_PROP_IMMUTABLE) + return false; + if (property->flags & DRM_MODE_PROP_RANGE) { + if (value < property->values[0] || value > property->values[1]) + return false; + return true; + } else { + int i; + for (i = 0; i < property->num_values; i++) + if (property->values[i] == value) + return true; + return false; + } +} + int drm_mode_connector_property_set_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv) { - struct drm_mode_connector_set_property *out_resp = data; - struct drm_mode_object *obj; - struct drm_property *property; - struct drm_connector *connector; + struct drm_mode_connector_set_property *conn_set_prop = data; + struct drm_mode_obj_set_property obj_set_prop = { + .value = conn_set_prop->value, + .prop_id = conn_set_prop->prop_id, + .obj_id = conn_set_prop->connector_id, + .obj_type = DRM_MODE_OBJECT_CONNECTOR + }; + + /* It does all the locking and checking we need */ + return drm_mode_obj_set_property_ioctl(dev, &obj_set_prop, file_priv); +} + +static int drm_mode_connector_set_obj_prop(struct drm_mode_object *obj, + struct drm_property *property, + uint64_t value) +{ + int ret = -EINVAL; + struct drm_connector *connector = obj_to_connector(obj); + + /* Do DPMS ourselves */ + if (property == connector->dev->mode_config.dpms_property) { + if (connector->funcs->dpms) + (*connector->funcs->dpms)(connector, (int)value); + ret = 0; + } else if (connector->funcs->set_property) + ret = connector->funcs->set_property(connector, property, value); + + /* store the property value if successful */ + if (!ret) + drm_connector_property_set_value(connector, property, value); + return ret; +} + +static int drm_mode_crtc_set_obj_prop(struct drm_mode_object *obj, + struct drm_property *property, + uint64_t value) +{ int ret = -EINVAL; + struct drm_crtc *crtc = obj_to_crtc(obj); + + if (crtc->funcs->set_property) + ret = crtc->funcs->set_property(crtc, property, value); + if (!ret) + drm_object_property_set_value(obj, property, value); + + return ret; +} + +int drm_mode_obj_get_properties_ioctl(struct drm_device *dev, void *data, + struct drm_file *file_priv) +{ + struct drm_mode_obj_get_properties *arg = data; + struct drm_mode_object *obj; + int ret = 0; int i; + int copied = 0; + int props_count = 0; + uint32_t __user *props_ptr; + uint64_t __user *prop_values_ptr; if (!drm_core_check_feature(dev, DRIVER_MODESET)) return -EINVAL; mutex_lock(&dev->mode_config.mutex); - obj = drm_mode_object_find(dev, out_resp->connector_id, DRM_MODE_OBJECT_CONNECTOR); + obj = drm_mode_object_find(dev, arg->obj_id, arg->obj_type); if (!obj) { + ret = -EINVAL; + goto out; + } + if (!obj->properties) { + ret = -EINVAL; goto out; } - connector = obj_to_connector(obj); - for (i = 0; i < DRM_CONNECTOR_MAX_PROPERTY; i++) { - if (connector->property_ids[i] == out_resp->prop_id) - break; + props_count = obj->properties->count; + + /* This ioctl is called twice, once to determine how much space is + * needed, and the 2nd time to fill it. */ + if ((arg->count_props >= props_count) && props_count) { + copied = 0; + props_ptr = (uint32_t __user *)(unsigned long)(arg->props_ptr); + prop_values_ptr = (uint64_t __user *)(unsigned long) + (arg->prop_values_ptr); + for (i = 0; i < props_count; i++) { + if (put_user(obj->properties->ids[i], + props_ptr + copied)) { + ret = -EFAULT; + goto out; + } + if (put_user(obj->properties->values[i], + prop_values_ptr + copied)) { + ret = -EFAULT; + goto out; + } + copied++; + } } + arg->count_props = props_count; +out: + mutex_unlock(&dev->mode_config.mutex); + return ret; +} + +int drm_mode_obj_set_property_ioctl(struct drm_device *dev, void *data, + struct drm_file *file_priv) +{ + struct drm_mode_obj_set_property *arg = data; + struct drm_mode_object *arg_obj; + struct drm_mode_object *prop_obj; + struct drm_property *property; + int ret = -EINVAL; + int i; - if (i == DRM_CONNECTOR_MAX_PROPERTY) { + if (!drm_core_check_feature(dev, DRIVER_MODESET)) + return -EINVAL; + + mutex_lock(&dev->mode_config.mutex); + + arg_obj = drm_mode_object_find(dev, arg->obj_id, arg->obj_type); + if (!arg_obj) + goto out; + if (!arg_obj->properties) goto out; - } - obj = drm_mode_object_find(dev, out_resp->prop_id, DRM_MODE_OBJECT_PROPERTY); - if (!obj) { + for (i = 0; i < arg_obj->properties->count; i++) + if (arg_obj->properties->ids[i] == arg->prop_id) + break; + + if (i == arg_obj->properties->count) goto out; - } - property = obj_to_property(obj); - if (property->flags & DRM_MODE_PROP_IMMUTABLE) + prop_obj = drm_mode_object_find(dev, arg->prop_id, + DRM_MODE_OBJECT_PROPERTY); + if (!prop_obj) goto out; + property = obj_to_property(prop_obj); - if (property->flags & DRM_MODE_PROP_RANGE) { - if (out_resp->value < property->values[0]) - goto out; + if (!drm_property_change_is_valid(property, arg->value)) + goto out; - if (out_resp->value > property->values[1]) - goto out; - } else { - int found = 0; - for (i = 0; i < property->num_values; i++) { - if (property->values[i] == out_resp->value) { - found = 1; - break; - } - } - if (!found) { - goto out; - } + switch (arg_obj->type) { + case DRM_MODE_OBJECT_CONNECTOR: + ret = drm_mode_connector_set_obj_prop(arg_obj, property, + arg->value); + break; + case DRM_MODE_OBJECT_CRTC: + ret = drm_mode_crtc_set_obj_prop(arg_obj, property, arg->value); + break; } - /* Do DPMS ourselves */ - if (property == connector->dev->mode_config.dpms_property) { - if (connector->funcs->dpms) - (*connector->funcs->dpms)(connector, (int) out_resp->value); - ret = 0; - } else if (connector->funcs->set_property) - ret = connector->funcs->set_property(connector, property, out_resp->value); - - /* store the property value if successful */ - if (!ret) - drm_connector_property_set_value(connector, property, out_resp->value); out: mutex_unlock(&dev->mode_config.mutex); return ret; @@ -3214,6 +3332,11 @@ int drm_mode_gamma_set_ioctl(struct drm_device *dev, } crtc = obj_to_crtc(obj); + if (crtc->funcs->gamma_set == NULL) { + ret = -ENOSYS; + goto out; + } + /* memcpy into gamma store */ if (crtc_lut->gamma_size != crtc->gamma_size) { ret = -EINVAL; diff --git a/drivers/gpu/drm/drm_crtc_helper.c b/drivers/gpu/drm/drm_crtc_helper.c index 974196a..3252e70 100644 --- a/drivers/gpu/drm/drm_crtc_helper.c +++ b/drivers/gpu/drm/drm_crtc_helper.c @@ -518,7 +518,7 @@ int drm_crtc_helper_set_config(struct drm_mode_set *set) int count = 0, ro, fail = 0; struct drm_crtc_helper_funcs *crtc_funcs; struct drm_mode_set save_set; - int ret = 0; + int ret; int i; DRM_DEBUG_KMS("\n"); diff --git a/drivers/gpu/drm/drm_drv.c b/drivers/gpu/drm/drm_drv.c index 6116e3b..8a9d079 100644 --- a/drivers/gpu/drm/drm_drv.c +++ b/drivers/gpu/drm/drm_drv.c @@ -163,7 +163,9 @@ static struct drm_ioctl_desc drm_ioctls[] = { DRM_IOCTL_DEF(DRM_IOCTL_MODE_DIRTYFB, drm_mode_dirtyfb_ioctl, DRM_MASTER|DRM_CONTROL_ALLOW|DRM_UNLOCKED), DRM_IOCTL_DEF(DRM_IOCTL_MODE_CREATE_DUMB, drm_mode_create_dumb_ioctl, DRM_MASTER|DRM_CONTROL_ALLOW|DRM_UNLOCKED), DRM_IOCTL_DEF(DRM_IOCTL_MODE_MAP_DUMB, drm_mode_mmap_dumb_ioctl, DRM_MASTER|DRM_CONTROL_ALLOW|DRM_UNLOCKED), - DRM_IOCTL_DEF(DRM_IOCTL_MODE_DESTROY_DUMB, drm_mode_destroy_dumb_ioctl, DRM_MASTER|DRM_CONTROL_ALLOW|DRM_UNLOCKED) + DRM_IOCTL_DEF(DRM_IOCTL_MODE_DESTROY_DUMB, drm_mode_destroy_dumb_ioctl, DRM_MASTER|DRM_CONTROL_ALLOW|DRM_UNLOCKED), + DRM_IOCTL_DEF(DRM_IOCTL_MODE_OBJ_GETPROPERTIES, drm_mode_obj_get_properties_ioctl, DRM_MASTER|DRM_CONTROL_ALLOW|DRM_UNLOCKED), + DRM_IOCTL_DEF(DRM_IOCTL_MODE_OBJ_SETPROPERTY, drm_mode_obj_set_property_ioctl, DRM_MASTER|DRM_CONTROL_ALLOW|DRM_UNLOCKED), }; #define DRM_CORE_IOCTL_COUNT ARRAY_SIZE( drm_ioctls ) diff --git a/drivers/gpu/drm/drm_edid_load.c b/drivers/gpu/drm/drm_edid_load.c index 48c927c..66d4a28 100644 --- a/drivers/gpu/drm/drm_edid_load.c +++ b/drivers/gpu/drm/drm_edid_load.c @@ -220,18 +220,18 @@ int drm_load_edid_firmware(struct drm_connector *connector) { char *connector_name = drm_get_connector_name(connector); char *edidname = edid_firmware, *last, *colon; - int ret = 0; + int ret; if (*edidname == '\0') - return ret; + return 0; colon = strchr(edidname, ':'); if (colon != NULL) { if (strncmp(connector_name, edidname, colon - edidname)) - return ret; + return 0; edidname = colon + 1; if (*edidname == '\0') - return ret; + return 0; } last = edidname + strlen(edidname) - 1; diff --git a/drivers/gpu/drm/drm_fb_helper.c b/drivers/gpu/drm/drm_fb_helper.c index 6e19dd1..5683b7f 100644 --- a/drivers/gpu/drm/drm_fb_helper.c +++ b/drivers/gpu/drm/drm_fb_helper.c @@ -136,6 +136,9 @@ static void drm_fb_helper_restore_lut_atomic(struct drm_crtc *crtc) { uint16_t *r_base, *g_base, *b_base; + if (crtc->funcs->gamma_set == NULL) + return; + r_base = crtc->gamma_store; g_base = r_base + crtc->gamma_size; b_base = g_base + crtc->gamma_size; @@ -383,7 +386,6 @@ int drm_fb_helper_init(struct drm_device *dev, int crtc_count, int max_conn_count) { struct drm_crtc *crtc; - int ret = 0; int i; fb_helper->dev = dev; @@ -408,10 +410,8 @@ int drm_fb_helper_init(struct drm_device *dev, sizeof(struct drm_connector *), GFP_KERNEL); - if (!fb_helper->crtc_info[i].mode_set.connectors) { - ret = -ENOMEM; + if (!fb_helper->crtc_info[i].mode_set.connectors) goto out_free; - } fb_helper->crtc_info[i].mode_set.num_connectors = 0; } diff --git a/drivers/gpu/drm/drm_gem.c b/drivers/gpu/drm/drm_gem.c index fc6ded8..72ba075 100644 --- a/drivers/gpu/drm/drm_gem.c +++ b/drivers/gpu/drm/drm_gem.c @@ -328,7 +328,7 @@ drm_gem_create_mmap_offset(struct drm_gem_object *obj) struct drm_gem_mm *mm = dev->mm_private; struct drm_map_list *list; struct drm_local_map *map; - int ret = 0; + int ret; /* Set the object up for mmap'ing */ list = &obj->map_list; @@ -626,7 +626,7 @@ void drm_gem_vm_open(struct vm_area_struct *vma) drm_gem_object_reference(obj); mutex_lock(&obj->dev->struct_mutex); - drm_vm_open_locked(vma); + drm_vm_open_locked(obj->dev, vma); mutex_unlock(&obj->dev->struct_mutex); } EXPORT_SYMBOL(drm_gem_vm_open); @@ -637,7 +637,7 @@ void drm_gem_vm_close(struct vm_area_struct *vma) struct drm_device *dev = obj->dev; mutex_lock(&dev->struct_mutex); - drm_vm_close_locked(vma); + drm_vm_close_locked(obj->dev, vma); drm_gem_object_unreference(obj); mutex_unlock(&dev->struct_mutex); } @@ -710,7 +710,7 @@ int drm_gem_mmap(struct file *filp, struct vm_area_struct *vma) */ drm_gem_object_reference(obj); - drm_vm_open_locked(vma); + drm_vm_open_locked(dev, vma); out_unlock: mutex_unlock(&dev->struct_mutex); diff --git a/drivers/gpu/drm/drm_ioctl.c b/drivers/gpu/drm/drm_ioctl.c index cf85155..64a62c6 100644 --- a/drivers/gpu/drm/drm_ioctl.c +++ b/drivers/gpu/drm/drm_ioctl.c @@ -283,6 +283,10 @@ int drm_getcap(struct drm_device *dev, void *data, struct drm_file *file_priv) case DRM_CAP_DUMB_PREFER_SHADOW: req->value = dev->mode_config.prefer_shadow; break; + case DRM_CAP_PRIME: + req->value |= dev->driver->prime_fd_to_handle ? DRM_PRIME_CAP_IMPORT : 0; + req->value |= dev->driver->prime_handle_to_fd ? DRM_PRIME_CAP_EXPORT : 0; + break; default: return -EINVAL; } diff --git a/drivers/gpu/drm/drm_irq.c b/drivers/gpu/drm/drm_irq.c index c869436..c798eea 100644 --- a/drivers/gpu/drm/drm_irq.c +++ b/drivers/gpu/drm/drm_irq.c @@ -189,7 +189,7 @@ void drm_vblank_cleanup(struct drm_device *dev) if (dev->num_crtcs == 0) return; - del_timer(&dev->vblank_disable_timer); + del_timer_sync(&dev->vblank_disable_timer); vblank_disable_fn((unsigned long)dev); @@ -310,7 +310,7 @@ static void drm_irq_vgaarb_nokms(void *cookie, bool state) */ int drm_irq_install(struct drm_device *dev) { - int ret = 0; + int ret; unsigned long sh_flags = 0; char *irqname; @@ -731,7 +731,7 @@ EXPORT_SYMBOL(drm_calc_vbltimestamp_from_scanoutpos); u32 drm_get_last_vbltimestamp(struct drm_device *dev, int crtc, struct timeval *tvblank, unsigned flags) { - int ret = 0; + int ret; /* Define requested maximum error on timestamps (nanoseconds). */ int max_error = (int) drm_timestamp_precision * 1000; @@ -1031,18 +1031,15 @@ int drm_modeset_ctl(struct drm_device *dev, void *data, struct drm_file *file_priv) { struct drm_modeset_ctl *modeset = data; - int ret = 0; unsigned int crtc; /* If drm_vblank_init() hasn't been called yet, just no-op */ if (!dev->num_crtcs) - goto out; + return 0; crtc = modeset->crtc; - if (crtc >= dev->num_crtcs) { - ret = -EINVAL; - goto out; - } + if (crtc >= dev->num_crtcs) + return -EINVAL; switch (modeset->cmd) { case _DRM_PRE_MODESET: @@ -1052,12 +1049,10 @@ int drm_modeset_ctl(struct drm_device *dev, void *data, drm_vblank_post_modeset(dev, crtc); break; default: - ret = -EINVAL; - break; + return -EINVAL; } -out: - return ret; + return 0; } static int drm_queue_vblank_event(struct drm_device *dev, int pipe, @@ -1154,7 +1149,7 @@ int drm_wait_vblank(struct drm_device *dev, void *data, struct drm_file *file_priv) { union drm_wait_vblank *vblwait = data; - int ret = 0; + int ret; unsigned int flags, seq, crtc, high_crtc; if ((!drm_dev_to_irq(dev)) || (!dev->irq_enabled)) diff --git a/drivers/gpu/drm/drm_lock.c b/drivers/gpu/drm/drm_lock.c index c79c713..5211520 100644 --- a/drivers/gpu/drm/drm_lock.c +++ b/drivers/gpu/drm/drm_lock.c @@ -331,7 +331,7 @@ static int drm_notifier(void *priv) void drm_idlelock_take(struct drm_lock_data *lock_data) { - int ret = 0; + int ret; spin_lock_bh(&lock_data->spinlock); lock_data->kernel_waiters++; diff --git a/drivers/gpu/drm/drm_stub.c b/drivers/gpu/drm/drm_stub.c index ae1ccf1..21bcd4a 100644 --- a/drivers/gpu/drm/drm_stub.c +++ b/drivers/gpu/drm/drm_stub.c @@ -210,7 +210,7 @@ EXPORT_SYMBOL(drm_master_put); int drm_setmaster_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv) { - int ret = 0; + int ret; if (file_priv->is_master) return 0; diff --git a/drivers/gpu/drm/drm_sysfs.c b/drivers/gpu/drm/drm_sysfs.c index 5a7bd51..45cf1dd 100644 --- a/drivers/gpu/drm/drm_sysfs.c +++ b/drivers/gpu/drm/drm_sysfs.c @@ -347,17 +347,17 @@ static struct bin_attribute edid_attr = { }; /** - * drm_sysfs_connector_add - add an connector to sysfs + * drm_sysfs_connector_add - add a connector to sysfs * @connector: connector to add * - * Create an connector device in sysfs, along with its associated connector + * Create a connector device in sysfs, along with its associated connector * properties (so far, connection status, dpms, mode list & edid) and * generate a hotplug event so userspace knows there's a new connector * available. * * Note: - * This routine should only be called *once* for each DRM minor registered. - * A second call for an already registered device will trigger the BUG_ON + * This routine should only be called *once* for each registered connector. + * A second call for an already registered connector will trigger the BUG_ON * below. */ int drm_sysfs_connector_add(struct drm_connector *connector) @@ -366,7 +366,7 @@ int drm_sysfs_connector_add(struct drm_connector *connector) int attr_cnt = 0; int opt_cnt = 0; int i; - int ret = 0; + int ret; /* We shouldn't get called more than once for the same connector */ BUG_ON(device_is_registered(&connector->kdev)); diff --git a/drivers/gpu/drm/drm_vm.c b/drivers/gpu/drm/drm_vm.c index 1495618..961ee08 100644 --- a/drivers/gpu/drm/drm_vm.c +++ b/drivers/gpu/drm/drm_vm.c @@ -406,10 +406,9 @@ static const struct vm_operations_struct drm_vm_sg_ops = { * Create a new drm_vma_entry structure as the \p vma private data entry and * add it to drm_device::vmalist. */ -void drm_vm_open_locked(struct vm_area_struct *vma) +void drm_vm_open_locked(struct drm_device *dev, + struct vm_area_struct *vma) { - struct drm_file *priv = vma->vm_file->private_data; - struct drm_device *dev = priv->minor->dev; struct drm_vma_entry *vma_entry; DRM_DEBUG("0x%08lx,0x%08lx\n", @@ -430,14 +429,13 @@ static void drm_vm_open(struct vm_area_struct *vma) struct drm_device *dev = priv->minor->dev; mutex_lock(&dev->struct_mutex); - drm_vm_open_locked(vma); + drm_vm_open_locked(dev, vma); mutex_unlock(&dev->struct_mutex); } -void drm_vm_close_locked(struct vm_area_struct *vma) +void drm_vm_close_locked(struct drm_device *dev, + struct vm_area_struct *vma) { - struct drm_file *priv = vma->vm_file->private_data; - struct drm_device *dev = priv->minor->dev; struct drm_vma_entry *pt, *temp; DRM_DEBUG("0x%08lx,0x%08lx\n", @@ -467,7 +465,7 @@ static void drm_vm_close(struct vm_area_struct *vma) struct drm_device *dev = priv->minor->dev; mutex_lock(&dev->struct_mutex); - drm_vm_close_locked(vma); + drm_vm_close_locked(dev, vma); mutex_unlock(&dev->struct_mutex); } @@ -519,7 +517,7 @@ static int drm_mmap_dma(struct file *filp, struct vm_area_struct *vma) vma->vm_flags |= VM_RESERVED; /* Don't swap */ vma->vm_flags |= VM_DONTEXPAND; - drm_vm_open_locked(vma); + drm_vm_open_locked(dev, vma); return 0; } @@ -670,7 +668,7 @@ int drm_mmap_locked(struct file *filp, struct vm_area_struct *vma) vma->vm_flags |= VM_RESERVED; /* Don't swap */ vma->vm_flags |= VM_DONTEXPAND; - drm_vm_open_locked(vma); + drm_vm_open_locked(dev, vma); return 0; } diff --git a/drivers/gpu/drm/exynos/exynos_drm_drv.c b/drivers/gpu/drm/exynos/exynos_drm_drv.c index 9d3204c..4209531 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_drv.c +++ b/drivers/gpu/drm/exynos/exynos_drm_drv.c @@ -205,7 +205,7 @@ static void exynos_drm_lastclose(struct drm_device *dev) exynos_drm_fbdev_restore_mode(dev); } -static struct vm_operations_struct exynos_drm_gem_vm_ops = { +static const struct vm_operations_struct exynos_drm_gem_vm_ops = { .fault = exynos_drm_gem_fault, .open = drm_gem_vm_open, .close = drm_gem_vm_close, diff --git a/drivers/gpu/drm/exynos/exynos_drm_fb.c b/drivers/gpu/drm/exynos/exynos_drm_fb.c index c38c8f4..f82a299 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_fb.c +++ b/drivers/gpu/drm/exynos/exynos_drm_fb.c @@ -191,7 +191,7 @@ static void exynos_drm_output_poll_changed(struct drm_device *dev) drm_fb_helper_hotplug_event(fb_helper); } -static struct drm_mode_config_funcs exynos_drm_mode_config_funcs = { +static const struct drm_mode_config_funcs exynos_drm_mode_config_funcs = { .fb_create = exynos_user_fb_create, .output_poll_changed = exynos_drm_output_poll_changed, }; diff --git a/drivers/gpu/drm/gma500/Makefile b/drivers/gpu/drm/gma500/Makefile index dd7d6b5..abfa2a93 100644 --- a/drivers/gpu/drm/gma500/Makefile +++ b/drivers/gpu/drm/gma500/Makefile @@ -13,7 +13,6 @@ gma500_gfx-y += gem_glue.o \ intel_i2c.o \ intel_gmbus.o \ mmu.o \ - opregion.o \ power.o \ psb_drv.o \ psb_intel_display.o \ @@ -25,6 +24,8 @@ gma500_gfx-y += gem_glue.o \ psb_device.o \ mid_bios.o +gma500_gfx-$(CONFIG_ACPI) += opregion.o \ + gma500_gfx-$(CONFIG_DRM_GMA3600) += cdv_device.o \ cdv_intel_crt.o \ cdv_intel_display.o \ diff --git a/drivers/gpu/drm/gma500/cdv_device.c b/drivers/gpu/drm/gma500/cdv_device.c index c10f020..9764045 100644 --- a/drivers/gpu/drm/gma500/cdv_device.c +++ b/drivers/gpu/drm/gma500/cdv_device.c @@ -255,7 +255,7 @@ static int cdv_save_display_registers(struct drm_device *dev) struct psb_save_area *regs = &dev_priv->regs; struct drm_connector *connector; - dev_info(dev->dev, "Saving GPU registers.\n"); + dev_dbg(dev->dev, "Saving GPU registers.\n"); pci_read_config_byte(dev->pdev, 0xF4, ®s->cdv.saveLBB); @@ -485,10 +485,68 @@ static void cdv_hotplug_enable(struct drm_device *dev, bool on) } } +/* Cedarview */ +static const struct psb_offset cdv_regmap[2] = { + { + .fp0 = FPA0, + .fp1 = FPA1, + .cntr = DSPACNTR, + .conf = PIPEACONF, + .src = PIPEASRC, + .dpll = DPLL_A, + .dpll_md = DPLL_A_MD, + .htotal = HTOTAL_A, + .hblank = HBLANK_A, + .hsync = HSYNC_A, + .vtotal = VTOTAL_A, + .vblank = VBLANK_A, + .vsync = VSYNC_A, + .stride = DSPASTRIDE, + .size = DSPASIZE, + .pos = DSPAPOS, + .base = DSPABASE, + .surf = DSPASURF, + .addr = DSPABASE, + .status = PIPEASTAT, + .linoff = DSPALINOFF, + .tileoff = DSPATILEOFF, + .palette = PALETTE_A, + }, + { + .fp0 = FPB0, + .fp1 = FPB1, + .cntr = DSPBCNTR, + .conf = PIPEBCONF, + .src = PIPEBSRC, + .dpll = DPLL_B, + .dpll_md = DPLL_B_MD, + .htotal = HTOTAL_B, + .hblank = HBLANK_B, + .hsync = HSYNC_B, + .vtotal = VTOTAL_B, + .vblank = VBLANK_B, + .vsync = VSYNC_B, + .stride = DSPBSTRIDE, + .size = DSPBSIZE, + .pos = DSPBPOS, + .base = DSPBBASE, + .surf = DSPBSURF, + .addr = DSPBBASE, + .status = PIPEBSTAT, + .linoff = DSPBLINOFF, + .tileoff = DSPBTILEOFF, + .palette = PALETTE_B, + } +}; + static int cdv_chip_setup(struct drm_device *dev) { struct drm_psb_private *dev_priv = dev->dev_private; INIT_WORK(&dev_priv->hotplug_work, cdv_hotplug_work_func); + + if (pci_enable_msi(dev->pdev)) + dev_warn(dev->dev, "Enabling MSI failed!\n"); + dev_priv->regmap = cdv_regmap; cdv_get_core_freq(dev); psb_intel_opregion_init(dev); psb_intel_init_bios(dev); @@ -505,6 +563,7 @@ const struct psb_ops cdv_chip_ops = { .crtcs = 2, .hdmi_mask = (1 << 0) | (1 << 1), .lvds_mask = (1 << 1), + .cursor_needs_phys = 0, .sgx_offset = MRST_SGX_OFFSET, .chip_setup = cdv_chip_setup, .errata = cdv_errata, diff --git a/drivers/gpu/drm/gma500/cdv_intel_display.c b/drivers/gpu/drm/gma500/cdv_intel_display.c index 2fab778..c3e9a0f 100644 --- a/drivers/gpu/drm/gma500/cdv_intel_display.c +++ b/drivers/gpu/drm/gma500/cdv_intel_display.c @@ -218,8 +218,7 @@ static int cdv_dpll_set_clock_cdv(struct drm_device *dev, struct drm_crtc *crtc, struct cdv_intel_clock_t *clock, bool is_lvds) { - struct psb_intel_crtc *psb_crtc = - to_psb_intel_crtc(crtc); + struct psb_intel_crtc *psb_crtc = to_psb_intel_crtc(crtc); int pipe = psb_crtc->pipe; u32 m, n_vco, p; int ret = 0; @@ -503,14 +502,12 @@ static int cdv_intel_pipe_set_base(struct drm_crtc *crtc, int x, int y, struct drm_framebuffer *old_fb) { struct drm_device *dev = crtc->dev; + struct drm_psb_private *dev_priv = dev->dev_private; struct psb_intel_crtc *psb_intel_crtc = to_psb_intel_crtc(crtc); struct psb_framebuffer *psbfb = to_psb_fb(crtc->fb); int pipe = psb_intel_crtc->pipe; + const struct psb_offset *map = &dev_priv->regmap[pipe]; unsigned long start, offset; - int dspbase = (pipe == 0 ? DSPABASE : DSPBBASE); - int dspsurf = (pipe == 0 ? DSPASURF : DSPBSURF); - int dspstride = (pipe == 0) ? DSPASTRIDE : DSPBSTRIDE; - int dspcntr_reg = (pipe == 0) ? DSPACNTR : DSPBCNTR; u32 dspcntr; int ret = 0; @@ -532,9 +529,9 @@ static int cdv_intel_pipe_set_base(struct drm_crtc *crtc, start = psbfb->gtt->offset; offset = y * crtc->fb->pitches[0] + x * (crtc->fb->bits_per_pixel / 8); - REG_WRITE(dspstride, crtc->fb->pitches[0]); + REG_WRITE(map->stride, crtc->fb->pitches[0]); - dspcntr = REG_READ(dspcntr_reg); + dspcntr = REG_READ(map->cntr); dspcntr &= ~DISPPLANE_PIXFORMAT_MASK; switch (crtc->fb->bits_per_pixel) { @@ -556,15 +553,15 @@ static int cdv_intel_pipe_set_base(struct drm_crtc *crtc, ret = -EINVAL; goto psb_intel_pipe_set_base_exit; } - REG_WRITE(dspcntr_reg, dspcntr); + REG_WRITE(map->cntr, dspcntr); dev_dbg(dev->dev, "Writing base %08lX %08lX %d %d\n", start, offset, x, y); - REG_WRITE(dspbase, offset); - REG_READ(dspbase); - REG_WRITE(dspsurf, start); - REG_READ(dspsurf); + REG_WRITE(map->base, offset); + REG_READ(map->base); + REG_WRITE(map->surf, start); + REG_READ(map->surf); psb_intel_pipe_cleaner: /* If there was a previous display we can now unpin it */ @@ -721,8 +718,7 @@ static void cdv_intel_update_watermark (struct drm_device *dev, struct drm_crtc static void cdv_intel_crtc_load_lut(struct drm_crtc *crtc) { struct drm_device *dev = crtc->dev; - struct drm_psb_private *dev_priv = - (struct drm_psb_private *)dev->dev_private; + struct drm_psb_private *dev_priv = dev->dev_private; struct psb_intel_crtc *psb_intel_crtc = to_psb_intel_crtc(crtc); int palreg = PALETTE_A; int i; @@ -758,7 +754,7 @@ static void cdv_intel_crtc_load_lut(struct drm_crtc *crtc) gma_power_end(dev); } else { for (i = 0; i < 256; i++) { - dev_priv->regs.psb.save_palette_a[i] = + dev_priv->regs.pipe[0].palette[i] = ((psb_intel_crtc->lut_r[i] + psb_intel_crtc->lut_adj[i]) << 16) | ((psb_intel_crtc->lut_g[i] + @@ -779,13 +775,10 @@ static void cdv_intel_crtc_load_lut(struct drm_crtc *crtc) static void cdv_intel_crtc_dpms(struct drm_crtc *crtc, int mode) { struct drm_device *dev = crtc->dev; + struct drm_psb_private *dev_priv = dev->dev_private; struct psb_intel_crtc *psb_intel_crtc = to_psb_intel_crtc(crtc); int pipe = psb_intel_crtc->pipe; - int dpll_reg = (pipe == 0) ? DPLL_A : DPLL_B; - int dspcntr_reg = (pipe == 0) ? DSPACNTR : DSPBCNTR; - int dspbase_reg = (pipe == 0) ? DSPABASE : DSPBBASE; - int pipeconf_reg = (pipe == 0) ? PIPEACONF : PIPEBCONF; - int pipestat_reg = (pipe == 0) ? PIPEASTAT : PIPEBSTAT; + const struct psb_offset *map = &dev_priv->regmap[pipe]; u32 temp; /* XXX: When our outputs are all unaware of DPMS modes other than off @@ -803,44 +796,44 @@ static void cdv_intel_crtc_dpms(struct drm_crtc *crtc, int mode) psb_intel_crtc->active = true; /* Enable the DPLL */ - temp = REG_READ(dpll_reg); + temp = REG_READ(map->dpll); if ((temp & DPLL_VCO_ENABLE) == 0) { - REG_WRITE(dpll_reg, temp); - REG_READ(dpll_reg); + REG_WRITE(map->dpll, temp); + REG_READ(map->dpll); /* Wait for the clocks to stabilize. */ udelay(150); - REG_WRITE(dpll_reg, temp | DPLL_VCO_ENABLE); - REG_READ(dpll_reg); + REG_WRITE(map->dpll, temp | DPLL_VCO_ENABLE); + REG_READ(map->dpll); /* Wait for the clocks to stabilize. */ udelay(150); - REG_WRITE(dpll_reg, temp | DPLL_VCO_ENABLE); - REG_READ(dpll_reg); + REG_WRITE(map->dpll, temp | DPLL_VCO_ENABLE); + REG_READ(map->dpll); /* Wait for the clocks to stabilize. */ udelay(150); } /* Jim Bish - switch plan and pipe per scott */ /* Enable the plane */ - temp = REG_READ(dspcntr_reg); + temp = REG_READ(map->cntr); if ((temp & DISPLAY_PLANE_ENABLE) == 0) { - REG_WRITE(dspcntr_reg, + REG_WRITE(map->cntr, temp | DISPLAY_PLANE_ENABLE); /* Flush the plane changes */ - REG_WRITE(dspbase_reg, REG_READ(dspbase_reg)); + REG_WRITE(map->base, REG_READ(map->base)); } udelay(150); /* Enable the pipe */ - temp = REG_READ(pipeconf_reg); + temp = REG_READ(map->conf); if ((temp & PIPEACONF_ENABLE) == 0) - REG_WRITE(pipeconf_reg, temp | PIPEACONF_ENABLE); + REG_WRITE(map->conf, temp | PIPEACONF_ENABLE); - temp = REG_READ(pipestat_reg); + temp = REG_READ(map->status); temp &= ~(0xFFFF); temp |= PIPE_FIFO_UNDERRUN; - REG_WRITE(pipestat_reg, temp); - REG_READ(pipestat_reg); + REG_WRITE(map->status, temp); + REG_READ(map->status); cdv_intel_update_watermark(dev, crtc); cdv_intel_crtc_load_lut(crtc); @@ -870,10 +863,10 @@ static void cdv_intel_crtc_dpms(struct drm_crtc *crtc, int mode) cdv_intel_wait_for_vblank(dev); /* Next, disable display pipes */ - temp = REG_READ(pipeconf_reg); + temp = REG_READ(map->conf); if ((temp & PIPEACONF_ENABLE) != 0) { - REG_WRITE(pipeconf_reg, temp & ~PIPEACONF_ENABLE); - REG_READ(pipeconf_reg); + REG_WRITE(map->conf, temp & ~PIPEACONF_ENABLE); + REG_READ(map->conf); } /* Wait for vblank for the disable to take effect. */ @@ -882,19 +875,19 @@ static void cdv_intel_crtc_dpms(struct drm_crtc *crtc, int mode) udelay(150); /* Disable display plane */ - temp = REG_READ(dspcntr_reg); + temp = REG_READ(map->cntr); if ((temp & DISPLAY_PLANE_ENABLE) != 0) { - REG_WRITE(dspcntr_reg, + REG_WRITE(map->cntr, temp & ~DISPLAY_PLANE_ENABLE); /* Flush the plane changes */ - REG_WRITE(dspbase_reg, REG_READ(dspbase_reg)); - REG_READ(dspbase_reg); + REG_WRITE(map->base, REG_READ(map->base)); + REG_READ(map->base); } - temp = REG_READ(dpll_reg); + temp = REG_READ(map->dpll); if ((temp & DPLL_VCO_ENABLE) != 0) { - REG_WRITE(dpll_reg, temp & ~DPLL_VCO_ENABLE); - REG_READ(dpll_reg); + REG_WRITE(map->dpll, temp & ~DPLL_VCO_ENABLE); + REG_READ(map->dpll); } /* Wait for the clocks to turn off. */ @@ -953,19 +946,7 @@ static int cdv_intel_crtc_mode_set(struct drm_crtc *crtc, struct drm_psb_private *dev_priv = dev->dev_private; struct psb_intel_crtc *psb_intel_crtc = to_psb_intel_crtc(crtc); int pipe = psb_intel_crtc->pipe; - int dpll_reg = (pipe == 0) ? DPLL_A : DPLL_B; - int dpll_md_reg = (psb_intel_crtc->pipe == 0) ? DPLL_A_MD : DPLL_B_MD; - int dspcntr_reg = (pipe == 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 = (pipe == 0) ? DSPASIZE : DSPBSIZE; - int dsppos_reg = (pipe == 0) ? DSPAPOS : DSPBPOS; - int pipesrc_reg = (pipe == 0) ? PIPEASRC : PIPEBSRC; + const struct psb_offset *map = &dev_priv->regmap[pipe]; int refclk; struct cdv_intel_clock_t clock; u32 dpll = 0, dspcntr, pipeconf; @@ -1036,7 +1017,7 @@ static int cdv_intel_crtc_mode_set(struct drm_crtc *crtc, /* dpll |= (2 << 11); */ /* setup pipeconf */ - pipeconf = REG_READ(pipeconf_reg); + pipeconf = REG_READ(map->conf); /* Set up the display plane register */ dspcntr = DISPPLANE_GAMMA_ENABLE; @@ -1049,8 +1030,8 @@ static int cdv_intel_crtc_mode_set(struct drm_crtc *crtc, dspcntr |= DISPLAY_PLANE_ENABLE; pipeconf |= PIPEACONF_ENABLE; - REG_WRITE(dpll_reg, dpll | DPLL_VGA_MODE_DIS | DPLL_SYNCLOCK_ENABLE); - REG_READ(dpll_reg); + REG_WRITE(map->dpll, dpll | DPLL_VGA_MODE_DIS | DPLL_SYNCLOCK_ENABLE); + REG_READ(map->dpll); cdv_dpll_set_clock_cdv(dev, crtc, &clock, is_lvds); @@ -1094,48 +1075,48 @@ static int cdv_intel_crtc_mode_set(struct drm_crtc *crtc, DRM_DEBUG_KMS("Mode for pipe %c:\n", pipe == 0 ? 'A' : 'B'); drm_mode_debug_printmodeline(mode); - REG_WRITE(dpll_reg, - (REG_READ(dpll_reg) & ~DPLL_LOCK) | DPLL_VCO_ENABLE); - REG_READ(dpll_reg); + REG_WRITE(map->dpll, + (REG_READ(map->dpll) & ~DPLL_LOCK) | DPLL_VCO_ENABLE); + REG_READ(map->dpll); /* Wait for the clocks to stabilize. */ udelay(150); /* 42 usec w/o calibration, 110 with. rounded up. */ - if (!(REG_READ(dpll_reg) & DPLL_LOCK)) { + if (!(REG_READ(map->dpll) & DPLL_LOCK)) { dev_err(dev->dev, "Failed to get DPLL lock\n"); return -EBUSY; } { int sdvo_pixel_multiply = adjusted_mode->clock / mode->clock; - REG_WRITE(dpll_md_reg, (0 << DPLL_MD_UDI_DIVIDER_SHIFT) | ((sdvo_pixel_multiply - 1) << DPLL_MD_UDI_MULTIPLIER_SHIFT)); + REG_WRITE(map->dpll_md, (0 << DPLL_MD_UDI_DIVIDER_SHIFT) | ((sdvo_pixel_multiply - 1) << DPLL_MD_UDI_MULTIPLIER_SHIFT)); } - REG_WRITE(htot_reg, (adjusted_mode->crtc_hdisplay - 1) | + REG_WRITE(map->htotal, (adjusted_mode->crtc_hdisplay - 1) | ((adjusted_mode->crtc_htotal - 1) << 16)); - REG_WRITE(hblank_reg, (adjusted_mode->crtc_hblank_start - 1) | + REG_WRITE(map->hblank, (adjusted_mode->crtc_hblank_start - 1) | ((adjusted_mode->crtc_hblank_end - 1) << 16)); - REG_WRITE(hsync_reg, (adjusted_mode->crtc_hsync_start - 1) | + REG_WRITE(map->hsync, (adjusted_mode->crtc_hsync_start - 1) | ((adjusted_mode->crtc_hsync_end - 1) << 16)); - REG_WRITE(vtot_reg, (adjusted_mode->crtc_vdisplay - 1) | + REG_WRITE(map->vtotal, (adjusted_mode->crtc_vdisplay - 1) | ((adjusted_mode->crtc_vtotal - 1) << 16)); - REG_WRITE(vblank_reg, (adjusted_mode->crtc_vblank_start - 1) | + REG_WRITE(map->vblank, (adjusted_mode->crtc_vblank_start - 1) | ((adjusted_mode->crtc_vblank_end - 1) << 16)); - REG_WRITE(vsync_reg, (adjusted_mode->crtc_vsync_start - 1) | + REG_WRITE(map->vsync, (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. */ - REG_WRITE(dspsize_reg, + REG_WRITE(map->size, ((mode->vdisplay - 1) << 16) | (mode->hdisplay - 1)); - REG_WRITE(dsppos_reg, 0); - REG_WRITE(pipesrc_reg, + REG_WRITE(map->pos, 0); + REG_WRITE(map->src, ((mode->hdisplay - 1) << 16) | (mode->vdisplay - 1)); - REG_WRITE(pipeconf_reg, pipeconf); - REG_READ(pipeconf_reg); + REG_WRITE(map->conf, pipeconf); + REG_READ(map->conf); cdv_intel_wait_for_vblank(dev); - REG_WRITE(dspcntr_reg, dspcntr); + REG_WRITE(map->cntr, dspcntr); /* Flush the plane changes */ { @@ -1156,11 +1137,10 @@ static int cdv_intel_crtc_mode_set(struct drm_crtc *crtc, static void cdv_intel_crtc_save(struct drm_crtc *crtc) { struct drm_device *dev = crtc->dev; - /* struct drm_psb_private *dev_priv = - (struct drm_psb_private *)dev->dev_private; */ + struct drm_psb_private *dev_priv = dev->dev_private; struct psb_intel_crtc *psb_intel_crtc = to_psb_intel_crtc(crtc); struct psb_intel_crtc_state *crtc_state = psb_intel_crtc->crtc_state; - int pipeA = (psb_intel_crtc->pipe == 0); + const struct psb_offset *map = &dev_priv->regmap[psb_intel_crtc->pipe]; uint32_t paletteReg; int i; @@ -1169,25 +1149,25 @@ static void cdv_intel_crtc_save(struct drm_crtc *crtc) return; } - crtc_state->saveDSPCNTR = REG_READ(pipeA ? DSPACNTR : DSPBCNTR); - crtc_state->savePIPECONF = REG_READ(pipeA ? PIPEACONF : PIPEBCONF); - crtc_state->savePIPESRC = REG_READ(pipeA ? PIPEASRC : PIPEBSRC); - crtc_state->saveFP0 = REG_READ(pipeA ? FPA0 : FPB0); - crtc_state->saveFP1 = REG_READ(pipeA ? FPA1 : FPB1); - crtc_state->saveDPLL = REG_READ(pipeA ? DPLL_A : DPLL_B); - crtc_state->saveHTOTAL = REG_READ(pipeA ? HTOTAL_A : HTOTAL_B); - crtc_state->saveHBLANK = REG_READ(pipeA ? HBLANK_A : HBLANK_B); - crtc_state->saveHSYNC = REG_READ(pipeA ? HSYNC_A : HSYNC_B); - crtc_state->saveVTOTAL = REG_READ(pipeA ? VTOTAL_A : VTOTAL_B); - crtc_state->saveVBLANK = REG_READ(pipeA ? VBLANK_A : VBLANK_B); - crtc_state->saveVSYNC = REG_READ(pipeA ? VSYNC_A : VSYNC_B); - crtc_state->saveDSPSTRIDE = REG_READ(pipeA ? DSPASTRIDE : DSPBSTRIDE); + crtc_state->saveDSPCNTR = REG_READ(map->cntr); + crtc_state->savePIPECONF = REG_READ(map->conf); + crtc_state->savePIPESRC = REG_READ(map->src); + crtc_state->saveFP0 = REG_READ(map->fp0); + crtc_state->saveFP1 = REG_READ(map->fp1); + crtc_state->saveDPLL = REG_READ(map->dpll); + crtc_state->saveHTOTAL = REG_READ(map->htotal); + crtc_state->saveHBLANK = REG_READ(map->hblank); + crtc_state->saveHSYNC = REG_READ(map->hsync); + crtc_state->saveVTOTAL = REG_READ(map->vtotal); + crtc_state->saveVBLANK = REG_READ(map->vblank); + crtc_state->saveVSYNC = REG_READ(map->vsync); + crtc_state->saveDSPSTRIDE = REG_READ(map->stride); /*NOTE: DSPSIZE DSPPOS only for psb*/ - crtc_state->saveDSPSIZE = REG_READ(pipeA ? DSPASIZE : DSPBSIZE); - crtc_state->saveDSPPOS = REG_READ(pipeA ? DSPAPOS : DSPBPOS); + crtc_state->saveDSPSIZE = REG_READ(map->size); + crtc_state->saveDSPPOS = REG_READ(map->pos); - crtc_state->saveDSPBASE = REG_READ(pipeA ? DSPABASE : DSPBBASE); + crtc_state->saveDSPBASE = REG_READ(map->base); DRM_DEBUG("(%x %x %x %x %x %x %x %x %x %x %x %x %x %x %x %x)\n", crtc_state->saveDSPCNTR, @@ -1208,7 +1188,7 @@ static void cdv_intel_crtc_save(struct drm_crtc *crtc) crtc_state->saveDSPBASE ); - paletteReg = pipeA ? PALETTE_A : PALETTE_B; + paletteReg = map->palette; for (i = 0; i < 256; ++i) crtc_state->savePalette[i] = REG_READ(paletteReg + (i << 2)); } @@ -1219,12 +1199,10 @@ static void cdv_intel_crtc_save(struct drm_crtc *crtc) static void cdv_intel_crtc_restore(struct drm_crtc *crtc) { struct drm_device *dev = crtc->dev; - /* struct drm_psb_private * dev_priv = - (struct drm_psb_private *)dev->dev_private; */ + struct drm_psb_private *dev_priv = dev->dev_private; struct psb_intel_crtc *psb_intel_crtc = to_psb_intel_crtc(crtc); struct psb_intel_crtc_state *crtc_state = psb_intel_crtc->crtc_state; - /* struct drm_crtc_helper_funcs * crtc_funcs = crtc->helper_private; */ - int pipeA = (psb_intel_crtc->pipe == 0); + const struct psb_offset *map = &dev_priv->regmap[psb_intel_crtc->pipe]; uint32_t paletteReg; int i; @@ -1235,23 +1213,23 @@ static void cdv_intel_crtc_restore(struct drm_crtc *crtc) DRM_DEBUG( "current:(%x %x %x %x %x %x %x %x %x %x %x %x %x %x %x %x)\n", - REG_READ(pipeA ? DSPACNTR : DSPBCNTR), - REG_READ(pipeA ? PIPEACONF : PIPEBCONF), - REG_READ(pipeA ? PIPEASRC : PIPEBSRC), - REG_READ(pipeA ? FPA0 : FPB0), - REG_READ(pipeA ? FPA1 : FPB1), - REG_READ(pipeA ? DPLL_A : DPLL_B), - REG_READ(pipeA ? HTOTAL_A : HTOTAL_B), - REG_READ(pipeA ? HBLANK_A : HBLANK_B), - REG_READ(pipeA ? HSYNC_A : HSYNC_B), - REG_READ(pipeA ? VTOTAL_A : VTOTAL_B), - REG_READ(pipeA ? VBLANK_A : VBLANK_B), - REG_READ(pipeA ? VSYNC_A : VSYNC_B), - REG_READ(pipeA ? DSPASTRIDE : DSPBSTRIDE), - REG_READ(pipeA ? DSPASIZE : DSPBSIZE), - REG_READ(pipeA ? DSPAPOS : DSPBPOS), - REG_READ(pipeA ? DSPABASE : DSPBBASE) - ); + REG_READ(map->cntr), + REG_READ(map->conf), + REG_READ(map->src), + REG_READ(map->fp0), + REG_READ(map->fp1), + REG_READ(map->dpll), + REG_READ(map->htotal), + REG_READ(map->hblank), + REG_READ(map->hsync), + REG_READ(map->vtotal), + REG_READ(map->vblank), + REG_READ(map->vsync), + REG_READ(map->stride), + REG_READ(map->size), + REG_READ(map->pos), + REG_READ(map->base) + ); DRM_DEBUG( "saved: (%x %x %x %x %x %x %x %x %x %x %x %x %x %x %x %x)\n", @@ -1271,51 +1249,51 @@ static void cdv_intel_crtc_restore(struct drm_crtc *crtc) crtc_state->saveDSPSIZE, crtc_state->saveDSPPOS, crtc_state->saveDSPBASE - ); + ); if (crtc_state->saveDPLL & DPLL_VCO_ENABLE) { - REG_WRITE(pipeA ? DPLL_A : DPLL_B, - crtc_state->saveDPLL & ~DPLL_VCO_ENABLE); - REG_READ(pipeA ? DPLL_A : DPLL_B); + REG_WRITE(map->dpll, + crtc_state->saveDPLL & ~DPLL_VCO_ENABLE); + REG_READ(map->dpll); DRM_DEBUG("write dpll: %x\n", - REG_READ(pipeA ? DPLL_A : DPLL_B)); + REG_READ(map->dpll)); udelay(150); } - REG_WRITE(pipeA ? FPA0 : FPB0, crtc_state->saveFP0); - REG_READ(pipeA ? FPA0 : FPB0); + REG_WRITE(map->fp0, crtc_state->saveFP0); + REG_READ(map->fp0); - REG_WRITE(pipeA ? FPA1 : FPB1, crtc_state->saveFP1); - REG_READ(pipeA ? FPA1 : FPB1); + REG_WRITE(map->fp1, crtc_state->saveFP1); + REG_READ(map->fp1); - REG_WRITE(pipeA ? DPLL_A : DPLL_B, crtc_state->saveDPLL); - REG_READ(pipeA ? DPLL_A : DPLL_B); + REG_WRITE(map->dpll, crtc_state->saveDPLL); + REG_READ(map->dpll); udelay(150); - REG_WRITE(pipeA ? HTOTAL_A : HTOTAL_B, crtc_state->saveHTOTAL); - REG_WRITE(pipeA ? HBLANK_A : HBLANK_B, crtc_state->saveHBLANK); - REG_WRITE(pipeA ? HSYNC_A : HSYNC_B, crtc_state->saveHSYNC); - REG_WRITE(pipeA ? VTOTAL_A : VTOTAL_B, crtc_state->saveVTOTAL); - REG_WRITE(pipeA ? VBLANK_A : VBLANK_B, crtc_state->saveVBLANK); - REG_WRITE(pipeA ? VSYNC_A : VSYNC_B, crtc_state->saveVSYNC); - REG_WRITE(pipeA ? DSPASTRIDE : DSPBSTRIDE, crtc_state->saveDSPSTRIDE); + REG_WRITE(map->htotal, crtc_state->saveHTOTAL); + REG_WRITE(map->hblank, crtc_state->saveHBLANK); + REG_WRITE(map->hsync, crtc_state->saveHSYNC); + REG_WRITE(map->vtotal, crtc_state->saveVTOTAL); + REG_WRITE(map->vblank, crtc_state->saveVBLANK); + REG_WRITE(map->vsync, crtc_state->saveVSYNC); + REG_WRITE(map->stride, crtc_state->saveDSPSTRIDE); - REG_WRITE(pipeA ? DSPASIZE : DSPBSIZE, crtc_state->saveDSPSIZE); - REG_WRITE(pipeA ? DSPAPOS : DSPBPOS, crtc_state->saveDSPPOS); + REG_WRITE(map->size, crtc_state->saveDSPSIZE); + REG_WRITE(map->pos, crtc_state->saveDSPPOS); - REG_WRITE(pipeA ? PIPEASRC : PIPEBSRC, crtc_state->savePIPESRC); - REG_WRITE(pipeA ? DSPABASE : DSPBBASE, crtc_state->saveDSPBASE); - REG_WRITE(pipeA ? PIPEACONF : PIPEBCONF, crtc_state->savePIPECONF); + REG_WRITE(map->src, crtc_state->savePIPESRC); + REG_WRITE(map->base, crtc_state->saveDSPBASE); + REG_WRITE(map->conf, crtc_state->savePIPECONF); cdv_intel_wait_for_vblank(dev); - REG_WRITE(pipeA ? DSPACNTR : DSPBCNTR, crtc_state->saveDSPCNTR); - REG_WRITE(pipeA ? DSPABASE : DSPBBASE, crtc_state->saveDSPBASE); + REG_WRITE(map->cntr, crtc_state->saveDSPCNTR); + REG_WRITE(map->base, crtc_state->saveDSPBASE); cdv_intel_wait_for_vblank(dev); - paletteReg = pipeA ? PALETTE_A : PALETTE_B; + paletteReg = map->palette; for (i = 0; i < 256; ++i) REG_WRITE(paletteReg + (i << 2), crtc_state->savePalette[i]); } @@ -1490,35 +1468,30 @@ static void i8xx_clock(int refclk, struct cdv_intel_clock_t *clock) static int cdv_intel_crtc_clock_get(struct drm_device *dev, struct drm_crtc *crtc) { + struct drm_psb_private *dev_priv = dev->dev_private; struct psb_intel_crtc *psb_intel_crtc = to_psb_intel_crtc(crtc); int pipe = psb_intel_crtc->pipe; + const struct psb_offset *map = &dev_priv->regmap[pipe]; u32 dpll; u32 fp; struct cdv_intel_clock_t clock; bool is_lvds; - struct drm_psb_private *dev_priv = dev->dev_private; + struct psb_pipe *p = &dev_priv->regs.pipe[pipe]; if (gma_power_begin(dev, false)) { - dpll = REG_READ((pipe == 0) ? DPLL_A : DPLL_B); + dpll = REG_READ(map->dpll); if ((dpll & DISPLAY_RATE_SELECT_FPA1) == 0) - fp = REG_READ((pipe == 0) ? FPA0 : FPB0); + fp = REG_READ(map->fp0); else - fp = REG_READ((pipe == 0) ? FPA1 : FPB1); + fp = REG_READ(map->fp1); is_lvds = (pipe == 1) && (REG_READ(LVDS) & LVDS_PORT_EN); gma_power_end(dev); } else { - dpll = (pipe == 0) ? - dev_priv->regs.psb.saveDPLL_A : - dev_priv->regs.psb.saveDPLL_B; - + dpll = p->dpll; if ((dpll & DISPLAY_RATE_SELECT_FPA1) == 0) - fp = (pipe == 0) ? - dev_priv->regs.psb.saveFPA0 : - dev_priv->regs.psb.saveFPB0; + fp = p->fp0; else - fp = (pipe == 0) ? - dev_priv->regs.psb.saveFPA1 : - dev_priv->regs.psb.saveFPB1; + fp = p->fp1; is_lvds = (pipe == 1) && (dev_priv->regs.psb.saveLVDS & LVDS_PORT_EN); @@ -1576,32 +1549,26 @@ struct drm_display_mode *cdv_intel_crtc_mode_get(struct drm_device *dev, { struct psb_intel_crtc *psb_intel_crtc = to_psb_intel_crtc(crtc); int pipe = psb_intel_crtc->pipe; + struct drm_psb_private *dev_priv = dev->dev_private; + struct psb_pipe *p = &dev_priv->regs.pipe[pipe]; + const struct psb_offset *map = &dev_priv->regmap[pipe]; struct drm_display_mode *mode; int htot; int hsync; int vtot; int vsync; - struct drm_psb_private *dev_priv = dev->dev_private; if (gma_power_begin(dev, false)) { - htot = REG_READ((pipe == 0) ? HTOTAL_A : HTOTAL_B); - hsync = REG_READ((pipe == 0) ? HSYNC_A : HSYNC_B); - vtot = REG_READ((pipe == 0) ? VTOTAL_A : VTOTAL_B); - vsync = REG_READ((pipe == 0) ? VSYNC_A : VSYNC_B); + htot = REG_READ(map->htotal); + hsync = REG_READ(map->hsync); + vtot = REG_READ(map->vtotal); + vsync = REG_READ(map->vsync); gma_power_end(dev); } else { - htot = (pipe == 0) ? - dev_priv->regs.psb.saveHTOTAL_A : - dev_priv->regs.psb.saveHTOTAL_B; - hsync = (pipe == 0) ? - dev_priv->regs.psb.saveHSYNC_A : - dev_priv->regs.psb.saveHSYNC_B; - vtot = (pipe == 0) ? - dev_priv->regs.psb.saveVTOTAL_A : - dev_priv->regs.psb.saveVTOTAL_B; - vsync = (pipe == 0) ? - dev_priv->regs.psb.saveVSYNC_A : - dev_priv->regs.psb.saveVSYNC_B; + htot = p->htotal; + hsync = p->hsync; + vtot = p->vtotal; + vsync = p->vsync; } mode = kzalloc(sizeof(*mode), GFP_KERNEL); diff --git a/drivers/gpu/drm/gma500/framebuffer.c b/drivers/gpu/drm/gma500/framebuffer.c index f47f883..5732b57 100644 --- a/drivers/gpu/drm/gma500/framebuffer.c +++ b/drivers/gpu/drm/gma500/framebuffer.c @@ -153,7 +153,7 @@ static void psbfb_vm_close(struct vm_area_struct *vma) { } -static struct vm_operations_struct psbfb_vm_ops = { +static const struct vm_operations_struct psbfb_vm_ops = { .fault = psbfb_vm_fault, .open = psbfb_vm_open, .close = psbfb_vm_close @@ -476,7 +476,7 @@ static int psbfb_create(struct psb_fbdev *fbdev, /* Use default scratch pixmap (info->pixmap.flags = FB_PIXMAP_SYSTEM) */ - dev_info(dev->dev, "allocated %dx%d fb\n", + dev_dbg(dev->dev, "allocated %dx%d fb\n", psbfb->base.width, psbfb->base.height); mutex_unlock(&dev->struct_mutex); @@ -782,7 +782,7 @@ void psb_modeset_init(struct drm_device *dev) dev->mode_config.min_width = 0; dev->mode_config.min_height = 0; - dev->mode_config.funcs = (void *) &psb_mode_funcs; + dev->mode_config.funcs = &psb_mode_funcs; /* set memory base */ /* Oaktrail and Poulsbo should use BAR 2*/ @@ -800,15 +800,20 @@ void psb_modeset_init(struct drm_device *dev) if (dev_priv->ops->errata) dev_priv->ops->errata(dev); + + dev_priv->modeset = true; } void psb_modeset_cleanup(struct drm_device *dev) { - mutex_lock(&dev->struct_mutex); + struct drm_psb_private *dev_priv = dev->dev_private; + if (dev_priv->modeset) { + mutex_lock(&dev->struct_mutex); - drm_kms_helper_poll_fini(dev); - psb_fbdev_fini(dev); - drm_mode_config_cleanup(dev); + drm_kms_helper_poll_fini(dev); + psb_fbdev_fini(dev); + drm_mode_config_cleanup(dev); - mutex_unlock(&dev->struct_mutex); + mutex_unlock(&dev->struct_mutex); + } } diff --git a/drivers/gpu/drm/gma500/gtt.c b/drivers/gpu/drm/gma500/gtt.c index 4cd33df..04a371a 100644 --- a/drivers/gpu/drm/gma500/gtt.c +++ b/drivers/gpu/drm/gma500/gtt.c @@ -416,7 +416,6 @@ int psb_gtt_init(struct drm_device *dev, int resume) unsigned long stolen_size, vram_stolen_size; unsigned i, num_pages; unsigned pfn_base; - uint32_t dvmt_mode = 0; struct psb_gtt *pg; int ret = 0; @@ -489,13 +488,8 @@ int psb_gtt_init(struct drm_device *dev, int resume) stolen_size = vram_stolen_size; - printk(KERN_INFO "Stolen memory information\n"); - printk(KERN_INFO " base in RAM: 0x%x\n", dev_priv->stolen_base); - printk(KERN_INFO " size: %luK, calculated by (GTT RAM base) - (Stolen base), seems wrong\n", - vram_stolen_size/1024); - dvmt_mode = (dev_priv->gmch_ctrl >> 4) & 0x7; - printk(KERN_INFO " the correct size should be: %dM(dvmt mode=%d)\n", - (dvmt_mode == 1) ? 1 : (2 << (dvmt_mode - 1)), dvmt_mode); + dev_dbg(dev->dev, "Stolen memory base 0x%x, size %luK\n", + dev_priv->stolen_base, vram_stolen_size / 1024); if (resume && (gtt_pages != pg->gtt_pages) && (stolen_size != pg->stolen_size)) { @@ -532,7 +526,7 @@ int psb_gtt_init(struct drm_device *dev, int resume) pfn_base = dev_priv->stolen_base >> PAGE_SHIFT; num_pages = vram_stolen_size >> PAGE_SHIFT; - printk(KERN_INFO"Set up %d stolen pages starting at 0x%08x, GTT offset %dK\n", + dev_dbg(dev->dev, "Set up %d stolen pages starting at 0x%08x, GTT offset %dK\n", num_pages, pfn_base << PAGE_SHIFT, 0); for (i = 0; i < num_pages; ++i) { pte = psb_gtt_mask_pte(pfn_base + i, 0); diff --git a/drivers/gpu/drm/gma500/intel_bios.c b/drivers/gpu/drm/gma500/intel_bios.c index 479e449..973d7f6 100644 --- a/drivers/gpu/drm/gma500/intel_bios.c +++ b/drivers/gpu/drm/gma500/intel_bios.c @@ -490,26 +490,8 @@ bool psb_intel_init_bios(struct drm_device *dev) void psb_intel_destroy_bios(struct drm_device *dev) { struct drm_psb_private *dev_priv = dev->dev_private; - struct drm_display_mode *sdvo_lvds_vbt_mode = - dev_priv->sdvo_lvds_vbt_mode; - struct drm_display_mode *lfp_lvds_vbt_mode = - dev_priv->lfp_lvds_vbt_mode; - struct bdb_lvds_backlight *lvds_bl = - dev_priv->lvds_bl; - - /*free sdvo panel mode*/ - if (sdvo_lvds_vbt_mode) { - dev_priv->sdvo_lvds_vbt_mode = NULL; - kfree(sdvo_lvds_vbt_mode); - } - - if (lfp_lvds_vbt_mode) { - dev_priv->lfp_lvds_vbt_mode = NULL; - kfree(lfp_lvds_vbt_mode); - } - if (lvds_bl) { - dev_priv->lvds_bl = NULL; - kfree(lvds_bl); - } + kfree(dev_priv->sdvo_lvds_vbt_mode); + kfree(dev_priv->lfp_lvds_vbt_mode); + kfree(dev_priv->lvds_bl); } diff --git a/drivers/gpu/drm/gma500/mdfld_device.c b/drivers/gpu/drm/gma500/mdfld_device.c index 717f4db..265ad0d 100644 --- a/drivers/gpu/drm/gma500/mdfld_device.c +++ b/drivers/gpu/drm/gma500/mdfld_device.c @@ -163,142 +163,30 @@ struct backlight_device *mdfld_get_backlight_device(void) * * Notes: FIXME_JLIU7 need to add the support for DPI MIPI & HDMI audio */ -static int mdfld_save_display_registers(struct drm_device *dev, int pipe) +static int mdfld_save_display_registers(struct drm_device *dev, int pipenum) { struct drm_psb_private *dev_priv = dev->dev_private; struct medfield_state *regs = &dev_priv->regs.mdfld; + struct psb_pipe *pipe = &dev_priv->regs.pipe[pipenum]; + const struct psb_offset *map = &dev_priv->regmap[pipenum]; int i; + u32 *mipi_val; /* register */ - u32 dpll_reg = MRST_DPLL_A; - u32 fp_reg = MRST_FPA0; - u32 pipeconf_reg = PIPEACONF; - u32 htot_reg = HTOTAL_A; - u32 hblank_reg = HBLANK_A; - u32 hsync_reg = HSYNC_A; - u32 vtot_reg = VTOTAL_A; - u32 vblank_reg = VBLANK_A; - u32 vsync_reg = VSYNC_A; - u32 pipesrc_reg = PIPEASRC; - u32 dspstride_reg = DSPASTRIDE; - u32 dsplinoff_reg = DSPALINOFF; - u32 dsptileoff_reg = DSPATILEOFF; - u32 dspsize_reg = DSPASIZE; - u32 dsppos_reg = DSPAPOS; - u32 dspsurf_reg = DSPASURF; u32 mipi_reg = MIPI; - u32 dspcntr_reg = DSPACNTR; - u32 dspstatus_reg = PIPEASTAT; - u32 palette_reg = PALETTE_A; - - /* pointer to values */ - u32 *dpll_val = ®s->saveDPLL_A; - u32 *fp_val = ®s->saveFPA0; - u32 *pipeconf_val = ®s->savePIPEACONF; - u32 *htot_val = ®s->saveHTOTAL_A; - u32 *hblank_val = ®s->saveHBLANK_A; - u32 *hsync_val = ®s->saveHSYNC_A; - u32 *vtot_val = ®s->saveVTOTAL_A; - u32 *vblank_val = ®s->saveVBLANK_A; - u32 *vsync_val = ®s->saveVSYNC_A; - u32 *pipesrc_val = ®s->savePIPEASRC; - u32 *dspstride_val = ®s->saveDSPASTRIDE; - u32 *dsplinoff_val = ®s->saveDSPALINOFF; - u32 *dsptileoff_val = ®s->saveDSPATILEOFF; - u32 *dspsize_val = ®s->saveDSPASIZE; - u32 *dsppos_val = ®s->saveDSPAPOS; - u32 *dspsurf_val = ®s->saveDSPASURF; - u32 *mipi_val = ®s->saveMIPI; - u32 *dspcntr_val = ®s->saveDSPACNTR; - u32 *dspstatus_val = ®s->saveDSPASTATUS; - u32 *palette_val = regs->save_palette_a; - - switch (pipe) { + + switch (pipenum) { case 0: + mipi_val = ®s->saveMIPI; break; case 1: - /* regester */ - dpll_reg = MDFLD_DPLL_B; - fp_reg = MDFLD_DPLL_DIV0; - pipeconf_reg = PIPEBCONF; - htot_reg = HTOTAL_B; - hblank_reg = HBLANK_B; - hsync_reg = HSYNC_B; - vtot_reg = VTOTAL_B; - vblank_reg = VBLANK_B; - vsync_reg = VSYNC_B; - pipesrc_reg = PIPEBSRC; - dspstride_reg = DSPBSTRIDE; - dsplinoff_reg = DSPBLINOFF; - dsptileoff_reg = DSPBTILEOFF; - dspsize_reg = DSPBSIZE; - dsppos_reg = DSPBPOS; - dspsurf_reg = DSPBSURF; - dspcntr_reg = DSPBCNTR; - dspstatus_reg = PIPEBSTAT; - palette_reg = PALETTE_B; - - /* values */ - dpll_val = ®s->saveDPLL_B; - fp_val = ®s->saveFPB0; - pipeconf_val = ®s->savePIPEBCONF; - htot_val = ®s->saveHTOTAL_B; - hblank_val = ®s->saveHBLANK_B; - hsync_val = ®s->saveHSYNC_B; - vtot_val = ®s->saveVTOTAL_B; - vblank_val = ®s->saveVBLANK_B; - vsync_val = ®s->saveVSYNC_B; - pipesrc_val = ®s->savePIPEBSRC; - dspstride_val = ®s->saveDSPBSTRIDE; - dsplinoff_val = ®s->saveDSPBLINOFF; - dsptileoff_val = ®s->saveDSPBTILEOFF; - dspsize_val = ®s->saveDSPBSIZE; - dsppos_val = ®s->saveDSPBPOS; - dspsurf_val = ®s->saveDSPBSURF; - dspcntr_val = ®s->saveDSPBCNTR; - dspstatus_val = ®s->saveDSPBSTATUS; - palette_val = regs->save_palette_b; + mipi_val = ®s->saveMIPI; break; case 2: /* register */ - pipeconf_reg = PIPECCONF; - htot_reg = HTOTAL_C; - hblank_reg = HBLANK_C; - hsync_reg = HSYNC_C; - vtot_reg = VTOTAL_C; - vblank_reg = VBLANK_C; - vsync_reg = VSYNC_C; - pipesrc_reg = PIPECSRC; - dspstride_reg = DSPCSTRIDE; - dsplinoff_reg = DSPCLINOFF; - dsptileoff_reg = DSPCTILEOFF; - dspsize_reg = DSPCSIZE; - dsppos_reg = DSPCPOS; - dspsurf_reg = DSPCSURF; mipi_reg = MIPI_C; - dspcntr_reg = DSPCCNTR; - dspstatus_reg = PIPECSTAT; - palette_reg = PALETTE_C; - /* pointer to values */ - pipeconf_val = ®s->savePIPECCONF; - htot_val = ®s->saveHTOTAL_C; - hblank_val = ®s->saveHBLANK_C; - hsync_val = ®s->saveHSYNC_C; - vtot_val = ®s->saveVTOTAL_C; - vblank_val = ®s->saveVBLANK_C; - vsync_val = ®s->saveVSYNC_C; - pipesrc_val = ®s->savePIPECSRC; - dspstride_val = ®s->saveDSPCSTRIDE; - dsplinoff_val = ®s->saveDSPCLINOFF; - dsptileoff_val = ®s->saveDSPCTILEOFF; - dspsize_val = ®s->saveDSPCSIZE; - dsppos_val = ®s->saveDSPCPOS; - dspsurf_val = ®s->saveDSPCSURF; mipi_val = ®s->saveMIPI_C; - dspcntr_val = ®s->saveDSPCCNTR; - dspstatus_val = ®s->saveDSPCSTATUS; - palette_val = regs->save_palette_c; break; default: DRM_ERROR("%s, invalid pipe number.\n", __func__); @@ -306,30 +194,30 @@ static int mdfld_save_display_registers(struct drm_device *dev, int pipe) } /* Pipe & plane A info */ - *dpll_val = PSB_RVDC32(dpll_reg); - *fp_val = PSB_RVDC32(fp_reg); - *pipeconf_val = PSB_RVDC32(pipeconf_reg); - *htot_val = PSB_RVDC32(htot_reg); - *hblank_val = PSB_RVDC32(hblank_reg); - *hsync_val = PSB_RVDC32(hsync_reg); - *vtot_val = PSB_RVDC32(vtot_reg); - *vblank_val = PSB_RVDC32(vblank_reg); - *vsync_val = PSB_RVDC32(vsync_reg); - *pipesrc_val = PSB_RVDC32(pipesrc_reg); - *dspstride_val = PSB_RVDC32(dspstride_reg); - *dsplinoff_val = PSB_RVDC32(dsplinoff_reg); - *dsptileoff_val = PSB_RVDC32(dsptileoff_reg); - *dspsize_val = PSB_RVDC32(dspsize_reg); - *dsppos_val = PSB_RVDC32(dsppos_reg); - *dspsurf_val = PSB_RVDC32(dspsurf_reg); - *dspcntr_val = PSB_RVDC32(dspcntr_reg); - *dspstatus_val = PSB_RVDC32(dspstatus_reg); + pipe->dpll = PSB_RVDC32(map->dpll); + pipe->fp0 = PSB_RVDC32(map->fp0); + pipe->conf = PSB_RVDC32(map->conf); + pipe->htotal = PSB_RVDC32(map->htotal); + pipe->hblank = PSB_RVDC32(map->hblank); + pipe->hsync = PSB_RVDC32(map->hsync); + pipe->vtotal = PSB_RVDC32(map->vtotal); + pipe->vblank = PSB_RVDC32(map->vblank); + pipe->vsync = PSB_RVDC32(map->vsync); + pipe->src = PSB_RVDC32(map->src); + pipe->stride = PSB_RVDC32(map->stride); + pipe->linoff = PSB_RVDC32(map->linoff); + pipe->tileoff = PSB_RVDC32(map->tileoff); + pipe->size = PSB_RVDC32(map->size); + pipe->pos = PSB_RVDC32(map->pos); + pipe->surf = PSB_RVDC32(map->surf); + pipe->cntr = PSB_RVDC32(map->cntr); + pipe->status = PSB_RVDC32(map->status); /*save palette (gamma) */ for (i = 0; i < 256; i++) - palette_val[i] = PSB_RVDC32(palette_reg + (i << 2)); + pipe->palette[i] = PSB_RVDC32(map->palette + (i << 2)); - if (pipe == 1) { + if (pipenum == 1) { regs->savePFIT_CONTROL = PSB_RVDC32(PFIT_CONTROL); regs->savePFIT_PGM_RATIOS = PSB_RVDC32(PFIT_PGM_RATIOS); @@ -349,7 +237,7 @@ static int mdfld_save_display_registers(struct drm_device *dev, int pipe) * * Notes: FIXME_JLIU7 need to add the support for DPI MIPI & HDMI audio */ -static int mdfld_restore_display_registers(struct drm_device *dev, int pipe) +static int mdfld_restore_display_registers(struct drm_device *dev, int pipenum) { /* To get panel out of ULPS mode. */ u32 temp = 0; @@ -357,142 +245,30 @@ static int mdfld_restore_display_registers(struct drm_device *dev, int pipe) struct drm_psb_private *dev_priv = dev->dev_private; struct mdfld_dsi_config *dsi_config = NULL; struct medfield_state *regs = &dev_priv->regs.mdfld; - u32 i = 0; - u32 dpll = 0; + struct psb_pipe *pipe = &dev_priv->regs.pipe[pipenum]; + const struct psb_offset *map = &dev_priv->regmap[pipenum]; + u32 i; + u32 dpll; u32 timeout = 0; - /* regester */ - u32 dpll_reg = MRST_DPLL_A; - u32 fp_reg = MRST_FPA0; - u32 pipeconf_reg = PIPEACONF; - u32 htot_reg = HTOTAL_A; - u32 hblank_reg = HBLANK_A; - u32 hsync_reg = HSYNC_A; - u32 vtot_reg = VTOTAL_A; - u32 vblank_reg = VBLANK_A; - u32 vsync_reg = VSYNC_A; - u32 pipesrc_reg = PIPEASRC; - u32 dspstride_reg = DSPASTRIDE; - u32 dsplinoff_reg = DSPALINOFF; - u32 dsptileoff_reg = DSPATILEOFF; - u32 dspsize_reg = DSPASIZE; - u32 dsppos_reg = DSPAPOS; - u32 dspsurf_reg = DSPASURF; - u32 dspstatus_reg = PIPEASTAT; + /* register */ u32 mipi_reg = MIPI; - u32 dspcntr_reg = DSPACNTR; - u32 palette_reg = PALETTE_A; /* values */ - u32 dpll_val = regs->saveDPLL_A & ~DPLL_VCO_ENABLE; - u32 fp_val = regs->saveFPA0; - u32 pipeconf_val = regs->savePIPEACONF; - u32 htot_val = regs->saveHTOTAL_A; - u32 hblank_val = regs->saveHBLANK_A; - u32 hsync_val = regs->saveHSYNC_A; - u32 vtot_val = regs->saveVTOTAL_A; - u32 vblank_val = regs->saveVBLANK_A; - u32 vsync_val = regs->saveVSYNC_A; - u32 pipesrc_val = regs->savePIPEASRC; - u32 dspstride_val = regs->saveDSPASTRIDE; - u32 dsplinoff_val = regs->saveDSPALINOFF; - u32 dsptileoff_val = regs->saveDSPATILEOFF; - u32 dspsize_val = regs->saveDSPASIZE; - u32 dsppos_val = regs->saveDSPAPOS; - u32 dspsurf_val = regs->saveDSPASURF; - u32 dspstatus_val = regs->saveDSPASTATUS; + u32 dpll_val = pipe->dpll; u32 mipi_val = regs->saveMIPI; - u32 dspcntr_val = regs->saveDSPACNTR; - u32 *palette_val = regs->save_palette_a; - switch (pipe) { + switch (pipenum) { case 0: + dpll_val &= ~DPLL_VCO_ENABLE; dsi_config = dev_priv->dsi_configs[0]; break; case 1: - /* regester */ - dpll_reg = MDFLD_DPLL_B; - fp_reg = MDFLD_DPLL_DIV0; - pipeconf_reg = PIPEBCONF; - htot_reg = HTOTAL_B; - hblank_reg = HBLANK_B; - hsync_reg = HSYNC_B; - vtot_reg = VTOTAL_B; - vblank_reg = VBLANK_B; - vsync_reg = VSYNC_B; - pipesrc_reg = PIPEBSRC; - dspstride_reg = DSPBSTRIDE; - dsplinoff_reg = DSPBLINOFF; - dsptileoff_reg = DSPBTILEOFF; - dspsize_reg = DSPBSIZE; - dsppos_reg = DSPBPOS; - dspsurf_reg = DSPBSURF; - dspcntr_reg = DSPBCNTR; - dspstatus_reg = PIPEBSTAT; - palette_reg = PALETTE_B; - - /* values */ - dpll_val = regs->saveDPLL_B & ~DPLL_VCO_ENABLE; - fp_val = regs->saveFPB0; - pipeconf_val = regs->savePIPEBCONF; - htot_val = regs->saveHTOTAL_B; - hblank_val = regs->saveHBLANK_B; - hsync_val = regs->saveHSYNC_B; - vtot_val = regs->saveVTOTAL_B; - vblank_val = regs->saveVBLANK_B; - vsync_val = regs->saveVSYNC_B; - pipesrc_val = regs->savePIPEBSRC; - dspstride_val = regs->saveDSPBSTRIDE; - dsplinoff_val = regs->saveDSPBLINOFF; - dsptileoff_val = regs->saveDSPBTILEOFF; - dspsize_val = regs->saveDSPBSIZE; - dsppos_val = regs->saveDSPBPOS; - dspsurf_val = regs->saveDSPBSURF; - dspcntr_val = regs->saveDSPBCNTR; - dspstatus_val = regs->saveDSPBSTATUS; - palette_val = regs->save_palette_b; + dpll_val &= ~DPLL_VCO_ENABLE; break; case 2: - /* regester */ - pipeconf_reg = PIPECCONF; - htot_reg = HTOTAL_C; - hblank_reg = HBLANK_C; - hsync_reg = HSYNC_C; - vtot_reg = VTOTAL_C; - vblank_reg = VBLANK_C; - vsync_reg = VSYNC_C; - pipesrc_reg = PIPECSRC; - dspstride_reg = DSPCSTRIDE; - dsplinoff_reg = DSPCLINOFF; - dsptileoff_reg = DSPCTILEOFF; - dspsize_reg = DSPCSIZE; - dsppos_reg = DSPCPOS; - dspsurf_reg = DSPCSURF; mipi_reg = MIPI_C; - dspcntr_reg = DSPCCNTR; - dspstatus_reg = PIPECSTAT; - palette_reg = PALETTE_C; - - /* values */ - pipeconf_val = regs->savePIPECCONF; - htot_val = regs->saveHTOTAL_C; - hblank_val = regs->saveHBLANK_C; - hsync_val = regs->saveHSYNC_C; - vtot_val = regs->saveVTOTAL_C; - vblank_val = regs->saveVBLANK_C; - vsync_val = regs->saveVSYNC_C; - pipesrc_val = regs->savePIPECSRC; - dspstride_val = regs->saveDSPCSTRIDE; - dsplinoff_val = regs->saveDSPCLINOFF; - dsptileoff_val = regs->saveDSPCTILEOFF; - dspsize_val = regs->saveDSPCSIZE; - dsppos_val = regs->saveDSPCPOS; - dspsurf_val = regs->saveDSPCSURF; mipi_val = regs->saveMIPI_C; - dspcntr_val = regs->saveDSPCCNTR; - dspstatus_val = regs->saveDSPCSTATUS; - palette_val = regs->save_palette_c; - dsi_config = dev_priv->dsi_configs[1]; break; default: @@ -503,14 +279,14 @@ static int mdfld_restore_display_registers(struct drm_device *dev, int pipe) /*make sure VGA plane is off. it initializes to on after reset!*/ PSB_WVDC32(0x80000000, VGACNTRL); - if (pipe == 1) { - PSB_WVDC32(dpll_val & ~DPLL_VCO_ENABLE, dpll_reg); - PSB_RVDC32(dpll_reg); + if (pipenum == 1) { + PSB_WVDC32(dpll_val & ~DPLL_VCO_ENABLE, map->dpll); + PSB_RVDC32(map->dpll); - PSB_WVDC32(fp_val, fp_reg); + PSB_WVDC32(pipe->fp0, map->fp0); } else { - dpll = PSB_RVDC32(dpll_reg); + dpll = PSB_RVDC32(map->dpll); if (!(dpll & DPLL_VCO_ENABLE)) { @@ -518,23 +294,23 @@ static int mdfld_restore_display_registers(struct drm_device *dev, int pipe) before enable the VCO */ if (dpll & MDFLD_PWR_GATE_EN) { dpll &= ~MDFLD_PWR_GATE_EN; - PSB_WVDC32(dpll, dpll_reg); + PSB_WVDC32(dpll, map->dpll); /* FIXME_MDFLD PO - change 500 to 1 after PO */ udelay(500); } - PSB_WVDC32(fp_val, fp_reg); - PSB_WVDC32(dpll_val, dpll_reg); + PSB_WVDC32(pipe->fp0, map->fp0); + PSB_WVDC32(dpll_val, map->dpll); /* FIXME_MDFLD PO - change 500 to 1 after PO */ udelay(500); dpll_val |= DPLL_VCO_ENABLE; - PSB_WVDC32(dpll_val, dpll_reg); - PSB_RVDC32(dpll_reg); + PSB_WVDC32(dpll_val, map->dpll); + PSB_RVDC32(map->dpll); /* wait for DSI PLL to lock */ while (timeout < 20000 && - !(PSB_RVDC32(pipeconf_reg) & PIPECONF_DSIPLL_LOCK)) { + !(PSB_RVDC32(map->conf) & PIPECONF_DSIPLL_LOCK)) { udelay(150); timeout++; } @@ -547,28 +323,28 @@ static int mdfld_restore_display_registers(struct drm_device *dev, int pipe) } } /* Restore mode */ - PSB_WVDC32(htot_val, htot_reg); - PSB_WVDC32(hblank_val, hblank_reg); - PSB_WVDC32(hsync_val, hsync_reg); - PSB_WVDC32(vtot_val, vtot_reg); - PSB_WVDC32(vblank_val, vblank_reg); - PSB_WVDC32(vsync_val, vsync_reg); - PSB_WVDC32(pipesrc_val, pipesrc_reg); - PSB_WVDC32(dspstatus_val, dspstatus_reg); + PSB_WVDC32(pipe->htotal, map->htotal); + PSB_WVDC32(pipe->hblank, map->hblank); + PSB_WVDC32(pipe->hsync, map->hsync); + PSB_WVDC32(pipe->vtotal, map->vtotal); + PSB_WVDC32(pipe->vblank, map->vblank); + PSB_WVDC32(pipe->vsync, map->vsync); + PSB_WVDC32(pipe->src, map->src); + PSB_WVDC32(pipe->status, map->status); /*set up the plane*/ - PSB_WVDC32(dspstride_val, dspstride_reg); - PSB_WVDC32(dsplinoff_val, dsplinoff_reg); - PSB_WVDC32(dsptileoff_val, dsptileoff_reg); - PSB_WVDC32(dspsize_val, dspsize_reg); - PSB_WVDC32(dsppos_val, dsppos_reg); - PSB_WVDC32(dspsurf_val, dspsurf_reg); - - if (pipe == 1) { + PSB_WVDC32(pipe->stride, map->stride); + PSB_WVDC32(pipe->linoff, map->linoff); + PSB_WVDC32(pipe->tileoff, map->tileoff); + PSB_WVDC32(pipe->size, map->size); + PSB_WVDC32(pipe->pos, map->pos); + PSB_WVDC32(pipe->surf, map->surf); + + if (pipenum == 1) { /* restore palette (gamma) */ /*DRM_UDELAY(50000); */ for (i = 0; i < 256; i++) - PSB_WVDC32(palette_val[i], palette_reg + (i << 2)); + PSB_WVDC32(pipe->palette[i], map->palette + (i << 2)); PSB_WVDC32(regs->savePFIT_CONTROL, PFIT_CONTROL); PSB_WVDC32(regs->savePFIT_PGM_RATIOS, PFIT_PGM_RATIOS); @@ -578,7 +354,7 @@ static int mdfld_restore_display_registers(struct drm_device *dev, int pipe) /*TODO: resume pipe*/ /*enable the plane*/ - PSB_WVDC32(dspcntr_val & ~DISPLAY_PLANE_ENABLE, dspcntr_reg); + PSB_WVDC32(pipe->cntr & ~DISPLAY_PLANE_ENABLE, map->cntr); return 0; } @@ -588,7 +364,7 @@ static int mdfld_restore_display_registers(struct drm_device *dev, int pipe) /*setup MIPI adapter + MIPI IP registers*/ if (dsi_config) - mdfld_dsi_controller_init(dsi_config, pipe); + mdfld_dsi_controller_init(dsi_config, pipenum); if (in_atomic() || in_interrupt()) mdelay(20); @@ -596,7 +372,7 @@ static int mdfld_restore_display_registers(struct drm_device *dev, int pipe) msleep(20); /*enable the plane*/ - PSB_WVDC32(dspcntr_val, dspcntr_reg); + PSB_WVDC32(pipe->cntr, map->cntr); if (in_atomic() || in_interrupt()) mdelay(20); @@ -625,12 +401,12 @@ static int mdfld_restore_display_registers(struct drm_device *dev, int pipe) mdelay(1); /*enable the pipe*/ - PSB_WVDC32(pipeconf_val, pipeconf_reg); + PSB_WVDC32(pipe->conf, map->conf); /* restore palette (gamma) */ /*DRM_UDELAY(50000); */ for (i = 0; i < 256; i++) - PSB_WVDC32(palette_val[i], palette_reg + (i << 2)); + PSB_WVDC32(pipe->palette[i], map->palette + (i << 2)); return 0; } @@ -667,6 +443,87 @@ static int mdfld_power_up(struct drm_device *dev) return 0; } +/* Medfield */ +static const struct psb_offset mdfld_regmap[3] = { + { + .fp0 = MRST_FPA0, + .fp1 = MRST_FPA1, + .cntr = DSPACNTR, + .conf = PIPEACONF, + .src = PIPEASRC, + .dpll = MRST_DPLL_A, + .htotal = HTOTAL_A, + .hblank = HBLANK_A, + .hsync = HSYNC_A, + .vtotal = VTOTAL_A, + .vblank = VBLANK_A, + .vsync = VSYNC_A, + .stride = DSPASTRIDE, + .size = DSPASIZE, + .pos = DSPAPOS, + .surf = DSPASURF, + .addr = MRST_DSPABASE, + .status = PIPEASTAT, + .linoff = DSPALINOFF, + .tileoff = DSPATILEOFF, + .palette = PALETTE_A, + }, + { + .fp0 = MDFLD_DPLL_DIV0, + .cntr = DSPBCNTR, + .conf = PIPEBCONF, + .src = PIPEBSRC, + .dpll = MDFLD_DPLL_B, + .htotal = HTOTAL_B, + .hblank = HBLANK_B, + .hsync = HSYNC_B, + .vtotal = VTOTAL_B, + .vblank = VBLANK_B, + .vsync = VSYNC_B, + .stride = DSPBSTRIDE, + .size = DSPBSIZE, + .pos = DSPBPOS, + .surf = DSPBSURF, + .addr = MRST_DSPBBASE, + .status = PIPEBSTAT, + .linoff = DSPBLINOFF, + .tileoff = DSPBTILEOFF, + .palette = PALETTE_B, + }, + { + .fp0 = MRST_FPA0, /* This is what the old code did ?? */ + .cntr = DSPCCNTR, + .conf = PIPECCONF, + .src = PIPECSRC, + /* No DPLL_C */ + .dpll = MRST_DPLL_A, + .htotal = HTOTAL_C, + .hblank = HBLANK_C, + .hsync = HSYNC_C, + .vtotal = VTOTAL_C, + .vblank = VBLANK_C, + .vsync = VSYNC_C, + .stride = DSPCSTRIDE, + .size = DSPBSIZE, + .pos = DSPCPOS, + .surf = DSPCSURF, + .addr = MDFLD_DSPCBASE, + .status = PIPECSTAT, + .linoff = DSPCLINOFF, + .tileoff = DSPCTILEOFF, + .palette = PALETTE_C, + }, +}; + +static int mdfld_chip_setup(struct drm_device *dev) +{ + struct drm_psb_private *dev_priv = dev->dev_private; + if (pci_enable_msi(dev->pdev)) + dev_warn(dev->dev, "Enabling MSI failed!\n"); + dev_priv->regmap = mdfld_regmap; + return mid_chip_setup(dev); +} + const struct psb_ops mdfld_chip_ops = { .name = "mdfld", .accel_2d = 0, @@ -674,9 +531,10 @@ const struct psb_ops mdfld_chip_ops = { .crtcs = 3, .lvds_mask = (1 << 1), .hdmi_mask = (1 << 1), + .cursor_needs_phys = 0, .sgx_offset = MRST_SGX_OFFSET, - .chip_setup = mid_chip_setup, + .chip_setup = mdfld_chip_setup, .crtc_helper = &mdfld_helper_funcs, .crtc_funcs = &psb_intel_crtc_funcs, diff --git a/drivers/gpu/drm/gma500/mdfld_dsi_pkg_sender.c b/drivers/gpu/drm/gma500/mdfld_dsi_pkg_sender.c index baa0e14..489ffd2 100644 --- a/drivers/gpu/drm/gma500/mdfld_dsi_pkg_sender.c +++ b/drivers/gpu/drm/gma500/mdfld_dsi_pkg_sender.c @@ -605,6 +605,8 @@ int mdfld_dsi_pkg_sender_init(struct mdfld_dsi_connector *dsi_connector, struct mdfld_dsi_config *dsi_config = mdfld_dsi_get_config(dsi_connector); struct drm_device *dev = dsi_config->dev; + struct drm_psb_private *dev_priv = dev->dev_private; + const struct psb_offset *map = &dev_priv->regmap[pipe]; u32 mipi_val = 0; if (!dsi_connector) { @@ -632,21 +634,13 @@ int mdfld_dsi_pkg_sender_init(struct mdfld_dsi_connector *dsi_connector, pkg_sender->status = MDFLD_DSI_PKG_SENDER_FREE; /*init regs*/ - if (pipe == 0) { - pkg_sender->dpll_reg = MRST_DPLL_A; - pkg_sender->dspcntr_reg = DSPACNTR; - pkg_sender->pipeconf_reg = PIPEACONF; - pkg_sender->dsplinoff_reg = DSPALINOFF; - pkg_sender->dspsurf_reg = DSPASURF; - pkg_sender->pipestat_reg = PIPEASTAT; - } else if (pipe == 2) { - pkg_sender->dpll_reg = MRST_DPLL_A; - pkg_sender->dspcntr_reg = DSPCCNTR; - pkg_sender->pipeconf_reg = PIPECCONF; - pkg_sender->dsplinoff_reg = DSPCLINOFF; - pkg_sender->dspsurf_reg = DSPCSURF; - pkg_sender->pipestat_reg = PIPECSTAT; - } + /* FIXME: should just copy the regmap ptr ? */ + pkg_sender->dpll_reg = map->dpll; + pkg_sender->dspcntr_reg = map->cntr; + pkg_sender->pipeconf_reg = map->conf; + pkg_sender->dsplinoff_reg = map->linoff; + pkg_sender->dspsurf_reg = map->surf; + pkg_sender->pipestat_reg = map->status; pkg_sender->mipi_intr_stat_reg = MIPI_INTR_STAT_REG(pipe); pkg_sender->mipi_lp_gen_data_reg = MIPI_LP_GEN_DATA_REG(pipe); diff --git a/drivers/gpu/drm/gma500/mdfld_intel_display.c b/drivers/gpu/drm/gma500/mdfld_intel_display.c index a35a292..3f3cd61 100644 --- a/drivers/gpu/drm/gma500/mdfld_intel_display.c +++ b/drivers/gpu/drm/gma500/mdfld_intel_display.c @@ -50,17 +50,14 @@ struct mrst_clock_t { void mdfldWaitForPipeDisable(struct drm_device *dev, int pipe) { + struct drm_psb_private *dev_priv = dev->dev_private; + const struct psb_offset *map = &dev_priv->regmap[pipe]; int count, temp; - u32 pipeconf_reg = PIPEACONF; switch (pipe) { case 0: - break; case 1: - pipeconf_reg = PIPEBCONF; - break; case 2: - pipeconf_reg = PIPECCONF; break; default: DRM_ERROR("Illegal Pipe Number.\n"); @@ -73,7 +70,7 @@ void mdfldWaitForPipeDisable(struct drm_device *dev, int pipe) /* Wait for for the pipe disable to take effect. */ for (count = 0; count < COUNT_MAX; count++) { - temp = REG_READ(pipeconf_reg); + temp = REG_READ(map->conf); if ((temp & PIPEACONF_PIPE_STATE) == 0) break; } @@ -81,17 +78,14 @@ void mdfldWaitForPipeDisable(struct drm_device *dev, int pipe) void mdfldWaitForPipeEnable(struct drm_device *dev, int pipe) { + struct drm_psb_private *dev_priv = dev->dev_private; + const struct psb_offset *map = &dev_priv->regmap[pipe]; int count, temp; - u32 pipeconf_reg = PIPEACONF; switch (pipe) { case 0: - break; case 1: - pipeconf_reg = PIPEBCONF; - break; case 2: - pipeconf_reg = PIPECCONF; break; default: DRM_ERROR("Illegal Pipe Number.\n"); @@ -104,7 +98,7 @@ void mdfldWaitForPipeEnable(struct drm_device *dev, int pipe) /* Wait for for the pipe enable to take effect. */ for (count = 0; count < COUNT_MAX; count++) { - temp = REG_READ(pipeconf_reg); + temp = REG_READ(map->conf); if ((temp & PIPEACONF_PIPE_STATE) == 1) break; } @@ -189,15 +183,12 @@ static int mdfld__intel_pipe_set_base(struct drm_crtc *crtc, int x, int y, struct drm_framebuffer *old_fb) { struct drm_device *dev = crtc->dev; - /* struct drm_i915_master_private *master_priv; */ + struct drm_psb_private *dev_priv = dev->dev_private; struct psb_intel_crtc *psb_intel_crtc = to_psb_intel_crtc(crtc); struct psb_framebuffer *psbfb = to_psb_fb(crtc->fb); int pipe = psb_intel_crtc->pipe; + const struct psb_offset *map = &dev_priv->regmap[pipe]; unsigned long start, offset; - int dsplinoff = DSPALINOFF; - int dspsurf = DSPASURF; - int dspstride = DSPASTRIDE; - int dspcntr_reg = DSPACNTR; u32 dspcntr; int ret; @@ -215,23 +206,7 @@ static int mdfld__intel_pipe_set_base(struct drm_crtc *crtc, int x, int y, if (ret) return ret; - switch (pipe) { - case 0: - dsplinoff = DSPALINOFF; - break; - case 1: - dsplinoff = DSPBLINOFF; - dspsurf = DSPBSURF; - dspstride = DSPBSTRIDE; - dspcntr_reg = DSPBCNTR; - break; - case 2: - dsplinoff = DSPCLINOFF; - dspsurf = DSPCSURF; - dspstride = DSPCSTRIDE; - dspcntr_reg = DSPCCNTR; - break; - default: + if (pipe > 2) { DRM_ERROR("Illegal Pipe Number.\n"); return -EINVAL; } @@ -242,8 +217,8 @@ static int mdfld__intel_pipe_set_base(struct drm_crtc *crtc, int x, int y, start = psbfb->gtt->offset; offset = y * crtc->fb->pitches[0] + x * (crtc->fb->bits_per_pixel / 8); - REG_WRITE(dspstride, crtc->fb->pitches[0]); - dspcntr = REG_READ(dspcntr_reg); + REG_WRITE(map->stride, crtc->fb->pitches[0]); + dspcntr = REG_READ(map->cntr); dspcntr &= ~DISPPLANE_PIXFORMAT_MASK; switch (crtc->fb->bits_per_pixel) { @@ -261,14 +236,14 @@ static int mdfld__intel_pipe_set_base(struct drm_crtc *crtc, int x, int y, dspcntr |= DISPPLANE_32BPP_NO_ALPHA; break; } - REG_WRITE(dspcntr_reg, dspcntr); + REG_WRITE(map->cntr, dspcntr); dev_dbg(dev->dev, "Writing base %08lX %08lX %d %d\n", start, offset, x, y); - REG_WRITE(dsplinoff, offset); - REG_READ(dsplinoff); - REG_WRITE(dspsurf, start); - REG_READ(dspsurf); + REG_WRITE(map->linoff, offset); + REG_READ(map->linoff); + REG_WRITE(map->surf, start); + REG_READ(map->surf); gma_power_end(dev); @@ -281,78 +256,56 @@ static int mdfld__intel_pipe_set_base(struct drm_crtc *crtc, int x, int y, */ void mdfld_disable_crtc(struct drm_device *dev, int pipe) { - int dpll_reg = MRST_DPLL_A; - int dspcntr_reg = DSPACNTR; - int dspbase_reg = MRST_DSPABASE; - int pipeconf_reg = PIPEACONF; + struct drm_psb_private *dev_priv = dev->dev_private; + const struct psb_offset *map = &dev_priv->regmap[pipe]; u32 temp; dev_dbg(dev->dev, "pipe = %d\n", pipe); - switch (pipe) { - case 0: - break; - case 1: - dpll_reg = MDFLD_DPLL_B; - dspcntr_reg = DSPBCNTR; - dspbase_reg = DSPBSURF; - pipeconf_reg = PIPEBCONF; - break; - case 2: - dpll_reg = MRST_DPLL_A; - dspcntr_reg = DSPCCNTR; - dspbase_reg = MDFLD_DSPCBASE; - pipeconf_reg = PIPECCONF; - break; - default: - DRM_ERROR("Illegal Pipe Number.\n"); - return; - } - if (pipe != 1) mdfld_dsi_gen_fifo_ready(dev, MIPI_GEN_FIFO_STAT_REG(pipe), HS_CTRL_FIFO_EMPTY | HS_DATA_FIFO_EMPTY); /* Disable display plane */ - temp = REG_READ(dspcntr_reg); + temp = REG_READ(map->cntr); if ((temp & DISPLAY_PLANE_ENABLE) != 0) { - REG_WRITE(dspcntr_reg, + REG_WRITE(map->cntr, temp & ~DISPLAY_PLANE_ENABLE); /* Flush the plane changes */ - REG_WRITE(dspbase_reg, REG_READ(dspbase_reg)); - REG_READ(dspbase_reg); + REG_WRITE(map->base, REG_READ(map->base)); + REG_READ(map->base); } /* FIXME_JLIU7 MDFLD_PO revisit */ /* Next, disable display pipes */ - temp = REG_READ(pipeconf_reg); + temp = REG_READ(map->conf); if ((temp & PIPEACONF_ENABLE) != 0) { temp &= ~PIPEACONF_ENABLE; temp |= PIPECONF_PLANE_OFF | PIPECONF_CURSOR_OFF; - REG_WRITE(pipeconf_reg, temp); - REG_READ(pipeconf_reg); + REG_WRITE(map->conf, temp); + REG_READ(map->conf); /* Wait for for the pipe disable to take effect. */ mdfldWaitForPipeDisable(dev, pipe); } - temp = REG_READ(dpll_reg); + temp = REG_READ(map->dpll); if (temp & DPLL_VCO_ENABLE) { if ((pipe != 1 && !((REG_READ(PIPEACONF) | REG_READ(PIPECCONF)) & PIPEACONF_ENABLE)) || pipe == 1) { temp &= ~(DPLL_VCO_ENABLE); - REG_WRITE(dpll_reg, temp); - REG_READ(dpll_reg); + REG_WRITE(map->dpll, temp); + REG_READ(map->dpll); /* Wait for the clocks to turn off. */ /* FIXME_MDFLD PO may need more delay */ udelay(500); if (!(temp & MDFLD_PWR_GATE_EN)) { /* gating power of DPLL */ - REG_WRITE(dpll_reg, temp | MDFLD_PWR_GATE_EN); + REG_WRITE(map->dpll, temp | MDFLD_PWR_GATE_EN); /* FIXME_MDFLD PO - change 500 to 1 after PO */ udelay(5000); } @@ -373,41 +326,15 @@ static void mdfld_crtc_dpms(struct drm_crtc *crtc, int mode) struct drm_psb_private *dev_priv = dev->dev_private; struct psb_intel_crtc *psb_intel_crtc = to_psb_intel_crtc(crtc); int pipe = psb_intel_crtc->pipe; - int dpll_reg = MRST_DPLL_A; - int dspcntr_reg = DSPACNTR; - int dspbase_reg = MRST_DSPABASE; - int pipeconf_reg = PIPEACONF; - u32 pipestat_reg = PIPEASTAT; + const struct psb_offset *map = &dev_priv->regmap[pipe]; u32 pipeconf = dev_priv->pipeconf[pipe]; u32 temp; int timeout = 0; dev_dbg(dev->dev, "mode = %d, pipe = %d\n", mode, pipe); -/* FIXME_JLIU7 MDFLD_PO replaced w/ the following function */ -/* mdfld_dbi_dpms (struct drm_device *dev, int pipe, bool enabled) */ - - switch (pipe) { - case 0: - break; - case 1: - dpll_reg = DPLL_B; - dspcntr_reg = DSPBCNTR; - dspbase_reg = MRST_DSPBBASE; - pipeconf_reg = PIPEBCONF; - dpll_reg = MDFLD_DPLL_B; - break; - case 2: - dpll_reg = MRST_DPLL_A; - dspcntr_reg = DSPCCNTR; - dspbase_reg = MDFLD_DSPCBASE; - pipeconf_reg = PIPECCONF; - pipestat_reg = PIPECSTAT; - break; - default: - DRM_ERROR("Illegal Pipe Number.\n"); - return; - } + /* Note: Old code uses pipe a stat for pipe b but that appears + to be a bug */ if (!gma_power_begin(dev, true)) return; @@ -420,25 +347,25 @@ static void mdfld_crtc_dpms(struct drm_crtc *crtc, int mode) case DRM_MODE_DPMS_STANDBY: case DRM_MODE_DPMS_SUSPEND: /* Enable the DPLL */ - temp = REG_READ(dpll_reg); + temp = REG_READ(map->dpll); if ((temp & DPLL_VCO_ENABLE) == 0) { /* When ungating power of DPLL, needs to wait 0.5us before enable the VCO */ if (temp & MDFLD_PWR_GATE_EN) { temp &= ~MDFLD_PWR_GATE_EN; - REG_WRITE(dpll_reg, temp); + REG_WRITE(map->dpll, temp); /* FIXME_MDFLD PO - change 500 to 1 after PO */ udelay(500); } - REG_WRITE(dpll_reg, temp); - REG_READ(dpll_reg); + REG_WRITE(map->dpll, temp); + REG_READ(map->dpll); /* FIXME_MDFLD PO - change 500 to 1 after PO */ udelay(500); - REG_WRITE(dpll_reg, temp | DPLL_VCO_ENABLE); - REG_READ(dpll_reg); + REG_WRITE(map->dpll, temp | DPLL_VCO_ENABLE); + REG_READ(map->dpll); /** * wait for DSI PLL to lock @@ -446,25 +373,25 @@ static void mdfld_crtc_dpms(struct drm_crtc *crtc, int mode) * since both MIPI pipes share the same PLL. */ while ((pipe != 2) && (timeout < 20000) && - !(REG_READ(pipeconf_reg) & PIPECONF_DSIPLL_LOCK)) { + !(REG_READ(map->conf) & PIPECONF_DSIPLL_LOCK)) { udelay(150); timeout++; } } /* Enable the plane */ - temp = REG_READ(dspcntr_reg); + temp = REG_READ(map->cntr); if ((temp & DISPLAY_PLANE_ENABLE) == 0) { - REG_WRITE(dspcntr_reg, + REG_WRITE(map->cntr, temp | DISPLAY_PLANE_ENABLE); /* Flush the plane changes */ - REG_WRITE(dspbase_reg, REG_READ(dspbase_reg)); + REG_WRITE(map->base, REG_READ(map->base)); } /* Enable the pipe */ - temp = REG_READ(pipeconf_reg); + temp = REG_READ(map->conf); if ((temp & PIPEACONF_ENABLE) == 0) { - REG_WRITE(pipeconf_reg, pipeconf); + REG_WRITE(map->conf, pipeconf); /* Wait for for the pipe enable to take effect. */ mdfldWaitForPipeEnable(dev, pipe); @@ -473,39 +400,39 @@ static void mdfld_crtc_dpms(struct drm_crtc *crtc, int mode) /*workaround for sighting 3741701 Random X blank display*/ /*perform w/a in video mode only on pipe A or C*/ if (pipe == 0 || pipe == 2) { - REG_WRITE(pipestat_reg, REG_READ(pipestat_reg)); + REG_WRITE(map->status, REG_READ(map->status)); msleep(100); - if (PIPE_VBLANK_STATUS & REG_READ(pipestat_reg)) + if (PIPE_VBLANK_STATUS & REG_READ(map->status)) dev_dbg(dev->dev, "OK"); else { dev_dbg(dev->dev, "STUCK!!!!"); /*shutdown controller*/ - temp = REG_READ(dspcntr_reg); - REG_WRITE(dspcntr_reg, + temp = REG_READ(map->cntr); + REG_WRITE(map->cntr, temp & ~DISPLAY_PLANE_ENABLE); - REG_WRITE(dspbase_reg, REG_READ(dspbase_reg)); + REG_WRITE(map->base, REG_READ(map->base)); /*mdfld_dsi_dpi_shut_down(dev, pipe);*/ REG_WRITE(0xb048, 1); msleep(100); - temp = REG_READ(pipeconf_reg); + temp = REG_READ(map->conf); temp &= ~PIPEACONF_ENABLE; - REG_WRITE(pipeconf_reg, temp); + REG_WRITE(map->conf, temp); msleep(100); /*wait for pipe disable*/ REG_WRITE(MIPI_DEVICE_READY_REG(pipe), 0); msleep(100); REG_WRITE(0xb004, REG_READ(0xb004)); /* try to bring the controller back up again*/ REG_WRITE(MIPI_DEVICE_READY_REG(pipe), 1); - temp = REG_READ(dspcntr_reg); - REG_WRITE(dspcntr_reg, + temp = REG_READ(map->cntr); + REG_WRITE(map->cntr, temp | DISPLAY_PLANE_ENABLE); - REG_WRITE(dspbase_reg, REG_READ(dspbase_reg)); + REG_WRITE(map->base, REG_READ(map->base)); /*mdfld_dsi_dpi_turn_on(dev, pipe);*/ REG_WRITE(0xb048, 2); msleep(100); - temp = REG_READ(pipeconf_reg); + temp = REG_READ(map->conf); temp |= PIPEACONF_ENABLE; - REG_WRITE(pipeconf_reg, temp); + REG_WRITE(map->conf, temp); } } @@ -529,35 +456,35 @@ static void mdfld_crtc_dpms(struct drm_crtc *crtc, int mode) REG_WRITE(VGACNTRL, VGA_DISP_DISABLE); /* Disable display plane */ - temp = REG_READ(dspcntr_reg); + temp = REG_READ(map->cntr); if ((temp & DISPLAY_PLANE_ENABLE) != 0) { - REG_WRITE(dspcntr_reg, + REG_WRITE(map->cntr, temp & ~DISPLAY_PLANE_ENABLE); /* Flush the plane changes */ - REG_WRITE(dspbase_reg, REG_READ(dspbase_reg)); - REG_READ(dspbase_reg); + REG_WRITE(map->base, REG_READ(map->base)); + REG_READ(map->base); } /* Next, disable display pipes */ - temp = REG_READ(pipeconf_reg); + temp = REG_READ(map->conf); if ((temp & PIPEACONF_ENABLE) != 0) { temp &= ~PIPEACONF_ENABLE; temp |= PIPECONF_PLANE_OFF | PIPECONF_CURSOR_OFF; - REG_WRITE(pipeconf_reg, temp); - REG_READ(pipeconf_reg); + REG_WRITE(map->conf, temp); + REG_READ(map->conf); /* Wait for for the pipe disable to take effect. */ mdfldWaitForPipeDisable(dev, pipe); } - temp = REG_READ(dpll_reg); + temp = REG_READ(map->dpll); if (temp & DPLL_VCO_ENABLE) { if ((pipe != 1 && !((REG_READ(PIPEACONF) | REG_READ(PIPECCONF)) & PIPEACONF_ENABLE)) || pipe == 1) { temp &= ~(DPLL_VCO_ENABLE); - REG_WRITE(dpll_reg, temp); - REG_READ(dpll_reg); + REG_WRITE(map->dpll, temp); + REG_READ(map->dpll); /* Wait for the clocks to turn off. */ /* FIXME_MDFLD PO may need more delay */ udelay(500); @@ -764,21 +691,7 @@ static int mdfld_crtc_mode_set(struct drm_crtc *crtc, struct psb_intel_crtc *psb_intel_crtc = to_psb_intel_crtc(crtc); struct drm_psb_private *dev_priv = dev->dev_private; int pipe = psb_intel_crtc->pipe; - int fp_reg = MRST_FPA0; - int dpll_reg = MRST_DPLL_A; - int dspcntr_reg = DSPACNTR; - int pipeconf_reg = PIPEACONF; - int htot_reg = HTOTAL_A; - int hblank_reg = HBLANK_A; - int hsync_reg = HSYNC_A; - int vtot_reg = VTOTAL_A; - int vblank_reg = VBLANK_A; - int vsync_reg = VSYNC_A; - int dspsize_reg = DSPASIZE; - int dsppos_reg = DSPAPOS; - int pipesrc_reg = PIPEASRC; - u32 *pipeconf = &dev_priv->pipeconf[pipe]; - u32 *dspcntr = &dev_priv->dspcntr[pipe]; + const struct psb_offset *map = &dev_priv->regmap[pipe]; int refclk = 0; int clk_n = 0, clk_p2 = 0, clk_byte = 1, clk = 0, m_conv = 0, clk_tmp = 0; @@ -806,45 +719,6 @@ static int mdfld_crtc_mode_set(struct drm_crtc *crtc, } #endif - switch (pipe) { - case 0: - break; - case 1: - fp_reg = FPB0; - dpll_reg = DPLL_B; - dspcntr_reg = DSPBCNTR; - pipeconf_reg = PIPEBCONF; - htot_reg = HTOTAL_B; - hblank_reg = HBLANK_B; - hsync_reg = HSYNC_B; - vtot_reg = VTOTAL_B; - vblank_reg = VBLANK_B; - vsync_reg = VSYNC_B; - dspsize_reg = DSPBSIZE; - dsppos_reg = DSPBPOS; - pipesrc_reg = PIPEBSRC; - fp_reg = MDFLD_DPLL_DIV0; - dpll_reg = MDFLD_DPLL_B; - break; - case 2: - dpll_reg = MRST_DPLL_A; - dspcntr_reg = DSPCCNTR; - pipeconf_reg = PIPECCONF; - htot_reg = HTOTAL_C; - hblank_reg = HBLANK_C; - hsync_reg = HSYNC_C; - vtot_reg = VTOTAL_C; - vblank_reg = VBLANK_C; - vsync_reg = VSYNC_C; - dspsize_reg = DSPCSIZE; - dsppos_reg = DSPCPOS; - pipesrc_reg = PIPECSRC; - break; - default: - DRM_ERROR("Illegal Pipe Number.\n"); - return 0; - } - ret = check_fb(crtc->fb); if (ret) return ret; @@ -929,21 +803,21 @@ static int mdfld_crtc_mode_set(struct drm_crtc *crtc, * contained within the displayable area of the screen image * (frame buffer). */ - REG_WRITE(dspsize_reg, ((min(mode->crtc_vdisplay, adjusted_mode->crtc_vdisplay) - 1) << 16) + REG_WRITE(map->size, ((min(mode->crtc_vdisplay, adjusted_mode->crtc_vdisplay) - 1) << 16) | (min(mode->crtc_hdisplay, adjusted_mode->crtc_hdisplay) - 1)); /* Set the CRTC with encoder mode. */ - REG_WRITE(pipesrc_reg, ((mode->crtc_hdisplay - 1) << 16) + REG_WRITE(map->src, ((mode->crtc_hdisplay - 1) << 16) | (mode->crtc_vdisplay - 1)); } else { - REG_WRITE(dspsize_reg, + REG_WRITE(map->size, ((mode->crtc_vdisplay - 1) << 16) | (mode->crtc_hdisplay - 1)); - REG_WRITE(pipesrc_reg, + REG_WRITE(map->src, ((mode->crtc_hdisplay - 1) << 16) | (mode->crtc_vdisplay - 1)); } - REG_WRITE(dsppos_reg, 0); + REG_WRITE(map->pos, 0); if (psb_intel_encoder) drm_connector_property_get_value(connector, @@ -961,34 +835,34 @@ static int mdfld_crtc_mode_set(struct drm_crtc *crtc, offsetY = (adjusted_mode->crtc_vdisplay - mode->crtc_vdisplay) / 2; - REG_WRITE(htot_reg, (mode->crtc_hdisplay - 1) | + REG_WRITE(map->htotal, (mode->crtc_hdisplay - 1) | ((adjusted_mode->crtc_htotal - 1) << 16)); - REG_WRITE(vtot_reg, (mode->crtc_vdisplay - 1) | + REG_WRITE(map->vtotal, (mode->crtc_vdisplay - 1) | ((adjusted_mode->crtc_vtotal - 1) << 16)); - REG_WRITE(hblank_reg, (adjusted_mode->crtc_hblank_start - + REG_WRITE(map->hblank, (adjusted_mode->crtc_hblank_start - offsetX - 1) | ((adjusted_mode->crtc_hblank_end - offsetX - 1) << 16)); - REG_WRITE(hsync_reg, (adjusted_mode->crtc_hsync_start - + REG_WRITE(map->hsync, (adjusted_mode->crtc_hsync_start - offsetX - 1) | ((adjusted_mode->crtc_hsync_end - offsetX - 1) << 16)); - REG_WRITE(vblank_reg, (adjusted_mode->crtc_vblank_start - + REG_WRITE(map->vblank, (adjusted_mode->crtc_vblank_start - offsetY - 1) | ((adjusted_mode->crtc_vblank_end - offsetY - 1) << 16)); - REG_WRITE(vsync_reg, (adjusted_mode->crtc_vsync_start - + REG_WRITE(map->vsync, (adjusted_mode->crtc_vsync_start - offsetY - 1) | ((adjusted_mode->crtc_vsync_end - offsetY - 1) << 16)); } else { - REG_WRITE(htot_reg, (adjusted_mode->crtc_hdisplay - 1) | + REG_WRITE(map->htotal, (adjusted_mode->crtc_hdisplay - 1) | ((adjusted_mode->crtc_htotal - 1) << 16)); - REG_WRITE(vtot_reg, (adjusted_mode->crtc_vdisplay - 1) | + REG_WRITE(map->vtotal, (adjusted_mode->crtc_vdisplay - 1) | ((adjusted_mode->crtc_vtotal - 1) << 16)); - REG_WRITE(hblank_reg, (adjusted_mode->crtc_hblank_start - 1) | + REG_WRITE(map->hblank, (adjusted_mode->crtc_hblank_start - 1) | ((adjusted_mode->crtc_hblank_end - 1) << 16)); - REG_WRITE(hsync_reg, (adjusted_mode->crtc_hsync_start - 1) | + REG_WRITE(map->hsync, (adjusted_mode->crtc_hsync_start - 1) | ((adjusted_mode->crtc_hsync_end - 1) << 16)); - REG_WRITE(vblank_reg, (adjusted_mode->crtc_vblank_start - 1) | + REG_WRITE(map->vblank, (adjusted_mode->crtc_vblank_start - 1) | ((adjusted_mode->crtc_vblank_end - 1) << 16)); - REG_WRITE(vsync_reg, (adjusted_mode->crtc_vsync_start - 1) | + REG_WRITE(map->vsync, (adjusted_mode->crtc_vsync_start - 1) | ((adjusted_mode->crtc_vsync_end - 1) << 16)); } @@ -1000,12 +874,12 @@ static int mdfld_crtc_mode_set(struct drm_crtc *crtc, } /* setup pipeconf */ - *pipeconf = PIPEACONF_ENABLE; /* FIXME_JLIU7 REG_READ(pipeconf_reg); */ + dev_priv->pipeconf[pipe] = PIPEACONF_ENABLE; /* FIXME_JLIU7 REG_READ(pipeconf_reg); */ /* Set up the display plane register */ - *dspcntr = REG_READ(dspcntr_reg); - *dspcntr |= pipe << DISPPLANE_SEL_PIPE_POS; - *dspcntr |= DISPLAY_PLANE_ENABLE; + dev_priv->dspcntr[pipe] = REG_READ(map->cntr); + dev_priv->dspcntr[pipe] |= pipe << DISPPLANE_SEL_PIPE_POS; + dev_priv->dspcntr[pipe] |= DISPLAY_PLANE_ENABLE; if (is_mipi2) goto mrst_crtc_mode_set_exit; @@ -1070,21 +944,21 @@ static int mdfld_crtc_mode_set(struct drm_crtc *crtc, clock.p1, m_conv); } - dpll = REG_READ(dpll_reg); + dpll = REG_READ(map->dpll); if (dpll & DPLL_VCO_ENABLE) { dpll &= ~DPLL_VCO_ENABLE; - REG_WRITE(dpll_reg, dpll); - REG_READ(dpll_reg); + REG_WRITE(map->dpll, dpll); + REG_READ(map->dpll); /* FIXME jliu7 check the DPLL lock bit PIPEACONF[29] */ /* FIXME_MDFLD PO - change 500 to 1 after PO */ udelay(500); /* reset M1, N1 & P1 */ - REG_WRITE(fp_reg, 0); + REG_WRITE(map->fp0, 0); dpll &= ~MDFLD_P1_MASK; - REG_WRITE(dpll_reg, dpll); + REG_WRITE(map->dpll, dpll); /* FIXME_MDFLD PO - change 500 to 1 after PO */ udelay(500); } @@ -1093,7 +967,7 @@ static int mdfld_crtc_mode_set(struct drm_crtc *crtc, * enable the VCO */ if (dpll & MDFLD_PWR_GATE_EN) { dpll &= ~MDFLD_PWR_GATE_EN; - REG_WRITE(dpll_reg, dpll); + REG_WRITE(map->dpll, dpll); /* FIXME_MDFLD PO - change 500 to 1 after PO */ udelay(500); } @@ -1134,18 +1008,18 @@ static int mdfld_crtc_mode_set(struct drm_crtc *crtc, fp = 0x000000c1; } - REG_WRITE(fp_reg, fp); - REG_WRITE(dpll_reg, dpll); + REG_WRITE(map->fp0, fp); + REG_WRITE(map->dpll, dpll); /* FIXME_MDFLD PO - change 500 to 1 after PO */ udelay(500); dpll |= DPLL_VCO_ENABLE; - REG_WRITE(dpll_reg, dpll); - REG_READ(dpll_reg); + REG_WRITE(map->dpll, dpll); + REG_READ(map->dpll); /* wait for DSI PLL to lock */ while (timeout < 20000 && - !(REG_READ(pipeconf_reg) & PIPECONF_DSIPLL_LOCK)) { + !(REG_READ(map->conf) & PIPECONF_DSIPLL_LOCK)) { udelay(150); timeout++; } @@ -1155,11 +1029,11 @@ static int mdfld_crtc_mode_set(struct drm_crtc *crtc, dev_dbg(dev->dev, "is_mipi = 0x%x\n", is_mipi); - REG_WRITE(pipeconf_reg, *pipeconf); - REG_READ(pipeconf_reg); + REG_WRITE(map->conf, dev_priv->pipeconf[pipe]); + REG_READ(map->conf); /* Wait for for the pipe enable to take effect. */ - REG_WRITE(dspcntr_reg, *dspcntr); + REG_WRITE(map->cntr, dev_priv->dspcntr[pipe]); psb_intel_wait_for_vblank(dev); mrst_crtc_mode_set_exit: diff --git a/drivers/gpu/drm/gma500/oaktrail_crtc.c b/drivers/gpu/drm/gma500/oaktrail_crtc.c index a39b0d0..f821c83 100644 --- a/drivers/gpu/drm/gma500/oaktrail_crtc.c +++ b/drivers/gpu/drm/gma500/oaktrail_crtc.c @@ -162,12 +162,10 @@ mrstFindBestPLL(struct drm_crtc *crtc, int target, int refclk, static void oaktrail_crtc_dpms(struct drm_crtc *crtc, int mode) { struct drm_device *dev = crtc->dev; + struct drm_psb_private *dev_priv = dev->dev_private; struct psb_intel_crtc *psb_intel_crtc = to_psb_intel_crtc(crtc); int pipe = psb_intel_crtc->pipe; - int dpll_reg = (pipe == 0) ? MRST_DPLL_A : DPLL_B; - int dspcntr_reg = (pipe == 0) ? DSPACNTR : DSPBCNTR; - int dspbase_reg = (pipe == 0) ? MRST_DSPABASE : DSPBBASE; - int pipeconf_reg = (pipe == 0) ? PIPEACONF : PIPEBCONF; + const struct psb_offset *map = &dev_priv->regmap[pipe]; u32 temp; if (!gma_power_begin(dev, true)) @@ -181,32 +179,32 @@ static void oaktrail_crtc_dpms(struct drm_crtc *crtc, int mode) case DRM_MODE_DPMS_STANDBY: case DRM_MODE_DPMS_SUSPEND: /* Enable the DPLL */ - temp = REG_READ(dpll_reg); + temp = REG_READ(map->dpll); if ((temp & DPLL_VCO_ENABLE) == 0) { - REG_WRITE(dpll_reg, temp); - REG_READ(dpll_reg); + REG_WRITE(map->dpll, temp); + REG_READ(map->dpll); /* Wait for the clocks to stabilize. */ udelay(150); - REG_WRITE(dpll_reg, temp | DPLL_VCO_ENABLE); - REG_READ(dpll_reg); + REG_WRITE(map->dpll, temp | DPLL_VCO_ENABLE); + REG_READ(map->dpll); /* Wait for the clocks to stabilize. */ udelay(150); - REG_WRITE(dpll_reg, temp | DPLL_VCO_ENABLE); - REG_READ(dpll_reg); + REG_WRITE(map->dpll, temp | DPLL_VCO_ENABLE); + REG_READ(map->dpll); /* Wait for the clocks to stabilize. */ udelay(150); } /* Enable the pipe */ - temp = REG_READ(pipeconf_reg); + temp = REG_READ(map->conf); if ((temp & PIPEACONF_ENABLE) == 0) - REG_WRITE(pipeconf_reg, temp | PIPEACONF_ENABLE); + REG_WRITE(map->conf, temp | PIPEACONF_ENABLE); /* Enable the plane */ - temp = REG_READ(dspcntr_reg); + temp = REG_READ(map->cntr); if ((temp & DISPLAY_PLANE_ENABLE) == 0) { - REG_WRITE(dspcntr_reg, + REG_WRITE(map->cntr, temp | DISPLAY_PLANE_ENABLE); /* Flush the plane changes */ - REG_WRITE(dspbase_reg, REG_READ(dspbase_reg)); + REG_WRITE(map->base, REG_READ(map->base)); } psb_intel_crtc_load_lut(crtc); @@ -223,28 +221,28 @@ static void oaktrail_crtc_dpms(struct drm_crtc *crtc, int mode) /* Disable the VGA plane that we never use */ REG_WRITE(VGACNTRL, VGA_DISP_DISABLE); /* Disable display plane */ - temp = REG_READ(dspcntr_reg); + temp = REG_READ(map->cntr); if ((temp & DISPLAY_PLANE_ENABLE) != 0) { - REG_WRITE(dspcntr_reg, + REG_WRITE(map->cntr, temp & ~DISPLAY_PLANE_ENABLE); /* Flush the plane changes */ - REG_WRITE(dspbase_reg, REG_READ(dspbase_reg)); - REG_READ(dspbase_reg); + REG_WRITE(map->base, REG_READ(map->base)); + REG_READ(map->base); } /* Next, disable display pipes */ - temp = REG_READ(pipeconf_reg); + temp = REG_READ(map->conf); if ((temp & PIPEACONF_ENABLE) != 0) { - REG_WRITE(pipeconf_reg, temp & ~PIPEACONF_ENABLE); - REG_READ(pipeconf_reg); + REG_WRITE(map->conf, temp & ~PIPEACONF_ENABLE); + REG_READ(map->conf); } /* Wait for for the pipe disable to take effect. */ psb_intel_wait_for_vblank(dev); - temp = REG_READ(dpll_reg); + temp = REG_READ(map->dpll); if ((temp & DPLL_VCO_ENABLE) != 0) { - REG_WRITE(dpll_reg, temp & ~DPLL_VCO_ENABLE); - REG_READ(dpll_reg); + REG_WRITE(map->dpll, temp & ~DPLL_VCO_ENABLE); + REG_READ(map->dpll); } /* Wait for the clocks to turn off. */ @@ -292,17 +290,7 @@ static int oaktrail_crtc_mode_set(struct drm_crtc *crtc, struct psb_intel_crtc *psb_intel_crtc = to_psb_intel_crtc(crtc); struct drm_psb_private *dev_priv = dev->dev_private; int pipe = psb_intel_crtc->pipe; - int fp_reg = (pipe == 0) ? MRST_FPA0 : FPB0; - int dpll_reg = (pipe == 0) ? MRST_DPLL_A : DPLL_B; - int dspcntr_reg = (pipe == 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 pipesrc_reg = (pipe == 0) ? PIPEASRC : PIPEBSRC; + const struct psb_offset *map = &dev_priv->regmap[pipe]; int refclk = 0; struct oaktrail_clock_t clock; u32 dpll = 0, fp = 0, dspcntr, pipeconf; @@ -350,7 +338,7 @@ static int oaktrail_crtc_mode_set(struct drm_crtc *crtc, if (oaktrail_panel_fitter_pipe(dev) == pipe) REG_WRITE(PFIT_CONTROL, 0); - REG_WRITE(pipesrc_reg, + REG_WRITE(map->src, ((mode->crtc_hdisplay - 1) << 16) | (mode->crtc_vdisplay - 1)); @@ -369,34 +357,34 @@ static int oaktrail_crtc_mode_set(struct drm_crtc *crtc, offsetY = (adjusted_mode->crtc_vdisplay - mode->crtc_vdisplay) / 2; - REG_WRITE(htot_reg, (mode->crtc_hdisplay - 1) | + REG_WRITE(map->htotal, (mode->crtc_hdisplay - 1) | ((adjusted_mode->crtc_htotal - 1) << 16)); - REG_WRITE(vtot_reg, (mode->crtc_vdisplay - 1) | + REG_WRITE(map->vtotal, (mode->crtc_vdisplay - 1) | ((adjusted_mode->crtc_vtotal - 1) << 16)); - REG_WRITE(hblank_reg, + REG_WRITE(map->hblank, (adjusted_mode->crtc_hblank_start - offsetX - 1) | ((adjusted_mode->crtc_hblank_end - offsetX - 1) << 16)); - REG_WRITE(hsync_reg, + REG_WRITE(map->hsync, (adjusted_mode->crtc_hsync_start - offsetX - 1) | ((adjusted_mode->crtc_hsync_end - offsetX - 1) << 16)); - REG_WRITE(vblank_reg, + REG_WRITE(map->vblank, (adjusted_mode->crtc_vblank_start - offsetY - 1) | ((adjusted_mode->crtc_vblank_end - offsetY - 1) << 16)); - REG_WRITE(vsync_reg, + REG_WRITE(map->vsync, (adjusted_mode->crtc_vsync_start - offsetY - 1) | ((adjusted_mode->crtc_vsync_end - offsetY - 1) << 16)); } else { - REG_WRITE(htot_reg, (adjusted_mode->crtc_hdisplay - 1) | + REG_WRITE(map->htotal, (adjusted_mode->crtc_hdisplay - 1) | ((adjusted_mode->crtc_htotal - 1) << 16)); - REG_WRITE(vtot_reg, (adjusted_mode->crtc_vdisplay - 1) | + REG_WRITE(map->vtotal, (adjusted_mode->crtc_vdisplay - 1) | ((adjusted_mode->crtc_vtotal - 1) << 16)); - REG_WRITE(hblank_reg, (adjusted_mode->crtc_hblank_start - 1) | + REG_WRITE(map->hblank, (adjusted_mode->crtc_hblank_start - 1) | ((adjusted_mode->crtc_hblank_end - 1) << 16)); - REG_WRITE(hsync_reg, (adjusted_mode->crtc_hsync_start - 1) | + REG_WRITE(map->hsync, (adjusted_mode->crtc_hsync_start - 1) | ((adjusted_mode->crtc_hsync_end - 1) << 16)); - REG_WRITE(vblank_reg, (adjusted_mode->crtc_vblank_start - 1) | + REG_WRITE(map->vblank, (adjusted_mode->crtc_vblank_start - 1) | ((adjusted_mode->crtc_vblank_end - 1) << 16)); - REG_WRITE(vsync_reg, (adjusted_mode->crtc_vsync_start - 1) | + REG_WRITE(map->vsync, (adjusted_mode->crtc_vsync_start - 1) | ((adjusted_mode->crtc_vsync_end - 1) << 16)); } @@ -408,10 +396,10 @@ static int oaktrail_crtc_mode_set(struct drm_crtc *crtc, } /* setup pipeconf */ - pipeconf = REG_READ(pipeconf_reg); + pipeconf = REG_READ(map->conf); /* Set up the display plane register */ - dspcntr = REG_READ(dspcntr_reg); + dspcntr = REG_READ(map->cntr); dspcntr |= DISPPLANE_GAMMA_ENABLE; if (pipe == 0) @@ -467,30 +455,30 @@ static int oaktrail_crtc_mode_set(struct drm_crtc *crtc, mrstPrintPll("chosen", &clock); if (dpll & DPLL_VCO_ENABLE) { - REG_WRITE(fp_reg, fp); - REG_WRITE(dpll_reg, dpll & ~DPLL_VCO_ENABLE); - REG_READ(dpll_reg); + REG_WRITE(map->fp0, fp); + REG_WRITE(map->dpll, dpll & ~DPLL_VCO_ENABLE); + REG_READ(map->dpll); /* Check the DPLLA lock bit PIPEACONF[29] */ udelay(150); } - REG_WRITE(fp_reg, fp); - REG_WRITE(dpll_reg, dpll); - REG_READ(dpll_reg); + REG_WRITE(map->fp0, fp); + REG_WRITE(map->dpll, dpll); + REG_READ(map->dpll); /* Wait for the clocks to stabilize. */ udelay(150); /* write it again -- the BIOS does, after all */ - REG_WRITE(dpll_reg, dpll); - REG_READ(dpll_reg); + REG_WRITE(map->dpll, dpll); + REG_READ(map->dpll); /* Wait for the clocks to stabilize. */ udelay(150); - REG_WRITE(pipeconf_reg, pipeconf); - REG_READ(pipeconf_reg); + REG_WRITE(map->conf, pipeconf); + REG_READ(map->conf); psb_intel_wait_for_vblank(dev); - REG_WRITE(dspcntr_reg, dspcntr); + REG_WRITE(map->cntr, dspcntr); psb_intel_wait_for_vblank(dev); oaktrail_crtc_mode_set_exit: @@ -509,15 +497,13 @@ static int oaktrail_pipe_set_base(struct drm_crtc *crtc, int x, int y, struct drm_framebuffer *old_fb) { struct drm_device *dev = crtc->dev; + struct drm_psb_private *dev_priv = dev->dev_private; struct psb_intel_crtc *psb_intel_crtc = to_psb_intel_crtc(crtc); struct psb_framebuffer *psbfb = to_psb_fb(crtc->fb); int pipe = psb_intel_crtc->pipe; + const struct psb_offset *map = &dev_priv->regmap[pipe]; unsigned long start, offset; - int dspbase = (pipe == 0 ? DSPALINOFF : DSPBBASE); - int dspsurf = (pipe == 0 ? DSPASURF : DSPBSURF); - int dspstride = (pipe == 0) ? DSPASTRIDE : DSPBSTRIDE; - int dspcntr_reg = (pipe == 0) ? DSPACNTR : DSPBCNTR; u32 dspcntr; int ret = 0; @@ -533,9 +519,9 @@ static int oaktrail_pipe_set_base(struct drm_crtc *crtc, start = psbfb->gtt->offset; offset = y * crtc->fb->pitches[0] + x * (crtc->fb->bits_per_pixel / 8); - REG_WRITE(dspstride, crtc->fb->pitches[0]); + REG_WRITE(map->stride, crtc->fb->pitches[0]); - dspcntr = REG_READ(dspcntr_reg); + dspcntr = REG_READ(map->cntr); dspcntr &= ~DISPPLANE_PIXFORMAT_MASK; switch (crtc->fb->bits_per_pixel) { @@ -557,12 +543,12 @@ static int oaktrail_pipe_set_base(struct drm_crtc *crtc, ret = -EINVAL; goto pipe_set_base_exit; } - REG_WRITE(dspcntr_reg, dspcntr); + REG_WRITE(map->cntr, dspcntr); - REG_WRITE(dspbase, offset); - REG_READ(dspbase); - REG_WRITE(dspsurf, start); - REG_READ(dspsurf); + REG_WRITE(map->base, offset); + REG_READ(map->base); + REG_WRITE(map->surf, start); + REG_READ(map->surf); pipe_set_base_exit: gma_power_end(dev); diff --git a/drivers/gpu/drm/gma500/oaktrail_device.c b/drivers/gpu/drm/gma500/oaktrail_device.c index 0bb74cc..0f9b7db 100644 --- a/drivers/gpu/drm/gma500/oaktrail_device.c +++ b/drivers/gpu/drm/gma500/oaktrail_device.c @@ -187,6 +187,7 @@ static int oaktrail_save_display_registers(struct drm_device *dev) { struct drm_psb_private *dev_priv = dev->dev_private; struct psb_save_area *regs = &dev_priv->regs; + struct psb_pipe *p = ®s->pipe[0]; int i; u32 pp_stat; @@ -201,24 +202,24 @@ static int oaktrail_save_display_registers(struct drm_device *dev) regs->psb.saveCHICKENBIT = PSB_RVDC32(DSPCHICKENBIT); /* Pipe & plane A info */ - regs->psb.savePIPEACONF = PSB_RVDC32(PIPEACONF); - regs->psb.savePIPEASRC = PSB_RVDC32(PIPEASRC); - regs->psb.saveFPA0 = PSB_RVDC32(MRST_FPA0); - regs->psb.saveFPA1 = PSB_RVDC32(MRST_FPA1); - regs->psb.saveDPLL_A = PSB_RVDC32(MRST_DPLL_A); - regs->psb.saveHTOTAL_A = PSB_RVDC32(HTOTAL_A); - regs->psb.saveHBLANK_A = PSB_RVDC32(HBLANK_A); - regs->psb.saveHSYNC_A = PSB_RVDC32(HSYNC_A); - regs->psb.saveVTOTAL_A = PSB_RVDC32(VTOTAL_A); - regs->psb.saveVBLANK_A = PSB_RVDC32(VBLANK_A); - regs->psb.saveVSYNC_A = PSB_RVDC32(VSYNC_A); + p->conf = PSB_RVDC32(PIPEACONF); + p->src = PSB_RVDC32(PIPEASRC); + p->fp0 = PSB_RVDC32(MRST_FPA0); + p->fp1 = PSB_RVDC32(MRST_FPA1); + p->dpll = PSB_RVDC32(MRST_DPLL_A); + p->htotal = PSB_RVDC32(HTOTAL_A); + p->hblank = PSB_RVDC32(HBLANK_A); + p->hsync = PSB_RVDC32(HSYNC_A); + p->vtotal = PSB_RVDC32(VTOTAL_A); + p->vblank = PSB_RVDC32(VBLANK_A); + p->vsync = PSB_RVDC32(VSYNC_A); regs->psb.saveBCLRPAT_A = PSB_RVDC32(BCLRPAT_A); - regs->psb.saveDSPACNTR = PSB_RVDC32(DSPACNTR); - regs->psb.saveDSPASTRIDE = PSB_RVDC32(DSPASTRIDE); - regs->psb.saveDSPAADDR = PSB_RVDC32(DSPABASE); - regs->psb.saveDSPASURF = PSB_RVDC32(DSPASURF); - regs->psb.saveDSPALINOFF = PSB_RVDC32(DSPALINOFF); - regs->psb.saveDSPATILEOFF = PSB_RVDC32(DSPATILEOFF); + p->cntr = PSB_RVDC32(DSPACNTR); + p->stride = PSB_RVDC32(DSPASTRIDE); + p->addr = PSB_RVDC32(DSPABASE); + p->surf = PSB_RVDC32(DSPASURF); + p->linoff = PSB_RVDC32(DSPALINOFF); + p->tileoff = PSB_RVDC32(DSPATILEOFF); /* Save cursor regs */ regs->psb.saveDSPACURSOR_CTRL = PSB_RVDC32(CURACNTR); @@ -227,7 +228,7 @@ static int oaktrail_save_display_registers(struct drm_device *dev) /* Save palette (gamma) */ for (i = 0; i < 256; i++) - regs->psb.save_palette_a[i] = PSB_RVDC32(PALETTE_A + (i << 2)); + p->palette[i] = PSB_RVDC32(PALETTE_A + (i << 2)); if (dev_priv->hdmi_priv) oaktrail_hdmi_save(dev); @@ -300,6 +301,7 @@ static int oaktrail_restore_display_registers(struct drm_device *dev) { struct drm_psb_private *dev_priv = dev->dev_private; struct psb_save_area *regs = &dev_priv->regs; + struct psb_pipe *p = ®s->pipe[0]; u32 pp_stat; int i; @@ -317,21 +319,21 @@ static int oaktrail_restore_display_registers(struct drm_device *dev) PSB_WVDC32(0x80000000, VGACNTRL); /* set the plls */ - PSB_WVDC32(regs->psb.saveFPA0, MRST_FPA0); - PSB_WVDC32(regs->psb.saveFPA1, MRST_FPA1); + PSB_WVDC32(p->fp0, MRST_FPA0); + PSB_WVDC32(p->fp1, MRST_FPA1); /* Actually enable it */ - PSB_WVDC32(regs->psb.saveDPLL_A, MRST_DPLL_A); + PSB_WVDC32(p->dpll, MRST_DPLL_A); DRM_UDELAY(150); /* Restore mode */ - PSB_WVDC32(regs->psb.saveHTOTAL_A, HTOTAL_A); - PSB_WVDC32(regs->psb.saveHBLANK_A, HBLANK_A); - PSB_WVDC32(regs->psb.saveHSYNC_A, HSYNC_A); - PSB_WVDC32(regs->psb.saveVTOTAL_A, VTOTAL_A); - PSB_WVDC32(regs->psb.saveVBLANK_A, VBLANK_A); - PSB_WVDC32(regs->psb.saveVSYNC_A, VSYNC_A); - PSB_WVDC32(regs->psb.savePIPEASRC, PIPEASRC); + PSB_WVDC32(p->htotal, HTOTAL_A); + PSB_WVDC32(p->hblank, HBLANK_A); + PSB_WVDC32(p->hsync, HSYNC_A); + PSB_WVDC32(p->vtotal, VTOTAL_A); + PSB_WVDC32(p->vblank, VBLANK_A); + PSB_WVDC32(p->vsync, VSYNC_A); + PSB_WVDC32(p->src, PIPEASRC); PSB_WVDC32(regs->psb.saveBCLRPAT_A, BCLRPAT_A); /* Restore performance mode*/ @@ -339,16 +341,16 @@ static int oaktrail_restore_display_registers(struct drm_device *dev) /* Enable the pipe*/ if (dev_priv->iLVDS_enable) - PSB_WVDC32(regs->psb.savePIPEACONF, PIPEACONF); + PSB_WVDC32(p->conf, PIPEACONF); /* Set up the plane*/ - PSB_WVDC32(regs->psb.saveDSPALINOFF, DSPALINOFF); - PSB_WVDC32(regs->psb.saveDSPASTRIDE, DSPASTRIDE); - PSB_WVDC32(regs->psb.saveDSPATILEOFF, DSPATILEOFF); + PSB_WVDC32(p->linoff, DSPALINOFF); + PSB_WVDC32(p->stride, DSPASTRIDE); + PSB_WVDC32(p->tileoff, DSPATILEOFF); /* Enable the plane */ - PSB_WVDC32(regs->psb.saveDSPACNTR, DSPACNTR); - PSB_WVDC32(regs->psb.saveDSPASURF, DSPASURF); + PSB_WVDC32(p->cntr, DSPACNTR); + PSB_WVDC32(p->surf, DSPASURF); /* Enable Cursor A */ PSB_WVDC32(regs->psb.saveDSPACURSOR_CTRL, CURACNTR); @@ -357,7 +359,7 @@ static int oaktrail_restore_display_registers(struct drm_device *dev) /* Restore palette (gamma) */ for (i = 0; i < 256; i++) - PSB_WVDC32(regs->psb.save_palette_a[i], PALETTE_A + (i << 2)); + PSB_WVDC32(p->palette[i], PALETTE_A + (i << 2)); if (dev_priv->hdmi_priv) oaktrail_hdmi_restore(dev); @@ -454,11 +456,65 @@ static int oaktrail_power_up(struct drm_device *dev) return 0; } +/* Oaktrail */ +static const struct psb_offset oaktrail_regmap[2] = { + { + .fp0 = MRST_FPA0, + .fp1 = MRST_FPA1, + .cntr = DSPACNTR, + .conf = PIPEACONF, + .src = PIPEASRC, + .dpll = MRST_DPLL_A, + .htotal = HTOTAL_A, + .hblank = HBLANK_A, + .hsync = HSYNC_A, + .vtotal = VTOTAL_A, + .vblank = VBLANK_A, + .vsync = VSYNC_A, + .stride = DSPASTRIDE, + .size = DSPASIZE, + .pos = DSPAPOS, + .surf = DSPASURF, + .addr = MRST_DSPABASE, + .status = PIPEASTAT, + .linoff = DSPALINOFF, + .tileoff = DSPATILEOFF, + .palette = PALETTE_A, + }, + { + .fp0 = FPB0, + .fp1 = FPB1, + .cntr = DSPBCNTR, + .conf = PIPEBCONF, + .src = PIPEBSRC, + .dpll = DPLL_B, + .htotal = HTOTAL_B, + .hblank = HBLANK_B, + .hsync = HSYNC_B, + .vtotal = VTOTAL_B, + .vblank = VBLANK_B, + .vsync = VSYNC_B, + .stride = DSPBSTRIDE, + .size = DSPBSIZE, + .pos = DSPBPOS, + .surf = DSPBSURF, + .addr = DSPBBASE, + .status = PIPEBSTAT, + .linoff = DSPBLINOFF, + .tileoff = DSPBTILEOFF, + .palette = PALETTE_B, + }, +}; static int oaktrail_chip_setup(struct drm_device *dev) { struct drm_psb_private *dev_priv = dev->dev_private; int ret; + + if (pci_enable_msi(dev->pdev)) + dev_warn(dev->dev, "Enabling MSI failed!\n"); + + dev_priv->regmap = oaktrail_regmap; ret = mid_chip_setup(dev); if (ret < 0) @@ -488,6 +544,7 @@ const struct psb_ops oaktrail_chip_ops = { .crtcs = 2, .hdmi_mask = (1 << 0), .lvds_mask = (1 << 0), + .cursor_needs_phys = 0, .sgx_offset = MRST_SGX_OFFSET, .chip_setup = oaktrail_chip_setup, diff --git a/drivers/gpu/drm/gma500/oaktrail_hdmi.c b/drivers/gpu/drm/gma500/oaktrail_hdmi.c index 2595660..c10899c 100644 --- a/drivers/gpu/drm/gma500/oaktrail_hdmi.c +++ b/drivers/gpu/drm/gma500/oaktrail_hdmi.c @@ -434,6 +434,7 @@ void oaktrail_hdmi_save(struct drm_device *dev) struct drm_psb_private *dev_priv = dev->dev_private; struct oaktrail_hdmi_dev *hdmi_dev = dev_priv->hdmi_priv; struct psb_state *regs = &dev_priv->regs.psb; + struct psb_pipe *pipeb = &dev_priv->regs.pipe[1]; int i; /* dpll */ @@ -444,14 +445,14 @@ void oaktrail_hdmi_save(struct drm_device *dev) hdmi_dev->saveDPLL_CLK_ENABLE = PSB_RVDC32(DPLL_CLK_ENABLE); /* pipe B */ - regs->savePIPEBCONF = PSB_RVDC32(PIPEBCONF); - regs->savePIPEBSRC = PSB_RVDC32(PIPEBSRC); - regs->saveHTOTAL_B = PSB_RVDC32(HTOTAL_B); - regs->saveHBLANK_B = PSB_RVDC32(HBLANK_B); - regs->saveHSYNC_B = PSB_RVDC32(HSYNC_B); - regs->saveVTOTAL_B = PSB_RVDC32(VTOTAL_B); - regs->saveVBLANK_B = PSB_RVDC32(VBLANK_B); - regs->saveVSYNC_B = PSB_RVDC32(VSYNC_B); + pipeb->conf = PSB_RVDC32(PIPEBCONF); + pipeb->src = PSB_RVDC32(PIPEBSRC); + pipeb->htotal = PSB_RVDC32(HTOTAL_B); + pipeb->hblank = PSB_RVDC32(HBLANK_B); + pipeb->hsync = PSB_RVDC32(HSYNC_B); + pipeb->vtotal = PSB_RVDC32(VTOTAL_B); + pipeb->vblank = PSB_RVDC32(VBLANK_B); + pipeb->vsync = PSB_RVDC32(VSYNC_B); hdmi_dev->savePCH_PIPEBCONF = PSB_RVDC32(PCH_PIPEBCONF); hdmi_dev->savePCH_PIPEBSRC = PSB_RVDC32(PCH_PIPEBSRC); @@ -463,12 +464,12 @@ void oaktrail_hdmi_save(struct drm_device *dev) hdmi_dev->savePCH_VSYNC_B = PSB_RVDC32(PCH_VSYNC_B); /* plane */ - regs->saveDSPBCNTR = PSB_RVDC32(DSPBCNTR); - regs->saveDSPBSTRIDE = PSB_RVDC32(DSPBSTRIDE); - regs->saveDSPBADDR = PSB_RVDC32(DSPBBASE); - regs->saveDSPBSURF = PSB_RVDC32(DSPBSURF); - regs->saveDSPBLINOFF = PSB_RVDC32(DSPBLINOFF); - regs->saveDSPBTILEOFF = PSB_RVDC32(DSPBTILEOFF); + pipeb->cntr = PSB_RVDC32(DSPBCNTR); + pipeb->stride = PSB_RVDC32(DSPBSTRIDE); + pipeb->addr = PSB_RVDC32(DSPBBASE); + pipeb->surf = PSB_RVDC32(DSPBSURF); + pipeb->linoff = PSB_RVDC32(DSPBLINOFF); + pipeb->tileoff = PSB_RVDC32(DSPBTILEOFF); /* cursor B */ regs->saveDSPBCURSOR_CTRL = PSB_RVDC32(CURBCNTR); @@ -477,7 +478,7 @@ void oaktrail_hdmi_save(struct drm_device *dev) /* save palette */ for (i = 0; i < 256; i++) - regs->save_palette_b[i] = PSB_RVDC32(PALETTE_B + (i << 2)); + pipeb->palette[i] = PSB_RVDC32(PALETTE_B + (i << 2)); } /* restore HDMI register state */ @@ -486,6 +487,7 @@ void oaktrail_hdmi_restore(struct drm_device *dev) struct drm_psb_private *dev_priv = dev->dev_private; struct oaktrail_hdmi_dev *hdmi_dev = dev_priv->hdmi_priv; struct psb_state *regs = &dev_priv->regs.psb; + struct psb_pipe *pipeb = &dev_priv->regs.pipe[1]; int i; /* dpll */ @@ -497,13 +499,13 @@ void oaktrail_hdmi_restore(struct drm_device *dev) DRM_UDELAY(150); /* pipe */ - PSB_WVDC32(regs->savePIPEBSRC, PIPEBSRC); - PSB_WVDC32(regs->saveHTOTAL_B, HTOTAL_B); - PSB_WVDC32(regs->saveHBLANK_B, HBLANK_B); - PSB_WVDC32(regs->saveHSYNC_B, HSYNC_B); - PSB_WVDC32(regs->saveVTOTAL_B, VTOTAL_B); - PSB_WVDC32(regs->saveVBLANK_B, VBLANK_B); - PSB_WVDC32(regs->saveVSYNC_B, VSYNC_B); + PSB_WVDC32(pipeb->src, PIPEBSRC); + PSB_WVDC32(pipeb->htotal, HTOTAL_B); + PSB_WVDC32(pipeb->hblank, HBLANK_B); + PSB_WVDC32(pipeb->hsync, HSYNC_B); + PSB_WVDC32(pipeb->vtotal, VTOTAL_B); + PSB_WVDC32(pipeb->vblank, VBLANK_B); + PSB_WVDC32(pipeb->vsync, VSYNC_B); PSB_WVDC32(hdmi_dev->savePCH_PIPEBSRC, PCH_PIPEBSRC); PSB_WVDC32(hdmi_dev->savePCH_HTOTAL_B, PCH_HTOTAL_B); @@ -513,15 +515,15 @@ void oaktrail_hdmi_restore(struct drm_device *dev) PSB_WVDC32(hdmi_dev->savePCH_VBLANK_B, PCH_VBLANK_B); PSB_WVDC32(hdmi_dev->savePCH_VSYNC_B, PCH_VSYNC_B); - PSB_WVDC32(regs->savePIPEBCONF, PIPEBCONF); + PSB_WVDC32(pipeb->conf, PIPEBCONF); PSB_WVDC32(hdmi_dev->savePCH_PIPEBCONF, PCH_PIPEBCONF); /* plane */ - PSB_WVDC32(regs->saveDSPBLINOFF, DSPBLINOFF); - PSB_WVDC32(regs->saveDSPBSTRIDE, DSPBSTRIDE); - PSB_WVDC32(regs->saveDSPBTILEOFF, DSPBTILEOFF); - PSB_WVDC32(regs->saveDSPBCNTR, DSPBCNTR); - PSB_WVDC32(regs->saveDSPBSURF, DSPBSURF); + PSB_WVDC32(pipeb->linoff, DSPBLINOFF); + PSB_WVDC32(pipeb->stride, DSPBSTRIDE); + PSB_WVDC32(pipeb->tileoff, DSPBTILEOFF); + PSB_WVDC32(pipeb->cntr, DSPBCNTR); + PSB_WVDC32(pipeb->surf, DSPBSURF); /* cursor B */ PSB_WVDC32(regs->saveDSPBCURSOR_CTRL, CURBCNTR); @@ -530,5 +532,5 @@ void oaktrail_hdmi_restore(struct drm_device *dev) /* restore palette */ for (i = 0; i < 256; i++) - PSB_WVDC32(regs->save_palette_b[i], PALETTE_B + (i << 2)); + PSB_WVDC32(pipeb->palette[i], PALETTE_B + (i << 2)); } diff --git a/drivers/gpu/drm/gma500/opregion.c b/drivers/gpu/drm/gma500/opregion.c index 05661bf..4f186ec 100644 --- a/drivers/gpu/drm/gma500/opregion.c +++ b/drivers/gpu/drm/gma500/opregion.c @@ -21,10 +21,8 @@ * DEALINGS IN THE SOFTWARE. * */ -#ifdef CONFIG_ACPI #include <linux/acpi.h> #include <linux/acpi_io.h> -#endif #include "psb_drv.h" #include "psb_intel_reg.h" @@ -151,7 +149,6 @@ static u32 asle_set_backlight(struct drm_device *dev, u32 bclp) struct drm_psb_private *dev_priv = dev->dev_private; struct opregion_asle *asle = dev_priv->opregion.asle; struct backlight_device *bd = dev_priv->backlight_device; - u32 max; DRM_DEBUG_DRIVER("asle set backlight %x\n", bclp); @@ -165,11 +162,12 @@ static u32 asle_set_backlight(struct drm_device *dev, u32 bclp) if (bclp > 255) return ASLE_BACKLIGHT_FAILED; -#ifdef CONFIG_BACKLIGHT_CLASS_DEVICE - max = bd->props.max_brightness; - bd->props.brightness = bclp * max / 255; - backlight_update_status(bd); -#endif + if (config_enabled(CONFIG_BACKLIGHT_CLASS_DEVICE)) { + int max = bd->props.max_brightness; + bd->props.brightness = bclp * max / 255; + backlight_update_status(bd); + } + asle->cblv = (bclp * 0x64) / 0xff | ASLE_CBLV_VALID; return 0; @@ -311,11 +309,7 @@ int psb_intel_opregion_setup(struct drm_device *dev) return -ENOTSUPP; } DRM_DEBUG("OpRegion detected at 0x%8x\n", opregion_phy); -#ifdef CONFIG_ACPI base = acpi_os_ioremap(opregion_phy, 8*1024); -#else - base = ioremap(opregion_phy, 8*1024); -#endif if (!base) return -ENOMEM; diff --git a/drivers/gpu/drm/gma500/opregion.h b/drivers/gpu/drm/gma500/opregion.h index a392ea8..72dc6b9 100644 --- a/drivers/gpu/drm/gma500/opregion.h +++ b/drivers/gpu/drm/gma500/opregion.h @@ -22,8 +22,28 @@ * */ +#if defined(CONFIG_ACPI) extern void psb_intel_opregion_asle_intr(struct drm_device *dev); -extern void psb_intel_opregion_enable_asle(struct drm_device *dev); extern void psb_intel_opregion_init(struct drm_device *dev); extern void psb_intel_opregion_fini(struct drm_device *dev); extern int psb_intel_opregion_setup(struct drm_device *dev); + +#else + +extern inline void psb_intel_opregion_asle_intr(struct drm_device *dev) +{ +} + +extern inline void psb_intel_opregion_init(struct drm_device *dev) +{ +} + +extern inline void psb_intel_opregion_fini(struct drm_device *dev) +{ +} + +extern inline int psb_intel_opregion_setup(struct drm_device *dev) +{ + return 0; +} +#endif diff --git a/drivers/gpu/drm/gma500/psb_device.c b/drivers/gpu/drm/gma500/psb_device.c index e95cddb..eff039b 100644 --- a/drivers/gpu/drm/gma500/psb_device.c +++ b/drivers/gpu/drm/gma500/psb_device.c @@ -197,7 +197,8 @@ static int psb_save_display_registers(struct drm_device *dev) } list_for_each_entry(connector, &dev->mode_config.connector_list, head) - connector->funcs->save(connector); + if (connector->funcs->save) + connector->funcs->save(connector); mutex_unlock(&dev->mode_config.mutex); return 0; @@ -235,7 +236,8 @@ static int psb_restore_display_registers(struct drm_device *dev) crtc->funcs->restore(crtc); list_for_each_entry(connector, &dev->mode_config.connector_list, head) - connector->funcs->restore(connector); + if (connector->funcs->restore) + connector->funcs->restore(connector); mutex_unlock(&dev->mode_config.mutex); return 0; @@ -289,8 +291,62 @@ static void psb_get_core_freq(struct drm_device *dev) } } +/* Poulsbo */ +static const struct psb_offset psb_regmap[2] = { + { + .fp0 = FPA0, + .fp1 = FPA1, + .cntr = DSPACNTR, + .conf = PIPEACONF, + .src = PIPEASRC, + .dpll = DPLL_A, + .htotal = HTOTAL_A, + .hblank = HBLANK_A, + .hsync = HSYNC_A, + .vtotal = VTOTAL_A, + .vblank = VBLANK_A, + .vsync = VSYNC_A, + .stride = DSPASTRIDE, + .size = DSPASIZE, + .pos = DSPAPOS, + .base = DSPABASE, + .surf = DSPASURF, + .addr = DSPABASE, + .status = PIPEASTAT, + .linoff = DSPALINOFF, + .tileoff = DSPATILEOFF, + .palette = PALETTE_A, + }, + { + .fp0 = FPB0, + .fp1 = FPB1, + .cntr = DSPBCNTR, + .conf = PIPEBCONF, + .src = PIPEBSRC, + .dpll = DPLL_B, + .htotal = HTOTAL_B, + .hblank = HBLANK_B, + .hsync = HSYNC_B, + .vtotal = VTOTAL_B, + .vblank = VBLANK_B, + .vsync = VSYNC_B, + .stride = DSPBSTRIDE, + .size = DSPBSIZE, + .pos = DSPBPOS, + .base = DSPBBASE, + .surf = DSPBSURF, + .addr = DSPBBASE, + .status = PIPEBSTAT, + .linoff = DSPBLINOFF, + .tileoff = DSPBTILEOFF, + .palette = PALETTE_B, + } +}; + static int psb_chip_setup(struct drm_device *dev) { + struct drm_psb_private *dev_priv = dev->dev_private; + dev_priv->regmap = psb_regmap; psb_get_core_freq(dev); gma_intel_setup_gmbus(dev); psb_intel_opregion_init(dev); @@ -298,8 +354,17 @@ static int psb_chip_setup(struct drm_device *dev) return 0; } +/* Not exactly an erratum more an irritation */ +static void psb_chip_errata(struct drm_device *dev) +{ + struct drm_psb_private *dev_priv = dev->dev_private; + psb_lid_timer_init(dev_priv); +} + static void psb_chip_teardown(struct drm_device *dev) { + struct drm_psb_private *dev_priv = dev->dev_private; + psb_lid_timer_takedown(dev_priv); gma_intel_teardown_gmbus(dev); } @@ -310,9 +375,11 @@ const struct psb_ops psb_chip_ops = { .crtcs = 2, .hdmi_mask = (1 << 0), .lvds_mask = (1 << 1), + .cursor_needs_phys = 1, .sgx_offset = PSB_SGX_OFFSET, .chip_setup = psb_chip_setup, .chip_teardown = psb_chip_teardown, + .errata = psb_chip_errata, .crtc_helper = &psb_intel_helper_funcs, .crtc_funcs = &psb_intel_crtc_funcs, diff --git a/drivers/gpu/drm/gma500/psb_drv.c b/drivers/gpu/drm/gma500/psb_drv.c index 0e85978..caba6e0 100644 --- a/drivers/gpu/drm/gma500/psb_drv.c +++ b/drivers/gpu/drm/gma500/psb_drv.c @@ -79,6 +79,14 @@ static DEFINE_PCI_DEVICE_TABLE(pciidlist) = { { 0x8086, 0x0be5, PCI_ANY_ID, PCI_ANY_ID, 0, 0, (long) &cdv_chip_ops}, { 0x8086, 0x0be6, PCI_ANY_ID, PCI_ANY_ID, 0, 0, (long) &cdv_chip_ops}, { 0x8086, 0x0be7, PCI_ANY_ID, PCI_ANY_ID, 0, 0, (long) &cdv_chip_ops}, + { 0x8086, 0x0be8, PCI_ANY_ID, PCI_ANY_ID, 0, 0, (long) &cdv_chip_ops}, + { 0x8086, 0x0be9, PCI_ANY_ID, PCI_ANY_ID, 0, 0, (long) &cdv_chip_ops}, + { 0x8086, 0x0bea, PCI_ANY_ID, PCI_ANY_ID, 0, 0, (long) &cdv_chip_ops}, + { 0x8086, 0x0beb, PCI_ANY_ID, PCI_ANY_ID, 0, 0, (long) &cdv_chip_ops}, + { 0x8086, 0x0bec, PCI_ANY_ID, PCI_ANY_ID, 0, 0, (long) &cdv_chip_ops}, + { 0x8086, 0x0bed, PCI_ANY_ID, PCI_ANY_ID, 0, 0, (long) &cdv_chip_ops}, + { 0x8086, 0x0bee, PCI_ANY_ID, PCI_ANY_ID, 0, 0, (long) &cdv_chip_ops}, + { 0x8086, 0x0bef, PCI_ANY_ID, PCI_ANY_ID, 0, 0, (long) &cdv_chip_ops}, #endif { 0, } }; @@ -144,10 +152,6 @@ static void psb_lastclose(struct drm_device *dev) return; } -static void psb_do_takedown(struct drm_device *dev) -{ -} - static int psb_do_init(struct drm_device *dev) { struct drm_psb_private *dev_priv = dev->dev_private; @@ -172,24 +176,6 @@ static int psb_do_init(struct drm_device *dev) dev_priv->gatt_free_offset = pg->mmu_gatt_start + (stolen_gtt << PAGE_SHIFT) * 1024; - if (1 || drm_debug) { - uint32_t core_id = PSB_RSGX32(PSB_CR_CORE_ID); - uint32_t core_rev = PSB_RSGX32(PSB_CR_CORE_REVISION); - DRM_INFO("SGX core id = 0x%08x\n", core_id); - DRM_INFO("SGX core rev major = 0x%02x, minor = 0x%02x\n", - (core_rev & _PSB_CC_REVISION_MAJOR_MASK) >> - _PSB_CC_REVISION_MAJOR_SHIFT, - (core_rev & _PSB_CC_REVISION_MINOR_MASK) >> - _PSB_CC_REVISION_MINOR_SHIFT); - DRM_INFO - ("SGX core rev maintenance = 0x%02x, designer = 0x%02x\n", - (core_rev & _PSB_CC_REVISION_MAINTENANCE_MASK) >> - _PSB_CC_REVISION_MAINTENANCE_SHIFT, - (core_rev & _PSB_CC_REVISION_DESIGNER_MASK) >> - _PSB_CC_REVISION_DESIGNER_SHIFT); - } - - spin_lock_init(&dev_priv->irqmask_lock); spin_lock_init(&dev_priv->lock_2d); @@ -204,7 +190,6 @@ static int psb_do_init(struct drm_device *dev) PSB_WSGX32(pg->gatt_start, PSB_CR_BIF_TWOD_REQ_BASE); return 0; out_err: - psb_do_takedown(dev); return ret; } @@ -214,17 +199,16 @@ static int psb_driver_unload(struct drm_device *dev) /* Kill vblank etc here */ - gma_backlight_exit(dev); - psb_modeset_cleanup(dev); if (dev_priv) { - psb_intel_opregion_fini(dev); - psb_lid_timer_takedown(dev_priv); + if (dev_priv->backlight_device) + gma_backlight_exit(dev); + psb_modeset_cleanup(dev); if (dev_priv->ops->chip_teardown) dev_priv->ops->chip_teardown(dev); - psb_do_takedown(dev); + psb_intel_opregion_fini(dev); if (dev_priv->pf_pd) { psb_mmu_free_pagedir(dev_priv->pf_pd); @@ -258,15 +242,13 @@ static int psb_driver_unload(struct drm_device *dev) dev_priv->sgx_reg = NULL; } + /* Destroy VBT data */ + psb_intel_destroy_bios(dev); + kfree(dev_priv); dev->dev_private = NULL; - - /*destroy VBT data*/ - psb_intel_destroy_bios(dev); } - gma_power_uninit(dev); - return 0; } @@ -290,11 +272,6 @@ static int psb_driver_load(struct drm_device *dev, unsigned long chipset) pci_set_master(dev->pdev); - if (!IS_PSB(dev)) { - if (pci_enable_msi(dev->pdev)) - dev_warn(dev->dev, "Enabling MSI failed!\n"); - } - dev_priv->num_pipe = dev_priv->ops->pipes; resource_start = pci_resource_start(dev->pdev, PSB_MMIO_RESOURCE); @@ -351,8 +328,6 @@ static int psb_driver_load(struct drm_device *dev, unsigned long chipset) PSB_WSGX32(0x30000000, PSB_CR_BIF_3D_REQ_BASE); acpi_video_register(); - if (dev_priv->opregion.lid_state) - psb_lid_timer_init(dev_priv); ret = drm_vblank_init(dev, dev_priv->num_pipe); if (ret) @@ -371,8 +346,8 @@ static int psb_driver_load(struct drm_device *dev, unsigned long chipset) PSB_WVDC32(0x00000000, PSB_INT_ENABLE_R); PSB_WVDC32(0xFFFFFFFF, PSB_INT_MASK_R); spin_unlock_irqrestore(&dev_priv->irqmask_lock, irqflags); - if (IS_PSB(dev) && drm_core_check_feature(dev, DRIVER_MODESET)) - drm_irq_install(dev); + + drm_irq_install(dev); dev->vblank_disable_allowed = 1; @@ -620,7 +595,7 @@ static const struct dev_pm_ops psb_pm_ops = { .runtime_idle = psb_runtime_idle, }; -static struct vm_operations_struct psb_gem_vm_ops = { +static const struct vm_operations_struct psb_gem_vm_ops = { .fault = psb_gem_fault, .open = drm_gem_vm_open, .close = drm_gem_vm_close, diff --git a/drivers/gpu/drm/gma500/psb_drv.h b/drivers/gpu/drm/gma500/psb_drv.h index 270a27b..1bd115e 100644 --- a/drivers/gpu/drm/gma500/psb_drv.h +++ b/drivers/gpu/drm/gma500/psb_drv.h @@ -281,50 +281,72 @@ struct intel_gmbus { }; /* + * Register offset maps + */ + +struct psb_offset { + u32 fp0; + u32 fp1; + u32 cntr; + u32 conf; + u32 src; + u32 dpll; + u32 dpll_md; + u32 htotal; + u32 hblank; + u32 hsync; + u32 vtotal; + u32 vblank; + u32 vsync; + u32 stride; + u32 size; + u32 pos; + u32 surf; + u32 addr; + u32 base; + u32 status; + u32 linoff; + u32 tileoff; + u32 palette; +}; + +/* * Register save state. This is used to hold the context when the * device is powered off. In the case of Oaktrail this can (but does not * yet) include screen blank. Operations occuring during the save * update the register cache instead. */ + +/* + * Common status for pipes. + */ +struct psb_pipe { + u32 fp0; + u32 fp1; + u32 cntr; + u32 conf; + u32 src; + u32 dpll; + u32 dpll_md; + u32 htotal; + u32 hblank; + u32 hsync; + u32 vtotal; + u32 vblank; + u32 vsync; + u32 stride; + u32 size; + u32 pos; + u32 base; + u32 surf; + u32 addr; + u32 status; + u32 linoff; + u32 tileoff; + u32 palette[256]; +}; + struct psb_state { - uint32_t saveDSPACNTR; - uint32_t saveDSPBCNTR; - uint32_t savePIPEACONF; - uint32_t savePIPEBCONF; - uint32_t savePIPEASRC; - uint32_t savePIPEBSRC; - uint32_t saveFPA0; - uint32_t saveFPA1; - uint32_t saveDPLL_A; - uint32_t saveDPLL_A_MD; - uint32_t saveHTOTAL_A; - uint32_t saveHBLANK_A; - uint32_t saveHSYNC_A; - uint32_t saveVTOTAL_A; - uint32_t saveVBLANK_A; - uint32_t saveVSYNC_A; - uint32_t saveDSPASTRIDE; - uint32_t saveDSPASIZE; - uint32_t saveDSPAPOS; - uint32_t saveDSPABASE; - uint32_t saveDSPASURF; - uint32_t saveDSPASTATUS; - uint32_t saveFPB0; - uint32_t saveFPB1; - uint32_t saveDPLL_B; - uint32_t saveDPLL_B_MD; - uint32_t saveHTOTAL_B; - uint32_t saveHBLANK_B; - uint32_t saveHSYNC_B; - uint32_t saveVTOTAL_B; - uint32_t saveVBLANK_B; - uint32_t saveVSYNC_B; - uint32_t saveDSPBSTRIDE; - uint32_t saveDSPBSIZE; - uint32_t saveDSPBPOS; - uint32_t saveDSPBBASE; - uint32_t saveDSPBSURF; - uint32_t saveDSPBSTATUS; uint32_t saveVCLK_DIVISOR_VGA0; uint32_t saveVCLK_DIVISOR_VGA1; uint32_t saveVCLK_POST_DIV; @@ -339,14 +361,8 @@ struct psb_state { uint32_t savePP_CONTROL; uint32_t savePP_CYCLE; uint32_t savePFIT_CONTROL; - uint32_t savePaletteA[256]; - uint32_t savePaletteB[256]; uint32_t saveCLOCKGATING; uint32_t saveDSPARB; - uint32_t saveDSPATILEOFF; - uint32_t saveDSPBTILEOFF; - uint32_t saveDSPAADDR; - uint32_t saveDSPBADDR; uint32_t savePFIT_AUTO_RATIOS; uint32_t savePFIT_PGM_RATIOS; uint32_t savePP_ON_DELAYS; @@ -354,8 +370,6 @@ struct psb_state { uint32_t savePP_DIVISOR; uint32_t saveBCLRPAT_A; uint32_t saveBCLRPAT_B; - uint32_t saveDSPALINOFF; - uint32_t saveDSPBLINOFF; uint32_t savePERF_MODE; uint32_t saveDSPFW1; uint32_t saveDSPFW2; @@ -370,8 +384,6 @@ struct psb_state { uint32_t saveDSPBCURSOR_BASE; uint32_t saveDSPACURSOR_POS; uint32_t saveDSPBCURSOR_POS; - uint32_t save_palette_a[256]; - uint32_t save_palette_b[256]; uint32_t saveOV_OVADD; uint32_t saveOV_OGAMC0; uint32_t saveOV_OGAMC1; @@ -394,64 +406,7 @@ struct psb_state { }; struct medfield_state { - uint32_t saveDPLL_A; - uint32_t saveFPA0; - uint32_t savePIPEACONF; - uint32_t saveHTOTAL_A; - uint32_t saveHBLANK_A; - uint32_t saveHSYNC_A; - uint32_t saveVTOTAL_A; - uint32_t saveVBLANK_A; - uint32_t saveVSYNC_A; - uint32_t savePIPEASRC; - uint32_t saveDSPASTRIDE; - uint32_t saveDSPALINOFF; - uint32_t saveDSPATILEOFF; - uint32_t saveDSPASIZE; - uint32_t saveDSPAPOS; - uint32_t saveDSPASURF; - uint32_t saveDSPACNTR; - uint32_t saveDSPASTATUS; - uint32_t save_palette_a[256]; uint32_t saveMIPI; - - uint32_t saveDPLL_B; - uint32_t saveFPB0; - uint32_t savePIPEBCONF; - uint32_t saveHTOTAL_B; - uint32_t saveHBLANK_B; - uint32_t saveHSYNC_B; - uint32_t saveVTOTAL_B; - uint32_t saveVBLANK_B; - uint32_t saveVSYNC_B; - uint32_t savePIPEBSRC; - uint32_t saveDSPBSTRIDE; - uint32_t saveDSPBLINOFF; - uint32_t saveDSPBTILEOFF; - uint32_t saveDSPBSIZE; - uint32_t saveDSPBPOS; - uint32_t saveDSPBSURF; - uint32_t saveDSPBCNTR; - uint32_t saveDSPBSTATUS; - uint32_t save_palette_b[256]; - - uint32_t savePIPECCONF; - uint32_t saveHTOTAL_C; - uint32_t saveHBLANK_C; - uint32_t saveHSYNC_C; - uint32_t saveVTOTAL_C; - uint32_t saveVBLANK_C; - uint32_t saveVSYNC_C; - uint32_t savePIPECSRC; - uint32_t saveDSPCSTRIDE; - uint32_t saveDSPCLINOFF; - uint32_t saveDSPCTILEOFF; - uint32_t saveDSPCSIZE; - uint32_t saveDSPCPOS; - uint32_t saveDSPCSURF; - uint32_t saveDSPCCNTR; - uint32_t saveDSPCSTATUS; - uint32_t save_palette_c[256]; uint32_t saveMIPI_C; uint32_t savePFIT_CONTROL; @@ -480,6 +435,7 @@ struct cdv_state { }; struct psb_save_area { + struct psb_pipe pipe[3]; uint32_t saveBSM; uint32_t saveVBT; union { @@ -498,6 +454,7 @@ struct psb_ops; struct drm_psb_private { struct drm_device *dev; const struct psb_ops *ops; + const struct psb_offset *regmap; struct child_device_config *child_dev; int child_dev_num; @@ -550,6 +507,7 @@ struct drm_psb_private { * Modesetting */ struct psb_intel_mode_device mode_dev; + bool modeset; /* true if we have done the mode_device setup */ struct drm_crtc *plane_to_crtc_mapping[PSB_NUM_PIPE]; struct drm_crtc *pipe_to_crtc_mapping[PSB_NUM_PIPE]; @@ -697,6 +655,7 @@ struct psb_ops { int sgx_offset; /* Base offset of SGX device */ int hdmi_mask; /* Mask of HDMI CRTCs */ int lvds_mask; /* Mask of LVDS CRTCs */ + int cursor_needs_phys; /* If cursor base reg need physical address */ /* Sub functions */ struct drm_crtc_helper_funcs const *crtc_helper; @@ -808,12 +767,6 @@ psb_disable_pipestat(struct drm_psb_private *dev_priv, int pipe, u32 mask); extern u32 psb_get_vblank_counter(struct drm_device *dev, int crtc); /* - * intel_opregion.c - */ -extern int gma_intel_opregion_init(struct drm_device *dev); -extern int gma_intel_opregion_exit(struct drm_device *dev); - -/* * framebuffer.c */ extern int psbfb_probed(struct drm_device *dev); diff --git a/drivers/gpu/drm/gma500/psb_intel_display.c b/drivers/gpu/drm/gma500/psb_intel_display.c index 2616558..36c3c99 100644 --- a/drivers/gpu/drm/gma500/psb_intel_display.c +++ b/drivers/gpu/drm/gma500/psb_intel_display.c @@ -337,15 +337,12 @@ static int psb_intel_pipe_set_base(struct drm_crtc *crtc, int x, int y, struct drm_framebuffer *old_fb) { struct drm_device *dev = crtc->dev; - /* struct drm_i915_master_private *master_priv; */ + struct drm_psb_private *dev_priv = dev->dev_private; struct psb_intel_crtc *psb_intel_crtc = to_psb_intel_crtc(crtc); struct psb_framebuffer *psbfb = to_psb_fb(crtc->fb); int pipe = psb_intel_crtc->pipe; + const struct psb_offset *map = &dev_priv->regmap[pipe]; unsigned long start, offset; - int dspbase = (pipe == 0 ? DSPABASE : DSPBBASE); - int dspsurf = (pipe == 0 ? DSPASURF : DSPBSURF); - int dspstride = (pipe == 0) ? DSPASTRIDE : DSPBSTRIDE; - int dspcntr_reg = (pipe == 0) ? DSPACNTR : DSPBCNTR; u32 dspcntr; int ret = 0; @@ -367,9 +364,9 @@ static int psb_intel_pipe_set_base(struct drm_crtc *crtc, offset = y * crtc->fb->pitches[0] + x * (crtc->fb->bits_per_pixel / 8); - REG_WRITE(dspstride, crtc->fb->pitches[0]); + REG_WRITE(map->stride, crtc->fb->pitches[0]); - dspcntr = REG_READ(dspcntr_reg); + dspcntr = REG_READ(map->cntr); dspcntr &= ~DISPPLANE_PIXFORMAT_MASK; switch (crtc->fb->bits_per_pixel) { @@ -392,18 +389,10 @@ static int psb_intel_pipe_set_base(struct drm_crtc *crtc, psb_gtt_unpin(psbfb->gtt); goto psb_intel_pipe_set_base_exit; } - REG_WRITE(dspcntr_reg, dspcntr); - + REG_WRITE(map->cntr, dspcntr); - if (0 /* FIXMEAC - check what PSB needs */) { - REG_WRITE(dspbase, offset); - REG_READ(dspbase); - REG_WRITE(dspsurf, start); - REG_READ(dspsurf); - } else { - REG_WRITE(dspbase, start + offset); - REG_READ(dspbase); - } + REG_WRITE(map->base, start + offset); + REG_READ(map->base); psb_intel_pipe_cleaner: /* If there was a previous display we can now unpin it */ @@ -424,14 +413,10 @@ psb_intel_pipe_set_base_exit: static void psb_intel_crtc_dpms(struct drm_crtc *crtc, int mode) { struct drm_device *dev = crtc->dev; - /* struct drm_i915_master_private *master_priv; */ - /* struct drm_i915_private *dev_priv = dev->dev_private; */ + struct drm_psb_private *dev_priv = dev->dev_private; struct psb_intel_crtc *psb_intel_crtc = to_psb_intel_crtc(crtc); int pipe = psb_intel_crtc->pipe; - int dpll_reg = (pipe == 0) ? DPLL_A : DPLL_B; - int dspcntr_reg = (pipe == 0) ? DSPACNTR : DSPBCNTR; - int dspbase_reg = (pipe == 0) ? DSPABASE : DSPBBASE; - int pipeconf_reg = (pipe == 0) ? PIPEACONF : PIPEBCONF; + const struct psb_offset *map = &dev_priv->regmap[pipe]; u32 temp; /* XXX: When our outputs are all unaware of DPMS modes other than off @@ -442,34 +427,34 @@ static void psb_intel_crtc_dpms(struct drm_crtc *crtc, int mode) case DRM_MODE_DPMS_STANDBY: case DRM_MODE_DPMS_SUSPEND: /* Enable the DPLL */ - temp = REG_READ(dpll_reg); + temp = REG_READ(map->dpll); if ((temp & DPLL_VCO_ENABLE) == 0) { - REG_WRITE(dpll_reg, temp); - REG_READ(dpll_reg); + REG_WRITE(map->dpll, temp); + REG_READ(map->dpll); /* Wait for the clocks to stabilize. */ udelay(150); - REG_WRITE(dpll_reg, temp | DPLL_VCO_ENABLE); - REG_READ(dpll_reg); + REG_WRITE(map->dpll, temp | DPLL_VCO_ENABLE); + REG_READ(map->dpll); /* Wait for the clocks to stabilize. */ udelay(150); - REG_WRITE(dpll_reg, temp | DPLL_VCO_ENABLE); - REG_READ(dpll_reg); + REG_WRITE(map->dpll, temp | DPLL_VCO_ENABLE); + REG_READ(map->dpll); /* Wait for the clocks to stabilize. */ udelay(150); } /* Enable the pipe */ - temp = REG_READ(pipeconf_reg); + temp = REG_READ(map->conf); if ((temp & PIPEACONF_ENABLE) == 0) - REG_WRITE(pipeconf_reg, temp | PIPEACONF_ENABLE); + REG_WRITE(map->conf, temp | PIPEACONF_ENABLE); /* Enable the plane */ - temp = REG_READ(dspcntr_reg); + temp = REG_READ(map->cntr); if ((temp & DISPLAY_PLANE_ENABLE) == 0) { - REG_WRITE(dspcntr_reg, + REG_WRITE(map->cntr, temp | DISPLAY_PLANE_ENABLE); /* Flush the plane changes */ - REG_WRITE(dspbase_reg, REG_READ(dspbase_reg)); + REG_WRITE(map->base, REG_READ(map->base)); } psb_intel_crtc_load_lut(crtc); @@ -487,29 +472,29 @@ static void psb_intel_crtc_dpms(struct drm_crtc *crtc, int mode) REG_WRITE(VGACNTRL, VGA_DISP_DISABLE); /* Disable display plane */ - temp = REG_READ(dspcntr_reg); + temp = REG_READ(map->cntr); if ((temp & DISPLAY_PLANE_ENABLE) != 0) { - REG_WRITE(dspcntr_reg, + REG_WRITE(map->cntr, temp & ~DISPLAY_PLANE_ENABLE); /* Flush the plane changes */ - REG_WRITE(dspbase_reg, REG_READ(dspbase_reg)); - REG_READ(dspbase_reg); + REG_WRITE(map->base, REG_READ(map->base)); + REG_READ(map->base); } /* Next, disable display pipes */ - temp = REG_READ(pipeconf_reg); + temp = REG_READ(map->conf); if ((temp & PIPEACONF_ENABLE) != 0) { - REG_WRITE(pipeconf_reg, temp & ~PIPEACONF_ENABLE); - REG_READ(pipeconf_reg); + REG_WRITE(map->conf, temp & ~PIPEACONF_ENABLE); + REG_READ(map->conf); } /* Wait for vblank for the disable to take effect. */ psb_intel_wait_for_vblank(dev); - temp = REG_READ(dpll_reg); + temp = REG_READ(map->dpll); if ((temp & DPLL_VCO_ENABLE) != 0) { - REG_WRITE(dpll_reg, temp & ~DPLL_VCO_ENABLE); - REG_READ(dpll_reg); + REG_WRITE(map->dpll, temp & ~DPLL_VCO_ENABLE); + REG_READ(map->dpll); } /* Wait for the clocks to turn off. */ @@ -589,22 +574,11 @@ static int psb_intel_crtc_mode_set(struct drm_crtc *crtc, struct drm_framebuffer *old_fb) { struct drm_device *dev = crtc->dev; + struct drm_psb_private *dev_priv = dev->dev_private; struct psb_intel_crtc *psb_intel_crtc = to_psb_intel_crtc(crtc); struct drm_crtc_helper_funcs *crtc_funcs = crtc->helper_private; int pipe = psb_intel_crtc->pipe; - int fp_reg = (pipe == 0) ? FPA0 : FPB0; - int dpll_reg = (pipe == 0) ? DPLL_A : DPLL_B; - int dspcntr_reg = (pipe == 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 = (pipe == 0) ? DSPASIZE : DSPBSIZE; - int dsppos_reg = (pipe == 0) ? DSPAPOS : DSPBPOS; - int pipesrc_reg = (pipe == 0) ? PIPEASRC : PIPEBSRC; + const struct psb_offset *map = &dev_priv->regmap[pipe]; int refclk; struct psb_intel_clock_t clock; u32 dpll = 0, fp = 0, dspcntr, pipeconf; @@ -690,7 +664,7 @@ static int psb_intel_crtc_mode_set(struct drm_crtc *crtc, dpll |= PLL_REF_INPUT_DREFCLK; /* setup pipeconf */ - pipeconf = REG_READ(pipeconf_reg); + pipeconf = REG_READ(map->conf); /* Set up the display plane register */ dspcntr = DISPPLANE_GAMMA_ENABLE; @@ -712,9 +686,9 @@ static int psb_intel_crtc_mode_set(struct drm_crtc *crtc, drm_mode_debug_printmodeline(mode); if (dpll & DPLL_VCO_ENABLE) { - REG_WRITE(fp_reg, fp); - REG_WRITE(dpll_reg, dpll & ~DPLL_VCO_ENABLE); - REG_READ(dpll_reg); + REG_WRITE(map->fp0, fp); + REG_WRITE(map->dpll, dpll & ~DPLL_VCO_ENABLE); + REG_READ(map->dpll); udelay(150); } @@ -747,45 +721,45 @@ static int psb_intel_crtc_mode_set(struct drm_crtc *crtc, REG_READ(LVDS); } - REG_WRITE(fp_reg, fp); - REG_WRITE(dpll_reg, dpll); - REG_READ(dpll_reg); + REG_WRITE(map->fp0, fp); + REG_WRITE(map->dpll, dpll); + REG_READ(map->dpll); /* Wait for the clocks to stabilize. */ udelay(150); /* write it again -- the BIOS does, after all */ - REG_WRITE(dpll_reg, dpll); + REG_WRITE(map->dpll, dpll); - REG_READ(dpll_reg); + REG_READ(map->dpll); /* Wait for the clocks to stabilize. */ udelay(150); - REG_WRITE(htot_reg, (adjusted_mode->crtc_hdisplay - 1) | + REG_WRITE(map->htotal, (adjusted_mode->crtc_hdisplay - 1) | ((adjusted_mode->crtc_htotal - 1) << 16)); - REG_WRITE(hblank_reg, (adjusted_mode->crtc_hblank_start - 1) | + REG_WRITE(map->hblank, (adjusted_mode->crtc_hblank_start - 1) | ((adjusted_mode->crtc_hblank_end - 1) << 16)); - REG_WRITE(hsync_reg, (adjusted_mode->crtc_hsync_start - 1) | + REG_WRITE(map->hsync, (adjusted_mode->crtc_hsync_start - 1) | ((adjusted_mode->crtc_hsync_end - 1) << 16)); - REG_WRITE(vtot_reg, (adjusted_mode->crtc_vdisplay - 1) | + REG_WRITE(map->vtotal, (adjusted_mode->crtc_vdisplay - 1) | ((adjusted_mode->crtc_vtotal - 1) << 16)); - REG_WRITE(vblank_reg, (adjusted_mode->crtc_vblank_start - 1) | + REG_WRITE(map->vblank, (adjusted_mode->crtc_vblank_start - 1) | ((adjusted_mode->crtc_vblank_end - 1) << 16)); - REG_WRITE(vsync_reg, (adjusted_mode->crtc_vsync_start - 1) | + REG_WRITE(map->vsync, (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. */ - REG_WRITE(dspsize_reg, + REG_WRITE(map->size, ((mode->vdisplay - 1) << 16) | (mode->hdisplay - 1)); - REG_WRITE(dsppos_reg, 0); - REG_WRITE(pipesrc_reg, + REG_WRITE(map->pos, 0); + REG_WRITE(map->src, ((mode->hdisplay - 1) << 16) | (mode->vdisplay - 1)); - REG_WRITE(pipeconf_reg, pipeconf); - REG_READ(pipeconf_reg); + REG_WRITE(map->conf, pipeconf); + REG_READ(map->conf); psb_intel_wait_for_vblank(dev); - REG_WRITE(dspcntr_reg, dspcntr); + REG_WRITE(map->cntr, dspcntr); /* Flush the plane changes */ crtc_funcs->mode_set_base(crtc, x, y, old_fb); @@ -799,10 +773,10 @@ static int psb_intel_crtc_mode_set(struct drm_crtc *crtc, void psb_intel_crtc_load_lut(struct drm_crtc *crtc) { struct drm_device *dev = crtc->dev; - struct drm_psb_private *dev_priv = - (struct drm_psb_private *)dev->dev_private; + struct drm_psb_private *dev_priv = dev->dev_private; struct psb_intel_crtc *psb_intel_crtc = to_psb_intel_crtc(crtc); - int palreg = PALETTE_A; + const struct psb_offset *map = &dev_priv->regmap[psb_intel_crtc->pipe]; + int palreg = map->palette; int i; /* The clocks have to be on to load the palette. */ @@ -811,12 +785,7 @@ void psb_intel_crtc_load_lut(struct drm_crtc *crtc) switch (psb_intel_crtc->pipe) { case 0: - break; case 1: - palreg = PALETTE_B; - break; - case 2: - palreg = PALETTE_C; break; default: dev_err(dev->dev, "Illegal Pipe Number.\n"); @@ -836,7 +805,7 @@ void psb_intel_crtc_load_lut(struct drm_crtc *crtc) gma_power_end(dev); } else { for (i = 0; i < 256; i++) { - dev_priv->regs.psb.save_palette_a[i] = + dev_priv->regs.pipe[0].palette[i] = ((psb_intel_crtc->lut_r[i] + psb_intel_crtc->lut_adj[i]) << 16) | ((psb_intel_crtc->lut_g[i] + @@ -854,11 +823,10 @@ void psb_intel_crtc_load_lut(struct drm_crtc *crtc) static void psb_intel_crtc_save(struct drm_crtc *crtc) { struct drm_device *dev = crtc->dev; - /* struct drm_psb_private *dev_priv = - (struct drm_psb_private *)dev->dev_private; */ + struct drm_psb_private *dev_priv = dev->dev_private; struct psb_intel_crtc *psb_intel_crtc = to_psb_intel_crtc(crtc); struct psb_intel_crtc_state *crtc_state = psb_intel_crtc->crtc_state; - int pipeA = (psb_intel_crtc->pipe == 0); + const struct psb_offset *map = &dev_priv->regmap[psb_intel_crtc->pipe]; uint32_t paletteReg; int i; @@ -867,27 +835,27 @@ static void psb_intel_crtc_save(struct drm_crtc *crtc) return; } - crtc_state->saveDSPCNTR = REG_READ(pipeA ? DSPACNTR : DSPBCNTR); - crtc_state->savePIPECONF = REG_READ(pipeA ? PIPEACONF : PIPEBCONF); - crtc_state->savePIPESRC = REG_READ(pipeA ? PIPEASRC : PIPEBSRC); - crtc_state->saveFP0 = REG_READ(pipeA ? FPA0 : FPB0); - crtc_state->saveFP1 = REG_READ(pipeA ? FPA1 : FPB1); - crtc_state->saveDPLL = REG_READ(pipeA ? DPLL_A : DPLL_B); - crtc_state->saveHTOTAL = REG_READ(pipeA ? HTOTAL_A : HTOTAL_B); - crtc_state->saveHBLANK = REG_READ(pipeA ? HBLANK_A : HBLANK_B); - crtc_state->saveHSYNC = REG_READ(pipeA ? HSYNC_A : HSYNC_B); - crtc_state->saveVTOTAL = REG_READ(pipeA ? VTOTAL_A : VTOTAL_B); - crtc_state->saveVBLANK = REG_READ(pipeA ? VBLANK_A : VBLANK_B); - crtc_state->saveVSYNC = REG_READ(pipeA ? VSYNC_A : VSYNC_B); - crtc_state->saveDSPSTRIDE = REG_READ(pipeA ? DSPASTRIDE : DSPBSTRIDE); + crtc_state->saveDSPCNTR = REG_READ(map->cntr); + crtc_state->savePIPECONF = REG_READ(map->conf); + crtc_state->savePIPESRC = REG_READ(map->src); + crtc_state->saveFP0 = REG_READ(map->fp0); + crtc_state->saveFP1 = REG_READ(map->fp1); + crtc_state->saveDPLL = REG_READ(map->dpll); + crtc_state->saveHTOTAL = REG_READ(map->htotal); + crtc_state->saveHBLANK = REG_READ(map->hblank); + crtc_state->saveHSYNC = REG_READ(map->hsync); + crtc_state->saveVTOTAL = REG_READ(map->vtotal); + crtc_state->saveVBLANK = REG_READ(map->vblank); + crtc_state->saveVSYNC = REG_READ(map->vsync); + crtc_state->saveDSPSTRIDE = REG_READ(map->stride); /*NOTE: DSPSIZE DSPPOS only for psb*/ - crtc_state->saveDSPSIZE = REG_READ(pipeA ? DSPASIZE : DSPBSIZE); - crtc_state->saveDSPPOS = REG_READ(pipeA ? DSPAPOS : DSPBPOS); + crtc_state->saveDSPSIZE = REG_READ(map->size); + crtc_state->saveDSPPOS = REG_READ(map->pos); - crtc_state->saveDSPBASE = REG_READ(pipeA ? DSPABASE : DSPBBASE); + crtc_state->saveDSPBASE = REG_READ(map->base); - paletteReg = pipeA ? PALETTE_A : PALETTE_B; + paletteReg = map->palette; for (i = 0; i < 256; ++i) crtc_state->savePalette[i] = REG_READ(paletteReg + (i << 2)); } @@ -898,12 +866,10 @@ static void psb_intel_crtc_save(struct drm_crtc *crtc) static void psb_intel_crtc_restore(struct drm_crtc *crtc) { struct drm_device *dev = crtc->dev; - /* struct drm_psb_private * dev_priv = - (struct drm_psb_private *)dev->dev_private; */ + struct drm_psb_private *dev_priv = dev->dev_private; struct psb_intel_crtc *psb_intel_crtc = to_psb_intel_crtc(crtc); struct psb_intel_crtc_state *crtc_state = psb_intel_crtc->crtc_state; - /* struct drm_crtc_helper_funcs * crtc_funcs = crtc->helper_private; */ - int pipeA = (psb_intel_crtc->pipe == 0); + const struct psb_offset *map = &dev_priv->regmap[psb_intel_crtc->pipe]; uint32_t paletteReg; int i; @@ -913,45 +879,45 @@ static void psb_intel_crtc_restore(struct drm_crtc *crtc) } if (crtc_state->saveDPLL & DPLL_VCO_ENABLE) { - REG_WRITE(pipeA ? DPLL_A : DPLL_B, + REG_WRITE(map->dpll, crtc_state->saveDPLL & ~DPLL_VCO_ENABLE); - REG_READ(pipeA ? DPLL_A : DPLL_B); + REG_READ(map->dpll); udelay(150); } - REG_WRITE(pipeA ? FPA0 : FPB0, crtc_state->saveFP0); - REG_READ(pipeA ? FPA0 : FPB0); + REG_WRITE(map->fp0, crtc_state->saveFP0); + REG_READ(map->fp0); - REG_WRITE(pipeA ? FPA1 : FPB1, crtc_state->saveFP1); - REG_READ(pipeA ? FPA1 : FPB1); + REG_WRITE(map->fp1, crtc_state->saveFP1); + REG_READ(map->fp1); - REG_WRITE(pipeA ? DPLL_A : DPLL_B, crtc_state->saveDPLL); - REG_READ(pipeA ? DPLL_A : DPLL_B); + REG_WRITE(map->dpll, crtc_state->saveDPLL); + REG_READ(map->dpll); udelay(150); - REG_WRITE(pipeA ? HTOTAL_A : HTOTAL_B, crtc_state->saveHTOTAL); - REG_WRITE(pipeA ? HBLANK_A : HBLANK_B, crtc_state->saveHBLANK); - REG_WRITE(pipeA ? HSYNC_A : HSYNC_B, crtc_state->saveHSYNC); - REG_WRITE(pipeA ? VTOTAL_A : VTOTAL_B, crtc_state->saveVTOTAL); - REG_WRITE(pipeA ? VBLANK_A : VBLANK_B, crtc_state->saveVBLANK); - REG_WRITE(pipeA ? VSYNC_A : VSYNC_B, crtc_state->saveVSYNC); - REG_WRITE(pipeA ? DSPASTRIDE : DSPBSTRIDE, crtc_state->saveDSPSTRIDE); + REG_WRITE(map->htotal, crtc_state->saveHTOTAL); + REG_WRITE(map->hblank, crtc_state->saveHBLANK); + REG_WRITE(map->hsync, crtc_state->saveHSYNC); + REG_WRITE(map->vtotal, crtc_state->saveVTOTAL); + REG_WRITE(map->vblank, crtc_state->saveVBLANK); + REG_WRITE(map->vsync, crtc_state->saveVSYNC); + REG_WRITE(map->stride, crtc_state->saveDSPSTRIDE); - REG_WRITE(pipeA ? DSPASIZE : DSPBSIZE, crtc_state->saveDSPSIZE); - REG_WRITE(pipeA ? DSPAPOS : DSPBPOS, crtc_state->saveDSPPOS); + REG_WRITE(map->size, crtc_state->saveDSPSIZE); + REG_WRITE(map->pos, crtc_state->saveDSPPOS); - REG_WRITE(pipeA ? PIPEASRC : PIPEBSRC, crtc_state->savePIPESRC); - REG_WRITE(pipeA ? DSPABASE : DSPBBASE, crtc_state->saveDSPBASE); - REG_WRITE(pipeA ? PIPEACONF : PIPEBCONF, crtc_state->savePIPECONF); + REG_WRITE(map->src, crtc_state->savePIPESRC); + REG_WRITE(map->base, crtc_state->saveDSPBASE); + REG_WRITE(map->conf, crtc_state->savePIPECONF); psb_intel_wait_for_vblank(dev); - REG_WRITE(pipeA ? DSPACNTR : DSPBCNTR, crtc_state->saveDSPCNTR); - REG_WRITE(pipeA ? DSPABASE : DSPBBASE, crtc_state->saveDSPBASE); + REG_WRITE(map->cntr, crtc_state->saveDSPCNTR); + REG_WRITE(map->base, crtc_state->saveDSPBASE); psb_intel_wait_for_vblank(dev); - paletteReg = pipeA ? PALETTE_A : PALETTE_B; + paletteReg = map->palette; for (i = 0; i < 256; ++i) REG_WRITE(paletteReg + (i << 2), crtc_state->savePalette[i]); } @@ -962,6 +928,7 @@ static int psb_intel_crtc_cursor_set(struct drm_crtc *crtc, uint32_t width, uint32_t height) { struct drm_device *dev = crtc->dev; + struct drm_psb_private *dev_priv = dev->dev_private; struct psb_intel_crtc *psb_intel_crtc = to_psb_intel_crtc(crtc); int pipe = psb_intel_crtc->pipe; uint32_t control = (pipe == 0) ? CURACNTR : CURBCNTR; @@ -969,8 +936,10 @@ static int psb_intel_crtc_cursor_set(struct drm_crtc *crtc, uint32_t temp; size_t addr = 0; struct gtt_range *gt; + struct gtt_range *cursor_gt = psb_intel_crtc->cursor_gt; struct drm_gem_object *obj; - int ret; + void *tmp_dst, *tmp_src; + int ret, i, cursor_pages; /* if we want to turn of the cursor ignore width and height */ if (!handle) { @@ -1019,10 +988,32 @@ static int psb_intel_crtc_cursor_set(struct drm_crtc *crtc, return ret; } + if (dev_priv->ops->cursor_needs_phys) { + if (cursor_gt == NULL) { + dev_err(dev->dev, "No hardware cursor mem available"); + return -ENOMEM; + } - addr = gt->offset; /* Or resource.start ??? */ + /* Prevent overflow */ + if (gt->npage > 4) + cursor_pages = 4; + else + cursor_pages = gt->npage; + + /* Copy the cursor to cursor mem */ + tmp_dst = dev_priv->vram_addr + cursor_gt->offset; + for (i = 0; i < cursor_pages; i++) { + tmp_src = kmap(gt->pages[i]); + memcpy(tmp_dst, tmp_src, PAGE_SIZE); + kunmap(gt->pages[i]); + tmp_dst += PAGE_SIZE; + } - psb_intel_crtc->cursor_addr = addr; + addr = psb_intel_crtc->cursor_addr; + } else { + addr = gt->offset; /* Or resource.start ??? */ + psb_intel_crtc->cursor_addr = addr; + } temp = 0; /* set the pipe for the cursor */ @@ -1115,34 +1106,30 @@ static int psb_intel_crtc_clock_get(struct drm_device *dev, struct drm_crtc *crtc) { struct psb_intel_crtc *psb_intel_crtc = to_psb_intel_crtc(crtc); + struct drm_psb_private *dev_priv = dev->dev_private; int pipe = psb_intel_crtc->pipe; + const struct psb_offset *map = &dev_priv->regmap[pipe]; u32 dpll; u32 fp; struct psb_intel_clock_t clock; bool is_lvds; - struct drm_psb_private *dev_priv = dev->dev_private; + struct psb_pipe *p = &dev_priv->regs.pipe[pipe]; if (gma_power_begin(dev, false)) { - dpll = REG_READ((pipe == 0) ? DPLL_A : DPLL_B); + dpll = REG_READ(map->dpll); if ((dpll & DISPLAY_RATE_SELECT_FPA1) == 0) - fp = REG_READ((pipe == 0) ? FPA0 : FPB0); + fp = REG_READ(map->fp0); else - fp = REG_READ((pipe == 0) ? FPA1 : FPB1); + fp = REG_READ(map->fp1); is_lvds = (pipe == 1) && (REG_READ(LVDS) & LVDS_PORT_EN); gma_power_end(dev); } else { - dpll = (pipe == 0) ? - dev_priv->regs.psb.saveDPLL_A : - dev_priv->regs.psb.saveDPLL_B; + dpll = p->dpll; if ((dpll & DISPLAY_RATE_SELECT_FPA1) == 0) - fp = (pipe == 0) ? - dev_priv->regs.psb.saveFPA0 : - dev_priv->regs.psb.saveFPB0; + fp = p->fp0; else - fp = (pipe == 0) ? - dev_priv->regs.psb.saveFPA1 : - dev_priv->regs.psb.saveFPB1; + fp = p->fp1; is_lvds = (pipe == 1) && (dev_priv->regs.psb.saveLVDS & LVDS_PORT_EN); @@ -1202,26 +1189,20 @@ struct drm_display_mode *psb_intel_crtc_mode_get(struct drm_device *dev, int vtot; int vsync; struct drm_psb_private *dev_priv = dev->dev_private; + struct psb_pipe *p = &dev_priv->regs.pipe[pipe]; + const struct psb_offset *map = &dev_priv->regmap[pipe]; if (gma_power_begin(dev, false)) { - htot = REG_READ((pipe == 0) ? HTOTAL_A : HTOTAL_B); - hsync = REG_READ((pipe == 0) ? HSYNC_A : HSYNC_B); - vtot = REG_READ((pipe == 0) ? VTOTAL_A : VTOTAL_B); - vsync = REG_READ((pipe == 0) ? VSYNC_A : VSYNC_B); + htot = REG_READ(map->htotal); + hsync = REG_READ(map->hsync); + vtot = REG_READ(map->vtotal); + vsync = REG_READ(map->vsync); gma_power_end(dev); } else { - htot = (pipe == 0) ? - dev_priv->regs.psb.saveHTOTAL_A : - dev_priv->regs.psb.saveHTOTAL_B; - hsync = (pipe == 0) ? - dev_priv->regs.psb.saveHSYNC_A : - dev_priv->regs.psb.saveHSYNC_B; - vtot = (pipe == 0) ? - dev_priv->regs.psb.saveVTOTAL_A : - dev_priv->regs.psb.saveVTOTAL_B; - vsync = (pipe == 0) ? - dev_priv->regs.psb.saveVSYNC_A : - dev_priv->regs.psb.saveVSYNC_B; + htot = p->htotal; + hsync = p->hsync; + vtot = p->vtotal; + vsync = p->vsync; } mode = kzalloc(sizeof(*mode), GFP_KERNEL); @@ -1257,6 +1238,9 @@ void psb_intel_crtc_destroy(struct drm_crtc *crtc) drm_gem_object_unreference(psb_intel_crtc->cursor_obj); psb_intel_crtc->cursor_obj = NULL; } + + if (psb_intel_crtc->cursor_gt != NULL) + psb_gtt_free_range(crtc->dev, psb_intel_crtc->cursor_gt); kfree(psb_intel_crtc->crtc_state); drm_crtc_cleanup(crtc); kfree(psb_intel_crtc); @@ -1285,13 +1269,33 @@ const struct drm_crtc_funcs psb_intel_crtc_funcs = { * Set the default value of cursor control and base register * to zero. This is a workaround for h/w defect on Oaktrail */ -static void psb_intel_cursor_init(struct drm_device *dev, int pipe) +static void psb_intel_cursor_init(struct drm_device *dev, + struct psb_intel_crtc *psb_intel_crtc) { + struct drm_psb_private *dev_priv = dev->dev_private; u32 control[3] = { CURACNTR, CURBCNTR, CURCCNTR }; u32 base[3] = { CURABASE, CURBBASE, CURCBASE }; + struct gtt_range *cursor_gt; + + if (dev_priv->ops->cursor_needs_phys) { + /* Allocate 4 pages of stolen mem for a hardware cursor. That + * is enough for the 64 x 64 ARGB cursors we support. + */ + cursor_gt = psb_gtt_alloc_range(dev, 4 * PAGE_SIZE, "cursor", 1); + if (!cursor_gt) { + psb_intel_crtc->cursor_gt = NULL; + goto out; + } + psb_intel_crtc->cursor_gt = cursor_gt; + psb_intel_crtc->cursor_addr = dev_priv->stolen_base + + cursor_gt->offset; + } else { + psb_intel_crtc->cursor_gt = NULL; + } - REG_WRITE(control[pipe], 0); - REG_WRITE(base[pipe], 0); +out: + REG_WRITE(control[psb_intel_crtc->pipe], 0); + REG_WRITE(base[psb_intel_crtc->pipe], 0); } void psb_intel_crtc_init(struct drm_device *dev, int pipe, @@ -1357,7 +1361,7 @@ void psb_intel_crtc_init(struct drm_device *dev, int pipe, psb_intel_crtc->mode_set.connectors = (struct drm_connector **) (psb_intel_crtc + 1); psb_intel_crtc->mode_set.num_connectors = 0; - psb_intel_cursor_init(dev, pipe); + psb_intel_cursor_init(dev, psb_intel_crtc); } int psb_intel_get_pipe_from_crtc_id(struct drm_device *dev, void *data, diff --git a/drivers/gpu/drm/gma500/psb_intel_drv.h b/drivers/gpu/drm/gma500/psb_intel_drv.h index 81852b4..2515f83 100644 --- a/drivers/gpu/drm/gma500/psb_intel_drv.h +++ b/drivers/gpu/drm/gma500/psb_intel_drv.h @@ -106,11 +106,6 @@ struct psb_intel_mode_device { size_t(*bo_offset) (struct drm_device *dev, void *bo); /* - * Cursor (Can go ?) - */ - int cursor_needs_physical; - - /* * LVDS info */ int backlight_duty_cycle; /* restore backlight to this value */ @@ -176,6 +171,7 @@ struct psb_intel_crtc { int pipe; int plane; uint32_t cursor_addr; + struct gtt_range *cursor_gt; u8 lut_r[256], lut_g[256], lut_b[256]; u8 lut_adj[256]; struct psb_intel_framebuffer *fbdev_fb; diff --git a/drivers/gpu/drm/gma500/psb_intel_sdvo.c b/drivers/gpu/drm/gma500/psb_intel_sdvo.c index 958b4e2..d39b15b 100644 --- a/drivers/gpu/drm/gma500/psb_intel_sdvo.c +++ b/drivers/gpu/drm/gma500/psb_intel_sdvo.c @@ -2038,8 +2038,7 @@ psb_intel_sdvo_add_hdmi_properties(struct psb_intel_sdvo_connector *connector) struct drm_device *dev = connector->base.base.dev; intel_attach_force_audio_property(&connector->base.base); - if (INTEL_INFO(dev)->gen >= 4 && IS_MOBILE(dev)) - intel_attach_broadcast_rgb_property(&connector->base.base); + intel_attach_broadcast_rgb_property(&connector->base.base); */ } diff --git a/drivers/gpu/drm/gma500/psb_lid.c b/drivers/gpu/drm/gma500/psb_lid.c index 7ff8bb2b..1d2ebb5 100644 --- a/drivers/gpu/drm/gma500/psb_lid.c +++ b/drivers/gpu/drm/gma500/psb_lid.c @@ -40,10 +40,16 @@ static void psb_lid_timer_func(unsigned long data) REG_WRITE(PP_CONTROL, REG_READ(PP_CONTROL) | POWER_TARGET_ON); do { pp_status = REG_READ(PP_STATUS); - } while ((pp_status & PP_ON) == 0); + } while ((pp_status & PP_ON) == 0 && + (pp_status & PP_SEQUENCE_MASK) != 0); - /*FIXME: should be backlight level before*/ - psb_intel_lvds_set_brightness(dev, 100); + if (REG_READ(PP_STATUS) & PP_ON) { + /*FIXME: should be backlight level before*/ + psb_intel_lvds_set_brightness(dev, 100); + } else { + DRM_DEBUG("LVDS panel never powered up"); + return; + } } else { psb_intel_lvds_set_brightness(dev, 0); diff --git a/drivers/gpu/drm/i915/Makefile b/drivers/gpu/drm/i915/Makefile index b65c06f..0ca7f76 100644 --- a/drivers/gpu/drm/i915/Makefile +++ b/drivers/gpu/drm/i915/Makefile @@ -11,6 +11,7 @@ i915-y := i915_drv.o i915_dma.o i915_irq.o \ i915_gem_evict.o \ i915_gem_execbuffer.o \ i915_gem_gtt.o \ + i915_gem_stolen.o \ i915_gem_tiling.o \ i915_sysfs.o \ i915_trace_points.o \ @@ -18,6 +19,7 @@ i915-y := i915_drv.o i915_dma.o i915_irq.o \ intel_crt.o \ intel_lvds.o \ intel_bios.o \ + intel_ddi.o \ intel_dp.o \ intel_hdmi.o \ intel_sdvo.o \ diff --git a/drivers/gpu/drm/i915/i915_debugfs.c b/drivers/gpu/drm/i915/i915_debugfs.c index a8db386..eb2b3c2 100644 --- a/drivers/gpu/drm/i915/i915_debugfs.c +++ b/drivers/gpu/drm/i915/i915_debugfs.c @@ -47,7 +47,6 @@ enum { FLUSHING_LIST, INACTIVE_LIST, PINNED_LIST, - DEFERRED_FREE_LIST, }; static const char *yesno(int v) @@ -178,18 +177,10 @@ static int i915_gem_object_list_info(struct seq_file *m, void *data) 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: mutex_unlock(&dev->struct_mutex); return -EINVAL; @@ -252,21 +243,11 @@ static int i915_gem_object_info(struct seq_file *m, void* data) count, mappable_count, size, mappable_size); size = count = mappable_size = mappable_count = 0; - count_objects(&dev_priv->mm.pinned_list, mm_list); - seq_printf(m, " %u [%u] pinned objects, %zu [%zu] bytes\n", - count, mappable_count, size, mappable_size); - - size = count = mappable_size = mappable_count = 0; count_objects(&dev_priv->mm.inactive_list, mm_list); seq_printf(m, " %u [%u] inactive objects, %zu [%zu] bytes\n", count, mappable_count, size, mappable_size); size = count = mappable_size = mappable_count = 0; - count_objects(&dev_priv->mm.deferred_free_list, mm_list); - seq_printf(m, " %u [%u] freed objects, %zu [%zu] bytes\n", - count, mappable_count, size, mappable_size); - - size = count = mappable_size = mappable_count = 0; list_for_each_entry(obj, &dev_priv->mm.gtt_list, gtt_list) { if (obj->fault_mappable) { size += obj->gtt_space->size; @@ -294,6 +275,7 @@ static int i915_gem_gtt_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; + uintptr_t list = (uintptr_t) node->info_ent->data; struct drm_i915_private *dev_priv = dev->dev_private; struct drm_i915_gem_object *obj; size_t total_obj_size, total_gtt_size; @@ -305,6 +287,9 @@ static int i915_gem_gtt_info(struct seq_file *m, void* data) total_obj_size = total_gtt_size = count = 0; list_for_each_entry(obj, &dev_priv->mm.gtt_list, gtt_list) { + if (list == PINNED_LIST && obj->pin_count == 0) + continue; + seq_printf(m, " "); describe_obj(m, obj); seq_printf(m, "\n"); @@ -321,7 +306,6 @@ static int i915_gem_gtt_info(struct seq_file *m, void* data) 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; @@ -430,10 +414,6 @@ static void i915_ring_seqno_info(struct seq_file *m, if (ring->get_seqno) { seq_printf(m, "Current sequence (%s): %d\n", ring->name, ring->get_seqno(ring)); - seq_printf(m, "Waiter sequence (%s): %d\n", - ring->name, ring->waiting_seqno); - seq_printf(m, "IRQ sequence (%s): %d\n", - ring->name, ring->irq_seqno); } } @@ -602,69 +582,6 @@ static int i915_hws_info(struct seq_file *m, void *data) return 0; } -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; - struct intel_ring_buffer *ring; - int ret; - - ret = mutex_lock_interruptible(&dev->struct_mutex); - if (ret) - return ret; - - ring = &dev_priv->ring[(uintptr_t)node->info_ent->data]; - if (!ring->obj) { - seq_printf(m, "No ringbuffer setup\n"); - } else { - const u8 __iomem *virt = ring->virtual_start; - uint32_t off; - - for (off = 0; off < 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; -} - -static int i915_ringbuffer_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_ring_buffer *ring; - int ret; - - ring = &dev_priv->ring[(uintptr_t)node->info_ent->data]; - if (ring->size == 0) - return 0; - - ret = mutex_lock_interruptible(&dev->struct_mutex); - if (ret) - return ret; - - seq_printf(m, "Ring %s:\n", ring->name); - seq_printf(m, " Head : %08x\n", I915_READ_HEAD(ring) & HEAD_ADDR); - seq_printf(m, " Tail : %08x\n", I915_READ_TAIL(ring) & TAIL_ADDR); - seq_printf(m, " Size : %08x\n", ring->size); - seq_printf(m, " Active : %08x\n", intel_ring_get_active_head(ring)); - seq_printf(m, " NOPID : %08x\n", I915_READ_NOPID(ring)); - if (IS_GEN6(dev) || IS_GEN7(dev)) { - seq_printf(m, " Sync 0 : %08x\n", I915_READ_SYNC_0(ring)); - seq_printf(m, " Sync 1 : %08x\n", I915_READ_SYNC_1(ring)); - } - seq_printf(m, " Control : %08x\n", I915_READ_CTL(ring)); - seq_printf(m, " Start : %08x\n", I915_READ_START(ring)); - - mutex_unlock(&dev->struct_mutex); - - return 0; -} - static const char *ring_str(int ring) { switch (ring) { @@ -766,31 +683,35 @@ static void i915_ring_error_state(struct seq_file *m, error->semaphore_mboxes[ring][1]); } seq_printf(m, " seqno: 0x%08x\n", error->seqno[ring]); + seq_printf(m, " waiting: %s\n", yesno(error->waiting[ring])); seq_printf(m, " ring->head: 0x%08x\n", error->cpu_ring_head[ring]); seq_printf(m, " ring->tail: 0x%08x\n", error->cpu_ring_tail[ring]); } +struct i915_error_state_file_priv { + struct drm_device *dev; + struct drm_i915_error_state *error; +}; + static int i915_error_state(struct seq_file *m, void *unused) { - struct drm_info_node *node = (struct drm_info_node *) m->private; - struct drm_device *dev = node->minor->dev; + struct i915_error_state_file_priv *error_priv = m->private; + struct drm_device *dev = error_priv->dev; drm_i915_private_t *dev_priv = dev->dev_private; - struct drm_i915_error_state *error; - unsigned long flags; + struct drm_i915_error_state *error = error_priv->error; + struct intel_ring_buffer *ring; int i, j, page, offset, elt; - spin_lock_irqsave(&dev_priv->error_lock, flags); - if (!dev_priv->first_error) { + if (!error) { seq_printf(m, "no error state collected\n"); - goto out; + return 0; } - error = dev_priv->first_error; - seq_printf(m, "Time: %ld s %ld us\n", error->time.tv_sec, error->time.tv_usec); seq_printf(m, "PCI ID: 0x%04x\n", dev->pci_device); seq_printf(m, "EIR: 0x%08x\n", error->eir); + seq_printf(m, "IER: 0x%08x\n", error->ier); seq_printf(m, "PGTBL_ER: 0x%08x\n", error->pgtbl_er); for (i = 0; i < dev_priv->num_fence_regs; i++) @@ -801,11 +722,8 @@ static int i915_error_state(struct seq_file *m, void *unused) seq_printf(m, "DONE_REG: 0x%08x\n", error->done_reg); } - i915_ring_error_state(m, dev, error, RCS); - if (HAS_BLT(dev)) - i915_ring_error_state(m, dev, error, BCS); - if (HAS_BSD(dev)) - i915_ring_error_state(m, dev, error, VCS); + for_each_ring(ring, dev_priv, i) + i915_ring_error_state(m, dev, error, i); if (error->active_bo) print_error_buffers(m, "Active", @@ -867,12 +785,71 @@ static int i915_error_state(struct seq_file *m, void *unused) if (error->display) intel_display_print_error_state(m, dev, error->display); -out: + return 0; +} + +static ssize_t +i915_error_state_write(struct file *filp, + const char __user *ubuf, + size_t cnt, + loff_t *ppos) +{ + struct seq_file *m = filp->private_data; + struct i915_error_state_file_priv *error_priv = m->private; + struct drm_device *dev = error_priv->dev; + + DRM_DEBUG_DRIVER("Resetting error state\n"); + + mutex_lock(&dev->struct_mutex); + i915_destroy_error_state(dev); + mutex_unlock(&dev->struct_mutex); + + return cnt; +} + +static int i915_error_state_open(struct inode *inode, struct file *file) +{ + struct drm_device *dev = inode->i_private; + drm_i915_private_t *dev_priv = dev->dev_private; + struct i915_error_state_file_priv *error_priv; + unsigned long flags; + + error_priv = kzalloc(sizeof(*error_priv), GFP_KERNEL); + if (!error_priv) + return -ENOMEM; + + error_priv->dev = dev; + + spin_lock_irqsave(&dev_priv->error_lock, flags); + error_priv->error = dev_priv->first_error; + if (error_priv->error) + kref_get(&error_priv->error->ref); spin_unlock_irqrestore(&dev_priv->error_lock, flags); - return 0; + return single_open(file, i915_error_state, error_priv); +} + +static int i915_error_state_release(struct inode *inode, struct file *file) +{ + struct seq_file *m = file->private_data; + struct i915_error_state_file_priv *error_priv = m->private; + + if (error_priv->error) + kref_put(&error_priv->error->ref, i915_error_state_free); + kfree(error_priv); + + return single_release(inode, file); } +static const struct file_operations i915_error_state_fops = { + .owner = THIS_MODULE, + .open = i915_error_state_open, + .read = seq_read, + .write = i915_error_state_write, + .llseek = default_llseek, + .release = i915_error_state_release, +}; + static int i915_rstdby_delays(struct seq_file *m, void *unused) { struct drm_info_node *node = (struct drm_info_node *) m->private; @@ -1356,17 +1333,25 @@ static int i915_opregion(struct seq_file *m, void *unused) struct drm_device *dev = node->minor->dev; drm_i915_private_t *dev_priv = dev->dev_private; struct intel_opregion *opregion = &dev_priv->opregion; + void *data = kmalloc(OPREGION_SIZE, GFP_KERNEL); int ret; + if (data == NULL) + return -ENOMEM; + ret = mutex_lock_interruptible(&dev->struct_mutex); if (ret) - return ret; + goto out; - if (opregion->header) - seq_write(m, opregion->header, OPREGION_SIZE); + if (opregion->header) { + memcpy_fromio(data, opregion->header, OPREGION_SIZE); + seq_write(m, data, OPREGION_SIZE); + } mutex_unlock(&dev->struct_mutex); +out: + kfree(data); return 0; } @@ -1659,6 +1644,65 @@ static const struct file_operations i915_wedged_fops = { }; static ssize_t +i915_ring_stop_read(struct file *filp, + char __user *ubuf, + size_t max, + loff_t *ppos) +{ + struct drm_device *dev = filp->private_data; + drm_i915_private_t *dev_priv = dev->dev_private; + char buf[20]; + int len; + + len = snprintf(buf, sizeof(buf), + "0x%08x\n", dev_priv->stop_rings); + + if (len > sizeof(buf)) + len = sizeof(buf); + + return simple_read_from_buffer(ubuf, max, ppos, buf, len); +} + +static ssize_t +i915_ring_stop_write(struct file *filp, + const char __user *ubuf, + size_t cnt, + loff_t *ppos) +{ + struct drm_device *dev = filp->private_data; + struct drm_i915_private *dev_priv = dev->dev_private; + char buf[20]; + int val = 0; + + if (cnt > 0) { + if (cnt > sizeof(buf) - 1) + return -EINVAL; + + if (copy_from_user(buf, ubuf, cnt)) + return -EFAULT; + buf[cnt] = 0; + + val = simple_strtoul(buf, NULL, 0); + } + + DRM_DEBUG_DRIVER("Stopping rings 0x%08x\n", val); + + mutex_lock(&dev->struct_mutex); + dev_priv->stop_rings = val; + mutex_unlock(&dev->struct_mutex); + + return cnt; +} + +static const struct file_operations i915_ring_stop_fops = { + .owner = THIS_MODULE, + .open = simple_open, + .read = i915_ring_stop_read, + .write = i915_ring_stop_write, + .llseek = default_llseek, +}; + +static ssize_t i915_max_freq_read(struct file *filp, char __user *ubuf, size_t max, @@ -1900,11 +1944,10 @@ static struct drm_info_list i915_debugfs_list[] = { {"i915_capabilities", i915_capabilities, 0}, {"i915_gem_objects", i915_gem_object_info, 0}, {"i915_gem_gtt", i915_gem_gtt_info, 0}, + {"i915_gem_pinned", i915_gem_gtt_info, 0, (void *) PINNED_LIST}, {"i915_gem_active", i915_gem_object_list_info, 0, (void *) ACTIVE_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}, @@ -1913,13 +1956,6 @@ static struct drm_info_list i915_debugfs_list[] = { {"i915_gem_hws", i915_hws_info, 0, (void *)RCS}, {"i915_gem_hws_blt", i915_hws_info, 0, (void *)BCS}, {"i915_gem_hws_bsd", i915_hws_info, 0, (void *)VCS}, - {"i915_ringbuffer_data", i915_ringbuffer_data, 0, (void *)RCS}, - {"i915_ringbuffer_info", i915_ringbuffer_info, 0, (void *)RCS}, - {"i915_bsd_ringbuffer_data", i915_ringbuffer_data, 0, (void *)VCS}, - {"i915_bsd_ringbuffer_info", i915_ringbuffer_info, 0, (void *)VCS}, - {"i915_blt_ringbuffer_data", i915_ringbuffer_data, 0, (void *)BCS}, - {"i915_blt_ringbuffer_info", i915_ringbuffer_info, 0, (void *)BCS}, - {"i915_error_state", i915_error_state, 0}, {"i915_rstdby_delays", i915_rstdby_delays, 0}, {"i915_cur_delayinfo", i915_cur_delayinfo, 0}, {"i915_delayfreq_table", i915_delayfreq_table, 0}, @@ -1965,6 +2001,17 @@ int i915_debugfs_init(struct drm_minor *minor) &i915_cache_sharing_fops); if (ret) return ret; + ret = i915_debugfs_create(minor->debugfs_root, minor, + "i915_ring_stop", + &i915_ring_stop_fops); + if (ret) + return ret; + + ret = i915_debugfs_create(minor->debugfs_root, minor, + "i915_error_state", + &i915_error_state_fops); + if (ret) + return ret; return drm_debugfs_create_files(i915_debugfs_list, I915_DEBUGFS_ENTRIES, @@ -1983,6 +2030,8 @@ void i915_debugfs_cleanup(struct drm_minor *minor) 1, minor); drm_debugfs_remove_files((struct drm_info_list *) &i915_cache_sharing_fops, 1, minor); + drm_debugfs_remove_files((struct drm_info_list *) &i915_ring_stop_fops, + 1, minor); } #endif /* CONFIG_DEBUG_FS */ diff --git a/drivers/gpu/drm/i915/i915_dma.c b/drivers/gpu/drm/i915/i915_dma.c index 068958c..f947926 100644 --- a/drivers/gpu/drm/i915/i915_dma.c +++ b/drivers/gpu/drm/i915/i915_dma.c @@ -36,17 +36,63 @@ #include "i915_drm.h" #include "i915_drv.h" #include "i915_trace.h" -#include "../../../platform/x86/intel_ips.h" #include <linux/pci.h> #include <linux/vgaarb.h> #include <linux/acpi.h> #include <linux/pnp.h> #include <linux/vga_switcheroo.h> #include <linux/slab.h> -#include <linux/module.h> #include <acpi/video.h> #include <asm/pat.h> +#define LP_RING(d) (&((struct drm_i915_private *)(d))->ring[RCS]) + +#define BEGIN_LP_RING(n) \ + intel_ring_begin(LP_RING(dev_priv), (n)) + +#define OUT_RING(x) \ + intel_ring_emit(LP_RING(dev_priv), x) + +#define ADVANCE_LP_RING() \ + intel_ring_advance(LP_RING(dev_priv)) + +/** + * Lock test for when it's just for synchronization of ring access. + * + * In that case, we don't need to do it when GEM is initialized as nobody else + * has access to the ring. + */ +#define RING_LOCK_TEST_WITH_RETURN(dev, file) do { \ + if (LP_RING(dev->dev_private)->obj == NULL) \ + LOCK_TEST_WITH_RETURN(dev, file); \ +} while (0) + +static inline u32 +intel_read_legacy_status_page(struct drm_i915_private *dev_priv, int reg) +{ + if (I915_NEED_GFX_HWS(dev_priv->dev)) + return ioread32(dev_priv->dri1.gfx_hws_cpu_addr + reg); + else + return intel_read_status_page(LP_RING(dev_priv), reg); +} + +#define READ_HWSP(dev_priv, reg) intel_read_legacy_status_page(dev_priv, reg) +#define READ_BREADCRUMB(dev_priv) READ_HWSP(dev_priv, I915_BREADCRUMB_INDEX) +#define I915_BREADCRUMB_INDEX 0x21 + +void i915_update_dri1_breadcrumb(struct drm_device *dev) +{ + drm_i915_private_t *dev_priv = dev->dev_private; + struct drm_i915_master_private *master_priv; + + if (dev->primary->master) { + master_priv = dev->primary->master->driver_priv; + if (master_priv->sarea_priv) + master_priv->sarea_priv->last_dispatch = + READ_BREADCRUMB(dev_priv); + } +} + static void i915_write_hws_pga(struct drm_device *dev) { drm_i915_private_t *dev_priv = dev->dev_private; @@ -100,7 +146,7 @@ static void i915_free_hws(struct drm_device *dev) if (ring->status_page.gfx_addr) { ring->status_page.gfx_addr = 0; - drm_core_ioremapfree(&dev_priv->hws_map, dev); + iounmap(dev_priv->dri1.gfx_hws_cpu_addr); } /* Need to rewrite hardware status page */ @@ -198,7 +244,7 @@ static int i915_initialize(struct drm_device * dev, drm_i915_init_t * init) /* Allow hardware batchbuffers unless told otherwise. */ - dev_priv->allow_batchbuffer = 1; + dev_priv->dri1.allow_batchbuffer = 1; return 0; } @@ -210,7 +256,7 @@ static int i915_dma_resume(struct drm_device * dev) DRM_DEBUG_DRIVER("%s\n", __func__); - if (ring->map.handle == NULL) { + if (ring->virtual_start == NULL) { DRM_ERROR("can not ioremap virtual address for" " ring buffer\n"); return -ENOMEM; @@ -239,6 +285,9 @@ static int i915_dma_init(struct drm_device *dev, void *data, drm_i915_init_t *init = data; int retcode = 0; + if (drm_core_check_feature(dev, DRIVER_MODESET)) + return -ENODEV; + switch (init->func) { case I915_INIT_DMA: retcode = i915_initialize(dev, init); @@ -581,6 +630,9 @@ static int i915_flush_ioctl(struct drm_device *dev, void *data, { int ret; + if (drm_core_check_feature(dev, DRIVER_MODESET)) + return -ENODEV; + RING_LOCK_TEST_WITH_RETURN(dev, file_priv); mutex_lock(&dev->struct_mutex); @@ -601,7 +653,10 @@ static int i915_batchbuffer(struct drm_device *dev, void *data, int ret; struct drm_clip_rect *cliprects = NULL; - if (!dev_priv->allow_batchbuffer) { + if (drm_core_check_feature(dev, DRIVER_MODESET)) + return -ENODEV; + + if (!dev_priv->dri1.allow_batchbuffer) { DRM_ERROR("Batchbuffer ioctl disabled\n"); return -EINVAL; } @@ -658,6 +713,9 @@ static int i915_cmdbuffer(struct drm_device *dev, void *data, DRM_DEBUG_DRIVER("i915 cmdbuffer, buf %p sz %d cliprects %d\n", cmdbuf->buf, cmdbuf->sz, cmdbuf->num_cliprects); + if (drm_core_check_feature(dev, DRIVER_MODESET)) + return -ENODEV; + RING_LOCK_TEST_WITH_RETURN(dev, file_priv); if (cmdbuf->num_cliprects < 0) @@ -709,11 +767,166 @@ fail_batch_free: return ret; } +static int i915_emit_irq(struct drm_device * dev) +{ + drm_i915_private_t *dev_priv = dev->dev_private; + struct drm_i915_master_private *master_priv = dev->primary->master->driver_priv; + + i915_kernel_lost_context(dev); + + DRM_DEBUG_DRIVER("\n"); + + dev_priv->counter++; + if (dev_priv->counter > 0x7FFFFFFFUL) + dev_priv->counter = 1; + if (master_priv->sarea_priv) + master_priv->sarea_priv->last_enqueue = dev_priv->counter; + + if (BEGIN_LP_RING(4) == 0) { + OUT_RING(MI_STORE_DWORD_INDEX); + OUT_RING(I915_BREADCRUMB_INDEX << MI_STORE_DWORD_INDEX_SHIFT); + OUT_RING(dev_priv->counter); + OUT_RING(MI_USER_INTERRUPT); + ADVANCE_LP_RING(); + } + + return dev_priv->counter; +} + +static int i915_wait_irq(struct drm_device * dev, int irq_nr) +{ + drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private; + struct drm_i915_master_private *master_priv = dev->primary->master->driver_priv; + int ret = 0; + struct intel_ring_buffer *ring = LP_RING(dev_priv); + + DRM_DEBUG_DRIVER("irq_nr=%d breadcrumb=%d\n", irq_nr, + READ_BREADCRUMB(dev_priv)); + + if (READ_BREADCRUMB(dev_priv) >= irq_nr) { + if (master_priv->sarea_priv) + master_priv->sarea_priv->last_dispatch = READ_BREADCRUMB(dev_priv); + return 0; + } + + if (master_priv->sarea_priv) + master_priv->sarea_priv->perf_boxes |= I915_BOX_WAIT; + + if (ring->irq_get(ring)) { + DRM_WAIT_ON(ret, ring->irq_queue, 3 * DRM_HZ, + READ_BREADCRUMB(dev_priv) >= irq_nr); + ring->irq_put(ring); + } else if (wait_for(READ_BREADCRUMB(dev_priv) >= irq_nr, 3000)) + ret = -EBUSY; + + if (ret == -EBUSY) { + DRM_ERROR("EBUSY -- rec: %d emitted: %d\n", + READ_BREADCRUMB(dev_priv), (int)dev_priv->counter); + } + + return ret; +} + +/* Needs the lock as it touches the ring. + */ +static int i915_irq_emit(struct drm_device *dev, void *data, + struct drm_file *file_priv) +{ + drm_i915_private_t *dev_priv = dev->dev_private; + drm_i915_irq_emit_t *emit = data; + int result; + + if (drm_core_check_feature(dev, DRIVER_MODESET)) + return -ENODEV; + + if (!dev_priv || !LP_RING(dev_priv)->virtual_start) { + DRM_ERROR("called with no initialization\n"); + return -EINVAL; + } + + RING_LOCK_TEST_WITH_RETURN(dev, file_priv); + + mutex_lock(&dev->struct_mutex); + result = i915_emit_irq(dev); + mutex_unlock(&dev->struct_mutex); + + if (DRM_COPY_TO_USER(emit->irq_seq, &result, sizeof(int))) { + DRM_ERROR("copy_to_user\n"); + return -EFAULT; + } + + return 0; +} + +/* Doesn't need the hardware lock. + */ +static int i915_irq_wait(struct drm_device *dev, void *data, + struct drm_file *file_priv) +{ + drm_i915_private_t *dev_priv = dev->dev_private; + drm_i915_irq_wait_t *irqwait = data; + + if (drm_core_check_feature(dev, DRIVER_MODESET)) + return -ENODEV; + + if (!dev_priv) { + DRM_ERROR("called with no initialization\n"); + return -EINVAL; + } + + return i915_wait_irq(dev, irqwait->irq_seq); +} + +static int i915_vblank_pipe_get(struct drm_device *dev, void *data, + struct drm_file *file_priv) +{ + drm_i915_private_t *dev_priv = dev->dev_private; + drm_i915_vblank_pipe_t *pipe = data; + + if (drm_core_check_feature(dev, DRIVER_MODESET)) + return -ENODEV; + + if (!dev_priv) { + DRM_ERROR("called with no initialization\n"); + return -EINVAL; + } + + pipe->pipe = DRM_I915_VBLANK_PIPE_A | DRM_I915_VBLANK_PIPE_B; + + return 0; +} + +/** + * Schedule buffer swap at given vertical blank. + */ +static int i915_vblank_swap(struct drm_device *dev, void *data, + struct drm_file *file_priv) +{ + /* The delayed swap mechanism was fundamentally racy, and has been + * removed. The model was that the client requested a delayed flip/swap + * from the kernel, then waited for vblank before continuing to perform + * rendering. The problem was that the kernel might wake the client + * up before it dispatched the vblank swap (since the lock has to be + * held while touching the ringbuffer), in which case the client would + * clear and start the next frame before the swap occurred, and + * flicker would occur in addition to likely missing the vblank. + * + * In the absence of this ioctl, userland falls back to a correct path + * of waiting for a vblank, then dispatching the swap on its own. + * Context switching to userland and back is plenty fast enough for + * meeting the requirements of vblank swapping. + */ + return -EINVAL; +} + static int i915_flip_bufs(struct drm_device *dev, void *data, struct drm_file *file_priv) { int ret; + if (drm_core_check_feature(dev, DRIVER_MODESET)) + return -ENODEV; + DRM_DEBUG_DRIVER("%s\n", __func__); RING_LOCK_TEST_WITH_RETURN(dev, file_priv); @@ -742,7 +955,7 @@ static int i915_getparam(struct drm_device *dev, void *data, value = dev->pdev->irq ? 1 : 0; break; case I915_PARAM_ALLOW_BATCHBUFFER: - value = dev_priv->allow_batchbuffer ? 1 : 0; + value = dev_priv->dri1.allow_batchbuffer ? 1 : 0; break; case I915_PARAM_LAST_DISPATCH: value = READ_BREADCRUMB(dev_priv); @@ -751,7 +964,7 @@ static int i915_getparam(struct drm_device *dev, void *data, value = dev->pci_device; break; case I915_PARAM_HAS_GEM: - value = dev_priv->has_gem; + value = 1; break; case I915_PARAM_NUM_FENCES_AVAIL: value = dev_priv->num_fence_regs - dev_priv->fence_reg_start; @@ -764,13 +977,13 @@ static int i915_getparam(struct drm_device *dev, void *data, break; case I915_PARAM_HAS_EXECBUF2: /* depends on GEM */ - value = dev_priv->has_gem; + value = 1; break; case I915_PARAM_HAS_BSD: - value = HAS_BSD(dev); + value = intel_ring_initialized(&dev_priv->ring[VCS]); break; case I915_PARAM_HAS_BLT: - value = HAS_BLT(dev); + value = intel_ring_initialized(&dev_priv->ring[BCS]); break; case I915_PARAM_HAS_RELAXED_FENCING: value = 1; @@ -822,10 +1035,9 @@ static int i915_setparam(struct drm_device *dev, void *data, case I915_SETPARAM_USE_MI_BATCHBUFFER_START: break; case I915_SETPARAM_TEX_LRU_LOG_GRANULARITY: - dev_priv->tex_lru_log_granularity = param->value; break; case I915_SETPARAM_ALLOW_BATCHBUFFER: - dev_priv->allow_batchbuffer = param->value; + dev_priv->dri1.allow_batchbuffer = param->value ? 1 : 0; break; case I915_SETPARAM_NUM_USED_FENCES: if (param->value > dev_priv->num_fence_regs || @@ -850,6 +1062,9 @@ static int i915_set_status_page(struct drm_device *dev, void *data, drm_i915_hws_addr_t *hws = data; struct intel_ring_buffer *ring = LP_RING(dev_priv); + if (drm_core_check_feature(dev, DRIVER_MODESET)) + return -ENODEV; + if (!I915_NEED_GFX_HWS(dev)) return -EINVAL; @@ -867,23 +1082,17 @@ static int i915_set_status_page(struct drm_device *dev, void *data, ring->status_page.gfx_addr = hws->addr & (0x1ffff<<12); - dev_priv->hws_map.offset = dev->agp->base + hws->addr; - dev_priv->hws_map.size = 4*1024; - dev_priv->hws_map.type = 0; - dev_priv->hws_map.flags = 0; - dev_priv->hws_map.mtrr = 0; - - drm_core_ioremap_wc(&dev_priv->hws_map, dev); - if (dev_priv->hws_map.handle == NULL) { + dev_priv->dri1.gfx_hws_cpu_addr = ioremap_wc(dev->agp->base + hws->addr, + 4096); + if (dev_priv->dri1.gfx_hws_cpu_addr == NULL) { i915_dma_cleanup(dev); ring->status_page.gfx_addr = 0; DRM_ERROR("can not ioremap virtual address for" " G33 hw status page\n"); return -ENOMEM; } - ring->status_page.page_addr = - (void __force __iomem *)dev_priv->hws_map.handle; - memset_io(ring->status_page.page_addr, 0, PAGE_SIZE); + + memset_io(dev_priv->dri1.gfx_hws_cpu_addr, 0, PAGE_SIZE); I915_WRITE(HWS_PGA, ring->status_page.gfx_addr); DRM_DEBUG_DRIVER("load hws HWS_PGA with gfx mem 0x%x\n", @@ -1019,133 +1228,6 @@ intel_teardown_mchbar(struct drm_device *dev) release_resource(&dev_priv->mch_res); } -#define PTE_ADDRESS_MASK 0xfffff000 -#define PTE_ADDRESS_MASK_HIGH 0x000000f0 /* i915+ */ -#define PTE_MAPPING_TYPE_UNCACHED (0 << 1) -#define PTE_MAPPING_TYPE_DCACHE (1 << 1) /* i830 only */ -#define PTE_MAPPING_TYPE_CACHED (3 << 1) -#define PTE_MAPPING_TYPE_MASK (3 << 1) -#define PTE_VALID (1 << 0) - -/** - * i915_stolen_to_phys - take an offset into stolen memory and turn it into - * a physical one - * @dev: drm device - * @offset: address to translate - * - * Some chip functions require allocations from stolen space and need the - * physical address of the memory in question. - */ -static unsigned long i915_stolen_to_phys(struct drm_device *dev, u32 offset) -{ - struct drm_i915_private *dev_priv = dev->dev_private; - struct pci_dev *pdev = dev_priv->bridge_dev; - u32 base; - -#if 0 - /* On the machines I have tested the Graphics Base of Stolen Memory - * is unreliable, so compute the base by subtracting the stolen memory - * from the Top of Low Usable DRAM which is where the BIOS places - * the graphics stolen memory. - */ - if (INTEL_INFO(dev)->gen > 3 || IS_G33(dev)) { - /* top 32bits are reserved = 0 */ - pci_read_config_dword(pdev, 0xA4, &base); - } else { - /* XXX presume 8xx is the same as i915 */ - pci_bus_read_config_dword(pdev->bus, 2, 0x5C, &base); - } -#else - if (INTEL_INFO(dev)->gen > 3 || IS_G33(dev)) { - u16 val; - pci_read_config_word(pdev, 0xb0, &val); - base = val >> 4 << 20; - } else { - u8 val; - pci_read_config_byte(pdev, 0x9c, &val); - base = val >> 3 << 27; - } - base -= dev_priv->mm.gtt->stolen_size; -#endif - - return base + offset; -} - -static void i915_warn_stolen(struct drm_device *dev) -{ - DRM_ERROR("not enough stolen space for compressed buffer, disabling\n"); - DRM_ERROR("hint: you may be able to increase stolen memory size in the BIOS to avoid this\n"); -} - -static void i915_setup_compression(struct drm_device *dev, int size) -{ - struct drm_i915_private *dev_priv = dev->dev_private; - struct drm_mm_node *compressed_fb, *uninitialized_var(compressed_llb); - unsigned long cfb_base; - unsigned long ll_base = 0; - - /* Just in case the BIOS is doing something questionable. */ - intel_disable_fbc(dev); - - compressed_fb = drm_mm_search_free(&dev_priv->mm.stolen, size, 4096, 0); - if (compressed_fb) - compressed_fb = drm_mm_get_block(compressed_fb, size, 4096); - if (!compressed_fb) - goto err; - - cfb_base = i915_stolen_to_phys(dev, compressed_fb->start); - if (!cfb_base) - goto err_fb; - - if (!(IS_GM45(dev) || HAS_PCH_SPLIT(dev))) { - compressed_llb = drm_mm_search_free(&dev_priv->mm.stolen, - 4096, 4096, 0); - if (compressed_llb) - compressed_llb = drm_mm_get_block(compressed_llb, - 4096, 4096); - if (!compressed_llb) - goto err_fb; - - ll_base = i915_stolen_to_phys(dev, compressed_llb->start); - if (!ll_base) - goto err_llb; - } - - dev_priv->cfb_size = size; - - dev_priv->compressed_fb = compressed_fb; - if (HAS_PCH_SPLIT(dev)) - I915_WRITE(ILK_DPFC_CB_BASE, compressed_fb->start); - else if (IS_GM45(dev)) { - I915_WRITE(DPFC_CB_BASE, compressed_fb->start); - } else { - I915_WRITE(FBC_CFB_BASE, cfb_base); - I915_WRITE(FBC_LL_BASE, ll_base); - dev_priv->compressed_llb = compressed_llb; - } - - DRM_DEBUG_KMS("FBC base 0x%08lx, ll base 0x%08lx, size %dM\n", - cfb_base, ll_base, size >> 20); - return; - -err_llb: - drm_mm_put_block(compressed_llb); -err_fb: - drm_mm_put_block(compressed_fb); -err: - dev_priv->no_fbc_reason = FBC_STOLEN_TOO_SMALL; - i915_warn_stolen(dev); -} - -static void i915_cleanup_compression(struct drm_device *dev) -{ - struct drm_i915_private *dev_priv = dev->dev_private; - - drm_mm_put_block(dev_priv->compressed_fb); - if (dev_priv->compressed_llb) - drm_mm_put_block(dev_priv->compressed_llb); -} - /* true = enable decode, false = disable decoder */ static unsigned int i915_vga_set_decode(void *cookie, bool state) { @@ -1189,87 +1271,11 @@ static bool i915_switcheroo_can_switch(struct pci_dev *pdev) return can_switch; } -static bool -intel_enable_ppgtt(struct drm_device *dev) -{ - if (i915_enable_ppgtt >= 0) - return i915_enable_ppgtt; - -#ifdef CONFIG_INTEL_IOMMU - /* Disable ppgtt on SNB if VT-d is on. */ - if (INTEL_INFO(dev)->gen == 6 && intel_iommu_gfx_mapped) - return false; -#endif - - return true; -} - -static int i915_load_gem_init(struct drm_device *dev) -{ - struct drm_i915_private *dev_priv = dev->dev_private; - unsigned long prealloc_size, gtt_size, mappable_size; - int ret; - - prealloc_size = dev_priv->mm.gtt->stolen_size; - gtt_size = dev_priv->mm.gtt->gtt_total_entries << PAGE_SHIFT; - mappable_size = dev_priv->mm.gtt->gtt_mappable_entries << PAGE_SHIFT; - - /* Basic memrange allocator for stolen space */ - drm_mm_init(&dev_priv->mm.stolen, 0, prealloc_size); - - mutex_lock(&dev->struct_mutex); - if (intel_enable_ppgtt(dev) && HAS_ALIASING_PPGTT(dev)) { - /* PPGTT pdes are stolen from global gtt ptes, so shrink the - * aperture accordingly when using aliasing ppgtt. */ - gtt_size -= I915_PPGTT_PD_ENTRIES*PAGE_SIZE; - - i915_gem_init_global_gtt(dev, 0, mappable_size, gtt_size); - - ret = i915_gem_init_aliasing_ppgtt(dev); - if (ret) { - mutex_unlock(&dev->struct_mutex); - return ret; - } - } else { - /* Let GEM Manage all of the aperture. - * - * However, leave one page at the end still bound to the scratch - * page. There are a number of places where the hardware - * apparently prefetches past the end of the object, and we've - * seen multiple hangs with the GPU head pointer stuck in a - * batchbuffer bound at the last page of the aperture. One page - * should be enough to keep any prefetching inside of the - * aperture. - */ - i915_gem_init_global_gtt(dev, 0, mappable_size, - gtt_size); - } - - ret = i915_gem_init_hw(dev); - mutex_unlock(&dev->struct_mutex); - if (ret) { - i915_gem_cleanup_aliasing_ppgtt(dev); - return ret; - } - - /* Try to set up FBC with a reasonable compressed buffer size */ - if (I915_HAS_FBC(dev) && i915_powersave) { - int cfb_size; - - /* Leave 1M for line length buffer & misc. */ - - /* Try to get a 32M buffer... */ - if (prealloc_size > (36*1024*1024)) - cfb_size = 32*1024*1024; - else /* fall back to 7/8 of the stolen space */ - cfb_size = prealloc_size * 7 / 8; - i915_setup_compression(dev, cfb_size); - } - - /* Allow hardware batchbuffers unless told otherwise. */ - dev_priv->allow_batchbuffer = 1; - return 0; -} +static const struct vga_switcheroo_client_ops i915_switcheroo_ops = { + .set_gpu_state = i915_switcheroo_set_state, + .reprobe = NULL, + .can_switch = i915_switcheroo_can_switch, +}; static int i915_load_modeset_init(struct drm_device *dev) { @@ -1293,22 +1299,22 @@ static int i915_load_modeset_init(struct drm_device *dev) intel_register_dsm_handler(); - ret = vga_switcheroo_register_client(dev->pdev, - i915_switcheroo_set_state, - NULL, - i915_switcheroo_can_switch); + ret = vga_switcheroo_register_client(dev->pdev, &i915_switcheroo_ops); if (ret) goto cleanup_vga_client; - /* IIR "flip pending" bit means done if this bit is set */ - if (IS_GEN3(dev) && (I915_READ(ECOSKPD) & ECO_FLIP_DONE)) - dev_priv->flip_pending_is_done = true; + /* Initialise stolen first so that we may reserve preallocated + * objects for the BIOS to KMS transition. + */ + ret = i915_gem_init_stolen(dev); + if (ret) + goto cleanup_vga_switcheroo; intel_modeset_init(dev); - ret = i915_load_gem_init(dev); + ret = i915_gem_init(dev); if (ret) - goto cleanup_vga_switcheroo; + goto cleanup_gem_stolen; intel_modeset_gem_init(dev); @@ -1338,6 +1344,8 @@ cleanup_gem: i915_gem_cleanup_ringbuffer(dev); mutex_unlock(&dev->struct_mutex); i915_gem_cleanup_aliasing_ppgtt(dev); +cleanup_gem_stolen: + i915_gem_cleanup_stolen(dev); cleanup_vga_switcheroo: vga_switcheroo_unregister_client(dev->pdev); cleanup_vga_client: @@ -1370,575 +1378,6 @@ void i915_master_destroy(struct drm_device *dev, struct drm_master *master) master->driver_priv = NULL; } -static void i915_pineview_get_mem_freq(struct drm_device *dev) -{ - drm_i915_private_t *dev_priv = dev->dev_private; - u32 tmp; - - tmp = I915_READ(CLKCFG); - - switch (tmp & CLKCFG_FSB_MASK) { - case CLKCFG_FSB_533: - dev_priv->fsb_freq = 533; /* 133*4 */ - break; - case CLKCFG_FSB_800: - dev_priv->fsb_freq = 800; /* 200*4 */ - break; - case CLKCFG_FSB_667: - dev_priv->fsb_freq = 667; /* 167*4 */ - break; - case CLKCFG_FSB_400: - dev_priv->fsb_freq = 400; /* 100*4 */ - break; - } - - switch (tmp & CLKCFG_MEM_MASK) { - case CLKCFG_MEM_533: - dev_priv->mem_freq = 533; - break; - case CLKCFG_MEM_667: - dev_priv->mem_freq = 667; - break; - case CLKCFG_MEM_800: - dev_priv->mem_freq = 800; - break; - } - - /* detect pineview DDR3 setting */ - tmp = I915_READ(CSHRDDR3CTL); - dev_priv->is_ddr3 = (tmp & CSHRDDR3CTL_DDR3) ? 1 : 0; -} - -static void i915_ironlake_get_mem_freq(struct drm_device *dev) -{ - drm_i915_private_t *dev_priv = dev->dev_private; - u16 ddrpll, csipll; - - ddrpll = I915_READ16(DDRMPLL1); - csipll = I915_READ16(CSIPLL0); - - switch (ddrpll & 0xff) { - case 0xc: - dev_priv->mem_freq = 800; - break; - case 0x10: - dev_priv->mem_freq = 1066; - break; - case 0x14: - dev_priv->mem_freq = 1333; - break; - case 0x18: - dev_priv->mem_freq = 1600; - break; - default: - DRM_DEBUG_DRIVER("unknown memory frequency 0x%02x\n", - ddrpll & 0xff); - dev_priv->mem_freq = 0; - break; - } - - dev_priv->r_t = dev_priv->mem_freq; - - switch (csipll & 0x3ff) { - case 0x00c: - dev_priv->fsb_freq = 3200; - break; - case 0x00e: - dev_priv->fsb_freq = 3733; - break; - case 0x010: - dev_priv->fsb_freq = 4266; - break; - case 0x012: - dev_priv->fsb_freq = 4800; - break; - case 0x014: - dev_priv->fsb_freq = 5333; - break; - case 0x016: - dev_priv->fsb_freq = 5866; - break; - case 0x018: - dev_priv->fsb_freq = 6400; - break; - default: - DRM_DEBUG_DRIVER("unknown fsb frequency 0x%04x\n", - csipll & 0x3ff); - dev_priv->fsb_freq = 0; - break; - } - - if (dev_priv->fsb_freq == 3200) { - dev_priv->c_m = 0; - } else if (dev_priv->fsb_freq > 3200 && dev_priv->fsb_freq <= 4800) { - dev_priv->c_m = 1; - } else { - dev_priv->c_m = 2; - } -} - -static const struct cparams { - u16 i; - u16 t; - u16 m; - u16 c; -} cparams[] = { - { 1, 1333, 301, 28664 }, - { 1, 1066, 294, 24460 }, - { 1, 800, 294, 25192 }, - { 0, 1333, 276, 27605 }, - { 0, 1066, 276, 27605 }, - { 0, 800, 231, 23784 }, -}; - -unsigned long i915_chipset_val(struct drm_i915_private *dev_priv) -{ - u64 total_count, diff, ret; - u32 count1, count2, count3, m = 0, c = 0; - unsigned long now = jiffies_to_msecs(jiffies), diff1; - int i; - - diff1 = now - dev_priv->last_time1; - - /* Prevent division-by-zero if we are asking too fast. - * Also, we don't get interesting results if we are polling - * faster than once in 10ms, so just return the saved value - * in such cases. - */ - if (diff1 <= 10) - return dev_priv->chipset_power; - - count1 = I915_READ(DMIEC); - count2 = I915_READ(DDREC); - count3 = I915_READ(CSIEC); - - total_count = count1 + count2 + count3; - - /* FIXME: handle per-counter overflow */ - if (total_count < dev_priv->last_count1) { - diff = ~0UL - dev_priv->last_count1; - diff += total_count; - } else { - diff = total_count - dev_priv->last_count1; - } - - for (i = 0; i < ARRAY_SIZE(cparams); i++) { - if (cparams[i].i == dev_priv->c_m && - cparams[i].t == dev_priv->r_t) { - m = cparams[i].m; - c = cparams[i].c; - break; - } - } - - diff = div_u64(diff, diff1); - ret = ((m * diff) + c); - ret = div_u64(ret, 10); - - dev_priv->last_count1 = total_count; - dev_priv->last_time1 = now; - - dev_priv->chipset_power = ret; - - return ret; -} - -unsigned long i915_mch_val(struct drm_i915_private *dev_priv) -{ - unsigned long m, x, b; - u32 tsfs; - - tsfs = I915_READ(TSFS); - - m = ((tsfs & TSFS_SLOPE_MASK) >> TSFS_SLOPE_SHIFT); - x = I915_READ8(TR1); - - b = tsfs & TSFS_INTR_MASK; - - return ((m * x) / 127) - b; -} - -static u16 pvid_to_extvid(struct drm_i915_private *dev_priv, u8 pxvid) -{ - static const struct v_table { - u16 vd; /* in .1 mil */ - u16 vm; /* in .1 mil */ - } v_table[] = { - { 0, 0, }, - { 375, 0, }, - { 500, 0, }, - { 625, 0, }, - { 750, 0, }, - { 875, 0, }, - { 1000, 0, }, - { 1125, 0, }, - { 4125, 3000, }, - { 4125, 3000, }, - { 4125, 3000, }, - { 4125, 3000, }, - { 4125, 3000, }, - { 4125, 3000, }, - { 4125, 3000, }, - { 4125, 3000, }, - { 4125, 3000, }, - { 4125, 3000, }, - { 4125, 3000, }, - { 4125, 3000, }, - { 4125, 3000, }, - { 4125, 3000, }, - { 4125, 3000, }, - { 4125, 3000, }, - { 4125, 3000, }, - { 4125, 3000, }, - { 4125, 3000, }, - { 4125, 3000, }, - { 4125, 3000, }, - { 4125, 3000, }, - { 4125, 3000, }, - { 4125, 3000, }, - { 4250, 3125, }, - { 4375, 3250, }, - { 4500, 3375, }, - { 4625, 3500, }, - { 4750, 3625, }, - { 4875, 3750, }, - { 5000, 3875, }, - { 5125, 4000, }, - { 5250, 4125, }, - { 5375, 4250, }, - { 5500, 4375, }, - { 5625, 4500, }, - { 5750, 4625, }, - { 5875, 4750, }, - { 6000, 4875, }, - { 6125, 5000, }, - { 6250, 5125, }, - { 6375, 5250, }, - { 6500, 5375, }, - { 6625, 5500, }, - { 6750, 5625, }, - { 6875, 5750, }, - { 7000, 5875, }, - { 7125, 6000, }, - { 7250, 6125, }, - { 7375, 6250, }, - { 7500, 6375, }, - { 7625, 6500, }, - { 7750, 6625, }, - { 7875, 6750, }, - { 8000, 6875, }, - { 8125, 7000, }, - { 8250, 7125, }, - { 8375, 7250, }, - { 8500, 7375, }, - { 8625, 7500, }, - { 8750, 7625, }, - { 8875, 7750, }, - { 9000, 7875, }, - { 9125, 8000, }, - { 9250, 8125, }, - { 9375, 8250, }, - { 9500, 8375, }, - { 9625, 8500, }, - { 9750, 8625, }, - { 9875, 8750, }, - { 10000, 8875, }, - { 10125, 9000, }, - { 10250, 9125, }, - { 10375, 9250, }, - { 10500, 9375, }, - { 10625, 9500, }, - { 10750, 9625, }, - { 10875, 9750, }, - { 11000, 9875, }, - { 11125, 10000, }, - { 11250, 10125, }, - { 11375, 10250, }, - { 11500, 10375, }, - { 11625, 10500, }, - { 11750, 10625, }, - { 11875, 10750, }, - { 12000, 10875, }, - { 12125, 11000, }, - { 12250, 11125, }, - { 12375, 11250, }, - { 12500, 11375, }, - { 12625, 11500, }, - { 12750, 11625, }, - { 12875, 11750, }, - { 13000, 11875, }, - { 13125, 12000, }, - { 13250, 12125, }, - { 13375, 12250, }, - { 13500, 12375, }, - { 13625, 12500, }, - { 13750, 12625, }, - { 13875, 12750, }, - { 14000, 12875, }, - { 14125, 13000, }, - { 14250, 13125, }, - { 14375, 13250, }, - { 14500, 13375, }, - { 14625, 13500, }, - { 14750, 13625, }, - { 14875, 13750, }, - { 15000, 13875, }, - { 15125, 14000, }, - { 15250, 14125, }, - { 15375, 14250, }, - { 15500, 14375, }, - { 15625, 14500, }, - { 15750, 14625, }, - { 15875, 14750, }, - { 16000, 14875, }, - { 16125, 15000, }, - }; - if (dev_priv->info->is_mobile) - return v_table[pxvid].vm; - else - return v_table[pxvid].vd; -} - -void i915_update_gfx_val(struct drm_i915_private *dev_priv) -{ - struct timespec now, diff1; - u64 diff; - unsigned long diffms; - u32 count; - - if (dev_priv->info->gen != 5) - return; - - getrawmonotonic(&now); - diff1 = timespec_sub(now, dev_priv->last_time2); - - /* Don't divide by 0 */ - diffms = diff1.tv_sec * 1000 + diff1.tv_nsec / 1000000; - if (!diffms) - return; - - count = I915_READ(GFXEC); - - if (count < dev_priv->last_count2) { - diff = ~0UL - dev_priv->last_count2; - diff += count; - } else { - diff = count - dev_priv->last_count2; - } - - dev_priv->last_count2 = count; - dev_priv->last_time2 = now; - - /* More magic constants... */ - diff = diff * 1181; - diff = div_u64(diff, diffms * 10); - dev_priv->gfx_power = diff; -} - -unsigned long i915_gfx_val(struct drm_i915_private *dev_priv) -{ - unsigned long t, corr, state1, corr2, state2; - u32 pxvid, ext_v; - - pxvid = I915_READ(PXVFREQ_BASE + (dev_priv->cur_delay * 4)); - pxvid = (pxvid >> 24) & 0x7f; - ext_v = pvid_to_extvid(dev_priv, pxvid); - - state1 = ext_v; - - t = i915_mch_val(dev_priv); - - /* Revel in the empirically derived constants */ - - /* Correction factor in 1/100000 units */ - if (t > 80) - corr = ((t * 2349) + 135940); - else if (t >= 50) - corr = ((t * 964) + 29317); - else /* < 50 */ - corr = ((t * 301) + 1004); - - corr = corr * ((150142 * state1) / 10000 - 78642); - corr /= 100000; - corr2 = (corr * dev_priv->corr); - - state2 = (corr2 * state1) / 10000; - state2 /= 100; /* convert to mW */ - - i915_update_gfx_val(dev_priv); - - return dev_priv->gfx_power + state2; -} - -/* Global for IPS driver to get at the current i915 device */ -static struct drm_i915_private *i915_mch_dev; -/* - * Lock protecting IPS related data structures - * - i915_mch_dev - * - dev_priv->max_delay - * - dev_priv->min_delay - * - dev_priv->fmax - * - dev_priv->gpu_busy - */ -static DEFINE_SPINLOCK(mchdev_lock); - -/** - * i915_read_mch_val - return value for IPS use - * - * Calculate and return a value for the IPS driver to use when deciding whether - * we have thermal and power headroom to increase CPU or GPU power budget. - */ -unsigned long i915_read_mch_val(void) -{ - struct drm_i915_private *dev_priv; - unsigned long chipset_val, graphics_val, ret = 0; - - spin_lock(&mchdev_lock); - if (!i915_mch_dev) - goto out_unlock; - dev_priv = i915_mch_dev; - - chipset_val = i915_chipset_val(dev_priv); - graphics_val = i915_gfx_val(dev_priv); - - ret = chipset_val + graphics_val; - -out_unlock: - spin_unlock(&mchdev_lock); - - return ret; -} -EXPORT_SYMBOL_GPL(i915_read_mch_val); - -/** - * i915_gpu_raise - raise GPU frequency limit - * - * Raise the limit; IPS indicates we have thermal headroom. - */ -bool i915_gpu_raise(void) -{ - struct drm_i915_private *dev_priv; - bool ret = true; - - spin_lock(&mchdev_lock); - if (!i915_mch_dev) { - ret = false; - goto out_unlock; - } - dev_priv = i915_mch_dev; - - if (dev_priv->max_delay > dev_priv->fmax) - dev_priv->max_delay--; - -out_unlock: - spin_unlock(&mchdev_lock); - - return ret; -} -EXPORT_SYMBOL_GPL(i915_gpu_raise); - -/** - * i915_gpu_lower - lower GPU frequency limit - * - * IPS indicates we're close to a thermal limit, so throttle back the GPU - * frequency maximum. - */ -bool i915_gpu_lower(void) -{ - struct drm_i915_private *dev_priv; - bool ret = true; - - spin_lock(&mchdev_lock); - if (!i915_mch_dev) { - ret = false; - goto out_unlock; - } - dev_priv = i915_mch_dev; - - if (dev_priv->max_delay < dev_priv->min_delay) - dev_priv->max_delay++; - -out_unlock: - spin_unlock(&mchdev_lock); - - return ret; -} -EXPORT_SYMBOL_GPL(i915_gpu_lower); - -/** - * i915_gpu_busy - indicate GPU business to IPS - * - * Tell the IPS driver whether or not the GPU is busy. - */ -bool i915_gpu_busy(void) -{ - struct drm_i915_private *dev_priv; - bool ret = false; - - spin_lock(&mchdev_lock); - if (!i915_mch_dev) - goto out_unlock; - dev_priv = i915_mch_dev; - - ret = dev_priv->busy; - -out_unlock: - spin_unlock(&mchdev_lock); - - return ret; -} -EXPORT_SYMBOL_GPL(i915_gpu_busy); - -/** - * i915_gpu_turbo_disable - disable graphics turbo - * - * Disable graphics turbo by resetting the max frequency and setting the - * current frequency to the default. - */ -bool i915_gpu_turbo_disable(void) -{ - struct drm_i915_private *dev_priv; - bool ret = true; - - spin_lock(&mchdev_lock); - if (!i915_mch_dev) { - ret = false; - goto out_unlock; - } - dev_priv = i915_mch_dev; - - dev_priv->max_delay = dev_priv->fstart; - - if (!ironlake_set_drps(dev_priv->dev, dev_priv->fstart)) - ret = false; - -out_unlock: - spin_unlock(&mchdev_lock); - - return ret; -} -EXPORT_SYMBOL_GPL(i915_gpu_turbo_disable); - -/** - * Tells the intel_ips driver that the i915 driver is now loaded, if - * IPS got loaded first. - * - * This awkward dance is so that neither module has to depend on the - * other in order for IPS to do the appropriate communication of - * GPU turbo limits to i915. - */ -static void -ips_ping_for_i915_load(void) -{ - void (*link)(void); - - link = symbol_get(ips_link_to_i915_driver); - if (link) { - link(); - symbol_put(ips_link_to_i915_driver); - } -} - static void i915_mtrr_setup(struct drm_i915_private *dev_priv, unsigned long base, unsigned long size) @@ -2072,9 +1511,6 @@ int i915_driver_load(struct drm_device *dev, unsigned long flags) goto out_mtrrfree; } - /* enable GEM by default */ - dev_priv->has_gem = 1; - intel_irq_init(dev); /* Try to make sure MCHBAR is enabled before poking at it */ @@ -2094,11 +1530,6 @@ int i915_driver_load(struct drm_device *dev, unsigned long flags) goto out_gem_unload; } - if (IS_PINEVIEW(dev)) - i915_pineview_get_mem_freq(dev); - else if (IS_GEN5(dev)) - i915_ironlake_get_mem_freq(dev); - /* On the 945G/GM, the chipset reports the MSI capability on the * integrated graphics even though the support isn't actually there * according to the published specs. It doesn't appear to function @@ -2151,14 +1582,8 @@ int i915_driver_load(struct drm_device *dev, unsigned long flags) setup_timer(&dev_priv->hangcheck_timer, i915_hangcheck_elapsed, (unsigned long) dev); - if (IS_GEN5(dev)) { - spin_lock(&mchdev_lock); - i915_mch_dev = dev_priv; - dev_priv->mchdev_lock = &mchdev_lock; - spin_unlock(&mchdev_lock); - - ips_ping_for_i915_load(); - } + if (IS_GEN5(dev)) + intel_gpu_ips_init(dev_priv); return 0; @@ -2193,9 +1618,7 @@ int i915_driver_unload(struct drm_device *dev) struct drm_i915_private *dev_priv = dev->dev_private; int ret; - spin_lock(&mchdev_lock); - i915_mch_dev = NULL; - spin_unlock(&mchdev_lock); + intel_gpu_ips_teardown(); i915_teardown_sysfs(dev); @@ -2203,9 +1626,10 @@ int i915_driver_unload(struct drm_device *dev) unregister_shrinker(&dev_priv->mm.inactive_shrinker); mutex_lock(&dev->struct_mutex); - ret = i915_gpu_idle(dev, true); + ret = i915_gpu_idle(dev); if (ret) DRM_ERROR("failed to idle hardware: %d\n", ret); + i915_gem_retire_requests(dev); mutex_unlock(&dev->struct_mutex); /* Cancel the retire work handler, which should be idle now. */ @@ -2257,8 +1681,7 @@ int i915_driver_unload(struct drm_device *dev) i915_gem_cleanup_ringbuffer(dev); mutex_unlock(&dev->struct_mutex); i915_gem_cleanup_aliasing_ppgtt(dev); - if (I915_HAS_FBC(dev) && i915_powersave) - i915_cleanup_compression(dev); + i915_gem_cleanup_stolen(dev); drm_mm_takedown(&dev_priv->mm.stolen); intel_cleanup_overlay(dev); @@ -2351,7 +1774,7 @@ struct drm_ioctl_desc i915_ioctls[] = { DRM_IOCTL_DEF_DRV(I915_INIT_HEAP, drm_noop, DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY), DRM_IOCTL_DEF_DRV(I915_CMDBUFFER, i915_cmdbuffer, DRM_AUTH), DRM_IOCTL_DEF_DRV(I915_DESTROY_HEAP, drm_noop, DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY), - DRM_IOCTL_DEF_DRV(I915_SET_VBLANK_PIPE, i915_vblank_pipe_set, DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY), + DRM_IOCTL_DEF_DRV(I915_SET_VBLANK_PIPE, drm_noop, DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY), DRM_IOCTL_DEF_DRV(I915_GET_VBLANK_PIPE, i915_vblank_pipe_get, DRM_AUTH), DRM_IOCTL_DEF_DRV(I915_VBLANK_SWAP, i915_vblank_swap, DRM_AUTH), DRM_IOCTL_DEF_DRV(I915_HWS_ADDR, i915_set_status_page, DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY), diff --git a/drivers/gpu/drm/i915/i915_drv.c b/drivers/gpu/drm/i915/i915_drv.c index 3effcf7..7b967d5 100644 --- a/drivers/gpu/drm/i915/i915_drv.c +++ b/drivers/gpu/drm/i915/i915_drv.c @@ -345,6 +345,13 @@ static const struct pci_device_id pciidlist[] = { /* aka */ INTEL_VGA_DEVICE(0x0162, &intel_ivybridge_d_info), /* GT2 desktop */ INTEL_VGA_DEVICE(0x015a, &intel_ivybridge_d_info), /* GT1 server */ INTEL_VGA_DEVICE(0x016a, &intel_ivybridge_d_info), /* GT2 server */ + INTEL_VGA_DEVICE(0x0402, &intel_haswell_d_info), /* GT1 desktop */ + INTEL_VGA_DEVICE(0x0412, &intel_haswell_d_info), /* GT2 desktop */ + INTEL_VGA_DEVICE(0x040a, &intel_haswell_d_info), /* GT1 server */ + INTEL_VGA_DEVICE(0x041a, &intel_haswell_d_info), /* GT2 server */ + INTEL_VGA_DEVICE(0x0406, &intel_haswell_m_info), /* GT1 mobile */ + INTEL_VGA_DEVICE(0x0416, &intel_haswell_m_info), /* GT2 mobile */ + INTEL_VGA_DEVICE(0x0c16, &intel_haswell_d_info), /* SDV */ {0, 0, 0} }; @@ -377,18 +384,23 @@ void intel_detect_pch(struct drm_device *dev) if (id == INTEL_PCH_IBX_DEVICE_ID_TYPE) { dev_priv->pch_type = PCH_IBX; + dev_priv->num_pch_pll = 2; DRM_DEBUG_KMS("Found Ibex Peak PCH\n"); } else if (id == INTEL_PCH_CPT_DEVICE_ID_TYPE) { dev_priv->pch_type = PCH_CPT; + dev_priv->num_pch_pll = 2; DRM_DEBUG_KMS("Found CougarPoint PCH\n"); } else if (id == INTEL_PCH_PPT_DEVICE_ID_TYPE) { /* PantherPoint is CPT compatible */ dev_priv->pch_type = PCH_CPT; + dev_priv->num_pch_pll = 2; DRM_DEBUG_KMS("Found PatherPoint PCH\n"); } else if (id == INTEL_PCH_LPT_DEVICE_ID_TYPE) { dev_priv->pch_type = PCH_LPT; + dev_priv->num_pch_pll = 0; DRM_DEBUG_KMS("Found LynxPoint PCH\n"); } + BUG_ON(dev_priv->num_pch_pll > I915_NUM_PLLS); } pci_dev_put(pch); } @@ -402,9 +414,11 @@ bool i915_semaphore_is_enabled(struct drm_device *dev) if (i915_semaphores >= 0) return i915_semaphores; +#ifdef CONFIG_INTEL_IOMMU /* Enable semaphores on SNB when IO remapping is off */ - if (INTEL_INFO(dev)->gen == 6) - return !intel_iommu_enabled; + if (INTEL_INFO(dev)->gen == 6 && intel_iommu_gfx_mapped) + return false; +#endif return 1; } @@ -433,7 +447,7 @@ void __gen6_gt_force_wake_mt_get(struct drm_i915_private *dev_priv) while (count++ < 50 && (I915_READ_NOTRACE(FORCEWAKE_MT_ACK) & 1)) udelay(10); - I915_WRITE_NOTRACE(FORCEWAKE_MT, (1<<16) | 1); + I915_WRITE_NOTRACE(FORCEWAKE_MT, _MASKED_BIT_ENABLE(1)); POSTING_READ(FORCEWAKE_MT); count = 0; @@ -475,7 +489,7 @@ void __gen6_gt_force_wake_put(struct drm_i915_private *dev_priv) void __gen6_gt_force_wake_mt_put(struct drm_i915_private *dev_priv) { - I915_WRITE_NOTRACE(FORCEWAKE_MT, (1<<16) | 0); + I915_WRITE_NOTRACE(FORCEWAKE_MT, _MASKED_BIT_DISABLE(1)); /* The below doubles as a POSTING_READ */ gen6_gt_check_fifodbg(dev_priv); } @@ -617,15 +631,16 @@ static int i915_drm_thaw(struct drm_device *dev) /* KMS EnterVT equivalent */ if (drm_core_check_feature(dev, DRIVER_MODESET)) { + if (HAS_PCH_SPLIT(dev)) + ironlake_init_pch_refclk(dev); + mutex_lock(&dev->struct_mutex); dev_priv->mm.suspended = 0; error = i915_gem_init_hw(dev); mutex_unlock(&dev->struct_mutex); - if (HAS_PCH_SPLIT(dev)) - ironlake_init_pch_refclk(dev); - + intel_modeset_init_hw(dev); drm_mode_config_reset(dev); drm_irq_install(dev); @@ -633,9 +648,6 @@ static int i915_drm_thaw(struct drm_device *dev) mutex_lock(&dev->mode_config.mutex); drm_helper_resume_force_mode(dev); mutex_unlock(&dev->mode_config.mutex); - - if (IS_IRONLAKE_M(dev)) - ironlake_enable_rc6(dev); } intel_opregion_init(dev); @@ -668,7 +680,7 @@ int i915_resume(struct drm_device *dev) return 0; } -static int i8xx_do_reset(struct drm_device *dev, u8 flags) +static int i8xx_do_reset(struct drm_device *dev) { struct drm_i915_private *dev_priv = dev->dev_private; @@ -702,11 +714,12 @@ static int i965_reset_complete(struct drm_device *dev) { u8 gdrst; pci_read_config_byte(dev->pdev, I965_GDRST, &gdrst); - return gdrst & 0x1; + return (gdrst & GRDOM_RESET_ENABLE) == 0; } -static int i965_do_reset(struct drm_device *dev, u8 flags) +static int i965_do_reset(struct drm_device *dev) { + int ret; u8 gdrst; /* @@ -715,20 +728,43 @@ static int i965_do_reset(struct drm_device *dev, u8 flags) * 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); + pci_write_config_byte(dev->pdev, I965_GDRST, + gdrst | GRDOM_RENDER | + GRDOM_RESET_ENABLE); + ret = wait_for(i965_reset_complete(dev), 500); + if (ret) + return ret; + + /* We can't reset render&media without also resetting display ... */ + pci_read_config_byte(dev->pdev, I965_GDRST, &gdrst); + pci_write_config_byte(dev->pdev, I965_GDRST, + gdrst | GRDOM_MEDIA | + GRDOM_RESET_ENABLE); return wait_for(i965_reset_complete(dev), 500); } -static int ironlake_do_reset(struct drm_device *dev, u8 flags) +static int ironlake_do_reset(struct drm_device *dev) { 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); + u32 gdrst; + int ret; + + gdrst = I915_READ(MCHBAR_MIRROR_BASE + ILK_GDSR); + I915_WRITE(MCHBAR_MIRROR_BASE + ILK_GDSR, + gdrst | GRDOM_RENDER | GRDOM_RESET_ENABLE); + ret = wait_for(I915_READ(MCHBAR_MIRROR_BASE + ILK_GDSR) & 0x1, 500); + if (ret) + return ret; + + /* We can't reset render&media without also resetting display ... */ + gdrst = I915_READ(MCHBAR_MIRROR_BASE + ILK_GDSR); + I915_WRITE(MCHBAR_MIRROR_BASE + ILK_GDSR, + gdrst | GRDOM_MEDIA | GRDOM_RESET_ENABLE); return wait_for(I915_READ(MCHBAR_MIRROR_BASE + ILK_GDSR) & 0x1, 500); } -static int gen6_do_reset(struct drm_device *dev, u8 flags) +static int gen6_do_reset(struct drm_device *dev) { struct drm_i915_private *dev_priv = dev->dev_private; int ret; @@ -763,10 +799,44 @@ static int gen6_do_reset(struct drm_device *dev, u8 flags) return ret; } +static int intel_gpu_reset(struct drm_device *dev) +{ + struct drm_i915_private *dev_priv = dev->dev_private; + int ret = -ENODEV; + + switch (INTEL_INFO(dev)->gen) { + case 7: + case 6: + ret = gen6_do_reset(dev); + break; + case 5: + ret = ironlake_do_reset(dev); + break; + case 4: + ret = i965_do_reset(dev); + break; + case 2: + ret = i8xx_do_reset(dev); + break; + } + + /* Also reset the gpu hangman. */ + if (dev_priv->stop_rings) { + DRM_DEBUG("Simulated gpu hang, resetting stop_rings\n"); + dev_priv->stop_rings = 0; + if (ret == -ENODEV) { + DRM_ERROR("Reset not implemented, but ignoring " + "error for simulated gpu hangs\n"); + ret = 0; + } + } + + return ret; +} + /** * i915_reset - reset chip after a hang * @dev: drm device to reset - * @flags: reset domains * * Reset the chip. Useful if a hang is detected. Returns zero on successful * reset or otherwise an error code. @@ -779,14 +849,9 @@ static int gen6_do_reset(struct drm_device *dev, u8 flags) * - re-init interrupt state * - re-init display */ -int i915_reset(struct drm_device *dev, u8 flags) +int i915_reset(struct drm_device *dev) { drm_i915_private_t *dev_priv = dev->dev_private; - /* - * We really should only reset the display subsystem if we actually - * need to - */ - bool need_display = true; int ret; if (!i915_try_reset) @@ -795,26 +860,16 @@ int i915_reset(struct drm_device *dev, u8 flags) if (!mutex_trylock(&dev->struct_mutex)) return -EBUSY; + dev_priv->stop_rings = 0; + i915_gem_reset(dev); ret = -ENODEV; - if (get_seconds() - dev_priv->last_gpu_reset < 5) { + 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 7: - case 6: - ret = gen6_do_reset(dev, flags); - break; - 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; - } + else + ret = intel_gpu_reset(dev); + dev_priv->last_gpu_reset = get_seconds(); if (ret) { DRM_ERROR("Failed to reset chip.\n"); @@ -838,15 +893,15 @@ int i915_reset(struct drm_device *dev, u8 flags) */ if (drm_core_check_feature(dev, DRIVER_MODESET) || !dev_priv->mm.suspended) { + struct intel_ring_buffer *ring; + int i; + dev_priv->mm.suspended = 0; i915_gem_init_swizzling(dev); - dev_priv->ring[RCS].init(&dev_priv->ring[RCS]); - if (HAS_BSD(dev)) - dev_priv->ring[VCS].init(&dev_priv->ring[VCS]); - if (HAS_BLT(dev)) - dev_priv->ring[BCS].init(&dev_priv->ring[BCS]); + for_each_ring(ring, dev_priv, i) + ring->init(ring); i915_gem_init_ppgtt(dev); @@ -856,23 +911,9 @@ int i915_reset(struct drm_device *dev, u8 flags) intel_modeset_init_hw(dev); drm_irq_uninstall(dev); - drm_mode_config_reset(dev); drm_irq_install(dev); - - mutex_lock(&dev->struct_mutex); - } - - mutex_unlock(&dev->struct_mutex); - - /* - * 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) { - mutex_lock(&dev->mode_config.mutex); - drm_helper_resume_force_mode(dev); - mutex_unlock(&dev->mode_config.mutex); + } else { + mutex_unlock(&dev->struct_mutex); } return 0; @@ -971,7 +1012,7 @@ static const struct dev_pm_ops i915_pm_ops = { .restore = i915_pm_resume, }; -static struct vm_operations_struct i915_gem_vm_ops = { +static const struct vm_operations_struct i915_gem_vm_ops = { .fault = i915_gem_fault, .open = drm_gem_vm_open, .close = drm_gem_vm_close, diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h index 69e1539..11c7a6a 100644 --- a/drivers/gpu/drm/i915/i915_drv.h +++ b/drivers/gpu/drm/i915/i915_drv.h @@ -39,6 +39,7 @@ #include <drm/intel-gtt.h> #include <linux/backlight.h> #include <linux/intel-iommu.h> +#include <linux/kref.h> /* General customization: */ @@ -78,6 +79,16 @@ enum port { #define for_each_pipe(p) for ((p) = 0; (p) < dev_priv->num_pipe; (p)++) +struct intel_pch_pll { + int refcount; /* count of number of CRTCs sharing this PLL */ + int active; /* count of number of active CRTCs (i.e. DPMS on) */ + bool on; /* is the PLL actually active? Disabled during modeset */ + int pll_reg; + int fp0_reg; + int fp1_reg; +}; +#define I915_NUM_PLLS 2 + /* Interface history: * * 1.1: Original. @@ -122,11 +133,11 @@ struct opregion_asle; struct drm_i915_private; struct intel_opregion { - struct opregion_header *header; - struct opregion_acpi *acpi; - struct opregion_swsci *swsci; - struct opregion_asle *asle; - void *vbt; + struct opregion_header __iomem *header; + struct opregion_acpi __iomem *acpi; + struct opregion_swsci __iomem *swsci; + struct opregion_asle __iomem *asle; + void __iomem *vbt; u32 __iomem *lid_state; }; #define OPREGION_SIZE (8*1024) @@ -161,8 +172,11 @@ struct sdvo_device_mapping { struct intel_display_error_state; struct drm_i915_error_state { + struct kref ref; u32 eir; u32 pgtbl_er; + u32 ier; + bool waiting[I915_NUM_RINGS]; u32 pipestat[I915_MAX_PIPES]; u32 tail[I915_NUM_RINGS]; u32 head[I915_NUM_RINGS]; @@ -228,11 +242,15 @@ struct drm_i915_display_funcs { void (*update_wm)(struct drm_device *dev); void (*update_sprite_wm)(struct drm_device *dev, int pipe, uint32_t sprite_width, int pixel_size); + void (*sanitize_pm)(struct drm_device *dev); + void (*update_linetime_wm)(struct drm_device *dev, int pipe, + struct drm_display_mode *mode); int (*crtc_mode_set)(struct drm_crtc *crtc, struct drm_display_mode *mode, struct drm_display_mode *adjusted_mode, int x, int y, struct drm_framebuffer *old_fb); + void (*off)(struct drm_crtc *crtc); void (*write_eld)(struct drm_connector *connector, struct drm_crtc *crtc); void (*fdi_link_train)(struct drm_crtc *crtc); @@ -328,7 +346,6 @@ typedef struct drm_i915_private { const struct intel_device_info *info; - int has_gem; int relative_constants_mode; void __iomem *regs; @@ -357,7 +374,6 @@ typedef struct drm_i915_private { drm_dma_handle_t *status_page_dmah; uint32_t counter; - drm_local_map_t hws_map; struct drm_i915_gem_object *pwrctx; struct drm_i915_gem_object *renderctx; @@ -386,22 +402,20 @@ typedef struct drm_i915_private { u32 hotplug_supported_mask; struct work_struct hotplug_work; - int tex_lru_log_granularity; - int allow_batchbuffer; unsigned int sr01, adpa, ppcr, dvob, dvoc, lvds; - int vblank_pipe; int num_pipe; + int num_pch_pll; /* For hangcheck timer */ #define DRM_I915_HANGCHECK_PERIOD 1500 /* in ms */ struct timer_list hangcheck_timer; int hangcheck_count; - uint32_t last_acthd; - uint32_t last_acthd_bsd; - uint32_t last_acthd_blt; + uint32_t last_acthd[I915_NUM_RINGS]; uint32_t last_instdone; uint32_t last_instdone1; + unsigned int stop_rings; + unsigned long cfb_size; unsigned int cfb_fb; enum plane cfb_plane; @@ -453,6 +467,7 @@ typedef struct drm_i915_private { unsigned int fsb_freq, mem_freq, is_ddr3; spinlock_t error_lock; + /* Protected by dev->error_lock. */ struct drm_i915_error_state *first_error; struct work_struct error_work; struct completion error_completion; @@ -677,24 +692,10 @@ 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; /** - * List of objects currently pending being freed. - * - * These objects are no longer in use, but due to a signal - * we were prevented from freeing them at the appointed time. - */ - struct list_head deferred_free_list; - - /** * We leave the user IRQ off as much as possible, * but this means that requests will finish and never * be retired once the system goes idle. Set a timer to @@ -742,6 +743,16 @@ typedef struct drm_i915_private { size_t object_memory; u32 object_count; } mm; + + /* Old dri1 support infrastructure, beware the dragons ya fools entering + * here! */ + struct { + unsigned allow_batchbuffer : 1; + u32 __iomem *gfx_hws_cpu_addr; + } dri1; + + /* Kernel Modesetting */ + struct sdvo_device_mapping sdvo_mappings[2]; /* indicate whether the LVDS_BORDER should be enabled or not */ unsigned int lvds_border_bits; @@ -751,7 +762,8 @@ typedef struct drm_i915_private { struct drm_crtc *plane_to_crtc_mapping[3]; struct drm_crtc *pipe_to_crtc_mapping[3]; wait_queue_head_t pending_flip_queue; - bool flip_pending_is_done; + + struct intel_pch_pll pch_plls[I915_NUM_PLLS]; /* Reclocking support */ bool render_reclock_avail; @@ -806,6 +818,11 @@ typedef struct drm_i915_private { struct drm_property *force_audio_property; } drm_i915_private_t; +/* Iterate over initialised rings */ +#define for_each_ring(ring__, dev_priv__, i__) \ + for ((i__) = 0; (i__) < I915_NUM_RINGS; (i__)++) \ + if (((ring__) = &(dev_priv__)->ring[(i__)]), intel_ring_initialized((ring__))) + enum hdmi_force_audio { HDMI_AUDIO_OFF_DVI = -2, /* no aux data for HDMI-DVI converter */ HDMI_AUDIO_OFF, /* force turn off HDMI audio */ @@ -869,7 +886,14 @@ struct drm_i915_gem_object { * Current tiling mode for the object. */ unsigned int tiling_mode:2; - unsigned int tiling_changed:1; + /** + * Whether the tiling parameters for the currently associated fence + * register have changed. Note that for the purposes of tracking + * tiling changes we also treat the unfenced register, the register + * slot that the object occupies whilst it executes a fenced + * command (such as BLT on gen2/3), as a "fence". + */ + unsigned int fence_dirty:1; /** How many users have pinned this object in GTT space. The following * users can each hold at most one reference: pwrite/pread, pin_ioctl @@ -1116,6 +1140,7 @@ extern int i915_master_create(struct drm_device *dev, struct drm_master *master) extern void i915_master_destroy(struct drm_device *dev, struct drm_master *master); /* i915_dma.c */ +void i915_update_dri1_breadcrumb(struct drm_device *dev); extern void i915_kernel_lost_context(struct drm_device * dev); extern int i915_driver_load(struct drm_device *, unsigned long flags); extern int i915_driver_unload(struct drm_device *); @@ -1133,7 +1158,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 *box, int DR1, int DR4); -extern int i915_reset(struct drm_device *dev, u8 flags); +extern int i915_reset(struct drm_device *dev); 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); @@ -1143,19 +1168,10 @@ extern void i915_update_gfx_val(struct drm_i915_private *dev_priv); /* i915_irq.c */ void i915_hangcheck_elapsed(unsigned long data); void i915_handle_error(struct drm_device *dev, bool wedged); -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, - struct drm_file *file_priv); extern void intel_irq_init(struct drm_device *dev); -extern int i915_vblank_pipe_set(struct drm_device *dev, void *data, - struct drm_file *file_priv); -extern int i915_vblank_pipe_get(struct drm_device *dev, void *data, - struct drm_file *file_priv); -extern int i915_vblank_swap(struct drm_device *dev, void *data, - struct drm_file *file_priv); +void i915_error_state_free(struct kref *error_ref); void i915_enable_pipestat(drm_i915_private_t *dev_priv, int pipe, u32 mask); @@ -1287,18 +1303,18 @@ int __must_check i915_gem_object_set_domain(struct drm_i915_gem_object *obj, uint32_t read_domains, uint32_t write_domain); int __must_check i915_gem_object_finish_gpu(struct drm_i915_gem_object *obj); +int __must_check i915_gem_init(struct drm_device *dev); int __must_check i915_gem_init_hw(struct drm_device *dev); void i915_gem_init_swizzling(struct drm_device *dev); void i915_gem_init_ppgtt(struct drm_device *dev); void i915_gem_cleanup_ringbuffer(struct drm_device *dev); -int __must_check i915_gpu_idle(struct drm_device *dev, bool do_retire); +int __must_check i915_gpu_idle(struct drm_device *dev); int __must_check i915_gem_idle(struct drm_device *dev); int __must_check i915_add_request(struct intel_ring_buffer *ring, struct drm_file *file, struct drm_i915_gem_request *request); int __must_check i915_wait_request(struct intel_ring_buffer *ring, - uint32_t seqno, - bool do_retire); + uint32_t seqno); int i915_gem_fault(struct vm_area_struct *vma, struct vm_fault *vmf); int __must_check i915_gem_object_set_to_gtt_domain(struct drm_i915_gem_object *obj, @@ -1349,10 +1365,11 @@ void i915_gem_init_global_gtt(struct drm_device *dev, /* i915_gem_evict.c */ int __must_check i915_gem_evict_something(struct drm_device *dev, int min_size, unsigned alignment, bool mappable); -int __must_check i915_gem_evict_everything(struct drm_device *dev, - bool purgeable_only); -int __must_check i915_gem_evict_inactive(struct drm_device *dev, - bool purgeable_only); +int i915_gem_evict_everything(struct drm_device *dev, bool purgeable_only); + +/* i915_gem_stolen.c */ +int i915_gem_init_stolen(struct drm_device *dev); +void i915_gem_cleanup_stolen(struct drm_device *dev); /* i915_gem_tiling.c */ void i915_gem_detect_bit_6_swizzle(struct drm_device *dev); @@ -1467,28 +1484,6 @@ extern void intel_display_print_error_state(struct seq_file *m, struct intel_display_error_state *error); #endif -#define LP_RING(d) (&((struct drm_i915_private *)(d))->ring[RCS]) - -#define BEGIN_LP_RING(n) \ - intel_ring_begin(LP_RING(dev_priv), (n)) - -#define OUT_RING(x) \ - intel_ring_emit(LP_RING(dev_priv), x) - -#define ADVANCE_LP_RING() \ - intel_ring_advance(LP_RING(dev_priv)) - -/** - * Lock test for when it's just for synchronization of ring access. - * - * In that case, we don't need to do it when GEM is initialized as nobody else - * has access to the ring. - */ -#define RING_LOCK_TEST_WITH_RETURN(dev, file) do { \ - if (LP_RING(dev->dev_private)->obj == NULL) \ - LOCK_TEST_WITH_RETURN(dev, file); \ -} while (0) - /* On SNB platform, before reading ring registers forcewake bit * must be set to prevent GT core from power down and stale values being * returned. diff --git a/drivers/gpu/drm/i915/i915_gem.c b/drivers/gpu/drm/i915/i915_gem.c index dd87937..6d2180c 100644 --- a/drivers/gpu/drm/i915/i915_gem.c +++ b/drivers/gpu/drm/i915/i915_gem.c @@ -46,7 +46,6 @@ static int i915_gem_phys_pwrite(struct drm_device *dev, struct drm_i915_gem_object *obj, struct drm_i915_gem_pwrite *args, struct drm_file *file); -static void i915_gem_free_object_tail(struct drm_i915_gem_object *obj); static void i915_gem_write_fence(struct drm_device *dev, int reg, struct drm_i915_gem_object *obj); @@ -66,7 +65,7 @@ static inline void i915_gem_object_fence_lost(struct drm_i915_gem_object *obj) /* As we do not have an associated fence register, we will force * a tiling change if we ever need to acquire one. */ - obj->tiling_changed = false; + obj->fence_dirty = false; obj->fence_reg = I915_FENCE_REG_NONE; } @@ -132,7 +131,7 @@ int i915_mutex_lock_interruptible(struct drm_device *dev) static inline bool i915_gem_object_is_inactive(struct drm_i915_gem_object *obj) { - return obj->gtt_space && !obj->active && obj->pin_count == 0; + return !obj->active; } int @@ -141,6 +140,9 @@ i915_gem_init_ioctl(struct drm_device *dev, void *data, { struct drm_i915_gem_init *args = data; + if (drm_core_check_feature(dev, DRIVER_MODESET)) + return -ENODEV; + if (args->gtt_start >= args->gtt_end || (args->gtt_end | args->gtt_start) & (PAGE_SIZE - 1)) return -EINVAL; @@ -166,13 +168,11 @@ i915_gem_get_aperture_ioctl(struct drm_device *dev, void *data, struct drm_i915_gem_object *obj; size_t pinned; - if (!(dev->driver->driver_features & DRIVER_GEM)) - return -ENODEV; - pinned = 0; mutex_lock(&dev->struct_mutex); - list_for_each_entry(obj, &dev_priv->mm.pinned_list, mm_list) - pinned += obj->gtt_space->size; + list_for_each_entry(obj, &dev_priv->mm.gtt_list, gtt_list) + if (obj->pin_count) + pinned += obj->gtt_space->size; mutex_unlock(&dev->struct_mutex); args->aper_size = dev_priv->mm.gtt_total; @@ -243,6 +243,7 @@ i915_gem_create_ioctl(struct drm_device *dev, void *data, struct drm_file *file) { struct drm_i915_gem_create *args = data; + return i915_gem_create(file, dev, args->size, &args->handle); } @@ -282,8 +283,8 @@ __copy_to_user_swizzled(char __user *cpu_vaddr, } static inline int -__copy_from_user_swizzled(char __user *gpu_vaddr, int gpu_offset, - const char *cpu_vaddr, +__copy_from_user_swizzled(char *gpu_vaddr, int gpu_offset, + const char __user *cpu_vaddr, int length) { int ret, cpu_offset = 0; @@ -558,11 +559,14 @@ fast_user_write(struct io_mapping *mapping, char __user *user_data, int length) { - char *vaddr_atomic; + void __iomem *vaddr_atomic; + void *vaddr; unsigned long unwritten; vaddr_atomic = io_mapping_map_atomic_wc(mapping, page_base); - unwritten = __copy_from_user_inatomic_nocache(vaddr_atomic + page_offset, + /* We can use the cpu mem copy function because this is X86. */ + vaddr = (void __force*)vaddr_atomic + page_offset; + unwritten = __copy_from_user_inatomic_nocache(vaddr, user_data, length); io_mapping_unmap_atomic(vaddr_atomic); return unwritten; @@ -925,9 +929,6 @@ i915_gem_set_domain_ioctl(struct drm_device *dev, void *data, uint32_t write_domain = args->write_domain; int ret; - if (!(dev->driver->driver_features & DRIVER_GEM)) - return -ENODEV; - /* Only handle setting domains to types used by the CPU. */ if (write_domain & I915_GEM_GPU_DOMAINS) return -EINVAL; @@ -981,9 +982,6 @@ i915_gem_sw_finish_ioctl(struct drm_device *dev, void *data, struct drm_i915_gem_object *obj; int ret = 0; - if (!(dev->driver->driver_features & DRIVER_GEM)) - return -ENODEV; - ret = i915_mutex_lock_interruptible(dev); if (ret) return ret; @@ -1019,9 +1017,6 @@ i915_gem_mmap_ioctl(struct drm_device *dev, void *data, struct drm_gem_object *obj; unsigned long addr; - if (!(dev->driver->driver_features & DRIVER_GEM)) - return -ENODEV; - obj = drm_gem_object_lookup(dev, file, args->handle); if (obj == NULL) return -ENOENT; @@ -1247,9 +1242,6 @@ i915_gem_mmap_gtt(struct drm_file *file, struct drm_i915_gem_object *obj; int ret; - if (!(dev->driver->driver_features & DRIVER_GEM)) - return -ENODEV; - ret = i915_mutex_lock_interruptible(dev); if (ret) return ret; @@ -1307,9 +1299,6 @@ i915_gem_mmap_gtt_ioctl(struct drm_device *dev, void *data, { struct drm_i915_gem_mmap_gtt *args = data; - if (!(dev->driver->driver_features & DRIVER_GEM)) - return -ENODEV; - return i915_gem_mmap_gtt(file, dev, args->handle, &args->offset); } @@ -1450,10 +1439,7 @@ i915_gem_object_move_to_inactive(struct drm_i915_gem_object *obj) struct drm_device *dev = obj->base.dev; struct drm_i915_private *dev_priv = dev->dev_private; - if (obj->pin_count != 0) - list_move_tail(&obj->mm_list, &dev_priv->mm.pinned_list); - else - list_move_tail(&obj->mm_list, &dev_priv->mm.inactive_list); + list_move_tail(&obj->mm_list, &dev_priv->mm.inactive_list); BUG_ON(!list_empty(&obj->gpu_write_list)); BUG_ON(!obj->active); @@ -1669,10 +1655,11 @@ void i915_gem_reset(struct drm_device *dev) { struct drm_i915_private *dev_priv = dev->dev_private; struct drm_i915_gem_object *obj; + struct intel_ring_buffer *ring; int i; - for (i = 0; i < I915_NUM_RINGS; i++) - i915_gem_reset_ring_lists(dev_priv, &dev_priv->ring[i]); + for_each_ring(ring, dev_priv, i) + i915_gem_reset_ring_lists(dev_priv, 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 @@ -1777,24 +1764,11 @@ void i915_gem_retire_requests(struct drm_device *dev) { drm_i915_private_t *dev_priv = dev->dev_private; + struct intel_ring_buffer *ring; int i; - if (!list_empty(&dev_priv->mm.deferred_free_list)) { - struct drm_i915_gem_object *obj, *next; - - /* We must be careful that during unbind() we do not - * accidentally infinitely recurse into retire requests. - * Currently: - * retire -> free -> unbind -> wait -> retire_ring - */ - list_for_each_entry_safe(obj, next, - &dev_priv->mm.deferred_free_list, - mm_list) - i915_gem_free_object_tail(obj); - } - - for (i = 0; i < I915_NUM_RINGS; i++) - i915_gem_retire_requests_ring(&dev_priv->ring[i]); + for_each_ring(ring, dev_priv, i) + i915_gem_retire_requests_ring(ring); } static void @@ -1802,6 +1776,7 @@ i915_gem_retire_work_handler(struct work_struct *work) { drm_i915_private_t *dev_priv; struct drm_device *dev; + struct intel_ring_buffer *ring; bool idle; int i; @@ -1821,9 +1796,7 @@ i915_gem_retire_work_handler(struct work_struct *work) * objects indefinitely. */ idle = true; - for (i = 0; i < I915_NUM_RINGS; i++) { - struct intel_ring_buffer *ring = &dev_priv->ring[i]; - + for_each_ring(ring, dev_priv, i) { if (!list_empty(&ring->gpu_write_list)) { struct drm_i915_gem_request *request; int ret; @@ -1845,20 +1818,10 @@ i915_gem_retire_work_handler(struct work_struct *work) mutex_unlock(&dev->struct_mutex); } -/** - * Waits for a sequence number to be signaled, and cleans up the - * request and object lists appropriately for that event. - */ -int -i915_wait_request(struct intel_ring_buffer *ring, - uint32_t seqno, - bool do_retire) +static int +i915_gem_check_wedge(struct drm_i915_private *dev_priv) { - drm_i915_private_t *dev_priv = ring->dev->dev_private; - u32 ier; - int ret = 0; - - BUG_ON(seqno == 0); + BUG_ON(!mutex_is_locked(&dev_priv->dev->struct_mutex)); if (atomic_read(&dev_priv->mm.wedged)) { struct completion *x = &dev_priv->error_completion; @@ -1873,6 +1836,20 @@ i915_wait_request(struct intel_ring_buffer *ring, return recovery_complete ? -EIO : -EAGAIN; } + return 0; +} + +/* + * Compare seqno against outstanding lazy request. Emit a request if they are + * equal. + */ +static int +i915_gem_check_olr(struct intel_ring_buffer *ring, u32 seqno) +{ + int ret = 0; + + BUG_ON(!mutex_is_locked(&ring->dev->struct_mutex)); + if (seqno == ring->outstanding_lazy_request) { struct drm_i915_gem_request *request; @@ -1886,56 +1863,67 @@ i915_wait_request(struct intel_ring_buffer *ring, return ret; } - seqno = request->seqno; + BUG_ON(seqno != request->seqno); } - if (!i915_seqno_passed(ring->get_seqno(ring), seqno)) { - if (HAS_PCH_SPLIT(ring->dev)) - ier = I915_READ(DEIER) | I915_READ(GTIER); - else if (IS_VALLEYVIEW(ring->dev)) - ier = I915_READ(GTIER) | I915_READ(VLV_IER); - else - ier = I915_READ(IER); - if (!ier) { - DRM_ERROR("something (likely vbetool) disabled " - "interrupts, re-enabling\n"); - ring->dev->driver->irq_preinstall(ring->dev); - ring->dev->driver->irq_postinstall(ring->dev); - } + return ret; +} - trace_i915_gem_request_wait_begin(ring, seqno); +static int __wait_seqno(struct intel_ring_buffer *ring, u32 seqno, + bool interruptible) +{ + drm_i915_private_t *dev_priv = ring->dev->dev_private; + int ret = 0; - ring->waiting_seqno = seqno; - if (ring->irq_get(ring)) { - if (dev_priv->mm.interruptible) - ret = wait_event_interruptible(ring->irq_queue, - i915_seqno_passed(ring->get_seqno(ring), seqno) - || atomic_read(&dev_priv->mm.wedged)); - else - wait_event(ring->irq_queue, - i915_seqno_passed(ring->get_seqno(ring), seqno) - || atomic_read(&dev_priv->mm.wedged)); + if (i915_seqno_passed(ring->get_seqno(ring), seqno)) + return 0; - ring->irq_put(ring); - } else if (wait_for_atomic(i915_seqno_passed(ring->get_seqno(ring), - seqno) || - atomic_read(&dev_priv->mm.wedged), 3000)) - ret = -EBUSY; - ring->waiting_seqno = 0; + trace_i915_gem_request_wait_begin(ring, seqno); + if (WARN_ON(!ring->irq_get(ring))) + return -ENODEV; - trace_i915_gem_request_wait_end(ring, seqno); - } +#define EXIT_COND \ + (i915_seqno_passed(ring->get_seqno(ring), seqno) || \ + atomic_read(&dev_priv->mm.wedged)) + + if (interruptible) + ret = wait_event_interruptible(ring->irq_queue, + EXIT_COND); + else + wait_event(ring->irq_queue, EXIT_COND); + + ring->irq_put(ring); + trace_i915_gem_request_wait_end(ring, seqno); +#undef EXIT_COND + + return ret; +} + +/** + * Waits for a sequence number to be signaled, and cleans up the + * request and object lists appropriately for that event. + */ +int +i915_wait_request(struct intel_ring_buffer *ring, + uint32_t seqno) +{ + drm_i915_private_t *dev_priv = ring->dev->dev_private; + int ret = 0; + + BUG_ON(seqno == 0); + + ret = i915_gem_check_wedge(dev_priv); + if (ret) + return ret; + + ret = i915_gem_check_olr(ring, seqno); + if (ret) + return ret; + + ret = __wait_seqno(ring, seqno, dev_priv->mm.interruptible); if (atomic_read(&dev_priv->mm.wedged)) ret = -EAGAIN; - /* Directly dispatch request retiring. While we have the work queue - * to handle this, the waiter on a request often wants an associated - * buffer to have made it to the inactive list, and we would need - * a separate wait queue to handle that. - */ - if (ret == 0 && do_retire) - i915_gem_retire_requests_ring(ring); - return ret; } @@ -1957,10 +1945,10 @@ i915_gem_object_wait_rendering(struct drm_i915_gem_object *obj) * it. */ if (obj->active) { - ret = i915_wait_request(obj->ring, obj->last_rendering_seqno, - true); + ret = i915_wait_request(obj->ring, obj->last_rendering_seqno); if (ret) return ret; + i915_gem_retire_requests_ring(obj->ring); } return 0; @@ -1998,22 +1986,9 @@ i915_gem_object_sync(struct drm_i915_gem_object *obj, if (seqno <= from->sync_seqno[idx]) return 0; - if (seqno == from->outstanding_lazy_request) { - struct drm_i915_gem_request *request; - - request = kzalloc(sizeof(*request), GFP_KERNEL); - if (request == NULL) - return -ENOMEM; - - ret = i915_add_request(from, NULL, request); - if (ret) { - kfree(request); - return ret; - } - - seqno = request->seqno; - } - + ret = i915_gem_check_olr(obj->ring, seqno); + if (ret) + return ret; ret = to->sync_to(to, from, seqno); if (!ret) @@ -2064,7 +2039,7 @@ i915_gem_object_unbind(struct drm_i915_gem_object *obj) } ret = i915_gem_object_finish_gpu(obj); - if (ret == -ERESTARTSYS) + if (ret) return ret; /* Continue on if we fail due to EIO, the GPU is hung so we * should be safe and we need to cleanup or else we might @@ -2091,7 +2066,7 @@ i915_gem_object_unbind(struct drm_i915_gem_object *obj) /* release the fence reg _after_ flushing */ ret = i915_gem_object_put_fence(obj); - if (ret == -ERESTARTSYS) + if (ret) return ret; trace_i915_gem_object_unbind(obj); @@ -2143,7 +2118,7 @@ i915_gem_flush_ring(struct intel_ring_buffer *ring, return 0; } -static int i915_ring_idle(struct intel_ring_buffer *ring, bool do_retire) +static int i915_ring_idle(struct intel_ring_buffer *ring) { int ret; @@ -2157,20 +2132,24 @@ static int i915_ring_idle(struct intel_ring_buffer *ring, bool do_retire) return ret; } - return i915_wait_request(ring, i915_gem_next_request_seqno(ring), - do_retire); + return i915_wait_request(ring, i915_gem_next_request_seqno(ring)); } -int i915_gpu_idle(struct drm_device *dev, bool do_retire) +int i915_gpu_idle(struct drm_device *dev) { drm_i915_private_t *dev_priv = dev->dev_private; + struct intel_ring_buffer *ring; int ret, i; /* Flush everything onto the inactive list. */ - for (i = 0; i < I915_NUM_RINGS; i++) { - ret = i915_ring_idle(&dev_priv->ring[i], do_retire); + for_each_ring(ring, dev_priv, i) { + ret = i915_ring_idle(ring); if (ret) return ret; + + /* Is the device fubar? */ + if (WARN_ON(!list_empty(&ring->gpu_write_list))) + return -EBUSY; } return 0; @@ -2357,9 +2336,7 @@ i915_gem_object_flush_fence(struct drm_i915_gem_object *obj) } if (obj->last_fenced_seqno) { - ret = i915_wait_request(obj->ring, - obj->last_fenced_seqno, - false); + ret = i915_wait_request(obj->ring, obj->last_fenced_seqno); if (ret) return ret; @@ -2454,7 +2431,7 @@ i915_gem_object_get_fence(struct drm_i915_gem_object *obj) /* Have we updated the tiling parameters upon the object and so * will need to serialise the write to the associated fence register? */ - if (obj->tiling_changed) { + if (obj->fence_dirty) { ret = i915_gem_object_flush_fence(obj); if (ret) return ret; @@ -2463,7 +2440,7 @@ i915_gem_object_get_fence(struct drm_i915_gem_object *obj) /* Just update our place in the LRU if our fence is getting reused. */ if (obj->fence_reg != I915_FENCE_REG_NONE) { reg = &dev_priv->fence_regs[obj->fence_reg]; - if (!obj->tiling_changed) { + if (!obj->fence_dirty) { list_move_tail(®->lru_list, &dev_priv->mm.fence_list); return 0; @@ -2486,7 +2463,7 @@ i915_gem_object_get_fence(struct drm_i915_gem_object *obj) return 0; i915_gem_object_update_fence(obj, reg, enable); - obj->tiling_changed = false; + obj->fence_dirty = false; return 0; } @@ -2732,6 +2709,7 @@ i915_gem_object_flush_cpu_write_domain(struct drm_i915_gem_object *obj) int i915_gem_object_set_to_gtt_domain(struct drm_i915_gem_object *obj, bool write) { + drm_i915_private_t *dev_priv = obj->base.dev->dev_private; uint32_t old_write_domain, old_read_domains; int ret; @@ -2772,6 +2750,10 @@ i915_gem_object_set_to_gtt_domain(struct drm_i915_gem_object *obj, bool write) old_read_domains, old_write_domain); + /* And bump the LRU for this access */ + if (i915_gem_object_is_inactive(obj)) + list_move_tail(&obj->mm_list, &dev_priv->mm.inactive_list); + return 0; } @@ -3020,28 +3002,7 @@ i915_gem_ring_throttle(struct drm_device *dev, struct drm_file *file) if (seqno == 0) return 0; - ret = 0; - if (!i915_seqno_passed(ring->get_seqno(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. - */ - if (ring->irq_get(ring)) { - ret = wait_event_interruptible(ring->irq_queue, - i915_seqno_passed(ring->get_seqno(ring), seqno) - || atomic_read(&dev_priv->mm.wedged)); - ring->irq_put(ring); - - if (ret == 0 && atomic_read(&dev_priv->mm.wedged)) - ret = -EIO; - } else if (wait_for_atomic(i915_seqno_passed(ring->get_seqno(ring), - seqno) || - atomic_read(&dev_priv->mm.wedged), 3000)) { - ret = -EBUSY; - } - } - + ret = __wait_seqno(ring, seqno, true); if (ret == 0) queue_delayed_work(dev_priv->wq, &dev_priv->mm.retire_work, 0); @@ -3053,12 +3014,9 @@ i915_gem_object_pin(struct drm_i915_gem_object *obj, uint32_t alignment, bool map_and_fenceable) { - struct drm_device *dev = obj->base.dev; - struct drm_i915_private *dev_priv = dev->dev_private; int ret; BUG_ON(obj->pin_count == DRM_I915_GEM_OBJECT_MAX_PIN_COUNT); - WARN_ON(i915_verify_lists(dev)); if (obj->gtt_space != NULL) { if ((alignment && obj->gtt_offset & (alignment - 1)) || @@ -3086,34 +3044,20 @@ i915_gem_object_pin(struct drm_i915_gem_object *obj, if (!obj->has_global_gtt_mapping && map_and_fenceable) i915_gem_gtt_bind_object(obj, obj->cache_level); - if (obj->pin_count++ == 0) { - if (!obj->active) - list_move_tail(&obj->mm_list, - &dev_priv->mm.pinned_list); - } + obj->pin_count++; obj->pin_mappable |= map_and_fenceable; - WARN_ON(i915_verify_lists(dev)); return 0; } void i915_gem_object_unpin(struct drm_i915_gem_object *obj) { - struct drm_device *dev = obj->base.dev; - drm_i915_private_t *dev_priv = dev->dev_private; - - WARN_ON(i915_verify_lists(dev)); BUG_ON(obj->pin_count == 0); BUG_ON(obj->gtt_space == NULL); - if (--obj->pin_count == 0) { - if (!obj->active) - list_move_tail(&obj->mm_list, - &dev_priv->mm.inactive_list); + if (--obj->pin_count == 0) obj->pin_mappable = false; - } - WARN_ON(i915_verify_lists(dev)); } int @@ -3237,20 +3181,9 @@ i915_gem_busy_ioctl(struct drm_device *dev, void *data, if (obj->base.write_domain & I915_GEM_GPU_DOMAINS) { ret = i915_gem_flush_ring(obj->ring, 0, obj->base.write_domain); - } else if (obj->ring->outstanding_lazy_request == - obj->last_rendering_seqno) { - struct drm_i915_gem_request *request; - - /* This ring is not being cleared by active usage, - * so emit a request to do so. - */ - request = kzalloc(sizeof(*request), GFP_KERNEL); - if (request) { - ret = i915_add_request(obj->ring, NULL, request); - if (ret) - kfree(request); - } else - ret = -ENOMEM; + } else { + ret = i915_gem_check_olr(obj->ring, + obj->last_rendering_seqno); } /* Update the active list for the hardware's current position. @@ -3386,21 +3319,29 @@ int i915_gem_init_object(struct drm_gem_object *obj) return 0; } -static void i915_gem_free_object_tail(struct drm_i915_gem_object *obj) +void i915_gem_free_object(struct drm_gem_object *gem_obj) { + struct drm_i915_gem_object *obj = to_intel_bo(gem_obj); struct drm_device *dev = obj->base.dev; drm_i915_private_t *dev_priv = dev->dev_private; - int ret; - - ret = i915_gem_object_unbind(obj); - if (ret == -ERESTARTSYS) { - list_move(&obj->mm_list, - &dev_priv->mm.deferred_free_list); - return; - } trace_i915_gem_object_destroy(obj); + if (obj->phys_obj) + i915_gem_detach_phys_object(dev, obj); + + obj->pin_count = 0; + if (WARN_ON(i915_gem_object_unbind(obj) == -ERESTARTSYS)) { + bool was_interruptible; + + was_interruptible = dev_priv->mm.interruptible; + dev_priv->mm.interruptible = false; + + WARN_ON(i915_gem_object_unbind(obj)); + + dev_priv->mm.interruptible = was_interruptible; + } + if (obj->base.map_list.map) drm_gem_free_mmap_offset(&obj->base); @@ -3411,20 +3352,6 @@ static void i915_gem_free_object_tail(struct drm_i915_gem_object *obj) kfree(obj); } -void i915_gem_free_object(struct drm_gem_object *gem_obj) -{ - struct drm_i915_gem_object *obj = to_intel_bo(gem_obj); - struct drm_device *dev = obj->base.dev; - - while (obj->pin_count > 0) - i915_gem_object_unpin(obj); - - if (obj->phys_obj) - i915_gem_detach_phys_object(dev, obj); - - i915_gem_free_object_tail(obj); -} - int i915_gem_idle(struct drm_device *dev) { @@ -3438,20 +3365,16 @@ i915_gem_idle(struct drm_device *dev) return 0; } - ret = i915_gpu_idle(dev, true); + ret = i915_gpu_idle(dev); if (ret) { mutex_unlock(&dev->struct_mutex); return ret; } + i915_gem_retire_requests(dev); /* Under UMS, be paranoid and evict. */ - if (!drm_core_check_feature(dev, DRIVER_MODESET)) { - ret = i915_gem_evict_inactive(dev, false); - if (ret) { - mutex_unlock(&dev->struct_mutex); - return ret; - } - } + if (!drm_core_check_feature(dev, DRIVER_MODESET)) + i915_gem_evict_everything(dev, false); i915_gem_reset_fences(dev); @@ -3489,9 +3412,9 @@ void i915_gem_init_swizzling(struct drm_device *dev) I915_WRITE(TILECTL, I915_READ(TILECTL) | TILECTL_SWZCTL); if (IS_GEN6(dev)) - I915_WRITE(ARB_MODE, ARB_MODE_ENABLE(ARB_MODE_SWIZZLE_SNB)); + I915_WRITE(ARB_MODE, _MASKED_BIT_ENABLE(ARB_MODE_SWIZZLE_SNB)); else - I915_WRITE(ARB_MODE, ARB_MODE_ENABLE(ARB_MODE_SWIZZLE_IVB)); + I915_WRITE(ARB_MODE, _MASKED_BIT_ENABLE(ARB_MODE_SWIZZLE_IVB)); } void i915_gem_init_ppgtt(struct drm_device *dev) @@ -3540,18 +3463,16 @@ void i915_gem_init_ppgtt(struct drm_device *dev) ecochk = I915_READ(GAM_ECOCHK); I915_WRITE(GAM_ECOCHK, ecochk | ECOCHK_SNB_BIT | ECOCHK_PPGTT_CACHE64B); - I915_WRITE(GFX_MODE, GFX_MODE_ENABLE(GFX_PPGTT_ENABLE)); + I915_WRITE(GFX_MODE, _MASKED_BIT_ENABLE(GFX_PPGTT_ENABLE)); } else if (INTEL_INFO(dev)->gen >= 7) { I915_WRITE(GAM_ECOCHK, ECOCHK_PPGTT_CACHE64B); /* GFX_MODE is per-ring on gen7+ */ } - for (i = 0; i < I915_NUM_RINGS; i++) { - ring = &dev_priv->ring[i]; - + for_each_ring(ring, dev_priv, i) { if (INTEL_INFO(dev)->gen >= 7) I915_WRITE(RING_MODE_GEN7(ring), - GFX_MODE_ENABLE(GFX_PPGTT_ENABLE)); + _MASKED_BIT_ENABLE(GFX_PPGTT_ENABLE)); I915_WRITE(RING_PP_DIR_DCLV(ring), PP_DIR_DCLV_2G); I915_WRITE(RING_PP_DIR_BASE(ring), pd_offset); @@ -3595,14 +3516,80 @@ cleanup_render_ring: return ret; } +static bool +intel_enable_ppgtt(struct drm_device *dev) +{ + if (i915_enable_ppgtt >= 0) + return i915_enable_ppgtt; + +#ifdef CONFIG_INTEL_IOMMU + /* Disable ppgtt on SNB if VT-d is on. */ + if (INTEL_INFO(dev)->gen == 6 && intel_iommu_gfx_mapped) + return false; +#endif + + return true; +} + +int i915_gem_init(struct drm_device *dev) +{ + struct drm_i915_private *dev_priv = dev->dev_private; + unsigned long gtt_size, mappable_size; + int ret; + + gtt_size = dev_priv->mm.gtt->gtt_total_entries << PAGE_SHIFT; + mappable_size = dev_priv->mm.gtt->gtt_mappable_entries << PAGE_SHIFT; + + mutex_lock(&dev->struct_mutex); + if (intel_enable_ppgtt(dev) && HAS_ALIASING_PPGTT(dev)) { + /* PPGTT pdes are stolen from global gtt ptes, so shrink the + * aperture accordingly when using aliasing ppgtt. */ + gtt_size -= I915_PPGTT_PD_ENTRIES*PAGE_SIZE; + + i915_gem_init_global_gtt(dev, 0, mappable_size, gtt_size); + + ret = i915_gem_init_aliasing_ppgtt(dev); + if (ret) { + mutex_unlock(&dev->struct_mutex); + return ret; + } + } else { + /* Let GEM Manage all of the aperture. + * + * However, leave one page at the end still bound to the scratch + * page. There are a number of places where the hardware + * apparently prefetches past the end of the object, and we've + * seen multiple hangs with the GPU head pointer stuck in a + * batchbuffer bound at the last page of the aperture. One page + * should be enough to keep any prefetching inside of the + * aperture. + */ + i915_gem_init_global_gtt(dev, 0, mappable_size, + gtt_size); + } + + ret = i915_gem_init_hw(dev); + mutex_unlock(&dev->struct_mutex); + if (ret) { + i915_gem_cleanup_aliasing_ppgtt(dev); + return ret; + } + + /* Allow hardware batchbuffers unless told otherwise, but not for KMS. */ + if (!drm_core_check_feature(dev, DRIVER_MODESET)) + dev_priv->dri1.allow_batchbuffer = 1; + return 0; +} + void i915_gem_cleanup_ringbuffer(struct drm_device *dev) { drm_i915_private_t *dev_priv = dev->dev_private; + struct intel_ring_buffer *ring; int i; - for (i = 0; i < I915_NUM_RINGS; i++) - intel_cleanup_ring_buffer(&dev_priv->ring[i]); + for_each_ring(ring, dev_priv, i) + intel_cleanup_ring_buffer(ring); } int @@ -3610,7 +3597,7 @@ i915_gem_entervt_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv) { drm_i915_private_t *dev_priv = dev->dev_private; - int ret, i; + int ret; if (drm_core_check_feature(dev, DRIVER_MODESET)) return 0; @@ -3632,10 +3619,6 @@ i915_gem_entervt_ioctl(struct drm_device *dev, void *data, BUG_ON(!list_empty(&dev_priv->mm.active_list)); BUG_ON(!list_empty(&dev_priv->mm.flushing_list)); BUG_ON(!list_empty(&dev_priv->mm.inactive_list)); - for (i = 0; i < I915_NUM_RINGS; i++) { - BUG_ON(!list_empty(&dev_priv->ring[i].active_list)); - BUG_ON(!list_empty(&dev_priv->ring[i].request_list)); - } mutex_unlock(&dev->struct_mutex); ret = drm_irq_install(dev); @@ -3694,9 +3677,7 @@ i915_gem_load(struct drm_device *dev) INIT_LIST_HEAD(&dev_priv->mm.active_list); INIT_LIST_HEAD(&dev_priv->mm.flushing_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->mm.gtt_list); for (i = 0; i < I915_NUM_RINGS; i++) init_ring_lists(&dev_priv->ring[i]); @@ -3708,12 +3689,8 @@ i915_gem_load(struct drm_device *dev) /* On GEN3 we really need to make sure the ARB C3 LP bit is set */ if (IS_GEN3(dev)) { - u32 tmp = I915_READ(MI_ARB_STATE); - if (!(tmp & MI_ARB_C3_LP_WRITE_ENABLE)) { - /* arb state is a masked write, so set bit + bit in mask */ - tmp = MI_ARB_C3_LP_WRITE_ENABLE | (MI_ARB_C3_LP_WRITE_ENABLE << MI_ARB_MASK_SHIFT); - I915_WRITE(MI_ARB_STATE, tmp); - } + I915_WRITE(MI_ARB_STATE, + _MASKED_BIT_ENABLE(MI_ARB_C3_LP_WRITE_ENABLE)); } dev_priv->relative_constants_mode = I915_EXEC_CONSTANTS_REL_GENERAL; @@ -4016,7 +3993,7 @@ rescan: * This has a dramatic impact to reduce the number of * OOM-killer events whilst running the GPU aggressively. */ - if (i915_gpu_idle(dev, true) == 0) + if (i915_gpu_idle(dev) == 0) goto rescan; } mutex_unlock(&dev->struct_mutex); diff --git a/drivers/gpu/drm/i915/i915_gem_debug.c b/drivers/gpu/drm/i915/i915_gem_debug.c index cc93cac..a4f6aaa 100644 --- a/drivers/gpu/drm/i915/i915_gem_debug.c +++ b/drivers/gpu/drm/i915/i915_gem_debug.c @@ -114,22 +114,6 @@ i915_verify_lists(struct drm_device *dev) } } - 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 */ diff --git a/drivers/gpu/drm/i915/i915_gem_evict.c b/drivers/gpu/drm/i915/i915_gem_evict.c index 21a8271..ae7c24e 100644 --- a/drivers/gpu/drm/i915/i915_gem_evict.c +++ b/drivers/gpu/drm/i915/i915_gem_evict.c @@ -35,6 +35,9 @@ static bool mark_free(struct drm_i915_gem_object *obj, struct list_head *unwind) { + if (obj->pin_count) + return false; + list_add(&obj->exec_list, unwind); return drm_mm_scan_add_block(obj->gtt_space); } @@ -90,7 +93,7 @@ i915_gem_evict_something(struct drm_device *dev, int min_size, /* Now merge in the soon-to-be-expired objects... */ list_for_each_entry(obj, &dev_priv->mm.active_list, mm_list) { /* Does the object require an outstanding flush? */ - if (obj->base.write_domain || obj->pin_count) + if (obj->base.write_domain) continue; if (mark_free(obj, &unwind_list)) @@ -99,14 +102,11 @@ i915_gem_evict_something(struct drm_device *dev, int min_size, /* Finally add anything with a pending flush (in order of retirement) */ list_for_each_entry(obj, &dev_priv->mm.flushing_list, mm_list) { - if (obj->pin_count) - continue; - if (mark_free(obj, &unwind_list)) goto found; } list_for_each_entry(obj, &dev_priv->mm.active_list, mm_list) { - if (!obj->base.write_domain || obj->pin_count) + if (!obj->base.write_domain) continue; if (mark_free(obj, &unwind_list)) @@ -166,8 +166,9 @@ int i915_gem_evict_everything(struct drm_device *dev, bool purgeable_only) { drm_i915_private_t *dev_priv = dev->dev_private; - int ret; + struct drm_i915_gem_object *obj, *next; bool lists_empty; + int ret; lists_empty = (list_empty(&dev_priv->mm.inactive_list) && list_empty(&dev_priv->mm.flushing_list) && @@ -177,29 +178,24 @@ i915_gem_evict_everything(struct drm_device *dev, bool purgeable_only) trace_i915_gem_evict_everything(dev, purgeable_only); - /* Flush everything (on to the inactive lists) and evict */ - ret = i915_gpu_idle(dev, true); + /* The gpu_idle will flush everything in the write domain to the + * active list. Then we must move everything off the active list + * with retire requests. + */ + ret = i915_gpu_idle(dev); if (ret) return ret; - BUG_ON(!list_empty(&dev_priv->mm.flushing_list)); + i915_gem_retire_requests(dev); - return i915_gem_evict_inactive(dev, purgeable_only); -} - -/** Unbinds all inactive objects. */ -int -i915_gem_evict_inactive(struct drm_device *dev, bool purgeable_only) -{ - drm_i915_private_t *dev_priv = dev->dev_private; - struct drm_i915_gem_object *obj, *next; + BUG_ON(!list_empty(&dev_priv->mm.flushing_list)); + /* Having flushed everything, unbind() should never raise an error */ list_for_each_entry_safe(obj, next, &dev_priv->mm.inactive_list, mm_list) { if (!purgeable_only || obj->madv != I915_MADV_WILLNEED) { - int ret = i915_gem_object_unbind(obj); - if (ret) - return ret; + if (obj->pin_count == 0) + WARN_ON(i915_gem_object_unbind(obj)); } } diff --git a/drivers/gpu/drm/i915/i915_gem_execbuffer.c b/drivers/gpu/drm/i915/i915_gem_execbuffer.c index c77bfa9..974a9f1 100644 --- a/drivers/gpu/drm/i915/i915_gem_execbuffer.c +++ b/drivers/gpu/drm/i915/i915_gem_execbuffer.c @@ -967,11 +967,14 @@ i915_gem_execbuffer_move_to_active(struct list_head *objects, obj->pending_gpu_write = true; list_move_tail(&obj->gpu_write_list, &ring->gpu_write_list); - intel_mark_busy(ring->dev, obj); + if (obj->pin_count) /* check for potential scanout */ + intel_mark_busy(ring->dev, obj); } trace_i915_gem_object_change_domain(obj, old_read, old_write); } + + intel_mark_busy(ring->dev, NULL); } static void @@ -1061,17 +1064,9 @@ i915_gem_do_execbuffer(struct drm_device *dev, void *data, ring = &dev_priv->ring[RCS]; break; case I915_EXEC_BSD: - if (!HAS_BSD(dev)) { - DRM_DEBUG("execbuf with invalid ring (BSD)\n"); - return -EINVAL; - } ring = &dev_priv->ring[VCS]; break; case I915_EXEC_BLT: - if (!HAS_BLT(dev)) { - DRM_DEBUG("execbuf with invalid ring (BLT)\n"); - return -EINVAL; - } ring = &dev_priv->ring[BCS]; break; default: @@ -1079,6 +1074,11 @@ i915_gem_do_execbuffer(struct drm_device *dev, void *data, (int)(args->flags & I915_EXEC_RING_MASK)); return -EINVAL; } + if (!intel_ring_initialized(ring)) { + DRM_DEBUG("execbuf with invalid ring: %d\n", + (int)(args->flags & I915_EXEC_RING_MASK)); + return -EINVAL; + } mode = args->flags & I915_EXEC_CONSTANTS_MASK; mask = I915_EXEC_CONSTANTS_MASK; @@ -1116,11 +1116,17 @@ i915_gem_do_execbuffer(struct drm_device *dev, void *data, return -EINVAL; } + if (INTEL_INFO(dev)->gen >= 5) { + DRM_DEBUG("clip rectangles are only valid on pre-gen5\n"); + return -EINVAL; + } + if (args->num_cliprects > UINT_MAX / sizeof(*cliprects)) { DRM_DEBUG("execbuf with %u cliprects\n", args->num_cliprects); return -EINVAL; } + cliprects = kmalloc(args->num_cliprects * sizeof(*cliprects), GFP_KERNEL); if (cliprects == NULL) { @@ -1225,9 +1231,10 @@ i915_gem_do_execbuffer(struct drm_device *dev, void *data, * so every billion or so execbuffers, we need to stall * the GPU in order to reset the counters. */ - ret = i915_gpu_idle(dev, true); + ret = i915_gpu_idle(dev); if (ret) goto err; + i915_gem_retire_requests(dev); BUG_ON(ring->sync_seqno[i]); } diff --git a/drivers/gpu/drm/i915/i915_gem_gtt.c b/drivers/gpu/drm/i915/i915_gem_gtt.c index 25c8bf9..29d573c 100644 --- a/drivers/gpu/drm/i915/i915_gem_gtt.c +++ b/drivers/gpu/drm/i915/i915_gem_gtt.c @@ -317,7 +317,7 @@ static bool do_idling(struct drm_i915_private *dev_priv) if (unlikely(dev_priv->mm.gtt->do_idle_maps)) { dev_priv->mm.interruptible = false; - if (i915_gpu_idle(dev_priv->dev, false)) { + if (i915_gpu_idle(dev_priv->dev)) { DRM_ERROR("Couldn't idle GPU\n"); /* Wait a bit, in hopes it avoids the hang */ udelay(10); diff --git a/drivers/gpu/drm/i915/i915_gem_stolen.c b/drivers/gpu/drm/i915/i915_gem_stolen.c new file mode 100644 index 0000000..ada2e90 --- /dev/null +++ b/drivers/gpu/drm/i915/i915_gem_stolen.c @@ -0,0 +1,202 @@ +/* + * Copyright © 2008-2012 Intel Corporation + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + * + * Authors: + * Eric Anholt <eric@anholt.net> + * Chris Wilson <chris@chris-wilson.co.uk> + * + */ + +#include "drmP.h" +#include "drm.h" +#include "i915_drm.h" +#include "i915_drv.h" + +/* + * The BIOS typically reserves some of the system's memory for the exclusive + * use of the integrated graphics. This memory is no longer available for + * use by the OS and so the user finds that his system has less memory + * available than he put in. We refer to this memory as stolen. + * + * The BIOS will allocate its framebuffer from the stolen memory. Our + * goal is try to reuse that object for our own fbcon which must always + * be available for panics. Anything else we can reuse the stolen memory + * for is a boon. + */ + +#define PTE_ADDRESS_MASK 0xfffff000 +#define PTE_ADDRESS_MASK_HIGH 0x000000f0 /* i915+ */ +#define PTE_MAPPING_TYPE_UNCACHED (0 << 1) +#define PTE_MAPPING_TYPE_DCACHE (1 << 1) /* i830 only */ +#define PTE_MAPPING_TYPE_CACHED (3 << 1) +#define PTE_MAPPING_TYPE_MASK (3 << 1) +#define PTE_VALID (1 << 0) + +/** + * i915_stolen_to_phys - take an offset into stolen memory and turn it into + * a physical one + * @dev: drm device + * @offset: address to translate + * + * Some chip functions require allocations from stolen space and need the + * physical address of the memory in question. + */ +static unsigned long i915_stolen_to_phys(struct drm_device *dev, u32 offset) +{ + struct drm_i915_private *dev_priv = dev->dev_private; + struct pci_dev *pdev = dev_priv->bridge_dev; + u32 base; + +#if 0 + /* On the machines I have tested the Graphics Base of Stolen Memory + * is unreliable, so compute the base by subtracting the stolen memory + * from the Top of Low Usable DRAM which is where the BIOS places + * the graphics stolen memory. + */ + if (INTEL_INFO(dev)->gen > 3 || IS_G33(dev)) { + /* top 32bits are reserved = 0 */ + pci_read_config_dword(pdev, 0xA4, &base); + } else { + /* XXX presume 8xx is the same as i915 */ + pci_bus_read_config_dword(pdev->bus, 2, 0x5C, &base); + } +#else + if (INTEL_INFO(dev)->gen > 3 || IS_G33(dev)) { + u16 val; + pci_read_config_word(pdev, 0xb0, &val); + base = val >> 4 << 20; + } else { + u8 val; + pci_read_config_byte(pdev, 0x9c, &val); + base = val >> 3 << 27; + } + base -= dev_priv->mm.gtt->stolen_size; +#endif + + return base + offset; +} + +static void i915_warn_stolen(struct drm_device *dev) +{ + DRM_INFO("not enough stolen space for compressed buffer, disabling\n"); + DRM_INFO("hint: you may be able to increase stolen memory size in the BIOS to avoid this\n"); +} + +static void i915_setup_compression(struct drm_device *dev, int size) +{ + struct drm_i915_private *dev_priv = dev->dev_private; + struct drm_mm_node *compressed_fb, *uninitialized_var(compressed_llb); + unsigned long cfb_base; + unsigned long ll_base = 0; + + /* Just in case the BIOS is doing something questionable. */ + intel_disable_fbc(dev); + + compressed_fb = drm_mm_search_free(&dev_priv->mm.stolen, size, 4096, 0); + if (compressed_fb) + compressed_fb = drm_mm_get_block(compressed_fb, size, 4096); + if (!compressed_fb) + goto err; + + cfb_base = i915_stolen_to_phys(dev, compressed_fb->start); + if (!cfb_base) + goto err_fb; + + if (!(IS_GM45(dev) || HAS_PCH_SPLIT(dev))) { + compressed_llb = drm_mm_search_free(&dev_priv->mm.stolen, + 4096, 4096, 0); + if (compressed_llb) + compressed_llb = drm_mm_get_block(compressed_llb, + 4096, 4096); + if (!compressed_llb) + goto err_fb; + + ll_base = i915_stolen_to_phys(dev, compressed_llb->start); + if (!ll_base) + goto err_llb; + } + + dev_priv->cfb_size = size; + + dev_priv->compressed_fb = compressed_fb; + if (HAS_PCH_SPLIT(dev)) + I915_WRITE(ILK_DPFC_CB_BASE, compressed_fb->start); + else if (IS_GM45(dev)) { + I915_WRITE(DPFC_CB_BASE, compressed_fb->start); + } else { + I915_WRITE(FBC_CFB_BASE, cfb_base); + I915_WRITE(FBC_LL_BASE, ll_base); + dev_priv->compressed_llb = compressed_llb; + } + + DRM_DEBUG_KMS("FBC base 0x%08lx, ll base 0x%08lx, size %dM\n", + cfb_base, ll_base, size >> 20); + return; + +err_llb: + drm_mm_put_block(compressed_llb); +err_fb: + drm_mm_put_block(compressed_fb); +err: + dev_priv->no_fbc_reason = FBC_STOLEN_TOO_SMALL; + i915_warn_stolen(dev); +} + +static void i915_cleanup_compression(struct drm_device *dev) +{ + struct drm_i915_private *dev_priv = dev->dev_private; + + drm_mm_put_block(dev_priv->compressed_fb); + if (dev_priv->compressed_llb) + drm_mm_put_block(dev_priv->compressed_llb); +} + +void i915_gem_cleanup_stolen(struct drm_device *dev) +{ + if (I915_HAS_FBC(dev) && i915_powersave) + i915_cleanup_compression(dev); +} + +int i915_gem_init_stolen(struct drm_device *dev) +{ + struct drm_i915_private *dev_priv = dev->dev_private; + unsigned long prealloc_size = dev_priv->mm.gtt->stolen_size; + + /* Basic memrange allocator for stolen space */ + drm_mm_init(&dev_priv->mm.stolen, 0, prealloc_size); + + /* Try to set up FBC with a reasonable compressed buffer size */ + if (I915_HAS_FBC(dev) && i915_powersave) { + int cfb_size; + + /* Leave 1M for line length buffer & misc. */ + + /* Try to get a 32M buffer... */ + if (prealloc_size > (36*1024*1024)) + cfb_size = 32*1024*1024; + else /* fall back to 7/8 of the stolen space */ + cfb_size = prealloc_size * 7 / 8; + i915_setup_compression(dev, cfb_size); + } + + return 0; +} diff --git a/drivers/gpu/drm/i915/i915_gem_tiling.c b/drivers/gpu/drm/i915/i915_gem_tiling.c index 1a93066..b964df5 100644 --- a/drivers/gpu/drm/i915/i915_gem_tiling.c +++ b/drivers/gpu/drm/i915/i915_gem_tiling.c @@ -354,9 +354,15 @@ i915_gem_set_tiling(struct drm_device *dev, void *data, /* We need to rebind the object if its current allocation * no longer meets the alignment restrictions for its new * tiling mode. Otherwise we can just leave it alone, but - * need to ensure that any fence register is cleared. + * need to ensure that any fence register is updated before + * the next fenced (either through the GTT or by the BLT unit + * on older GPUs) access. + * + * After updating the tiling parameters, we then flag whether + * we need to update an associated fence register. Note this + * has to also include the unfenced register the GPU uses + * whilst executing a fenced command for an untiled object. */ - i915_gem_release_mmap(obj); obj->map_and_fenceable = obj->gtt_space == NULL || @@ -374,9 +380,15 @@ i915_gem_set_tiling(struct drm_device *dev, void *data, } if (ret == 0) { - obj->tiling_changed = true; + obj->fence_dirty = + obj->fenced_gpu_access || + obj->fence_reg != I915_FENCE_REG_NONE; + obj->tiling_mode = args->tiling_mode; obj->stride = args->stride; + + /* Force the fence to be reacquired for GTT access */ + i915_gem_release_mmap(obj); } } /* we have to maintain this existing ABI... */ diff --git a/drivers/gpu/drm/i915/i915_irq.c b/drivers/gpu/drm/i915/i915_irq.c index ab023ca..cc4a633 100644 --- a/drivers/gpu/drm/i915/i915_irq.c +++ b/drivers/gpu/drm/i915/i915_irq.c @@ -37,35 +37,6 @@ #include "i915_trace.h" #include "intel_drv.h" -#define MAX_NOPID ((u32)~0) - -/** - * Interrupts that are always left unmasked. - * - * Since pipe events are edge-triggered from the PIPESTAT register to IIR, - * we leave them always unmasked in IMR and then control enabling them through - * PIPESTAT alone. - */ -#define I915_INTERRUPT_ENABLE_FIX \ - (I915_ASLE_INTERRUPT | \ - I915_DISPLAY_PIPE_A_EVENT_INTERRUPT | \ - I915_DISPLAY_PIPE_B_EVENT_INTERRUPT | \ - I915_DISPLAY_PLANE_A_FLIP_PENDING_INTERRUPT | \ - I915_DISPLAY_PLANE_B_FLIP_PENDING_INTERRUPT | \ - I915_RENDER_COMMAND_PARSER_ERROR_INTERRUPT) - -/** Interrupts that we mask and unmask at runtime. */ -#define I915_INTERRUPT_ENABLE_VAR (I915_USER_INTERRUPT | I915_BSD_USER_INTERRUPT) - -#define I915_PIPE_VBLANK_STATUS (PIPE_START_VBLANK_INTERRUPT_STATUS |\ - PIPE_VBLANK_INTERRUPT_STATUS) - -#define I915_PIPE_VBLANK_ENABLE (PIPE_START_VBLANK_INTERRUPT_ENABLE |\ - PIPE_VBLANK_INTERRUPT_ENABLE) - -#define DRM_I915_VBLANK_PIPE_ALL (DRM_I915_VBLANK_PIPE_A | \ - DRM_I915_VBLANK_PIPE_B) - /* For display hotplug interrupt */ static void ironlake_enable_display_irq(drm_i915_private_t *dev_priv, u32 mask) @@ -360,15 +331,12 @@ static void notify_ring(struct drm_device *dev, struct intel_ring_buffer *ring) { struct drm_i915_private *dev_priv = dev->dev_private; - u32 seqno; if (ring->obj == NULL) return; - seqno = ring->get_seqno(ring); - trace_i915_gem_request_complete(ring, seqno); + trace_i915_gem_request_complete(ring, ring->get_seqno(ring)); - ring->irq_seqno = seqno; wake_up_all(&ring->irq_queue); if (i915_enable_hangcheck) { dev_priv->hangcheck_count = 0; @@ -541,17 +509,13 @@ static irqreturn_t valleyview_irq_handler(DRM_IRQ_ARGS) if (iir & I915_DISPLAY_PIPE_A_VBLANK_INTERRUPT) { drm_handle_vblank(dev, 0); vblank++; - if (!dev_priv->flip_pending_is_done) { - intel_finish_page_flip(dev, 0); - } + intel_finish_page_flip(dev, 0); } if (iir & I915_DISPLAY_PIPE_B_VBLANK_INTERRUPT) { drm_handle_vblank(dev, 1); vblank++; - if (!dev_priv->flip_pending_is_done) { - intel_finish_page_flip(dev, 0); - } + intel_finish_page_flip(dev, 0); } if (pipe_stats[pipe] & PIPE_LEGACY_BLC_EVENT_STATUS) @@ -569,14 +533,11 @@ out: return ret; } -static void pch_irq_handler(struct drm_device *dev) +static void pch_irq_handler(struct drm_device *dev, u32 pch_iir) { drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private; - u32 pch_iir; int pipe; - pch_iir = I915_READ(SDEIIR); - if (pch_iir & SDE_AUDIO_POWER_MASK) DRM_DEBUG_DRIVER("PCH audio power change on port %d\n", (pch_iir & SDE_AUDIO_POWER_MASK) >> @@ -616,72 +577,61 @@ static irqreturn_t ivybridge_irq_handler(DRM_IRQ_ARGS) { struct drm_device *dev = (struct drm_device *) arg; drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private; - int ret = IRQ_NONE; - u32 de_iir, gt_iir, de_ier, pch_iir, pm_iir; - struct drm_i915_master_private *master_priv; + u32 de_iir, gt_iir, de_ier, pm_iir; + irqreturn_t ret = IRQ_NONE; + int i; atomic_inc(&dev_priv->irq_received); /* disable master interrupt before clearing iir */ de_ier = I915_READ(DEIER); I915_WRITE(DEIER, de_ier & ~DE_MASTER_IRQ_CONTROL); - POSTING_READ(DEIER); - de_iir = I915_READ(DEIIR); gt_iir = I915_READ(GTIIR); - pch_iir = I915_READ(SDEIIR); - pm_iir = I915_READ(GEN6_PMIIR); - - if (de_iir == 0 && gt_iir == 0 && pch_iir == 0 && pm_iir == 0) - goto done; - - ret = IRQ_HANDLED; - - if (dev->primary->master) { - master_priv = dev->primary->master->driver_priv; - if (master_priv->sarea_priv) - master_priv->sarea_priv->last_dispatch = - READ_BREADCRUMB(dev_priv); + if (gt_iir) { + snb_gt_irq_handler(dev, dev_priv, gt_iir); + I915_WRITE(GTIIR, gt_iir); + ret = IRQ_HANDLED; } - snb_gt_irq_handler(dev, dev_priv, gt_iir); - - if (de_iir & DE_GSE_IVB) - intel_opregion_gse_intr(dev); - - if (de_iir & DE_PLANEA_FLIP_DONE_IVB) { - intel_prepare_page_flip(dev, 0); - intel_finish_page_flip_plane(dev, 0); - } + de_iir = I915_READ(DEIIR); + if (de_iir) { + if (de_iir & DE_GSE_IVB) + intel_opregion_gse_intr(dev); + + for (i = 0; i < 3; i++) { + if (de_iir & (DE_PLANEA_FLIP_DONE_IVB << (5 * i))) { + intel_prepare_page_flip(dev, i); + intel_finish_page_flip_plane(dev, i); + } + if (de_iir & (DE_PIPEA_VBLANK_IVB << (5 * i))) + drm_handle_vblank(dev, i); + } - if (de_iir & DE_PLANEB_FLIP_DONE_IVB) { - intel_prepare_page_flip(dev, 1); - intel_finish_page_flip_plane(dev, 1); - } + /* check event from PCH */ + if (de_iir & DE_PCH_EVENT_IVB) { + u32 pch_iir = I915_READ(SDEIIR); - if (de_iir & DE_PIPEA_VBLANK_IVB) - drm_handle_vblank(dev, 0); + if (pch_iir & SDE_HOTPLUG_MASK_CPT) + queue_work(dev_priv->wq, &dev_priv->hotplug_work); + pch_irq_handler(dev, pch_iir); - if (de_iir & DE_PIPEB_VBLANK_IVB) - drm_handle_vblank(dev, 1); + /* clear PCH hotplug event before clear CPU irq */ + I915_WRITE(SDEIIR, pch_iir); + } - /* check event from PCH */ - if (de_iir & DE_PCH_EVENT_IVB) { - if (pch_iir & SDE_HOTPLUG_MASK_CPT) - queue_work(dev_priv->wq, &dev_priv->hotplug_work); - pch_irq_handler(dev); + I915_WRITE(DEIIR, de_iir); + ret = IRQ_HANDLED; } - if (pm_iir & GEN6_PM_DEFERRED_EVENTS) - gen6_queue_rps_work(dev_priv, pm_iir); - - /* should clear PCH hotplug event before clear CPU irq */ - I915_WRITE(SDEIIR, pch_iir); - I915_WRITE(GTIIR, gt_iir); - I915_WRITE(DEIIR, de_iir); - I915_WRITE(GEN6_PMIIR, pm_iir); + pm_iir = I915_READ(GEN6_PMIIR); + if (pm_iir) { + if (pm_iir & GEN6_PM_DEFERRED_EVENTS) + gen6_queue_rps_work(dev_priv, pm_iir); + I915_WRITE(GEN6_PMIIR, pm_iir); + ret = IRQ_HANDLED; + } -done: I915_WRITE(DEIER, de_ier); POSTING_READ(DEIER); @@ -705,7 +655,6 @@ static irqreturn_t ironlake_irq_handler(DRM_IRQ_ARGS) int ret = IRQ_NONE; u32 de_iir, gt_iir, de_ier, pch_iir, pm_iir; u32 hotplug_mask; - struct drm_i915_master_private *master_priv; atomic_inc(&dev_priv->irq_received); @@ -730,13 +679,6 @@ static irqreturn_t ironlake_irq_handler(DRM_IRQ_ARGS) ret = IRQ_HANDLED; - if (dev->primary->master) { - master_priv = dev->primary->master->driver_priv; - if (master_priv->sarea_priv) - master_priv->sarea_priv->last_dispatch = - READ_BREADCRUMB(dev_priv); - } - if (IS_GEN5(dev)) ilk_gt_irq_handler(dev, dev_priv, gt_iir); else @@ -765,7 +707,7 @@ static irqreturn_t ironlake_irq_handler(DRM_IRQ_ARGS) if (de_iir & DE_PCH_EVENT) { if (pch_iir & hotplug_mask) queue_work(dev_priv->wq, &dev_priv->hotplug_work); - pch_irq_handler(dev); + pch_irq_handler(dev, pch_iir); } if (de_iir & DE_PCU_EVENT) { @@ -810,7 +752,7 @@ static void i915_error_work_func(struct work_struct *work) if (atomic_read(&dev_priv->mm.wedged)) { DRM_DEBUG_DRIVER("resetting chip\n"); kobject_uevent_env(&dev->primary->kdev.kobj, KOBJ_CHANGE, reset_event); - if (!i915_reset(dev, GRDOM_RENDER)) { + if (!i915_reset(dev)) { atomic_set(&dev_priv->mm.wedged, 0); kobject_uevent_env(&dev->primary->kdev.kobj, KOBJ_CHANGE, reset_done_event); } @@ -902,10 +844,11 @@ i915_error_object_free(struct drm_i915_error_object *obj) kfree(obj); } -static void -i915_error_state_free(struct drm_device *dev, - struct drm_i915_error_state *error) +void +i915_error_state_free(struct kref *error_ref) { + struct drm_i915_error_state *error = container_of(error_ref, + typeof(*error), ref); int i; for (i = 0; i < ARRAY_SIZE(error->ring); i++) { @@ -918,37 +861,56 @@ i915_error_state_free(struct drm_device *dev, kfree(error->overlay); kfree(error); } - -static u32 capture_bo_list(struct drm_i915_error_buffer *err, - int count, - struct list_head *head) +static void capture_bo(struct drm_i915_error_buffer *err, + struct drm_i915_gem_object *obj) +{ + err->size = obj->base.size; + err->name = obj->base.name; + err->seqno = obj->last_rendering_seqno; + err->gtt_offset = obj->gtt_offset; + err->read_domains = obj->base.read_domains; + err->write_domain = obj->base.write_domain; + err->fence_reg = obj->fence_reg; + err->pinned = 0; + if (obj->pin_count > 0) + err->pinned = 1; + if (obj->user_pin_count > 0) + err->pinned = -1; + err->tiling = obj->tiling_mode; + err->dirty = obj->dirty; + err->purgeable = obj->madv != I915_MADV_WILLNEED; + err->ring = obj->ring ? obj->ring->id : -1; + err->cache_level = obj->cache_level; +} + +static u32 capture_active_bo(struct drm_i915_error_buffer *err, + int count, struct list_head *head) { struct drm_i915_gem_object *obj; int i = 0; list_for_each_entry(obj, head, mm_list) { - err->size = obj->base.size; - err->name = obj->base.name; - err->seqno = obj->last_rendering_seqno; - err->gtt_offset = obj->gtt_offset; - err->read_domains = obj->base.read_domains; - err->write_domain = obj->base.write_domain; - err->fence_reg = obj->fence_reg; - err->pinned = 0; - if (obj->pin_count > 0) - err->pinned = 1; - if (obj->user_pin_count > 0) - err->pinned = -1; - err->tiling = obj->tiling_mode; - err->dirty = obj->dirty; - err->purgeable = obj->madv != I915_MADV_WILLNEED; - err->ring = obj->ring ? obj->ring->id : -1; - err->cache_level = obj->cache_level; - + capture_bo(err++, obj); if (++i == count) break; + } + + return i; +} + +static u32 capture_pinned_bo(struct drm_i915_error_buffer *err, + int count, struct list_head *head) +{ + struct drm_i915_gem_object *obj; + int i = 0; + + list_for_each_entry(obj, head, gtt_list) { + if (obj->pin_count == 0) + continue; - err++; + capture_bo(err++, obj); + if (++i == count) + break; } return i; @@ -1045,6 +1007,7 @@ static void i915_record_ring_state(struct drm_device *dev, error->instdone[ring->id] = I915_READ(INSTDONE); } + error->waiting[ring->id] = waitqueue_active(&ring->irq_queue); error->instpm[ring->id] = I915_READ(RING_INSTPM(ring->mmio_base)); error->seqno[ring->id] = ring->get_seqno(ring); error->acthd[ring->id] = intel_ring_get_active_head(ring); @@ -1059,15 +1022,11 @@ static void i915_gem_record_rings(struct drm_device *dev, struct drm_i915_error_state *error) { struct drm_i915_private *dev_priv = dev->dev_private; + struct intel_ring_buffer *ring; struct drm_i915_gem_request *request; int i, count; - for (i = 0; i < I915_NUM_RINGS; i++) { - struct intel_ring_buffer *ring = &dev_priv->ring[i]; - - if (ring->obj == NULL) - continue; - + for_each_ring(ring, dev_priv, i) { i915_record_ring_state(dev, error, ring); error->ring[i].batchbuffer = @@ -1134,8 +1093,19 @@ static void i915_capture_error_state(struct drm_device *dev) DRM_INFO("capturing error event; look for more information in /debug/dri/%d/i915_error_state\n", dev->primary->index); + kref_init(&error->ref); error->eir = I915_READ(EIR); error->pgtbl_er = I915_READ(PGTBL_ER); + + if (HAS_PCH_SPLIT(dev)) + error->ier = I915_READ(DEIER) | I915_READ(GTIER); + else if (IS_VALLEYVIEW(dev)) + error->ier = I915_READ(GTIER) | I915_READ(VLV_IER); + else if (IS_GEN2(dev)) + error->ier = I915_READ16(IER); + else + error->ier = I915_READ(IER); + for_each_pipe(pipe) error->pipestat[pipe] = I915_READ(PIPESTAT(pipe)); @@ -1155,8 +1125,9 @@ static void i915_capture_error_state(struct drm_device *dev) list_for_each_entry(obj, &dev_priv->mm.active_list, mm_list) i++; error->active_bo_count = i; - list_for_each_entry(obj, &dev_priv->mm.pinned_list, mm_list) - i++; + list_for_each_entry(obj, &dev_priv->mm.gtt_list, gtt_list) + if (obj->pin_count) + i++; error->pinned_bo_count = i - error->active_bo_count; error->active_bo = NULL; @@ -1171,15 +1142,15 @@ static void i915_capture_error_state(struct drm_device *dev) if (error->active_bo) error->active_bo_count = - capture_bo_list(error->active_bo, - error->active_bo_count, - &dev_priv->mm.active_list); + capture_active_bo(error->active_bo, + error->active_bo_count, + &dev_priv->mm.active_list); if (error->pinned_bo) error->pinned_bo_count = - capture_bo_list(error->pinned_bo, - error->pinned_bo_count, - &dev_priv->mm.pinned_list); + capture_pinned_bo(error->pinned_bo, + error->pinned_bo_count, + &dev_priv->mm.gtt_list); do_gettimeofday(&error->time); @@ -1194,7 +1165,7 @@ static void i915_capture_error_state(struct drm_device *dev) spin_unlock_irqrestore(&dev_priv->error_lock, flags); if (error) - i915_error_state_free(dev, error); + i915_error_state_free(&error->ref); } void i915_destroy_error_state(struct drm_device *dev) @@ -1209,7 +1180,7 @@ void i915_destroy_error_state(struct drm_device *dev) spin_unlock_irqrestore(&dev_priv->error_lock, flags); if (error) - i915_error_state_free(dev, error); + kref_put(&error->ref, i915_error_state_free); } #else #define i915_capture_error_state(x) @@ -1320,6 +1291,8 @@ static void i915_report_and_clear_eir(struct drm_device *dev) void i915_handle_error(struct drm_device *dev, bool wedged) { struct drm_i915_private *dev_priv = dev->dev_private; + struct intel_ring_buffer *ring; + int i; i915_capture_error_state(dev); i915_report_and_clear_eir(dev); @@ -1331,11 +1304,8 @@ void i915_handle_error(struct drm_device *dev, bool wedged) /* * Wakeup waiting processes so they don't hang */ - wake_up_all(&dev_priv->ring[RCS].irq_queue); - if (HAS_BSD(dev)) - wake_up_all(&dev_priv->ring[VCS].irq_queue); - if (HAS_BLT(dev)) - wake_up_all(&dev_priv->ring[BCS].irq_queue); + for_each_ring(ring, dev_priv, i) + wake_up_all(&ring->irq_queue); } queue_work(dev_priv->wq, &dev_priv->error_work); @@ -1385,248 +1355,6 @@ static void i915_pageflip_stall_check(struct drm_device *dev, int pipe) } } -static irqreturn_t i915_driver_irq_handler(DRM_IRQ_ARGS) -{ - struct drm_device *dev = (struct drm_device *) arg; - drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private; - struct drm_i915_master_private *master_priv; - u32 iir, new_iir; - u32 pipe_stats[I915_MAX_PIPES]; - u32 vblank_status; - int vblank = 0; - unsigned long irqflags; - int irq_received; - int ret = IRQ_NONE, pipe; - bool blc_event = false; - - atomic_inc(&dev_priv->irq_received); - - iir = I915_READ(IIR); - - if (INTEL_INFO(dev)->gen >= 4) - vblank_status = PIPE_START_VBLANK_INTERRUPT_STATUS; - else - vblank_status = PIPE_VBLANK_INTERRUPT_STATUS; - - for (;;) { - irq_received = iir != 0; - - /* Can't rely on pipestat interrupt bit in iir as it might - * have been cleared after the pipestat interrupt was received. - * It doesn't set the bit in iir again, but it still produces - * interrupts (for non-MSI). - */ - spin_lock_irqsave(&dev_priv->irq_lock, irqflags); - if (iir & I915_RENDER_COMMAND_PARSER_ERROR_INTERRUPT) - i915_handle_error(dev, false); - - for_each_pipe(pipe) { - int reg = PIPESTAT(pipe); - pipe_stats[pipe] = I915_READ(reg); - - /* - * Clear the PIPE*STAT regs before the IIR - */ - if (pipe_stats[pipe] & 0x8000ffff) { - if (pipe_stats[pipe] & PIPE_FIFO_UNDERRUN_STATUS) - DRM_DEBUG_DRIVER("pipe %c underrun\n", - pipe_name(pipe)); - I915_WRITE(reg, pipe_stats[pipe]); - irq_received = 1; - } - } - spin_unlock_irqrestore(&dev_priv->irq_lock, irqflags); - - if (!irq_received) - break; - - ret = IRQ_HANDLED; - - /* Consume port. Then clear IIR or we'll miss events */ - if ((I915_HAS_HOTPLUG(dev)) && - (iir & I915_DISPLAY_PORT_INTERRUPT)) { - u32 hotplug_status = I915_READ(PORT_HOTPLUG_STAT); - - DRM_DEBUG_DRIVER("hotplug event received, stat 0x%08x\n", - hotplug_status); - if (hotplug_status & dev_priv->hotplug_supported_mask) - queue_work(dev_priv->wq, - &dev_priv->hotplug_work); - - I915_WRITE(PORT_HOTPLUG_STAT, hotplug_status); - I915_READ(PORT_HOTPLUG_STAT); - } - - I915_WRITE(IIR, iir); - new_iir = I915_READ(IIR); /* Flush posted writes */ - - if (dev->primary->master) { - master_priv = dev->primary->master->driver_priv; - if (master_priv->sarea_priv) - master_priv->sarea_priv->last_dispatch = - READ_BREADCRUMB(dev_priv); - } - - if (iir & I915_USER_INTERRUPT) - notify_ring(dev, &dev_priv->ring[RCS]); - if (iir & I915_BSD_USER_INTERRUPT) - notify_ring(dev, &dev_priv->ring[VCS]); - - if (iir & I915_DISPLAY_PLANE_A_FLIP_PENDING_INTERRUPT) { - intel_prepare_page_flip(dev, 0); - if (dev_priv->flip_pending_is_done) - intel_finish_page_flip_plane(dev, 0); - } - - if (iir & I915_DISPLAY_PLANE_B_FLIP_PENDING_INTERRUPT) { - intel_prepare_page_flip(dev, 1); - if (dev_priv->flip_pending_is_done) - intel_finish_page_flip_plane(dev, 1); - } - - for_each_pipe(pipe) { - if (pipe_stats[pipe] & vblank_status && - drm_handle_vblank(dev, pipe)) { - vblank++; - if (!dev_priv->flip_pending_is_done) { - i915_pageflip_stall_check(dev, pipe); - intel_finish_page_flip(dev, pipe); - } - } - - if (pipe_stats[pipe] & PIPE_LEGACY_BLC_EVENT_STATUS) - blc_event = true; - } - - - if (blc_event || (iir & I915_ASLE_INTERRUPT)) - intel_opregion_asle_intr(dev); - - /* With MSI, interrupts are only generated when iir - * transitions from zero to nonzero. If another bit got - * set while we were handling the existing iir bits, then - * we would never get another interrupt. - * - * This is fine on non-MSI as well, as if we hit this path - * we avoid exiting the interrupt handler only to generate - * another one. - * - * Note that for MSI this could cause a stray interrupt report - * if an interrupt landed in the time between writing IIR and - * the posting read. This should be rare enough to never - * trigger the 99% of 100,000 interrupts test for disabling - * stray interrupts. - */ - iir = new_iir; - } - - return ret; -} - -static int i915_emit_irq(struct drm_device * dev) -{ - drm_i915_private_t *dev_priv = dev->dev_private; - struct drm_i915_master_private *master_priv = dev->primary->master->driver_priv; - - i915_kernel_lost_context(dev); - - DRM_DEBUG_DRIVER("\n"); - - dev_priv->counter++; - if (dev_priv->counter > 0x7FFFFFFFUL) - dev_priv->counter = 1; - if (master_priv->sarea_priv) - master_priv->sarea_priv->last_enqueue = dev_priv->counter; - - if (BEGIN_LP_RING(4) == 0) { - OUT_RING(MI_STORE_DWORD_INDEX); - OUT_RING(I915_BREADCRUMB_INDEX << MI_STORE_DWORD_INDEX_SHIFT); - OUT_RING(dev_priv->counter); - OUT_RING(MI_USER_INTERRUPT); - ADVANCE_LP_RING(); - } - - return dev_priv->counter; -} - -static int i915_wait_irq(struct drm_device * dev, int irq_nr) -{ - drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private; - struct drm_i915_master_private *master_priv = dev->primary->master->driver_priv; - int ret = 0; - struct intel_ring_buffer *ring = LP_RING(dev_priv); - - DRM_DEBUG_DRIVER("irq_nr=%d breadcrumb=%d\n", irq_nr, - READ_BREADCRUMB(dev_priv)); - - if (READ_BREADCRUMB(dev_priv) >= irq_nr) { - if (master_priv->sarea_priv) - master_priv->sarea_priv->last_dispatch = READ_BREADCRUMB(dev_priv); - return 0; - } - - if (master_priv->sarea_priv) - master_priv->sarea_priv->perf_boxes |= I915_BOX_WAIT; - - if (ring->irq_get(ring)) { - DRM_WAIT_ON(ret, ring->irq_queue, 3 * DRM_HZ, - READ_BREADCRUMB(dev_priv) >= irq_nr); - ring->irq_put(ring); - } else if (wait_for(READ_BREADCRUMB(dev_priv) >= irq_nr, 3000)) - ret = -EBUSY; - - if (ret == -EBUSY) { - DRM_ERROR("EBUSY -- rec: %d emitted: %d\n", - READ_BREADCRUMB(dev_priv), (int)dev_priv->counter); - } - - return ret; -} - -/* Needs the lock as it touches the ring. - */ -int i915_irq_emit(struct drm_device *dev, void *data, - struct drm_file *file_priv) -{ - drm_i915_private_t *dev_priv = dev->dev_private; - drm_i915_irq_emit_t *emit = data; - int result; - - if (!dev_priv || !LP_RING(dev_priv)->virtual_start) { - DRM_ERROR("called with no initialization\n"); - return -EINVAL; - } - - RING_LOCK_TEST_WITH_RETURN(dev, file_priv); - - mutex_lock(&dev->struct_mutex); - result = i915_emit_irq(dev); - mutex_unlock(&dev->struct_mutex); - - if (DRM_COPY_TO_USER(emit->irq_seq, &result, sizeof(int))) { - DRM_ERROR("copy_to_user\n"); - return -EFAULT; - } - - return 0; -} - -/* Doesn't need the hardware lock. - */ -int i915_irq_wait(struct drm_device *dev, void *data, - struct drm_file *file_priv) -{ - drm_i915_private_t *dev_priv = dev->dev_private; - drm_i915_irq_wait_t *irqwait = data; - - if (!dev_priv) { - DRM_ERROR("called with no initialization\n"); - return -EINVAL; - } - - return i915_wait_irq(dev, irqwait->irq_seq); -} - /* Called from drm generic code, passed 'crtc' which * we use as a pipe index */ @@ -1648,7 +1376,7 @@ static int i915_enable_vblank(struct drm_device *dev, int pipe) /* maintain vblank delivery even in deep C-states */ if (dev_priv->info->gen == 3) - I915_WRITE(INSTPM, INSTPM_AGPBUSY_DIS << 16); + I915_WRITE(INSTPM, _MASKED_BIT_DISABLE(INSTPM_AGPBUSY_DIS)); spin_unlock_irqrestore(&dev_priv->irq_lock, irqflags); return 0; @@ -1679,8 +1407,8 @@ static int ivybridge_enable_vblank(struct drm_device *dev, int pipe) return -EINVAL; spin_lock_irqsave(&dev_priv->irq_lock, irqflags); - ironlake_enable_display_irq(dev_priv, (pipe == 0) ? - DE_PIPEA_VBLANK_IVB : DE_PIPEB_VBLANK_IVB); + ironlake_enable_display_irq(dev_priv, + DE_PIPEA_VBLANK_IVB << (5 * pipe)); spin_unlock_irqrestore(&dev_priv->irq_lock, irqflags); return 0; @@ -1722,8 +1450,7 @@ static void i915_disable_vblank(struct drm_device *dev, int pipe) spin_lock_irqsave(&dev_priv->irq_lock, irqflags); if (dev_priv->info->gen == 3) - I915_WRITE(INSTPM, - INSTPM_AGPBUSY_DIS << 16 | INSTPM_AGPBUSY_DIS); + I915_WRITE(INSTPM, _MASKED_BIT_ENABLE(INSTPM_AGPBUSY_DIS)); i915_disable_pipestat(dev_priv, pipe, PIPE_VBLANK_INTERRUPT_ENABLE | @@ -1748,8 +1475,8 @@ static void ivybridge_disable_vblank(struct drm_device *dev, int pipe) unsigned long irqflags; spin_lock_irqsave(&dev_priv->irq_lock, irqflags); - ironlake_disable_display_irq(dev_priv, (pipe == 0) ? - DE_PIPEA_VBLANK_IVB : DE_PIPEB_VBLANK_IVB); + ironlake_disable_display_irq(dev_priv, + DE_PIPEA_VBLANK_IVB << (pipe * 5)); spin_unlock_irqrestore(&dev_priv->irq_lock, irqflags); } @@ -1774,61 +1501,6 @@ static void valleyview_disable_vblank(struct drm_device *dev, int pipe) spin_unlock_irqrestore(&dev_priv->irq_lock, irqflags); } - -/* Set the vblank monitor pipe - */ -int i915_vblank_pipe_set(struct drm_device *dev, void *data, - struct drm_file *file_priv) -{ - drm_i915_private_t *dev_priv = dev->dev_private; - - if (!dev_priv) { - DRM_ERROR("called with no initialization\n"); - return -EINVAL; - } - - return 0; -} - -int i915_vblank_pipe_get(struct drm_device *dev, void *data, - struct drm_file *file_priv) -{ - drm_i915_private_t *dev_priv = dev->dev_private; - drm_i915_vblank_pipe_t *pipe = data; - - if (!dev_priv) { - DRM_ERROR("called with no initialization\n"); - return -EINVAL; - } - - pipe->pipe = DRM_I915_VBLANK_PIPE_A | DRM_I915_VBLANK_PIPE_B; - - return 0; -} - -/** - * Schedule buffer swap at given vertical blank. - */ -int i915_vblank_swap(struct drm_device *dev, void *data, - struct drm_file *file_priv) -{ - /* The delayed swap mechanism was fundamentally racy, and has been - * removed. The model was that the client requested a delayed flip/swap - * from the kernel, then waited for vblank before continuing to perform - * rendering. The problem was that the kernel might wake the client - * up before it dispatched the vblank swap (since the lock has to be - * held while touching the ringbuffer), in which case the client would - * clear and start the next frame before the swap occurred, and - * flicker would occur in addition to likely missing the vblank. - * - * In the absence of this ioctl, userland falls back to a correct path - * of waiting for a vblank, then dispatching the swap on its own. - * Context switching to userland and back is plenty fast enough for - * meeting the requirements of vblank swapping. - */ - return -EINVAL; -} - static u32 ring_last_seqno(struct intel_ring_buffer *ring) { @@ -1841,11 +1513,9 @@ static bool i915_hangcheck_ring_idle(struct intel_ring_buffer *ring, bool *err) if (list_empty(&ring->request_list) || i915_seqno_passed(ring->get_seqno(ring), ring_last_seqno(ring))) { /* Issue a wake-up to catch stuck h/w. */ - if (ring->waiting_seqno && waitqueue_active(&ring->irq_queue)) { - DRM_ERROR("Hangcheck timer elapsed... %s idle [waiting on %d, at %d], missed IRQ?\n", - ring->name, - ring->waiting_seqno, - ring->get_seqno(ring)); + if (waitqueue_active(&ring->irq_queue)) { + DRM_ERROR("Hangcheck timer elapsed... %s idle\n", + ring->name); wake_up_all(&ring->irq_queue); *err = true; } @@ -1873,26 +1543,25 @@ static bool i915_hangcheck_hung(struct drm_device *dev) drm_i915_private_t *dev_priv = dev->dev_private; if (dev_priv->hangcheck_count++ > 1) { + bool hung = true; + DRM_ERROR("Hangcheck timer elapsed... GPU hung\n"); i915_handle_error(dev, true); if (!IS_GEN2(dev)) { + struct intel_ring_buffer *ring; + int i; + /* 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. */ - if (kick_ring(&dev_priv->ring[RCS])) - return false; - - if (HAS_BSD(dev) && kick_ring(&dev_priv->ring[VCS])) - return false; - - if (HAS_BLT(dev) && kick_ring(&dev_priv->ring[BCS])) - return false; + for_each_ring(ring, dev_priv, i) + hung &= !kick_ring(ring); } - return true; + return hung; } return false; @@ -1908,16 +1577,23 @@ void i915_hangcheck_elapsed(unsigned long data) { struct drm_device *dev = (struct drm_device *)data; drm_i915_private_t *dev_priv = dev->dev_private; - uint32_t acthd, instdone, instdone1, acthd_bsd, acthd_blt; - bool err = false; + uint32_t acthd[I915_NUM_RINGS], instdone, instdone1; + struct intel_ring_buffer *ring; + bool err = false, idle; + int i; if (!i915_enable_hangcheck) return; + memset(acthd, 0, sizeof(acthd)); + idle = true; + for_each_ring(ring, dev_priv, i) { + idle &= i915_hangcheck_ring_idle(ring, &err); + acthd[i] = intel_ring_get_active_head(ring); + } + /* If all work is done then ACTHD clearly hasn't advanced. */ - if (i915_hangcheck_ring_idle(&dev_priv->ring[RCS], &err) && - i915_hangcheck_ring_idle(&dev_priv->ring[VCS], &err) && - i915_hangcheck_ring_idle(&dev_priv->ring[BCS], &err)) { + if (idle) { if (err) { if (i915_hangcheck_hung(dev)) return; @@ -1936,15 +1612,8 @@ void i915_hangcheck_elapsed(unsigned long data) instdone = I915_READ(INSTDONE_I965); instdone1 = I915_READ(INSTDONE1); } - acthd = intel_ring_get_active_head(&dev_priv->ring[RCS]); - acthd_bsd = HAS_BSD(dev) ? - intel_ring_get_active_head(&dev_priv->ring[VCS]) : 0; - acthd_blt = HAS_BLT(dev) ? - intel_ring_get_active_head(&dev_priv->ring[BCS]) : 0; - if (dev_priv->last_acthd == acthd && - dev_priv->last_acthd_bsd == acthd_bsd && - dev_priv->last_acthd_blt == acthd_blt && + if (memcmp(dev_priv->last_acthd, acthd, sizeof(acthd)) == 0 && dev_priv->last_instdone == instdone && dev_priv->last_instdone1 == instdone1) { if (i915_hangcheck_hung(dev)) @@ -1952,9 +1621,7 @@ void i915_hangcheck_elapsed(unsigned long data) } else { dev_priv->hangcheck_count = 0; - dev_priv->last_acthd = acthd; - dev_priv->last_acthd_bsd = acthd_bsd; - dev_priv->last_acthd_blt = acthd_blt; + memcpy(dev_priv->last_acthd, acthd, sizeof(acthd)); dev_priv->last_instdone = instdone; dev_priv->last_instdone1 = instdone1; } @@ -1973,10 +1640,6 @@ static void ironlake_irq_preinstall(struct drm_device *dev) atomic_set(&dev_priv->irq_received, 0); - INIT_WORK(&dev_priv->hotplug_work, i915_hotplug_work_func); - INIT_WORK(&dev_priv->error_work, i915_error_work_func); - if (IS_GEN6(dev) || IS_IVYBRIDGE(dev)) - INIT_WORK(&dev_priv->rps_work, gen6_pm_rps_work); I915_WRITE(HWSTAM, 0xeffe); @@ -2004,9 +1667,6 @@ static void valleyview_irq_preinstall(struct drm_device *dev) atomic_set(&dev_priv->irq_received, 0); - INIT_WORK(&dev_priv->hotplug_work, i915_hotplug_work_func); - INIT_WORK(&dev_priv->error_work, i915_error_work_func); - /* VLV magic */ I915_WRITE(VLV_IMR, 0); I915_WRITE(RING_IMR(RENDER_RING_BASE), 0); @@ -2061,13 +1721,6 @@ static int ironlake_irq_postinstall(struct drm_device *dev) u32 render_irqs; u32 hotplug_mask; - DRM_INIT_WAITQUEUE(&dev_priv->ring[RCS].irq_queue); - if (HAS_BSD(dev)) - DRM_INIT_WAITQUEUE(&dev_priv->ring[VCS].irq_queue); - if (HAS_BLT(dev)) - DRM_INIT_WAITQUEUE(&dev_priv->ring[BCS].irq_queue); - - dev_priv->vblank_pipe = DRM_I915_VBLANK_PIPE_A | DRM_I915_VBLANK_PIPE_B; dev_priv->irq_mask = ~display_mask; /* should always can generate irq */ @@ -2130,26 +1783,24 @@ static int ivybridge_irq_postinstall(struct drm_device *dev) { drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private; /* enable kind of interrupts always enabled */ - u32 display_mask = DE_MASTER_IRQ_CONTROL | DE_GSE_IVB | - DE_PCH_EVENT_IVB | DE_PLANEA_FLIP_DONE_IVB | - DE_PLANEB_FLIP_DONE_IVB; + u32 display_mask = + DE_MASTER_IRQ_CONTROL | DE_GSE_IVB | DE_PCH_EVENT_IVB | + DE_PLANEC_FLIP_DONE_IVB | + DE_PLANEB_FLIP_DONE_IVB | + DE_PLANEA_FLIP_DONE_IVB; u32 render_irqs; u32 hotplug_mask; - DRM_INIT_WAITQUEUE(&dev_priv->ring[RCS].irq_queue); - if (HAS_BSD(dev)) - DRM_INIT_WAITQUEUE(&dev_priv->ring[VCS].irq_queue); - if (HAS_BLT(dev)) - DRM_INIT_WAITQUEUE(&dev_priv->ring[BCS].irq_queue); - - dev_priv->vblank_pipe = DRM_I915_VBLANK_PIPE_A | DRM_I915_VBLANK_PIPE_B; dev_priv->irq_mask = ~display_mask; /* should always can generate irq */ I915_WRITE(DEIIR, I915_READ(DEIIR)); I915_WRITE(DEIMR, dev_priv->irq_mask); - I915_WRITE(DEIER, display_mask | DE_PIPEA_VBLANK_IVB | - DE_PIPEB_VBLANK_IVB); + I915_WRITE(DEIER, + display_mask | + DE_PIPEC_VBLANK_IVB | + DE_PIPEB_VBLANK_IVB | + DE_PIPEA_VBLANK_IVB); POSTING_READ(DEIER); dev_priv->gt_irq_mask = ~0; @@ -2192,16 +1843,9 @@ static int valleyview_irq_postinstall(struct drm_device *dev) dev_priv->irq_mask = ~enable_mask; - - DRM_INIT_WAITQUEUE(&dev_priv->ring[RCS].irq_queue); - DRM_INIT_WAITQUEUE(&dev_priv->ring[VCS].irq_queue); - DRM_INIT_WAITQUEUE(&dev_priv->ring[BCS].irq_queue); - dev_priv->pipestat[0] = 0; dev_priv->pipestat[1] = 0; - dev_priv->vblank_pipe = DRM_I915_VBLANK_PIPE_A | DRM_I915_VBLANK_PIPE_B; - /* Hack for broken MSIs on VLV */ pci_write_config_dword(dev_priv->dev->pdev, 0x94, 0xfee00000); pci_read_config_word(dev->pdev, 0x98, &msid); @@ -2268,15 +1912,413 @@ static int valleyview_irq_postinstall(struct drm_device *dev) return 0; } -static void i915_driver_irq_preinstall(struct drm_device * dev) +static void valleyview_irq_uninstall(struct drm_device *dev) +{ + drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private; + int pipe; + + if (!dev_priv) + return; + + for_each_pipe(pipe) + I915_WRITE(PIPESTAT(pipe), 0xffff); + + I915_WRITE(HWSTAM, 0xffffffff); + I915_WRITE(PORT_HOTPLUG_EN, 0); + I915_WRITE(PORT_HOTPLUG_STAT, I915_READ(PORT_HOTPLUG_STAT)); + for_each_pipe(pipe) + I915_WRITE(PIPESTAT(pipe), 0xffff); + I915_WRITE(VLV_IIR, 0xffffffff); + I915_WRITE(VLV_IMR, 0xffffffff); + I915_WRITE(VLV_IER, 0x0); + POSTING_READ(VLV_IER); +} + +static void ironlake_irq_uninstall(struct drm_device *dev) +{ + drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private; + + if (!dev_priv) + return; + + I915_WRITE(HWSTAM, 0xffffffff); + + I915_WRITE(DEIMR, 0xffffffff); + I915_WRITE(DEIER, 0x0); + I915_WRITE(DEIIR, I915_READ(DEIIR)); + + I915_WRITE(GTIMR, 0xffffffff); + I915_WRITE(GTIER, 0x0); + I915_WRITE(GTIIR, I915_READ(GTIIR)); + + I915_WRITE(SDEIMR, 0xffffffff); + I915_WRITE(SDEIER, 0x0); + I915_WRITE(SDEIIR, I915_READ(SDEIIR)); +} + +static void i8xx_irq_preinstall(struct drm_device * dev) { drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private; int pipe; atomic_set(&dev_priv->irq_received, 0); - INIT_WORK(&dev_priv->hotplug_work, i915_hotplug_work_func); - INIT_WORK(&dev_priv->error_work, i915_error_work_func); + for_each_pipe(pipe) + I915_WRITE(PIPESTAT(pipe), 0); + I915_WRITE16(IMR, 0xffff); + I915_WRITE16(IER, 0x0); + POSTING_READ16(IER); +} + +static int i8xx_irq_postinstall(struct drm_device *dev) +{ + drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private; + + dev_priv->pipestat[0] = 0; + dev_priv->pipestat[1] = 0; + + I915_WRITE16(EMR, + ~(I915_ERROR_PAGE_TABLE | I915_ERROR_MEMORY_REFRESH)); + + /* Unmask the interrupts that we always want on. */ + dev_priv->irq_mask = + ~(I915_DISPLAY_PIPE_A_EVENT_INTERRUPT | + I915_DISPLAY_PIPE_B_EVENT_INTERRUPT | + I915_DISPLAY_PLANE_A_FLIP_PENDING_INTERRUPT | + I915_DISPLAY_PLANE_B_FLIP_PENDING_INTERRUPT | + I915_RENDER_COMMAND_PARSER_ERROR_INTERRUPT); + I915_WRITE16(IMR, dev_priv->irq_mask); + + I915_WRITE16(IER, + I915_DISPLAY_PIPE_A_EVENT_INTERRUPT | + I915_DISPLAY_PIPE_B_EVENT_INTERRUPT | + I915_RENDER_COMMAND_PARSER_ERROR_INTERRUPT | + I915_USER_INTERRUPT); + POSTING_READ16(IER); + + return 0; +} + +static irqreturn_t i8xx_irq_handler(DRM_IRQ_ARGS) +{ + struct drm_device *dev = (struct drm_device *) arg; + drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private; + u16 iir, new_iir; + u32 pipe_stats[2]; + unsigned long irqflags; + int irq_received; + int pipe; + u16 flip_mask = + I915_DISPLAY_PLANE_A_FLIP_PENDING_INTERRUPT | + I915_DISPLAY_PLANE_B_FLIP_PENDING_INTERRUPT; + + atomic_inc(&dev_priv->irq_received); + + iir = I915_READ16(IIR); + if (iir == 0) + return IRQ_NONE; + + while (iir & ~flip_mask) { + /* Can't rely on pipestat interrupt bit in iir as it might + * have been cleared after the pipestat interrupt was received. + * It doesn't set the bit in iir again, but it still produces + * interrupts (for non-MSI). + */ + spin_lock_irqsave(&dev_priv->irq_lock, irqflags); + if (iir & I915_RENDER_COMMAND_PARSER_ERROR_INTERRUPT) + i915_handle_error(dev, false); + + for_each_pipe(pipe) { + int reg = PIPESTAT(pipe); + pipe_stats[pipe] = I915_READ(reg); + + /* + * Clear the PIPE*STAT regs before the IIR + */ + if (pipe_stats[pipe] & 0x8000ffff) { + if (pipe_stats[pipe] & PIPE_FIFO_UNDERRUN_STATUS) + DRM_DEBUG_DRIVER("pipe %c underrun\n", + pipe_name(pipe)); + I915_WRITE(reg, pipe_stats[pipe]); + irq_received = 1; + } + } + spin_unlock_irqrestore(&dev_priv->irq_lock, irqflags); + + I915_WRITE16(IIR, iir & ~flip_mask); + new_iir = I915_READ16(IIR); /* Flush posted writes */ + + i915_update_dri1_breadcrumb(dev); + + if (iir & I915_USER_INTERRUPT) + notify_ring(dev, &dev_priv->ring[RCS]); + + if (pipe_stats[0] & PIPE_VBLANK_INTERRUPT_STATUS && + drm_handle_vblank(dev, 0)) { + if (iir & I915_DISPLAY_PLANE_A_FLIP_PENDING_INTERRUPT) { + intel_prepare_page_flip(dev, 0); + intel_finish_page_flip(dev, 0); + flip_mask &= ~I915_DISPLAY_PLANE_A_FLIP_PENDING_INTERRUPT; + } + } + + if (pipe_stats[1] & PIPE_VBLANK_INTERRUPT_STATUS && + drm_handle_vblank(dev, 1)) { + if (iir & I915_DISPLAY_PLANE_B_FLIP_PENDING_INTERRUPT) { + intel_prepare_page_flip(dev, 1); + intel_finish_page_flip(dev, 1); + flip_mask &= ~I915_DISPLAY_PLANE_B_FLIP_PENDING_INTERRUPT; + } + } + + iir = new_iir; + } + + return IRQ_HANDLED; +} + +static void i8xx_irq_uninstall(struct drm_device * dev) +{ + drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private; + int pipe; + + for_each_pipe(pipe) { + /* Clear enable bits; then clear status bits */ + I915_WRITE(PIPESTAT(pipe), 0); + I915_WRITE(PIPESTAT(pipe), I915_READ(PIPESTAT(pipe))); + } + I915_WRITE16(IMR, 0xffff); + I915_WRITE16(IER, 0x0); + I915_WRITE16(IIR, I915_READ16(IIR)); +} + +static void i915_irq_preinstall(struct drm_device * dev) +{ + drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private; + int pipe; + + atomic_set(&dev_priv->irq_received, 0); + + if (I915_HAS_HOTPLUG(dev)) { + I915_WRITE(PORT_HOTPLUG_EN, 0); + I915_WRITE(PORT_HOTPLUG_STAT, I915_READ(PORT_HOTPLUG_STAT)); + } + + I915_WRITE16(HWSTAM, 0xeffe); + for_each_pipe(pipe) + I915_WRITE(PIPESTAT(pipe), 0); + I915_WRITE(IMR, 0xffffffff); + I915_WRITE(IER, 0x0); + POSTING_READ(IER); +} + +static int i915_irq_postinstall(struct drm_device *dev) +{ + drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private; + u32 enable_mask; + + dev_priv->pipestat[0] = 0; + dev_priv->pipestat[1] = 0; + + I915_WRITE(EMR, ~(I915_ERROR_PAGE_TABLE | I915_ERROR_MEMORY_REFRESH)); + + /* Unmask the interrupts that we always want on. */ + dev_priv->irq_mask = + ~(I915_ASLE_INTERRUPT | + I915_DISPLAY_PIPE_A_EVENT_INTERRUPT | + I915_DISPLAY_PIPE_B_EVENT_INTERRUPT | + I915_DISPLAY_PLANE_A_FLIP_PENDING_INTERRUPT | + I915_DISPLAY_PLANE_B_FLIP_PENDING_INTERRUPT | + I915_RENDER_COMMAND_PARSER_ERROR_INTERRUPT); + + enable_mask = + I915_ASLE_INTERRUPT | + I915_DISPLAY_PIPE_A_EVENT_INTERRUPT | + I915_DISPLAY_PIPE_B_EVENT_INTERRUPT | + I915_RENDER_COMMAND_PARSER_ERROR_INTERRUPT | + I915_USER_INTERRUPT; + + if (I915_HAS_HOTPLUG(dev)) { + /* Enable in IER... */ + enable_mask |= I915_DISPLAY_PORT_INTERRUPT; + /* and unmask in IMR */ + dev_priv->irq_mask &= ~I915_DISPLAY_PORT_INTERRUPT; + } + + I915_WRITE(IMR, dev_priv->irq_mask); + I915_WRITE(IER, enable_mask); + POSTING_READ(IER); + + if (I915_HAS_HOTPLUG(dev)) { + u32 hotplug_en = I915_READ(PORT_HOTPLUG_EN); + + if (dev_priv->hotplug_supported_mask & HDMIB_HOTPLUG_INT_STATUS) + hotplug_en |= HDMIB_HOTPLUG_INT_EN; + if (dev_priv->hotplug_supported_mask & HDMIC_HOTPLUG_INT_STATUS) + hotplug_en |= HDMIC_HOTPLUG_INT_EN; + if (dev_priv->hotplug_supported_mask & HDMID_HOTPLUG_INT_STATUS) + hotplug_en |= HDMID_HOTPLUG_INT_EN; + if (dev_priv->hotplug_supported_mask & SDVOC_HOTPLUG_INT_STATUS) + hotplug_en |= SDVOC_HOTPLUG_INT_EN; + if (dev_priv->hotplug_supported_mask & SDVOB_HOTPLUG_INT_STATUS) + hotplug_en |= SDVOB_HOTPLUG_INT_EN; + if (dev_priv->hotplug_supported_mask & CRT_HOTPLUG_INT_STATUS) { + hotplug_en |= CRT_HOTPLUG_INT_EN; + hotplug_en |= CRT_HOTPLUG_VOLTAGE_COMPARE_50; + } + + /* Ignore TV since it's buggy */ + + I915_WRITE(PORT_HOTPLUG_EN, hotplug_en); + } + + intel_opregion_enable_asle(dev); + + return 0; +} + +static irqreturn_t i915_irq_handler(DRM_IRQ_ARGS) +{ + struct drm_device *dev = (struct drm_device *) arg; + drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private; + u32 iir, new_iir, pipe_stats[I915_MAX_PIPES]; + unsigned long irqflags; + u32 flip_mask = + I915_DISPLAY_PLANE_A_FLIP_PENDING_INTERRUPT | + I915_DISPLAY_PLANE_B_FLIP_PENDING_INTERRUPT; + u32 flip[2] = { + I915_DISPLAY_PLANE_A_FLIP_PENDING_INTERRUPT, + I915_DISPLAY_PLANE_B_FLIP_PENDING_INTERRUPT + }; + int pipe, ret = IRQ_NONE; + + atomic_inc(&dev_priv->irq_received); + + iir = I915_READ(IIR); + do { + bool irq_received = (iir & ~flip_mask) != 0; + bool blc_event = false; + + /* Can't rely on pipestat interrupt bit in iir as it might + * have been cleared after the pipestat interrupt was received. + * It doesn't set the bit in iir again, but it still produces + * interrupts (for non-MSI). + */ + spin_lock_irqsave(&dev_priv->irq_lock, irqflags); + if (iir & I915_RENDER_COMMAND_PARSER_ERROR_INTERRUPT) + i915_handle_error(dev, false); + + for_each_pipe(pipe) { + int reg = PIPESTAT(pipe); + pipe_stats[pipe] = I915_READ(reg); + + /* Clear the PIPE*STAT regs before the IIR */ + if (pipe_stats[pipe] & 0x8000ffff) { + if (pipe_stats[pipe] & PIPE_FIFO_UNDERRUN_STATUS) + DRM_DEBUG_DRIVER("pipe %c underrun\n", + pipe_name(pipe)); + I915_WRITE(reg, pipe_stats[pipe]); + irq_received = true; + } + } + spin_unlock_irqrestore(&dev_priv->irq_lock, irqflags); + + if (!irq_received) + break; + + /* Consume port. Then clear IIR or we'll miss events */ + if ((I915_HAS_HOTPLUG(dev)) && + (iir & I915_DISPLAY_PORT_INTERRUPT)) { + u32 hotplug_status = I915_READ(PORT_HOTPLUG_STAT); + + DRM_DEBUG_DRIVER("hotplug event received, stat 0x%08x\n", + hotplug_status); + if (hotplug_status & dev_priv->hotplug_supported_mask) + queue_work(dev_priv->wq, + &dev_priv->hotplug_work); + + I915_WRITE(PORT_HOTPLUG_STAT, hotplug_status); + POSTING_READ(PORT_HOTPLUG_STAT); + } + + I915_WRITE(IIR, iir & ~flip_mask); + new_iir = I915_READ(IIR); /* Flush posted writes */ + + if (iir & I915_USER_INTERRUPT) + notify_ring(dev, &dev_priv->ring[RCS]); + + for_each_pipe(pipe) { + int plane = pipe; + if (IS_MOBILE(dev)) + plane = !plane; + if (pipe_stats[pipe] & PIPE_VBLANK_INTERRUPT_STATUS && + drm_handle_vblank(dev, pipe)) { + if (iir & flip[plane]) { + intel_prepare_page_flip(dev, plane); + intel_finish_page_flip(dev, pipe); + flip_mask &= ~flip[plane]; + } + } + + if (pipe_stats[pipe] & PIPE_LEGACY_BLC_EVENT_STATUS) + blc_event = true; + } + + if (blc_event || (iir & I915_ASLE_INTERRUPT)) + intel_opregion_asle_intr(dev); + + /* With MSI, interrupts are only generated when iir + * transitions from zero to nonzero. If another bit got + * set while we were handling the existing iir bits, then + * we would never get another interrupt. + * + * This is fine on non-MSI as well, as if we hit this path + * we avoid exiting the interrupt handler only to generate + * another one. + * + * Note that for MSI this could cause a stray interrupt report + * if an interrupt landed in the time between writing IIR and + * the posting read. This should be rare enough to never + * trigger the 99% of 100,000 interrupts test for disabling + * stray interrupts. + */ + ret = IRQ_HANDLED; + iir = new_iir; + } while (iir & ~flip_mask); + + i915_update_dri1_breadcrumb(dev); + + return ret; +} + +static void i915_irq_uninstall(struct drm_device * dev) +{ + drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private; + int pipe; + + if (I915_HAS_HOTPLUG(dev)) { + I915_WRITE(PORT_HOTPLUG_EN, 0); + I915_WRITE(PORT_HOTPLUG_STAT, I915_READ(PORT_HOTPLUG_STAT)); + } + + I915_WRITE16(HWSTAM, 0xffff); + for_each_pipe(pipe) { + /* Clear enable bits; then clear status bits */ + I915_WRITE(PIPESTAT(pipe), 0); + I915_WRITE(PIPESTAT(pipe), I915_READ(PIPESTAT(pipe))); + } + I915_WRITE(IMR, 0xffffffff); + I915_WRITE(IER, 0x0); + + I915_WRITE(IIR, I915_READ(IIR)); +} + +static void i965_irq_preinstall(struct drm_device * dev) +{ + drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private; + int pipe; + + atomic_set(&dev_priv->irq_received, 0); if (I915_HAS_HOTPLUG(dev)) { I915_WRITE(PORT_HOTPLUG_EN, 0); @@ -2291,20 +2333,25 @@ static void i915_driver_irq_preinstall(struct drm_device * dev) POSTING_READ(IER); } -/* - * Must be called after intel_modeset_init or hotplug interrupts won't be - * enabled correctly. - */ -static int i915_driver_irq_postinstall(struct drm_device *dev) +static int i965_irq_postinstall(struct drm_device *dev) { drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private; - u32 enable_mask = I915_INTERRUPT_ENABLE_FIX | I915_INTERRUPT_ENABLE_VAR; + u32 enable_mask; u32 error_mask; - dev_priv->vblank_pipe = DRM_I915_VBLANK_PIPE_A | DRM_I915_VBLANK_PIPE_B; - /* Unmask the interrupts that we always want on. */ - dev_priv->irq_mask = ~I915_INTERRUPT_ENABLE_FIX; + dev_priv->irq_mask = ~(I915_ASLE_INTERRUPT | + I915_DISPLAY_PIPE_A_EVENT_INTERRUPT | + I915_DISPLAY_PIPE_B_EVENT_INTERRUPT | + I915_DISPLAY_PLANE_A_FLIP_PENDING_INTERRUPT | + I915_DISPLAY_PLANE_B_FLIP_PENDING_INTERRUPT | + I915_RENDER_COMMAND_PARSER_ERROR_INTERRUPT); + + enable_mask = ~dev_priv->irq_mask; + enable_mask |= I915_USER_INTERRUPT; + + if (IS_G4X(dev)) + enable_mask |= I915_BSD_USER_INTERRUPT; dev_priv->pipestat[0] = 0; dev_priv->pipestat[1] = 0; @@ -2371,55 +2418,124 @@ static int i915_driver_irq_postinstall(struct drm_device *dev) return 0; } -static void valleyview_irq_uninstall(struct drm_device *dev) +static irqreturn_t i965_irq_handler(DRM_IRQ_ARGS) { + struct drm_device *dev = (struct drm_device *) arg; drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private; - int pipe; + u32 iir, new_iir; + u32 pipe_stats[I915_MAX_PIPES]; + unsigned long irqflags; + int irq_received; + int ret = IRQ_NONE, pipe; - if (!dev_priv) - return; + atomic_inc(&dev_priv->irq_received); - dev_priv->vblank_pipe = 0; + iir = I915_READ(IIR); - for_each_pipe(pipe) - I915_WRITE(PIPESTAT(pipe), 0xffff); + for (;;) { + bool blc_event = false; - I915_WRITE(HWSTAM, 0xffffffff); - I915_WRITE(PORT_HOTPLUG_EN, 0); - I915_WRITE(PORT_HOTPLUG_STAT, I915_READ(PORT_HOTPLUG_STAT)); - for_each_pipe(pipe) - I915_WRITE(PIPESTAT(pipe), 0xffff); - I915_WRITE(VLV_IIR, 0xffffffff); - I915_WRITE(VLV_IMR, 0xffffffff); - I915_WRITE(VLV_IER, 0x0); - POSTING_READ(VLV_IER); -} + irq_received = iir != 0; -static void ironlake_irq_uninstall(struct drm_device *dev) -{ - drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private; + /* Can't rely on pipestat interrupt bit in iir as it might + * have been cleared after the pipestat interrupt was received. + * It doesn't set the bit in iir again, but it still produces + * interrupts (for non-MSI). + */ + spin_lock_irqsave(&dev_priv->irq_lock, irqflags); + if (iir & I915_RENDER_COMMAND_PARSER_ERROR_INTERRUPT) + i915_handle_error(dev, false); - if (!dev_priv) - return; + for_each_pipe(pipe) { + int reg = PIPESTAT(pipe); + pipe_stats[pipe] = I915_READ(reg); + + /* + * Clear the PIPE*STAT regs before the IIR + */ + if (pipe_stats[pipe] & 0x8000ffff) { + if (pipe_stats[pipe] & PIPE_FIFO_UNDERRUN_STATUS) + DRM_DEBUG_DRIVER("pipe %c underrun\n", + pipe_name(pipe)); + I915_WRITE(reg, pipe_stats[pipe]); + irq_received = 1; + } + } + spin_unlock_irqrestore(&dev_priv->irq_lock, irqflags); - dev_priv->vblank_pipe = 0; + if (!irq_received) + break; - I915_WRITE(HWSTAM, 0xffffffff); + ret = IRQ_HANDLED; - I915_WRITE(DEIMR, 0xffffffff); - I915_WRITE(DEIER, 0x0); - I915_WRITE(DEIIR, I915_READ(DEIIR)); + /* Consume port. Then clear IIR or we'll miss events */ + if ((I915_HAS_HOTPLUG(dev)) && + (iir & I915_DISPLAY_PORT_INTERRUPT)) { + u32 hotplug_status = I915_READ(PORT_HOTPLUG_STAT); - I915_WRITE(GTIMR, 0xffffffff); - I915_WRITE(GTIER, 0x0); - I915_WRITE(GTIIR, I915_READ(GTIIR)); + DRM_DEBUG_DRIVER("hotplug event received, stat 0x%08x\n", + hotplug_status); + if (hotplug_status & dev_priv->hotplug_supported_mask) + queue_work(dev_priv->wq, + &dev_priv->hotplug_work); - I915_WRITE(SDEIMR, 0xffffffff); - I915_WRITE(SDEIER, 0x0); - I915_WRITE(SDEIIR, I915_READ(SDEIIR)); + I915_WRITE(PORT_HOTPLUG_STAT, hotplug_status); + I915_READ(PORT_HOTPLUG_STAT); + } + + I915_WRITE(IIR, iir); + new_iir = I915_READ(IIR); /* Flush posted writes */ + + if (iir & I915_USER_INTERRUPT) + notify_ring(dev, &dev_priv->ring[RCS]); + if (iir & I915_BSD_USER_INTERRUPT) + notify_ring(dev, &dev_priv->ring[VCS]); + + if (iir & I915_DISPLAY_PLANE_A_FLIP_PENDING_INTERRUPT) + intel_prepare_page_flip(dev, 0); + + if (iir & I915_DISPLAY_PLANE_B_FLIP_PENDING_INTERRUPT) + intel_prepare_page_flip(dev, 1); + + for_each_pipe(pipe) { + if (pipe_stats[pipe] & PIPE_START_VBLANK_INTERRUPT_STATUS && + drm_handle_vblank(dev, pipe)) { + i915_pageflip_stall_check(dev, pipe); + intel_finish_page_flip(dev, pipe); + } + + if (pipe_stats[pipe] & PIPE_LEGACY_BLC_EVENT_STATUS) + blc_event = true; + } + + + if (blc_event || (iir & I915_ASLE_INTERRUPT)) + intel_opregion_asle_intr(dev); + + /* With MSI, interrupts are only generated when iir + * transitions from zero to nonzero. If another bit got + * set while we were handling the existing iir bits, then + * we would never get another interrupt. + * + * This is fine on non-MSI as well, as if we hit this path + * we avoid exiting the interrupt handler only to generate + * another one. + * + * Note that for MSI this could cause a stray interrupt report + * if an interrupt landed in the time between writing IIR and + * the posting read. This should be rare enough to never + * trigger the 99% of 100,000 interrupts test for disabling + * stray interrupts. + */ + iir = new_iir; + } + + i915_update_dri1_breadcrumb(dev); + + return ret; } -static void i915_driver_irq_uninstall(struct drm_device * dev) +static void i965_irq_uninstall(struct drm_device * dev) { drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private; int pipe; @@ -2427,8 +2543,6 @@ static void i915_driver_irq_uninstall(struct drm_device * dev) if (!dev_priv) return; - dev_priv->vblank_pipe = 0; - if (I915_HAS_HOTPLUG(dev)) { I915_WRITE(PORT_HOTPLUG_EN, 0); I915_WRITE(PORT_HOTPLUG_STAT, I915_READ(PORT_HOTPLUG_STAT)); @@ -2448,10 +2562,15 @@ static void i915_driver_irq_uninstall(struct drm_device * dev) void intel_irq_init(struct drm_device *dev) { + struct drm_i915_private *dev_priv = dev->dev_private; + + INIT_WORK(&dev_priv->hotplug_work, i915_hotplug_work_func); + INIT_WORK(&dev_priv->error_work, i915_error_work_func); + INIT_WORK(&dev_priv->rps_work, gen6_pm_rps_work); + dev->driver->get_vblank_counter = i915_get_vblank_counter; dev->max_vblank_count = 0xffffff; /* only 24 bits of frame count */ - if (IS_G4X(dev) || IS_GEN5(dev) || IS_GEN6(dev) || IS_IVYBRIDGE(dev) || - IS_VALLEYVIEW(dev)) { + if (IS_G4X(dev) || INTEL_INFO(dev)->gen >= 5) { dev->max_vblank_count = 0xffffffff; /* full 32 bit counter */ dev->driver->get_vblank_counter = gm45_get_vblank_counter; } @@ -2477,6 +2596,14 @@ void intel_irq_init(struct drm_device *dev) dev->driver->irq_uninstall = ironlake_irq_uninstall; dev->driver->enable_vblank = ivybridge_enable_vblank; dev->driver->disable_vblank = ivybridge_disable_vblank; + } else if (IS_HASWELL(dev)) { + /* Share interrupts handling with IVB */ + dev->driver->irq_handler = ivybridge_irq_handler; + dev->driver->irq_preinstall = ironlake_irq_preinstall; + dev->driver->irq_postinstall = ivybridge_irq_postinstall; + dev->driver->irq_uninstall = ironlake_irq_uninstall; + dev->driver->enable_vblank = ivybridge_enable_vblank; + dev->driver->disable_vblank = ivybridge_disable_vblank; } else if (HAS_PCH_SPLIT(dev)) { dev->driver->irq_handler = ironlake_irq_handler; dev->driver->irq_preinstall = ironlake_irq_preinstall; @@ -2485,10 +2612,25 @@ void intel_irq_init(struct drm_device *dev) dev->driver->enable_vblank = ironlake_enable_vblank; dev->driver->disable_vblank = ironlake_disable_vblank; } else { - dev->driver->irq_preinstall = i915_driver_irq_preinstall; - dev->driver->irq_postinstall = i915_driver_irq_postinstall; - dev->driver->irq_uninstall = i915_driver_irq_uninstall; - dev->driver->irq_handler = i915_driver_irq_handler; + if (INTEL_INFO(dev)->gen == 2) { + dev->driver->irq_preinstall = i8xx_irq_preinstall; + dev->driver->irq_postinstall = i8xx_irq_postinstall; + dev->driver->irq_handler = i8xx_irq_handler; + dev->driver->irq_uninstall = i8xx_irq_uninstall; + } else if (INTEL_INFO(dev)->gen == 3) { + /* IIR "flip pending" means done if this bit is set */ + I915_WRITE(ECOSKPD, _MASKED_BIT_DISABLE(ECO_FLIP_DONE)); + + dev->driver->irq_preinstall = i915_irq_preinstall; + dev->driver->irq_postinstall = i915_irq_postinstall; + dev->driver->irq_uninstall = i915_irq_uninstall; + dev->driver->irq_handler = i915_irq_handler; + } else { + dev->driver->irq_preinstall = i965_irq_preinstall; + dev->driver->irq_postinstall = i965_irq_postinstall; + dev->driver->irq_uninstall = i965_irq_uninstall; + dev->driver->irq_handler = i965_irq_handler; + } dev->driver->enable_vblank = i915_enable_vblank; dev->driver->disable_vblank = i915_disable_vblank; } diff --git a/drivers/gpu/drm/i915/i915_reg.h b/drivers/gpu/drm/i915/i915_reg.h index 5ac9837..2d49b95 100644 --- a/drivers/gpu/drm/i915/i915_reg.h +++ b/drivers/gpu/drm/i915/i915_reg.h @@ -29,6 +29,9 @@ #define _PORT(port, a, b) ((a) + (port)*((b)-(a))) +#define _MASKED_BIT_ENABLE(a) (((a) << 16) | (a)) +#define _MASKED_BIT_DISABLE(a) ((a) << 16) + /* * The Bridge device's PCI config space has information about the * fb aperture size and the amount of pre-reserved memory. @@ -79,6 +82,7 @@ #define GRDOM_FULL (0<<2) #define GRDOM_RENDER (1<<2) #define GRDOM_MEDIA (3<<2) +#define GRDOM_RESET_ENABLE (1<<0) #define GEN6_MBCUNIT_SNPCR 0x900c /* for LLC config */ #define GEN6_MBC_SNPCR_SHIFT 21 @@ -425,8 +429,6 @@ #define ARB_MODE 0x04030 #define ARB_MODE_SWIZZLE_SNB (1<<4) #define ARB_MODE_SWIZZLE_IVB (1<<5) -#define ARB_MODE_ENABLE(x) GFX_MODE_ENABLE(x) -#define ARB_MODE_DISABLE(x) GFX_MODE_DISABLE(x) #define RENDER_HWS_PGA_GEN7 (0x04080) #define RING_FAULT_REG(ring) (0x4094 + 0x100*(ring)->id) #define DONE_REG 0x40b0 @@ -514,9 +516,6 @@ #define GFX_PSMI_GRANULARITY (1<<10) #define GFX_PPGTT_ENABLE (1<<9) -#define GFX_MODE_ENABLE(bit) (((bit) << 16) | (bit)) -#define GFX_MODE_DISABLE(bit) (((bit) << 16) | (0)) - #define SCPD0 0x0209c /* 915+ only */ #define IER 0x020a0 #define IIR 0x020a4 @@ -572,7 +571,6 @@ #define LM_BURST_LENGTH 0x00000700 #define LM_FIFO_WATERMARK 0x0000001F #define MI_ARB_STATE 0x020e4 /* 915+ only */ -#define MI_ARB_MASK_SHIFT 16 /* shift for enable bits */ /* Make render/texture TLB fetches lower priorty than associated data * fetches. This is not turned on by default @@ -637,7 +635,6 @@ #define MI_ARB_DISPLAY_PRIORITY_B_A (1 << 0) /* display B > display A */ #define CACHE_MODE_0 0x02120 /* 915+ only */ -#define CM0_MASK_SHIFT 16 #define CM0_IZ_OPT_DISABLE (1<<6) #define CM0_ZR_OPT_DISABLE (1<<5) #define CM0_STC_EVICT_DISABLE_LRA_SNB (1<<5) @@ -1700,9 +1697,12 @@ /* Video Data Island Packet control */ #define VIDEO_DIP_DATA 0x61178 #define VIDEO_DIP_CTL 0x61170 +/* Pre HSW: */ #define VIDEO_DIP_ENABLE (1 << 31) #define VIDEO_DIP_PORT_B (1 << 29) #define VIDEO_DIP_PORT_C (2 << 29) +#define VIDEO_DIP_PORT_D (3 << 29) +#define VIDEO_DIP_PORT_MASK (3 << 29) #define VIDEO_DIP_ENABLE_AVI (1 << 21) #define VIDEO_DIP_ENABLE_VENDOR (2 << 21) #define VIDEO_DIP_ENABLE_SPD (8 << 21) @@ -1713,6 +1713,10 @@ #define VIDEO_DIP_FREQ_ONCE (0 << 16) #define VIDEO_DIP_FREQ_VSYNC (1 << 16) #define VIDEO_DIP_FREQ_2VSYNC (2 << 16) +#define VIDEO_DIP_FREQ_MASK (3 << 16) +/* HSW and later: */ +#define VIDEO_DIP_ENABLE_AVI_HSW (1 << 12) +#define VIDEO_DIP_ENABLE_SPD_HSW (1 << 0) /* Panel power sequencing */ #define PP_STATUS 0x61200 @@ -2479,7 +2483,8 @@ /* Pipe A */ #define _PIPEADSL 0x70000 -#define DSL_LINEMASK 0x00000fff +#define DSL_LINEMASK_GEN2 0x00000fff +#define DSL_LINEMASK_GEN3 0x00001fff #define _PIPEACONF 0x70008 #define PIPECONF_ENABLE (1<<31) #define PIPECONF_DISABLE 0 @@ -3224,11 +3229,14 @@ #define DE_PCH_EVENT_IVB (1<<28) #define DE_DP_A_HOTPLUG_IVB (1<<27) #define DE_AUX_CHANNEL_A_IVB (1<<26) +#define DE_SPRITEC_FLIP_DONE_IVB (1<<14) +#define DE_PLANEC_FLIP_DONE_IVB (1<<13) +#define DE_PIPEC_VBLANK_IVB (1<<10) #define DE_SPRITEB_FLIP_DONE_IVB (1<<9) -#define DE_SPRITEA_FLIP_DONE_IVB (1<<4) #define DE_PLANEB_FLIP_DONE_IVB (1<<8) -#define DE_PLANEA_FLIP_DONE_IVB (1<<3) #define DE_PIPEB_VBLANK_IVB (1<<5) +#define DE_SPRITEA_FLIP_DONE_IVB (1<<4) +#define DE_PLANEA_FLIP_DONE_IVB (1<<3) #define DE_PIPEA_VBLANK_IVB (1<<0) #define VLV_MASTER_IER 0x4400c /* Gunit master IER */ @@ -3402,15 +3410,15 @@ #define _PCH_DPLL_A 0xc6014 #define _PCH_DPLL_B 0xc6018 -#define PCH_DPLL(pipe) (pipe == 0 ? _PCH_DPLL_A : _PCH_DPLL_B) +#define _PCH_DPLL(pll) (pll == 0 ? _PCH_DPLL_A : _PCH_DPLL_B) #define _PCH_FPA0 0xc6040 #define FP_CB_TUNE (0x3<<22) #define _PCH_FPA1 0xc6044 #define _PCH_FPB0 0xc6048 #define _PCH_FPB1 0xc604c -#define PCH_FP0(pipe) (pipe == 0 ? _PCH_FPA0 : _PCH_FPB0) -#define PCH_FP1(pipe) (pipe == 0 ? _PCH_FPA1 : _PCH_FPB1) +#define _PCH_FP0(pll) (pll == 0 ? _PCH_FPA0 : _PCH_FPB0) +#define _PCH_FP1(pll) (pll == 0 ? _PCH_FPA1 : _PCH_FPB1) #define PCH_DPLL_TEST 0xc606c @@ -3520,6 +3528,42 @@ #define VLV_TVIDEO_DIP_GCP(pipe) \ _PIPE(pipe, VLV_VIDEO_DIP_GDCP_PAYLOAD_A, VLV_VIDEO_DIP_GDCP_PAYLOAD_B) +/* Haswell DIP controls */ +#define HSW_VIDEO_DIP_CTL_A 0x60200 +#define HSW_VIDEO_DIP_AVI_DATA_A 0x60220 +#define HSW_VIDEO_DIP_VS_DATA_A 0x60260 +#define HSW_VIDEO_DIP_SPD_DATA_A 0x602A0 +#define HSW_VIDEO_DIP_GMP_DATA_A 0x602E0 +#define HSW_VIDEO_DIP_VSC_DATA_A 0x60320 +#define HSW_VIDEO_DIP_AVI_ECC_A 0x60240 +#define HSW_VIDEO_DIP_VS_ECC_A 0x60280 +#define HSW_VIDEO_DIP_SPD_ECC_A 0x602C0 +#define HSW_VIDEO_DIP_GMP_ECC_A 0x60300 +#define HSW_VIDEO_DIP_VSC_ECC_A 0x60344 +#define HSW_VIDEO_DIP_GCP_A 0x60210 + +#define HSW_VIDEO_DIP_CTL_B 0x61200 +#define HSW_VIDEO_DIP_AVI_DATA_B 0x61220 +#define HSW_VIDEO_DIP_VS_DATA_B 0x61260 +#define HSW_VIDEO_DIP_SPD_DATA_B 0x612A0 +#define HSW_VIDEO_DIP_GMP_DATA_B 0x612E0 +#define HSW_VIDEO_DIP_VSC_DATA_B 0x61320 +#define HSW_VIDEO_DIP_BVI_ECC_B 0x61240 +#define HSW_VIDEO_DIP_VS_ECC_B 0x61280 +#define HSW_VIDEO_DIP_SPD_ECC_B 0x612C0 +#define HSW_VIDEO_DIP_GMP_ECC_B 0x61300 +#define HSW_VIDEO_DIP_VSC_ECC_B 0x61344 +#define HSW_VIDEO_DIP_GCP_B 0x61210 + +#define HSW_TVIDEO_DIP_CTL(pipe) \ + _PIPE(pipe, HSW_VIDEO_DIP_CTL_A, HSW_VIDEO_DIP_CTL_B) +#define HSW_TVIDEO_DIP_AVI_DATA(pipe) \ + _PIPE(pipe, HSW_VIDEO_DIP_AVI_DATA_A, HSW_VIDEO_DIP_AVI_DATA_B) +#define HSW_TVIDEO_DIP_SPD_DATA(pipe) \ + _PIPE(pipe, HSW_VIDEO_DIP_SPD_DATA_A, HSW_VIDEO_DIP_SPD_DATA_B) +#define HSW_TVIDEO_DIP_GCP(pipe) \ + _PIPE(pipe, HSW_VIDEO_DIP_GCP_A, HSW_VIDEO_DIP_GCP_B) + #define _TRANS_HTOTAL_B 0xe1000 #define _TRANS_HBLANK_B 0xe1004 #define _TRANS_HSYNC_B 0xe1008 diff --git a/drivers/gpu/drm/i915/i915_suspend.c b/drivers/gpu/drm/i915/i915_suspend.c index 0c3e3bf..0ede02a 100644 --- a/drivers/gpu/drm/i915/i915_suspend.c +++ b/drivers/gpu/drm/i915/i915_suspend.c @@ -40,7 +40,7 @@ static bool i915_pipe_enabled(struct drm_device *dev, enum pipe pipe) return false; if (HAS_PCH_SPLIT(dev)) - dpll_reg = PCH_DPLL(pipe); + dpll_reg = _PCH_DPLL(pipe); else dpll_reg = (pipe == PIPE_A) ? _DPLL_A : _DPLL_B; @@ -876,12 +876,6 @@ int i915_restore_state(struct drm_device *dev) I915_WRITE(IER, dev_priv->saveIER); I915_WRITE(IMR, dev_priv->saveIMR); } - mutex_unlock(&dev->struct_mutex); - - if (drm_core_check_feature(dev, DRIVER_MODESET)) - intel_modeset_init_hw(dev); - - mutex_lock(&dev->struct_mutex); /* Cache mode state */ I915_WRITE(CACHE_MODE_0, dev_priv->saveCACHE_MODE_0 | 0xffff0000); diff --git a/drivers/gpu/drm/i915/intel_crt.c b/drivers/gpu/drm/i915/intel_crt.c index 417ca99..75a70c4 100644 --- a/drivers/gpu/drm/i915/intel_crt.c +++ b/drivers/gpu/drm/i915/intel_crt.c @@ -615,7 +615,11 @@ void intel_crt_init(struct drm_device *dev) crt->base.clone_mask = (1 << INTEL_SDVO_NON_TV_CLONE_BIT | 1 << INTEL_ANALOG_CLONE_BIT | 1 << INTEL_SDVO_LVDS_CLONE_BIT); - crt->base.crtc_mask = (1 << 0) | (1 << 1); + if (IS_HASWELL(dev)) + crt->base.crtc_mask = (1 << 0); + else + crt->base.crtc_mask = (1 << 0) | (1 << 1); + if (IS_GEN2(dev)) connector->interlace_allowed = 0; else diff --git a/drivers/gpu/drm/i915/intel_ddi.c b/drivers/gpu/drm/i915/intel_ddi.c new file mode 100644 index 0000000..46d1e88 --- /dev/null +++ b/drivers/gpu/drm/i915/intel_ddi.c @@ -0,0 +1,755 @@ +/* + * Copyright © 2012 Intel Corporation + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + * + * Authors: + * Eugeni Dodonov <eugeni.dodonov@intel.com> + * + */ + +#include "i915_drv.h" +#include "intel_drv.h" + +/* HDMI/DVI modes ignore everything but the last 2 items. So we share + * them for both DP and FDI transports, allowing those ports to + * automatically adapt to HDMI connections as well + */ +static const u32 hsw_ddi_translations_dp[] = { + 0x00FFFFFF, 0x0006000E, /* DP parameters */ + 0x00D75FFF, 0x0005000A, + 0x00C30FFF, 0x00040006, + 0x80AAAFFF, 0x000B0000, + 0x00FFFFFF, 0x0005000A, + 0x00D75FFF, 0x000C0004, + 0x80C30FFF, 0x000B0000, + 0x00FFFFFF, 0x00040006, + 0x80D75FFF, 0x000B0000, + 0x00FFFFFF, 0x00040006 /* HDMI parameters */ +}; + +static const u32 hsw_ddi_translations_fdi[] = { + 0x00FFFFFF, 0x0007000E, /* FDI parameters */ + 0x00D75FFF, 0x000F000A, + 0x00C30FFF, 0x00060006, + 0x00AAAFFF, 0x001E0000, + 0x00FFFFFF, 0x000F000A, + 0x00D75FFF, 0x00160004, + 0x00C30FFF, 0x001E0000, + 0x00FFFFFF, 0x00060006, + 0x00D75FFF, 0x001E0000, + 0x00FFFFFF, 0x00040006 /* HDMI parameters */ +}; + +/* On Haswell, DDI port buffers must be programmed with correct values + * in advance. The buffer values are different for FDI and DP modes, + * but the HDMI/DVI fields are shared among those. So we program the DDI + * in either FDI or DP modes only, as HDMI connections will work with both + * of those + */ +void intel_prepare_ddi_buffers(struct drm_device *dev, enum port port, bool use_fdi_mode) +{ + struct drm_i915_private *dev_priv = dev->dev_private; + u32 reg; + int i; + const u32 *ddi_translations = ((use_fdi_mode) ? + hsw_ddi_translations_fdi : + hsw_ddi_translations_dp); + + DRM_DEBUG_DRIVER("Initializing DDI buffers for port %c in %s mode\n", + port_name(port), + use_fdi_mode ? "FDI" : "DP"); + + WARN((use_fdi_mode && (port != PORT_E)), + "Programming port %c in FDI mode, this probably will not work.\n", + port_name(port)); + + for (i=0, reg=DDI_BUF_TRANS(port); i < ARRAY_SIZE(hsw_ddi_translations_fdi); i++) { + I915_WRITE(reg, ddi_translations[i]); + reg += 4; + } +} + +/* Program DDI buffers translations for DP. By default, program ports A-D in DP + * mode and port E for FDI. + */ +void intel_prepare_ddi(struct drm_device *dev) +{ + int port; + + if (IS_HASWELL(dev)) { + for (port = PORT_A; port < PORT_E; port++) + intel_prepare_ddi_buffers(dev, port, false); + + /* DDI E is the suggested one to work in FDI mode, so program is as such by + * default. It will have to be re-programmed in case a digital DP output + * will be detected on it + */ + intel_prepare_ddi_buffers(dev, PORT_E, true); + } +} + +static const long hsw_ddi_buf_ctl_values[] = { + DDI_BUF_EMP_400MV_0DB_HSW, + DDI_BUF_EMP_400MV_3_5DB_HSW, + DDI_BUF_EMP_400MV_6DB_HSW, + DDI_BUF_EMP_400MV_9_5DB_HSW, + DDI_BUF_EMP_600MV_0DB_HSW, + DDI_BUF_EMP_600MV_3_5DB_HSW, + DDI_BUF_EMP_600MV_6DB_HSW, + DDI_BUF_EMP_800MV_0DB_HSW, + DDI_BUF_EMP_800MV_3_5DB_HSW +}; + + +/* Starting with Haswell, different DDI ports can work in FDI mode for + * connection to the PCH-located connectors. For this, it is necessary to train + * both the DDI port and PCH receiver for the desired DDI buffer settings. + * + * The recommended port to work in FDI mode is DDI E, which we use here. Also, + * please note that when FDI mode is active on DDI E, it shares 2 lines with + * DDI A (which is used for eDP) + */ + +void hsw_fdi_link_train(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; + u32 reg, temp, i; + + /* Configure CPU PLL, wait for warmup */ + I915_WRITE(SPLL_CTL, + SPLL_PLL_ENABLE | + SPLL_PLL_FREQ_1350MHz | + SPLL_PLL_SCC); + + /* Use SPLL to drive the output when in FDI mode */ + I915_WRITE(PORT_CLK_SEL(PORT_E), + PORT_CLK_SEL_SPLL); + I915_WRITE(PIPE_CLK_SEL(pipe), + PIPE_CLK_SEL_PORT(PORT_E)); + + udelay(20); + + /* Start the training iterating through available voltages and emphasis */ + for (i=0; i < ARRAY_SIZE(hsw_ddi_buf_ctl_values); i++) { + /* Configure DP_TP_CTL with auto-training */ + I915_WRITE(DP_TP_CTL(PORT_E), + DP_TP_CTL_FDI_AUTOTRAIN | + DP_TP_CTL_ENHANCED_FRAME_ENABLE | + DP_TP_CTL_LINK_TRAIN_PAT1 | + DP_TP_CTL_ENABLE); + + /* Configure and enable DDI_BUF_CTL for DDI E with next voltage */ + temp = I915_READ(DDI_BUF_CTL(PORT_E)); + temp = (temp & ~DDI_BUF_EMP_MASK); + I915_WRITE(DDI_BUF_CTL(PORT_E), + temp | + DDI_BUF_CTL_ENABLE | + DDI_PORT_WIDTH_X2 | + hsw_ddi_buf_ctl_values[i]); + + udelay(600); + + /* Enable CPU FDI Receiver with auto-training */ + reg = FDI_RX_CTL(pipe); + I915_WRITE(reg, + I915_READ(reg) | + FDI_LINK_TRAIN_AUTO | + FDI_RX_ENABLE | + FDI_LINK_TRAIN_PATTERN_1_CPT | + FDI_RX_ENHANCE_FRAME_ENABLE | + FDI_PORT_WIDTH_2X_LPT | + FDI_RX_PLL_ENABLE); + POSTING_READ(reg); + udelay(100); + + temp = I915_READ(DP_TP_STATUS(PORT_E)); + if (temp & DP_TP_STATUS_AUTOTRAIN_DONE) { + DRM_DEBUG_DRIVER("BUF_CTL training done on %d step\n", i); + + /* Enable normal pixel sending for FDI */ + I915_WRITE(DP_TP_CTL(PORT_E), + DP_TP_CTL_FDI_AUTOTRAIN | + DP_TP_CTL_LINK_TRAIN_NORMAL | + DP_TP_CTL_ENHANCED_FRAME_ENABLE | + DP_TP_CTL_ENABLE); + + /* Enable PIPE_DDI_FUNC_CTL for the pipe to work in FDI mode */ + temp = I915_READ(DDI_FUNC_CTL(pipe)); + temp &= ~PIPE_DDI_PORT_MASK; + temp |= PIPE_DDI_SELECT_PORT(PORT_E) | + PIPE_DDI_MODE_SELECT_FDI | + PIPE_DDI_FUNC_ENABLE | + PIPE_DDI_PORT_WIDTH_X2; + I915_WRITE(DDI_FUNC_CTL(pipe), + temp); + break; + } else { + DRM_ERROR("Error training BUF_CTL %d\n", i); + + /* Disable DP_TP_CTL and FDI_RX_CTL) and retry */ + I915_WRITE(DP_TP_CTL(PORT_E), + I915_READ(DP_TP_CTL(PORT_E)) & + ~DP_TP_CTL_ENABLE); + I915_WRITE(FDI_RX_CTL(pipe), + I915_READ(FDI_RX_CTL(pipe)) & + ~FDI_RX_PLL_ENABLE); + continue; + } + } + + DRM_DEBUG_KMS("FDI train done.\n"); +} + +/* For DDI connections, it is possible to support different outputs over the + * same DDI port, such as HDMI or DP or even VGA via FDI. So we don't know by + * the time the output is detected what exactly is on the other end of it. This + * function aims at providing support for this detection and proper output + * configuration. + */ +void intel_ddi_init(struct drm_device *dev, enum port port) +{ + /* For now, we don't do any proper output detection and assume that we + * handle HDMI only */ + + switch(port){ + case PORT_A: + /* We don't handle eDP and DP yet */ + DRM_DEBUG_DRIVER("Found digital output on DDI port A\n"); + break; + /* Assume that the ports B, C and D are working in HDMI mode for now */ + case PORT_B: + case PORT_C: + case PORT_D: + intel_hdmi_init(dev, DDI_BUF_CTL(port)); + break; + default: + DRM_DEBUG_DRIVER("No handlers defined for port %d, skipping DDI initialization\n", + port); + break; + } +} + +/* WRPLL clock dividers */ +struct wrpll_tmds_clock { + u32 clock; + u16 p; /* Post divider */ + u16 n2; /* Feedback divider */ + u16 r2; /* Reference divider */ +}; + +/* Table of matching values for WRPLL clocks programming for each frequency */ +static const struct wrpll_tmds_clock wrpll_tmds_clock_table[] = { + {19750, 38, 25, 18}, + {20000, 48, 32, 18}, + {21000, 36, 21, 15}, + {21912, 42, 29, 17}, + {22000, 36, 22, 15}, + {23000, 36, 23, 15}, + {23500, 40, 40, 23}, + {23750, 26, 16, 14}, + {23750, 26, 16, 14}, + {24000, 36, 24, 15}, + {25000, 36, 25, 15}, + {25175, 26, 40, 33}, + {25200, 30, 21, 15}, + {26000, 36, 26, 15}, + {27000, 30, 21, 14}, + {27027, 18, 100, 111}, + {27500, 30, 29, 19}, + {28000, 34, 30, 17}, + {28320, 26, 30, 22}, + {28322, 32, 42, 25}, + {28750, 24, 23, 18}, + {29000, 30, 29, 18}, + {29750, 32, 30, 17}, + {30000, 30, 25, 15}, + {30750, 30, 41, 24}, + {31000, 30, 31, 18}, + {31500, 30, 28, 16}, + {32000, 30, 32, 18}, + {32500, 28, 32, 19}, + {33000, 24, 22, 15}, + {34000, 28, 30, 17}, + {35000, 26, 32, 19}, + {35500, 24, 30, 19}, + {36000, 26, 26, 15}, + {36750, 26, 46, 26}, + {37000, 24, 23, 14}, + {37762, 22, 40, 26}, + {37800, 20, 21, 15}, + {38000, 24, 27, 16}, + {38250, 24, 34, 20}, + {39000, 24, 26, 15}, + {40000, 24, 32, 18}, + {40500, 20, 21, 14}, + {40541, 22, 147, 89}, + {40750, 18, 19, 14}, + {41000, 16, 17, 14}, + {41500, 22, 44, 26}, + {41540, 22, 44, 26}, + {42000, 18, 21, 15}, + {42500, 22, 45, 26}, + {43000, 20, 43, 27}, + {43163, 20, 24, 15}, + {44000, 18, 22, 15}, + {44900, 20, 108, 65}, + {45000, 20, 25, 15}, + {45250, 20, 52, 31}, + {46000, 18, 23, 15}, + {46750, 20, 45, 26}, + {47000, 20, 40, 23}, + {48000, 18, 24, 15}, + {49000, 18, 49, 30}, + {49500, 16, 22, 15}, + {50000, 18, 25, 15}, + {50500, 18, 32, 19}, + {51000, 18, 34, 20}, + {52000, 18, 26, 15}, + {52406, 14, 34, 25}, + {53000, 16, 22, 14}, + {54000, 16, 24, 15}, + {54054, 16, 173, 108}, + {54500, 14, 24, 17}, + {55000, 12, 22, 18}, + {56000, 14, 45, 31}, + {56250, 16, 25, 15}, + {56750, 14, 25, 17}, + {57000, 16, 27, 16}, + {58000, 16, 43, 25}, + {58250, 16, 38, 22}, + {58750, 16, 40, 23}, + {59000, 14, 26, 17}, + {59341, 14, 40, 26}, + {59400, 16, 44, 25}, + {60000, 16, 32, 18}, + {60500, 12, 39, 29}, + {61000, 14, 49, 31}, + {62000, 14, 37, 23}, + {62250, 14, 42, 26}, + {63000, 12, 21, 15}, + {63500, 14, 28, 17}, + {64000, 12, 27, 19}, + {65000, 14, 32, 19}, + {65250, 12, 29, 20}, + {65500, 12, 32, 22}, + {66000, 12, 22, 15}, + {66667, 14, 38, 22}, + {66750, 10, 21, 17}, + {67000, 14, 33, 19}, + {67750, 14, 58, 33}, + {68000, 14, 30, 17}, + {68179, 14, 46, 26}, + {68250, 14, 46, 26}, + {69000, 12, 23, 15}, + {70000, 12, 28, 18}, + {71000, 12, 30, 19}, + {72000, 12, 24, 15}, + {73000, 10, 23, 17}, + {74000, 12, 23, 14}, + {74176, 8, 100, 91}, + {74250, 10, 22, 16}, + {74481, 12, 43, 26}, + {74500, 10, 29, 21}, + {75000, 12, 25, 15}, + {75250, 10, 39, 28}, + {76000, 12, 27, 16}, + {77000, 12, 53, 31}, + {78000, 12, 26, 15}, + {78750, 12, 28, 16}, + {79000, 10, 38, 26}, + {79500, 10, 28, 19}, + {80000, 12, 32, 18}, + {81000, 10, 21, 14}, + {81081, 6, 100, 111}, + {81624, 8, 29, 24}, + {82000, 8, 17, 14}, + {83000, 10, 40, 26}, + {83950, 10, 28, 18}, + {84000, 10, 28, 18}, + {84750, 6, 16, 17}, + {85000, 6, 17, 18}, + {85250, 10, 30, 19}, + {85750, 10, 27, 17}, + {86000, 10, 43, 27}, + {87000, 10, 29, 18}, + {88000, 10, 44, 27}, + {88500, 10, 41, 25}, + {89000, 10, 28, 17}, + {89012, 6, 90, 91}, + {89100, 10, 33, 20}, + {90000, 10, 25, 15}, + {91000, 10, 32, 19}, + {92000, 10, 46, 27}, + {93000, 10, 31, 18}, + {94000, 10, 40, 23}, + {94500, 10, 28, 16}, + {95000, 10, 44, 25}, + {95654, 10, 39, 22}, + {95750, 10, 39, 22}, + {96000, 10, 32, 18}, + {97000, 8, 23, 16}, + {97750, 8, 42, 29}, + {98000, 8, 45, 31}, + {99000, 8, 22, 15}, + {99750, 8, 34, 23}, + {100000, 6, 20, 18}, + {100500, 6, 19, 17}, + {101000, 6, 37, 33}, + {101250, 8, 21, 14}, + {102000, 6, 17, 15}, + {102250, 6, 25, 22}, + {103000, 8, 29, 19}, + {104000, 8, 37, 24}, + {105000, 8, 28, 18}, + {106000, 8, 22, 14}, + {107000, 8, 46, 29}, + {107214, 8, 27, 17}, + {108000, 8, 24, 15}, + {108108, 8, 173, 108}, + {109000, 6, 23, 19}, + {109000, 6, 23, 19}, + {110000, 6, 22, 18}, + {110013, 6, 22, 18}, + {110250, 8, 49, 30}, + {110500, 8, 36, 22}, + {111000, 8, 23, 14}, + {111264, 8, 150, 91}, + {111375, 8, 33, 20}, + {112000, 8, 63, 38}, + {112500, 8, 25, 15}, + {113100, 8, 57, 34}, + {113309, 8, 42, 25}, + {114000, 8, 27, 16}, + {115000, 6, 23, 18}, + {116000, 8, 43, 25}, + {117000, 8, 26, 15}, + {117500, 8, 40, 23}, + {118000, 6, 38, 29}, + {119000, 8, 30, 17}, + {119500, 8, 46, 26}, + {119651, 8, 39, 22}, + {120000, 8, 32, 18}, + {121000, 6, 39, 29}, + {121250, 6, 31, 23}, + {121750, 6, 23, 17}, + {122000, 6, 42, 31}, + {122614, 6, 30, 22}, + {123000, 6, 41, 30}, + {123379, 6, 37, 27}, + {124000, 6, 51, 37}, + {125000, 6, 25, 18}, + {125250, 4, 13, 14}, + {125750, 4, 27, 29}, + {126000, 6, 21, 15}, + {127000, 6, 24, 17}, + {127250, 6, 41, 29}, + {128000, 6, 27, 19}, + {129000, 6, 43, 30}, + {129859, 4, 25, 26}, + {130000, 6, 26, 18}, + {130250, 6, 42, 29}, + {131000, 6, 32, 22}, + {131500, 6, 38, 26}, + {131850, 6, 41, 28}, + {132000, 6, 22, 15}, + {132750, 6, 28, 19}, + {133000, 6, 34, 23}, + {133330, 6, 37, 25}, + {134000, 6, 61, 41}, + {135000, 6, 21, 14}, + {135250, 6, 167, 111}, + {136000, 6, 62, 41}, + {137000, 6, 35, 23}, + {138000, 6, 23, 15}, + {138500, 6, 40, 26}, + {138750, 6, 37, 24}, + {139000, 6, 34, 22}, + {139050, 6, 34, 22}, + {139054, 6, 34, 22}, + {140000, 6, 28, 18}, + {141000, 6, 36, 23}, + {141500, 6, 22, 14}, + {142000, 6, 30, 19}, + {143000, 6, 27, 17}, + {143472, 4, 17, 16}, + {144000, 6, 24, 15}, + {145000, 6, 29, 18}, + {146000, 6, 47, 29}, + {146250, 6, 26, 16}, + {147000, 6, 49, 30}, + {147891, 6, 23, 14}, + {148000, 6, 23, 14}, + {148250, 6, 28, 17}, + {148352, 4, 100, 91}, + {148500, 6, 33, 20}, + {149000, 6, 48, 29}, + {150000, 6, 25, 15}, + {151000, 4, 19, 17}, + {152000, 6, 27, 16}, + {152280, 6, 44, 26}, + {153000, 6, 34, 20}, + {154000, 6, 53, 31}, + {155000, 6, 31, 18}, + {155250, 6, 50, 29}, + {155750, 6, 45, 26}, + {156000, 6, 26, 15}, + {157000, 6, 61, 35}, + {157500, 6, 28, 16}, + {158000, 6, 65, 37}, + {158250, 6, 44, 25}, + {159000, 6, 53, 30}, + {159500, 6, 39, 22}, + {160000, 6, 32, 18}, + {161000, 4, 31, 26}, + {162000, 4, 18, 15}, + {162162, 4, 131, 109}, + {162500, 4, 53, 44}, + {163000, 4, 29, 24}, + {164000, 4, 17, 14}, + {165000, 4, 22, 18}, + {166000, 4, 32, 26}, + {167000, 4, 26, 21}, + {168000, 4, 46, 37}, + {169000, 4, 104, 83}, + {169128, 4, 64, 51}, + {169500, 4, 39, 31}, + {170000, 4, 34, 27}, + {171000, 4, 19, 15}, + {172000, 4, 51, 40}, + {172750, 4, 32, 25}, + {172800, 4, 32, 25}, + {173000, 4, 41, 32}, + {174000, 4, 49, 38}, + {174787, 4, 22, 17}, + {175000, 4, 35, 27}, + {176000, 4, 30, 23}, + {177000, 4, 38, 29}, + {178000, 4, 29, 22}, + {178500, 4, 37, 28}, + {179000, 4, 53, 40}, + {179500, 4, 73, 55}, + {180000, 4, 20, 15}, + {181000, 4, 55, 41}, + {182000, 4, 31, 23}, + {183000, 4, 42, 31}, + {184000, 4, 30, 22}, + {184750, 4, 26, 19}, + {185000, 4, 37, 27}, + {186000, 4, 51, 37}, + {187000, 4, 36, 26}, + {188000, 4, 32, 23}, + {189000, 4, 21, 15}, + {190000, 4, 38, 27}, + {190960, 4, 41, 29}, + {191000, 4, 41, 29}, + {192000, 4, 27, 19}, + {192250, 4, 37, 26}, + {193000, 4, 20, 14}, + {193250, 4, 53, 37}, + {194000, 4, 23, 16}, + {194208, 4, 23, 16}, + {195000, 4, 26, 18}, + {196000, 4, 45, 31}, + {197000, 4, 35, 24}, + {197750, 4, 41, 28}, + {198000, 4, 22, 15}, + {198500, 4, 25, 17}, + {199000, 4, 28, 19}, + {200000, 4, 37, 25}, + {201000, 4, 61, 41}, + {202000, 4, 112, 75}, + {202500, 4, 21, 14}, + {203000, 4, 146, 97}, + {204000, 4, 62, 41}, + {204750, 4, 44, 29}, + {205000, 4, 38, 25}, + {206000, 4, 29, 19}, + {207000, 4, 23, 15}, + {207500, 4, 40, 26}, + {208000, 4, 37, 24}, + {208900, 4, 48, 31}, + {209000, 4, 48, 31}, + {209250, 4, 31, 20}, + {210000, 4, 28, 18}, + {211000, 4, 25, 16}, + {212000, 4, 22, 14}, + {213000, 4, 30, 19}, + {213750, 4, 38, 24}, + {214000, 4, 46, 29}, + {214750, 4, 35, 22}, + {215000, 4, 43, 27}, + {216000, 4, 24, 15}, + {217000, 4, 37, 23}, + {218000, 4, 42, 26}, + {218250, 4, 42, 26}, + {218750, 4, 34, 21}, + {219000, 4, 47, 29}, + {219000, 4, 47, 29}, + {220000, 4, 44, 27}, + {220640, 4, 49, 30}, + {220750, 4, 36, 22}, + {221000, 4, 36, 22}, + {222000, 4, 23, 14}, + {222525, 4, 28, 17}, + {222750, 4, 33, 20}, + {227000, 4, 37, 22}, + {230250, 4, 29, 17}, + {233500, 4, 38, 22}, + {235000, 4, 40, 23}, + {238000, 4, 30, 17}, + {241500, 2, 17, 19}, + {245250, 2, 20, 22}, + {247750, 2, 22, 24}, + {253250, 2, 15, 16}, + {256250, 2, 18, 19}, + {262500, 2, 31, 32}, + {267250, 2, 66, 67}, + {268500, 2, 94, 95}, + {270000, 2, 14, 14}, + {272500, 2, 77, 76}, + {273750, 2, 57, 56}, + {280750, 2, 24, 23}, + {281250, 2, 23, 22}, + {286000, 2, 17, 16}, + {291750, 2, 26, 24}, + {296703, 2, 56, 51}, + {297000, 2, 22, 20}, + {298000, 2, 21, 19}, +}; + +void intel_ddi_mode_set(struct drm_encoder *encoder, + struct drm_display_mode *mode, + struct drm_display_mode *adjusted_mode) +{ + struct drm_device *dev = encoder->dev; + 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_hdmi *intel_hdmi = enc_to_intel_hdmi(encoder); + int port = intel_hdmi->ddi_port; + int pipe = intel_crtc->pipe; + int p, n2, r2, valid=0; + u32 temp, i; + + /* On Haswell, we need to enable the clocks and prepare DDI function to + * work in HDMI mode for this pipe. + */ + DRM_DEBUG_KMS("Preparing HDMI DDI mode for Haswell on port %c, pipe %c\n", port_name(port), pipe_name(pipe)); + + for (i=0; i < ARRAY_SIZE(wrpll_tmds_clock_table); i++) { + if (crtc->mode.clock == wrpll_tmds_clock_table[i].clock) { + p = wrpll_tmds_clock_table[i].p; + n2 = wrpll_tmds_clock_table[i].n2; + r2 = wrpll_tmds_clock_table[i].r2; + + DRM_DEBUG_KMS("WR PLL clock: found settings for %dKHz refresh rate: p=%d, n2=%d, r2=%d\n", + crtc->mode.clock, + p, n2, r2); + + valid = 1; + break; + } + } + + if (!valid) { + DRM_ERROR("Unable to find WR PLL clock settings for %dKHz refresh rate\n", + crtc->mode.clock); + return; + } + + /* Enable LCPLL if disabled */ + temp = I915_READ(LCPLL_CTL); + if (temp & LCPLL_PLL_DISABLE) + I915_WRITE(LCPLL_CTL, + temp & ~LCPLL_PLL_DISABLE); + + /* Configure WR PLL 1, program the correct divider values for + * the desired frequency and wait for warmup */ + I915_WRITE(WRPLL_CTL1, + WRPLL_PLL_ENABLE | + WRPLL_PLL_SELECT_LCPLL_2700 | + WRPLL_DIVIDER_REFERENCE(r2) | + WRPLL_DIVIDER_FEEDBACK(n2) | + WRPLL_DIVIDER_POST(p)); + + udelay(20); + + /* Use WRPLL1 clock to drive the output to the port, and tell the pipe to use + * this port for connection. + */ + I915_WRITE(PORT_CLK_SEL(port), + PORT_CLK_SEL_WRPLL1); + I915_WRITE(PIPE_CLK_SEL(pipe), + PIPE_CLK_SEL_PORT(port)); + + udelay(20); + + if (intel_hdmi->has_audio) { + /* Proper support for digital audio needs a new logic and a new set + * of registers, so we leave it for future patch bombing. + */ + DRM_DEBUG_DRIVER("HDMI audio on pipe %c not yet supported on DDI\n", + pipe_name(intel_crtc->pipe)); + } + + /* Enable PIPE_DDI_FUNC_CTL for the pipe to work in HDMI mode */ + temp = I915_READ(DDI_FUNC_CTL(pipe)); + temp &= ~PIPE_DDI_PORT_MASK; + temp &= ~PIPE_DDI_BPC_12; + temp |= PIPE_DDI_SELECT_PORT(port) | + PIPE_DDI_MODE_SELECT_HDMI | + ((intel_crtc->bpp > 24) ? + PIPE_DDI_BPC_12 : + PIPE_DDI_BPC_8) | + PIPE_DDI_FUNC_ENABLE; + + I915_WRITE(DDI_FUNC_CTL(pipe), temp); + + intel_hdmi_set_avi_infoframe(encoder, adjusted_mode); + intel_hdmi_set_spd_infoframe(encoder); +} + +void intel_ddi_dpms(struct drm_encoder *encoder, int mode) +{ + struct drm_device *dev = encoder->dev; + struct drm_i915_private *dev_priv = dev->dev_private; + struct intel_hdmi *intel_hdmi = enc_to_intel_hdmi(encoder); + int port = intel_hdmi->ddi_port; + u32 temp; + + temp = I915_READ(DDI_BUF_CTL(port)); + + if (mode != DRM_MODE_DPMS_ON) { + temp &= ~DDI_BUF_CTL_ENABLE; + } else { + temp |= DDI_BUF_CTL_ENABLE; + } + + /* Enable DDI_BUF_CTL. In HDMI/DVI mode, the port width, + * and swing/emphasis values are ignored so nothing special needs + * to be done besides enabling the port. + */ + I915_WRITE(DDI_BUF_CTL(port), + temp); +} diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index 8c239f2..ee61ad1 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -384,28 +384,6 @@ out_unlock: return val; } -static void intel_dpio_write(struct drm_i915_private *dev_priv, int reg, - u32 val) -{ - unsigned long flags; - - spin_lock_irqsave(&dev_priv->dpio_lock, flags); - if (wait_for_atomic_us((I915_READ(DPIO_PKT) & DPIO_BUSY) == 0, 100)) { - DRM_ERROR("DPIO idle wait timed out\n"); - goto out_unlock; - } - - I915_WRITE(DPIO_DATA, val); - I915_WRITE(DPIO_REG, reg); - I915_WRITE(DPIO_PKT, DPIO_RID | DPIO_OP_WRITE | DPIO_PORTID | - DPIO_BYTE); - if (wait_for_atomic_us((I915_READ(DPIO_PKT) & DPIO_BUSY) == 0, 100)) - DRM_ERROR("DPIO write wait timed out\n"); - -out_unlock: - spin_unlock_irqrestore(&dev_priv->dpio_lock, flags); -} - static void vlv_init_dpio(struct drm_device *dev) { struct drm_i915_private *dev_priv = dev->dev_private; @@ -806,6 +784,17 @@ intel_find_pll_g4x_dp(const intel_limit_t *limit, struct drm_crtc *crtc, return true; } +static void ironlake_wait_for_vblank(struct drm_device *dev, int pipe) +{ + struct drm_i915_private *dev_priv = dev->dev_private; + u32 frame, frame_reg = PIPEFRAME(pipe); + + frame = I915_READ(frame_reg); + + if (wait_for(I915_READ_NOTRACE(frame_reg) != frame, 50)) + DRM_DEBUG_KMS("vblank wait timed out\n"); +} + /** * intel_wait_for_vblank - wait for vblank on a given pipe * @dev: drm device @@ -819,6 +808,11 @@ void intel_wait_for_vblank(struct drm_device *dev, int pipe) struct drm_i915_private *dev_priv = dev->dev_private; int pipestat_reg = PIPESTAT(pipe); + if (INTEL_INFO(dev)->gen >= 5) { + ironlake_wait_for_vblank(dev, pipe); + return; + } + /* Clear existing vblank status. Note this will clear any other * sticky status fields as well. * @@ -871,15 +865,20 @@ void intel_wait_for_pipe_off(struct drm_device *dev, int pipe) 100)) DRM_DEBUG_KMS("pipe_off wait timed out\n"); } else { - u32 last_line; + u32 last_line, line_mask; int reg = PIPEDSL(pipe); unsigned long timeout = jiffies + msecs_to_jiffies(100); + if (IS_GEN2(dev)) + line_mask = DSL_LINEMASK_GEN2; + else + line_mask = DSL_LINEMASK_GEN3; + /* Wait for the display line to settle */ do { - last_line = I915_READ(reg) & DSL_LINEMASK; + last_line = I915_READ(reg) & line_mask; mdelay(5); - } while (((I915_READ(reg) & DSL_LINEMASK) != last_line) && + } while (((I915_READ(reg) & line_mask) != last_line) && time_after(timeout, jiffies)); if (time_after(jiffies, timeout)) DRM_DEBUG_KMS("pipe_off wait timed out\n"); @@ -911,26 +910,33 @@ static void assert_pll(struct drm_i915_private *dev_priv, /* For ILK+ */ static void assert_pch_pll(struct drm_i915_private *dev_priv, - enum pipe pipe, bool state) + struct intel_crtc *intel_crtc, bool state) { int reg; u32 val; bool cur_state; + if (HAS_PCH_LPT(dev_priv->dev)) { + DRM_DEBUG_DRIVER("LPT detected: skipping PCH PLL test\n"); + return; + } + + if (!intel_crtc->pch_pll) { + WARN(1, "asserting PCH PLL enabled with no PLL\n"); + return; + } + if (HAS_PCH_CPT(dev_priv->dev)) { u32 pch_dpll; pch_dpll = I915_READ(PCH_DPLL_SEL); /* Make sure the selected PLL is enabled to the transcoder */ - WARN(!((pch_dpll >> (4 * pipe)) & 8), - "transcoder %d PLL not enabled\n", pipe); - - /* Convert the transcoder pipe number to a pll pipe number */ - pipe = (pch_dpll >> (4 * pipe)) & 1; + WARN(!((pch_dpll >> (4 * intel_crtc->pipe)) & 8), + "transcoder %d PLL not enabled\n", intel_crtc->pipe); } - reg = PCH_DPLL(pipe); + reg = intel_crtc->pch_pll->pll_reg; val = I915_READ(reg); cur_state = !!(val & DPLL_VCO_ENABLE); WARN(cur_state != state, @@ -947,9 +953,16 @@ static void assert_fdi_tx(struct drm_i915_private *dev_priv, u32 val; bool cur_state; - reg = FDI_TX_CTL(pipe); - val = I915_READ(reg); - cur_state = !!(val & FDI_TX_ENABLE); + if (IS_HASWELL(dev_priv->dev)) { + /* On Haswell, DDI is used instead of FDI_TX_CTL */ + reg = DDI_FUNC_CTL(pipe); + val = I915_READ(reg); + cur_state = !!(val & PIPE_DDI_FUNC_ENABLE); + } else { + reg = FDI_TX_CTL(pipe); + val = I915_READ(reg); + cur_state = !!(val & FDI_TX_ENABLE); + } WARN(cur_state != state, "FDI TX state assertion failure (expected %s, current %s)\n", state_string(state), state_string(cur_state)); @@ -964,9 +977,14 @@ static void assert_fdi_rx(struct drm_i915_private *dev_priv, u32 val; bool cur_state; - reg = FDI_RX_CTL(pipe); - val = I915_READ(reg); - cur_state = !!(val & FDI_RX_ENABLE); + if (IS_HASWELL(dev_priv->dev) && pipe > 0) { + DRM_ERROR("Attempting to enable FDI_RX on Haswell pipe > 0\n"); + return; + } else { + reg = FDI_RX_CTL(pipe); + val = I915_READ(reg); + cur_state = !!(val & FDI_RX_ENABLE); + } WARN(cur_state != state, "FDI RX state assertion failure (expected %s, current %s)\n", state_string(state), state_string(cur_state)); @@ -984,6 +1002,10 @@ static void assert_fdi_tx_pll_enabled(struct drm_i915_private *dev_priv, if (dev_priv->info->gen == 5) return; + /* On Haswell, DDI ports are responsible for the FDI PLL setup */ + if (IS_HASWELL(dev_priv->dev)) + return; + reg = FDI_TX_CTL(pipe); val = I915_READ(reg); WARN(!(val & FDI_TX_PLL_ENABLE), "FDI TX PLL assertion failure, should be active but is disabled\n"); @@ -995,6 +1017,10 @@ static void assert_fdi_rx_pll_enabled(struct drm_i915_private *dev_priv, int reg; u32 val; + if (IS_HASWELL(dev_priv->dev) && pipe > 0) { + DRM_ERROR("Attempting to enable FDI on Haswell with pipe > 0\n"); + return; + } reg = FDI_RX_CTL(pipe); val = I915_READ(reg); WARN(!(val & FDI_RX_PLL_ENABLE), "FDI RX PLL assertion failure, should be active but is disabled\n"); @@ -1100,6 +1126,11 @@ static void assert_pch_refclk_enabled(struct drm_i915_private *dev_priv) u32 val; bool enabled; + if (HAS_PCH_LPT(dev_priv->dev)) { + DRM_DEBUG_DRIVER("LPT does not has PCH refclk, skipping check\n"); + return; + } + val = I915_READ(PCH_DREF_CONTROL); enabled = !!(val & (DREF_SSC_SOURCE_MASK | DREF_NONSPREAD_SOURCE_MASK | DREF_SUPERSPREAD_SOURCE_MASK)); @@ -1298,6 +1329,69 @@ static void intel_disable_pll(struct drm_i915_private *dev_priv, enum pipe pipe) POSTING_READ(reg); } +/* SBI access */ +static void +intel_sbi_write(struct drm_i915_private *dev_priv, u16 reg, u32 value) +{ + unsigned long flags; + + spin_lock_irqsave(&dev_priv->dpio_lock, flags); + if (wait_for((I915_READ(SBI_CTL_STAT) & SBI_READY) == 0, + 100)) { + DRM_ERROR("timeout waiting for SBI to become ready\n"); + goto out_unlock; + } + + I915_WRITE(SBI_ADDR, + (reg << 16)); + I915_WRITE(SBI_DATA, + value); + I915_WRITE(SBI_CTL_STAT, + SBI_BUSY | + SBI_CTL_OP_CRWR); + + if (wait_for((I915_READ(SBI_CTL_STAT) & (SBI_READY | SBI_RESPONSE_SUCCESS)) == 0, + 100)) { + DRM_ERROR("timeout waiting for SBI to complete write transaction\n"); + goto out_unlock; + } + +out_unlock: + spin_unlock_irqrestore(&dev_priv->dpio_lock, flags); +} + +static u32 +intel_sbi_read(struct drm_i915_private *dev_priv, u16 reg) +{ + unsigned long flags; + u32 value; + + spin_lock_irqsave(&dev_priv->dpio_lock, flags); + if (wait_for((I915_READ(SBI_CTL_STAT) & SBI_READY) == 0, + 100)) { + DRM_ERROR("timeout waiting for SBI to become ready\n"); + goto out_unlock; + } + + I915_WRITE(SBI_ADDR, + (reg << 16)); + I915_WRITE(SBI_CTL_STAT, + SBI_BUSY | + SBI_CTL_OP_CRRD); + + if (wait_for((I915_READ(SBI_CTL_STAT) & (SBI_READY | SBI_RESPONSE_SUCCESS)) == 0, + 100)) { + DRM_ERROR("timeout waiting for SBI to complete read transaction\n"); + goto out_unlock; + } + + value = I915_READ(SBI_DATA); + +out_unlock: + spin_unlock_irqrestore(&dev_priv->dpio_lock, flags); + return value; +} + /** * intel_enable_pch_pll - enable PCH PLL * @dev_priv: i915 private structure @@ -1306,60 +1400,88 @@ static void intel_disable_pll(struct drm_i915_private *dev_priv, enum pipe pipe) * The PCH PLL needs to be enabled before the PCH transcoder, since it * drives the transcoder clock. */ -static void intel_enable_pch_pll(struct drm_i915_private *dev_priv, - enum pipe pipe) +static void intel_enable_pch_pll(struct intel_crtc *intel_crtc) { + struct drm_i915_private *dev_priv = intel_crtc->base.dev->dev_private; + struct intel_pch_pll *pll; int reg; u32 val; - if (pipe > 1) + /* PCH PLLs only available on ILK, SNB and IVB */ + BUG_ON(dev_priv->info->gen < 5); + pll = intel_crtc->pch_pll; + if (pll == NULL) return; - /* PCH only available on ILK+ */ - BUG_ON(dev_priv->info->gen < 5); + if (WARN_ON(pll->refcount == 0)) + return; + + DRM_DEBUG_KMS("enable PCH PLL %x (active %d, on? %d)for crtc %d\n", + pll->pll_reg, pll->active, pll->on, + intel_crtc->base.base.id); /* PCH refclock must be enabled first */ assert_pch_refclk_enabled(dev_priv); - reg = PCH_DPLL(pipe); + if (pll->active++ && pll->on) { + assert_pch_pll_enabled(dev_priv, intel_crtc); + return; + } + + DRM_DEBUG_KMS("enabling PCH PLL %x\n", pll->pll_reg); + + reg = pll->pll_reg; val = I915_READ(reg); val |= DPLL_VCO_ENABLE; I915_WRITE(reg, val); POSTING_READ(reg); udelay(200); + + pll->on = true; } -static void intel_disable_pch_pll(struct drm_i915_private *dev_priv, - enum pipe pipe) +static void intel_disable_pch_pll(struct intel_crtc *intel_crtc) { + struct drm_i915_private *dev_priv = intel_crtc->base.dev->dev_private; + struct intel_pch_pll *pll = intel_crtc->pch_pll; int reg; - u32 val, pll_mask = TRANSC_DPLL_ENABLE | TRANSC_DPLLB_SEL, - pll_sel = TRANSC_DPLL_ENABLE; - - if (pipe > 1) - return; + u32 val; /* PCH only available on ILK+ */ BUG_ON(dev_priv->info->gen < 5); + if (pll == NULL) + return; - /* Make sure transcoder isn't still depending on us */ - assert_transcoder_disabled(dev_priv, pipe); + if (WARN_ON(pll->refcount == 0)) + return; - if (pipe == 0) - pll_sel |= TRANSC_DPLLA_SEL; - else if (pipe == 1) - pll_sel |= TRANSC_DPLLB_SEL; + DRM_DEBUG_KMS("disable PCH PLL %x (active %d, on? %d) for crtc %d\n", + pll->pll_reg, pll->active, pll->on, + intel_crtc->base.base.id); + if (WARN_ON(pll->active == 0)) { + assert_pch_pll_disabled(dev_priv, intel_crtc); + return; + } - if ((I915_READ(PCH_DPLL_SEL) & pll_mask) == pll_sel) + if (--pll->active) { + assert_pch_pll_enabled(dev_priv, intel_crtc); return; + } - reg = PCH_DPLL(pipe); + DRM_DEBUG_KMS("disabling PCH PLL %x\n", pll->pll_reg); + + /* Make sure transcoder isn't still depending on us */ + assert_transcoder_disabled(dev_priv, intel_crtc->pipe); + + reg = pll->pll_reg; val = I915_READ(reg); val &= ~DPLL_VCO_ENABLE; I915_WRITE(reg, val); POSTING_READ(reg); udelay(200); + + pll->on = false; } static void intel_enable_transcoder(struct drm_i915_private *dev_priv, @@ -1373,12 +1495,16 @@ static void intel_enable_transcoder(struct drm_i915_private *dev_priv, BUG_ON(dev_priv->info->gen < 5); /* Make sure PCH DPLL is enabled */ - assert_pch_pll_enabled(dev_priv, pipe); + assert_pch_pll_enabled(dev_priv, to_intel_crtc(crtc)); /* FDI must be feeding us bits for PCH ports */ assert_fdi_tx_enabled(dev_priv, pipe); assert_fdi_rx_enabled(dev_priv, pipe); + if (IS_HASWELL(dev_priv->dev) && pipe > 0) { + DRM_ERROR("Attempting to enable transcoder on Haswell with pipe > 0\n"); + return; + } reg = TRANSCONF(pipe); val = I915_READ(reg); pipeconf_val = I915_READ(PIPECONF(pipe)); @@ -1897,16 +2023,10 @@ intel_pipe_set_base(struct drm_crtc *crtc, int x, int y, return 0; } - switch (intel_crtc->plane) { - case 0: - case 1: - break; - case 2: - if (IS_IVYBRIDGE(dev)) - break; - /* fall through otherwise */ - default: - DRM_ERROR("no plane for crtc\n"); + if(intel_crtc->plane > dev_priv->num_pipe) { + DRM_ERROR("no plane for crtc: plane %d, num_pipes %d\n", + intel_crtc->plane, + dev_priv->num_pipe); return -EINVAL; } @@ -2427,14 +2547,18 @@ static void ironlake_fdi_pll_enable(struct drm_crtc *crtc) POSTING_READ(reg); udelay(200); - /* 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); + /* On Haswell, the PLL configuration for ports and pipes is handled + * separately, as part of DDI setup */ + if (!IS_HASWELL(dev)) { + /* Enable CPU FDI TX PLL, always on for Ironlake */ + reg = FDI_TX_CTL(pipe); + temp = I915_READ(reg); + if ((temp & FDI_TX_PLL_ENABLE) == 0) { + I915_WRITE(reg, temp | FDI_TX_PLL_ENABLE); - POSTING_READ(reg); - udelay(100); + POSTING_READ(reg); + udelay(100); + } } } @@ -2507,26 +2631,6 @@ static void ironlake_fdi_disable(struct drm_crtc *crtc) udelay(100); } -/* - * 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; - struct intel_ring_buffer *ring; - u32 tmp; - - if (IS_GEN2(dev)) - /* Can't break the hang on i8xx */ - return; - - ring = LP_RING(dev_priv); - tmp = I915_READ_CTL(ring); - if (tmp & RING_WAIT) - I915_WRITE_CTL(ring, tmp); -} - static void intel_crtc_wait_for_pending_flips(struct drm_crtc *crtc) { struct drm_device *dev = crtc->dev; @@ -2553,6 +2657,22 @@ static bool intel_crtc_driving_pch(struct drm_crtc *crtc) if (encoder->base.crtc != crtc) continue; + /* On Haswell, LPT PCH handles the VGA connection via FDI, and Haswell + * CPU handles all others */ + if (IS_HASWELL(dev)) { + /* It is still unclear how this will work on PPT, so throw up a warning */ + WARN_ON(!HAS_PCH_LPT(dev)); + + if (encoder->type == DRM_MODE_ENCODER_DAC) { + DRM_DEBUG_KMS("Haswell detected DAC encoder, assuming is PCH\n"); + return true; + } else { + DRM_DEBUG_KMS("Haswell detected encoder %d, assuming is CPU\n", + encoder->type); + return false; + } + } + switch (encoder->type) { case INTEL_OUTPUT_EDP: if (!intel_encoder_is_pch_edp(&encoder->base)) @@ -2564,6 +2684,97 @@ static bool intel_crtc_driving_pch(struct drm_crtc *crtc) return true; } +/* Program iCLKIP clock to the desired frequency */ +static void lpt_program_iclkip(struct drm_crtc *crtc) +{ + struct drm_device *dev = crtc->dev; + struct drm_i915_private *dev_priv = dev->dev_private; + u32 divsel, phaseinc, auxdiv, phasedir = 0; + u32 temp; + + /* It is necessary to ungate the pixclk gate prior to programming + * the divisors, and gate it back when it is done. + */ + I915_WRITE(PIXCLK_GATE, PIXCLK_GATE_GATE); + + /* Disable SSCCTL */ + intel_sbi_write(dev_priv, SBI_SSCCTL6, + intel_sbi_read(dev_priv, SBI_SSCCTL6) | + SBI_SSCCTL_DISABLE); + + /* 20MHz is a corner case which is out of range for the 7-bit divisor */ + if (crtc->mode.clock == 20000) { + auxdiv = 1; + divsel = 0x41; + phaseinc = 0x20; + } else { + /* The iCLK virtual clock root frequency is in MHz, + * but the crtc->mode.clock in in KHz. To get the divisors, + * it is necessary to divide one by another, so we + * convert the virtual clock precision to KHz here for higher + * precision. + */ + u32 iclk_virtual_root_freq = 172800 * 1000; + u32 iclk_pi_range = 64; + u32 desired_divisor, msb_divisor_value, pi_value; + + desired_divisor = (iclk_virtual_root_freq / crtc->mode.clock); + msb_divisor_value = desired_divisor / iclk_pi_range; + pi_value = desired_divisor % iclk_pi_range; + + auxdiv = 0; + divsel = msb_divisor_value - 2; + phaseinc = pi_value; + } + + /* This should not happen with any sane values */ + WARN_ON(SBI_SSCDIVINTPHASE_DIVSEL(divsel) & + ~SBI_SSCDIVINTPHASE_DIVSEL_MASK); + WARN_ON(SBI_SSCDIVINTPHASE_DIR(phasedir) & + ~SBI_SSCDIVINTPHASE_INCVAL_MASK); + + DRM_DEBUG_KMS("iCLKIP clock: found settings for %dKHz refresh rate: auxdiv=%x, divsel=%x, phasedir=%x, phaseinc=%x\n", + crtc->mode.clock, + auxdiv, + divsel, + phasedir, + phaseinc); + + /* Program SSCDIVINTPHASE6 */ + temp = intel_sbi_read(dev_priv, SBI_SSCDIVINTPHASE6); + temp &= ~SBI_SSCDIVINTPHASE_DIVSEL_MASK; + temp |= SBI_SSCDIVINTPHASE_DIVSEL(divsel); + temp &= ~SBI_SSCDIVINTPHASE_INCVAL_MASK; + temp |= SBI_SSCDIVINTPHASE_INCVAL(phaseinc); + temp |= SBI_SSCDIVINTPHASE_DIR(phasedir); + temp |= SBI_SSCDIVINTPHASE_PROPAGATE; + + intel_sbi_write(dev_priv, + SBI_SSCDIVINTPHASE6, + temp); + + /* Program SSCAUXDIV */ + temp = intel_sbi_read(dev_priv, SBI_SSCAUXDIV6); + temp &= ~SBI_SSCAUXDIV_FINALDIV2SEL(1); + temp |= SBI_SSCAUXDIV_FINALDIV2SEL(auxdiv); + intel_sbi_write(dev_priv, + SBI_SSCAUXDIV6, + temp); + + + /* Enable modulator and associated divider */ + temp = intel_sbi_read(dev_priv, SBI_SSCCTL6); + temp &= ~SBI_SSCCTL_DISABLE; + intel_sbi_write(dev_priv, + SBI_SSCCTL6, + temp); + + /* Wait for initialization time */ + udelay(24); + + I915_WRITE(PIXCLK_GATE, PIXCLK_GATE_UNGATE); +} + /* * Enable PCH resources required for PCH ports: * - PCH PLLs @@ -2578,29 +2789,41 @@ static void ironlake_pch_enable(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; - u32 reg, temp, transc_sel; + u32 reg, temp; + + assert_transcoder_disabled(dev_priv, pipe); /* For PCH output, training FDI link */ dev_priv->display.fdi_link_train(crtc); - intel_enable_pch_pll(dev_priv, pipe); + intel_enable_pch_pll(intel_crtc); - if (HAS_PCH_CPT(dev)) { - transc_sel = intel_crtc->use_pll_a ? TRANSC_DPLLA_SEL : - TRANSC_DPLLB_SEL; + if (HAS_PCH_LPT(dev)) { + DRM_DEBUG_KMS("LPT detected: programming iCLKIP\n"); + lpt_program_iclkip(crtc); + } else if (HAS_PCH_CPT(dev)) { + u32 sel; - /* Be sure PCH DPLL SEL is set */ temp = I915_READ(PCH_DPLL_SEL); - if (pipe == 0) { - temp &= ~(TRANSA_DPLLB_SEL); - temp |= (TRANSA_DPLL_ENABLE | TRANSA_DPLLA_SEL); - } else if (pipe == 1) { - temp &= ~(TRANSB_DPLLB_SEL); - temp |= (TRANSB_DPLL_ENABLE | TRANSB_DPLLB_SEL); - } else if (pipe == 2) { - temp &= ~(TRANSC_DPLLB_SEL); - temp |= (TRANSC_DPLL_ENABLE | transc_sel); + switch (pipe) { + default: + case 0: + temp |= TRANSA_DPLL_ENABLE; + sel = TRANSA_DPLLB_SEL; + break; + case 1: + temp |= TRANSB_DPLL_ENABLE; + sel = TRANSB_DPLLB_SEL; + break; + case 2: + temp |= TRANSC_DPLL_ENABLE; + sel = TRANSC_DPLLB_SEL; + break; } + if (intel_crtc->pch_pll->pll_reg == _PCH_DPLL_B) + temp |= sel; + else + temp &= ~sel; I915_WRITE(PCH_DPLL_SEL, temp); } @@ -2615,7 +2838,8 @@ static void ironlake_pch_enable(struct drm_crtc *crtc) I915_WRITE(TRANS_VSYNC(pipe), I915_READ(VSYNC(pipe))); I915_WRITE(TRANS_VSYNCSHIFT(pipe), I915_READ(VSYNCSHIFT(pipe))); - intel_fdi_normal_train(crtc); + if (!IS_HASWELL(dev)) + intel_fdi_normal_train(crtc); /* For PCH DP, enable TRANS_DP_CTL */ if (HAS_PCH_CPT(dev) && @@ -2658,6 +2882,93 @@ static void ironlake_pch_enable(struct drm_crtc *crtc) intel_enable_transcoder(dev_priv, pipe); } +static void intel_put_pch_pll(struct intel_crtc *intel_crtc) +{ + struct intel_pch_pll *pll = intel_crtc->pch_pll; + + if (pll == NULL) + return; + + if (pll->refcount == 0) { + WARN(1, "bad PCH PLL refcount\n"); + return; + } + + --pll->refcount; + intel_crtc->pch_pll = NULL; +} + +static struct intel_pch_pll *intel_get_pch_pll(struct intel_crtc *intel_crtc, u32 dpll, u32 fp) +{ + struct drm_i915_private *dev_priv = intel_crtc->base.dev->dev_private; + struct intel_pch_pll *pll; + int i; + + pll = intel_crtc->pch_pll; + if (pll) { + DRM_DEBUG_KMS("CRTC:%d reusing existing PCH PLL %x\n", + intel_crtc->base.base.id, pll->pll_reg); + goto prepare; + } + + if (HAS_PCH_IBX(dev_priv->dev)) { + /* Ironlake PCH has a fixed PLL->PCH pipe mapping. */ + i = intel_crtc->pipe; + pll = &dev_priv->pch_plls[i]; + + DRM_DEBUG_KMS("CRTC:%d using pre-allocated PCH PLL %x\n", + intel_crtc->base.base.id, pll->pll_reg); + + goto found; + } + + for (i = 0; i < dev_priv->num_pch_pll; i++) { + pll = &dev_priv->pch_plls[i]; + + /* Only want to check enabled timings first */ + if (pll->refcount == 0) + continue; + + if (dpll == (I915_READ(pll->pll_reg) & 0x7fffffff) && + fp == I915_READ(pll->fp0_reg)) { + DRM_DEBUG_KMS("CRTC:%d sharing existing PCH PLL %x (refcount %d, ative %d)\n", + intel_crtc->base.base.id, + pll->pll_reg, pll->refcount, pll->active); + + goto found; + } + } + + /* Ok no matching timings, maybe there's a free one? */ + for (i = 0; i < dev_priv->num_pch_pll; i++) { + pll = &dev_priv->pch_plls[i]; + if (pll->refcount == 0) { + DRM_DEBUG_KMS("CRTC:%d allocated PCH PLL %x\n", + intel_crtc->base.base.id, pll->pll_reg); + goto found; + } + } + + return NULL; + +found: + intel_crtc->pch_pll = pll; + pll->refcount++; + DRM_DEBUG_DRIVER("using pll %d for pipe %d\n", i, intel_crtc->pipe); +prepare: /* separate function? */ + DRM_DEBUG_DRIVER("switching PLL %x off\n", pll->pll_reg); + + /* Wait for the clocks to stabilize before rewriting the regs */ + I915_WRITE(pll->pll_reg, dpll & ~DPLL_VCO_ENABLE); + POSTING_READ(pll->pll_reg); + udelay(150); + + I915_WRITE(pll->fp0_reg, fp); + I915_WRITE(pll->pll_reg, dpll & ~DPLL_VCO_ENABLE); + pll->on = false; + return pll; +} + void intel_cpt_verify_modeset(struct drm_device *dev, int pipe) { struct drm_i915_private *dev_priv = dev->dev_private; @@ -2802,8 +3113,7 @@ static void ironlake_crtc_disable(struct drm_crtc *crtc) } /* disable PCH DPLL */ - if (!intel_crtc->no_pll) - intel_disable_pch_pll(dev_priv, pipe); + intel_disable_pch_pll(intel_crtc); /* Switch from PCDclk to Rawclk */ reg = FDI_RX_CTL(pipe); @@ -2831,7 +3141,6 @@ static void ironlake_crtc_disable(struct drm_crtc *crtc) mutex_lock(&dev->struct_mutex); intel_update_fbc(dev); - intel_clear_scanline_wait(dev); mutex_unlock(&dev->struct_mutex); } @@ -2859,6 +3168,12 @@ static void ironlake_crtc_dpms(struct drm_crtc *crtc, int mode) } } +static void ironlake_crtc_off(struct drm_crtc *crtc) +{ + struct intel_crtc *intel_crtc = to_intel_crtc(crtc); + intel_put_pch_pll(intel_crtc); +} + static void intel_crtc_dpms_overlay(struct intel_crtc *intel_crtc, bool enable) { if (!enable && intel_crtc->overlay) { @@ -2930,7 +3245,6 @@ static void i9xx_crtc_disable(struct drm_crtc *crtc) 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) @@ -2950,6 +3264,10 @@ static void i9xx_crtc_dpms(struct drm_crtc *crtc, int mode) } } +static void i9xx_crtc_off(struct drm_crtc *crtc) +{ +} + /** * Sets the power management mode of the pipe and plane. */ @@ -2997,8 +3315,11 @@ 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; + struct drm_i915_private *dev_priv = dev->dev_private; crtc_funcs->dpms(crtc, DRM_MODE_DPMS_OFF); + dev_priv->display.off(crtc); + assert_plane_disabled(dev->dev_private, to_intel_crtc(crtc)->plane); assert_pipe_disabled(dev->dev_private, to_intel_crtc(crtc)->pipe); @@ -3048,8 +3369,7 @@ void intel_encoder_commit(struct drm_encoder *encoder) { struct drm_encoder_helper_funcs *encoder_funcs = encoder->helper_private; struct drm_device *dev = encoder->dev; - struct intel_encoder *intel_encoder = to_intel_encoder(encoder); - struct intel_crtc *intel_crtc = to_intel_crtc(intel_encoder->base.crtc); + struct intel_crtc *intel_crtc = to_intel_crtc(encoder->crtc); /* lvds has its own version of commit see intel_lvds_commit */ encoder_funcs->dpms(encoder, DRM_MODE_DPMS_ON); @@ -3822,7 +4142,6 @@ static int i9xx_crtc_mode_set(struct drm_crtc *crtc, I915_WRITE(DSPCNTR(plane), dspcntr); POSTING_READ(DSPCNTR(plane)); - intel_enable_plane(dev_priv, plane, pipe); ret = intel_pipe_set_base(crtc, x, y, old_fb); @@ -4241,29 +4560,22 @@ static int ironlake_crtc_mode_set(struct drm_crtc *crtc, DRM_DEBUG_KMS("Mode for pipe %d:\n", pipe); drm_mode_debug_printmodeline(mode); - /* PCH eDP needs FDI, but CPU eDP does not */ - if (!intel_crtc->no_pll) { - if (!is_cpu_edp) { - I915_WRITE(PCH_FP0(pipe), fp); - I915_WRITE(PCH_DPLL(pipe), dpll & ~DPLL_VCO_ENABLE); - - POSTING_READ(PCH_DPLL(pipe)); - udelay(150); - } - } else { - if (dpll == (I915_READ(PCH_DPLL(0)) & 0x7fffffff) && - fp == I915_READ(PCH_FP0(0))) { - intel_crtc->use_pll_a = true; - DRM_DEBUG_KMS("using pipe a dpll\n"); - } else if (dpll == (I915_READ(PCH_DPLL(1)) & 0x7fffffff) && - fp == I915_READ(PCH_FP0(1))) { - intel_crtc->use_pll_a = false; - DRM_DEBUG_KMS("using pipe b dpll\n"); - } else { - DRM_DEBUG_KMS("no matching PLL configuration for pipe 2\n"); + /* CPU eDP is the only output that doesn't need a PCH PLL of its own on + * pre-Haswell/LPT generation */ + if (HAS_PCH_LPT(dev)) { + DRM_DEBUG_KMS("LPT detected: no PLL for pipe %d necessary\n", + pipe); + } else if (!is_cpu_edp) { + struct intel_pch_pll *pll; + + pll = intel_get_pch_pll(intel_crtc, dpll, fp); + if (pll == NULL) { + DRM_DEBUG_DRIVER("failed to find PLL for pipe %d\n", + pipe); return -EINVAL; } - } + } else + intel_put_pch_pll(intel_crtc); /* The LVDS pin pair needs to be on before the DPLLs are enabled. * This is an exception to the general rule that mode_set doesn't turn @@ -4320,11 +4632,11 @@ static int ironlake_crtc_mode_set(struct drm_crtc *crtc, I915_WRITE(TRANSDPLINK_N1(pipe), 0); } - if (!intel_crtc->no_pll && (!edp_encoder || is_pch_edp)) { - I915_WRITE(PCH_DPLL(pipe), dpll); + if (intel_crtc->pch_pll) { + I915_WRITE(intel_crtc->pch_pll->pll_reg, dpll); /* Wait for the clocks to stabilize. */ - POSTING_READ(PCH_DPLL(pipe)); + POSTING_READ(intel_crtc->pch_pll->pll_reg); udelay(150); /* The pixel multiplier can only be updated once the @@ -4332,20 +4644,20 @@ static int ironlake_crtc_mode_set(struct drm_crtc *crtc, * * So write it again. */ - I915_WRITE(PCH_DPLL(pipe), dpll); + I915_WRITE(intel_crtc->pch_pll->pll_reg, dpll); } intel_crtc->lowfreq_avail = false; - if (!intel_crtc->no_pll) { + if (intel_crtc->pch_pll) { if (is_lvds && has_reduced_clock && i915_powersave) { - I915_WRITE(PCH_FP1(pipe), fp2); + I915_WRITE(intel_crtc->pch_pll->fp1_reg, fp2); intel_crtc->lowfreq_avail = true; if (HAS_PIPE_CXSR(dev)) { DRM_DEBUG_KMS("enabling CxSR downclocking\n"); pipeconf |= PIPECONF_CXSR_DOWNCLOCK; } } else { - I915_WRITE(PCH_FP1(pipe), fp); + I915_WRITE(intel_crtc->pch_pll->fp1_reg, fp); if (HAS_PIPE_CXSR(dev)) { DRM_DEBUG_KMS("disabling CxSR downclocking\n"); pipeconf &= ~PIPECONF_CXSR_DOWNCLOCK; @@ -4413,6 +4725,8 @@ static int ironlake_crtc_mode_set(struct drm_crtc *crtc, intel_update_watermarks(dev); + intel_update_linetime_watermarks(dev, pipe, adjusted_mode); + return ret; } @@ -4777,9 +5091,6 @@ static void intel_crtc_update_cursor(struct drm_crtc *crtc, else i9xx_update_cursor(crtc, base); } - - if (visible) - intel_mark_busy(dev, to_intel_framebuffer(crtc->fb)->obj); } static int intel_crtc_cursor_set(struct drm_crtc *crtc, @@ -5303,7 +5614,6 @@ struct drm_display_mode *intel_crtc_mode_get(struct drm_device *dev, mode->vsync_end = ((vsync & 0xffff0000) >> 16) + 1; drm_mode_set_name(mode); - drm_mode_set_crtcinfo(mode, 0); return mode; } @@ -5475,12 +5785,16 @@ void intel_mark_busy(struct drm_device *dev, struct drm_i915_gem_object *obj) if (!drm_core_check_feature(dev, DRIVER_MODESET)) return; - if (!dev_priv->busy) + if (!dev_priv->busy) { + intel_sanitize_pm(dev); dev_priv->busy = true; - else + } else mod_timer(&dev_priv->idle_timer, jiffies + msecs_to_jiffies(GPU_IDLE_TIMEOUT)); + if (obj == NULL) + return; + list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { if (!crtc->fb) continue; @@ -5653,16 +5967,17 @@ static int intel_gen2_queue_flip(struct drm_device *dev, struct intel_crtc *intel_crtc = to_intel_crtc(crtc); unsigned long offset; u32 flip_mask; + struct intel_ring_buffer *ring = &dev_priv->ring[RCS]; int ret; - ret = intel_pin_and_fence_fb_obj(dev, obj, LP_RING(dev_priv)); + ret = intel_pin_and_fence_fb_obj(dev, obj, ring); if (ret) goto err; /* Offset into the new buffer for cases of shared fbs between CRTCs */ offset = crtc->y * fb->pitches[0] + crtc->x * fb->bits_per_pixel/8; - ret = BEGIN_LP_RING(6); + ret = intel_ring_begin(ring, 6); if (ret) goto err_unpin; @@ -5673,14 +5988,14 @@ static int intel_gen2_queue_flip(struct drm_device *dev, flip_mask = MI_WAIT_FOR_PLANE_B_FLIP; else flip_mask = MI_WAIT_FOR_PLANE_A_FLIP; - OUT_RING(MI_WAIT_FOR_EVENT | flip_mask); - OUT_RING(MI_NOOP); - OUT_RING(MI_DISPLAY_FLIP | - MI_DISPLAY_FLIP_PLANE(intel_crtc->plane)); - OUT_RING(fb->pitches[0]); - OUT_RING(obj->gtt_offset + offset); - OUT_RING(0); /* aux display base address, unused */ - ADVANCE_LP_RING(); + intel_ring_emit(ring, MI_WAIT_FOR_EVENT | flip_mask); + intel_ring_emit(ring, MI_NOOP); + intel_ring_emit(ring, MI_DISPLAY_FLIP | + MI_DISPLAY_FLIP_PLANE(intel_crtc->plane)); + intel_ring_emit(ring, fb->pitches[0]); + intel_ring_emit(ring, obj->gtt_offset + offset); + intel_ring_emit(ring, 0); /* aux display base address, unused */ + intel_ring_advance(ring); return 0; err_unpin: @@ -5698,16 +6013,17 @@ static int intel_gen3_queue_flip(struct drm_device *dev, struct intel_crtc *intel_crtc = to_intel_crtc(crtc); unsigned long offset; u32 flip_mask; + struct intel_ring_buffer *ring = &dev_priv->ring[RCS]; int ret; - ret = intel_pin_and_fence_fb_obj(dev, obj, LP_RING(dev_priv)); + ret = intel_pin_and_fence_fb_obj(dev, obj, ring); if (ret) goto err; /* Offset into the new buffer for cases of shared fbs between CRTCs */ offset = crtc->y * fb->pitches[0] + crtc->x * fb->bits_per_pixel/8; - ret = BEGIN_LP_RING(6); + ret = intel_ring_begin(ring, 6); if (ret) goto err_unpin; @@ -5715,15 +6031,15 @@ static int intel_gen3_queue_flip(struct drm_device *dev, flip_mask = MI_WAIT_FOR_PLANE_B_FLIP; else flip_mask = MI_WAIT_FOR_PLANE_A_FLIP; - OUT_RING(MI_WAIT_FOR_EVENT | flip_mask); - OUT_RING(MI_NOOP); - OUT_RING(MI_DISPLAY_FLIP_I915 | - MI_DISPLAY_FLIP_PLANE(intel_crtc->plane)); - OUT_RING(fb->pitches[0]); - OUT_RING(obj->gtt_offset + offset); - OUT_RING(MI_NOOP); - - ADVANCE_LP_RING(); + intel_ring_emit(ring, MI_WAIT_FOR_EVENT | flip_mask); + intel_ring_emit(ring, MI_NOOP); + intel_ring_emit(ring, MI_DISPLAY_FLIP_I915 | + MI_DISPLAY_FLIP_PLANE(intel_crtc->plane)); + intel_ring_emit(ring, fb->pitches[0]); + intel_ring_emit(ring, obj->gtt_offset + offset); + intel_ring_emit(ring, MI_NOOP); + + intel_ring_advance(ring); return 0; err_unpin: @@ -5740,13 +6056,14 @@ static int intel_gen4_queue_flip(struct drm_device *dev, struct drm_i915_private *dev_priv = dev->dev_private; struct intel_crtc *intel_crtc = to_intel_crtc(crtc); uint32_t pf, pipesrc; + struct intel_ring_buffer *ring = &dev_priv->ring[RCS]; int ret; - ret = intel_pin_and_fence_fb_obj(dev, obj, LP_RING(dev_priv)); + ret = intel_pin_and_fence_fb_obj(dev, obj, ring); if (ret) goto err; - ret = BEGIN_LP_RING(4); + ret = intel_ring_begin(ring, 4); if (ret) goto err_unpin; @@ -5754,10 +6071,10 @@ static int intel_gen4_queue_flip(struct drm_device *dev, * Display Registers (which do not change across a page-flip) * so we need only reprogram the base address. */ - OUT_RING(MI_DISPLAY_FLIP | - MI_DISPLAY_FLIP_PLANE(intel_crtc->plane)); - OUT_RING(fb->pitches[0]); - OUT_RING(obj->gtt_offset | obj->tiling_mode); + intel_ring_emit(ring, MI_DISPLAY_FLIP | + MI_DISPLAY_FLIP_PLANE(intel_crtc->plane)); + intel_ring_emit(ring, fb->pitches[0]); + intel_ring_emit(ring, obj->gtt_offset | obj->tiling_mode); /* XXX Enabling the panel-fitter across page-flip is so far * untested on non-native modes, so ignore it for now. @@ -5765,8 +6082,8 @@ static int intel_gen4_queue_flip(struct drm_device *dev, */ pf = 0; pipesrc = I915_READ(PIPESRC(intel_crtc->pipe)) & 0x0fff0fff; - OUT_RING(pf | pipesrc); - ADVANCE_LP_RING(); + intel_ring_emit(ring, pf | pipesrc); + intel_ring_advance(ring); return 0; err_unpin: @@ -5782,21 +6099,22 @@ static int intel_gen6_queue_flip(struct drm_device *dev, { struct drm_i915_private *dev_priv = dev->dev_private; struct intel_crtc *intel_crtc = to_intel_crtc(crtc); + struct intel_ring_buffer *ring = &dev_priv->ring[RCS]; uint32_t pf, pipesrc; int ret; - ret = intel_pin_and_fence_fb_obj(dev, obj, LP_RING(dev_priv)); + ret = intel_pin_and_fence_fb_obj(dev, obj, ring); if (ret) goto err; - ret = BEGIN_LP_RING(4); + ret = intel_ring_begin(ring, 4); if (ret) goto err_unpin; - OUT_RING(MI_DISPLAY_FLIP | - MI_DISPLAY_FLIP_PLANE(intel_crtc->plane)); - OUT_RING(fb->pitches[0] | obj->tiling_mode); - OUT_RING(obj->gtt_offset); + intel_ring_emit(ring, MI_DISPLAY_FLIP | + MI_DISPLAY_FLIP_PLANE(intel_crtc->plane)); + intel_ring_emit(ring, fb->pitches[0] | obj->tiling_mode); + intel_ring_emit(ring, obj->gtt_offset); /* Contrary to the suggestions in the documentation, * "Enable Panel Fitter" does not seem to be required when page @@ -5806,8 +6124,8 @@ static int intel_gen6_queue_flip(struct drm_device *dev, */ pf = 0; pipesrc = I915_READ(PIPESRC(intel_crtc->pipe)) & 0x0fff0fff; - OUT_RING(pf | pipesrc); - ADVANCE_LP_RING(); + intel_ring_emit(ring, pf | pipesrc); + intel_ring_advance(ring); return 0; err_unpin: @@ -5926,6 +6244,7 @@ static int intel_crtc_page_flip(struct drm_crtc *crtc, goto cleanup_pending; intel_disable_fbc(dev); + intel_mark_busy(dev, obj); mutex_unlock(&dev->struct_mutex); trace_i915_flip_request(intel_crtc->plane, obj); @@ -5954,10 +6273,11 @@ static void intel_sanitize_modesetting(struct drm_device *dev, { struct drm_i915_private *dev_priv = dev->dev_private; u32 reg, val; + int i; /* Clear any frame start delays used for debugging left by the BIOS */ - for_each_pipe(pipe) { - reg = PIPECONF(pipe); + for_each_pipe(i) { + reg = PIPECONF(i); I915_WRITE(reg, I915_READ(reg) & ~PIPECONF_FRAME_START_DELAY_MASK); } @@ -6027,6 +6347,23 @@ static const struct drm_crtc_funcs intel_crtc_funcs = { .page_flip = intel_crtc_page_flip, }; +static void intel_pch_pll_init(struct drm_device *dev) +{ + drm_i915_private_t *dev_priv = dev->dev_private; + int i; + + if (dev_priv->num_pch_pll == 0) { + DRM_DEBUG_KMS("No PCH PLLs on this hardware, skipping initialisation\n"); + return; + } + + for (i = 0; i < dev_priv->num_pch_pll; i++) { + dev_priv->pch_plls[i].pll_reg = _PCH_DPLL(i); + dev_priv->pch_plls[i].fp0_reg = _PCH_FP0(i); + dev_priv->pch_plls[i].fp1_reg = _PCH_FP1(i); + } +} + static void intel_crtc_init(struct drm_device *dev, int pipe) { drm_i915_private_t *dev_priv = dev->dev_private; @@ -6064,8 +6401,6 @@ static void intel_crtc_init(struct drm_device *dev, int pipe) intel_crtc->bpp = 24; /* default for pre-Ironlake */ if (HAS_PCH_SPLIT(dev)) { - if (pipe == 2 && IS_IVYBRIDGE(dev)) - intel_crtc->no_pll = true; intel_helper_funcs.prepare = ironlake_crtc_prepare; intel_helper_funcs.commit = ironlake_crtc_commit; } else { @@ -6084,15 +6419,12 @@ static void intel_crtc_init(struct drm_device *dev, int pipe) int intel_get_pipe_from_crtc_id(struct drm_device *dev, void *data, struct drm_file *file) { - drm_i915_private_t *dev_priv = dev->dev_private; struct drm_i915_get_pipe_from_crtc_id *pipe_from_crtc_id = data; struct drm_mode_object *drmmode_obj; struct intel_crtc *crtc; - if (!dev_priv) { - DRM_ERROR("called with no initialization\n"); - return -EINVAL; - } + if (!drm_core_check_feature(dev, DRIVER_MODESET)) + return -ENODEV; drmmode_obj = drm_mode_object_find(dev, pipe_from_crtc_id->crtc_id, DRM_MODE_OBJECT_CRTC); @@ -6165,7 +6497,26 @@ static void intel_setup_outputs(struct drm_device *dev) intel_crt_init(dev); - if (HAS_PCH_SPLIT(dev)) { + if (IS_HASWELL(dev)) { + int found; + + /* Haswell uses DDI functions to detect digital outputs */ + found = I915_READ(DDI_BUF_CTL_A) & DDI_INIT_DISPLAY_DETECTED; + /* DDI A only supports eDP */ + if (found) + intel_ddi_init(dev, PORT_A); + + /* DDI B, C and D detection is indicated by the SFUSE_STRAP + * register */ + found = I915_READ(SFUSE_STRAP); + + if (found & SFUSE_STRAP_DDIB_DETECTED) + intel_ddi_init(dev, PORT_B); + if (found & SFUSE_STRAP_DDIC_DETECTED) + intel_ddi_init(dev, PORT_C); + if (found & SFUSE_STRAP_DDID_DETECTED) + intel_ddi_init(dev, PORT_D); + } else if (HAS_PCH_SPLIT(dev)) { int found; if (I915_READ(HDMIB) & PORT_DETECTED) { @@ -6348,10 +6699,12 @@ static void intel_init_display(struct drm_device *dev) if (HAS_PCH_SPLIT(dev)) { dev_priv->display.dpms = ironlake_crtc_dpms; dev_priv->display.crtc_mode_set = ironlake_crtc_mode_set; + dev_priv->display.off = ironlake_crtc_off; dev_priv->display.update_plane = ironlake_update_plane; } else { dev_priv->display.dpms = i9xx_crtc_dpms; dev_priv->display.crtc_mode_set = i9xx_crtc_mode_set; + dev_priv->display.off = i9xx_crtc_off; dev_priv->display.update_plane = i9xx_update_plane; } @@ -6392,6 +6745,9 @@ static void intel_init_display(struct drm_device *dev) /* FIXME: detect B0+ stepping and use auto training */ dev_priv->display.fdi_link_train = ivb_manual_fdi_link_train; dev_priv->display.write_eld = ironlake_write_eld; + } else if (IS_HASWELL(dev)) { + dev_priv->display.fdi_link_train = hsw_fdi_link_train; + dev_priv->display.write_eld = ironlake_write_eld; } else dev_priv->display.update_wm = NULL; } else if (IS_VALLEYVIEW(dev)) { @@ -6559,6 +6915,7 @@ void intel_modeset_init_hw(struct drm_device *dev) if (IS_IRONLAKE_M(dev)) { ironlake_enable_drps(dev); + ironlake_enable_rc6(dev); intel_init_emon(dev); } @@ -6584,12 +6941,14 @@ void intel_modeset_init(struct drm_device *dev) dev->mode_config.preferred_depth = 24; dev->mode_config.prefer_shadow = 1; - dev->mode_config.funcs = (void *)&intel_mode_funcs; + dev->mode_config.funcs = &intel_mode_funcs; intel_init_quirks(dev); intel_init_pm(dev); + intel_prepare_ddi(dev); + intel_init_display(dev); if (IS_GEN2(dev)) { @@ -6614,12 +6973,12 @@ void intel_modeset_init(struct drm_device *dev) DRM_DEBUG_KMS("plane %d init failed: %d\n", i, ret); } + intel_pch_pll_init(dev); + /* Just disable it once at startup */ i915_disable_vga(dev); intel_setup_outputs(dev); - intel_modeset_init_hw(dev); - INIT_WORK(&dev_priv->idle_work, intel_idle_update); setup_timer(&dev_priv->idle_timer, intel_gpu_idle_timer, (unsigned long)dev); @@ -6627,8 +6986,7 @@ void intel_modeset_init(struct drm_device *dev) void intel_modeset_gem_init(struct drm_device *dev) { - if (IS_IRONLAKE_M(dev)) - ironlake_enable_rc6(dev); + intel_modeset_init_hw(dev); intel_setup_overlay(dev); } diff --git a/drivers/gpu/drm/i915/intel_dp.c b/drivers/gpu/drm/i915/intel_dp.c index 44cf32c..71c7096 100644 --- a/drivers/gpu/drm/i915/intel_dp.c +++ b/drivers/gpu/drm/i915/intel_dp.c @@ -688,7 +688,7 @@ intel_dp_mode_fixup(struct drm_encoder *encoder, struct drm_display_mode *mode, int lane_count, clock; int max_lane_count = intel_dp_max_lane_count(intel_dp); int max_clock = intel_dp_max_link_bw(intel_dp) == DP_LINK_BW_2_7 ? 1 : 0; - int bpp; + int bpp, mode_rate; static int bws[2] = { DP_LINK_BW_1_62, DP_LINK_BW_2_7 }; if (is_edp(intel_dp) && intel_dp->panel_fixed_mode) { @@ -702,24 +702,30 @@ intel_dp_mode_fixup(struct drm_encoder *encoder, struct drm_display_mode *mode, mode->clock = intel_dp->panel_fixed_mode->clock; } + DRM_DEBUG_KMS("DP link computation with max lane count %i " + "max bw %02x pixel clock %iKHz\n", + max_lane_count, bws[max_clock], mode->clock); + if (!intel_dp_adjust_dithering(intel_dp, mode, adjusted_mode)) return false; bpp = adjusted_mode->private_flags & INTEL_MODE_DP_FORCE_6BPC ? 18 : 24; + mode_rate = intel_dp_link_required(mode->clock, bpp); for (lane_count = 1; lane_count <= max_lane_count; lane_count <<= 1) { for (clock = 0; clock <= max_clock; clock++) { int link_avail = intel_dp_max_data_rate(intel_dp_link_clock(bws[clock]), lane_count); - if (intel_dp_link_required(mode->clock, bpp) - <= link_avail) { + if (mode_rate <= link_avail) { intel_dp->link_bw = bws[clock]; intel_dp->lane_count = lane_count; adjusted_mode->clock = intel_dp_link_clock(intel_dp->link_bw); - DRM_DEBUG_KMS("Display port link bw %02x lane " - "count %d clock %d\n", + DRM_DEBUG_KMS("DP link bw %02x lane " + "count %d clock %d bpp %d\n", intel_dp->link_bw, intel_dp->lane_count, - adjusted_mode->clock); + adjusted_mode->clock, bpp); + DRM_DEBUG_KMS("DP link bw required %i available %i\n", + mode_rate, link_avail); return true; } } @@ -1955,6 +1961,23 @@ intel_dp_get_dpcd(struct intel_dp *intel_dp) return false; } +static void +intel_dp_probe_oui(struct intel_dp *intel_dp) +{ + u8 buf[3]; + + if (!(intel_dp->dpcd[DP_DOWN_STREAM_PORT_COUNT] & DP_OUI_SUPPORT)) + return; + + if (intel_dp_aux_native_read_retry(intel_dp, DP_SINK_OUI, buf, 3)) + DRM_DEBUG_KMS("Sink OUI: %02hx%02hx%02hx\n", + buf[0], buf[1], buf[2]); + + if (intel_dp_aux_native_read_retry(intel_dp, DP_BRANCH_OUI, buf, 3)) + DRM_DEBUG_KMS("Branch OUI: %02hx%02hx%02hx\n", + buf[0], buf[1], buf[2]); +} + static bool intel_dp_get_sink_irq(struct intel_dp *intel_dp, u8 *sink_irq_vector) { @@ -2138,6 +2161,8 @@ intel_dp_detect(struct drm_connector *connector, bool force) if (status != connector_status_connected) return status; + intel_dp_probe_oui(intel_dp); + if (intel_dp->force_audio != HDMI_AUDIO_AUTO) { intel_dp->has_audio = (intel_dp->force_audio == HDMI_AUDIO_ON); } else { @@ -2439,6 +2464,7 @@ intel_dp_init(struct drm_device *dev, int output_reg) } intel_encoder->crtc_mask = (1 << 0) | (1 << 1) | (1 << 2); + connector->interlace_allowed = true; connector->doublescan_allowed = 0; diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h index 7a7cae7..3e09188 100644 --- a/drivers/gpu/drm/i915/intel_drv.h +++ b/drivers/gpu/drm/i915/intel_drv.h @@ -183,8 +183,8 @@ struct intel_crtc { bool cursor_visible; unsigned int bpp; - bool no_pll; /* tertiary pipe for IVB */ - bool use_pll_a; + /* We can share PLLs across outputs if the timings match */ + struct intel_pch_pll *pch_pll; }; struct intel_plane { @@ -238,6 +238,8 @@ struct cxsr_latency { #define DIP_TYPE_AVI 0x82 #define DIP_VERSION_AVI 0x2 #define DIP_LEN_AVI 13 +#define DIP_AVI_PR_1 0 +#define DIP_AVI_PR_2 1 #define DIP_TYPE_SPD 0x83 #define DIP_VERSION_SPD 0x1 @@ -271,23 +273,36 @@ struct dip_infoframe { uint8_t ITC_EC_Q_SC; /* PB4 - VIC 6:0 */ uint8_t VIC; - /* PB5 - PR 3:0 */ - uint8_t PR; + /* PB5 - YQ 7:6, CN 5:4, PR 3:0 */ + uint8_t YQ_CN_PR; /* PB6 to PB13 */ uint16_t top_bar_end; uint16_t bottom_bar_start; uint16_t left_bar_end; uint16_t right_bar_start; - } avi; + } __attribute__ ((packed)) avi; struct { uint8_t vn[8]; uint8_t pd[16]; uint8_t sdi; - } spd; + } __attribute__ ((packed)) spd; uint8_t payload[27]; } __attribute__ ((packed)) body; } __attribute__((packed)); +struct intel_hdmi { + struct intel_encoder base; + u32 sdvox_reg; + int ddc_bus; + int ddi_port; + uint32_t color_range; + bool has_hdmi_sink; + bool has_audio; + enum hdmi_force_audio force_audio; + void (*write_infoframe)(struct drm_encoder *encoder, + struct dip_infoframe *frame); +}; + static inline struct drm_crtc * intel_get_crtc_for_pipe(struct drm_device *dev, int pipe) { @@ -327,7 +342,11 @@ extern void intel_attach_broadcast_rgb_property(struct drm_connector *connector) extern void intel_crt_init(struct drm_device *dev); extern void intel_hdmi_init(struct drm_device *dev, int sdvox_reg); -void intel_dip_infoframe_csum(struct dip_infoframe *avi_if); +extern struct intel_hdmi *enc_to_intel_hdmi(struct drm_encoder *encoder); +extern void intel_hdmi_set_avi_infoframe(struct drm_encoder *encoder, + struct drm_display_mode *adjusted_mode); +extern void intel_hdmi_set_spd_infoframe(struct drm_encoder *encoder); +extern void intel_dip_infoframe_csum(struct dip_infoframe *avi_if); extern bool intel_sdvo_init(struct drm_device *dev, uint32_t sdvo_reg, bool is_sdvob); extern void intel_dvo_init(struct drm_device *dev); @@ -346,6 +365,8 @@ extern int intel_plane_init(struct drm_device *dev, enum pipe pipe); extern void intel_flush_display_plane(struct drm_i915_private *dev_priv, enum plane plane); +void intel_sanitize_pm(struct drm_device *dev); + /* intel_panel.c */ extern void intel_fixed_panel_mode(struct drm_display_mode *fixed_mode, struct drm_display_mode *adjusted_mode); @@ -405,10 +426,6 @@ extern void intel_enable_clock_gating(struct drm_device *dev); extern void ironlake_disable_rc6(struct drm_device *dev); extern void ironlake_enable_drps(struct drm_device *dev); extern void ironlake_disable_drps(struct drm_device *dev); -extern void gen6_enable_rps(struct drm_i915_private *dev_priv); -extern void gen6_update_ring_freq(struct drm_i915_private *dev_priv); -extern void gen6_disable_rps(struct drm_device *dev); -extern void intel_init_emon(struct drm_device *dev); extern int intel_pin_and_fence_fb_obj(struct drm_device *dev, struct drm_i915_gem_object *obj, @@ -446,12 +463,17 @@ extern void intel_init_clock_gating(struct drm_device *dev); extern void intel_write_eld(struct drm_encoder *encoder, struct drm_display_mode *mode); extern void intel_cpt_verify_modeset(struct drm_device *dev, int pipe); +extern void intel_prepare_ddi(struct drm_device *dev); +extern void hsw_fdi_link_train(struct drm_crtc *crtc); +extern void intel_ddi_init(struct drm_device *dev, enum port port); /* For use by IVB LP watermark workaround in intel_sprite.c */ extern void intel_update_watermarks(struct drm_device *dev); extern void intel_update_sprite_watermarks(struct drm_device *dev, int pipe, uint32_t sprite_width, int pixel_size); +extern void intel_update_linetime_watermarks(struct drm_device *dev, int pipe, + struct drm_display_mode *mode); extern int intel_sprite_set_colorkey(struct drm_device *dev, void *data, struct drm_file *file_priv); @@ -466,5 +488,18 @@ extern void intel_init_pm(struct drm_device *dev); extern bool intel_fbc_enabled(struct drm_device *dev); extern void intel_enable_fbc(struct drm_crtc *crtc, unsigned long interval); extern void intel_update_fbc(struct drm_device *dev); +/* IPS */ +extern void intel_gpu_ips_init(struct drm_i915_private *dev_priv); +extern void intel_gpu_ips_teardown(void); + +extern void gen6_enable_rps(struct drm_i915_private *dev_priv); +extern void gen6_update_ring_freq(struct drm_i915_private *dev_priv); +extern void gen6_disable_rps(struct drm_device *dev); +extern void intel_init_emon(struct drm_device *dev); + +extern void intel_ddi_dpms(struct drm_encoder *encoder, int mode); +extern void intel_ddi_mode_set(struct drm_encoder *encoder, + struct drm_display_mode *mode, + struct drm_display_mode *adjusted_mode); #endif /* __INTEL_DRV_H__ */ diff --git a/drivers/gpu/drm/i915/intel_hdmi.c b/drivers/gpu/drm/i915/intel_hdmi.c index 1eef50d..2ead3bf 100644 --- a/drivers/gpu/drm/i915/intel_hdmi.c +++ b/drivers/gpu/drm/i915/intel_hdmi.c @@ -37,19 +37,7 @@ #include "i915_drm.h" #include "i915_drv.h" -struct intel_hdmi { - struct intel_encoder base; - u32 sdvox_reg; - int ddc_bus; - uint32_t color_range; - bool has_hdmi_sink; - bool has_audio; - enum hdmi_force_audio force_audio; - void (*write_infoframe)(struct drm_encoder *encoder, - struct dip_infoframe *frame); -}; - -static struct intel_hdmi *enc_to_intel_hdmi(struct drm_encoder *encoder) +struct intel_hdmi *enc_to_intel_hdmi(struct drm_encoder *encoder) { return container_of(encoder, struct intel_hdmi, base.base); } @@ -75,107 +63,182 @@ void intel_dip_infoframe_csum(struct dip_infoframe *frame) frame->checksum = 0x100 - sum; } -static u32 intel_infoframe_index(struct dip_infoframe *frame) +static u32 g4x_infoframe_index(struct dip_infoframe *frame) { - u32 flags = 0; - switch (frame->type) { case DIP_TYPE_AVI: - flags |= VIDEO_DIP_SELECT_AVI; - break; + return VIDEO_DIP_SELECT_AVI; case DIP_TYPE_SPD: - flags |= VIDEO_DIP_SELECT_SPD; - break; + return VIDEO_DIP_SELECT_SPD; default: DRM_DEBUG_DRIVER("unknown info frame type %d\n", frame->type); - break; + return 0; } - - return flags; } -static u32 intel_infoframe_flags(struct dip_infoframe *frame) +static u32 g4x_infoframe_enable(struct dip_infoframe *frame) { - u32 flags = 0; + switch (frame->type) { + case DIP_TYPE_AVI: + return VIDEO_DIP_ENABLE_AVI; + case DIP_TYPE_SPD: + return VIDEO_DIP_ENABLE_SPD; + default: + DRM_DEBUG_DRIVER("unknown info frame type %d\n", frame->type); + return 0; + } +} +static u32 hsw_infoframe_enable(struct dip_infoframe *frame) +{ switch (frame->type) { case DIP_TYPE_AVI: - flags |= VIDEO_DIP_ENABLE_AVI | VIDEO_DIP_FREQ_VSYNC; - break; + return VIDEO_DIP_ENABLE_AVI_HSW; case DIP_TYPE_SPD: - flags |= VIDEO_DIP_ENABLE_SPD | VIDEO_DIP_FREQ_VSYNC; - break; + return VIDEO_DIP_ENABLE_SPD_HSW; default: DRM_DEBUG_DRIVER("unknown info frame type %d\n", frame->type); - break; + return 0; } +} - return flags; +static u32 hsw_infoframe_data_reg(struct dip_infoframe *frame, enum pipe pipe) +{ + switch (frame->type) { + case DIP_TYPE_AVI: + return HSW_TVIDEO_DIP_AVI_DATA(pipe); + case DIP_TYPE_SPD: + return HSW_TVIDEO_DIP_SPD_DATA(pipe); + default: + DRM_DEBUG_DRIVER("unknown info frame type %d\n", frame->type); + return 0; + } } -static void i9xx_write_infoframe(struct drm_encoder *encoder, - struct dip_infoframe *frame) +static void g4x_write_infoframe(struct drm_encoder *encoder, + struct dip_infoframe *frame) { uint32_t *data = (uint32_t *)frame; struct drm_device *dev = encoder->dev; struct drm_i915_private *dev_priv = dev->dev_private; struct intel_hdmi *intel_hdmi = enc_to_intel_hdmi(encoder); - u32 port, flags, val = I915_READ(VIDEO_DIP_CTL); + u32 val = I915_READ(VIDEO_DIP_CTL); unsigned i, len = DIP_HEADER_SIZE + frame->len; - - /* XXX first guess at handling video port, is this corrent? */ + val &= ~VIDEO_DIP_PORT_MASK; if (intel_hdmi->sdvox_reg == SDVOB) - port = VIDEO_DIP_PORT_B; + val |= VIDEO_DIP_PORT_B; else if (intel_hdmi->sdvox_reg == SDVOC) - port = VIDEO_DIP_PORT_C; + val |= VIDEO_DIP_PORT_C; else return; - flags = intel_infoframe_index(frame); + val &= ~(VIDEO_DIP_SELECT_MASK | 0xf); /* clear DIP data offset */ + val |= g4x_infoframe_index(frame); - val &= ~VIDEO_DIP_SELECT_MASK; + val &= ~g4x_infoframe_enable(frame); + val |= VIDEO_DIP_ENABLE; - I915_WRITE(VIDEO_DIP_CTL, VIDEO_DIP_ENABLE | val | port | flags); + I915_WRITE(VIDEO_DIP_CTL, val); for (i = 0; i < len; i += 4) { I915_WRITE(VIDEO_DIP_DATA, *data); data++; } - flags |= intel_infoframe_flags(frame); + val |= g4x_infoframe_enable(frame); + val &= ~VIDEO_DIP_FREQ_MASK; + val |= VIDEO_DIP_FREQ_VSYNC; - I915_WRITE(VIDEO_DIP_CTL, VIDEO_DIP_ENABLE | val | port | flags); + I915_WRITE(VIDEO_DIP_CTL, val); } -static void ironlake_write_infoframe(struct drm_encoder *encoder, - struct dip_infoframe *frame) +static void ibx_write_infoframe(struct drm_encoder *encoder, + struct dip_infoframe *frame) { uint32_t *data = (uint32_t *)frame; struct drm_device *dev = encoder->dev; 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_crtc *intel_crtc = to_intel_crtc(encoder->crtc); + struct intel_hdmi *intel_hdmi = enc_to_intel_hdmi(encoder); int reg = TVIDEO_DIP_CTL(intel_crtc->pipe); unsigned i, len = DIP_HEADER_SIZE + frame->len; - u32 flags, val = I915_READ(reg); + u32 val = I915_READ(reg); + + val &= ~VIDEO_DIP_PORT_MASK; + switch (intel_hdmi->sdvox_reg) { + case HDMIB: + val |= VIDEO_DIP_PORT_B; + break; + case HDMIC: + val |= VIDEO_DIP_PORT_C; + break; + case HDMID: + val |= VIDEO_DIP_PORT_D; + break; + default: + return; + } intel_wait_for_vblank(dev, intel_crtc->pipe); - flags = intel_infoframe_index(frame); + val &= ~(VIDEO_DIP_SELECT_MASK | 0xf); /* clear DIP data offset */ + val |= g4x_infoframe_index(frame); + + val &= ~g4x_infoframe_enable(frame); + val |= VIDEO_DIP_ENABLE; + + I915_WRITE(reg, val); + + for (i = 0; i < len; i += 4) { + I915_WRITE(TVIDEO_DIP_DATA(intel_crtc->pipe), *data); + data++; + } + + val |= g4x_infoframe_enable(frame); + val &= ~VIDEO_DIP_FREQ_MASK; + val |= VIDEO_DIP_FREQ_VSYNC; + + I915_WRITE(reg, val); +} + +static void cpt_write_infoframe(struct drm_encoder *encoder, + struct dip_infoframe *frame) +{ + uint32_t *data = (uint32_t *)frame; + 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); + int reg = TVIDEO_DIP_CTL(intel_crtc->pipe); + unsigned i, len = DIP_HEADER_SIZE + frame->len; + u32 val = I915_READ(reg); + + intel_wait_for_vblank(dev, intel_crtc->pipe); val &= ~(VIDEO_DIP_SELECT_MASK | 0xf); /* clear DIP data offset */ + val |= g4x_infoframe_index(frame); + + /* The DIP control register spec says that we need to update the AVI + * infoframe without clearing its enable bit */ + if (frame->type == DIP_TYPE_AVI) + val |= VIDEO_DIP_ENABLE_AVI; + else + val &= ~g4x_infoframe_enable(frame); - I915_WRITE(reg, VIDEO_DIP_ENABLE | val | flags); + val |= VIDEO_DIP_ENABLE; + + I915_WRITE(reg, val); for (i = 0; i < len; i += 4) { I915_WRITE(TVIDEO_DIP_DATA(intel_crtc->pipe), *data); data++; } - flags |= intel_infoframe_flags(frame); + val |= g4x_infoframe_enable(frame); + val &= ~VIDEO_DIP_FREQ_MASK; + val |= VIDEO_DIP_FREQ_VSYNC; - I915_WRITE(reg, VIDEO_DIP_ENABLE | val | flags); + I915_WRITE(reg, val); } static void vlv_write_infoframe(struct drm_encoder *encoder, @@ -184,28 +247,60 @@ static void vlv_write_infoframe(struct drm_encoder *encoder, uint32_t *data = (uint32_t *)frame; struct drm_device *dev = encoder->dev; 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_crtc *intel_crtc = to_intel_crtc(encoder->crtc); int reg = VLV_TVIDEO_DIP_CTL(intel_crtc->pipe); unsigned i, len = DIP_HEADER_SIZE + frame->len; - u32 flags, val = I915_READ(reg); + u32 val = I915_READ(reg); intel_wait_for_vblank(dev, intel_crtc->pipe); - flags = intel_infoframe_index(frame); - val &= ~(VIDEO_DIP_SELECT_MASK | 0xf); /* clear DIP data offset */ + val |= g4x_infoframe_index(frame); + + val &= ~g4x_infoframe_enable(frame); + val |= VIDEO_DIP_ENABLE; - I915_WRITE(reg, VIDEO_DIP_ENABLE | val | flags); + I915_WRITE(reg, val); for (i = 0; i < len; i += 4) { I915_WRITE(VLV_TVIDEO_DIP_DATA(intel_crtc->pipe), *data); data++; } - flags |= intel_infoframe_flags(frame); + val |= g4x_infoframe_enable(frame); + val &= ~VIDEO_DIP_FREQ_MASK; + val |= VIDEO_DIP_FREQ_VSYNC; - I915_WRITE(reg, VIDEO_DIP_ENABLE | val | flags); + I915_WRITE(reg, val); +} + +static void hsw_write_infoframe(struct drm_encoder *encoder, + struct dip_infoframe *frame) +{ + uint32_t *data = (uint32_t *)frame; + 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); + u32 ctl_reg = HSW_TVIDEO_DIP_CTL(intel_crtc->pipe); + u32 data_reg = hsw_infoframe_data_reg(frame, intel_crtc->pipe); + unsigned int i, len = DIP_HEADER_SIZE + frame->len; + u32 val = I915_READ(ctl_reg); + + if (data_reg == 0) + return; + + intel_wait_for_vblank(dev, intel_crtc->pipe); + + val &= ~hsw_infoframe_enable(frame); + I915_WRITE(ctl_reg, val); + + for (i = 0; i < len; i += 4) { + I915_WRITE(data_reg + i, *data); + data++; + } + + val |= hsw_infoframe_enable(frame); + I915_WRITE(ctl_reg, val); } static void intel_set_infoframe(struct drm_encoder *encoder, @@ -220,7 +315,8 @@ static void intel_set_infoframe(struct drm_encoder *encoder, intel_hdmi->write_infoframe(encoder, frame); } -static void intel_hdmi_set_avi_infoframe(struct drm_encoder *encoder) +void intel_hdmi_set_avi_infoframe(struct drm_encoder *encoder, + struct drm_display_mode *adjusted_mode) { struct dip_infoframe avi_if = { .type = DIP_TYPE_AVI, @@ -228,10 +324,13 @@ static void intel_hdmi_set_avi_infoframe(struct drm_encoder *encoder) .len = DIP_LEN_AVI, }; + if (adjusted_mode->flags & DRM_MODE_FLAG_DBLCLK) + avi_if.body.avi.YQ_CN_PR |= DIP_AVI_PR_2; + intel_set_infoframe(encoder, &avi_if); } -static void intel_hdmi_set_spd_infoframe(struct drm_encoder *encoder) +void intel_hdmi_set_spd_infoframe(struct drm_encoder *encoder) { struct dip_infoframe spd_if; @@ -252,8 +351,7 @@ static void intel_hdmi_mode_set(struct drm_encoder *encoder, { struct drm_device *dev = encoder->dev; 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_crtc *intel_crtc = to_intel_crtc(encoder->crtc); struct intel_hdmi *intel_hdmi = enc_to_intel_hdmi(encoder); u32 sdvox; @@ -290,7 +388,7 @@ static void intel_hdmi_mode_set(struct drm_encoder *encoder, I915_WRITE(intel_hdmi->sdvox_reg, sdvox); POSTING_READ(intel_hdmi->sdvox_reg); - intel_hdmi_set_avi_infoframe(encoder); + intel_hdmi_set_avi_infoframe(encoder, adjusted_mode); intel_hdmi_set_spd_infoframe(encoder); } @@ -427,8 +525,8 @@ intel_hdmi_detect_audio(struct drm_connector *connector) static int intel_hdmi_set_property(struct drm_connector *connector, - struct drm_property *property, - uint64_t val) + struct drm_property *property, + uint64_t val) { struct intel_hdmi *intel_hdmi = intel_attached_hdmi(connector); struct drm_i915_private *dev_priv = connector->dev->dev_private; @@ -487,6 +585,14 @@ static void intel_hdmi_destroy(struct drm_connector *connector) kfree(connector); } +static const struct drm_encoder_helper_funcs intel_hdmi_helper_funcs_hsw = { + .dpms = intel_ddi_dpms, + .mode_fixup = intel_hdmi_mode_fixup, + .prepare = intel_encoder_prepare, + .mode_set = intel_ddi_mode_set, + .commit = intel_encoder_commit, +}; + static const struct drm_encoder_helper_funcs intel_hdmi_helper_funcs = { .dpms = intel_hdmi_dpms, .mode_fixup = intel_hdmi_mode_fixup, @@ -576,24 +682,60 @@ void intel_hdmi_init(struct drm_device *dev, int sdvox_reg) intel_encoder->clone_mask = (1 << INTEL_HDMIF_CLONE_BIT); intel_hdmi->ddc_bus = GMBUS_PORT_DPD; dev_priv->hotplug_supported_mask |= HDMID_HOTPLUG_INT_STATUS; + } else if (sdvox_reg == DDI_BUF_CTL(PORT_B)) { + DRM_DEBUG_DRIVER("LPT: detected output on DDI B\n"); + intel_encoder->clone_mask = (1 << INTEL_HDMIB_CLONE_BIT); + intel_hdmi->ddc_bus = GMBUS_PORT_DPB; + intel_hdmi->ddi_port = PORT_B; + dev_priv->hotplug_supported_mask |= HDMIB_HOTPLUG_INT_STATUS; + } else if (sdvox_reg == DDI_BUF_CTL(PORT_C)) { + DRM_DEBUG_DRIVER("LPT: detected output on DDI C\n"); + intel_encoder->clone_mask = (1 << INTEL_HDMIC_CLONE_BIT); + intel_hdmi->ddc_bus = GMBUS_PORT_DPC; + intel_hdmi->ddi_port = PORT_C; + dev_priv->hotplug_supported_mask |= HDMIC_HOTPLUG_INT_STATUS; + } else if (sdvox_reg == DDI_BUF_CTL(PORT_D)) { + DRM_DEBUG_DRIVER("LPT: detected output on DDI D\n"); + intel_encoder->clone_mask = (1 << INTEL_HDMID_CLONE_BIT); + intel_hdmi->ddc_bus = GMBUS_PORT_DPD; + intel_hdmi->ddi_port = PORT_D; + dev_priv->hotplug_supported_mask |= HDMID_HOTPLUG_INT_STATUS; + } else { + /* If we got an unknown sdvox_reg, things are pretty much broken + * in a way that we should let the kernel know about it */ + BUG(); } intel_hdmi->sdvox_reg = sdvox_reg; if (!HAS_PCH_SPLIT(dev)) { - intel_hdmi->write_infoframe = i9xx_write_infoframe; + intel_hdmi->write_infoframe = g4x_write_infoframe; I915_WRITE(VIDEO_DIP_CTL, 0); } else if (IS_VALLEYVIEW(dev)) { intel_hdmi->write_infoframe = vlv_write_infoframe; for_each_pipe(i) I915_WRITE(VLV_TVIDEO_DIP_CTL(i), 0); - } else { - intel_hdmi->write_infoframe = ironlake_write_infoframe; + } else if (IS_HASWELL(dev)) { + /* FIXME: Haswell has a new set of DIP frame registers, but we are + * just doing the minimal required for HDMI to work at this stage. + */ + intel_hdmi->write_infoframe = hsw_write_infoframe; + for_each_pipe(i) + I915_WRITE(HSW_TVIDEO_DIP_CTL(i), 0); + } else if (HAS_PCH_IBX(dev)) { + intel_hdmi->write_infoframe = ibx_write_infoframe; + for_each_pipe(i) + I915_WRITE(TVIDEO_DIP_CTL(i), 0); + } else { + intel_hdmi->write_infoframe = cpt_write_infoframe; for_each_pipe(i) I915_WRITE(TVIDEO_DIP_CTL(i), 0); } - drm_encoder_helper_add(&intel_encoder->base, &intel_hdmi_helper_funcs); + if (IS_HASWELL(dev)) + drm_encoder_helper_add(&intel_encoder->base, &intel_hdmi_helper_funcs_hsw); + else + drm_encoder_helper_add(&intel_encoder->base, &intel_hdmi_helper_funcs); intel_hdmi_add_properties(intel_hdmi, connector); diff --git a/drivers/gpu/drm/i915/intel_i2c.c b/drivers/gpu/drm/i915/intel_i2c.c index e04255e..4a9707d 100644 --- a/drivers/gpu/drm/i915/intel_i2c.c +++ b/drivers/gpu/drm/i915/intel_i2c.c @@ -490,6 +490,10 @@ int intel_setup_gmbus(struct drm_device *dev) /* By default use a conservative clock rate */ bus->reg0 = port | GMBUS_RATE_100KHZ; + /* gmbus seems to be broken on i830 */ + if (IS_I830(dev)) + bus->force_bit = true; + intel_gpio_setup(bus, port); } diff --git a/drivers/gpu/drm/i915/intel_opregion.c b/drivers/gpu/drm/i915/intel_opregion.c index 34929ae..18bd0af 100644 --- a/drivers/gpu/drm/i915/intel_opregion.c +++ b/drivers/gpu/drm/i915/intel_opregion.c @@ -151,7 +151,7 @@ struct opregion_asle { 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; + struct opregion_asle __iomem *asle = dev_priv->opregion.asle; u32 max; if (!(bclp & ASLE_BCLP_VALID)) @@ -163,7 +163,7 @@ static u32 asle_set_backlight(struct drm_device *dev, u32 bclp) max = intel_panel_get_max_backlight(dev); intel_panel_set_backlight(dev, bclp * max / 255); - asle->cblv = (bclp*0x64)/0xff | ASLE_CBLV_VALID; + iowrite32((bclp*0x64)/0xff | ASLE_CBLV_VALID, &asle->cblv); return 0; } @@ -200,14 +200,14 @@ static u32 asle_set_pfit(struct drm_device *dev, u32 pfit) 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; + struct opregion_asle __iomem *asle = dev_priv->opregion.asle; u32 asle_stat = 0; u32 asle_req; if (!asle) return; - asle_req = asle->aslc & ASLE_REQ_MSK; + asle_req = ioread32(&asle->aslc) & ASLE_REQ_MSK; if (!asle_req) { DRM_DEBUG_DRIVER("non asle set request??\n"); @@ -215,31 +215,31 @@ void intel_opregion_asle_intr(struct drm_device *dev) } if (asle_req & ASLE_SET_ALS_ILLUM) - asle_stat |= asle_set_als_illum(dev, asle->alsi); + asle_stat |= asle_set_als_illum(dev, ioread32(&asle->alsi)); if (asle_req & ASLE_SET_BACKLIGHT) - asle_stat |= asle_set_backlight(dev, asle->bclp); + asle_stat |= asle_set_backlight(dev, ioread32(&asle->bclp)); if (asle_req & ASLE_SET_PFIT) - asle_stat |= asle_set_pfit(dev, asle->pfit); + asle_stat |= asle_set_pfit(dev, ioread32(&asle->pfit)); if (asle_req & ASLE_SET_PWM_FREQ) - asle_stat |= asle_set_pwm_freq(dev, asle->pfmb); + asle_stat |= asle_set_pwm_freq(dev, ioread32(&asle->pfmb)); - asle->aslc = asle_stat; + iowrite32(asle_stat, &asle->aslc); } 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; + struct opregion_asle __iomem *asle = dev_priv->opregion.asle; u32 asle_stat = 0; u32 asle_req; if (!asle) return; - asle_req = asle->aslc & ASLE_REQ_MSK; + asle_req = ioread32(&asle->aslc) & ASLE_REQ_MSK; if (!asle_req) { DRM_DEBUG_DRIVER("non asle set request??\n"); @@ -252,7 +252,7 @@ void intel_opregion_gse_intr(struct drm_device *dev) } if (asle_req & ASLE_SET_BACKLIGHT) - asle_stat |= asle_set_backlight(dev, asle->bclp); + asle_stat |= asle_set_backlight(dev, ioread32(&asle->bclp)); if (asle_req & ASLE_SET_PFIT) { DRM_DEBUG_DRIVER("Pfit is not supported\n"); @@ -264,7 +264,7 @@ void intel_opregion_gse_intr(struct drm_device *dev) asle_stat |= ASLE_PWM_FREQ_FAILED; } - asle->aslc = asle_stat; + iowrite32(asle_stat, &asle->aslc); } #define ASLE_ALS_EN (1<<0) #define ASLE_BLC_EN (1<<1) @@ -274,15 +274,16 @@ void intel_opregion_gse_intr(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; + struct opregion_asle __iomem *asle = dev_priv->opregion.asle; if (asle) { if (IS_MOBILE(dev)) intel_enable_asle(dev); - asle->tche = ASLE_ALS_EN | ASLE_BLC_EN | ASLE_PFIT_EN | - ASLE_PFMB_EN; - asle->ardy = 1; + iowrite32(ASLE_ALS_EN | ASLE_BLC_EN | ASLE_PFIT_EN | + ASLE_PFMB_EN, + &asle->tche); + iowrite32(1, &asle->ardy); } } @@ -300,7 +301,7 @@ static int intel_opregion_video_event(struct notifier_block *nb, Linux, these are handled by the dock, button and video drivers. */ - struct opregion_acpi *acpi; + struct opregion_acpi __iomem *acpi; struct acpi_bus_event *event = data; int ret = NOTIFY_OK; @@ -312,10 +313,11 @@ static int intel_opregion_video_event(struct notifier_block *nb, acpi = system_opregion->acpi; - if (event->type == 0x80 && !(acpi->cevt & 0x1)) + if (event->type == 0x80 && + (ioread32(&acpi->cevt) & 1) == 0) ret = NOTIFY_BAD; - acpi->csts = 0; + iowrite32(0, &acpi->csts); return ret; } @@ -339,6 +341,7 @@ static void intel_didl_outputs(struct drm_device *dev) struct acpi_device *acpi_dev, *acpi_cdev, *acpi_video_bus = NULL; unsigned long long device_id; acpi_status status; + u32 temp; int i = 0; handle = DEVICE_ACPI_HANDLE(&dev->pdev->dev); @@ -373,7 +376,8 @@ static void intel_didl_outputs(struct drm_device *dev) if (ACPI_SUCCESS(status)) { if (!device_id) goto blind_set; - opregion->acpi->didl[i] = (u32)(device_id & 0x0f0f); + iowrite32((u32)(device_id & 0x0f0f), + &opregion->acpi->didl[i]); i++; } } @@ -381,7 +385,7 @@ static void intel_didl_outputs(struct drm_device *dev) end: /* If fewer than 8 outputs, the list must be null terminated */ if (i < 8) - opregion->acpi->didl[i] = 0; + iowrite32(0, &opregion->acpi->didl[i]); return; blind_set: @@ -415,7 +419,9 @@ blind_set: output_type = ACPI_LVDS_OUTPUT; break; } - opregion->acpi->didl[i] |= (1<<31) | output_type | i; + temp = ioread32(&opregion->acpi->didl[i]); + iowrite32(temp | (1<<31) | output_type | i, + &opregion->acpi->didl[i]); i++; } goto end; @@ -436,8 +442,8 @@ void intel_opregion_init(struct drm_device *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; + iowrite32(0, &opregion->acpi->csts); + iowrite32(1, &opregion->acpi->drdy); system_opregion = opregion; register_acpi_notifier(&intel_opregion_notifier); @@ -456,7 +462,7 @@ void intel_opregion_fini(struct drm_device *dev) return; if (opregion->acpi) { - opregion->acpi->drdy = 0; + iowrite32(0, &opregion->acpi->drdy); system_opregion = NULL; unregister_acpi_notifier(&intel_opregion_notifier); @@ -476,8 +482,9 @@ int intel_opregion_setup(struct drm_device *dev) { struct drm_i915_private *dev_priv = dev->dev_private; struct intel_opregion *opregion = &dev_priv->opregion; - void *base; + void __iomem *base; u32 asls, mboxes; + char buf[sizeof(OPREGION_SIGNATURE)]; int err = 0; pci_read_config_dword(dev->pdev, PCI_ASLS, &asls); @@ -491,7 +498,9 @@ int intel_opregion_setup(struct drm_device *dev) if (!base) return -ENOMEM; - if (memcmp(base, OPREGION_SIGNATURE, 16)) { + memcpy_fromio(buf, base, sizeof(buf)); + + if (memcmp(buf, OPREGION_SIGNATURE, 16)) { DRM_DEBUG_DRIVER("opregion signature mismatch\n"); err = -EINVAL; goto err_out; @@ -501,7 +510,7 @@ int intel_opregion_setup(struct drm_device *dev) opregion->lid_state = base + ACPI_CLID; - mboxes = opregion->header->mboxes; + mboxes = ioread32(&opregion->header->mboxes); if (mboxes & MBOX_ACPI) { DRM_DEBUG_DRIVER("Public ACPI methods supported\n"); opregion->acpi = base + OPREGION_ACPI_OFFSET; diff --git a/drivers/gpu/drm/i915/intel_overlay.c b/drivers/gpu/drm/i915/intel_overlay.c index 80b331c..458743d 100644 --- a/drivers/gpu/drm/i915/intel_overlay.c +++ b/drivers/gpu/drm/i915/intel_overlay.c @@ -187,14 +187,14 @@ struct intel_overlay { void (*flip_tail)(struct intel_overlay *); }; -static struct overlay_registers * +static struct overlay_registers __iomem * intel_overlay_map_regs(struct intel_overlay *overlay) { drm_i915_private_t *dev_priv = overlay->dev->dev_private; - struct overlay_registers *regs; + struct overlay_registers __iomem *regs; if (OVERLAY_NEEDS_PHYSICAL(overlay->dev)) - regs = overlay->reg_bo->phys_obj->handle->vaddr; + regs = (struct overlay_registers __iomem *)overlay->reg_bo->phys_obj->handle->vaddr; else regs = io_mapping_map_wc(dev_priv->mm.gtt_mapping, overlay->reg_bo->gtt_offset); @@ -203,7 +203,7 @@ intel_overlay_map_regs(struct intel_overlay *overlay) } static void intel_overlay_unmap_regs(struct intel_overlay *overlay, - struct overlay_registers *regs) + struct overlay_registers __iomem *regs) { if (!OVERLAY_NEEDS_PHYSICAL(overlay->dev)) io_mapping_unmap(regs); @@ -215,20 +215,21 @@ static int intel_overlay_do_wait_request(struct intel_overlay *overlay, { struct drm_device *dev = overlay->dev; drm_i915_private_t *dev_priv = dev->dev_private; + struct intel_ring_buffer *ring = &dev_priv->ring[RCS]; int ret; BUG_ON(overlay->last_flip_req); - ret = i915_add_request(LP_RING(dev_priv), NULL, request); + ret = i915_add_request(ring, NULL, request); if (ret) { kfree(request); return ret; } overlay->last_flip_req = request->seqno; overlay->flip_tail = tail; - ret = i915_wait_request(LP_RING(dev_priv), overlay->last_flip_req, - true); + ret = i915_wait_request(ring, overlay->last_flip_req); if (ret) return ret; + i915_gem_retire_requests(dev); overlay->last_flip_req = 0; return 0; @@ -262,7 +263,7 @@ i830_activate_pipe_a(struct drm_device *dev) DRM_DEBUG_DRIVER("Enabling pipe A in order to enable overlay\n"); mode = drm_mode_duplicate(dev, &vesa_640x480); - drm_mode_set_crtcinfo(mode, 0); + if (!drm_crtc_helper_set_mode(&crtc->base, mode, crtc->base.x, crtc->base.y, crtc->base.fb)) @@ -287,6 +288,7 @@ static int intel_overlay_on(struct intel_overlay *overlay) { struct drm_device *dev = overlay->dev; struct drm_i915_private *dev_priv = dev->dev_private; + struct intel_ring_buffer *ring = &dev_priv->ring[RCS]; struct drm_i915_gem_request *request; int pipe_a_quirk = 0; int ret; @@ -306,17 +308,17 @@ static int intel_overlay_on(struct intel_overlay *overlay) goto out; } - ret = BEGIN_LP_RING(4); + ret = intel_ring_begin(ring, 4); if (ret) { kfree(request); goto out; } - OUT_RING(MI_OVERLAY_FLIP | MI_OVERLAY_ON); - OUT_RING(overlay->flip_addr | OFC_UPDATE); - OUT_RING(MI_WAIT_FOR_EVENT | MI_WAIT_FOR_OVERLAY_FLIP); - OUT_RING(MI_NOOP); - ADVANCE_LP_RING(); + intel_ring_emit(ring, MI_OVERLAY_FLIP | MI_OVERLAY_ON); + intel_ring_emit(ring, overlay->flip_addr | OFC_UPDATE); + intel_ring_emit(ring, MI_WAIT_FOR_EVENT | MI_WAIT_FOR_OVERLAY_FLIP); + intel_ring_emit(ring, MI_NOOP); + intel_ring_advance(ring); ret = intel_overlay_do_wait_request(overlay, request, NULL); out: @@ -332,6 +334,7 @@ static int intel_overlay_continue(struct intel_overlay *overlay, { struct drm_device *dev = overlay->dev; drm_i915_private_t *dev_priv = dev->dev_private; + struct intel_ring_buffer *ring = &dev_priv->ring[RCS]; struct drm_i915_gem_request *request; u32 flip_addr = overlay->flip_addr; u32 tmp; @@ -351,16 +354,16 @@ static int intel_overlay_continue(struct intel_overlay *overlay, if (tmp & (1 << 17)) DRM_DEBUG("overlay underrun, DOVSTA: %x\n", tmp); - ret = BEGIN_LP_RING(2); + ret = intel_ring_begin(ring, 2); if (ret) { kfree(request); return ret; } - OUT_RING(MI_OVERLAY_FLIP | MI_OVERLAY_CONTINUE); - OUT_RING(flip_addr); - ADVANCE_LP_RING(); + intel_ring_emit(ring, MI_OVERLAY_FLIP | MI_OVERLAY_CONTINUE); + intel_ring_emit(ring, flip_addr); + intel_ring_advance(ring); - ret = i915_add_request(LP_RING(dev_priv), NULL, request); + ret = i915_add_request(ring, NULL, request); if (ret) { kfree(request); return ret; @@ -401,6 +404,7 @@ static int intel_overlay_off(struct intel_overlay *overlay) { struct drm_device *dev = overlay->dev; struct drm_i915_private *dev_priv = dev->dev_private; + struct intel_ring_buffer *ring = &dev_priv->ring[RCS]; u32 flip_addr = overlay->flip_addr; struct drm_i915_gem_request *request; int ret; @@ -417,20 +421,20 @@ static int intel_overlay_off(struct intel_overlay *overlay) * of the hw. Do it in both cases */ flip_addr |= OFC_UPDATE; - ret = BEGIN_LP_RING(6); + ret = intel_ring_begin(ring, 6); if (ret) { kfree(request); return ret; } /* wait for overlay to go idle */ - OUT_RING(MI_OVERLAY_FLIP | MI_OVERLAY_CONTINUE); - OUT_RING(flip_addr); - OUT_RING(MI_WAIT_FOR_EVENT | MI_WAIT_FOR_OVERLAY_FLIP); + intel_ring_emit(ring, MI_OVERLAY_FLIP | MI_OVERLAY_CONTINUE); + intel_ring_emit(ring, flip_addr); + intel_ring_emit(ring, MI_WAIT_FOR_EVENT | MI_WAIT_FOR_OVERLAY_FLIP); /* turn 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); - ADVANCE_LP_RING(); + intel_ring_emit(ring, MI_OVERLAY_FLIP | MI_OVERLAY_OFF); + intel_ring_emit(ring, flip_addr); + intel_ring_emit(ring, MI_WAIT_FOR_EVENT | MI_WAIT_FOR_OVERLAY_FLIP); + intel_ring_advance(ring); return intel_overlay_do_wait_request(overlay, request, intel_overlay_off_tail); @@ -442,15 +446,16 @@ static int intel_overlay_recover_from_interrupt(struct intel_overlay *overlay) { struct drm_device *dev = overlay->dev; drm_i915_private_t *dev_priv = dev->dev_private; + struct intel_ring_buffer *ring = &dev_priv->ring[RCS]; int ret; if (overlay->last_flip_req == 0) return 0; - ret = i915_wait_request(LP_RING(dev_priv), overlay->last_flip_req, - true); + ret = i915_wait_request(ring, overlay->last_flip_req); if (ret) return ret; + i915_gem_retire_requests(dev); if (overlay->flip_tail) overlay->flip_tail(overlay); @@ -467,6 +472,7 @@ 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; + struct intel_ring_buffer *ring = &dev_priv->ring[RCS]; int ret; /* Only wait if there is actually an old frame to release to @@ -483,15 +489,15 @@ static int intel_overlay_release_old_vid(struct intel_overlay *overlay) if (request == NULL) return -ENOMEM; - ret = BEGIN_LP_RING(2); + ret = intel_ring_begin(ring, 2); if (ret) { kfree(request); return ret; } - OUT_RING(MI_WAIT_FOR_EVENT | MI_WAIT_FOR_OVERLAY_FLIP); - OUT_RING(MI_NOOP); - ADVANCE_LP_RING(); + intel_ring_emit(ring, MI_WAIT_FOR_EVENT | MI_WAIT_FOR_OVERLAY_FLIP); + intel_ring_emit(ring, MI_NOOP); + intel_ring_advance(ring); ret = intel_overlay_do_wait_request(overlay, request, intel_overlay_release_old_vid_tail); @@ -619,14 +625,15 @@ static const u16 uv_static_hcoeffs[N_HORIZ_UV_TAPS * N_PHASES] = { 0x3000, 0x0800, 0x3000 }; -static void update_polyphase_filter(struct overlay_registers *regs) +static void update_polyphase_filter(struct overlay_registers __iomem *regs) { - memcpy(regs->Y_HCOEFS, y_static_hcoeffs, sizeof(y_static_hcoeffs)); - memcpy(regs->UV_HCOEFS, uv_static_hcoeffs, sizeof(uv_static_hcoeffs)); + memcpy_toio(regs->Y_HCOEFS, y_static_hcoeffs, sizeof(y_static_hcoeffs)); + memcpy_toio(regs->UV_HCOEFS, uv_static_hcoeffs, + sizeof(uv_static_hcoeffs)); } static bool update_scaling_factors(struct intel_overlay *overlay, - struct overlay_registers *regs, + struct overlay_registers __iomem *regs, struct put_image_params *params) { /* fixed point with a 12 bit shift */ @@ -665,16 +672,19 @@ static bool update_scaling_factors(struct intel_overlay *overlay, overlay->old_xscale = xscale; overlay->old_yscale = yscale; - regs->YRGBSCALE = (((yscale & FRACT_MASK) << 20) | - ((xscale >> FP_SHIFT) << 16) | - ((xscale & FRACT_MASK) << 3)); + iowrite32(((yscale & FRACT_MASK) << 20) | + ((xscale >> FP_SHIFT) << 16) | + ((xscale & FRACT_MASK) << 3), + ®s->YRGBSCALE); - regs->UVSCALE = (((yscale_UV & FRACT_MASK) << 20) | - ((xscale_UV >> FP_SHIFT) << 16) | - ((xscale_UV & FRACT_MASK) << 3)); + iowrite32(((yscale_UV & FRACT_MASK) << 20) | + ((xscale_UV >> FP_SHIFT) << 16) | + ((xscale_UV & FRACT_MASK) << 3), + ®s->UVSCALE); - regs->UVSCALEV = ((((yscale >> FP_SHIFT) << 16) | - ((yscale_UV >> FP_SHIFT) << 0))); + iowrite32((((yscale >> FP_SHIFT) << 16) | + ((yscale_UV >> FP_SHIFT) << 0)), + ®s->UVSCALEV); if (scale_changed) update_polyphase_filter(regs); @@ -683,30 +693,32 @@ static bool update_scaling_factors(struct intel_overlay *overlay, } static void update_colorkey(struct intel_overlay *overlay, - struct overlay_registers *regs) + struct overlay_registers __iomem *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; + iowrite32(0, ®s->DCLRKV); + iowrite32(CLK_RGB8I_MASK | DST_KEY_ENABLE, ®s->DCLRKM); break; case 16: if (overlay->crtc->base.fb->depth == 15) { - regs->DCLRKV = RGB15_TO_COLORKEY(key); - regs->DCLRKM = CLK_RGB15_MASK | DST_KEY_ENABLE; + iowrite32(RGB15_TO_COLORKEY(key), ®s->DCLRKV); + iowrite32(CLK_RGB15_MASK | DST_KEY_ENABLE, + ®s->DCLRKM); } else { - regs->DCLRKV = RGB16_TO_COLORKEY(key); - regs->DCLRKM = CLK_RGB16_MASK | DST_KEY_ENABLE; + iowrite32(RGB16_TO_COLORKEY(key), ®s->DCLRKV); + iowrite32(CLK_RGB16_MASK | DST_KEY_ENABLE, + ®s->DCLRKM); } break; case 24: case 32: - regs->DCLRKV = key; - regs->DCLRKM = CLK_RGB24_MASK | DST_KEY_ENABLE; + iowrite32(key, ®s->DCLRKV); + iowrite32(CLK_RGB24_MASK | DST_KEY_ENABLE, ®s->DCLRKM); break; } } @@ -761,9 +773,10 @@ static int intel_overlay_do_put_image(struct intel_overlay *overlay, struct put_image_params *params) { int ret, tmp_width; - struct overlay_registers *regs; + struct overlay_registers __iomem *regs; bool scale_changed = false; struct drm_device *dev = overlay->dev; + u32 swidth, swidthsw, sheight, ostride; BUG_ON(!mutex_is_locked(&dev->struct_mutex)); BUG_ON(!mutex_is_locked(&dev->mode_config.mutex)); @@ -782,16 +795,18 @@ static int intel_overlay_do_put_image(struct intel_overlay *overlay, goto out_unpin; if (!overlay->active) { + u32 oconfig; regs = intel_overlay_map_regs(overlay); if (!regs) { ret = -ENOMEM; goto out_unpin; } - regs->OCONFIG = OCONF_CC_OUT_8BIT; + oconfig = OCONF_CC_OUT_8BIT; if (IS_GEN4(overlay->dev)) - regs->OCONFIG |= OCONF_CSC_MODE_BT709; - regs->OCONFIG |= overlay->crtc->pipe == 0 ? + oconfig |= OCONF_CSC_MODE_BT709; + oconfig |= overlay->crtc->pipe == 0 ? OCONF_PIPE_A : OCONF_PIPE_B; + iowrite32(oconfig, ®s->OCONFIG); intel_overlay_unmap_regs(overlay, regs); ret = intel_overlay_on(overlay); @@ -805,42 +820,46 @@ static int intel_overlay_do_put_image(struct intel_overlay *overlay, goto out_unpin; } - regs->DWINPOS = (params->dst_y << 16) | params->dst_x; - regs->DWINSZ = (params->dst_h << 16) | params->dst_w; + iowrite32((params->dst_y << 16) | params->dst_x, ®s->DWINPOS); + iowrite32((params->dst_h << 16) | params->dst_w, ®s->DWINSZ); if (params->format & I915_OVERLAY_YUV_PACKED) tmp_width = packed_width_bytes(params->format, params->src_w); else tmp_width = params->src_w; - regs->SWIDTH = params->src_w; - regs->SWIDTHSW = calc_swidthsw(overlay->dev, - params->offset_Y, tmp_width); - regs->SHEIGHT = params->src_h; - regs->OBUF_0Y = new_bo->gtt_offset + params->offset_Y; - regs->OSTRIDE = params->stride_Y; + swidth = params->src_w; + swidthsw = calc_swidthsw(overlay->dev, params->offset_Y, tmp_width); + sheight = params->src_h; + iowrite32(new_bo->gtt_offset + params->offset_Y, ®s->OBUF_0Y); + ostride = params->stride_Y; if (params->format & I915_OVERLAY_YUV_PLANAR) { int uv_hscale = uv_hsubsampling(params->format); int uv_vscale = uv_vsubsampling(params->format); u32 tmp_U, tmp_V; - regs->SWIDTH |= (params->src_w/uv_hscale) << 16; + swidth |= (params->src_w/uv_hscale) << 16; tmp_U = calc_swidthsw(overlay->dev, params->offset_U, params->src_w/uv_hscale); tmp_V = calc_swidthsw(overlay->dev, params->offset_V, 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 = new_bo->gtt_offset + params->offset_U; - regs->OBUF_0V = new_bo->gtt_offset + params->offset_V; - regs->OSTRIDE |= params->stride_UV << 16; + swidthsw |= max_t(u32, tmp_U, tmp_V) << 16; + sheight |= (params->src_h/uv_vscale) << 16; + iowrite32(new_bo->gtt_offset + params->offset_U, ®s->OBUF_0U); + iowrite32(new_bo->gtt_offset + params->offset_V, ®s->OBUF_0V); + ostride |= params->stride_UV << 16; } + iowrite32(swidth, ®s->SWIDTH); + iowrite32(swidthsw, ®s->SWIDTHSW); + iowrite32(sheight, ®s->SHEIGHT); + iowrite32(ostride, ®s->OSTRIDE); + scale_changed = update_scaling_factors(overlay, regs, params); update_colorkey(overlay, regs); - regs->OCMD = overlay_cmd_reg(params); + iowrite32(overlay_cmd_reg(params), ®s->OCMD); intel_overlay_unmap_regs(overlay, regs); @@ -860,7 +879,7 @@ out_unpin: int intel_overlay_switch_off(struct intel_overlay *overlay) { - struct overlay_registers *regs; + struct overlay_registers __iomem *regs; struct drm_device *dev = overlay->dev; int ret; @@ -879,7 +898,7 @@ int intel_overlay_switch_off(struct intel_overlay *overlay) return ret; regs = intel_overlay_map_regs(overlay); - regs->OCMD = 0; + iowrite32(0, ®s->OCMD); intel_overlay_unmap_regs(overlay, regs); ret = intel_overlay_off(overlay); @@ -1109,11 +1128,7 @@ int intel_overlay_put_image(struct drm_device *dev, void *data, struct put_image_params *params; int ret; - if (!dev_priv) { - DRM_ERROR("called with no initialization\n"); - return -EINVAL; - } - + /* No need to check for DRIVER_MODESET - we don't set it up then. */ overlay = dev_priv->overlay; if (!overlay) { DRM_DEBUG("userspace bug: no overlay\n"); @@ -1250,10 +1265,11 @@ out_free: } static void update_reg_attrs(struct intel_overlay *overlay, - struct overlay_registers *regs) + struct overlay_registers __iomem *regs) { - regs->OCLRC0 = (overlay->contrast << 18) | (overlay->brightness & 0xff); - regs->OCLRC1 = overlay->saturation; + iowrite32((overlay->contrast << 18) | (overlay->brightness & 0xff), + ®s->OCLRC0); + iowrite32(overlay->saturation, ®s->OCLRC1); } static bool check_gamma_bounds(u32 gamma1, u32 gamma2) @@ -1306,14 +1322,10 @@ int intel_overlay_attrs(struct drm_device *dev, void *data, struct drm_intel_overlay_attrs *attrs = data; drm_i915_private_t *dev_priv = dev->dev_private; struct intel_overlay *overlay; - struct overlay_registers *regs; + struct overlay_registers __iomem *regs; int ret; - if (!dev_priv) { - DRM_ERROR("called with no initialization\n"); - return -EINVAL; - } - + /* No need to check for DRIVER_MODESET - we don't set it up then. */ overlay = dev_priv->overlay; if (!overlay) { DRM_DEBUG("userspace bug: no overlay\n"); @@ -1396,7 +1408,7 @@ void intel_setup_overlay(struct drm_device *dev) drm_i915_private_t *dev_priv = dev->dev_private; struct intel_overlay *overlay; struct drm_i915_gem_object *reg_bo; - struct overlay_registers *regs; + struct overlay_registers __iomem *regs; int ret; if (!HAS_OVERLAY(dev)) @@ -1451,7 +1463,7 @@ void intel_setup_overlay(struct drm_device *dev) if (!regs) goto out_unpin_bo; - memset(regs, 0, sizeof(struct overlay_registers)); + memset_io(regs, 0, sizeof(struct overlay_registers)); update_polyphase_filter(regs); update_reg_attrs(overlay, regs); @@ -1499,14 +1511,17 @@ struct intel_overlay_error_state { u32 isr; }; -static struct overlay_registers * +static struct overlay_registers __iomem * intel_overlay_map_regs_atomic(struct intel_overlay *overlay) { drm_i915_private_t *dev_priv = overlay->dev->dev_private; - struct overlay_registers *regs; + struct overlay_registers __iomem *regs; if (OVERLAY_NEEDS_PHYSICAL(overlay->dev)) - regs = overlay->reg_bo->phys_obj->handle->vaddr; + /* Cast to make sparse happy, but it's wc memory anyway, so + * equivalent to the wc io mapping on X86. */ + regs = (struct overlay_registers __iomem *) + overlay->reg_bo->phys_obj->handle->vaddr; else regs = io_mapping_map_atomic_wc(dev_priv->mm.gtt_mapping, overlay->reg_bo->gtt_offset); @@ -1515,7 +1530,7 @@ intel_overlay_map_regs_atomic(struct intel_overlay *overlay) } static void intel_overlay_unmap_regs_atomic(struct intel_overlay *overlay, - struct overlay_registers *regs) + struct overlay_registers __iomem *regs) { if (!OVERLAY_NEEDS_PHYSICAL(overlay->dev)) io_mapping_unmap_atomic(regs); @@ -1540,9 +1555,9 @@ intel_overlay_capture_error_state(struct drm_device *dev) error->dovsta = I915_READ(DOVSTA); error->isr = I915_READ(ISR); if (OVERLAY_NEEDS_PHYSICAL(overlay->dev)) - error->base = (long) overlay->reg_bo->phys_obj->handle->vaddr; + error->base = (__force long)overlay->reg_bo->phys_obj->handle->vaddr; else - error->base = (long) overlay->reg_bo->gtt_offset; + error->base = overlay->reg_bo->gtt_offset; regs = intel_overlay_map_regs_atomic(overlay); if (!regs) diff --git a/drivers/gpu/drm/i915/intel_panel.c b/drivers/gpu/drm/i915/intel_panel.c index 2b2e011..2a1625d 100644 --- a/drivers/gpu/drm/i915/intel_panel.c +++ b/drivers/gpu/drm/i915/intel_panel.c @@ -368,6 +368,7 @@ int intel_panel_setup_backlight(struct drm_device *dev) else return -ENODEV; + memset(&props, 0, sizeof(props)); props.type = BACKLIGHT_RAW; props.max_brightness = intel_panel_get_max_backlight(dev); dev_priv->backlight = diff --git a/drivers/gpu/drm/i915/intel_pm.c b/drivers/gpu/drm/i915/intel_pm.c index 36940a3..8e79ff6 100644 --- a/drivers/gpu/drm/i915/intel_pm.c +++ b/drivers/gpu/drm/i915/intel_pm.c @@ -28,6 +28,8 @@ #include <linux/cpufreq.h> #include "i915_drv.h" #include "intel_drv.h" +#include "../../../platform/x86/intel_ips.h" +#include <linux/module.h> /* FBC, or Frame Buffer Compression, is a technique employed to compress the * framebuffer contents in-memory, aiming at reducing the required bandwidth @@ -524,6 +526,113 @@ out_disable: } } +static void i915_pineview_get_mem_freq(struct drm_device *dev) +{ + drm_i915_private_t *dev_priv = dev->dev_private; + u32 tmp; + + tmp = I915_READ(CLKCFG); + + switch (tmp & CLKCFG_FSB_MASK) { + case CLKCFG_FSB_533: + dev_priv->fsb_freq = 533; /* 133*4 */ + break; + case CLKCFG_FSB_800: + dev_priv->fsb_freq = 800; /* 200*4 */ + break; + case CLKCFG_FSB_667: + dev_priv->fsb_freq = 667; /* 167*4 */ + break; + case CLKCFG_FSB_400: + dev_priv->fsb_freq = 400; /* 100*4 */ + break; + } + + switch (tmp & CLKCFG_MEM_MASK) { + case CLKCFG_MEM_533: + dev_priv->mem_freq = 533; + break; + case CLKCFG_MEM_667: + dev_priv->mem_freq = 667; + break; + case CLKCFG_MEM_800: + dev_priv->mem_freq = 800; + break; + } + + /* detect pineview DDR3 setting */ + tmp = I915_READ(CSHRDDR3CTL); + dev_priv->is_ddr3 = (tmp & CSHRDDR3CTL_DDR3) ? 1 : 0; +} + +static void i915_ironlake_get_mem_freq(struct drm_device *dev) +{ + drm_i915_private_t *dev_priv = dev->dev_private; + u16 ddrpll, csipll; + + ddrpll = I915_READ16(DDRMPLL1); + csipll = I915_READ16(CSIPLL0); + + switch (ddrpll & 0xff) { + case 0xc: + dev_priv->mem_freq = 800; + break; + case 0x10: + dev_priv->mem_freq = 1066; + break; + case 0x14: + dev_priv->mem_freq = 1333; + break; + case 0x18: + dev_priv->mem_freq = 1600; + break; + default: + DRM_DEBUG_DRIVER("unknown memory frequency 0x%02x\n", + ddrpll & 0xff); + dev_priv->mem_freq = 0; + break; + } + + dev_priv->r_t = dev_priv->mem_freq; + + switch (csipll & 0x3ff) { + case 0x00c: + dev_priv->fsb_freq = 3200; + break; + case 0x00e: + dev_priv->fsb_freq = 3733; + break; + case 0x010: + dev_priv->fsb_freq = 4266; + break; + case 0x012: + dev_priv->fsb_freq = 4800; + break; + case 0x014: + dev_priv->fsb_freq = 5333; + break; + case 0x016: + dev_priv->fsb_freq = 5866; + break; + case 0x018: + dev_priv->fsb_freq = 6400; + break; + default: + DRM_DEBUG_DRIVER("unknown fsb frequency 0x%04x\n", + csipll & 0x3ff); + dev_priv->fsb_freq = 0; + break; + } + + if (dev_priv->fsb_freq == 3200) { + dev_priv->c_m = 0; + } else if (dev_priv->fsb_freq > 3200 && dev_priv->fsb_freq <= 4800) { + dev_priv->c_m = 1; + } else { + dev_priv->c_m = 2; + } +} + static const struct cxsr_latency cxsr_latency_table[] = { {1, 0, 800, 400, 3382, 33382, 3983, 33983}, /* DDR2-400 SC */ {1, 0, 800, 667, 3354, 33354, 3807, 33807}, /* DDR2-667 SC */ @@ -562,7 +671,7 @@ static const struct cxsr_latency cxsr_latency_table[] = { {0, 1, 400, 800, 6042, 36042, 6584, 36584}, /* DDR3-800 SC */ }; -const struct cxsr_latency *intel_get_cxsr_latency(int is_desktop, +static const struct cxsr_latency *intel_get_cxsr_latency(int is_desktop, int is_ddr3, int fsb, int mem) @@ -1694,8 +1803,7 @@ static void sandybridge_update_wm(struct drm_device *dev) enabled |= 2; } - /* IVB has 3 pipes */ - if (IS_IVYBRIDGE(dev) && + if ((dev_priv->num_pipe == 3) && g4x_compute_wm0(dev, 2, &sandybridge_display_wm_info, latency, &sandybridge_cursor_wm_info, latency, @@ -1775,6 +1883,33 @@ static void sandybridge_update_wm(struct drm_device *dev) cursor_wm); } +static void +haswell_update_linetime_wm(struct drm_device *dev, int pipe, + struct drm_display_mode *mode) +{ + struct drm_i915_private *dev_priv = dev->dev_private; + u32 temp; + + temp = I915_READ(PIPE_WM_LINETIME(pipe)); + temp &= ~PIPE_WM_LINETIME_MASK; + + /* The WM are computed with base on how long it takes to fill a single + * row at the given clock rate, multiplied by 8. + * */ + temp |= PIPE_WM_LINETIME_TIME( + ((mode->crtc_hdisplay * 1000) / mode->clock) * 8); + + /* IPS watermarks are only used by pipe A, and are ignored by + * pipes B and C. They are calculated similarly to the common + * linetime values, except that we are using CD clock frequency + * in MHz instead of pixel rate for the division. + * + * This is a placeholder for the IPS watermark calculation code. + */ + + I915_WRITE(PIPE_WM_LINETIME(pipe), temp); +} + static bool sandybridge_compute_sprite_wm(struct drm_device *dev, int plane, uint32_t sprite_width, int pixel_size, @@ -1970,6 +2105,15 @@ void intel_update_watermarks(struct drm_device *dev) dev_priv->display.update_wm(dev); } +void intel_update_linetime_watermarks(struct drm_device *dev, + int pipe, struct drm_display_mode *mode) +{ + struct drm_i915_private *dev_priv = dev->dev_private; + + if (dev_priv->display.update_linetime_wm) + dev_priv->display.update_linetime_wm(dev, pipe, mode); +} + void intel_update_sprite_watermarks(struct drm_device *dev, int pipe, uint32_t sprite_width, int pixel_size) { @@ -2182,6 +2326,7 @@ int intel_enable_rc6(const struct drm_device *dev) void gen6_enable_rps(struct drm_i915_private *dev_priv) { + struct intel_ring_buffer *ring; u32 rp_state_cap = I915_READ(GEN6_RP_STATE_CAP); u32 gt_perf_status = I915_READ(GEN6_GT_PERF_STATUS); u32 pcu_mbox, rc6_mask = 0; @@ -2216,8 +2361,8 @@ void gen6_enable_rps(struct drm_i915_private *dev_priv) I915_WRITE(GEN6_RC_EVALUATION_INTERVAL, 125000); I915_WRITE(GEN6_RC_IDLE_HYSTERSIS, 25); - for (i = 0; i < I915_NUM_RINGS; i++) - I915_WRITE(RING_MAX_IDLE(dev_priv->ring[i].mmio_base), 10); + for_each_ring(ring, dev_priv, i) + I915_WRITE(RING_MAX_IDLE(ring->mmio_base), 10); I915_WRITE(GEN6_RC_SLEEP, 0); I915_WRITE(GEN6_RC1e_THRESHOLD, 1000); @@ -2436,6 +2581,7 @@ static int ironlake_setup_rc6(struct drm_device *dev) void ironlake_enable_rc6(struct drm_device *dev) { struct drm_i915_private *dev_priv = dev->dev_private; + struct intel_ring_buffer *ring = &dev_priv->ring[RCS]; int ret; /* rc6 disabled by default due to repeated reports of hanging during @@ -2455,31 +2601,31 @@ void ironlake_enable_rc6(struct drm_device *dev) * GPU can automatically power down the render unit if given a page * to save state. */ - ret = BEGIN_LP_RING(6); + ret = intel_ring_begin(ring, 6); if (ret) { ironlake_teardown_rc6(dev); mutex_unlock(&dev->struct_mutex); return; } - OUT_RING(MI_SUSPEND_FLUSH | MI_SUSPEND_FLUSH_EN); - OUT_RING(MI_SET_CONTEXT); - OUT_RING(dev_priv->renderctx->gtt_offset | - MI_MM_SPACE_GTT | - MI_SAVE_EXT_STATE_EN | - MI_RESTORE_EXT_STATE_EN | - MI_RESTORE_INHIBIT); - OUT_RING(MI_SUSPEND_FLUSH); - OUT_RING(MI_NOOP); - OUT_RING(MI_FLUSH); - ADVANCE_LP_RING(); + intel_ring_emit(ring, MI_SUSPEND_FLUSH | MI_SUSPEND_FLUSH_EN); + intel_ring_emit(ring, MI_SET_CONTEXT); + intel_ring_emit(ring, dev_priv->renderctx->gtt_offset | + MI_MM_SPACE_GTT | + MI_SAVE_EXT_STATE_EN | + MI_RESTORE_EXT_STATE_EN | + MI_RESTORE_INHIBIT); + intel_ring_emit(ring, MI_SUSPEND_FLUSH); + intel_ring_emit(ring, MI_NOOP); + intel_ring_emit(ring, MI_FLUSH); + intel_ring_advance(ring); /* * Wait for the command parser to advance past MI_SET_CONTEXT. The HW * does an implicit flush, combined with MI_FLUSH above, it should be * safe to assume that renderctx is valid */ - ret = intel_wait_ring_idle(LP_RING(dev_priv)); + ret = intel_wait_ring_idle(ring); if (ret) { DRM_ERROR("failed to enable ironlake power power savings\n"); ironlake_teardown_rc6(dev); @@ -2507,6 +2653,485 @@ static unsigned long intel_pxfreq(u32 vidfreq) return freq; } +static const struct cparams { + u16 i; + u16 t; + u16 m; + u16 c; +} cparams[] = { + { 1, 1333, 301, 28664 }, + { 1, 1066, 294, 24460 }, + { 1, 800, 294, 25192 }, + { 0, 1333, 276, 27605 }, + { 0, 1066, 276, 27605 }, + { 0, 800, 231, 23784 }, +}; + +unsigned long i915_chipset_val(struct drm_i915_private *dev_priv) +{ + u64 total_count, diff, ret; + u32 count1, count2, count3, m = 0, c = 0; + unsigned long now = jiffies_to_msecs(jiffies), diff1; + int i; + + diff1 = now - dev_priv->last_time1; + + /* Prevent division-by-zero if we are asking too fast. + * Also, we don't get interesting results if we are polling + * faster than once in 10ms, so just return the saved value + * in such cases. + */ + if (diff1 <= 10) + return dev_priv->chipset_power; + + count1 = I915_READ(DMIEC); + count2 = I915_READ(DDREC); + count3 = I915_READ(CSIEC); + + total_count = count1 + count2 + count3; + + /* FIXME: handle per-counter overflow */ + if (total_count < dev_priv->last_count1) { + diff = ~0UL - dev_priv->last_count1; + diff += total_count; + } else { + diff = total_count - dev_priv->last_count1; + } + + for (i = 0; i < ARRAY_SIZE(cparams); i++) { + if (cparams[i].i == dev_priv->c_m && + cparams[i].t == dev_priv->r_t) { + m = cparams[i].m; + c = cparams[i].c; + break; + } + } + + diff = div_u64(diff, diff1); + ret = ((m * diff) + c); + ret = div_u64(ret, 10); + + dev_priv->last_count1 = total_count; + dev_priv->last_time1 = now; + + dev_priv->chipset_power = ret; + + return ret; +} + +unsigned long i915_mch_val(struct drm_i915_private *dev_priv) +{ + unsigned long m, x, b; + u32 tsfs; + + tsfs = I915_READ(TSFS); + + m = ((tsfs & TSFS_SLOPE_MASK) >> TSFS_SLOPE_SHIFT); + x = I915_READ8(TR1); + + b = tsfs & TSFS_INTR_MASK; + + return ((m * x) / 127) - b; +} + +static u16 pvid_to_extvid(struct drm_i915_private *dev_priv, u8 pxvid) +{ + static const struct v_table { + u16 vd; /* in .1 mil */ + u16 vm; /* in .1 mil */ + } v_table[] = { + { 0, 0, }, + { 375, 0, }, + { 500, 0, }, + { 625, 0, }, + { 750, 0, }, + { 875, 0, }, + { 1000, 0, }, + { 1125, 0, }, + { 4125, 3000, }, + { 4125, 3000, }, + { 4125, 3000, }, + { 4125, 3000, }, + { 4125, 3000, }, + { 4125, 3000, }, + { 4125, 3000, }, + { 4125, 3000, }, + { 4125, 3000, }, + { 4125, 3000, }, + { 4125, 3000, }, + { 4125, 3000, }, + { 4125, 3000, }, + { 4125, 3000, }, + { 4125, 3000, }, + { 4125, 3000, }, + { 4125, 3000, }, + { 4125, 3000, }, + { 4125, 3000, }, + { 4125, 3000, }, + { 4125, 3000, }, + { 4125, 3000, }, + { 4125, 3000, }, + { 4125, 3000, }, + { 4250, 3125, }, + { 4375, 3250, }, + { 4500, 3375, }, + { 4625, 3500, }, + { 4750, 3625, }, + { 4875, 3750, }, + { 5000, 3875, }, + { 5125, 4000, }, + { 5250, 4125, }, + { 5375, 4250, }, + { 5500, 4375, }, + { 5625, 4500, }, + { 5750, 4625, }, + { 5875, 4750, }, + { 6000, 4875, }, + { 6125, 5000, }, + { 6250, 5125, }, + { 6375, 5250, }, + { 6500, 5375, }, + { 6625, 5500, }, + { 6750, 5625, }, + { 6875, 5750, }, + { 7000, 5875, }, + { 7125, 6000, }, + { 7250, 6125, }, + { 7375, 6250, }, + { 7500, 6375, }, + { 7625, 6500, }, + { 7750, 6625, }, + { 7875, 6750, }, + { 8000, 6875, }, + { 8125, 7000, }, + { 8250, 7125, }, + { 8375, 7250, }, + { 8500, 7375, }, + { 8625, 7500, }, + { 8750, 7625, }, + { 8875, 7750, }, + { 9000, 7875, }, + { 9125, 8000, }, + { 9250, 8125, }, + { 9375, 8250, }, + { 9500, 8375, }, + { 9625, 8500, }, + { 9750, 8625, }, + { 9875, 8750, }, + { 10000, 8875, }, + { 10125, 9000, }, + { 10250, 9125, }, + { 10375, 9250, }, + { 10500, 9375, }, + { 10625, 9500, }, + { 10750, 9625, }, + { 10875, 9750, }, + { 11000, 9875, }, + { 11125, 10000, }, + { 11250, 10125, }, + { 11375, 10250, }, + { 11500, 10375, }, + { 11625, 10500, }, + { 11750, 10625, }, + { 11875, 10750, }, + { 12000, 10875, }, + { 12125, 11000, }, + { 12250, 11125, }, + { 12375, 11250, }, + { 12500, 11375, }, + { 12625, 11500, }, + { 12750, 11625, }, + { 12875, 11750, }, + { 13000, 11875, }, + { 13125, 12000, }, + { 13250, 12125, }, + { 13375, 12250, }, + { 13500, 12375, }, + { 13625, 12500, }, + { 13750, 12625, }, + { 13875, 12750, }, + { 14000, 12875, }, + { 14125, 13000, }, + { 14250, 13125, }, + { 14375, 13250, }, + { 14500, 13375, }, + { 14625, 13500, }, + { 14750, 13625, }, + { 14875, 13750, }, + { 15000, 13875, }, + { 15125, 14000, }, + { 15250, 14125, }, + { 15375, 14250, }, + { 15500, 14375, }, + { 15625, 14500, }, + { 15750, 14625, }, + { 15875, 14750, }, + { 16000, 14875, }, + { 16125, 15000, }, + }; + if (dev_priv->info->is_mobile) + return v_table[pxvid].vm; + else + return v_table[pxvid].vd; +} + +void i915_update_gfx_val(struct drm_i915_private *dev_priv) +{ + struct timespec now, diff1; + u64 diff; + unsigned long diffms; + u32 count; + + if (dev_priv->info->gen != 5) + return; + + getrawmonotonic(&now); + diff1 = timespec_sub(now, dev_priv->last_time2); + + /* Don't divide by 0 */ + diffms = diff1.tv_sec * 1000 + diff1.tv_nsec / 1000000; + if (!diffms) + return; + + count = I915_READ(GFXEC); + + if (count < dev_priv->last_count2) { + diff = ~0UL - dev_priv->last_count2; + diff += count; + } else { + diff = count - dev_priv->last_count2; + } + + dev_priv->last_count2 = count; + dev_priv->last_time2 = now; + + /* More magic constants... */ + diff = diff * 1181; + diff = div_u64(diff, diffms * 10); + dev_priv->gfx_power = diff; +} + +unsigned long i915_gfx_val(struct drm_i915_private *dev_priv) +{ + unsigned long t, corr, state1, corr2, state2; + u32 pxvid, ext_v; + + pxvid = I915_READ(PXVFREQ_BASE + (dev_priv->cur_delay * 4)); + pxvid = (pxvid >> 24) & 0x7f; + ext_v = pvid_to_extvid(dev_priv, pxvid); + + state1 = ext_v; + + t = i915_mch_val(dev_priv); + + /* Revel in the empirically derived constants */ + + /* Correction factor in 1/100000 units */ + if (t > 80) + corr = ((t * 2349) + 135940); + else if (t >= 50) + corr = ((t * 964) + 29317); + else /* < 50 */ + corr = ((t * 301) + 1004); + + corr = corr * ((150142 * state1) / 10000 - 78642); + corr /= 100000; + corr2 = (corr * dev_priv->corr); + + state2 = (corr2 * state1) / 10000; + state2 /= 100; /* convert to mW */ + + i915_update_gfx_val(dev_priv); + + return dev_priv->gfx_power + state2; +} + +/* Global for IPS driver to get at the current i915 device */ +static struct drm_i915_private *i915_mch_dev; +/* + * Lock protecting IPS related data structures + * - i915_mch_dev + * - dev_priv->max_delay + * - dev_priv->min_delay + * - dev_priv->fmax + * - dev_priv->gpu_busy + */ +static DEFINE_SPINLOCK(mchdev_lock); + +/** + * i915_read_mch_val - return value for IPS use + * + * Calculate and return a value for the IPS driver to use when deciding whether + * we have thermal and power headroom to increase CPU or GPU power budget. + */ +unsigned long i915_read_mch_val(void) +{ + struct drm_i915_private *dev_priv; + unsigned long chipset_val, graphics_val, ret = 0; + + spin_lock(&mchdev_lock); + if (!i915_mch_dev) + goto out_unlock; + dev_priv = i915_mch_dev; + + chipset_val = i915_chipset_val(dev_priv); + graphics_val = i915_gfx_val(dev_priv); + + ret = chipset_val + graphics_val; + +out_unlock: + spin_unlock(&mchdev_lock); + + return ret; +} +EXPORT_SYMBOL_GPL(i915_read_mch_val); + +/** + * i915_gpu_raise - raise GPU frequency limit + * + * Raise the limit; IPS indicates we have thermal headroom. + */ +bool i915_gpu_raise(void) +{ + struct drm_i915_private *dev_priv; + bool ret = true; + + spin_lock(&mchdev_lock); + if (!i915_mch_dev) { + ret = false; + goto out_unlock; + } + dev_priv = i915_mch_dev; + + if (dev_priv->max_delay > dev_priv->fmax) + dev_priv->max_delay--; + +out_unlock: + spin_unlock(&mchdev_lock); + + return ret; +} +EXPORT_SYMBOL_GPL(i915_gpu_raise); + +/** + * i915_gpu_lower - lower GPU frequency limit + * + * IPS indicates we're close to a thermal limit, so throttle back the GPU + * frequency maximum. + */ +bool i915_gpu_lower(void) +{ + struct drm_i915_private *dev_priv; + bool ret = true; + + spin_lock(&mchdev_lock); + if (!i915_mch_dev) { + ret = false; + goto out_unlock; + } + dev_priv = i915_mch_dev; + + if (dev_priv->max_delay < dev_priv->min_delay) + dev_priv->max_delay++; + +out_unlock: + spin_unlock(&mchdev_lock); + + return ret; +} +EXPORT_SYMBOL_GPL(i915_gpu_lower); + +/** + * i915_gpu_busy - indicate GPU business to IPS + * + * Tell the IPS driver whether or not the GPU is busy. + */ +bool i915_gpu_busy(void) +{ + struct drm_i915_private *dev_priv; + bool ret = false; + + spin_lock(&mchdev_lock); + if (!i915_mch_dev) + goto out_unlock; + dev_priv = i915_mch_dev; + + ret = dev_priv->busy; + +out_unlock: + spin_unlock(&mchdev_lock); + + return ret; +} +EXPORT_SYMBOL_GPL(i915_gpu_busy); + +/** + * i915_gpu_turbo_disable - disable graphics turbo + * + * Disable graphics turbo by resetting the max frequency and setting the + * current frequency to the default. + */ +bool i915_gpu_turbo_disable(void) +{ + struct drm_i915_private *dev_priv; + bool ret = true; + + spin_lock(&mchdev_lock); + if (!i915_mch_dev) { + ret = false; + goto out_unlock; + } + dev_priv = i915_mch_dev; + + dev_priv->max_delay = dev_priv->fstart; + + if (!ironlake_set_drps(dev_priv->dev, dev_priv->fstart)) + ret = false; + +out_unlock: + spin_unlock(&mchdev_lock); + + return ret; +} +EXPORT_SYMBOL_GPL(i915_gpu_turbo_disable); + +/** + * Tells the intel_ips driver that the i915 driver is now loaded, if + * IPS got loaded first. + * + * This awkward dance is so that neither module has to depend on the + * other in order for IPS to do the appropriate communication of + * GPU turbo limits to i915. + */ +static void +ips_ping_for_i915_load(void) +{ + void (*link)(void); + + link = symbol_get(ips_link_to_i915_driver); + if (link) { + link(); + symbol_put(ips_link_to_i915_driver); + } +} + +void intel_gpu_ips_init(struct drm_i915_private *dev_priv) +{ + spin_lock(&mchdev_lock); + i915_mch_dev = dev_priv; + dev_priv->mchdev_lock = &mchdev_lock; + spin_unlock(&mchdev_lock); + + ips_ping_for_i915_load(); +} + +void intel_gpu_ips_teardown(void) +{ + spin_lock(&mchdev_lock); + i915_mch_dev = NULL; + spin_unlock(&mchdev_lock); +} + void intel_init_emon(struct drm_device *dev) { struct drm_i915_private *dev_priv = dev->dev_private; @@ -2663,9 +3288,8 @@ static void gen6_init_clock_gating(struct drm_device *dev) I915_WRITE(WM2_LP_ILK, 0); I915_WRITE(WM1_LP_ILK, 0); - /* clear masked bit */ I915_WRITE(CACHE_MODE_0, - CM0_STC_EVICT_DISABLE_LRA_SNB << CM0_MASK_SHIFT); + _MASKED_BIT_DISABLE(CM0_STC_EVICT_DISABLE_LRA_SNB)); I915_WRITE(GEN6_UCGCTL1, I915_READ(GEN6_UCGCTL1) | @@ -2776,6 +3400,10 @@ static void ivybridge_init_clock_gating(struct drm_device *dev) } gen7_setup_fixed_func_scheduler(dev_priv); + + /* WaDisable4x2SubspanOptimization */ + I915_WRITE(CACHE_MODE_1, + _MASKED_BIT_ENABLE(PIXEL_SUBSPAN_COLLECT_OPT_DISABLE)); } static void valleyview_init_clock_gating(struct drm_device *dev) @@ -2821,9 +3449,8 @@ static void valleyview_init_clock_gating(struct drm_device *dev) intel_flush_display_plane(dev_priv, pipe); } - I915_WRITE(CACHE_MODE_1, I915_READ(CACHE_MODE_1) | - (PIXEL_SUBSPAN_COLLECT_OPT_DISABLE << 16) | - PIXEL_SUBSPAN_COLLECT_OPT_DISABLE); + I915_WRITE(CACHE_MODE_1, + _MASKED_BIT_ENABLE(PIXEL_SUBSPAN_COLLECT_OPT_DISABLE)); } static void g4x_init_clock_gating(struct drm_device *dev) @@ -2875,6 +3502,9 @@ static void gen3_init_clock_gating(struct drm_device *dev) dstate |= DSTATE_PLL_D3_OFF | DSTATE_GFX_CLOCK_GATING | DSTATE_DOT_CLOCK_GATING; I915_WRITE(D_STATE, dstate); + + if (IS_PINEVIEW(dev)) + I915_WRITE(ECOSKPD, _MASKED_BIT_ENABLE(ECO_GATING_CX_ONLY)); } static void i85x_init_clock_gating(struct drm_device *dev) @@ -2931,6 +3561,72 @@ void intel_init_clock_gating(struct drm_device *dev) dev_priv->display.init_pch_clock_gating(dev); } +static void gen6_sanitize_pm(struct drm_device *dev) +{ + struct drm_i915_private *dev_priv = dev->dev_private; + u32 limits, delay, old; + + gen6_gt_force_wake_get(dev_priv); + + old = limits = I915_READ(GEN6_RP_INTERRUPT_LIMITS); + /* Make sure we continue to get interrupts + * until we hit the minimum or maximum frequencies. + */ + limits &= ~(0x3f << 16 | 0x3f << 24); + delay = dev_priv->cur_delay; + if (delay < dev_priv->max_delay) + limits |= (dev_priv->max_delay & 0x3f) << 24; + if (delay > dev_priv->min_delay) + limits |= (dev_priv->min_delay & 0x3f) << 16; + + if (old != limits) { + DRM_ERROR("Power management discrepancy: GEN6_RP_INTERRUPT_LIMITS expected %08x, was %08x\n", + limits, old); + I915_WRITE(GEN6_RP_INTERRUPT_LIMITS, limits); + } + + gen6_gt_force_wake_put(dev_priv); +} + +void intel_sanitize_pm(struct drm_device *dev) +{ + struct drm_i915_private *dev_priv = dev->dev_private; + + if (dev_priv->display.sanitize_pm) + dev_priv->display.sanitize_pm(dev); +} + +/* Starting with Haswell, we have different power wells for + * different parts of the GPU. This attempts to enable them all. + */ +void intel_init_power_wells(struct drm_device *dev) +{ + struct drm_i915_private *dev_priv = dev->dev_private; + unsigned long power_wells[] = { + HSW_PWR_WELL_CTL1, + HSW_PWR_WELL_CTL2, + HSW_PWR_WELL_CTL4 + }; + int i; + + if (!IS_HASWELL(dev)) + return; + + mutex_lock(&dev->struct_mutex); + + for (i = 0; i < ARRAY_SIZE(power_wells); i++) { + int well = I915_READ(power_wells[i]); + + if ((well & HSW_PWR_WELL_STATE) == 0) { + I915_WRITE(power_wells[i], well & HSW_PWR_WELL_ENABLE); + if (wait_for(I915_READ(power_wells[i] & HSW_PWR_WELL_STATE), 20)) + DRM_ERROR("Error enabling power well %lx\n", power_wells[i]); + } + } + + mutex_unlock(&dev->struct_mutex); +} + /* Set up chip specific power management-related functions */ void intel_init_pm(struct drm_device *dev) { @@ -2953,6 +3649,12 @@ void intel_init_pm(struct drm_device *dev) /* 855GM needs testing */ } + /* For cxsr */ + if (IS_PINEVIEW(dev)) + i915_pineview_get_mem_freq(dev); + else if (IS_GEN5(dev)) + i915_ironlake_get_mem_freq(dev); + /* For FIFO watermark updates */ if (HAS_PCH_SPLIT(dev)) { dev_priv->display.force_wake_get = __gen6_gt_force_wake_get; @@ -3007,6 +3709,7 @@ void intel_init_pm(struct drm_device *dev) dev_priv->display.update_wm = NULL; } dev_priv->display.init_clock_gating = gen6_init_clock_gating; + dev_priv->display.sanitize_pm = gen6_sanitize_pm; } else if (IS_IVYBRIDGE(dev)) { /* FIXME: detect B0+ stepping and use auto training */ if (SNB_READ_WM0_LATENCY()) { @@ -3018,6 +3721,19 @@ void intel_init_pm(struct drm_device *dev) dev_priv->display.update_wm = NULL; } dev_priv->display.init_clock_gating = ivybridge_init_clock_gating; + dev_priv->display.sanitize_pm = gen6_sanitize_pm; + } else if (IS_HASWELL(dev)) { + if (SNB_READ_WM0_LATENCY()) { + dev_priv->display.update_wm = sandybridge_update_wm; + dev_priv->display.update_sprite_wm = sandybridge_update_sprite_wm; + dev_priv->display.update_linetime_wm = haswell_update_linetime_wm; + } else { + DRM_DEBUG_KMS("Failed to read display plane latency. " + "Disable CxSR\n"); + dev_priv->display.update_wm = NULL; + } + dev_priv->display.init_clock_gating = ivybridge_init_clock_gating; + dev_priv->display.sanitize_pm = gen6_sanitize_pm; } else dev_priv->display.update_wm = NULL; } else if (IS_VALLEYVIEW(dev)) { @@ -3071,5 +3787,10 @@ void intel_init_pm(struct drm_device *dev) else dev_priv->display.get_fifo_size = i830_get_fifo_size; } + + /* We attempt to init the necessary power wells early in the initialization + * time, so the subsystems that expect power to be enabled can work. + */ + intel_init_power_wells(dev); } diff --git a/drivers/gpu/drm/i915/intel_ringbuffer.c b/drivers/gpu/drm/i915/intel_ringbuffer.c index b5ef7c1..b59b6d5 100644 --- a/drivers/gpu/drm/i915/intel_ringbuffer.c +++ b/drivers/gpu/drm/i915/intel_ringbuffer.c @@ -401,12 +401,11 @@ static int init_render_ring(struct intel_ring_buffer *ring) int ret = init_ring_common(ring); if (INTEL_INFO(dev)->gen > 3) { - int mode = VS_TIMER_DISPATCH << 16 | VS_TIMER_DISPATCH; - I915_WRITE(MI_MODE, mode); + I915_WRITE(MI_MODE, _MASKED_BIT_ENABLE(VS_TIMER_DISPATCH)); if (IS_GEN7(dev)) I915_WRITE(GFX_MODE_GEN7, - GFX_MODE_DISABLE(GFX_TLB_INVALIDATE_ALWAYS) | - GFX_MODE_ENABLE(GFX_REPLAY_MODE)); + _MASKED_BIT_DISABLE(GFX_TLB_INVALIDATE_ALWAYS) | + _MASKED_BIT_ENABLE(GFX_REPLAY_MODE)); } if (INTEL_INFO(dev)->gen >= 5) { @@ -415,19 +414,19 @@ static int init_render_ring(struct intel_ring_buffer *ring) return ret; } - if (INTEL_INFO(dev)->gen >= 6) { - I915_WRITE(INSTPM, - INSTPM_FORCE_ORDERING << 16 | INSTPM_FORCE_ORDERING); - + if (IS_GEN6(dev)) { /* From the Sandybridge PRM, volume 1 part 3, page 24: * "If this bit is set, STCunit will have LRA as replacement * policy. [...] This bit must be reset. LRA replacement * policy is not supported." */ I915_WRITE(CACHE_MODE_0, - CM0_STC_EVICT_DISABLE_LRA_SNB << CM0_MASK_SHIFT); + _MASKED_BIT_DISABLE(CM0_STC_EVICT_DISABLE_LRA_SNB)); } + if (INTEL_INFO(dev)->gen >= 6) + I915_WRITE(INSTPM, _MASKED_BIT_ENABLE(INSTPM_FORCE_ORDERING)); + return ret; } @@ -621,17 +620,18 @@ gen5_ring_get_irq(struct intel_ring_buffer *ring) { struct drm_device *dev = ring->dev; drm_i915_private_t *dev_priv = dev->dev_private; + unsigned long flags; if (!dev->irq_enabled) return false; - spin_lock(&ring->irq_lock); + spin_lock_irqsave(&dev_priv->irq_lock, flags); if (ring->irq_refcount++ == 0) { dev_priv->gt_irq_mask &= ~ring->irq_enable_mask; I915_WRITE(GTIMR, dev_priv->gt_irq_mask); POSTING_READ(GTIMR); } - spin_unlock(&ring->irq_lock); + spin_unlock_irqrestore(&dev_priv->irq_lock, flags); return true; } @@ -641,14 +641,15 @@ gen5_ring_put_irq(struct intel_ring_buffer *ring) { struct drm_device *dev = ring->dev; drm_i915_private_t *dev_priv = dev->dev_private; + unsigned long flags; - spin_lock(&ring->irq_lock); + spin_lock_irqsave(&dev_priv->irq_lock, flags); if (--ring->irq_refcount == 0) { dev_priv->gt_irq_mask |= ring->irq_enable_mask; I915_WRITE(GTIMR, dev_priv->gt_irq_mask); POSTING_READ(GTIMR); } - spin_unlock(&ring->irq_lock); + spin_unlock_irqrestore(&dev_priv->irq_lock, flags); } static bool @@ -656,17 +657,18 @@ i9xx_ring_get_irq(struct intel_ring_buffer *ring) { struct drm_device *dev = ring->dev; drm_i915_private_t *dev_priv = dev->dev_private; + unsigned long flags; if (!dev->irq_enabled) return false; - spin_lock(&ring->irq_lock); + spin_lock_irqsave(&dev_priv->irq_lock, flags); if (ring->irq_refcount++ == 0) { dev_priv->irq_mask &= ~ring->irq_enable_mask; I915_WRITE(IMR, dev_priv->irq_mask); POSTING_READ(IMR); } - spin_unlock(&ring->irq_lock); + spin_unlock_irqrestore(&dev_priv->irq_lock, flags); return true; } @@ -676,14 +678,52 @@ i9xx_ring_put_irq(struct intel_ring_buffer *ring) { struct drm_device *dev = ring->dev; drm_i915_private_t *dev_priv = dev->dev_private; + unsigned long flags; - spin_lock(&ring->irq_lock); + spin_lock_irqsave(&dev_priv->irq_lock, flags); if (--ring->irq_refcount == 0) { dev_priv->irq_mask |= ring->irq_enable_mask; I915_WRITE(IMR, dev_priv->irq_mask); POSTING_READ(IMR); } - spin_unlock(&ring->irq_lock); + spin_unlock_irqrestore(&dev_priv->irq_lock, flags); +} + +static bool +i8xx_ring_get_irq(struct intel_ring_buffer *ring) +{ + struct drm_device *dev = ring->dev; + drm_i915_private_t *dev_priv = dev->dev_private; + unsigned long flags; + + if (!dev->irq_enabled) + return false; + + spin_lock_irqsave(&dev_priv->irq_lock, flags); + if (ring->irq_refcount++ == 0) { + dev_priv->irq_mask &= ~ring->irq_enable_mask; + I915_WRITE16(IMR, dev_priv->irq_mask); + POSTING_READ16(IMR); + } + spin_unlock_irqrestore(&dev_priv->irq_lock, flags); + + return true; +} + +static void +i8xx_ring_put_irq(struct intel_ring_buffer *ring) +{ + struct drm_device *dev = ring->dev; + drm_i915_private_t *dev_priv = dev->dev_private; + unsigned long flags; + + spin_lock_irqsave(&dev_priv->irq_lock, flags); + if (--ring->irq_refcount == 0) { + dev_priv->irq_mask |= ring->irq_enable_mask; + I915_WRITE16(IMR, dev_priv->irq_mask); + POSTING_READ16(IMR); + } + spin_unlock_irqrestore(&dev_priv->irq_lock, flags); } void intel_ring_setup_status_page(struct intel_ring_buffer *ring) @@ -762,6 +802,7 @@ gen6_ring_get_irq(struct intel_ring_buffer *ring) { struct drm_device *dev = ring->dev; drm_i915_private_t *dev_priv = dev->dev_private; + unsigned long flags; if (!dev->irq_enabled) return false; @@ -771,14 +812,14 @@ gen6_ring_get_irq(struct intel_ring_buffer *ring) * blt/bsd rings on ivb. */ gen6_gt_force_wake_get(dev_priv); - spin_lock(&ring->irq_lock); + spin_lock_irqsave(&dev_priv->irq_lock, flags); if (ring->irq_refcount++ == 0) { I915_WRITE_IMR(ring, ~ring->irq_enable_mask); dev_priv->gt_irq_mask &= ~ring->irq_enable_mask; I915_WRITE(GTIMR, dev_priv->gt_irq_mask); POSTING_READ(GTIMR); } - spin_unlock(&ring->irq_lock); + spin_unlock_irqrestore(&dev_priv->irq_lock, flags); return true; } @@ -788,15 +829,16 @@ gen6_ring_put_irq(struct intel_ring_buffer *ring) { struct drm_device *dev = ring->dev; drm_i915_private_t *dev_priv = dev->dev_private; + unsigned long flags; - spin_lock(&ring->irq_lock); + spin_lock_irqsave(&dev_priv->irq_lock, flags); if (--ring->irq_refcount == 0) { I915_WRITE_IMR(ring, ~0); dev_priv->gt_irq_mask |= ring->irq_enable_mask; I915_WRITE(GTIMR, dev_priv->gt_irq_mask); POSTING_READ(GTIMR); } - spin_unlock(&ring->irq_lock); + spin_unlock_irqrestore(&dev_priv->irq_lock, flags); gen6_gt_force_wake_put(dev_priv); } @@ -858,7 +900,6 @@ i915_dispatch_execbuffer(struct intel_ring_buffer *ring, static void cleanup_status_page(struct intel_ring_buffer *ring) { - drm_i915_private_t *dev_priv = ring->dev->dev_private; struct drm_i915_gem_object *obj; obj = ring->status_page.obj; @@ -869,14 +910,11 @@ static void cleanup_status_page(struct intel_ring_buffer *ring) i915_gem_object_unpin(obj); drm_gem_object_unreference(&obj->base); ring->status_page.obj = NULL; - - memset(&dev_priv->hws_map, 0, sizeof(dev_priv->hws_map)); } static int init_status_page(struct intel_ring_buffer *ring) { struct drm_device *dev = ring->dev; - drm_i915_private_t *dev_priv = dev->dev_private; struct drm_i915_gem_object *obj; int ret; @@ -897,7 +935,6 @@ static int init_status_page(struct intel_ring_buffer *ring) ring->status_page.gfx_addr = obj->gtt_offset; ring->status_page.page_addr = kmap(obj->pages[0]); if (ring->status_page.page_addr == NULL) { - memset(&dev_priv->hws_map, 0, sizeof(dev_priv->hws_map)); goto err_unpin; } ring->status_page.obj = obj; @@ -930,7 +967,6 @@ static int intel_init_ring_buffer(struct drm_device *dev, ring->size = 32 * PAGE_SIZE; init_waitqueue_head(&ring->irq_queue); - spin_lock_init(&ring->irq_lock); if (I915_NEED_GFX_HWS(dev)) { ret = init_status_page(ring); @@ -951,20 +987,14 @@ static int intel_init_ring_buffer(struct drm_device *dev, if (ret) goto err_unref; - ring->map.size = ring->size; - ring->map.offset = dev->agp->base + obj->gtt_offset; - ring->map.type = 0; - ring->map.flags = 0; - ring->map.mtrr = 0; - - drm_core_ioremap_wc(&ring->map, dev); - if (ring->map.handle == NULL) { + ring->virtual_start = ioremap_wc(dev->agp->base + obj->gtt_offset, + ring->size); + if (ring->virtual_start == NULL) { DRM_ERROR("Failed to map ringbuffer.\n"); ret = -EINVAL; goto err_unpin; } - ring->virtual_start = ring->map.handle; ret = ring->init(ring); if (ret) goto err_unmap; @@ -980,7 +1010,7 @@ static int intel_init_ring_buffer(struct drm_device *dev, return 0; err_unmap: - drm_core_ioremapfree(&ring->map, dev); + iounmap(ring->virtual_start); err_unpin: i915_gem_object_unpin(obj); err_unref: @@ -1008,7 +1038,7 @@ void intel_cleanup_ring_buffer(struct intel_ring_buffer *ring) I915_WRITE_CTL(ring, 0); - drm_core_ioremapfree(&ring->map, ring->dev); + iounmap(ring->virtual_start); i915_gem_object_unpin(ring->obj); drm_gem_object_unreference(&ring->obj->base); @@ -1022,7 +1052,7 @@ void intel_cleanup_ring_buffer(struct intel_ring_buffer *ring) static int intel_wrap_ring_buffer(struct intel_ring_buffer *ring) { - unsigned int *virt; + uint32_t __iomem *virt; int rem = ring->size - ring->tail; if (ring->space < rem) { @@ -1031,12 +1061,10 @@ static int intel_wrap_ring_buffer(struct intel_ring_buffer *ring) return ret; } - virt = (unsigned int *)(ring->virtual_start + ring->tail); - rem /= 8; - while (rem--) { - *virt++ = MI_NOOP; - *virt++ = MI_NOOP; - } + virt = ring->virtual_start + ring->tail; + rem /= 4; + while (rem--) + iowrite32(MI_NOOP, virt++); ring->tail = 0; ring->space = ring_space(ring); @@ -1057,9 +1085,11 @@ static int intel_ring_wait_seqno(struct intel_ring_buffer *ring, u32 seqno) was_interruptible = dev_priv->mm.interruptible; dev_priv->mm.interruptible = false; - ret = i915_wait_request(ring, seqno, true); + ret = i915_wait_request(ring, seqno); dev_priv->mm.interruptible = was_interruptible; + if (!ret) + i915_gem_retire_requests_ring(ring); return ret; } @@ -1133,15 +1163,12 @@ int intel_wait_ring_buffer(struct intel_ring_buffer *ring, int n) return ret; trace_i915_ring_wait_begin(ring); - if (drm_core_check_feature(dev, DRIVER_GEM)) - /* With GEM the hangcheck timer should kick us out of the loop, - * leaving it early runs the risk of corrupting GEM state (due - * to running on almost untested codepaths). But on resume - * timers don't work yet, so prevent a complete hang in that - * case by choosing an insanely large timeout. */ - end = jiffies + 60 * HZ; - else - end = jiffies + 3 * HZ; + /* With GEM the hangcheck timer should kick us out of the loop, + * leaving it early runs the risk of corrupting GEM state (due + * to running on almost untested codepaths). But on resume + * timers don't work yet, so prevent a complete hang in that + * case by choosing an insanely large timeout. */ + end = jiffies + 60 * HZ; do { ring->head = I915_READ_HEAD(ring); @@ -1193,7 +1220,11 @@ int intel_ring_begin(struct intel_ring_buffer *ring, void intel_ring_advance(struct intel_ring_buffer *ring) { + struct drm_i915_private *dev_priv = ring->dev->dev_private; + ring->tail &= ring->size - 1; + if (dev_priv->stop_rings & intel_ring_flag(ring)) + return; ring->write_tail(ring, ring->tail); } @@ -1318,8 +1349,13 @@ int intel_init_render_ring_buffer(struct drm_device *dev) else ring->flush = gen4_render_ring_flush; ring->get_seqno = ring_get_seqno; - ring->irq_get = i9xx_ring_get_irq; - ring->irq_put = i9xx_ring_put_irq; + if (IS_GEN2(dev)) { + ring->irq_get = i8xx_ring_get_irq; + ring->irq_put = i8xx_ring_put_irq; + } else { + ring->irq_get = i9xx_ring_get_irq; + ring->irq_put = i9xx_ring_put_irq; + } ring->irq_enable_mask = I915_USER_INTERRUPT; } ring->write_tail = ring_write_tail; @@ -1366,8 +1402,13 @@ int intel_render_ring_init_dri(struct drm_device *dev, u64 start, u32 size) else ring->flush = gen4_render_ring_flush; ring->get_seqno = ring_get_seqno; - ring->irq_get = i9xx_ring_get_irq; - ring->irq_put = i9xx_ring_put_irq; + if (IS_GEN2(dev)) { + ring->irq_get = i8xx_ring_get_irq; + ring->irq_put = i8xx_ring_put_irq; + } else { + ring->irq_get = i9xx_ring_get_irq; + ring->irq_put = i9xx_ring_put_irq; + } ring->irq_enable_mask = I915_USER_INTERRUPT; ring->write_tail = ring_write_tail; if (INTEL_INFO(dev)->gen >= 4) @@ -1392,20 +1433,13 @@ int intel_render_ring_init_dri(struct drm_device *dev, u64 start, u32 size) if (IS_I830(ring->dev)) ring->effective_size -= 128; - ring->map.offset = start; - ring->map.size = size; - ring->map.type = 0; - ring->map.flags = 0; - ring->map.mtrr = 0; - - drm_core_ioremap_wc(&ring->map, dev); - if (ring->map.handle == NULL) { + ring->virtual_start = ioremap_wc(start, size); + if (ring->virtual_start == NULL) { DRM_ERROR("can not ioremap virtual address for" " ring buffer\n"); return -ENOMEM; } - ring->virtual_start = (void __force __iomem *)ring->map.handle; return 0; } diff --git a/drivers/gpu/drm/i915/intel_ringbuffer.h b/drivers/gpu/drm/i915/intel_ringbuffer.h index 06a66ad..55d3da2 100644 --- a/drivers/gpu/drm/i915/intel_ringbuffer.h +++ b/drivers/gpu/drm/i915/intel_ringbuffer.h @@ -2,7 +2,7 @@ #define _INTEL_RINGBUFFER_H_ struct intel_hw_status_page { - u32 __iomem *page_addr; + u32 *page_addr; unsigned int gfx_addr; struct drm_i915_gem_object *obj; }; @@ -56,12 +56,9 @@ struct intel_ring_buffer { */ u32 last_retired_head; - spinlock_t irq_lock; - u32 irq_refcount; + u32 irq_refcount; /* protected by dev_priv->irq_lock */ u32 irq_enable_mask; /* bitmask to enable ring interrupt */ - u32 irq_seqno; /* last seq seem at irq time */ u32 trace_irq_seqno; - u32 waiting_seqno; u32 sync_seqno[I915_NUM_RINGS-1]; bool __must_check (*irq_get)(struct intel_ring_buffer *ring); void (*irq_put)(struct intel_ring_buffer *ring); @@ -118,11 +115,16 @@ struct intel_ring_buffer { u32 outstanding_lazy_request; wait_queue_head_t irq_queue; - drm_local_map_t map; void *private; }; +static inline bool +intel_ring_initialized(struct intel_ring_buffer *ring) +{ + return ring->obj != NULL; +} + static inline unsigned intel_ring_flag(struct intel_ring_buffer *ring) { @@ -152,7 +154,9 @@ static inline u32 intel_read_status_page(struct intel_ring_buffer *ring, int reg) { - return ioread32(ring->status_page.page_addr + reg); + /* Ensure that the compiler doesn't optimize away the load. */ + barrier(); + return ring->status_page.page_addr[reg]; } /** @@ -170,10 +174,7 @@ intel_read_status_page(struct intel_ring_buffer *ring, * * The area from dword 0x20 to 0x3ff is available for driver usage. */ -#define READ_HWSP(dev_priv, reg) intel_read_status_page(LP_RING(dev_priv), reg) -#define READ_BREADCRUMB(dev_priv) READ_HWSP(dev_priv, I915_BREADCRUMB_INDEX) #define I915_GEM_HWS_INDEX 0x20 -#define I915_BREADCRUMB_INDEX 0x21 void intel_cleanup_ring_buffer(struct intel_ring_buffer *ring); diff --git a/drivers/gpu/drm/i915/intel_sdvo.c b/drivers/gpu/drm/i915/intel_sdvo.c index 3d9dfa5..125228e 100644 --- a/drivers/gpu/drm/i915/intel_sdvo.c +++ b/drivers/gpu/drm/i915/intel_sdvo.c @@ -747,18 +747,18 @@ static void intel_sdvo_get_dtd_from_mode(struct intel_sdvo_dtd *dtd, uint16_t h_sync_offset, v_sync_offset; int mode_clock; - width = mode->crtc_hdisplay; - height = mode->crtc_vdisplay; + width = mode->hdisplay; + height = mode->vdisplay; /* do some mode translations */ - h_blank_len = mode->crtc_hblank_end - mode->crtc_hblank_start; - h_sync_len = mode->crtc_hsync_end - mode->crtc_hsync_start; + h_blank_len = mode->htotal - mode->hdisplay; + h_sync_len = mode->hsync_end - mode->hsync_start; - v_blank_len = mode->crtc_vblank_end - mode->crtc_vblank_start; - v_sync_len = mode->crtc_vsync_end - mode->crtc_vsync_start; + v_blank_len = mode->vtotal - mode->vdisplay; + v_sync_len = mode->vsync_end - mode->vsync_start; - h_sync_offset = mode->crtc_hsync_start - mode->crtc_hblank_start; - v_sync_offset = mode->crtc_vsync_start - mode->crtc_vblank_start; + h_sync_offset = mode->hsync_start - mode->hdisplay; + v_sync_offset = mode->vsync_start - mode->vdisplay; mode_clock = mode->clock; mode_clock /= intel_mode_get_pixel_multiplier(mode) ?: 1; @@ -887,17 +887,24 @@ static bool intel_sdvo_set_avi_infoframe(struct intel_sdvo *intel_sdvo) }; uint8_t tx_rate = SDVO_HBUF_TX_VSYNC; uint8_t set_buf_index[2] = { 1, 0 }; - uint64_t *data = (uint64_t *)&avi_if; + uint8_t sdvo_data[4 + sizeof(avi_if.body.avi)]; + uint64_t *data = (uint64_t *)sdvo_data; unsigned i; intel_dip_infoframe_csum(&avi_if); + /* sdvo spec says that the ecc is handled by the hw, and it looks like + * we must not send the ecc field, either. */ + memcpy(sdvo_data, &avi_if, 3); + sdvo_data[3] = avi_if.checksum; + memcpy(&sdvo_data[4], &avi_if.body, sizeof(avi_if.body.avi)); + if (!intel_sdvo_set_value(intel_sdvo, SDVO_CMD_SET_HBUF_INDEX, set_buf_index, 2)) return false; - for (i = 0; i < sizeof(avi_if); i += 8) { + for (i = 0; i < sizeof(sdvo_data); i += 8) { if (!intel_sdvo_set_value(intel_sdvo, SDVO_CMD_SET_HBUF_DATA, data, 8)) @@ -1578,9 +1585,6 @@ end: 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; } diff --git a/drivers/gpu/drm/i915/intel_sprite.c b/drivers/gpu/drm/i915/intel_sprite.c index fbf03b9..2a20fb0 100644 --- a/drivers/gpu/drm/i915/intel_sprite.c +++ b/drivers/gpu/drm/i915/intel_sprite.c @@ -110,14 +110,18 @@ ivb_update_plane(struct drm_plane *plane, struct drm_framebuffer *fb, * when scaling is disabled. */ if (crtc_w != src_w || crtc_h != src_h) { - dev_priv->sprite_scaling_enabled = true; - intel_update_watermarks(dev); - intel_wait_for_vblank(dev, pipe); + if (!dev_priv->sprite_scaling_enabled) { + dev_priv->sprite_scaling_enabled = true; + intel_update_watermarks(dev); + intel_wait_for_vblank(dev, pipe); + } sprscale = SPRITE_SCALE_ENABLE | (src_w << 16) | src_h; } else { - dev_priv->sprite_scaling_enabled = false; - /* potentially re-enable LP watermarks */ - intel_update_watermarks(dev); + if (dev_priv->sprite_scaling_enabled) { + dev_priv->sprite_scaling_enabled = false; + /* potentially re-enable LP watermarks */ + intel_update_watermarks(dev); + } } I915_WRITE(SPRSTRIDE(pipe), fb->pitches[0]); @@ -151,6 +155,9 @@ ivb_disable_plane(struct drm_plane *plane) /* Activate double buffered register update */ I915_MODIFY_DISPBASE(SPRSURF(pipe), 0); POSTING_READ(SPRSURF(pipe)); + + dev_priv->sprite_scaling_enabled = false; + intel_update_watermarks(dev); } static int @@ -551,14 +558,13 @@ int intel_sprite_set_colorkey(struct drm_device *dev, void *data, struct drm_file *file_priv) { struct drm_intel_sprite_colorkey *set = data; - struct drm_i915_private *dev_priv = dev->dev_private; struct drm_mode_object *obj; struct drm_plane *plane; struct intel_plane *intel_plane; int ret = 0; - if (!dev_priv) - return -EINVAL; + if (!drm_core_check_feature(dev, DRIVER_MODESET)) + return -ENODEV; /* Make sure we don't try to enable both src & dest simultaneously */ if ((set->flags & (I915_SET_COLORKEY_DESTINATION | I915_SET_COLORKEY_SOURCE)) == (I915_SET_COLORKEY_DESTINATION | I915_SET_COLORKEY_SOURCE)) @@ -585,14 +591,13 @@ int intel_sprite_get_colorkey(struct drm_device *dev, void *data, struct drm_file *file_priv) { struct drm_intel_sprite_colorkey *get = data; - struct drm_i915_private *dev_priv = dev->dev_private; struct drm_mode_object *obj; struct drm_plane *plane; struct intel_plane *intel_plane; int ret = 0; - if (!dev_priv) - return -EINVAL; + if (!drm_core_check_feature(dev, DRIVER_MODESET)) + return -ENODEV; mutex_lock(&dev->mode_config.mutex); diff --git a/drivers/gpu/drm/i915/intel_tv.c b/drivers/gpu/drm/i915/intel_tv.c index 67f444d..3346612 100644 --- a/drivers/gpu/drm/i915/intel_tv.c +++ b/drivers/gpu/drm/i915/intel_tv.c @@ -1249,11 +1249,8 @@ intel_tv_detect(struct drm_connector *connector, bool force) int type; mode = reported_modes[0]; - drm_mode_set_crtcinfo(&mode, 0); - if (intel_tv->base.base.crtc && intel_tv->base.base.crtc->enabled) { - type = intel_tv_detect_type(intel_tv, connector); - } else if (force) { + if (force) { struct intel_load_detect_pipe tmp; if (intel_get_load_detect_pipe(&intel_tv->base, connector, diff --git a/drivers/gpu/drm/mgag200/Kconfig b/drivers/gpu/drm/mgag200/Kconfig new file mode 100644 index 0000000..d630134 --- /dev/null +++ b/drivers/gpu/drm/mgag200/Kconfig @@ -0,0 +1,15 @@ +config DRM_MGAG200 + tristate "Kernel modesetting driver for MGA G200 server engines" + depends on DRM && PCI && EXPERIMENTAL + select FB_SYS_FILLRECT + select FB_SYS_COPYAREA + select FB_SYS_IMAGEBLIT + select DRM_KMS_HELPER + select DRM_TTM + help + This is a KMS driver for the MGA G200 server chips, it + does not support the original MGA G200 or any of the desktop + chips. It requires 0.3.0 of the modesetting userspace driver, + and a version of mga driver that will fail on KMS enabled + devices. + diff --git a/drivers/gpu/drm/mgag200/Makefile b/drivers/gpu/drm/mgag200/Makefile new file mode 100644 index 0000000..7db592e --- /dev/null +++ b/drivers/gpu/drm/mgag200/Makefile @@ -0,0 +1,5 @@ +ccflags-y := -Iinclude/drm +mgag200-y := mgag200_main.o mgag200_mode.o \ + mgag200_drv.o mgag200_fb.o mgag200_i2c.o mgag200_ttm.o + +obj-$(CONFIG_DRM_MGAG200) += mgag200.o diff --git a/drivers/gpu/drm/mgag200/mgag200_drv.c b/drivers/gpu/drm/mgag200/mgag200_drv.c new file mode 100644 index 0000000..3c8e04f --- /dev/null +++ b/drivers/gpu/drm/mgag200/mgag200_drv.c @@ -0,0 +1,116 @@ +/* + * Copyright 2012 Red Hat + * + * This file is subject to the terms and conditions of the GNU General + * Public License version 2. See the file COPYING in the main + * directory of this archive for more details. + * + * Authors: Matthew Garrett + * Dave Airlie + */ +#include <linux/module.h> +#include <linux/console.h> +#include "drmP.h" +#include "drm.h" + +#include "mgag200_drv.h" + +#include "drm_pciids.h" + +/* + * This is the generic driver code. This binds the driver to the drm core, + * which then performs further device association and calls our graphics init + * functions + */ +int mgag200_modeset = -1; + +MODULE_PARM_DESC(modeset, "Disable/Enable modesetting"); +module_param_named(modeset, mgag200_modeset, int, 0400); + +static struct drm_driver driver; + +static DEFINE_PCI_DEVICE_TABLE(pciidlist) = { + { PCI_VENDOR_ID_MATROX, 0x522, PCI_ANY_ID, PCI_ANY_ID, 0, 0, G200_SE_A }, + { PCI_VENDOR_ID_MATROX, 0x524, PCI_ANY_ID, PCI_ANY_ID, 0, 0, G200_SE_B }, + { PCI_VENDOR_ID_MATROX, 0x530, PCI_ANY_ID, PCI_ANY_ID, 0, 0, G200_EV }, + { PCI_VENDOR_ID_MATROX, 0x532, PCI_ANY_ID, PCI_ANY_ID, 0, 0, G200_WB }, + { PCI_VENDOR_ID_MATROX, 0x533, PCI_ANY_ID, PCI_ANY_ID, 0, 0, G200_EH }, + { PCI_VENDOR_ID_MATROX, 0x534, PCI_ANY_ID, PCI_ANY_ID, 0, 0, G200_ER }, + {0,} +}; + +MODULE_DEVICE_TABLE(pci, pciidlist); + +static int __devinit +mga_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent) +{ + return drm_get_pci_dev(pdev, ent, &driver); +} + +static void mga_pci_remove(struct pci_dev *pdev) +{ + struct drm_device *dev = pci_get_drvdata(pdev); + + drm_put_dev(dev); +} + +static const struct file_operations mgag200_driver_fops = { + .owner = THIS_MODULE, + .open = drm_open, + .release = drm_release, + .unlocked_ioctl = drm_ioctl, + .mmap = mgag200_mmap, + .poll = drm_poll, + .fasync = drm_fasync, + .read = drm_read, +}; + +static struct drm_driver driver = { + .driver_features = DRIVER_GEM | DRIVER_MODESET | DRIVER_USE_MTRR, + .load = mgag200_driver_load, + .unload = mgag200_driver_unload, + .fops = &mgag200_driver_fops, + .name = DRIVER_NAME, + .desc = DRIVER_DESC, + .date = DRIVER_DATE, + .major = DRIVER_MAJOR, + .minor = DRIVER_MINOR, + .patchlevel = DRIVER_PATCHLEVEL, + + .gem_init_object = mgag200_gem_init_object, + .gem_free_object = mgag200_gem_free_object, + .dumb_create = mgag200_dumb_create, + .dumb_map_offset = mgag200_dumb_mmap_offset, + .dumb_destroy = mgag200_dumb_destroy, +}; + +static struct pci_driver mgag200_pci_driver = { + .name = DRIVER_NAME, + .id_table = pciidlist, + .probe = mga_pci_probe, + .remove = mga_pci_remove, +}; + +static int __init mgag200_init(void) +{ +#ifdef CONFIG_VGA_CONSOLE + if (vgacon_text_force() && mgag200_modeset == -1) + return -EINVAL; +#endif + + if (mgag200_modeset == 0) + return -EINVAL; + return drm_pci_init(&driver, &mgag200_pci_driver); +} + +static void __exit mgag200_exit(void) +{ + drm_pci_exit(&driver, &mgag200_pci_driver); +} + +module_init(mgag200_init); +module_exit(mgag200_exit); + +MODULE_AUTHOR(DRIVER_AUTHOR); +MODULE_DESCRIPTION(DRIVER_DESC); +MODULE_LICENSE("GPL"); diff --git a/drivers/gpu/drm/mgag200/mgag200_drv.h b/drivers/gpu/drm/mgag200/mgag200_drv.h new file mode 100644 index 0000000..6f13b35 --- /dev/null +++ b/drivers/gpu/drm/mgag200/mgag200_drv.h @@ -0,0 +1,276 @@ +/* + * Copyright 2010 Matt Turner. + * Copyright 2012 Red Hat + * + * This file is subject to the terms and conditions of the GNU General + * Public License version 2. See the file COPYING in the main + * directory of this archive for more details. + * + * Authors: Matthew Garrett + * Matt Turner + * Dave Airlie + */ +#ifndef __MGAG200_DRV_H__ +#define __MGAG200_DRV_H__ + +#include <video/vga.h> + +#include "drm/drm_fb_helper.h" +#include "ttm/ttm_bo_api.h" +#include "ttm/ttm_bo_driver.h" +#include "ttm/ttm_placement.h" +#include "ttm/ttm_memory.h" +#include "ttm/ttm_module.h" + +#include <linux/i2c.h> +#include <linux/i2c-algo-bit.h> + +#include "mgag200_reg.h" + +#define DRIVER_AUTHOR "Matthew Garrett" + +#define DRIVER_NAME "mgag200" +#define DRIVER_DESC "MGA G200 SE" +#define DRIVER_DATE "20110418" + +#define DRIVER_MAJOR 1 +#define DRIVER_MINOR 0 +#define DRIVER_PATCHLEVEL 0 + +#define MGAG200FB_CONN_LIMIT 1 + +#define RREG8(reg) ioread8(((void __iomem *)mdev->rmmio) + (reg)) +#define WREG8(reg, v) iowrite8(v, ((void __iomem *)mdev->rmmio) + (reg)) +#define RREG32(reg) ioread32(((void __iomem *)mdev->rmmio) + (reg)) +#define WREG32(reg, v) iowrite32(v, ((void __iomem *)mdev->rmmio) + (reg)) + +#define ATTR_INDEX 0x1fc0 +#define ATTR_DATA 0x1fc1 + +#define WREG_ATTR(reg, v) \ + do { \ + RREG8(0x1fda); \ + WREG8(ATTR_INDEX, reg); \ + WREG8(ATTR_DATA, v); \ + } while (0) \ + +#define WREG_SEQ(reg, v) \ + do { \ + WREG8(MGAREG_SEQ_INDEX, reg); \ + WREG8(MGAREG_SEQ_DATA, v); \ + } while (0) \ + +#define WREG_CRT(reg, v) \ + do { \ + WREG8(MGAREG_CRTC_INDEX, reg); \ + WREG8(MGAREG_CRTC_DATA, v); \ + } while (0) \ + + +#define WREG_ECRT(reg, v) \ + do { \ + WREG8(MGAREG_CRTCEXT_INDEX, reg); \ + WREG8(MGAREG_CRTCEXT_DATA, v); \ + } while (0) \ + +#define GFX_INDEX 0x1fce +#define GFX_DATA 0x1fcf + +#define WREG_GFX(reg, v) \ + do { \ + WREG8(GFX_INDEX, reg); \ + WREG8(GFX_DATA, v); \ + } while (0) \ + +#define DAC_INDEX 0x3c00 +#define DAC_DATA 0x3c0a + +#define WREG_DAC(reg, v) \ + do { \ + WREG8(DAC_INDEX, reg); \ + WREG8(DAC_DATA, v); \ + } while (0) \ + +#define MGA_MISC_OUT 0x1fc2 +#define MGA_MISC_IN 0x1fcc + +#define MGAG200_MAX_FB_HEIGHT 4096 +#define MGAG200_MAX_FB_WIDTH 4096 + +#define MATROX_DPMS_CLEARED (-1) + +#define to_mga_crtc(x) container_of(x, struct mga_crtc, base) +#define to_mga_encoder(x) container_of(x, struct mga_encoder, base) +#define to_mga_connector(x) container_of(x, struct mga_connector, base) +#define to_mga_framebuffer(x) container_of(x, struct mga_framebuffer, base) + +struct mga_framebuffer { + struct drm_framebuffer base; + struct drm_gem_object *obj; +}; + +struct mga_fbdev { + struct drm_fb_helper helper; + struct mga_framebuffer mfb; + struct list_head fbdev_list; + void *sysram; + int size; + struct ttm_bo_kmap_obj mapping; +}; + +struct mga_crtc { + struct drm_crtc base; + u8 lut_r[256], lut_g[256], lut_b[256]; + int last_dpms; + bool enabled; +}; + +struct mga_mode_info { + bool mode_config_initialized; + struct mga_crtc *crtc; +}; + +struct mga_encoder { + struct drm_encoder base; + int last_dpms; +}; + + +struct mga_i2c_chan { + struct i2c_adapter adapter; + struct drm_device *dev; + struct i2c_algo_bit_data bit; + int data, clock; +}; + +struct mga_connector { + struct drm_connector base; + struct mga_i2c_chan *i2c; +}; + + +struct mga_mc { + resource_size_t vram_size; + resource_size_t vram_base; + resource_size_t vram_window; +}; + +enum mga_type { + G200_SE_A, + G200_SE_B, + G200_WB, + G200_EV, + G200_EH, + G200_ER, +}; + +#define IS_G200_SE(mdev) (mdev->type == G200_SE_A || mdev->type == G200_SE_B) + +struct mga_device { + struct drm_device *dev; + unsigned long flags; + + resource_size_t rmmio_base; + resource_size_t rmmio_size; + void __iomem *rmmio; + + drm_local_map_t *framebuffer; + + struct mga_mc mc; + struct mga_mode_info mode_info; + + struct mga_fbdev *mfbdev; + + bool suspended; + int num_crtc; + enum mga_type type; + int has_sdram; + struct drm_display_mode mode; + + int bpp_shifts[4]; + + int fb_mtrr; + + struct { + struct drm_global_reference mem_global_ref; + struct ttm_bo_global_ref bo_global_ref; + struct ttm_bo_device bdev; + atomic_t validate_sequence; + } ttm; + + u32 reg_1e24; /* SE model number */ +}; + + +struct mgag200_bo { + struct ttm_buffer_object bo; + struct ttm_placement placement; + struct ttm_bo_kmap_obj kmap; + struct drm_gem_object gem; + u32 placements[3]; + int pin_count; +}; +#define gem_to_mga_bo(gobj) container_of((gobj), struct mgag200_bo, gem) + +static inline struct mgag200_bo * +mgag200_bo(struct ttm_buffer_object *bo) +{ + return container_of(bo, struct mgag200_bo, bo); +} + /* mga_crtc.c */ +void mga_crtc_fb_gamma_set(struct drm_crtc *crtc, u16 red, u16 green, + u16 blue, int regno); +void mga_crtc_fb_gamma_get(struct drm_crtc *crtc, u16 *red, u16 *green, + u16 *blue, int regno); + + /* mgag200_mode.c */ +int mgag200_modeset_init(struct mga_device *mdev); +void mgag200_modeset_fini(struct mga_device *mdev); + + /* mga_fbdev.c */ +int mgag200_fbdev_init(struct mga_device *mdev); +void mgag200_fbdev_fini(struct mga_device *mdev); + + /* mgag200_main.c */ +int mgag200_framebuffer_init(struct drm_device *dev, + struct mga_framebuffer *mfb, + struct drm_mode_fb_cmd2 *mode_cmd, + struct drm_gem_object *obj); + + +int mgag200_driver_load(struct drm_device *dev, unsigned long flags); +int mgag200_driver_unload(struct drm_device *dev); +int mgag200_gem_create(struct drm_device *dev, + u32 size, bool iskernel, + struct drm_gem_object **obj); +int mgag200_gem_init_object(struct drm_gem_object *obj); +int mgag200_dumb_create(struct drm_file *file, + struct drm_device *dev, + struct drm_mode_create_dumb *args); +int mgag200_dumb_destroy(struct drm_file *file, + struct drm_device *dev, + uint32_t handle); +void mgag200_gem_free_object(struct drm_gem_object *obj); +int +mgag200_dumb_mmap_offset(struct drm_file *file, + struct drm_device *dev, + uint32_t handle, + uint64_t *offset); + /* mga_i2c.c */ +struct mga_i2c_chan *mgag200_i2c_create(struct drm_device *dev); +void mgag200_i2c_destroy(struct mga_i2c_chan *i2c); + +#define DRM_FILE_PAGE_OFFSET (0x100000000ULL >> PAGE_SHIFT) +void mgag200_ttm_placement(struct mgag200_bo *bo, int domain); + +int mgag200_bo_reserve(struct mgag200_bo *bo, bool no_wait); +void mgag200_bo_unreserve(struct mgag200_bo *bo); +int mgag200_bo_create(struct drm_device *dev, int size, int align, + uint32_t flags, struct mgag200_bo **pastbo); +int mgag200_mm_init(struct mga_device *mdev); +void mgag200_mm_fini(struct mga_device *mdev); +int mgag200_mmap(struct file *filp, struct vm_area_struct *vma); +int mgag200_bo_pin(struct mgag200_bo *bo, u32 pl_flag, u64 *gpu_addr); +int mgag200_bo_unpin(struct mgag200_bo *bo); +int mgag200_bo_push_sysram(struct mgag200_bo *bo); +#endif /* __MGAG200_DRV_H__ */ diff --git a/drivers/gpu/drm/mgag200/mgag200_fb.c b/drivers/gpu/drm/mgag200/mgag200_fb.c new file mode 100644 index 0000000..880d336 --- /dev/null +++ b/drivers/gpu/drm/mgag200/mgag200_fb.c @@ -0,0 +1,294 @@ +/* + * Copyright 2010 Matt Turner. + * Copyright 2012 Red Hat + * + * This file is subject to the terms and conditions of the GNU General + * Public License version 2. See the file COPYING in the main + * directory of this archive for more details. + * + * Authors: Matthew Garrett + * Matt Turner + * Dave Airlie + */ +#include <linux/module.h> +#include "drmP.h" +#include "drm.h" +#include "drm_fb_helper.h" + +#include <linux/fb.h> + +#include "mgag200_drv.h" + +static void mga_dirty_update(struct mga_fbdev *mfbdev, + int x, int y, int width, int height) +{ + int i; + struct drm_gem_object *obj; + struct mgag200_bo *bo; + int src_offset, dst_offset; + int bpp = (mfbdev->mfb.base.bits_per_pixel + 7)/8; + int ret; + bool unmap = false; + + obj = mfbdev->mfb.obj; + bo = gem_to_mga_bo(obj); + + ret = mgag200_bo_reserve(bo, true); + if (ret) { + DRM_ERROR("failed to reserve fb bo\n"); + return; + } + + if (!bo->kmap.virtual) { + ret = ttm_bo_kmap(&bo->bo, 0, bo->bo.num_pages, &bo->kmap); + if (ret) { + DRM_ERROR("failed to kmap fb updates\n"); + mgag200_bo_unreserve(bo); + return; + } + unmap = true; + } + for (i = y; i < y + height; i++) { + /* assume equal stride for now */ + src_offset = dst_offset = i * mfbdev->mfb.base.pitches[0] + (x * bpp); + memcpy_toio(bo->kmap.virtual + src_offset, mfbdev->sysram + src_offset, width * bpp); + + } + if (unmap) + ttm_bo_kunmap(&bo->kmap); + + mgag200_bo_unreserve(bo); +} + +static void mga_fillrect(struct fb_info *info, + const struct fb_fillrect *rect) +{ + struct mga_fbdev *mfbdev = info->par; + sys_fillrect(info, rect); + mga_dirty_update(mfbdev, rect->dx, rect->dy, rect->width, + rect->height); +} + +static void mga_copyarea(struct fb_info *info, + const struct fb_copyarea *area) +{ + struct mga_fbdev *mfbdev = info->par; + sys_copyarea(info, area); + mga_dirty_update(mfbdev, area->dx, area->dy, area->width, + area->height); +} + +static void mga_imageblit(struct fb_info *info, + const struct fb_image *image) +{ + struct mga_fbdev *mfbdev = info->par; + sys_imageblit(info, image); + mga_dirty_update(mfbdev, image->dx, image->dy, image->width, + image->height); +} + + +static struct fb_ops mgag200fb_ops = { + .owner = THIS_MODULE, + .fb_check_var = drm_fb_helper_check_var, + .fb_set_par = drm_fb_helper_set_par, + .fb_fillrect = mga_fillrect, + .fb_copyarea = mga_copyarea, + .fb_imageblit = mga_imageblit, + .fb_pan_display = drm_fb_helper_pan_display, + .fb_blank = drm_fb_helper_blank, + .fb_setcmap = drm_fb_helper_setcmap, +}; + +static int mgag200fb_create_object(struct mga_fbdev *afbdev, + struct drm_mode_fb_cmd2 *mode_cmd, + struct drm_gem_object **gobj_p) +{ + struct drm_device *dev = afbdev->helper.dev; + u32 bpp, depth; + u32 size; + struct drm_gem_object *gobj; + + int ret = 0; + drm_fb_get_bpp_depth(mode_cmd->pixel_format, &depth, &bpp); + + size = mode_cmd->pitches[0] * mode_cmd->height; + ret = mgag200_gem_create(dev, size, true, &gobj); + if (ret) + return ret; + + *gobj_p = gobj; + return ret; +} + +static int mgag200fb_create(struct mga_fbdev *mfbdev, + struct drm_fb_helper_surface_size *sizes) +{ + struct drm_device *dev = mfbdev->helper.dev; + struct drm_mode_fb_cmd2 mode_cmd; + struct mga_device *mdev = dev->dev_private; + struct fb_info *info; + struct drm_framebuffer *fb; + struct drm_gem_object *gobj = NULL; + struct device *device = &dev->pdev->dev; + struct mgag200_bo *bo; + int ret; + void *sysram; + int size; + + mode_cmd.width = sizes->surface_width; + mode_cmd.height = sizes->surface_height; + mode_cmd.pitches[0] = mode_cmd.width * ((sizes->surface_bpp + 7) / 8); + + mode_cmd.pixel_format = drm_mode_legacy_fb_format(sizes->surface_bpp, + sizes->surface_depth); + size = mode_cmd.pitches[0] * mode_cmd.height; + + ret = mgag200fb_create_object(mfbdev, &mode_cmd, &gobj); + if (ret) { + DRM_ERROR("failed to create fbcon backing object %d\n", ret); + return ret; + } + bo = gem_to_mga_bo(gobj); + + sysram = vmalloc(size); + if (!sysram) + return -ENOMEM; + + info = framebuffer_alloc(0, device); + if (info == NULL) + return -ENOMEM; + + info->par = mfbdev; + + ret = mgag200_framebuffer_init(dev, &mfbdev->mfb, &mode_cmd, gobj); + if (ret) + return ret; + + mfbdev->sysram = sysram; + mfbdev->size = size; + + fb = &mfbdev->mfb.base; + + /* setup helper */ + mfbdev->helper.fb = fb; + mfbdev->helper.fbdev = info; + + ret = fb_alloc_cmap(&info->cmap, 256, 0); + if (ret) { + DRM_ERROR("%s: can't allocate color map\n", info->fix.id); + ret = -ENOMEM; + goto out; + } + + strcpy(info->fix.id, "mgadrmfb"); + + info->flags = FBINFO_DEFAULT | FBINFO_CAN_FORCE_OUTPUT; + info->fbops = &mgag200fb_ops; + + /* setup aperture base/size for vesafb takeover */ + info->apertures = alloc_apertures(1); + if (!info->apertures) { + ret = -ENOMEM; + goto out; + } + info->apertures->ranges[0].base = mdev->dev->mode_config.fb_base; + info->apertures->ranges[0].size = mdev->mc.vram_size; + + drm_fb_helper_fill_fix(info, fb->pitches[0], fb->depth); + drm_fb_helper_fill_var(info, &mfbdev->helper, sizes->fb_width, + sizes->fb_height); + + info->screen_base = sysram; + info->screen_size = size; + info->pixmap.flags = FB_PIXMAP_SYSTEM; + + DRM_DEBUG_KMS("allocated %dx%d\n", + fb->width, fb->height); + return 0; +out: + return ret; +} + +static int mga_fb_find_or_create_single(struct drm_fb_helper *helper, + struct drm_fb_helper_surface_size + *sizes) +{ + struct mga_fbdev *mfbdev = (struct mga_fbdev *)helper; + int new_fb = 0; + int ret; + + if (!helper->fb) { + ret = mgag200fb_create(mfbdev, sizes); + if (ret) + return ret; + new_fb = 1; + } + return new_fb; +} + +static int mga_fbdev_destroy(struct drm_device *dev, + struct mga_fbdev *mfbdev) +{ + struct fb_info *info; + struct mga_framebuffer *mfb = &mfbdev->mfb; + + if (mfbdev->helper.fbdev) { + info = mfbdev->helper.fbdev; + + unregister_framebuffer(info); + if (info->cmap.len) + fb_dealloc_cmap(&info->cmap); + framebuffer_release(info); + } + + if (mfb->obj) { + drm_gem_object_unreference_unlocked(mfb->obj); + mfb->obj = NULL; + } + drm_fb_helper_fini(&mfbdev->helper); + vfree(mfbdev->sysram); + drm_framebuffer_cleanup(&mfb->base); + + return 0; +} + +static struct drm_fb_helper_funcs mga_fb_helper_funcs = { + .gamma_set = mga_crtc_fb_gamma_set, + .gamma_get = mga_crtc_fb_gamma_get, + .fb_probe = mga_fb_find_or_create_single, +}; + +int mgag200_fbdev_init(struct mga_device *mdev) +{ + struct mga_fbdev *mfbdev; + int ret; + + mfbdev = kzalloc(sizeof(struct mga_fbdev), GFP_KERNEL); + if (!mfbdev) + return -ENOMEM; + + mdev->mfbdev = mfbdev; + mfbdev->helper.funcs = &mga_fb_helper_funcs; + + ret = drm_fb_helper_init(mdev->dev, &mfbdev->helper, + mdev->num_crtc, MGAG200FB_CONN_LIMIT); + if (ret) { + kfree(mfbdev); + return ret; + } + drm_fb_helper_single_add_all_connectors(&mfbdev->helper); + drm_fb_helper_initial_config(&mfbdev->helper, 32); + + return 0; +} + +void mgag200_fbdev_fini(struct mga_device *mdev) +{ + if (!mdev->mfbdev) + return; + + mga_fbdev_destroy(mdev->dev, mdev->mfbdev); + kfree(mdev->mfbdev); + mdev->mfbdev = NULL; +} diff --git a/drivers/gpu/drm/mgag200/mgag200_i2c.c b/drivers/gpu/drm/mgag200/mgag200_i2c.c new file mode 100644 index 0000000..dd3568a --- /dev/null +++ b/drivers/gpu/drm/mgag200/mgag200_i2c.c @@ -0,0 +1,156 @@ +/* + * Copyright 2012 Red Hat Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sub license, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDERS, AUTHORS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, + * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR + * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE + * USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * The above copyright notice and this permission notice (including the + * next paragraph) shall be included in all copies or substantial portions + * of the Software. + * + */ +/* + * Authors: Dave Airlie <airlied@redhat.com> + */ +#include <linux/export.h> +#include <linux/i2c.h> +#include <linux/i2c-algo-bit.h> +#include "drmP.h" +#include "drm.h" + +#include "mgag200_drv.h" + +static int mga_i2c_read_gpio(struct mga_device *mdev) +{ + WREG8(DAC_INDEX, MGA1064_GEN_IO_DATA); + return RREG8(DAC_DATA); +} + +static void mga_i2c_set_gpio(struct mga_device *mdev, int mask, int val) +{ + int tmp; + + WREG8(DAC_INDEX, MGA1064_GEN_IO_CTL); + tmp = (RREG8(DAC_DATA) & mask) | val; + WREG_DAC(MGA1064_GEN_IO_CTL, tmp); + WREG_DAC(MGA1064_GEN_IO_DATA, 0); +} + +static inline void mga_i2c_set(struct mga_device *mdev, int mask, int state) +{ + if (state) + state = 0; + else + state = mask; + mga_i2c_set_gpio(mdev, ~mask, state); +} + +static void mga_gpio_setsda(void *data, int state) +{ + struct mga_i2c_chan *i2c = data; + struct mga_device *mdev = i2c->dev->dev_private; + mga_i2c_set(mdev, i2c->data, state); +} + +static void mga_gpio_setscl(void *data, int state) +{ + struct mga_i2c_chan *i2c = data; + struct mga_device *mdev = i2c->dev->dev_private; + mga_i2c_set(mdev, i2c->clock, state); +} + +static int mga_gpio_getsda(void *data) +{ + struct mga_i2c_chan *i2c = data; + struct mga_device *mdev = i2c->dev->dev_private; + return (mga_i2c_read_gpio(mdev) & i2c->data) ? 1 : 0; +} + +static int mga_gpio_getscl(void *data) +{ + struct mga_i2c_chan *i2c = data; + struct mga_device *mdev = i2c->dev->dev_private; + return (mga_i2c_read_gpio(mdev) & i2c->clock) ? 1 : 0; +} + +struct mga_i2c_chan *mgag200_i2c_create(struct drm_device *dev) +{ + struct mga_device *mdev = dev->dev_private; + struct mga_i2c_chan *i2c; + int ret; + int data, clock; + + WREG_DAC(MGA1064_GEN_IO_DATA, 0xff); + WREG_DAC(MGA1064_GEN_IO_CTL, 0); + + switch (mdev->type) { + case G200_SE_A: + case G200_SE_B: + case G200_EV: + case G200_WB: + data = 1; + clock = 2; + break; + case G200_EH: + case G200_ER: + data = 2; + clock = 1; + break; + default: + data = 2; + clock = 8; + break; + } + + i2c = kzalloc(sizeof(struct mga_i2c_chan), GFP_KERNEL); + if (!i2c) + return NULL; + + i2c->data = data; + i2c->clock = clock; + i2c->adapter.owner = THIS_MODULE; + i2c->adapter.class = I2C_CLASS_DDC; + i2c->adapter.dev.parent = &dev->pdev->dev; + i2c->dev = dev; + i2c_set_adapdata(&i2c->adapter, i2c); + snprintf(i2c->adapter.name, sizeof(i2c->adapter.name), "mga i2c"); + + i2c->adapter.algo_data = &i2c->bit; + + i2c->bit.udelay = 10; + i2c->bit.timeout = 2; + i2c->bit.data = i2c; + i2c->bit.setsda = mga_gpio_setsda; + i2c->bit.setscl = mga_gpio_setscl; + i2c->bit.getsda = mga_gpio_getsda; + i2c->bit.getscl = mga_gpio_getscl; + + ret = i2c_bit_add_bus(&i2c->adapter); + if (ret) { + kfree(i2c); + i2c = NULL; + } + return i2c; +} + +void mgag200_i2c_destroy(struct mga_i2c_chan *i2c) +{ + if (!i2c) + return; + i2c_del_adapter(&i2c->adapter); + kfree(i2c); +} + diff --git a/drivers/gpu/drm/mgag200/mgag200_main.c b/drivers/gpu/drm/mgag200/mgag200_main.c new file mode 100644 index 0000000..636a81c --- /dev/null +++ b/drivers/gpu/drm/mgag200/mgag200_main.c @@ -0,0 +1,388 @@ +/* + * Copyright 2010 Matt Turner. + * Copyright 2012 Red Hat + * + * This file is subject to the terms and conditions of the GNU General + * Public License version 2. See the file COPYING in the main + * directory of this archive for more details. + * + * Authors: Matthew Garrett + * Matt Turner + * Dave Airlie + */ +#include "drmP.h" +#include "drm.h" +#include "drm_crtc_helper.h" +#include "mgag200_drv.h" + +static void mga_user_framebuffer_destroy(struct drm_framebuffer *fb) +{ + struct mga_framebuffer *mga_fb = to_mga_framebuffer(fb); + if (mga_fb->obj) + drm_gem_object_unreference_unlocked(mga_fb->obj); + drm_framebuffer_cleanup(fb); + kfree(fb); +} + +static int mga_user_framebuffer_create_handle(struct drm_framebuffer *fb, + struct drm_file *file_priv, + unsigned int *handle) +{ + return 0; +} + +static const struct drm_framebuffer_funcs mga_fb_funcs = { + .destroy = mga_user_framebuffer_destroy, + .create_handle = mga_user_framebuffer_create_handle, +}; + +int mgag200_framebuffer_init(struct drm_device *dev, + struct mga_framebuffer *gfb, + struct drm_mode_fb_cmd2 *mode_cmd, + struct drm_gem_object *obj) +{ + int ret = drm_framebuffer_init(dev, &gfb->base, &mga_fb_funcs); + if (ret) { + DRM_ERROR("drm_framebuffer_init failed: %d\n", ret); + return ret; + } + drm_helper_mode_fill_fb_struct(&gfb->base, mode_cmd); + gfb->obj = obj; + return 0; +} + +static struct drm_framebuffer * +mgag200_user_framebuffer_create(struct drm_device *dev, + struct drm_file *filp, + struct drm_mode_fb_cmd2 *mode_cmd) +{ + struct drm_gem_object *obj; + struct mga_framebuffer *mga_fb; + int ret; + + obj = drm_gem_object_lookup(dev, filp, mode_cmd->handles[0]); + if (obj == NULL) + return ERR_PTR(-ENOENT); + + mga_fb = kzalloc(sizeof(*mga_fb), GFP_KERNEL); + if (!mga_fb) { + drm_gem_object_unreference_unlocked(obj); + return ERR_PTR(-ENOMEM); + } + + ret = mgag200_framebuffer_init(dev, mga_fb, mode_cmd, obj); + if (ret) { + drm_gem_object_unreference_unlocked(obj); + kfree(mga_fb); + return ERR_PTR(ret); + } + return &mga_fb->base; +} + +static const struct drm_mode_config_funcs mga_mode_funcs = { + .fb_create = mgag200_user_framebuffer_create, +}; + +/* Unmap the framebuffer from the core and release the memory */ +static void mga_vram_fini(struct mga_device *mdev) +{ + pci_iounmap(mdev->dev->pdev, mdev->rmmio); + mdev->rmmio = NULL; + if (mdev->mc.vram_base) + release_mem_region(mdev->mc.vram_base, mdev->mc.vram_window); +} + +static int mga_probe_vram(struct mga_device *mdev, void __iomem *mem) +{ + int offset; + int orig; + int test1, test2; + int orig1, orig2; + + /* Probe */ + orig = ioread16(mem); + iowrite16(0, mem); + + for (offset = 0x100000; offset < mdev->mc.vram_window; offset += 0x4000) { + orig1 = ioread8(mem + offset); + orig2 = ioread8(mem + offset + 0x100); + + iowrite16(0xaa55, mem + offset); + iowrite16(0xaa55, mem + offset + 0x100); + + test1 = ioread16(mem + offset); + test2 = ioread16(mem); + + iowrite16(orig1, mem + offset); + iowrite16(orig2, mem + offset + 0x100); + + if (test1 != 0xaa55) { + break; + } + + if (test2) { + break; + } + } + + iowrite16(orig, mem); + return offset - 65536; +} + +/* Map the framebuffer from the card and configure the core */ +static int mga_vram_init(struct mga_device *mdev) +{ + void __iomem *mem; + struct apertures_struct *aper = alloc_apertures(1); + + /* BAR 0 is VRAM */ + mdev->mc.vram_base = pci_resource_start(mdev->dev->pdev, 0); + mdev->mc.vram_window = pci_resource_len(mdev->dev->pdev, 0); + + aper->ranges[0].base = mdev->mc.vram_base; + aper->ranges[0].size = mdev->mc.vram_window; + aper->count = 1; + + remove_conflicting_framebuffers(aper, "mgafb", true); + + if (!request_mem_region(mdev->mc.vram_base, mdev->mc.vram_window, + "mgadrmfb_vram")) { + DRM_ERROR("can't reserve VRAM\n"); + return -ENXIO; + } + + mem = pci_iomap(mdev->dev->pdev, 0, 0); + + mdev->mc.vram_size = mga_probe_vram(mdev, mem); + + pci_iounmap(mdev->dev->pdev, mem); + + return 0; +} + +static int mgag200_device_init(struct drm_device *dev, + uint32_t flags) +{ + struct mga_device *mdev = dev->dev_private; + int ret, option; + + mdev->type = flags; + + /* Hardcode the number of CRTCs to 1 */ + mdev->num_crtc = 1; + + pci_read_config_dword(dev->pdev, PCI_MGA_OPTION, &option); + mdev->has_sdram = !(option & (1 << 14)); + + /* BAR 0 is the framebuffer, BAR 1 contains registers */ + mdev->rmmio_base = pci_resource_start(mdev->dev->pdev, 1); + mdev->rmmio_size = pci_resource_len(mdev->dev->pdev, 1); + + if (!request_mem_region(mdev->rmmio_base, mdev->rmmio_size, + "mgadrmfb_mmio")) { + DRM_ERROR("can't reserve mmio registers\n"); + return -ENOMEM; + } + + mdev->rmmio = pci_iomap(dev->pdev, 1, 0); + if (mdev->rmmio == NULL) + return -ENOMEM; + + /* stash G200 SE model number for later use */ + if (IS_G200_SE(mdev)) + mdev->reg_1e24 = RREG32(0x1e24); + + ret = mga_vram_init(mdev); + if (ret) { + release_mem_region(mdev->rmmio_base, mdev->rmmio_size); + return ret; + } + + mdev->bpp_shifts[0] = 0; + mdev->bpp_shifts[1] = 1; + mdev->bpp_shifts[2] = 0; + mdev->bpp_shifts[3] = 2; + return 0; +} + +void mgag200_device_fini(struct mga_device *mdev) +{ + release_mem_region(mdev->rmmio_base, mdev->rmmio_size); + mga_vram_fini(mdev); +} + +/* + * Functions here will be called by the core once it's bound the driver to + * a PCI device + */ + + +int mgag200_driver_load(struct drm_device *dev, unsigned long flags) +{ + struct mga_device *mdev; + int r; + + mdev = kzalloc(sizeof(struct mga_device), GFP_KERNEL); + if (mdev == NULL) + return -ENOMEM; + dev->dev_private = (void *)mdev; + mdev->dev = dev; + + r = mgag200_device_init(dev, flags); + if (r) { + dev_err(&dev->pdev->dev, "Fatal error during GPU init: %d\n", r); + goto out; + } + r = mgag200_mm_init(mdev); + if (r) + goto out; + + drm_mode_config_init(dev); + dev->mode_config.funcs = (void *)&mga_mode_funcs; + dev->mode_config.min_width = 0; + dev->mode_config.min_height = 0; + dev->mode_config.preferred_depth = 24; + dev->mode_config.prefer_shadow = 1; + + r = mgag200_modeset_init(mdev); + if (r) + dev_err(&dev->pdev->dev, "Fatal error during modeset init: %d\n", r); +out: + if (r) + mgag200_driver_unload(dev); + return r; +} + +int mgag200_driver_unload(struct drm_device *dev) +{ + struct mga_device *mdev = dev->dev_private; + + if (mdev == NULL) + return 0; + mgag200_modeset_fini(mdev); + mgag200_fbdev_fini(mdev); + drm_mode_config_cleanup(dev); + mgag200_mm_fini(mdev); + mgag200_device_fini(mdev); + kfree(mdev); + dev->dev_private = NULL; + return 0; +} + +int mgag200_gem_create(struct drm_device *dev, + u32 size, bool iskernel, + struct drm_gem_object **obj) +{ + struct mgag200_bo *astbo; + int ret; + + *obj = NULL; + + size = roundup(size, PAGE_SIZE); + if (size == 0) + return -EINVAL; + + ret = mgag200_bo_create(dev, size, 0, 0, &astbo); + if (ret) { + if (ret != -ERESTARTSYS) + DRM_ERROR("failed to allocate GEM object\n"); + return ret; + } + *obj = &astbo->gem; + return 0; +} + +int mgag200_dumb_create(struct drm_file *file, + struct drm_device *dev, + struct drm_mode_create_dumb *args) +{ + int ret; + struct drm_gem_object *gobj; + u32 handle; + + args->pitch = args->width * ((args->bpp + 7) / 8); + args->size = args->pitch * args->height; + + ret = mgag200_gem_create(dev, args->size, false, + &gobj); + if (ret) + return ret; + + ret = drm_gem_handle_create(file, gobj, &handle); + drm_gem_object_unreference_unlocked(gobj); + if (ret) + return ret; + + args->handle = handle; + return 0; +} + +int mgag200_dumb_destroy(struct drm_file *file, + struct drm_device *dev, + uint32_t handle) +{ + return drm_gem_handle_delete(file, handle); +} + +int mgag200_gem_init_object(struct drm_gem_object *obj) +{ + BUG(); + return 0; +} + +void mgag200_bo_unref(struct mgag200_bo **bo) +{ + struct ttm_buffer_object *tbo; + + if ((*bo) == NULL) + return; + + tbo = &((*bo)->bo); + ttm_bo_unref(&tbo); + if (tbo == NULL) + *bo = NULL; + +} + +void mgag200_gem_free_object(struct drm_gem_object *obj) +{ + struct mgag200_bo *mgag200_bo = gem_to_mga_bo(obj); + + if (!mgag200_bo) + return; + mgag200_bo_unref(&mgag200_bo); +} + + +static inline u64 mgag200_bo_mmap_offset(struct mgag200_bo *bo) +{ + return bo->bo.addr_space_offset; +} + +int +mgag200_dumb_mmap_offset(struct drm_file *file, + struct drm_device *dev, + uint32_t handle, + uint64_t *offset) +{ + struct drm_gem_object *obj; + int ret; + struct mgag200_bo *bo; + + mutex_lock(&dev->struct_mutex); + obj = drm_gem_object_lookup(dev, file, handle); + if (obj == NULL) { + ret = -ENOENT; + goto out_unlock; + } + + bo = gem_to_mga_bo(obj); + *offset = mgag200_bo_mmap_offset(bo); + + drm_gem_object_unreference(obj); + ret = 0; +out_unlock: + mutex_unlock(&dev->struct_mutex); + return ret; + +} diff --git a/drivers/gpu/drm/mgag200/mgag200_mode.c b/drivers/gpu/drm/mgag200/mgag200_mode.c new file mode 100644 index 0000000..d303061 --- /dev/null +++ b/drivers/gpu/drm/mgag200/mgag200_mode.c @@ -0,0 +1,1533 @@ +/* + * Copyright 2010 Matt Turner. + * Copyright 2012 Red Hat + * + * This file is subject to the terms and conditions of the GNU General + * Public License version 2. See the file COPYING in the main + * directory of this archive for more details. + * + * Authors: Matthew Garrett + * Matt Turner + * Dave Airlie + */ + +#include <linux/delay.h> + +#include "drmP.h" +#include "drm.h" +#include "drm_crtc_helper.h" + +#include "mgag200_drv.h" + +#define MGAG200_LUT_SIZE 256 + +/* + * This file contains setup code for the CRTC. + */ + +static void mga_crtc_load_lut(struct drm_crtc *crtc) +{ + struct mga_crtc *mga_crtc = to_mga_crtc(crtc); + struct drm_device *dev = crtc->dev; + struct mga_device *mdev = dev->dev_private; + int i; + + if (!crtc->enabled) + return; + + WREG8(DAC_INDEX + MGA1064_INDEX, 0); + + for (i = 0; i < MGAG200_LUT_SIZE; i++) { + /* VGA registers */ + WREG8(DAC_INDEX + MGA1064_COL_PAL, mga_crtc->lut_r[i]); + WREG8(DAC_INDEX + MGA1064_COL_PAL, mga_crtc->lut_g[i]); + WREG8(DAC_INDEX + MGA1064_COL_PAL, mga_crtc->lut_b[i]); + } +} + +static inline void mga_wait_vsync(struct mga_device *mdev) +{ + unsigned int count = 0; + unsigned int status = 0; + + do { + status = RREG32(MGAREG_Status); + count++; + } while ((status & 0x08) && (count < 250000)); + count = 0; + status = 0; + do { + status = RREG32(MGAREG_Status); + count++; + } while (!(status & 0x08) && (count < 250000)); +} + +static inline void mga_wait_busy(struct mga_device *mdev) +{ + unsigned int count = 0; + unsigned int status = 0; + do { + status = RREG8(MGAREG_Status + 2); + count++; + } while ((status & 0x01) && (count < 500000)); +} + +/* + * The core passes the desired mode to the CRTC code to see whether any + * CRTC-specific modifications need to be made to it. We're in a position + * to just pass that straight through, so this does nothing + */ +static bool mga_crtc_mode_fixup(struct drm_crtc *crtc, + struct drm_display_mode *mode, + struct drm_display_mode *adjusted_mode) +{ + return true; +} + +static int mga_g200se_set_plls(struct mga_device *mdev, long clock) +{ + unsigned int vcomax, vcomin, pllreffreq; + unsigned int delta, tmpdelta, permitteddelta; + unsigned int testp, testm, testn; + unsigned int p, m, n; + unsigned int computed; + + m = n = p = 0; + vcomax = 320000; + vcomin = 160000; + pllreffreq = 25000; + + delta = 0xffffffff; + permitteddelta = clock * 5 / 1000; + + for (testp = 8; testp > 0; testp /= 2) { + if (clock * testp > vcomax) + continue; + if (clock * testp < vcomin) + continue; + + for (testn = 17; testn < 256; testn++) { + for (testm = 1; testm < 32; testm++) { + computed = (pllreffreq * testn) / + (testm * testp); + if (computed > clock) + tmpdelta = computed - clock; + else + tmpdelta = clock - computed; + if (tmpdelta < delta) { + delta = tmpdelta; + m = testm - 1; + n = testn - 1; + p = testp - 1; + } + } + } + } + + if (delta > permitteddelta) { + printk(KERN_WARNING "PLL delta too large\n"); + return 1; + } + + WREG_DAC(MGA1064_PIX_PLLC_M, m); + WREG_DAC(MGA1064_PIX_PLLC_N, n); + WREG_DAC(MGA1064_PIX_PLLC_P, p); + return 0; +} + +static int mga_g200wb_set_plls(struct mga_device *mdev, long clock) +{ + unsigned int vcomax, vcomin, pllreffreq; + unsigned int delta, tmpdelta, permitteddelta; + unsigned int testp, testm, testn; + unsigned int p, m, n; + unsigned int computed; + int i, j, tmpcount, vcount; + bool pll_locked = false; + u8 tmp; + + m = n = p = 0; + vcomax = 550000; + vcomin = 150000; + pllreffreq = 48000; + + delta = 0xffffffff; + permitteddelta = clock * 5 / 1000; + + for (testp = 1; testp < 9; testp++) { + if (clock * testp > vcomax) + continue; + if (clock * testp < vcomin) + continue; + + for (testm = 1; testm < 17; testm++) { + for (testn = 1; testn < 151; testn++) { + computed = (pllreffreq * testn) / + (testm * testp); + if (computed > clock) + tmpdelta = computed - clock; + else + tmpdelta = clock - computed; + if (tmpdelta < delta) { + delta = tmpdelta; + n = testn - 1; + m = (testm - 1) | ((n >> 1) & 0x80); + p = testp - 1; + } + } + } + } + + for (i = 0; i <= 32 && pll_locked == false; i++) { + if (i > 0) { + WREG8(MGAREG_CRTC_INDEX, 0x1e); + tmp = RREG8(MGAREG_CRTC_DATA); + if (tmp < 0xff) + WREG8(MGAREG_CRTC_DATA, tmp+1); + } + + /* set pixclkdis to 1 */ + WREG8(DAC_INDEX, MGA1064_PIX_CLK_CTL); + tmp = RREG8(DAC_DATA); + tmp |= MGA1064_PIX_CLK_CTL_CLK_DIS; + WREG_DAC(MGA1064_PIX_CLK_CTL_CLK_DIS, tmp); + + WREG8(DAC_INDEX, MGA1064_REMHEADCTL); + tmp = RREG8(DAC_DATA); + tmp |= MGA1064_REMHEADCTL_CLKDIS; + WREG_DAC(MGA1064_REMHEADCTL, tmp); + + /* select PLL Set C */ + tmp = RREG8(MGAREG_MEM_MISC_READ); + tmp |= 0x3 << 2; + WREG8(MGAREG_MEM_MISC_WRITE, tmp); + + WREG8(DAC_INDEX, MGA1064_PIX_CLK_CTL); + tmp = RREG8(DAC_DATA); + tmp |= MGA1064_PIX_CLK_CTL_CLK_POW_DOWN | 0x80; + WREG_DAC(MGA1064_PIX_CLK_CTL, tmp); + + udelay(500); + + /* reset the PLL */ + WREG8(DAC_INDEX, MGA1064_VREF_CTL); + tmp = RREG8(DAC_DATA); + tmp &= ~0x04; + WREG_DAC(MGA1064_VREF_CTL, tmp); + + udelay(50); + + /* program pixel pll register */ + WREG_DAC(MGA1064_WB_PIX_PLLC_N, n); + WREG_DAC(MGA1064_WB_PIX_PLLC_M, m); + WREG_DAC(MGA1064_WB_PIX_PLLC_P, p); + + udelay(50); + + /* turn pll on */ + WREG8(DAC_INDEX, MGA1064_VREF_CTL); + tmp = RREG8(DAC_DATA); + tmp |= 0x04; + WREG_DAC(MGA1064_VREF_CTL, tmp); + + udelay(500); + + /* select the pixel pll */ + WREG8(DAC_INDEX, MGA1064_PIX_CLK_CTL); + tmp = RREG8(DAC_DATA); + tmp &= ~MGA1064_PIX_CLK_CTL_SEL_MSK; + tmp |= MGA1064_PIX_CLK_CTL_SEL_PLL; + WREG_DAC(MGA1064_PIX_CLK_CTL, tmp); + + WREG8(DAC_INDEX, MGA1064_REMHEADCTL); + tmp = RREG8(DAC_DATA); + tmp &= ~MGA1064_REMHEADCTL_CLKSL_MSK; + tmp |= MGA1064_REMHEADCTL_CLKSL_PLL; + WREG_DAC(MGA1064_REMHEADCTL, tmp); + + /* reset dotclock rate bit */ + WREG8(MGAREG_SEQ_INDEX, 1); + tmp = RREG8(MGAREG_SEQ_DATA); + tmp &= ~0x8; + WREG8(MGAREG_SEQ_DATA, tmp); + + WREG8(DAC_INDEX, MGA1064_PIX_CLK_CTL); + tmp = RREG8(DAC_DATA); + tmp &= ~MGA1064_PIX_CLK_CTL_CLK_DIS; + WREG_DAC(MGA1064_PIX_CLK_CTL, tmp); + + vcount = RREG8(MGAREG_VCOUNT); + + for (j = 0; j < 30 && pll_locked == false; j++) { + tmpcount = RREG8(MGAREG_VCOUNT); + if (tmpcount < vcount) + vcount = 0; + if ((tmpcount - vcount) > 2) + pll_locked = true; + else + udelay(5); + } + } + WREG8(DAC_INDEX, MGA1064_REMHEADCTL); + tmp = RREG8(DAC_DATA); + tmp &= ~MGA1064_REMHEADCTL_CLKDIS; + WREG_DAC(MGA1064_REMHEADCTL, tmp); + return 0; +} + +static int mga_g200ev_set_plls(struct mga_device *mdev, long clock) +{ + unsigned int vcomax, vcomin, pllreffreq; + unsigned int delta, tmpdelta, permitteddelta; + unsigned int testp, testm, testn; + unsigned int p, m, n; + unsigned int computed; + u8 tmp; + + m = n = p = 0; + vcomax = 550000; + vcomin = 150000; + pllreffreq = 50000; + + delta = 0xffffffff; + permitteddelta = clock * 5 / 1000; + + for (testp = 16; testp > 0; testp--) { + if (clock * testp > vcomax) + continue; + if (clock * testp < vcomin) + continue; + + for (testn = 1; testn < 257; testn++) { + for (testm = 1; testm < 17; testm++) { + computed = (pllreffreq * testn) / + (testm * testp); + if (computed > clock) + tmpdelta = computed - clock; + else + tmpdelta = clock - computed; + if (tmpdelta < delta) { + delta = tmpdelta; + n = testn - 1; + m = testm - 1; + p = testp - 1; + } + } + } + } + + WREG8(DAC_INDEX, MGA1064_PIX_CLK_CTL); + tmp = RREG8(DAC_DATA); + tmp |= MGA1064_PIX_CLK_CTL_CLK_DIS; + WREG_DAC(MGA1064_PIX_CLK_CTL_CLK_DIS, tmp); + + tmp = RREG8(MGAREG_MEM_MISC_READ); + tmp |= 0x3 << 2; + WREG8(MGAREG_MEM_MISC_WRITE, tmp); + + WREG8(DAC_INDEX, MGA1064_PIX_PLL_STAT); + tmp = RREG8(DAC_DATA); + WREG_DAC(MGA1064_PIX_PLL_STAT, tmp & ~0x40); + + WREG8(DAC_INDEX, MGA1064_PIX_CLK_CTL); + tmp = RREG8(DAC_DATA); + tmp |= MGA1064_PIX_CLK_CTL_CLK_POW_DOWN; + WREG_DAC(MGA1064_PIX_CLK_CTL, tmp); + + WREG_DAC(MGA1064_EV_PIX_PLLC_M, m); + WREG_DAC(MGA1064_EV_PIX_PLLC_N, n); + WREG_DAC(MGA1064_EV_PIX_PLLC_P, p); + + udelay(50); + + WREG8(DAC_INDEX, MGA1064_PIX_CLK_CTL); + tmp = RREG8(DAC_DATA); + tmp &= ~MGA1064_PIX_CLK_CTL_CLK_POW_DOWN; + WREG_DAC(MGA1064_PIX_CLK_CTL, tmp); + + udelay(500); + + WREG8(DAC_INDEX, MGA1064_PIX_CLK_CTL); + tmp = RREG8(DAC_DATA); + tmp &= ~MGA1064_PIX_CLK_CTL_SEL_MSK; + tmp |= MGA1064_PIX_CLK_CTL_SEL_PLL; + WREG_DAC(MGA1064_PIX_CLK_CTL, tmp); + + WREG8(DAC_INDEX, MGA1064_PIX_PLL_STAT); + tmp = RREG8(DAC_DATA); + WREG_DAC(MGA1064_PIX_PLL_STAT, tmp | 0x40); + + tmp = RREG8(MGAREG_MEM_MISC_READ); + tmp |= (0x3 << 2); + WREG8(MGAREG_MEM_MISC_WRITE, tmp); + + WREG8(DAC_INDEX, MGA1064_PIX_CLK_CTL); + tmp = RREG8(DAC_DATA); + tmp &= ~MGA1064_PIX_CLK_CTL_CLK_DIS; + WREG_DAC(MGA1064_PIX_CLK_CTL, tmp); + + return 0; +} + +static int mga_g200eh_set_plls(struct mga_device *mdev, long clock) +{ + unsigned int vcomax, vcomin, pllreffreq; + unsigned int delta, tmpdelta, permitteddelta; + unsigned int testp, testm, testn; + unsigned int p, m, n; + unsigned int computed; + int i, j, tmpcount, vcount; + u8 tmp; + bool pll_locked = false; + + m = n = p = 0; + vcomax = 800000; + vcomin = 400000; + pllreffreq = 3333; + + delta = 0xffffffff; + permitteddelta = clock * 5 / 1000; + + for (testp = 16; testp > 0; testp--) { + if (clock * testp > vcomax) + continue; + if (clock * testp < vcomin) + continue; + + for (testm = 1; testm < 33; testm++) { + for (testn = 1; testn < 257; testn++) { + computed = (pllreffreq * testn) / + (testm * testp); + if (computed > clock) + tmpdelta = computed - clock; + else + tmpdelta = clock - computed; + if (tmpdelta < delta) { + delta = tmpdelta; + n = testn - 1; + m = (testm - 1) | ((n >> 1) & 0x80); + p = testp - 1; + } + if ((clock * testp) >= 600000) + p |= 80; + } + } + } + for (i = 0; i <= 32 && pll_locked == false; i++) { + WREG8(DAC_INDEX, MGA1064_PIX_CLK_CTL); + tmp = RREG8(DAC_DATA); + tmp |= MGA1064_PIX_CLK_CTL_CLK_DIS; + WREG_DAC(MGA1064_PIX_CLK_CTL_CLK_DIS, tmp); + + tmp = RREG8(MGAREG_MEM_MISC_READ); + tmp |= 0x3 << 2; + WREG8(MGAREG_MEM_MISC_WRITE, tmp); + + WREG8(DAC_INDEX, MGA1064_PIX_CLK_CTL); + tmp = RREG8(DAC_DATA); + tmp |= MGA1064_PIX_CLK_CTL_CLK_POW_DOWN; + WREG_DAC(MGA1064_PIX_CLK_CTL, tmp); + + udelay(500); + + WREG_DAC(MGA1064_EH_PIX_PLLC_M, m); + WREG_DAC(MGA1064_EH_PIX_PLLC_N, n); + WREG_DAC(MGA1064_EH_PIX_PLLC_P, p); + + udelay(500); + + WREG8(DAC_INDEX, MGA1064_PIX_CLK_CTL); + tmp = RREG8(DAC_DATA); + tmp &= ~MGA1064_PIX_CLK_CTL_SEL_MSK; + tmp |= MGA1064_PIX_CLK_CTL_SEL_PLL; + WREG_DAC(MGA1064_PIX_CLK_CTL, tmp); + + WREG8(DAC_INDEX, MGA1064_PIX_CLK_CTL); + tmp = RREG8(DAC_DATA); + tmp &= ~MGA1064_PIX_CLK_CTL_CLK_DIS; + tmp &= ~MGA1064_PIX_CLK_CTL_CLK_POW_DOWN; + WREG_DAC(MGA1064_PIX_CLK_CTL, tmp); + + vcount = RREG8(MGAREG_VCOUNT); + + for (j = 0; j < 30 && pll_locked == false; j++) { + tmpcount = RREG8(MGAREG_VCOUNT); + if (tmpcount < vcount) + vcount = 0; + if ((tmpcount - vcount) > 2) + pll_locked = true; + else + udelay(5); + } + } + + return 0; +} + +static int mga_g200er_set_plls(struct mga_device *mdev, long clock) +{ + unsigned int vcomax, vcomin, pllreffreq; + unsigned int delta, tmpdelta; + unsigned int testr, testn, testm, testo; + unsigned int p, m, n; + unsigned int computed; + int tmp; + + m = n = p = 0; + vcomax = 1488000; + vcomin = 1056000; + pllreffreq = 48000; + + delta = 0xffffffff; + + for (testr = 0; testr < 4; testr++) { + if (delta == 0) + break; + for (testn = 5; testn < 129; testn++) { + if (delta == 0) + break; + for (testm = 3; testm >= 0; testm--) { + if (delta == 0) + break; + for (testo = 5; testo < 33; testo++) { + computed = pllreffreq * (testn + 1) / + (testr + 1); + if (computed < vcomin) + continue; + if (computed > vcomax) + continue; + if (computed > clock) + tmpdelta = computed - clock; + else + tmpdelta = clock - computed; + if (tmpdelta < delta) { + delta = tmpdelta; + m = testm | (testo << 3); + n = testn; + p = testr | (testr << 3); + } + } + } + } + } + + WREG8(DAC_INDEX, MGA1064_PIX_CLK_CTL); + tmp = RREG8(DAC_DATA); + tmp |= MGA1064_PIX_CLK_CTL_CLK_DIS; + WREG_DAC(MGA1064_PIX_CLK_CTL_CLK_DIS, tmp); + + WREG8(DAC_INDEX, MGA1064_REMHEADCTL); + tmp = RREG8(DAC_DATA); + tmp |= MGA1064_REMHEADCTL_CLKDIS; + WREG_DAC(MGA1064_REMHEADCTL, tmp); + + tmp = RREG8(MGAREG_MEM_MISC_READ); + tmp |= (0x3<<2) | 0xc0; + WREG8(MGAREG_MEM_MISC_WRITE, tmp); + + WREG8(DAC_INDEX, MGA1064_PIX_CLK_CTL); + tmp = RREG8(DAC_DATA); + tmp &= ~MGA1064_PIX_CLK_CTL_CLK_DIS; + tmp |= MGA1064_PIX_CLK_CTL_CLK_POW_DOWN; + WREG_DAC(MGA1064_PIX_CLK_CTL, tmp); + + udelay(500); + + WREG_DAC(MGA1064_ER_PIX_PLLC_N, n); + WREG_DAC(MGA1064_ER_PIX_PLLC_M, m); + WREG_DAC(MGA1064_ER_PIX_PLLC_P, p); + + udelay(50); + + return 0; +} + +static int mga_crtc_set_plls(struct mga_device *mdev, long clock) +{ + switch(mdev->type) { + case G200_SE_A: + case G200_SE_B: + return mga_g200se_set_plls(mdev, clock); + break; + case G200_WB: + return mga_g200wb_set_plls(mdev, clock); + break; + case G200_EV: + return mga_g200ev_set_plls(mdev, clock); + break; + case G200_EH: + return mga_g200eh_set_plls(mdev, clock); + break; + case G200_ER: + return mga_g200er_set_plls(mdev, clock); + break; + } + return 0; +} + +static void mga_g200wb_prepare(struct drm_crtc *crtc) +{ + struct mga_device *mdev = crtc->dev->dev_private; + u8 tmp; + int iter_max; + + /* 1- The first step is to warn the BMC of an upcoming mode change. + * We are putting the misc<0> to output.*/ + + WREG8(DAC_INDEX, MGA1064_GEN_IO_CTL); + tmp = RREG8(DAC_DATA); + tmp |= 0x10; + WREG_DAC(MGA1064_GEN_IO_CTL, tmp); + + /* we are putting a 1 on the misc<0> line */ + WREG8(DAC_INDEX, MGA1064_GEN_IO_DATA); + tmp = RREG8(DAC_DATA); + tmp |= 0x10; + WREG_DAC(MGA1064_GEN_IO_DATA, tmp); + + /* 2- Second step to mask and further scan request + * This will be done by asserting the remfreqmsk bit (XSPAREREG<7>) + */ + WREG8(DAC_INDEX, MGA1064_SPAREREG); + tmp = RREG8(DAC_DATA); + tmp |= 0x80; + WREG_DAC(MGA1064_SPAREREG, tmp); + + /* 3a- the third step is to verifu if there is an active scan + * We are searching for a 0 on remhsyncsts <XSPAREREG<0>) + */ + iter_max = 300; + while (!(tmp & 0x1) && iter_max) { + WREG8(DAC_INDEX, MGA1064_SPAREREG); + tmp = RREG8(DAC_DATA); + udelay(1000); + iter_max--; + } + + /* 3b- this step occurs only if the remove is actually scanning + * we are waiting for the end of the frame which is a 1 on + * remvsyncsts (XSPAREREG<1>) + */ + if (iter_max) { + iter_max = 300; + while ((tmp & 0x2) && iter_max) { + WREG8(DAC_INDEX, MGA1064_SPAREREG); + tmp = RREG8(DAC_DATA); + udelay(1000); + iter_max--; + } + } +} + +static void mga_g200wb_commit(struct drm_crtc *crtc) +{ + u8 tmp; + struct mga_device *mdev = crtc->dev->dev_private; + + /* 1- The first step is to ensure that the vrsten and hrsten are set */ + WREG8(MGAREG_CRTCEXT_INDEX, 1); + tmp = RREG8(MGAREG_CRTCEXT_DATA); + WREG8(MGAREG_CRTCEXT_DATA, tmp | 0x88); + + /* 2- second step is to assert the rstlvl2 */ + WREG8(DAC_INDEX, MGA1064_REMHEADCTL2); + tmp = RREG8(DAC_DATA); + tmp |= 0x8; + WREG8(DAC_DATA, tmp); + + /* wait 10 us */ + udelay(10); + + /* 3- deassert rstlvl2 */ + tmp &= ~0x08; + WREG8(DAC_INDEX, MGA1064_REMHEADCTL2); + WREG8(DAC_DATA, tmp); + + /* 4- remove mask of scan request */ + WREG8(DAC_INDEX, MGA1064_SPAREREG); + tmp = RREG8(DAC_DATA); + tmp &= ~0x80; + WREG8(DAC_DATA, tmp); + + /* 5- put back a 0 on the misc<0> line */ + WREG8(DAC_INDEX, MGA1064_GEN_IO_DATA); + tmp = RREG8(DAC_DATA); + tmp &= ~0x10; + WREG_DAC(MGA1064_GEN_IO_DATA, tmp); +} + + +void mga_set_start_address(struct drm_crtc *crtc, unsigned offset) +{ + struct mga_device *mdev = crtc->dev->dev_private; + u32 addr; + int count; + + while (RREG8(0x1fda) & 0x08); + while (!(RREG8(0x1fda) & 0x08)); + + count = RREG8(MGAREG_VCOUNT) + 2; + while (RREG8(MGAREG_VCOUNT) < count); + + addr = offset >> 2; + WREG_CRT(0x0d, (u8)(addr & 0xff)); + WREG_CRT(0x0c, (u8)(addr >> 8) & 0xff); + WREG_CRT(0xaf, (u8)(addr >> 16) & 0xf); +} + + +/* ast is different - we will force move buffers out of VRAM */ +static int mga_crtc_do_set_base(struct drm_crtc *crtc, + struct drm_framebuffer *fb, + int x, int y, int atomic) +{ + struct mga_device *mdev = crtc->dev->dev_private; + struct drm_gem_object *obj; + struct mga_framebuffer *mga_fb; + struct mgag200_bo *bo; + int ret; + u64 gpu_addr; + + /* push the previous fb to system ram */ + if (!atomic && fb) { + mga_fb = to_mga_framebuffer(fb); + obj = mga_fb->obj; + bo = gem_to_mga_bo(obj); + ret = mgag200_bo_reserve(bo, false); + if (ret) + return ret; + mgag200_bo_push_sysram(bo); + mgag200_bo_unreserve(bo); + } + + mga_fb = to_mga_framebuffer(crtc->fb); + obj = mga_fb->obj; + bo = gem_to_mga_bo(obj); + + ret = mgag200_bo_reserve(bo, false); + if (ret) + return ret; + + ret = mgag200_bo_pin(bo, TTM_PL_FLAG_VRAM, &gpu_addr); + if (ret) { + mgag200_bo_unreserve(bo); + return ret; + } + + if (&mdev->mfbdev->mfb == mga_fb) { + /* if pushing console in kmap it */ + ret = ttm_bo_kmap(&bo->bo, 0, bo->bo.num_pages, &bo->kmap); + if (ret) + DRM_ERROR("failed to kmap fbcon\n"); + + } + mgag200_bo_unreserve(bo); + + DRM_INFO("mga base %llx\n", gpu_addr); + + mga_set_start_address(crtc, (u32)gpu_addr); + + return 0; +} + +static int mga_crtc_mode_set_base(struct drm_crtc *crtc, int x, int y, + struct drm_framebuffer *old_fb) +{ + return mga_crtc_do_set_base(crtc, old_fb, x, y, 0); +} + +static int mga_crtc_mode_set(struct drm_crtc *crtc, + struct drm_display_mode *mode, + struct drm_display_mode *adjusted_mode, + int x, int y, struct drm_framebuffer *old_fb) +{ + struct drm_device *dev = crtc->dev; + struct mga_device *mdev = dev->dev_private; + int hdisplay, hsyncstart, hsyncend, htotal; + int vdisplay, vsyncstart, vsyncend, vtotal; + int pitch; + int option = 0, option2 = 0; + int i; + unsigned char misc = 0; + unsigned char ext_vga[6]; + unsigned char ext_vga_index24; + unsigned char dac_index90 = 0; + u8 bppshift; + + static unsigned char dacvalue[] = { + /* 0x00: */ 0, 0, 0, 0, 0, 0, 0x00, 0, + /* 0x08: */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x10: */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x18: */ 0x00, 0, 0xC9, 0xFF, 0xBF, 0x20, 0x1F, 0x20, + /* 0x20: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + /* 0x28: */ 0x00, 0x00, 0x00, 0x00, 0, 0, 0, 0x40, + /* 0x30: */ 0x00, 0xB0, 0x00, 0xC2, 0x34, 0x14, 0x02, 0x83, + /* 0x38: */ 0x00, 0x93, 0x00, 0x77, 0x00, 0x00, 0x00, 0x3A, + /* 0x40: */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x48: */ 0, 0, 0, 0, 0, 0, 0, 0 + }; + + bppshift = mdev->bpp_shifts[(crtc->fb->bits_per_pixel >> 3) - 1]; + + switch (mdev->type) { + case G200_SE_A: + case G200_SE_B: + dacvalue[MGA1064_VREF_CTL] = 0x03; + dacvalue[MGA1064_PIX_CLK_CTL] = MGA1064_PIX_CLK_CTL_SEL_PLL; + dacvalue[MGA1064_MISC_CTL] = MGA1064_MISC_CTL_DAC_EN | + MGA1064_MISC_CTL_VGA8 | + MGA1064_MISC_CTL_DAC_RAM_CS; + if (mdev->has_sdram) + option = 0x40049120; + else + option = 0x4004d120; + option2 = 0x00008000; + break; + case G200_WB: + dacvalue[MGA1064_VREF_CTL] = 0x07; + option = 0x41049120; + option2 = 0x0000b000; + break; + case G200_EV: + dacvalue[MGA1064_PIX_CLK_CTL] = MGA1064_PIX_CLK_CTL_SEL_PLL; + dacvalue[MGA1064_MISC_CTL] = MGA1064_MISC_CTL_VGA8 | + MGA1064_MISC_CTL_DAC_RAM_CS; + option = 0x00000120; + option2 = 0x0000b000; + break; + case G200_EH: + dacvalue[MGA1064_MISC_CTL] = MGA1064_MISC_CTL_VGA8 | + MGA1064_MISC_CTL_DAC_RAM_CS; + option = 0x00000120; + option2 = 0x0000b000; + break; + case G200_ER: + dac_index90 = 0; + break; + } + + switch (crtc->fb->bits_per_pixel) { + case 8: + dacvalue[MGA1064_MUL_CTL] = MGA1064_MUL_CTL_8bits; + break; + case 16: + if (crtc->fb->depth == 15) + dacvalue[MGA1064_MUL_CTL] = MGA1064_MUL_CTL_15bits; + else + dacvalue[MGA1064_MUL_CTL] = MGA1064_MUL_CTL_16bits; + break; + case 24: + dacvalue[MGA1064_MUL_CTL] = MGA1064_MUL_CTL_24bits; + break; + case 32: + dacvalue[MGA1064_MUL_CTL] = MGA1064_MUL_CTL_32_24bits; + break; + } + + if (mode->flags & DRM_MODE_FLAG_NHSYNC) + misc |= 0x40; + if (mode->flags & DRM_MODE_FLAG_NVSYNC) + misc |= 0x80; + + + for (i = 0; i < sizeof(dacvalue); i++) { + if ((i <= 0x03) || + (i == 0x07) || + (i == 0x0b) || + (i == 0x0f) || + ((i >= 0x13) && (i <= 0x17)) || + (i == 0x1b) || + (i == 0x1c) || + ((i >= 0x1f) && (i <= 0x29)) || + ((i >= 0x30) && (i <= 0x37))) + continue; + if (IS_G200_SE(mdev) && + ((i == 0x2c) || (i == 0x2d) || (i == 0x2e))) + continue; + if ((mdev->type == G200_EV || mdev->type == G200_WB || mdev->type == G200_EH) && + (i >= 0x44) && (i <= 0x4e)) + continue; + + WREG_DAC(i, dacvalue[i]); + } + + if (mdev->type == G200_ER) { + WREG_DAC(0x90, dac_index90); + } + + + if (option) + pci_write_config_dword(dev->pdev, PCI_MGA_OPTION, option); + if (option2) + pci_write_config_dword(dev->pdev, PCI_MGA_OPTION2, option2); + + WREG_SEQ(2, 0xf); + WREG_SEQ(3, 0); + WREG_SEQ(4, 0xe); + + pitch = crtc->fb->pitches[0] / (crtc->fb->bits_per_pixel / 8); + if (crtc->fb->bits_per_pixel == 24) + pitch = pitch >> (4 - bppshift); + else + pitch = pitch >> (4 - bppshift); + + hdisplay = mode->hdisplay / 8 - 1; + hsyncstart = mode->hsync_start / 8 - 1; + hsyncend = mode->hsync_end / 8 - 1; + htotal = mode->htotal / 8 - 1; + + /* Work around hardware quirk */ + if ((htotal & 0x07) == 0x06 || (htotal & 0x07) == 0x04) + htotal++; + + vdisplay = mode->vdisplay - 1; + vsyncstart = mode->vsync_start - 1; + vsyncend = mode->vsync_end - 1; + vtotal = mode->vtotal - 2; + + WREG_GFX(0, 0); + WREG_GFX(1, 0); + WREG_GFX(2, 0); + WREG_GFX(3, 0); + WREG_GFX(4, 0); + WREG_GFX(5, 0x40); + WREG_GFX(6, 0x5); + WREG_GFX(7, 0xf); + WREG_GFX(8, 0xf); + + WREG_CRT(0, htotal - 4); + WREG_CRT(1, hdisplay); + WREG_CRT(2, hdisplay); + WREG_CRT(3, (htotal & 0x1F) | 0x80); + WREG_CRT(4, hsyncstart); + WREG_CRT(5, ((htotal & 0x20) << 2) | (hsyncend & 0x1F)); + WREG_CRT(6, vtotal & 0xFF); + WREG_CRT(7, ((vtotal & 0x100) >> 8) | + ((vdisplay & 0x100) >> 7) | + ((vsyncstart & 0x100) >> 6) | + ((vdisplay & 0x100) >> 5) | + ((vdisplay & 0x100) >> 4) | /* linecomp */ + ((vtotal & 0x200) >> 4)| + ((vdisplay & 0x200) >> 3) | + ((vsyncstart & 0x200) >> 2)); + WREG_CRT(9, ((vdisplay & 0x200) >> 4) | + ((vdisplay & 0x200) >> 3)); + WREG_CRT(10, 0); + WREG_CRT(11, 0); + WREG_CRT(12, 0); + WREG_CRT(13, 0); + WREG_CRT(14, 0); + WREG_CRT(15, 0); + WREG_CRT(16, vsyncstart & 0xFF); + WREG_CRT(17, (vsyncend & 0x0F) | 0x20); + WREG_CRT(18, vdisplay & 0xFF); + WREG_CRT(19, pitch & 0xFF); + WREG_CRT(20, 0); + WREG_CRT(21, vdisplay & 0xFF); + WREG_CRT(22, (vtotal + 1) & 0xFF); + WREG_CRT(23, 0xc3); + WREG_CRT(24, vdisplay & 0xFF); + + ext_vga[0] = 0; + ext_vga[5] = 0; + + /* TODO interlace */ + + ext_vga[0] |= (pitch & 0x300) >> 4; + ext_vga[1] = (((htotal - 4) & 0x100) >> 8) | + ((hdisplay & 0x100) >> 7) | + ((hsyncstart & 0x100) >> 6) | + (htotal & 0x40); + ext_vga[2] = ((vtotal & 0xc00) >> 10) | + ((vdisplay & 0x400) >> 8) | + ((vdisplay & 0xc00) >> 7) | + ((vsyncstart & 0xc00) >> 5) | + ((vdisplay & 0x400) >> 3); + if (crtc->fb->bits_per_pixel == 24) + ext_vga[3] = (((1 << bppshift) * 3) - 1) | 0x80; + else + ext_vga[3] = ((1 << bppshift) - 1) | 0x80; + ext_vga[4] = 0; + if (mdev->type == G200_WB) + ext_vga[1] |= 0x88; + + ext_vga_index24 = 0x05; + + /* Set pixel clocks */ + misc = 0x2d; + WREG8(MGA_MISC_OUT, misc); + + mga_crtc_set_plls(mdev, mode->clock); + + for (i = 0; i < 6; i++) { + WREG_ECRT(i, ext_vga[i]); + } + + if (mdev->type == G200_ER) + WREG_ECRT(24, ext_vga_index24); + + if (mdev->type == G200_EV) { + WREG_ECRT(6, 0); + } + + WREG_ECRT(0, ext_vga[0]); + /* Enable mga pixel clock */ + misc = 0x2d; + + WREG8(MGA_MISC_OUT, misc); + + if (adjusted_mode) + memcpy(&mdev->mode, mode, sizeof(struct drm_display_mode)); + + mga_crtc_do_set_base(crtc, old_fb, x, y, 0); + + /* reset tagfifo */ + if (mdev->type == G200_ER) { + u32 mem_ctl = RREG32(MGAREG_MEMCTL); + u8 seq1; + + /* screen off */ + WREG8(MGAREG_SEQ_INDEX, 0x01); + seq1 = RREG8(MGAREG_SEQ_DATA) | 0x20; + WREG8(MGAREG_SEQ_DATA, seq1); + + WREG32(MGAREG_MEMCTL, mem_ctl | 0x00200000); + udelay(1000); + WREG32(MGAREG_MEMCTL, mem_ctl & ~0x00200000); + + WREG8(MGAREG_SEQ_DATA, seq1 & ~0x20); + } + + + if (IS_G200_SE(mdev)) { + if (mdev->reg_1e24 >= 0x02) { + u8 hi_pri_lvl; + u32 bpp; + u32 mb; + + if (crtc->fb->bits_per_pixel > 16) + bpp = 32; + else if (crtc->fb->bits_per_pixel > 8) + bpp = 16; + else + bpp = 8; + + mb = (mode->clock * bpp) / 1000; + if (mb > 3100) + hi_pri_lvl = 0; + else if (mb > 2600) + hi_pri_lvl = 1; + else if (mb > 1900) + hi_pri_lvl = 2; + else if (mb > 1160) + hi_pri_lvl = 3; + else if (mb > 440) + hi_pri_lvl = 4; + else + hi_pri_lvl = 5; + + WREG8(0x1fde, 0x06); + WREG8(0x1fdf, hi_pri_lvl); + } else { + if (mdev->reg_1e24 >= 0x01) + WREG8(0x1fdf, 0x03); + else + WREG8(0x1fdf, 0x04); + } + } + return 0; +} + +#if 0 /* code from mjg to attempt D3 on crtc dpms off - revisit later */ +static int mga_suspend(struct drm_crtc *crtc) +{ + struct mga_crtc *mga_crtc = to_mga_crtc(crtc); + struct drm_device *dev = crtc->dev; + struct mga_device *mdev = dev->dev_private; + struct pci_dev *pdev = dev->pdev; + int option; + + if (mdev->suspended) + return 0; + + WREG_SEQ(1, 0x20); + WREG_ECRT(1, 0x30); + /* Disable the pixel clock */ + WREG_DAC(0x1a, 0x05); + /* Power down the DAC */ + WREG_DAC(0x1e, 0x18); + /* Power down the pixel PLL */ + WREG_DAC(0x1a, 0x0d); + + /* Disable PLLs and clocks */ + pci_read_config_dword(pdev, PCI_MGA_OPTION, &option); + option &= ~(0x1F8024); + pci_write_config_dword(pdev, PCI_MGA_OPTION, option); + pci_set_power_state(pdev, PCI_D3hot); + pci_disable_device(pdev); + + mdev->suspended = true; + + return 0; +} + +static int mga_resume(struct drm_crtc *crtc) +{ + struct mga_crtc *mga_crtc = to_mga_crtc(crtc); + struct drm_device *dev = crtc->dev; + struct mga_device *mdev = dev->dev_private; + struct pci_dev *pdev = dev->pdev; + int option; + + if (!mdev->suspended) + return 0; + + pci_set_power_state(pdev, PCI_D0); + pci_enable_device(pdev); + + /* Disable sysclk */ + pci_read_config_dword(pdev, PCI_MGA_OPTION, &option); + option &= ~(0x4); + pci_write_config_dword(pdev, PCI_MGA_OPTION, option); + + mdev->suspended = false; + + return 0; +} + +#endif + +static void mga_crtc_dpms(struct drm_crtc *crtc, int mode) +{ + struct drm_device *dev = crtc->dev; + struct mga_device *mdev = dev->dev_private; + u8 seq1 = 0, crtcext1 = 0; + + switch (mode) { + case DRM_MODE_DPMS_ON: + seq1 = 0; + crtcext1 = 0; + mga_crtc_load_lut(crtc); + break; + case DRM_MODE_DPMS_STANDBY: + seq1 = 0x20; + crtcext1 = 0x10; + break; + case DRM_MODE_DPMS_SUSPEND: + seq1 = 0x20; + crtcext1 = 0x20; + break; + case DRM_MODE_DPMS_OFF: + seq1 = 0x20; + crtcext1 = 0x30; + break; + } + +#if 0 + if (mode == DRM_MODE_DPMS_OFF) { + mga_suspend(crtc); + } +#endif + WREG8(MGAREG_SEQ_INDEX, 0x01); + seq1 |= RREG8(MGAREG_SEQ_DATA) & ~0x20; + mga_wait_vsync(mdev); + mga_wait_busy(mdev); + WREG8(MGAREG_SEQ_DATA, seq1); + msleep(20); + WREG8(MGAREG_CRTCEXT_INDEX, 0x01); + crtcext1 |= RREG8(MGAREG_CRTCEXT_DATA) & ~0x30; + WREG8(MGAREG_CRTCEXT_DATA, crtcext1); + +#if 0 + if (mode == DRM_MODE_DPMS_ON && mdev->suspended == true) { + mga_resume(crtc); + drm_helper_resume_force_mode(dev); + } +#endif +} + +/* + * This is called before a mode is programmed. A typical use might be to + * enable DPMS during the programming to avoid seeing intermediate stages, + * but that's not relevant to us + */ +static void mga_crtc_prepare(struct drm_crtc *crtc) +{ + struct drm_device *dev = crtc->dev; + struct mga_device *mdev = dev->dev_private; + u8 tmp; + + /* mga_resume(crtc);*/ + + WREG8(MGAREG_CRTC_INDEX, 0x11); + tmp = RREG8(MGAREG_CRTC_DATA); + WREG_CRT(0x11, tmp | 0x80); + + if (mdev->type == G200_SE_A || mdev->type == G200_SE_B) { + WREG_SEQ(0, 1); + msleep(50); + WREG_SEQ(1, 0x20); + msleep(20); + } else { + WREG8(MGAREG_SEQ_INDEX, 0x1); + tmp = RREG8(MGAREG_SEQ_DATA); + + /* start sync reset */ + WREG_SEQ(0, 1); + WREG_SEQ(1, tmp | 0x20); + } + + if (mdev->type == G200_WB) + mga_g200wb_prepare(crtc); + + WREG_CRT(17, 0); +} + +/* + * This is called after a mode is programmed. It should reverse anything done + * by the prepare function + */ +static void mga_crtc_commit(struct drm_crtc *crtc) +{ + struct drm_device *dev = crtc->dev; + struct mga_device *mdev = dev->dev_private; + struct drm_crtc_helper_funcs *crtc_funcs = crtc->helper_private; + u8 tmp; + + if (mdev->type == G200_WB) + mga_g200wb_commit(crtc); + + if (mdev->type == G200_SE_A || mdev->type == G200_SE_B) { + msleep(50); + WREG_SEQ(1, 0x0); + msleep(20); + WREG_SEQ(0, 0x3); + } else { + WREG8(MGAREG_SEQ_INDEX, 0x1); + tmp = RREG8(MGAREG_SEQ_DATA); + + tmp &= ~0x20; + WREG_SEQ(0x1, tmp); + WREG_SEQ(0, 3); + } + crtc_funcs->dpms(crtc, DRM_MODE_DPMS_ON); +} + +/* + * The core can pass us a set of gamma values to program. We actually only + * use this for 8-bit mode so can't perform smooth fades on deeper modes, + * but it's a requirement that we provide the function + */ +static void mga_crtc_gamma_set(struct drm_crtc *crtc, u16 *red, u16 *green, + u16 *blue, uint32_t start, uint32_t size) +{ + struct mga_crtc *mga_crtc = to_mga_crtc(crtc); + int end = (start + size > MGAG200_LUT_SIZE) ? MGAG200_LUT_SIZE : start + size; + int i; + + for (i = start; i < end; i++) { + mga_crtc->lut_r[i] = red[i] >> 8; + mga_crtc->lut_g[i] = green[i] >> 8; + mga_crtc->lut_b[i] = blue[i] >> 8; + } + mga_crtc_load_lut(crtc); +} + +/* Simple cleanup function */ +static void mga_crtc_destroy(struct drm_crtc *crtc) +{ + struct mga_crtc *mga_crtc = to_mga_crtc(crtc); + + drm_crtc_cleanup(crtc); + kfree(mga_crtc); +} + +/* These provide the minimum set of functions required to handle a CRTC */ +static const struct drm_crtc_funcs mga_crtc_funcs = { + .gamma_set = mga_crtc_gamma_set, + .set_config = drm_crtc_helper_set_config, + .destroy = mga_crtc_destroy, +}; + +static const struct drm_crtc_helper_funcs mga_helper_funcs = { + .dpms = mga_crtc_dpms, + .mode_fixup = mga_crtc_mode_fixup, + .mode_set = mga_crtc_mode_set, + .mode_set_base = mga_crtc_mode_set_base, + .prepare = mga_crtc_prepare, + .commit = mga_crtc_commit, + .load_lut = mga_crtc_load_lut, +}; + +/* CRTC setup */ +static void mga_crtc_init(struct drm_device *dev) +{ + struct mga_device *mdev = dev->dev_private; + struct mga_crtc *mga_crtc; + int i; + + mga_crtc = kzalloc(sizeof(struct mga_crtc) + + (MGAG200FB_CONN_LIMIT * sizeof(struct drm_connector *)), + GFP_KERNEL); + + if (mga_crtc == NULL) + return; + + drm_crtc_init(dev, &mga_crtc->base, &mga_crtc_funcs); + + drm_mode_crtc_set_gamma_size(&mga_crtc->base, MGAG200_LUT_SIZE); + mdev->mode_info.crtc = mga_crtc; + + for (i = 0; i < MGAG200_LUT_SIZE; i++) { + mga_crtc->lut_r[i] = i; + mga_crtc->lut_g[i] = i; + mga_crtc->lut_b[i] = i; + } + + drm_crtc_helper_add(&mga_crtc->base, &mga_helper_funcs); +} + +/** Sets the color ramps on behalf of fbcon */ +void mga_crtc_fb_gamma_set(struct drm_crtc *crtc, u16 red, u16 green, + u16 blue, int regno) +{ + struct mga_crtc *mga_crtc = to_mga_crtc(crtc); + + mga_crtc->lut_r[regno] = red >> 8; + mga_crtc->lut_g[regno] = green >> 8; + mga_crtc->lut_b[regno] = blue >> 8; +} + +/** Gets the color ramps on behalf of fbcon */ +void mga_crtc_fb_gamma_get(struct drm_crtc *crtc, u16 *red, u16 *green, + u16 *blue, int regno) +{ + struct mga_crtc *mga_crtc = to_mga_crtc(crtc); + + *red = (u16)mga_crtc->lut_r[regno] << 8; + *green = (u16)mga_crtc->lut_g[regno] << 8; + *blue = (u16)mga_crtc->lut_b[regno] << 8; +} + +/* + * The encoder comes after the CRTC in the output pipeline, but before + * the connector. It's responsible for ensuring that the digital + * stream is appropriately converted into the output format. Setup is + * very simple in this case - all we have to do is inform qemu of the + * colour depth in order to ensure that it displays appropriately + */ + +/* + * These functions are analagous to those in the CRTC code, but are intended + * to handle any encoder-specific limitations + */ +static bool mga_encoder_mode_fixup(struct drm_encoder *encoder, + struct drm_display_mode *mode, + struct drm_display_mode *adjusted_mode) +{ + return true; +} + +static void mga_encoder_mode_set(struct drm_encoder *encoder, + struct drm_display_mode *mode, + struct drm_display_mode *adjusted_mode) +{ + +} + +static void mga_encoder_dpms(struct drm_encoder *encoder, int state) +{ + return; +} + +static void mga_encoder_prepare(struct drm_encoder *encoder) +{ +} + +static void mga_encoder_commit(struct drm_encoder *encoder) +{ +} + +void mga_encoder_destroy(struct drm_encoder *encoder) +{ + struct mga_encoder *mga_encoder = to_mga_encoder(encoder); + drm_encoder_cleanup(encoder); + kfree(mga_encoder); +} + +static const struct drm_encoder_helper_funcs mga_encoder_helper_funcs = { + .dpms = mga_encoder_dpms, + .mode_fixup = mga_encoder_mode_fixup, + .mode_set = mga_encoder_mode_set, + .prepare = mga_encoder_prepare, + .commit = mga_encoder_commit, +}; + +static const struct drm_encoder_funcs mga_encoder_encoder_funcs = { + .destroy = mga_encoder_destroy, +}; + +static struct drm_encoder *mga_encoder_init(struct drm_device *dev) +{ + struct drm_encoder *encoder; + struct mga_encoder *mga_encoder; + + mga_encoder = kzalloc(sizeof(struct mga_encoder), GFP_KERNEL); + if (!mga_encoder) + return NULL; + + encoder = &mga_encoder->base; + encoder->possible_crtcs = 0x1; + + drm_encoder_init(dev, encoder, &mga_encoder_encoder_funcs, + DRM_MODE_ENCODER_DAC); + drm_encoder_helper_add(encoder, &mga_encoder_helper_funcs); + + return encoder; +} + + +static int mga_vga_get_modes(struct drm_connector *connector) +{ + struct mga_connector *mga_connector = to_mga_connector(connector); + struct edid *edid; + int ret = 0; + + edid = drm_get_edid(connector, &mga_connector->i2c->adapter); + if (edid) { + drm_mode_connector_update_edid_property(connector, edid); + ret = drm_add_edid_modes(connector, edid); + connector->display_info.raw_edid = NULL; + kfree(edid); + } + return ret; +} + +static int mga_vga_mode_valid(struct drm_connector *connector, + struct drm_display_mode *mode) +{ + /* FIXME: Add bandwidth and g200se limitations */ + + if (mode->crtc_hdisplay > 2048 || mode->crtc_hsync_start > 4096 || + mode->crtc_hsync_end > 4096 || mode->crtc_htotal > 4096 || + mode->crtc_vdisplay > 2048 || mode->crtc_vsync_start > 4096 || + mode->crtc_vsync_end > 4096 || mode->crtc_vtotal > 4096) { + return MODE_BAD; + } + + return MODE_OK; +} + +struct drm_encoder *mga_connector_best_encoder(struct drm_connector + *connector) +{ + int enc_id = connector->encoder_ids[0]; + struct drm_mode_object *obj; + struct drm_encoder *encoder; + + /* pick the encoder ids */ + if (enc_id) { + obj = + drm_mode_object_find(connector->dev, enc_id, + DRM_MODE_OBJECT_ENCODER); + if (!obj) + return NULL; + encoder = obj_to_encoder(obj); + return encoder; + } + return NULL; +} + +static enum drm_connector_status mga_vga_detect(struct drm_connector + *connector, bool force) +{ + return connector_status_connected; +} + +static void mga_connector_destroy(struct drm_connector *connector) +{ + struct mga_connector *mga_connector = to_mga_connector(connector); + mgag200_i2c_destroy(mga_connector->i2c); + drm_connector_cleanup(connector); + kfree(connector); +} + +struct drm_connector_helper_funcs mga_vga_connector_helper_funcs = { + .get_modes = mga_vga_get_modes, + .mode_valid = mga_vga_mode_valid, + .best_encoder = mga_connector_best_encoder, +}; + +struct drm_connector_funcs mga_vga_connector_funcs = { + .dpms = drm_helper_connector_dpms, + .detect = mga_vga_detect, + .fill_modes = drm_helper_probe_single_connector_modes, + .destroy = mga_connector_destroy, +}; + +static struct drm_connector *mga_vga_init(struct drm_device *dev) +{ + struct drm_connector *connector; + struct mga_connector *mga_connector; + + mga_connector = kzalloc(sizeof(struct mga_connector), GFP_KERNEL); + if (!mga_connector) + return NULL; + + connector = &mga_connector->base; + + drm_connector_init(dev, connector, + &mga_vga_connector_funcs, DRM_MODE_CONNECTOR_VGA); + + drm_connector_helper_add(connector, &mga_vga_connector_helper_funcs); + + mga_connector->i2c = mgag200_i2c_create(dev); + if (!mga_connector->i2c) + DRM_ERROR("failed to add ddc bus\n"); + + return connector; +} + + +int mgag200_modeset_init(struct mga_device *mdev) +{ + struct drm_encoder *encoder; + struct drm_connector *connector; + int ret; + + mdev->mode_info.mode_config_initialized = true; + + mdev->dev->mode_config.max_width = MGAG200_MAX_FB_WIDTH; + mdev->dev->mode_config.max_height = MGAG200_MAX_FB_HEIGHT; + + mdev->dev->mode_config.fb_base = mdev->mc.vram_base; + + mga_crtc_init(mdev->dev); + + encoder = mga_encoder_init(mdev->dev); + if (!encoder) { + DRM_ERROR("mga_encoder_init failed\n"); + return -1; + } + + connector = mga_vga_init(mdev->dev); + if (!connector) { + DRM_ERROR("mga_vga_init failed\n"); + return -1; + } + + drm_mode_connector_attach_encoder(connector, encoder); + + ret = mgag200_fbdev_init(mdev); + if (ret) { + DRM_ERROR("mga_fbdev_init failed\n"); + return ret; + } + + return 0; +} + +void mgag200_modeset_fini(struct mga_device *mdev) +{ + +} diff --git a/drivers/gpu/drm/mgag200/mgag200_reg.h b/drivers/gpu/drm/mgag200/mgag200_reg.h new file mode 100644 index 0000000..fb24d86 --- /dev/null +++ b/drivers/gpu/drm/mgag200/mgag200_reg.h @@ -0,0 +1,661 @@ +/* + * MGA Millennium (MGA2064W) functions + * MGA Mystique (MGA1064SG) functions + * + * Copyright 1996 The XFree86 Project, Inc. + * + * Authors + * Dirk Hohndel + * hohndel@XFree86.Org + * David Dawes + * dawes@XFree86.Org + * Contributors: + * Guy DESBIEF, Aix-en-provence, France + * g.desbief@aix.pacwan.net + * MGA1064SG Mystique register file + */ + + +#ifndef _MGA_REG_H_ +#define _MGA_REG_H_ + +#define MGAREG_DWGCTL 0x1c00 +#define MGAREG_MACCESS 0x1c04 +/* the following is a mystique only register */ +#define MGAREG_MCTLWTST 0x1c08 +#define MGAREG_ZORG 0x1c0c + +#define MGAREG_PAT0 0x1c10 +#define MGAREG_PAT1 0x1c14 +#define MGAREG_PLNWT 0x1c1c + +#define MGAREG_BCOL 0x1c20 +#define MGAREG_FCOL 0x1c24 + +#define MGAREG_SRC0 0x1c30 +#define MGAREG_SRC1 0x1c34 +#define MGAREG_SRC2 0x1c38 +#define MGAREG_SRC3 0x1c3c + +#define MGAREG_XYSTRT 0x1c40 +#define MGAREG_XYEND 0x1c44 + +#define MGAREG_SHIFT 0x1c50 +/* the following is a mystique only register */ +#define MGAREG_DMAPAD 0x1c54 +#define MGAREG_SGN 0x1c58 +#define MGAREG_LEN 0x1c5c + +#define MGAREG_AR0 0x1c60 +#define MGAREG_AR1 0x1c64 +#define MGAREG_AR2 0x1c68 +#define MGAREG_AR3 0x1c6c +#define MGAREG_AR4 0x1c70 +#define MGAREG_AR5 0x1c74 +#define MGAREG_AR6 0x1c78 + +#define MGAREG_CXBNDRY 0x1c80 +#define MGAREG_FXBNDRY 0x1c84 +#define MGAREG_YDSTLEN 0x1c88 +#define MGAREG_PITCH 0x1c8c + +#define MGAREG_YDST 0x1c90 +#define MGAREG_YDSTORG 0x1c94 +#define MGAREG_YTOP 0x1c98 +#define MGAREG_YBOT 0x1c9c + +#define MGAREG_CXLEFT 0x1ca0 +#define MGAREG_CXRIGHT 0x1ca4 +#define MGAREG_FXLEFT 0x1ca8 +#define MGAREG_FXRIGHT 0x1cac + +#define MGAREG_XDST 0x1cb0 + +#define MGAREG_DR0 0x1cc0 +#define MGAREG_DR1 0x1cc4 +#define MGAREG_DR2 0x1cc8 +#define MGAREG_DR3 0x1ccc + +#define MGAREG_DR4 0x1cd0 +#define MGAREG_DR5 0x1cd4 +#define MGAREG_DR6 0x1cd8 +#define MGAREG_DR7 0x1cdc + +#define MGAREG_DR8 0x1ce0 +#define MGAREG_DR9 0x1ce4 +#define MGAREG_DR10 0x1ce8 +#define MGAREG_DR11 0x1cec + +#define MGAREG_DR12 0x1cf0 +#define MGAREG_DR13 0x1cf4 +#define MGAREG_DR14 0x1cf8 +#define MGAREG_DR15 0x1cfc + +#define MGAREG_SRCORG 0x2cb4 +#define MGAREG_DSTORG 0x2cb8 + +/* add or or this to one of the previous "power registers" to start + the drawing engine */ + +#define MGAREG_EXEC 0x0100 + +#define MGAREG_FIFOSTATUS 0x1e10 +#define MGAREG_Status 0x1e14 +#define MGAREG_CACHEFLUSH 0x1fff +#define MGAREG_ICLEAR 0x1e18 +#define MGAREG_IEN 0x1e1c + +#define MGAREG_VCOUNT 0x1e20 + +#define MGAREG_Reset 0x1e40 + +#define MGAREG_OPMODE 0x1e54 + +/* Warp Registers */ +#define MGAREG_WIADDR 0x1dc0 +#define MGAREG_WIADDR2 0x1dd8 +#define MGAREG_WGETMSB 0x1dc8 +#define MGAREG_WVRTXSZ 0x1dcc +#define MGAREG_WACCEPTSEQ 0x1dd4 +#define MGAREG_WMISC 0x1e70 + +#define MGAREG_MEMCTL 0x2e08 + +/* OPMODE register additives */ + +#define MGAOPM_DMA_GENERAL (0x00 << 2) +#define MGAOPM_DMA_BLIT (0x01 << 2) +#define MGAOPM_DMA_VECTOR (0x10 << 2) + +/* MACCESS register additives */ +#define MGAMAC_PW8 0x00 +#define MGAMAC_PW16 0x01 +#define MGAMAC_PW24 0x03 /* not a typo */ +#define MGAMAC_PW32 0x02 /* not a typo */ +#define MGAMAC_BYPASS332 0x10000000 +#define MGAMAC_NODITHER 0x40000000 +#define MGAMAC_DIT555 0x80000000 + +/* DWGCTL register additives */ + +/* Lines */ + +#define MGADWG_LINE_OPEN 0x00 +#define MGADWG_AUTOLINE_OPEN 0x01 +#define MGADWG_LINE_CLOSE 0x02 +#define MGADWG_AUTOLINE_CLOSE 0x03 + +/* Trapezoids */ +#define MGADWG_TRAP 0x04 +#define MGADWG_TEXTURE_TRAP 0x06 + +/* BitBlts */ + +#define MGADWG_BITBLT 0x08 +#define MGADWG_FBITBLT 0x0c +#define MGADWG_ILOAD 0x09 +#define MGADWG_ILOAD_SCALE 0x0d +#define MGADWG_ILOAD_FILTER 0x0f +#define MGADWG_ILOAD_HIQH 0x07 +#define MGADWG_ILOAD_HIQHV 0x0e +#define MGADWG_IDUMP 0x0a + +/* atype access to WRAM */ + +#define MGADWG_RPL ( 0x00 << 4 ) +#define MGADWG_RSTR ( 0x01 << 4 ) +#define MGADWG_ZI ( 0x03 << 4 ) +#define MGADWG_BLK ( 0x04 << 4 ) +#define MGADWG_I ( 0x07 << 4 ) + +/* specifies whether bit blits are linear or xy */ +#define MGADWG_LINEAR ( 0x01 << 7 ) + +/* z drawing mode. use MGADWG_NOZCMP for always */ + +#define MGADWG_NOZCMP ( 0x00 << 8 ) +#define MGADWG_ZE ( 0x02 << 8 ) +#define MGADWG_ZNE ( 0x03 << 8 ) +#define MGADWG_ZLT ( 0x04 << 8 ) +#define MGADWG_ZLTE ( 0x05 << 8 ) +#define MGADWG_GT ( 0x06 << 8 ) +#define MGADWG_GTE ( 0x07 << 8 ) + +/* use this to force colour expansion circuitry to do its stuff */ + +#define MGADWG_SOLID ( 0x01 << 11 ) + +/* ar register at zero */ + +#define MGADWG_ARZERO ( 0x01 << 12 ) + +#define MGADWG_SGNZERO ( 0x01 << 13 ) + +#define MGADWG_SHIFTZERO ( 0x01 << 14 ) + +/* See table on 4-43 for bop ALU operations */ + +/* See table on 4-44 for translucidity masks */ + +#define MGADWG_BMONOLEF ( 0x00 << 25 ) +#define MGADWG_BMONOWF ( 0x04 << 25 ) +#define MGADWG_BPLAN ( 0x01 << 25 ) + +/* note that if bfcol is specified and you're doing a bitblt, it causes + a fbitblt to be performed, so check that you obey the fbitblt rules */ + +#define MGADWG_BFCOL ( 0x02 << 25 ) +#define MGADWG_BUYUV ( 0x0e << 25 ) +#define MGADWG_BU32BGR ( 0x03 << 25 ) +#define MGADWG_BU32RGB ( 0x07 << 25 ) +#define MGADWG_BU24BGR ( 0x0b << 25 ) +#define MGADWG_BU24RGB ( 0x0f << 25 ) + +#define MGADWG_PATTERN ( 0x01 << 29 ) +#define MGADWG_TRANSC ( 0x01 << 30 ) +#define MGAREG_MISC_WRITE 0x3c2 +#define MGAREG_MISC_READ 0x3cc +#define MGAREG_MEM_MISC_WRITE 0x1fc2 +#define MGAREG_MEM_MISC_READ 0x1fcc + +#define MGAREG_MISC_IOADSEL (0x1 << 0) +#define MGAREG_MISC_RAMMAPEN (0x1 << 1) +#define MGAREG_MISC_CLK_SEL_VGA25 (0x0 << 2) +#define MGAREG_MISC_CLK_SEL_VGA28 (0x1 << 2) +#define MGAREG_MISC_CLK_SEL_MGA_PIX (0x2 << 2) +#define MGAREG_MISC_CLK_SEL_MGA_MSK (0x3 << 2) +#define MGAREG_MISC_VIDEO_DIS (0x1 << 4) +#define MGAREG_MISC_HIGH_PG_SEL (0x1 << 5) + +/* MMIO VGA registers */ +#define MGAREG_SEQ_INDEX 0x1fc4 +#define MGAREG_SEQ_DATA 0x1fc5 +#define MGAREG_CRTC_INDEX 0x1fd4 +#define MGAREG_CRTC_DATA 0x1fd5 +#define MGAREG_CRTCEXT_INDEX 0x1fde +#define MGAREG_CRTCEXT_DATA 0x1fdf + + + +/* MGA bits for registers PCI_OPTION_REG */ +#define MGA1064_OPT_SYS_CLK_PCI ( 0x00 << 0 ) +#define MGA1064_OPT_SYS_CLK_PLL ( 0x01 << 0 ) +#define MGA1064_OPT_SYS_CLK_EXT ( 0x02 << 0 ) +#define MGA1064_OPT_SYS_CLK_MSK ( 0x03 << 0 ) + +#define MGA1064_OPT_SYS_CLK_DIS ( 0x01 << 2 ) +#define MGA1064_OPT_G_CLK_DIV_1 ( 0x01 << 3 ) +#define MGA1064_OPT_M_CLK_DIV_1 ( 0x01 << 4 ) + +#define MGA1064_OPT_SYS_PLL_PDN ( 0x01 << 5 ) +#define MGA1064_OPT_VGA_ION ( 0x01 << 8 ) + +/* MGA registers in PCI config space */ +#define PCI_MGA_INDEX 0x44 +#define PCI_MGA_DATA 0x48 +#define PCI_MGA_OPTION 0x40 +#define PCI_MGA_OPTION2 0x50 +#define PCI_MGA_OPTION3 0x54 + +#define RAMDAC_OFFSET 0x3c00 + +/* TVP3026 direct registers */ + +#define TVP3026_INDEX 0x00 +#define TVP3026_WADR_PAL 0x00 +#define TVP3026_COL_PAL 0x01 +#define TVP3026_PIX_RD_MSK 0x02 +#define TVP3026_RADR_PAL 0x03 +#define TVP3026_CUR_COL_ADDR 0x04 +#define TVP3026_CUR_COL_DATA 0x05 +#define TVP3026_DATA 0x0a +#define TVP3026_CUR_RAM 0x0b +#define TVP3026_CUR_XLOW 0x0c +#define TVP3026_CUR_XHI 0x0d +#define TVP3026_CUR_YLOW 0x0e +#define TVP3026_CUR_YHI 0x0f + +/* TVP3026 indirect registers */ + +#define TVP3026_SILICON_REV 0x01 +#define TVP3026_CURSOR_CTL 0x06 +#define TVP3026_LATCH_CTL 0x0f +#define TVP3026_TRUE_COLOR_CTL 0x18 +#define TVP3026_MUX_CTL 0x19 +#define TVP3026_CLK_SEL 0x1a +#define TVP3026_PAL_PAGE 0x1c +#define TVP3026_GEN_CTL 0x1d +#define TVP3026_MISC_CTL 0x1e +#define TVP3026_GEN_IO_CTL 0x2a +#define TVP3026_GEN_IO_DATA 0x2b +#define TVP3026_PLL_ADDR 0x2c +#define TVP3026_PIX_CLK_DATA 0x2d +#define TVP3026_MEM_CLK_DATA 0x2e +#define TVP3026_LOAD_CLK_DATA 0x2f +#define TVP3026_KEY_RED_LOW 0x32 +#define TVP3026_KEY_RED_HI 0x33 +#define TVP3026_KEY_GREEN_LOW 0x34 +#define TVP3026_KEY_GREEN_HI 0x35 +#define TVP3026_KEY_BLUE_LOW 0x36 +#define TVP3026_KEY_BLUE_HI 0x37 +#define TVP3026_KEY_CTL 0x38 +#define TVP3026_MCLK_CTL 0x39 +#define TVP3026_SENSE_TEST 0x3a +#define TVP3026_TEST_DATA 0x3b +#define TVP3026_CRC_LSB 0x3c +#define TVP3026_CRC_MSB 0x3d +#define TVP3026_CRC_CTL 0x3e +#define TVP3026_ID 0x3f +#define TVP3026_RESET 0xff + + +/* MGA1064 DAC Register file */ +/* MGA1064 direct registers */ + +#define MGA1064_INDEX 0x00 +#define MGA1064_WADR_PAL 0x00 +#define MGA1064_SPAREREG 0x00 +#define MGA1064_COL_PAL 0x01 +#define MGA1064_PIX_RD_MSK 0x02 +#define MGA1064_RADR_PAL 0x03 +#define MGA1064_DATA 0x0a + +#define MGA1064_CUR_XLOW 0x0c +#define MGA1064_CUR_XHI 0x0d +#define MGA1064_CUR_YLOW 0x0e +#define MGA1064_CUR_YHI 0x0f + +/* MGA1064 indirect registers */ +#define MGA1064_DVI_PIPE_CTL 0x03 +#define MGA1064_CURSOR_BASE_ADR_LOW 0x04 +#define MGA1064_CURSOR_BASE_ADR_HI 0x05 +#define MGA1064_CURSOR_CTL 0x06 +#define MGA1064_CURSOR_COL0_RED 0x08 +#define MGA1064_CURSOR_COL0_GREEN 0x09 +#define MGA1064_CURSOR_COL0_BLUE 0x0a + +#define MGA1064_CURSOR_COL1_RED 0x0c +#define MGA1064_CURSOR_COL1_GREEN 0x0d +#define MGA1064_CURSOR_COL1_BLUE 0x0e + +#define MGA1064_CURSOR_COL2_RED 0x010 +#define MGA1064_CURSOR_COL2_GREEN 0x011 +#define MGA1064_CURSOR_COL2_BLUE 0x012 + +#define MGA1064_VREF_CTL 0x018 + +#define MGA1064_MUL_CTL 0x19 +#define MGA1064_MUL_CTL_8bits 0x0 +#define MGA1064_MUL_CTL_15bits 0x01 +#define MGA1064_MUL_CTL_16bits 0x02 +#define MGA1064_MUL_CTL_24bits 0x03 +#define MGA1064_MUL_CTL_32bits 0x04 +#define MGA1064_MUL_CTL_2G8V16bits 0x05 +#define MGA1064_MUL_CTL_G16V16bits 0x06 +#define MGA1064_MUL_CTL_32_24bits 0x07 + +#define MGA1064_PIX_CLK_CTL 0x1a +#define MGA1064_PIX_CLK_CTL_CLK_DIS ( 0x01 << 2 ) +#define MGA1064_PIX_CLK_CTL_CLK_POW_DOWN ( 0x01 << 3 ) +#define MGA1064_PIX_CLK_CTL_SEL_PCI ( 0x00 << 0 ) +#define MGA1064_PIX_CLK_CTL_SEL_PLL ( 0x01 << 0 ) +#define MGA1064_PIX_CLK_CTL_SEL_EXT ( 0x02 << 0 ) +#define MGA1064_PIX_CLK_CTL_SEL_MSK ( 0x03 << 0 ) + +#define MGA1064_GEN_CTL 0x1d +#define MGA1064_GEN_CTL_SYNC_ON_GREEN_DIS (0x01 << 5) +#define MGA1064_MISC_CTL 0x1e +#define MGA1064_MISC_CTL_DAC_EN ( 0x01 << 0 ) +#define MGA1064_MISC_CTL_VGA ( 0x01 << 1 ) +#define MGA1064_MISC_CTL_DIS_CON ( 0x03 << 1 ) +#define MGA1064_MISC_CTL_MAFC ( 0x02 << 1 ) +#define MGA1064_MISC_CTL_VGA8 ( 0x01 << 3 ) +#define MGA1064_MISC_CTL_DAC_RAM_CS ( 0x01 << 4 ) + +#define MGA1064_GEN_IO_CTL2 0x29 +#define MGA1064_GEN_IO_CTL 0x2a +#define MGA1064_GEN_IO_DATA 0x2b +#define MGA1064_SYS_PLL_M 0x2c +#define MGA1064_SYS_PLL_N 0x2d +#define MGA1064_SYS_PLL_P 0x2e +#define MGA1064_SYS_PLL_STAT 0x2f + +#define MGA1064_REMHEADCTL 0x30 +#define MGA1064_REMHEADCTL_CLKDIS ( 0x01 << 0 ) +#define MGA1064_REMHEADCTL_CLKSL_OFF ( 0x00 << 1 ) +#define MGA1064_REMHEADCTL_CLKSL_PLL ( 0x01 << 1 ) +#define MGA1064_REMHEADCTL_CLKSL_PCI ( 0x02 << 1 ) +#define MGA1064_REMHEADCTL_CLKSL_MSK ( 0x03 << 1 ) + +#define MGA1064_REMHEADCTL2 0x31 + +#define MGA1064_ZOOM_CTL 0x38 +#define MGA1064_SENSE_TST 0x3a + +#define MGA1064_CRC_LSB 0x3c +#define MGA1064_CRC_MSB 0x3d +#define MGA1064_CRC_CTL 0x3e +#define MGA1064_COL_KEY_MSK_LSB 0x40 +#define MGA1064_COL_KEY_MSK_MSB 0x41 +#define MGA1064_COL_KEY_LSB 0x42 +#define MGA1064_COL_KEY_MSB 0x43 +#define MGA1064_PIX_PLLA_M 0x44 +#define MGA1064_PIX_PLLA_N 0x45 +#define MGA1064_PIX_PLLA_P 0x46 +#define MGA1064_PIX_PLLB_M 0x48 +#define MGA1064_PIX_PLLB_N 0x49 +#define MGA1064_PIX_PLLB_P 0x4a +#define MGA1064_PIX_PLLC_M 0x4c +#define MGA1064_PIX_PLLC_N 0x4d +#define MGA1064_PIX_PLLC_P 0x4e + +#define MGA1064_PIX_PLL_STAT 0x4f + +/*Added for G450 dual head*/ + +#define MGA1064_VID_PLL_STAT 0x8c +#define MGA1064_VID_PLL_P 0x8D +#define MGA1064_VID_PLL_M 0x8E +#define MGA1064_VID_PLL_N 0x8F + +/* Modified PLL for G200 Winbond (G200WB) */ +#define MGA1064_WB_PIX_PLLC_M 0xb7 +#define MGA1064_WB_PIX_PLLC_N 0xb6 +#define MGA1064_WB_PIX_PLLC_P 0xb8 + +/* Modified PLL for G200 Maxim (G200EV) */ +#define MGA1064_EV_PIX_PLLC_M 0xb6 +#define MGA1064_EV_PIX_PLLC_N 0xb7 +#define MGA1064_EV_PIX_PLLC_P 0xb8 + +/* Modified PLL for G200 EH */ +#define MGA1064_EH_PIX_PLLC_M 0xb6 +#define MGA1064_EH_PIX_PLLC_N 0xb7 +#define MGA1064_EH_PIX_PLLC_P 0xb8 + +/* Modified PLL for G200 Maxim (G200ER) */ +#define MGA1064_ER_PIX_PLLC_M 0xb7 +#define MGA1064_ER_PIX_PLLC_N 0xb6 +#define MGA1064_ER_PIX_PLLC_P 0xb8 + +#define MGA1064_DISP_CTL 0x8a +#define MGA1064_DISP_CTL_DAC1OUTSEL_MASK 0x01 +#define MGA1064_DISP_CTL_DAC1OUTSEL_DIS 0x00 +#define MGA1064_DISP_CTL_DAC1OUTSEL_EN 0x01 +#define MGA1064_DISP_CTL_DAC2OUTSEL_MASK (0x03 << 2) +#define MGA1064_DISP_CTL_DAC2OUTSEL_DIS 0x00 +#define MGA1064_DISP_CTL_DAC2OUTSEL_CRTC1 (0x01 << 2) +#define MGA1064_DISP_CTL_DAC2OUTSEL_CRTC2 (0x02 << 2) +#define MGA1064_DISP_CTL_DAC2OUTSEL_TVE (0x03 << 2) +#define MGA1064_DISP_CTL_PANOUTSEL_MASK (0x03 << 5) +#define MGA1064_DISP_CTL_PANOUTSEL_DIS 0x00 +#define MGA1064_DISP_CTL_PANOUTSEL_CRTC1 (0x01 << 5) +#define MGA1064_DISP_CTL_PANOUTSEL_CRTC2RGB (0x02 << 5) +#define MGA1064_DISP_CTL_PANOUTSEL_CRTC2656 (0x03 << 5) + +#define MGA1064_SYNC_CTL 0x8b + +#define MGA1064_PWR_CTL 0xa0 +#define MGA1064_PWR_CTL_DAC2_EN (0x01 << 0) +#define MGA1064_PWR_CTL_VID_PLL_EN (0x01 << 1) +#define MGA1064_PWR_CTL_PANEL_EN (0x01 << 2) +#define MGA1064_PWR_CTL_RFIFO_EN (0x01 << 3) +#define MGA1064_PWR_CTL_CFIFO_EN (0x01 << 4) + +#define MGA1064_PAN_CTL 0xa2 + +/* Using crtc2 */ +#define MGAREG2_C2CTL 0x10 +#define MGAREG2_C2HPARAM 0x14 +#define MGAREG2_C2HSYNC 0x18 +#define MGAREG2_C2VPARAM 0x1c +#define MGAREG2_C2VSYNC 0x20 +#define MGAREG2_C2STARTADD0 0x28 + +#define MGAREG2_C2OFFSET 0x40 +#define MGAREG2_C2DATACTL 0x4c + +#define MGAREG_C2CTL 0x3c10 +#define MGAREG_C2CTL_C2_EN 0x01 + +#define MGAREG_C2_HIPRILVL_M (0x07 << 4) +#define MGAREG_C2_MAXHIPRI_M (0x07 << 8) + +#define MGAREG_C2CTL_PIXCLKSEL_MASK (0x03 << 1) +#define MGAREG_C2CTL_PIXCLKSELH_MASK (0x01 << 14) +#define MGAREG_C2CTL_PIXCLKSEL_PCICLK 0x00 +#define MGAREG_C2CTL_PIXCLKSEL_VDOCLK (0x01 << 1) +#define MGAREG_C2CTL_PIXCLKSEL_PIXELPLL (0x02 << 1) +#define MGAREG_C2CTL_PIXCLKSEL_VIDEOPLL (0x03 << 1) +#define MGAREG_C2CTL_PIXCLKSEL_VDCLK (0x01 << 14) + +#define MGAREG_C2CTL_PIXCLKSEL_CRISTAL (0x01 << 1) | (0x01 << 14) +#define MGAREG_C2CTL_PIXCLKSEL_SYSTEMPLL (0x02 << 1) | (0x01 << 14) + +#define MGAREG_C2CTL_PIXCLKDIS_MASK (0x01 << 3) +#define MGAREG_C2CTL_PIXCLKDIS_DISABLE (0x01 << 3) + +#define MGAREG_C2CTL_CRTCDACSEL_MASK (0x01 << 20) +#define MGAREG_C2CTL_CRTCDACSEL_CRTC1 0x00 +#define MGAREG_C2CTL_CRTCDACSEL_CRTC2 (0x01 << 20) + +#define MGAREG_C2HPARAM 0x3c14 +#define MGAREG_C2HSYNC 0x3c18 +#define MGAREG_C2VPARAM 0x3c1c +#define MGAREG_C2VSYNC 0x3c20 +#define MGAREG_C2STARTADD0 0x3c28 + +#define MGAREG_C2OFFSET 0x3c40 +#define MGAREG_C2DATACTL 0x3c4c + +/* video register */ + +#define MGAREG_BESA1C3ORG 0x3d60 +#define MGAREG_BESA1CORG 0x3d10 +#define MGAREG_BESA1ORG 0x3d00 +#define MGAREG_BESCTL 0x3d20 +#define MGAREG_BESGLOBCTL 0x3dc0 +#define MGAREG_BESHCOORD 0x3d28 +#define MGAREG_BESHISCAL 0x3d30 +#define MGAREG_BESHSRCEND 0x3d3c +#define MGAREG_BESHSRCLST 0x3d50 +#define MGAREG_BESHSRCST 0x3d38 +#define MGAREG_BESLUMACTL 0x3d40 +#define MGAREG_BESPITCH 0x3d24 +#define MGAREG_BESV1SRCLST 0x3d54 +#define MGAREG_BESV1WGHT 0x3d48 +#define MGAREG_BESVCOORD 0x3d2c +#define MGAREG_BESVISCAL 0x3d34 + +/* texture engine registers */ + +#define MGAREG_TMR0 0x2c00 +#define MGAREG_TMR1 0x2c04 +#define MGAREG_TMR2 0x2c08 +#define MGAREG_TMR3 0x2c0c +#define MGAREG_TMR4 0x2c10 +#define MGAREG_TMR5 0x2c14 +#define MGAREG_TMR6 0x2c18 +#define MGAREG_TMR7 0x2c1c +#define MGAREG_TMR8 0x2c20 +#define MGAREG_TEXORG 0x2c24 +#define MGAREG_TEXWIDTH 0x2c28 +#define MGAREG_TEXHEIGHT 0x2c2c +#define MGAREG_TEXCTL 0x2c30 +# define MGA_TW4 (0x00000000) +# define MGA_TW8 (0x00000001) +# define MGA_TW15 (0x00000002) +# define MGA_TW16 (0x00000003) +# define MGA_TW12 (0x00000004) +# define MGA_TW32 (0x00000006) +# define MGA_TW8A (0x00000007) +# define MGA_TW8AL (0x00000008) +# define MGA_TW422 (0x0000000A) +# define MGA_TW422UYVY (0x0000000B) +# define MGA_PITCHLIN (0x00000100) +# define MGA_NOPERSPECTIVE (0x00200000) +# define MGA_TAKEY (0x02000000) +# define MGA_TAMASK (0x04000000) +# define MGA_CLAMPUV (0x18000000) +# define MGA_TEXMODULATE (0x20000000) +#define MGAREG_TEXCTL2 0x2c3c +# define MGA_G400_TC2_MAGIC (0x00008000) +# define MGA_TC2_DECALBLEND (0x00000001) +# define MGA_TC2_IDECAL (0x00000002) +# define MGA_TC2_DECALDIS (0x00000004) +# define MGA_TC2_CKSTRANSDIS (0x00000010) +# define MGA_TC2_BORDEREN (0x00000020) +# define MGA_TC2_SPECEN (0x00000040) +# define MGA_TC2_DUALTEX (0x00000080) +# define MGA_TC2_TABLEFOG (0x00000100) +# define MGA_TC2_BUMPMAP (0x00000200) +# define MGA_TC2_SELECT_TMU1 (0x80000000) +#define MGAREG_TEXTRANS 0x2c34 +#define MGAREG_TEXTRANSHIGH 0x2c38 +#define MGAREG_TEXFILTER 0x2c58 +# define MGA_MIN_NRST (0x00000000) +# define MGA_MIN_BILIN (0x00000002) +# define MGA_MIN_ANISO (0x0000000D) +# define MGA_MAG_NRST (0x00000000) +# define MGA_MAG_BILIN (0x00000020) +# define MGA_FILTERALPHA (0x00100000) +#define MGAREG_ALPHASTART 0x2c70 +#define MGAREG_ALPHAXINC 0x2c74 +#define MGAREG_ALPHAYINC 0x2c78 +#define MGAREG_ALPHACTRL 0x2c7c +# define MGA_SRC_ZERO (0x00000000) +# define MGA_SRC_ONE (0x00000001) +# define MGA_SRC_DST_COLOR (0x00000002) +# define MGA_SRC_ONE_MINUS_DST_COLOR (0x00000003) +# define MGA_SRC_ALPHA (0x00000004) +# define MGA_SRC_ONE_MINUS_SRC_ALPHA (0x00000005) +# define MGA_SRC_DST_ALPHA (0x00000006) +# define MGA_SRC_ONE_MINUS_DST_ALPHA (0x00000007) +# define MGA_SRC_SRC_ALPHA_SATURATE (0x00000008) +# define MGA_SRC_BLEND_MASK (0x0000000f) +# define MGA_DST_ZERO (0x00000000) +# define MGA_DST_ONE (0x00000010) +# define MGA_DST_SRC_COLOR (0x00000020) +# define MGA_DST_ONE_MINUS_SRC_COLOR (0x00000030) +# define MGA_DST_SRC_ALPHA (0x00000040) +# define MGA_DST_ONE_MINUS_SRC_ALPHA (0x00000050) +# define MGA_DST_DST_ALPHA (0x00000060) +# define MGA_DST_ONE_MINUS_DST_ALPHA (0x00000070) +# define MGA_DST_BLEND_MASK (0x00000070) +# define MGA_ALPHACHANNEL (0x00000100) +# define MGA_VIDEOALPHA (0x00000200) +# define MGA_DIFFUSEDALPHA (0x01000000) +# define MGA_MODULATEDALPHA (0x02000000) +#define MGAREG_TDUALSTAGE0 (0x2CF8) +#define MGAREG_TDUALSTAGE1 (0x2CFC) +# define MGA_TDS_COLOR_ARG2_DIFFUSE (0x00000000) +# define MGA_TDS_COLOR_ARG2_SPECULAR (0x00000001) +# define MGA_TDS_COLOR_ARG2_FCOL (0x00000002) +# define MGA_TDS_COLOR_ARG2_PREVSTAGE (0x00000003) +# define MGA_TDS_COLOR_ALPHA_DIFFUSE (0x00000000) +# define MGA_TDS_COLOR_ALPHA_FCOL (0x00000004) +# define MGA_TDS_COLOR_ALPHA_CURRTEX (0x00000008) +# define MGA_TDS_COLOR_ALPHA_PREVTEX (0x0000000c) +# define MGA_TDS_COLOR_ALPHA_PREVSTAGE (0x00000010) +# define MGA_TDS_COLOR_ARG1_REPLICATEALPHA (0x00000020) +# define MGA_TDS_COLOR_ARG1_INV (0x00000040) +# define MGA_TDS_COLOR_ARG2_REPLICATEALPHA (0x00000080) +# define MGA_TDS_COLOR_ARG2_INV (0x00000100) +# define MGA_TDS_COLOR_ALPHA1INV (0x00000200) +# define MGA_TDS_COLOR_ALPHA2INV (0x00000400) +# define MGA_TDS_COLOR_ARG1MUL_ALPHA1 (0x00000800) +# define MGA_TDS_COLOR_ARG2MUL_ALPHA2 (0x00001000) +# define MGA_TDS_COLOR_ARG1ADD_MULOUT (0x00002000) +# define MGA_TDS_COLOR_ARG2ADD_MULOUT (0x00004000) +# define MGA_TDS_COLOR_MODBRIGHT_2X (0x00008000) +# define MGA_TDS_COLOR_MODBRIGHT_4X (0x00010000) +# define MGA_TDS_COLOR_ADD_SUB (0x00000000) +# define MGA_TDS_COLOR_ADD_ADD (0x00020000) +# define MGA_TDS_COLOR_ADD2X (0x00040000) +# define MGA_TDS_COLOR_ADDBIAS (0x00080000) +# define MGA_TDS_COLOR_BLEND (0x00100000) +# define MGA_TDS_COLOR_SEL_ARG1 (0x00000000) +# define MGA_TDS_COLOR_SEL_ARG2 (0x00200000) +# define MGA_TDS_COLOR_SEL_ADD (0x00400000) +# define MGA_TDS_COLOR_SEL_MUL (0x00600000) +# define MGA_TDS_ALPHA_ARG1_INV (0x00800000) +# define MGA_TDS_ALPHA_ARG2_DIFFUSE (0x00000000) +# define MGA_TDS_ALPHA_ARG2_FCOL (0x01000000) +# define MGA_TDS_ALPHA_ARG2_PREVTEX (0x02000000) +# define MGA_TDS_ALPHA_ARG2_PREVSTAGE (0x03000000) +# define MGA_TDS_ALPHA_ARG2_INV (0x04000000) +# define MGA_TDS_ALPHA_ADD (0x08000000) +# define MGA_TDS_ALPHA_ADDBIAS (0x10000000) +# define MGA_TDS_ALPHA_ADD2X (0x20000000) +# define MGA_TDS_ALPHA_SEL_ARG1 (0x00000000) +# define MGA_TDS_ALPHA_SEL_ARG2 (0x40000000) +# define MGA_TDS_ALPHA_SEL_ADD (0x80000000) +# define MGA_TDS_ALPHA_SEL_MUL (0xc0000000) + +#define MGAREG_DWGSYNC 0x2c4c + +#define MGAREG_AGP_PLL 0x1e4c +#define MGA_AGP2XPLL_ENABLE 0x1 +#define MGA_AGP2XPLL_DISABLE 0x0 + +#endif diff --git a/drivers/gpu/drm/mgag200/mgag200_ttm.c b/drivers/gpu/drm/mgag200/mgag200_ttm.c new file mode 100644 index 0000000..1226479 --- /dev/null +++ b/drivers/gpu/drm/mgag200/mgag200_ttm.c @@ -0,0 +1,452 @@ +/* + * Copyright 2012 Red Hat Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sub license, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDERS, AUTHORS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, + * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR + * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE + * USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * The above copyright notice and this permission notice (including the + * next paragraph) shall be included in all copies or substantial portions + * of the Software. + * + */ +/* + * Authors: Dave Airlie <airlied@redhat.com> + */ +#include "drmP.h" +#include "mgag200_drv.h" +#include <ttm/ttm_page_alloc.h> + +static inline struct mga_device * +mgag200_bdev(struct ttm_bo_device *bd) +{ + return container_of(bd, struct mga_device, ttm.bdev); +} + +static int +mgag200_ttm_mem_global_init(struct drm_global_reference *ref) +{ + return ttm_mem_global_init(ref->object); +} + +static void +mgag200_ttm_mem_global_release(struct drm_global_reference *ref) +{ + ttm_mem_global_release(ref->object); +} + +static int mgag200_ttm_global_init(struct mga_device *ast) +{ + struct drm_global_reference *global_ref; + int r; + + global_ref = &ast->ttm.mem_global_ref; + global_ref->global_type = DRM_GLOBAL_TTM_MEM; + global_ref->size = sizeof(struct ttm_mem_global); + global_ref->init = &mgag200_ttm_mem_global_init; + global_ref->release = &mgag200_ttm_mem_global_release; + r = drm_global_item_ref(global_ref); + if (r != 0) { + DRM_ERROR("Failed setting up TTM memory accounting " + "subsystem.\n"); + return r; + } + + ast->ttm.bo_global_ref.mem_glob = + ast->ttm.mem_global_ref.object; + global_ref = &ast->ttm.bo_global_ref.ref; + global_ref->global_type = DRM_GLOBAL_TTM_BO; + global_ref->size = sizeof(struct ttm_bo_global); + global_ref->init = &ttm_bo_global_init; + global_ref->release = &ttm_bo_global_release; + r = drm_global_item_ref(global_ref); + if (r != 0) { + DRM_ERROR("Failed setting up TTM BO subsystem.\n"); + drm_global_item_unref(&ast->ttm.mem_global_ref); + return r; + } + return 0; +} + +void +mgag200_ttm_global_release(struct mga_device *ast) +{ + if (ast->ttm.mem_global_ref.release == NULL) + return; + + drm_global_item_unref(&ast->ttm.bo_global_ref.ref); + drm_global_item_unref(&ast->ttm.mem_global_ref); + ast->ttm.mem_global_ref.release = NULL; +} + + +static void mgag200_bo_ttm_destroy(struct ttm_buffer_object *tbo) +{ + struct mgag200_bo *bo; + + bo = container_of(tbo, struct mgag200_bo, bo); + + drm_gem_object_release(&bo->gem); + kfree(bo); +} + +bool mgag200_ttm_bo_is_mgag200_bo(struct ttm_buffer_object *bo) +{ + if (bo->destroy == &mgag200_bo_ttm_destroy) + return true; + return false; +} + +static int +mgag200_bo_init_mem_type(struct ttm_bo_device *bdev, uint32_t type, + struct ttm_mem_type_manager *man) +{ + switch (type) { + case TTM_PL_SYSTEM: + man->flags = TTM_MEMTYPE_FLAG_MAPPABLE; + man->available_caching = TTM_PL_MASK_CACHING; + man->default_caching = TTM_PL_FLAG_CACHED; + break; + case TTM_PL_VRAM: + man->func = &ttm_bo_manager_func; + man->flags = TTM_MEMTYPE_FLAG_FIXED | + TTM_MEMTYPE_FLAG_MAPPABLE; + man->available_caching = TTM_PL_FLAG_UNCACHED | + TTM_PL_FLAG_WC; + man->default_caching = TTM_PL_FLAG_WC; + break; + default: + DRM_ERROR("Unsupported memory type %u\n", (unsigned)type); + return -EINVAL; + } + return 0; +} + +static void +mgag200_bo_evict_flags(struct ttm_buffer_object *bo, struct ttm_placement *pl) +{ + struct mgag200_bo *mgabo = mgag200_bo(bo); + + if (!mgag200_ttm_bo_is_mgag200_bo(bo)) + return; + + mgag200_ttm_placement(mgabo, TTM_PL_FLAG_SYSTEM); + *pl = mgabo->placement; +} + +static int mgag200_bo_verify_access(struct ttm_buffer_object *bo, struct file *filp) +{ + return 0; +} + +static int mgag200_ttm_io_mem_reserve(struct ttm_bo_device *bdev, + struct ttm_mem_reg *mem) +{ + struct ttm_mem_type_manager *man = &bdev->man[mem->mem_type]; + struct mga_device *mdev = mgag200_bdev(bdev); + + mem->bus.addr = NULL; + mem->bus.offset = 0; + mem->bus.size = mem->num_pages << PAGE_SHIFT; + mem->bus.base = 0; + mem->bus.is_iomem = false; + if (!(man->flags & TTM_MEMTYPE_FLAG_MAPPABLE)) + return -EINVAL; + switch (mem->mem_type) { + case TTM_PL_SYSTEM: + /* system memory */ + return 0; + case TTM_PL_VRAM: + mem->bus.offset = mem->start << PAGE_SHIFT; + mem->bus.base = pci_resource_start(mdev->dev->pdev, 0); + mem->bus.is_iomem = true; + break; + default: + return -EINVAL; + break; + } + return 0; +} + +static void mgag200_ttm_io_mem_free(struct ttm_bo_device *bdev, struct ttm_mem_reg *mem) +{ +} + +static int mgag200_bo_move(struct ttm_buffer_object *bo, + bool evict, bool interruptible, + bool no_wait_reserve, bool no_wait_gpu, + struct ttm_mem_reg *new_mem) +{ + int r; + r = ttm_bo_move_memcpy(bo, evict, no_wait_reserve, no_wait_gpu, new_mem); + return r; +} + + +static void mgag200_ttm_backend_destroy(struct ttm_tt *tt) +{ + ttm_tt_fini(tt); + kfree(tt); +} + +static struct ttm_backend_func mgag200_tt_backend_func = { + .destroy = &mgag200_ttm_backend_destroy, +}; + + +struct ttm_tt *mgag200_ttm_tt_create(struct ttm_bo_device *bdev, + unsigned long size, uint32_t page_flags, + struct page *dummy_read_page) +{ + struct ttm_tt *tt; + + tt = kzalloc(sizeof(struct ttm_tt), GFP_KERNEL); + if (tt == NULL) + return NULL; + tt->func = &mgag200_tt_backend_func; + if (ttm_tt_init(tt, bdev, size, page_flags, dummy_read_page)) { + kfree(tt); + return NULL; + } + return tt; +} + +static int mgag200_ttm_tt_populate(struct ttm_tt *ttm) +{ + return ttm_pool_populate(ttm); +} + +static void mgag200_ttm_tt_unpopulate(struct ttm_tt *ttm) +{ + ttm_pool_unpopulate(ttm); +} + +struct ttm_bo_driver mgag200_bo_driver = { + .ttm_tt_create = mgag200_ttm_tt_create, + .ttm_tt_populate = mgag200_ttm_tt_populate, + .ttm_tt_unpopulate = mgag200_ttm_tt_unpopulate, + .init_mem_type = mgag200_bo_init_mem_type, + .evict_flags = mgag200_bo_evict_flags, + .move = mgag200_bo_move, + .verify_access = mgag200_bo_verify_access, + .io_mem_reserve = &mgag200_ttm_io_mem_reserve, + .io_mem_free = &mgag200_ttm_io_mem_free, +}; + +int mgag200_mm_init(struct mga_device *mdev) +{ + int ret; + struct drm_device *dev = mdev->dev; + struct ttm_bo_device *bdev = &mdev->ttm.bdev; + + ret = mgag200_ttm_global_init(mdev); + if (ret) + return ret; + + ret = ttm_bo_device_init(&mdev->ttm.bdev, + mdev->ttm.bo_global_ref.ref.object, + &mgag200_bo_driver, DRM_FILE_PAGE_OFFSET, + true); + if (ret) { + DRM_ERROR("Error initialising bo driver; %d\n", ret); + return ret; + } + + ret = ttm_bo_init_mm(bdev, TTM_PL_VRAM, mdev->mc.vram_size >> PAGE_SHIFT); + if (ret) { + DRM_ERROR("Failed ttm VRAM init: %d\n", ret); + return ret; + } + + mdev->fb_mtrr = drm_mtrr_add(pci_resource_start(dev->pdev, 0), + pci_resource_len(dev->pdev, 0), + DRM_MTRR_WC); + + return 0; +} + +void mgag200_mm_fini(struct mga_device *mdev) +{ + struct drm_device *dev = mdev->dev; + ttm_bo_device_release(&mdev->ttm.bdev); + + mgag200_ttm_global_release(mdev); + + if (mdev->fb_mtrr >= 0) { + drm_mtrr_del(mdev->fb_mtrr, + pci_resource_start(dev->pdev, 0), + pci_resource_len(dev->pdev, 0), DRM_MTRR_WC); + mdev->fb_mtrr = -1; + } +} + +void mgag200_ttm_placement(struct mgag200_bo *bo, int domain) +{ + u32 c = 0; + bo->placement.fpfn = 0; + bo->placement.lpfn = 0; + bo->placement.placement = bo->placements; + bo->placement.busy_placement = bo->placements; + if (domain & TTM_PL_FLAG_VRAM) + bo->placements[c++] = TTM_PL_FLAG_WC | TTM_PL_FLAG_UNCACHED | TTM_PL_FLAG_VRAM; + if (domain & TTM_PL_FLAG_SYSTEM) + bo->placements[c++] = TTM_PL_MASK_CACHING | TTM_PL_FLAG_SYSTEM; + if (!c) + bo->placements[c++] = TTM_PL_MASK_CACHING | TTM_PL_FLAG_SYSTEM; + bo->placement.num_placement = c; + bo->placement.num_busy_placement = c; +} + +int mgag200_bo_reserve(struct mgag200_bo *bo, bool no_wait) +{ + int ret; + + ret = ttm_bo_reserve(&bo->bo, true, no_wait, false, 0); + if (ret) { + if (ret != -ERESTARTSYS) + DRM_ERROR("reserve failed %p\n", bo); + return ret; + } + return 0; +} + +void mgag200_bo_unreserve(struct mgag200_bo *bo) +{ + ttm_bo_unreserve(&bo->bo); +} + +int mgag200_bo_create(struct drm_device *dev, int size, int align, + uint32_t flags, struct mgag200_bo **pmgabo) +{ + struct mga_device *mdev = dev->dev_private; + struct mgag200_bo *mgabo; + size_t acc_size; + int ret; + + mgabo = kzalloc(sizeof(struct mgag200_bo), GFP_KERNEL); + if (!mgabo) + return -ENOMEM; + + ret = drm_gem_object_init(dev, &mgabo->gem, size); + if (ret) { + kfree(mgabo); + return ret; + } + + mgabo->gem.driver_private = NULL; + mgabo->bo.bdev = &mdev->ttm.bdev; + + mgag200_ttm_placement(mgabo, TTM_PL_FLAG_VRAM | TTM_PL_FLAG_SYSTEM); + + acc_size = ttm_bo_dma_acc_size(&mdev->ttm.bdev, size, + sizeof(struct mgag200_bo)); + + ret = ttm_bo_init(&mdev->ttm.bdev, &mgabo->bo, size, + ttm_bo_type_device, &mgabo->placement, + align >> PAGE_SHIFT, 0, false, NULL, acc_size, + mgag200_bo_ttm_destroy); + if (ret) + return ret; + + *pmgabo = mgabo; + return 0; +} + +static inline u64 mgag200_bo_gpu_offset(struct mgag200_bo *bo) +{ + return bo->bo.offset; +} + +int mgag200_bo_pin(struct mgag200_bo *bo, u32 pl_flag, u64 *gpu_addr) +{ + int i, ret; + + if (bo->pin_count) { + bo->pin_count++; + if (gpu_addr) + *gpu_addr = mgag200_bo_gpu_offset(bo); + } + + mgag200_ttm_placement(bo, pl_flag); + for (i = 0; i < bo->placement.num_placement; i++) + bo->placements[i] |= TTM_PL_FLAG_NO_EVICT; + ret = ttm_bo_validate(&bo->bo, &bo->placement, false, false, false); + if (ret) + return ret; + + bo->pin_count = 1; + if (gpu_addr) + *gpu_addr = mgag200_bo_gpu_offset(bo); + return 0; +} + +int mgag200_bo_unpin(struct mgag200_bo *bo) +{ + int i, ret; + if (!bo->pin_count) { + DRM_ERROR("unpin bad %p\n", bo); + return 0; + } + bo->pin_count--; + if (bo->pin_count) + return 0; + + for (i = 0; i < bo->placement.num_placement ; i++) + bo->placements[i] &= ~TTM_PL_FLAG_NO_EVICT; + ret = ttm_bo_validate(&bo->bo, &bo->placement, false, false, false); + if (ret) + return ret; + + return 0; +} + +int mgag200_bo_push_sysram(struct mgag200_bo *bo) +{ + int i, ret; + if (!bo->pin_count) { + DRM_ERROR("unpin bad %p\n", bo); + return 0; + } + bo->pin_count--; + if (bo->pin_count) + return 0; + + if (bo->kmap.virtual) + ttm_bo_kunmap(&bo->kmap); + + mgag200_ttm_placement(bo, TTM_PL_FLAG_SYSTEM); + for (i = 0; i < bo->placement.num_placement ; i++) + bo->placements[i] |= TTM_PL_FLAG_NO_EVICT; + + ret = ttm_bo_validate(&bo->bo, &bo->placement, false, false, false); + if (ret) { + DRM_ERROR("pushing to VRAM failed\n"); + return ret; + } + return 0; +} + +int mgag200_mmap(struct file *filp, struct vm_area_struct *vma) +{ + struct drm_file *file_priv; + struct mga_device *mdev; + + if (unlikely(vma->vm_pgoff < DRM_FILE_PAGE_OFFSET)) + return drm_mmap(filp, vma); + + file_priv = filp->private_data; + mdev = file_priv->minor->dev->dev_private; + return ttm_bo_mmap(filp, vma, &mdev->ttm.bdev); +} diff --git a/drivers/gpu/drm/nouveau/nouveau_bo.c b/drivers/gpu/drm/nouveau/nouveau_bo.c index 7d15a77..12ce044 100644 --- a/drivers/gpu/drm/nouveau/nouveau_bo.c +++ b/drivers/gpu/drm/nouveau/nouveau_bo.c @@ -1030,7 +1030,7 @@ nouveau_ttm_fault_reserve_notify(struct ttm_buffer_object *bo) nvbo->placement.fpfn = 0; nvbo->placement.lpfn = dev_priv->fb_mappable_pages; - nouveau_bo_placement_set(nvbo, TTM_PL_VRAM, 0); + nouveau_bo_placement_set(nvbo, TTM_PL_FLAG_VRAM, 0); return nouveau_bo_validate(nvbo, false, true, false); } diff --git a/drivers/gpu/drm/nouveau/nouveau_display.c b/drivers/gpu/drm/nouveau/nouveau_display.c index a85e112..4b1cf74 100644 --- a/drivers/gpu/drm/nouveau/nouveau_display.c +++ b/drivers/gpu/drm/nouveau/nouveau_display.c @@ -300,7 +300,7 @@ nouveau_display_create(struct drm_device *dev) disp->color_vibrance_property->values[1] = 200; /* -100..+100 */ } - dev->mode_config.funcs = (void *)&nouveau_mode_config_funcs; + dev->mode_config.funcs = &nouveau_mode_config_funcs; dev->mode_config.fb_base = pci_resource_start(dev->pdev, 1); dev->mode_config.min_width = 0; diff --git a/drivers/gpu/drm/nouveau/nouveau_dp.c b/drivers/gpu/drm/nouveau/nouveau_dp.c index d996134..7e289d2 100644 --- a/drivers/gpu/drm/nouveau/nouveau_dp.c +++ b/drivers/gpu/drm/nouveau/nouveau_dp.c @@ -510,6 +510,25 @@ nouveau_dp_dpms(struct drm_encoder *encoder, int mode, u32 datarate, nouveau_dp_link_train(encoder, datarate, func); } +static void +nouveau_dp_probe_oui(struct drm_device *dev, struct nouveau_i2c_chan *auxch, + u8 *dpcd) +{ + u8 buf[3]; + + if (!(dpcd[DP_DOWN_STREAM_PORT_COUNT] & DP_OUI_SUPPORT)) + return; + + if (!auxch_tx(dev, auxch->drive, 9, DP_SINK_OUI, buf, 3)) + NV_DEBUG_KMS(dev, "Sink OUI: %02hx%02hx%02hx\n", + buf[0], buf[1], buf[2]); + + if (!auxch_tx(dev, auxch->drive, 9, DP_BRANCH_OUI, buf, 3)) + NV_DEBUG_KMS(dev, "Branch OUI: %02hx%02hx%02hx\n", + buf[0], buf[1], buf[2]); + +} + bool nouveau_dp_detect(struct drm_encoder *encoder) { @@ -544,6 +563,8 @@ nouveau_dp_detect(struct drm_encoder *encoder) NV_DEBUG_KMS(dev, "maximum: %dx%d\n", nv_encoder->dp.link_nr, nv_encoder->dp.link_bw); + nouveau_dp_probe_oui(dev, auxch, dpcd); + return true; } diff --git a/drivers/gpu/drm/nouveau/nouveau_state.c b/drivers/gpu/drm/nouveau/nouveau_state.c index c2a8511..298c09b 100644 --- a/drivers/gpu/drm/nouveau/nouveau_state.c +++ b/drivers/gpu/drm/nouveau/nouveau_state.c @@ -662,6 +662,12 @@ error: return ret; } +static const struct vga_switcheroo_client_ops nouveau_switcheroo_ops = { + .set_gpu_state = nouveau_switcheroo_set_state, + .reprobe = nouveau_switcheroo_reprobe, + .can_switch = nouveau_switcheroo_can_switch, +}; + int nouveau_card_init(struct drm_device *dev) { @@ -670,9 +676,7 @@ nouveau_card_init(struct drm_device *dev) int ret, e = 0; vga_client_register(dev->pdev, dev, NULL, nouveau_vga_set_decode); - vga_switcheroo_register_client(dev->pdev, nouveau_switcheroo_set_state, - nouveau_switcheroo_reprobe, - nouveau_switcheroo_can_switch); + vga_switcheroo_register_client(dev->pdev, &nouveau_switcheroo_ops); /* Initialise internal driver API hooks */ ret = nouveau_init_engine_ptrs(dev); diff --git a/drivers/gpu/drm/radeon/Makefile b/drivers/gpu/drm/radeon/Makefile index 9d83729..1efb6eb 100644 --- a/drivers/gpu/drm/radeon/Makefile +++ b/drivers/gpu/drm/radeon/Makefile @@ -70,8 +70,9 @@ radeon-y += radeon_device.o radeon_asic.o radeon_kms.o \ r200.o radeon_legacy_tv.o r600_cs.o r600_blit.o r600_blit_shaders.o \ r600_blit_kms.o radeon_pm.o atombios_dp.o r600_audio.o r600_hdmi.o \ evergreen.o evergreen_cs.o evergreen_blit_shaders.o evergreen_blit_kms.o \ - radeon_trace_points.o ni.o cayman_blit_shaders.o atombios_encoders.o \ - radeon_semaphore.o radeon_sa.o atombios_i2c.o si.o si_blit_shaders.o + evergreen_hdmi.o radeon_trace_points.o ni.o cayman_blit_shaders.o \ + atombios_encoders.o radeon_semaphore.o radeon_sa.o atombios_i2c.o si.o \ + si_blit_shaders.o radeon-$(CONFIG_COMPAT) += radeon_ioc32.o radeon-$(CONFIG_VGA_SWITCHEROO) += radeon_atpx_handler.o diff --git a/drivers/gpu/drm/radeon/atombios_dp.c b/drivers/gpu/drm/radeon/atombios_dp.c index cadbb10..5131b3b 100644 --- a/drivers/gpu/drm/radeon/atombios_dp.c +++ b/drivers/gpu/drm/radeon/atombios_dp.c @@ -530,6 +530,23 @@ u8 radeon_dp_getsinktype(struct radeon_connector *radeon_connector) dig_connector->dp_i2c_bus->rec.i2c_id, 0); } +static void radeon_dp_probe_oui(struct radeon_connector *radeon_connector) +{ + struct radeon_connector_atom_dig *dig_connector = radeon_connector->con_priv; + u8 buf[3]; + + if (!(dig_connector->dpcd[DP_DOWN_STREAM_PORT_COUNT] & DP_OUI_SUPPORT)) + return; + + if (radeon_dp_aux_native_read(radeon_connector, DP_SINK_OUI, buf, 3, 0)) + DRM_DEBUG_KMS("Sink OUI: %02hx%02hx%02hx\n", + buf[0], buf[1], buf[2]); + + if (radeon_dp_aux_native_read(radeon_connector, DP_BRANCH_OUI, buf, 3, 0)) + DRM_DEBUG_KMS("Branch OUI: %02hx%02hx%02hx\n", + buf[0], buf[1], buf[2]); +} + bool radeon_dp_getdpcd(struct radeon_connector *radeon_connector) { struct radeon_connector_atom_dig *dig_connector = radeon_connector->con_priv; @@ -543,6 +560,9 @@ bool radeon_dp_getdpcd(struct radeon_connector *radeon_connector) for (i = 0; i < 8; i++) DRM_DEBUG_KMS("%02x ", msg[i]); DRM_DEBUG_KMS("\n"); + + radeon_dp_probe_oui(radeon_connector); + return true; } dig_connector->dpcd[0] = 0; diff --git a/drivers/gpu/drm/radeon/atombios_encoders.c b/drivers/gpu/drm/radeon/atombios_encoders.c index b92a694..e7b1ec5 100644 --- a/drivers/gpu/drm/radeon/atombios_encoders.c +++ b/drivers/gpu/drm/radeon/atombios_encoders.c @@ -1926,7 +1926,10 @@ radeon_atom_encoder_mode_set(struct drm_encoder *encoder, if (atombios_get_encoder_mode(encoder) == ATOM_ENCODER_MODE_HDMI) { r600_hdmi_enable(encoder); - r600_hdmi_setmode(encoder, adjusted_mode); + if (ASIC_IS_DCE4(rdev)) + evergreen_hdmi_setmode(encoder, adjusted_mode); + else + r600_hdmi_setmode(encoder, adjusted_mode); } } @@ -2081,6 +2084,7 @@ radeon_atom_ext_encoder_setup_ddc(struct drm_encoder *encoder) static void radeon_atom_encoder_prepare(struct drm_encoder *encoder) { + struct radeon_device *rdev = encoder->dev->dev_private; struct radeon_encoder *radeon_encoder = to_radeon_encoder(encoder); struct drm_connector *connector = radeon_get_connector_for_encoder(encoder); @@ -2089,8 +2093,16 @@ static void radeon_atom_encoder_prepare(struct drm_encoder *encoder) (radeon_encoder_get_dp_bridge_encoder_id(encoder) != ENCODER_OBJECT_ID_NONE)) { struct radeon_encoder_atom_dig *dig = radeon_encoder->enc_priv; - if (dig) + if (dig) { dig->dig_encoder = radeon_atom_pick_dig_encoder(encoder); + if (radeon_encoder->active_device & ATOM_DEVICE_DFP_SUPPORT) { + if (rdev->family >= CHIP_R600) + dig->afmt = rdev->mode_info.afmt[dig->dig_encoder]; + else + /* RS600/690/740 have only 1 afmt block */ + dig->afmt = rdev->mode_info.afmt[0]; + } + } } radeon_atom_output_lock(encoder, true); diff --git a/drivers/gpu/drm/radeon/evergreen.c b/drivers/gpu/drm/radeon/evergreen.c index ecc29bc..58991af 100644 --- a/drivers/gpu/drm/radeon/evergreen.c +++ b/drivers/gpu/drm/radeon/evergreen.c @@ -3435,10 +3435,6 @@ int evergreen_init(struct radeon_device *rdev) { int r; - /* This don't do much */ - r = radeon_gem_init(rdev); - if (r) - return r; /* Read BIOS */ if (!radeon_get_bios(rdev)) { if (ASIC_IS_AVIVO(rdev)) @@ -3550,7 +3546,6 @@ void evergreen_fini(struct radeon_device *rdev) evergreen_pcie_gart_fini(rdev); r600_vram_scratch_fini(rdev); radeon_gem_fini(rdev); - radeon_semaphore_driver_fini(rdev); radeon_fence_driver_fini(rdev); radeon_agp_fini(rdev); radeon_bo_fini(rdev); diff --git a/drivers/gpu/drm/radeon/evergreen_blit_kms.c b/drivers/gpu/drm/radeon/evergreen_blit_kms.c index 222acd2..30f0480 100644 --- a/drivers/gpu/drm/radeon/evergreen_blit_kms.c +++ b/drivers/gpu/drm/radeon/evergreen_blit_kms.c @@ -637,7 +637,6 @@ int evergreen_blit_init(struct radeon_device *rdev) if (rdev->r600_blit.shader_obj) goto done; - mutex_init(&rdev->r600_blit.mutex); rdev->r600_blit.state_offset = 0; if (rdev->family < CHIP_CAYMAN) diff --git a/drivers/gpu/drm/radeon/evergreen_cs.c b/drivers/gpu/drm/radeon/evergreen_cs.c index 70089d3..4e7dd2b 100644 --- a/drivers/gpu/drm/radeon/evergreen_cs.c +++ b/drivers/gpu/drm/radeon/evergreen_cs.c @@ -1057,7 +1057,7 @@ static int evergreen_cs_packet_parse_vline(struct radeon_cs_parser *p) uint32_t header, h_idx, reg, wait_reg_mem_info; volatile uint32_t *ib; - ib = p->ib->ptr; + ib = p->ib.ptr; /* parse the WAIT_REG_MEM */ r = evergreen_cs_packet_parse(p, &wait_reg_mem, p->idx); @@ -1215,7 +1215,7 @@ static int evergreen_cs_check_reg(struct radeon_cs_parser *p, u32 reg, u32 idx) if (!(evergreen_reg_safe_bm[i] & m)) return 0; } - ib = p->ib->ptr; + ib = p->ib.ptr; switch (reg) { /* force following reg to 0 in an attempt to disable out buffer * which will need us to better understand how it works to perform @@ -1896,7 +1896,7 @@ static int evergreen_packet3_check(struct radeon_cs_parser *p, u32 idx_value; track = (struct evergreen_cs_track *)p->track; - ib = p->ib->ptr; + ib = p->ib.ptr; idx = pkt->idx + 1; idx_value = radeon_get_ib_value(p, idx); @@ -2610,8 +2610,8 @@ int evergreen_cs_parse(struct radeon_cs_parser *p) } } while (p->idx < p->chunks[p->chunk_ib_idx].length_dw); #if 0 - for (r = 0; r < p->ib->length_dw; r++) { - printk(KERN_INFO "%05d 0x%08X\n", r, p->ib->ptr[r]); + for (r = 0; r < p->ib.length_dw; r++) { + printk(KERN_INFO "%05d 0x%08X\n", r, p->ib.ptr[r]); mdelay(1); } #endif diff --git a/drivers/gpu/drm/radeon/evergreen_hdmi.c b/drivers/gpu/drm/radeon/evergreen_hdmi.c new file mode 100644 index 0000000..a51f880 --- /dev/null +++ b/drivers/gpu/drm/radeon/evergreen_hdmi.c @@ -0,0 +1,216 @@ +/* + * Copyright 2008 Advanced Micro Devices, Inc. + * Copyright 2008 Red Hat Inc. + * Copyright 2009 Christian König. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: Christian König + * RafaÅ‚ MiÅ‚ecki + */ +#include "drmP.h" +#include "radeon_drm.h" +#include "radeon.h" +#include "radeon_asic.h" +#include "evergreend.h" +#include "atom.h" + +/* + * update the N and CTS parameters for a given pixel clock rate + */ +static void evergreen_hdmi_update_ACR(struct drm_encoder *encoder, uint32_t clock) +{ + struct drm_device *dev = encoder->dev; + struct radeon_device *rdev = dev->dev_private; + struct radeon_hdmi_acr acr = r600_hdmi_acr(clock); + struct radeon_encoder *radeon_encoder = to_radeon_encoder(encoder); + struct radeon_encoder_atom_dig *dig = radeon_encoder->enc_priv; + uint32_t offset = dig->afmt->offset; + + WREG32(HDMI_ACR_32_0 + offset, HDMI_ACR_CTS_32(acr.cts_32khz)); + WREG32(HDMI_ACR_32_1 + offset, acr.n_32khz); + + WREG32(HDMI_ACR_44_0 + offset, HDMI_ACR_CTS_44(acr.cts_44_1khz)); + WREG32(HDMI_ACR_44_1 + offset, acr.n_44_1khz); + + WREG32(HDMI_ACR_48_0 + offset, HDMI_ACR_CTS_48(acr.cts_48khz)); + WREG32(HDMI_ACR_48_1 + offset, acr.n_48khz); +} + +/* + * calculate the crc for a given info frame + */ +static void evergreen_hdmi_infoframe_checksum(uint8_t packetType, + uint8_t versionNumber, + uint8_t length, + uint8_t *frame) +{ + int i; + frame[0] = packetType + versionNumber + length; + for (i = 1; i <= length; i++) + frame[0] += frame[i]; + frame[0] = 0x100 - frame[0]; +} + +/* + * build a HDMI Video Info Frame + */ +static void evergreen_hdmi_videoinfoframe( + struct drm_encoder *encoder, + uint8_t color_format, + int active_information_present, + uint8_t active_format_aspect_ratio, + uint8_t scan_information, + uint8_t colorimetry, + uint8_t ex_colorimetry, + uint8_t quantization, + int ITC, + uint8_t picture_aspect_ratio, + uint8_t video_format_identification, + uint8_t pixel_repetition, + uint8_t non_uniform_picture_scaling, + uint8_t bar_info_data_valid, + uint16_t top_bar, + uint16_t bottom_bar, + uint16_t left_bar, + uint16_t right_bar +) +{ + struct drm_device *dev = encoder->dev; + struct radeon_device *rdev = dev->dev_private; + struct radeon_encoder *radeon_encoder = to_radeon_encoder(encoder); + struct radeon_encoder_atom_dig *dig = radeon_encoder->enc_priv; + uint32_t offset = dig->afmt->offset; + + uint8_t frame[14]; + + frame[0x0] = 0; + frame[0x1] = + (scan_information & 0x3) | + ((bar_info_data_valid & 0x3) << 2) | + ((active_information_present & 0x1) << 4) | + ((color_format & 0x3) << 5); + frame[0x2] = + (active_format_aspect_ratio & 0xF) | + ((picture_aspect_ratio & 0x3) << 4) | + ((colorimetry & 0x3) << 6); + frame[0x3] = + (non_uniform_picture_scaling & 0x3) | + ((quantization & 0x3) << 2) | + ((ex_colorimetry & 0x7) << 4) | + ((ITC & 0x1) << 7); + frame[0x4] = (video_format_identification & 0x7F); + frame[0x5] = (pixel_repetition & 0xF); + frame[0x6] = (top_bar & 0xFF); + frame[0x7] = (top_bar >> 8); + frame[0x8] = (bottom_bar & 0xFF); + frame[0x9] = (bottom_bar >> 8); + frame[0xA] = (left_bar & 0xFF); + frame[0xB] = (left_bar >> 8); + frame[0xC] = (right_bar & 0xFF); + frame[0xD] = (right_bar >> 8); + + evergreen_hdmi_infoframe_checksum(0x82, 0x02, 0x0D, frame); + /* Our header values (type, version, length) should be alright, Intel + * is using the same. Checksum function also seems to be OK, it works + * fine for audio infoframe. However calculated value is always lower + * by 2 in comparison to fglrx. It breaks displaying anything in case + * of TVs that strictly check the checksum. Hack it manually here to + * workaround this issue. */ + frame[0x0] += 2; + + WREG32(AFMT_AVI_INFO0 + offset, + frame[0x0] | (frame[0x1] << 8) | (frame[0x2] << 16) | (frame[0x3] << 24)); + WREG32(AFMT_AVI_INFO1 + offset, + frame[0x4] | (frame[0x5] << 8) | (frame[0x6] << 16) | (frame[0x7] << 24)); + WREG32(AFMT_AVI_INFO2 + offset, + frame[0x8] | (frame[0x9] << 8) | (frame[0xA] << 16) | (frame[0xB] << 24)); + WREG32(AFMT_AVI_INFO3 + offset, + frame[0xC] | (frame[0xD] << 8)); +} + +/* + * update the info frames with the data from the current display mode + */ +void evergreen_hdmi_setmode(struct drm_encoder *encoder, struct drm_display_mode *mode) +{ + struct drm_device *dev = encoder->dev; + struct radeon_device *rdev = dev->dev_private; + struct radeon_encoder *radeon_encoder = to_radeon_encoder(encoder); + struct radeon_encoder_atom_dig *dig = radeon_encoder->enc_priv; + uint32_t offset; + + if (ASIC_IS_DCE5(rdev)) + return; + + /* Silent, r600_hdmi_enable will raise WARN for us */ + if (!dig->afmt->enabled) + return; + offset = dig->afmt->offset; + + r600_audio_set_clock(encoder, mode->clock); + + WREG32(HDMI_VBI_PACKET_CONTROL + offset, + HDMI_NULL_SEND); /* send null packets when required */ + + WREG32(AFMT_AUDIO_CRC_CONTROL + offset, 0x1000); + + WREG32(HDMI_AUDIO_PACKET_CONTROL + offset, + HDMI_AUDIO_DELAY_EN(1) | /* set the default audio delay */ + HDMI_AUDIO_PACKETS_PER_LINE(3)); /* should be suffient for all audio modes and small enough for all hblanks */ + + WREG32(AFMT_AUDIO_PACKET_CONTROL + offset, + AFMT_AUDIO_SAMPLE_SEND | /* send audio packets */ + AFMT_60958_CS_UPDATE); /* allow 60958 channel status fields to be updated */ + + WREG32(HDMI_ACR_PACKET_CONTROL + offset, + HDMI_ACR_AUTO_SEND | /* allow hw to sent ACR packets when required */ + HDMI_ACR_SOURCE); /* select SW CTS value */ + + WREG32(HDMI_VBI_PACKET_CONTROL + offset, + HDMI_NULL_SEND | /* send null packets when required */ + HDMI_GC_SEND | /* send general control packets */ + HDMI_GC_CONT); /* send general control packets every frame */ + + WREG32(HDMI_INFOFRAME_CONTROL0 + offset, + HDMI_AVI_INFO_SEND | /* enable AVI info frames */ + HDMI_AVI_INFO_CONT | /* send AVI info frames every frame/field */ + HDMI_AUDIO_INFO_SEND | /* enable audio info frames (frames won't be set until audio is enabled) */ + HDMI_AUDIO_INFO_CONT); /* required for audio info values to be updated */ + + WREG32(AFMT_INFOFRAME_CONTROL0 + offset, + AFMT_AUDIO_INFO_UPDATE); /* required for audio info values to be updated */ + + WREG32(HDMI_INFOFRAME_CONTROL1 + offset, + HDMI_AVI_INFO_LINE(2) | /* anything other than 0 */ + HDMI_AUDIO_INFO_LINE(2)); /* anything other than 0 */ + + WREG32(HDMI_GC + offset, 0); /* unset HDMI_GC_AVMUTE */ + + evergreen_hdmi_videoinfoframe(encoder, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0); + + evergreen_hdmi_update_ACR(encoder, mode->clock); + + /* it's unknown what these bits do excatly, but it's indeed quite useful for debugging */ + WREG32(AFMT_RAMP_CONTROL0 + offset, 0x00FFFFFF); + WREG32(AFMT_RAMP_CONTROL1 + offset, 0x007FFFFF); + WREG32(AFMT_RAMP_CONTROL2 + offset, 0x00000001); + WREG32(AFMT_RAMP_CONTROL3 + offset, 0x00000001); +} diff --git a/drivers/gpu/drm/radeon/ni.c b/drivers/gpu/drm/radeon/ni.c index 9cd2657..b01c2dd 100644 --- a/drivers/gpu/drm/radeon/ni.c +++ b/drivers/gpu/drm/radeon/ni.c @@ -1629,10 +1629,6 @@ int cayman_init(struct radeon_device *rdev) struct radeon_ring *ring = &rdev->ring[RADEON_RING_TYPE_GFX_INDEX]; int r; - /* This don't do much */ - r = radeon_gem_init(rdev); - if (r) - return r; /* Read BIOS */ if (!radeon_get_bios(rdev)) { if (ASIC_IS_AVIVO(rdev)) @@ -1744,7 +1740,6 @@ void cayman_fini(struct radeon_device *rdev) cayman_pcie_gart_fini(rdev); r600_vram_scratch_fini(rdev); radeon_gem_fini(rdev); - radeon_semaphore_driver_fini(rdev); radeon_fence_driver_fini(rdev); radeon_bo_fini(rdev); radeon_atombios_fini(rdev); diff --git a/drivers/gpu/drm/radeon/r100.c b/drivers/gpu/drm/radeon/r100.c index ad6ceb7..fb44e7e 100644 --- a/drivers/gpu/drm/radeon/r100.c +++ b/drivers/gpu/drm/radeon/r100.c @@ -139,9 +139,9 @@ int r100_reloc_pitch_offset(struct radeon_cs_parser *p, } tmp |= tile_flags; - p->ib->ptr[idx] = (value & 0x3fc00000) | tmp; + p->ib.ptr[idx] = (value & 0x3fc00000) | tmp; } else - p->ib->ptr[idx] = (value & 0xffc00000) | tmp; + p->ib.ptr[idx] = (value & 0xffc00000) | tmp; return 0; } @@ -156,7 +156,7 @@ int r100_packet3_load_vbpntr(struct radeon_cs_parser *p, volatile uint32_t *ib; u32 idx_value; - ib = p->ib->ptr; + ib = p->ib.ptr; track = (struct r100_cs_track *)p->track; c = radeon_get_ib_value(p, idx++) & 0x1F; if (c > 16) { @@ -1275,7 +1275,7 @@ void r100_cs_dump_packet(struct radeon_cs_parser *p, unsigned i; unsigned idx; - ib = p->ib->ptr; + ib = p->ib.ptr; idx = pkt->idx; for (i = 0; i <= (pkt->count + 1); i++, idx++) { DRM_INFO("ib[%d]=0x%08X\n", idx, ib[idx]); @@ -1354,7 +1354,7 @@ int r100_cs_packet_parse_vline(struct radeon_cs_parser *p) uint32_t header, h_idx, reg; volatile uint32_t *ib; - ib = p->ib->ptr; + ib = p->ib.ptr; /* parse the wait until */ r = r100_cs_packet_parse(p, &waitreloc, p->idx); @@ -1533,7 +1533,7 @@ static int r100_packet0_check(struct radeon_cs_parser *p, u32 tile_flags = 0; u32 idx_value; - ib = p->ib->ptr; + ib = p->ib.ptr; track = (struct r100_cs_track *)p->track; idx_value = radeon_get_ib_value(p, idx); @@ -1889,7 +1889,7 @@ static int r100_packet3_check(struct radeon_cs_parser *p, volatile uint32_t *ib; int r; - ib = p->ib->ptr; + ib = p->ib.ptr; idx = pkt->idx + 1; track = (struct r100_cs_track *)p->track; switch (pkt->opcode) { @@ -2008,6 +2008,8 @@ int r100_cs_parse(struct radeon_cs_parser *p) int r; track = kzalloc(sizeof(*track), GFP_KERNEL); + if (!track) + return -ENOMEM; r100_cs_track_clear(p->rdev, track); p->track = track; do { @@ -3684,7 +3686,7 @@ void r100_ring_ib_execute(struct radeon_device *rdev, struct radeon_ib *ib) int r100_ib_test(struct radeon_device *rdev, struct radeon_ring *ring) { - struct radeon_ib *ib; + struct radeon_ib ib; uint32_t scratch; uint32_t tmp = 0; unsigned i; @@ -3700,22 +3702,22 @@ int r100_ib_test(struct radeon_device *rdev, struct radeon_ring *ring) if (r) { return r; } - ib->ptr[0] = PACKET0(scratch, 0); - ib->ptr[1] = 0xDEADBEEF; - ib->ptr[2] = PACKET2(0); - ib->ptr[3] = PACKET2(0); - ib->ptr[4] = PACKET2(0); - ib->ptr[5] = PACKET2(0); - ib->ptr[6] = PACKET2(0); - ib->ptr[7] = PACKET2(0); - ib->length_dw = 8; - r = radeon_ib_schedule(rdev, ib); + ib.ptr[0] = PACKET0(scratch, 0); + ib.ptr[1] = 0xDEADBEEF; + ib.ptr[2] = PACKET2(0); + ib.ptr[3] = PACKET2(0); + ib.ptr[4] = PACKET2(0); + ib.ptr[5] = PACKET2(0); + ib.ptr[6] = PACKET2(0); + ib.ptr[7] = PACKET2(0); + ib.length_dw = 8; + r = radeon_ib_schedule(rdev, &ib); if (r) { radeon_scratch_free(rdev, scratch); radeon_ib_free(rdev, &ib); return r; } - r = radeon_fence_wait(ib->fence, false); + r = radeon_fence_wait(ib.fence, false); if (r) { return r; } diff --git a/drivers/gpu/drm/radeon/r200.c b/drivers/gpu/drm/radeon/r200.c index a59cc47..a26144d 100644 --- a/drivers/gpu/drm/radeon/r200.c +++ b/drivers/gpu/drm/radeon/r200.c @@ -154,7 +154,7 @@ int r200_packet0_check(struct radeon_cs_parser *p, u32 tile_flags = 0; u32 idx_value; - ib = p->ib->ptr; + ib = p->ib.ptr; track = (struct r100_cs_track *)p->track; idx_value = radeon_get_ib_value(p, idx); switch (reg) { diff --git a/drivers/gpu/drm/radeon/r300.c b/drivers/gpu/drm/radeon/r300.c index 6419a59..97722a3 100644 --- a/drivers/gpu/drm/radeon/r300.c +++ b/drivers/gpu/drm/radeon/r300.c @@ -604,7 +604,7 @@ static int r300_packet0_check(struct radeon_cs_parser *p, int r; u32 idx_value; - ib = p->ib->ptr; + ib = p->ib.ptr; track = (struct r100_cs_track *)p->track; idx_value = radeon_get_ib_value(p, idx); @@ -1146,7 +1146,7 @@ static int r300_packet3_check(struct radeon_cs_parser *p, unsigned idx; int r; - ib = p->ib->ptr; + ib = p->ib.ptr; idx = pkt->idx + 1; track = (struct r100_cs_track *)p->track; switch(pkt->opcode) { diff --git a/drivers/gpu/drm/radeon/r600.c b/drivers/gpu/drm/radeon/r600.c index d02f13f..ab5d6f2 100644 --- a/drivers/gpu/drm/radeon/r600.c +++ b/drivers/gpu/drm/radeon/r600.c @@ -713,6 +713,14 @@ void r600_hpd_init(struct radeon_device *rdev) list_for_each_entry(connector, &dev->mode_config.connector_list, head) { struct radeon_connector *radeon_connector = to_radeon_connector(connector); + if (connector->connector_type == DRM_MODE_CONNECTOR_eDP || + connector->connector_type == DRM_MODE_CONNECTOR_LVDS) { + /* don't try to enable hpd on eDP or LVDS avoid breaking the + * aux dp channel on imac and help (but not completely fix) + * https://bugzilla.redhat.com/show_bug.cgi?id=726143 + */ + continue; + } if (ASIC_IS_DCE3(rdev)) { u32 tmp = DC_HPDx_CONNECTION_TIMER(0x9c4) | DC_HPDx_RX_INT_TIMER(0xfa); if (ASIC_IS_DCE32(rdev)) @@ -2363,20 +2371,15 @@ int r600_copy_blit(struct radeon_device *rdev, unsigned num_gpu_pages, struct radeon_fence *fence) { + struct radeon_sa_bo *vb = NULL; int r; - mutex_lock(&rdev->r600_blit.mutex); - rdev->r600_blit.vb_ib = NULL; - r = r600_blit_prepare_copy(rdev, num_gpu_pages); + r = r600_blit_prepare_copy(rdev, num_gpu_pages, &vb); if (r) { - if (rdev->r600_blit.vb_ib) - radeon_ib_free(rdev, &rdev->r600_blit.vb_ib); - mutex_unlock(&rdev->r600_blit.mutex); return r; } - r600_kms_blit_copy(rdev, src_offset, dst_offset, num_gpu_pages); - r600_blit_done_copy(rdev, fence); - mutex_unlock(&rdev->r600_blit.mutex); + r600_kms_blit_copy(rdev, src_offset, dst_offset, num_gpu_pages, vb); + r600_blit_done_copy(rdev, fence, vb); return 0; } @@ -2557,10 +2560,6 @@ int r600_init(struct radeon_device *rdev) if (r600_debugfs_mc_info_init(rdev)) { DRM_ERROR("Failed to register debugfs file for mc !\n"); } - /* This don't do much */ - r = radeon_gem_init(rdev); - if (r) - return r; /* Read BIOS */ if (!radeon_get_bios(rdev)) { if (ASIC_IS_AVIVO(rdev)) @@ -2658,7 +2657,6 @@ void r600_fini(struct radeon_device *rdev) r600_vram_scratch_fini(rdev); radeon_agp_fini(rdev); radeon_gem_fini(rdev); - radeon_semaphore_driver_fini(rdev); radeon_fence_driver_fini(rdev); radeon_bo_fini(rdev); radeon_atombios_fini(rdev); @@ -2687,7 +2685,7 @@ void r600_ring_ib_execute(struct radeon_device *rdev, struct radeon_ib *ib) int r600_ib_test(struct radeon_device *rdev, struct radeon_ring *ring) { - struct radeon_ib *ib; + struct radeon_ib ib; uint32_t scratch; uint32_t tmp = 0; unsigned i; @@ -2705,18 +2703,18 @@ int r600_ib_test(struct radeon_device *rdev, struct radeon_ring *ring) DRM_ERROR("radeon: failed to get ib (%d).\n", r); return r; } - ib->ptr[0] = PACKET3(PACKET3_SET_CONFIG_REG, 1); - ib->ptr[1] = ((scratch - PACKET3_SET_CONFIG_REG_OFFSET) >> 2); - ib->ptr[2] = 0xDEADBEEF; - ib->length_dw = 3; - r = radeon_ib_schedule(rdev, ib); + ib.ptr[0] = PACKET3(PACKET3_SET_CONFIG_REG, 1); + ib.ptr[1] = ((scratch - PACKET3_SET_CONFIG_REG_OFFSET) >> 2); + ib.ptr[2] = 0xDEADBEEF; + ib.length_dw = 3; + r = radeon_ib_schedule(rdev, &ib); if (r) { radeon_scratch_free(rdev, scratch); radeon_ib_free(rdev, &ib); DRM_ERROR("radeon: failed to schedule ib (%d).\n", r); return r; } - r = radeon_fence_wait(ib->fence, false); + r = radeon_fence_wait(ib.fence, false); if (r) { DRM_ERROR("radeon: fence wait failed (%d).\n", r); return r; @@ -2728,7 +2726,7 @@ int r600_ib_test(struct radeon_device *rdev, struct radeon_ring *ring) DRM_UDELAY(1); } if (i < rdev->usec_timeout) { - DRM_INFO("ib test on ring %d succeeded in %u usecs\n", ib->fence->ring, i); + DRM_INFO("ib test on ring %d succeeded in %u usecs\n", ib.fence->ring, i); } else { DRM_ERROR("radeon: ib test failed (scratch(0x%04X)=0x%08X)\n", scratch, tmp); diff --git a/drivers/gpu/drm/radeon/r600_audio.c b/drivers/gpu/drm/radeon/r600_audio.c index b922a3c..7c4fa77 100644 --- a/drivers/gpu/drm/radeon/r600_audio.c +++ b/drivers/gpu/drm/radeon/r600_audio.c @@ -30,6 +30,29 @@ #include "atom.h" /* + * check if enc_priv stores radeon_encoder_atom_dig + */ +static bool radeon_dig_encoder(struct drm_encoder *encoder) +{ + struct radeon_encoder *radeon_encoder = to_radeon_encoder(encoder); + switch (radeon_encoder->encoder_id) { + case ENCODER_OBJECT_ID_INTERNAL_LVDS: + case ENCODER_OBJECT_ID_INTERNAL_TMDS1: + case ENCODER_OBJECT_ID_INTERNAL_KLDSCP_TMDS1: + case ENCODER_OBJECT_ID_INTERNAL_LVTM1: + case ENCODER_OBJECT_ID_INTERNAL_DVO1: + case ENCODER_OBJECT_ID_INTERNAL_KLDSCP_DVO1: + case ENCODER_OBJECT_ID_INTERNAL_DDI: + case ENCODER_OBJECT_ID_INTERNAL_UNIPHY: + case ENCODER_OBJECT_ID_INTERNAL_KLDSCP_LVTMA: + case ENCODER_OBJECT_ID_INTERNAL_UNIPHY1: + case ENCODER_OBJECT_ID_INTERNAL_UNIPHY2: + return true; + } + return false; +} + +/* * check if the chipset is supported */ static int r600_audio_chipset_supported(struct radeon_device *rdev) @@ -40,67 +63,56 @@ static int r600_audio_chipset_supported(struct radeon_device *rdev) || rdev->family == CHIP_RS740; } -/* - * current number of channels - */ -int r600_audio_channels(struct radeon_device *rdev) -{ - return (RREG32(R600_AUDIO_RATE_BPS_CHANNEL) & 0x7) + 1; -} - -/* - * current bits per sample - */ -int r600_audio_bits_per_sample(struct radeon_device *rdev) +struct r600_audio r600_audio_status(struct radeon_device *rdev) { - uint32_t value = (RREG32(R600_AUDIO_RATE_BPS_CHANNEL) & 0xF0) >> 4; - switch (value) { - case 0x0: return 8; - case 0x1: return 16; - case 0x2: return 20; - case 0x3: return 24; - case 0x4: return 32; - } + struct r600_audio status; + uint32_t value; - dev_err(rdev->dev, "Unknown bits per sample 0x%x using 16 instead\n", - (int)value); + value = RREG32(R600_AUDIO_RATE_BPS_CHANNEL); - return 16; -} + /* number of channels */ + status.channels = (value & 0x7) + 1; -/* - * current sampling rate in HZ - */ -int r600_audio_rate(struct radeon_device *rdev) -{ - uint32_t value = RREG32(R600_AUDIO_RATE_BPS_CHANNEL); - uint32_t result; + /* bits per sample */ + switch ((value & 0xF0) >> 4) { + case 0x0: + status.bits_per_sample = 8; + break; + case 0x1: + status.bits_per_sample = 16; + break; + case 0x2: + status.bits_per_sample = 20; + break; + case 0x3: + status.bits_per_sample = 24; + break; + case 0x4: + status.bits_per_sample = 32; + break; + default: + dev_err(rdev->dev, "Unknown bits per sample 0x%x, using 16\n", + (int)value); + status.bits_per_sample = 16; + } + /* current sampling rate in HZ */ if (value & 0x4000) - result = 44100; + status.rate = 44100; else - result = 48000; + status.rate = 48000; + status.rate *= ((value >> 11) & 0x7) + 1; + status.rate /= ((value >> 8) & 0x7) + 1; - result *= ((value >> 11) & 0x7) + 1; - result /= ((value >> 8) & 0x7) + 1; + value = RREG32(R600_AUDIO_STATUS_BITS); - return result; -} + /* iec 60958 status bits */ + status.status_bits = value & 0xff; -/* - * iec 60958 status bits - */ -uint8_t r600_audio_status_bits(struct radeon_device *rdev) -{ - return RREG32(R600_AUDIO_STATUS_BITS) & 0xff; -} + /* iec 60958 category code */ + status.category_code = (value >> 8) & 0xff; -/* - * iec 60958 category code - */ -uint8_t r600_audio_category_code(struct radeon_device *rdev) -{ - return (RREG32(R600_AUDIO_STATUS_BITS) >> 8) & 0xff; + return status; } /* @@ -111,31 +123,23 @@ void r600_audio_update_hdmi(struct work_struct *work) struct radeon_device *rdev = container_of(work, struct radeon_device, audio_work); struct drm_device *dev = rdev->ddev; - - int channels = r600_audio_channels(rdev); - int rate = r600_audio_rate(rdev); - int bps = r600_audio_bits_per_sample(rdev); - uint8_t status_bits = r600_audio_status_bits(rdev); - uint8_t category_code = r600_audio_category_code(rdev); + struct r600_audio audio_status = r600_audio_status(rdev); struct drm_encoder *encoder; - int changes = 0; - - changes |= channels != rdev->audio.channels; - changes |= rate != rdev->audio.rate; - changes |= bps != rdev->audio.bits_per_sample; - changes |= status_bits != rdev->audio.status_bits; - changes |= category_code != rdev->audio.category_code; - - if (changes) { - rdev->audio.channels = channels; - rdev->audio.rate = rate; - rdev->audio.bits_per_sample = bps; - rdev->audio.status_bits = status_bits; - rdev->audio.category_code = category_code; + bool changed = false; + + if (rdev->audio_status.channels != audio_status.channels || + rdev->audio_status.rate != audio_status.rate || + rdev->audio_status.bits_per_sample != audio_status.bits_per_sample || + rdev->audio_status.status_bits != audio_status.status_bits || + rdev->audio_status.category_code != audio_status.category_code) { + rdev->audio_status = audio_status; + changed = true; } list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) { - if (changes || r600_hdmi_buffer_status_changed(encoder)) + if (!radeon_dig_encoder(encoder)) + continue; + if (changed || r600_hdmi_buffer_status_changed(encoder)) r600_hdmi_update_audio_settings(encoder); } } @@ -157,7 +161,7 @@ static void r600_audio_engine_enable(struct radeon_device *rdev, bool enable) WREG32_P(R600_AUDIO_ENABLE, enable ? 0x81000000 : 0x0, ~0x81000000); } - rdev->audio.enabled = enable; + rdev->audio_enabled = enable; } /* @@ -170,11 +174,11 @@ int r600_audio_init(struct radeon_device *rdev) r600_audio_engine_enable(rdev, true); - rdev->audio.channels = -1; - rdev->audio.rate = -1; - rdev->audio.bits_per_sample = -1; - rdev->audio.status_bits = 0; - rdev->audio.category_code = 0; + rdev->audio_status.channels = -1; + rdev->audio_status.rate = -1; + rdev->audio_status.bits_per_sample = -1; + rdev->audio_status.status_bits = 0; + rdev->audio_status.category_code = 0; return 0; } @@ -243,7 +247,7 @@ void r600_audio_set_clock(struct drm_encoder *encoder, int clock) */ void r600_audio_fini(struct radeon_device *rdev) { - if (!rdev->audio.enabled) + if (!rdev->audio_enabled) return; r600_audio_engine_enable(rdev, false); diff --git a/drivers/gpu/drm/radeon/r600_blit_kms.c b/drivers/gpu/drm/radeon/r600_blit_kms.c index db38f58..ef20822 100644 --- a/drivers/gpu/drm/radeon/r600_blit_kms.c +++ b/drivers/gpu/drm/radeon/r600_blit_kms.c @@ -513,7 +513,6 @@ int r600_blit_init(struct radeon_device *rdev) rdev->r600_blit.primitives.set_default_state = set_default_state; rdev->r600_blit.ring_size_common = 40; /* shaders + def state */ - rdev->r600_blit.ring_size_common += 16; /* fence emit for VB IB */ rdev->r600_blit.ring_size_common += 5; /* done copy */ rdev->r600_blit.ring_size_common += 16; /* fence emit for done copy */ @@ -528,7 +527,6 @@ int r600_blit_init(struct radeon_device *rdev) if (rdev->r600_blit.shader_obj) goto done; - mutex_init(&rdev->r600_blit.mutex); rdev->r600_blit.state_offset = 0; if (rdev->family >= CHIP_RV770) @@ -621,27 +619,6 @@ void r600_blit_fini(struct radeon_device *rdev) radeon_bo_unref(&rdev->r600_blit.shader_obj); } -static int r600_vb_ib_get(struct radeon_device *rdev, unsigned size) -{ - int r; - r = radeon_ib_get(rdev, RADEON_RING_TYPE_GFX_INDEX, - &rdev->r600_blit.vb_ib, size); - if (r) { - DRM_ERROR("failed to get IB for vertex buffer\n"); - return r; - } - - rdev->r600_blit.vb_total = size; - rdev->r600_blit.vb_used = 0; - return 0; -} - -static void r600_vb_ib_put(struct radeon_device *rdev) -{ - radeon_fence_emit(rdev, rdev->r600_blit.vb_ib->fence); - radeon_ib_free(rdev, &rdev->r600_blit.vb_ib); -} - static unsigned r600_blit_create_rect(unsigned num_gpu_pages, int *width, int *height, int max_dim) { @@ -688,7 +665,8 @@ static unsigned r600_blit_create_rect(unsigned num_gpu_pages, } -int r600_blit_prepare_copy(struct radeon_device *rdev, unsigned num_gpu_pages) +int r600_blit_prepare_copy(struct radeon_device *rdev, unsigned num_gpu_pages, + struct radeon_sa_bo **vb) { struct radeon_ring *ring = &rdev->ring[RADEON_RING_TYPE_GFX_INDEX]; int r; @@ -705,46 +683,54 @@ int r600_blit_prepare_copy(struct radeon_device *rdev, unsigned num_gpu_pages) } /* 48 bytes for vertex per loop */ - r = r600_vb_ib_get(rdev, (num_loops*48)+256); - if (r) + r = radeon_sa_bo_new(rdev, &rdev->ring_tmp_bo, vb, + (num_loops*48)+256, 256, true); + if (r) { return r; + } /* calculate number of loops correctly */ ring_size = num_loops * dwords_per_loop; ring_size += rdev->r600_blit.ring_size_common; r = radeon_ring_lock(rdev, ring, ring_size); - if (r) + if (r) { + radeon_sa_bo_free(rdev, vb, NULL); return r; + } rdev->r600_blit.primitives.set_default_state(rdev); rdev->r600_blit.primitives.set_shaders(rdev); return 0; } -void r600_blit_done_copy(struct radeon_device *rdev, struct radeon_fence *fence) +void r600_blit_done_copy(struct radeon_device *rdev, struct radeon_fence *fence, + struct radeon_sa_bo *vb) { + struct radeon_ring *ring = &rdev->ring[RADEON_RING_TYPE_GFX_INDEX]; int r; - if (rdev->r600_blit.vb_ib) - r600_vb_ib_put(rdev); - - if (fence) - r = radeon_fence_emit(rdev, fence); + r = radeon_fence_emit(rdev, fence); + if (r) { + radeon_ring_unlock_undo(rdev, ring); + return; + } - radeon_ring_unlock_commit(rdev, &rdev->ring[RADEON_RING_TYPE_GFX_INDEX]); + radeon_ring_unlock_commit(rdev, ring); + radeon_sa_bo_free(rdev, &vb, fence); } void r600_kms_blit_copy(struct radeon_device *rdev, u64 src_gpu_addr, u64 dst_gpu_addr, - unsigned num_gpu_pages) + unsigned num_gpu_pages, + struct radeon_sa_bo *vb) { u64 vb_gpu_addr; - u32 *vb; + u32 *vb_cpu_addr; - DRM_DEBUG("emitting copy %16llx %16llx %d %d\n", - src_gpu_addr, dst_gpu_addr, - num_gpu_pages, rdev->r600_blit.vb_used); - vb = (u32 *)(rdev->r600_blit.vb_ib->ptr + rdev->r600_blit.vb_used); + DRM_DEBUG("emitting copy %16llx %16llx %d\n", + src_gpu_addr, dst_gpu_addr, num_gpu_pages); + vb_cpu_addr = (u32 *)radeon_sa_bo_cpu_addr(vb); + vb_gpu_addr = radeon_sa_bo_gpu_addr(vb); while (num_gpu_pages) { int w, h; @@ -756,39 +742,34 @@ void r600_kms_blit_copy(struct radeon_device *rdev, size_in_bytes = pages_per_loop * RADEON_GPU_PAGE_SIZE; DRM_DEBUG("rectangle w=%d h=%d\n", w, h); - if ((rdev->r600_blit.vb_used + 48) > rdev->r600_blit.vb_total) { - WARN_ON(1); - } - - vb[0] = 0; - vb[1] = 0; - vb[2] = 0; - vb[3] = 0; + vb_cpu_addr[0] = 0; + vb_cpu_addr[1] = 0; + vb_cpu_addr[2] = 0; + vb_cpu_addr[3] = 0; - vb[4] = 0; - vb[5] = i2f(h); - vb[6] = 0; - vb[7] = i2f(h); + vb_cpu_addr[4] = 0; + vb_cpu_addr[5] = i2f(h); + vb_cpu_addr[6] = 0; + vb_cpu_addr[7] = i2f(h); - vb[8] = i2f(w); - vb[9] = i2f(h); - vb[10] = i2f(w); - vb[11] = i2f(h); + vb_cpu_addr[8] = i2f(w); + vb_cpu_addr[9] = i2f(h); + vb_cpu_addr[10] = i2f(w); + vb_cpu_addr[11] = i2f(h); rdev->r600_blit.primitives.set_tex_resource(rdev, FMT_8_8_8_8, w, h, w, src_gpu_addr, size_in_bytes); rdev->r600_blit.primitives.set_render_target(rdev, COLOR_8_8_8_8, w, h, dst_gpu_addr); rdev->r600_blit.primitives.set_scissors(rdev, 0, 0, w, h); - vb_gpu_addr = rdev->r600_blit.vb_ib->gpu_addr + rdev->r600_blit.vb_used; rdev->r600_blit.primitives.set_vtx_resource(rdev, vb_gpu_addr); rdev->r600_blit.primitives.draw_auto(rdev); rdev->r600_blit.primitives.cp_set_surface_sync(rdev, PACKET3_CB_ACTION_ENA | PACKET3_CB0_DEST_BASE_ENA, size_in_bytes, dst_gpu_addr); - vb += 12; - rdev->r600_blit.vb_used += 4*12; + vb_cpu_addr += 12; + vb_gpu_addr += 4*12; src_gpu_addr += size_in_bytes; dst_gpu_addr += size_in_bytes; num_gpu_pages -= pages_per_loop; diff --git a/drivers/gpu/drm/radeon/r600_cs.c b/drivers/gpu/drm/radeon/r600_cs.c index b8e12af..0133f5f 100644 --- a/drivers/gpu/drm/radeon/r600_cs.c +++ b/drivers/gpu/drm/radeon/r600_cs.c @@ -345,7 +345,7 @@ static int r600_cs_track_validate_cb(struct radeon_cs_parser *p, int i) u32 height, height_align, pitch, pitch_align, depth_align; u64 base_offset, base_align; struct array_mode_checker array_check; - volatile u32 *ib = p->ib->ptr; + volatile u32 *ib = p->ib.ptr; unsigned array_mode; u32 format; @@ -471,7 +471,7 @@ static int r600_cs_track_validate_db(struct radeon_cs_parser *p) u64 base_offset, base_align; struct array_mode_checker array_check; int array_mode; - volatile u32 *ib = p->ib->ptr; + volatile u32 *ib = p->ib.ptr; if (track->db_bo == NULL) { @@ -961,7 +961,7 @@ static int r600_cs_packet_parse_vline(struct radeon_cs_parser *p) uint32_t header, h_idx, reg, wait_reg_mem_info; volatile uint32_t *ib; - ib = p->ib->ptr; + ib = p->ib.ptr; /* parse the WAIT_REG_MEM */ r = r600_cs_packet_parse(p, &wait_reg_mem, p->idx); @@ -1110,7 +1110,7 @@ static int r600_cs_check_reg(struct radeon_cs_parser *p, u32 reg, u32 idx) m = 1 << ((reg >> 2) & 31); if (!(r600_reg_safe_bm[i] & m)) return 0; - ib = p->ib->ptr; + ib = p->ib.ptr; switch (reg) { /* force following reg to 0 in an attempt to disable out buffer * which will need us to better understand how it works to perform @@ -1714,7 +1714,7 @@ static int r600_packet3_check(struct radeon_cs_parser *p, u32 idx_value; track = (struct r600_cs_track *)p->track; - ib = p->ib->ptr; + ib = p->ib.ptr; idx = pkt->idx + 1; idx_value = radeon_get_ib_value(p, idx); @@ -2249,8 +2249,8 @@ int r600_cs_parse(struct radeon_cs_parser *p) } } while (p->idx < p->chunks[p->chunk_ib_idx].length_dw); #if 0 - for (r = 0; r < p->ib->length_dw; r++) { - printk(KERN_INFO "%05d 0x%08X\n", r, p->ib->ptr[r]); + for (r = 0; r < p->ib.length_dw; r++) { + printk(KERN_INFO "%05d 0x%08X\n", r, p->ib.ptr[r]); mdelay(1); } #endif @@ -2298,7 +2298,6 @@ int r600_cs_legacy(struct drm_device *dev, void *data, struct drm_file *filp, { struct radeon_cs_parser parser; struct radeon_cs_chunk *ib_chunk; - struct radeon_ib fake_ib; struct r600_cs_track *track; int r; @@ -2314,9 +2313,8 @@ int r600_cs_legacy(struct drm_device *dev, void *data, struct drm_file *filp, parser.dev = &dev->pdev->dev; parser.rdev = NULL; parser.family = family; - parser.ib = &fake_ib; parser.track = track; - fake_ib.ptr = ib; + parser.ib.ptr = ib; r = radeon_cs_parser_init(&parser, data); if (r) { DRM_ERROR("Failed to initialize parser !\n"); @@ -2333,8 +2331,8 @@ int r600_cs_legacy(struct drm_device *dev, void *data, struct drm_file *filp, * input memory (cached) and write to the IB (which can be * uncached). */ ib_chunk = &parser.chunks[parser.chunk_ib_idx]; - parser.ib->length_dw = ib_chunk->length_dw; - *l = parser.ib->length_dw; + parser.ib.length_dw = ib_chunk->length_dw; + *l = parser.ib.length_dw; r = r600_cs_parse(&parser); if (r) { DRM_ERROR("Invalid command stream !\n"); diff --git a/drivers/gpu/drm/radeon/r600_hdmi.c b/drivers/gpu/drm/radeon/r600_hdmi.c index c6de0022..226379e 100644 --- a/drivers/gpu/drm/radeon/r600_hdmi.c +++ b/drivers/gpu/drm/radeon/r600_hdmi.c @@ -53,19 +53,7 @@ enum r600_hdmi_iec_status_bits { AUDIO_STATUS_LEVEL = 0x80 }; -struct { - uint32_t Clock; - - int N_32kHz; - int CTS_32kHz; - - int N_44_1kHz; - int CTS_44_1kHz; - - int N_48kHz; - int CTS_48kHz; - -} r600_hdmi_ACR[] = { +struct radeon_hdmi_acr r600_hdmi_predefined_acr[] = { /* 32kHz 44.1kHz 48kHz */ /* Clock N CTS N CTS N CTS */ { 25174, 4576, 28125, 7007, 31250, 6864, 28125 }, /* 25,20/1.001 MHz */ @@ -84,7 +72,7 @@ struct { /* * calculate CTS value if it's not found in the table */ -static void r600_hdmi_calc_CTS(uint32_t clock, int *CTS, int N, int freq) +static void r600_hdmi_calc_cts(uint32_t clock, int *CTS, int N, int freq) { if (*CTS == 0) *CTS = clock * N / (128 * freq) * 1000; @@ -92,6 +80,24 @@ static void r600_hdmi_calc_CTS(uint32_t clock, int *CTS, int N, int freq) N, *CTS, freq); } +struct radeon_hdmi_acr r600_hdmi_acr(uint32_t clock) +{ + struct radeon_hdmi_acr res; + u8 i; + + for (i = 0; r600_hdmi_predefined_acr[i].clock != clock && + r600_hdmi_predefined_acr[i].clock != 0; i++) + ; + res = r600_hdmi_predefined_acr[i]; + + /* In case some CTS are missing */ + r600_hdmi_calc_cts(clock, &res.cts_32khz, res.n_32khz, 32000); + r600_hdmi_calc_cts(clock, &res.cts_44_1khz, res.n_44_1khz, 44100); + r600_hdmi_calc_cts(clock, &res.cts_48khz, res.n_48khz, 48000); + + return res; +} + /* * update the N and CTS parameters for a given pixel clock rate */ @@ -99,30 +105,19 @@ static void r600_hdmi_update_ACR(struct drm_encoder *encoder, uint32_t clock) { struct drm_device *dev = encoder->dev; struct radeon_device *rdev = dev->dev_private; - uint32_t offset = to_radeon_encoder(encoder)->hdmi_offset; - int CTS; - int N; - int i; + struct radeon_hdmi_acr acr = r600_hdmi_acr(clock); + struct radeon_encoder *radeon_encoder = to_radeon_encoder(encoder); + struct radeon_encoder_atom_dig *dig = radeon_encoder->enc_priv; + uint32_t offset = dig->afmt->offset; + + WREG32(HDMI0_ACR_32_0 + offset, HDMI0_ACR_CTS_32(acr.cts_32khz)); + WREG32(HDMI0_ACR_32_1 + offset, acr.n_32khz); + + WREG32(HDMI0_ACR_44_0 + offset, HDMI0_ACR_CTS_44(acr.cts_44_1khz)); + WREG32(HDMI0_ACR_44_1 + offset, acr.n_44_1khz); - for (i = 0; r600_hdmi_ACR[i].Clock != clock && r600_hdmi_ACR[i].Clock != 0; i++); - - CTS = r600_hdmi_ACR[i].CTS_32kHz; - N = r600_hdmi_ACR[i].N_32kHz; - r600_hdmi_calc_CTS(clock, &CTS, N, 32000); - WREG32(HDMI0_ACR_32_0 + offset, HDMI0_ACR_CTS_32(CTS)); - WREG32(HDMI0_ACR_32_1 + offset, N); - - CTS = r600_hdmi_ACR[i].CTS_44_1kHz; - N = r600_hdmi_ACR[i].N_44_1kHz; - r600_hdmi_calc_CTS(clock, &CTS, N, 44100); - WREG32(HDMI0_ACR_44_0 + offset, HDMI0_ACR_CTS_44(CTS)); - WREG32(HDMI0_ACR_44_1 + offset, N); - - CTS = r600_hdmi_ACR[i].CTS_48kHz; - N = r600_hdmi_ACR[i].N_48kHz; - r600_hdmi_calc_CTS(clock, &CTS, N, 48000); - WREG32(HDMI0_ACR_48_0 + offset, HDMI0_ACR_CTS_48(CTS)); - WREG32(HDMI0_ACR_48_1 + offset, N); + WREG32(HDMI0_ACR_48_0 + offset, HDMI0_ACR_CTS_48(acr.cts_48khz)); + WREG32(HDMI0_ACR_48_1 + offset, acr.n_48khz); } /* @@ -166,7 +161,9 @@ static void r600_hdmi_videoinfoframe( { struct drm_device *dev = encoder->dev; struct radeon_device *rdev = dev->dev_private; - uint32_t offset = to_radeon_encoder(encoder)->hdmi_offset; + struct radeon_encoder *radeon_encoder = to_radeon_encoder(encoder); + struct radeon_encoder_atom_dig *dig = radeon_encoder->enc_priv; + uint32_t offset = dig->afmt->offset; uint8_t frame[14]; @@ -232,7 +229,9 @@ static void r600_hdmi_audioinfoframe( { struct drm_device *dev = encoder->dev; struct radeon_device *rdev = dev->dev_private; - uint32_t offset = to_radeon_encoder(encoder)->hdmi_offset; + struct radeon_encoder *radeon_encoder = to_radeon_encoder(encoder); + struct radeon_encoder_atom_dig *dig = radeon_encoder->enc_priv; + uint32_t offset = dig->afmt->offset; uint8_t frame[11]; @@ -259,11 +258,13 @@ static void r600_hdmi_audioinfoframe( /* * test if audio buffer is filled enough to start playing */ -static int r600_hdmi_is_audio_buffer_filled(struct drm_encoder *encoder) +static bool r600_hdmi_is_audio_buffer_filled(struct drm_encoder *encoder) { struct drm_device *dev = encoder->dev; struct radeon_device *rdev = dev->dev_private; - uint32_t offset = to_radeon_encoder(encoder)->hdmi_offset; + struct radeon_encoder *radeon_encoder = to_radeon_encoder(encoder); + struct radeon_encoder_atom_dig *dig = radeon_encoder->enc_priv; + uint32_t offset = dig->afmt->offset; return (RREG32(HDMI0_STATUS + offset) & 0x10) != 0; } @@ -274,14 +275,15 @@ static int r600_hdmi_is_audio_buffer_filled(struct drm_encoder *encoder) int r600_hdmi_buffer_status_changed(struct drm_encoder *encoder) { struct radeon_encoder *radeon_encoder = to_radeon_encoder(encoder); + struct radeon_encoder_atom_dig *dig = radeon_encoder->enc_priv; int status, result; - if (!radeon_encoder->hdmi_enabled) + if (!dig->afmt || !dig->afmt->enabled) return 0; status = r600_hdmi_is_audio_buffer_filled(encoder); - result = radeon_encoder->hdmi_buffer_status != status; - radeon_encoder->hdmi_buffer_status = status; + result = dig->afmt->last_buffer_filled_status != status; + dig->afmt->last_buffer_filled_status = status; return result; } @@ -289,26 +291,23 @@ int r600_hdmi_buffer_status_changed(struct drm_encoder *encoder) /* * write the audio workaround status to the hardware */ -void r600_hdmi_audio_workaround(struct drm_encoder *encoder) +static void r600_hdmi_audio_workaround(struct drm_encoder *encoder) { struct drm_device *dev = encoder->dev; struct radeon_device *rdev = dev->dev_private; struct radeon_encoder *radeon_encoder = to_radeon_encoder(encoder); - uint32_t offset = radeon_encoder->hdmi_offset; - - if (!radeon_encoder->hdmi_enabled) - return; - - if (!radeon_encoder->hdmi_audio_workaround || - r600_hdmi_is_audio_buffer_filled(encoder)) { - - /* disable audio workaround */ - WREG32_P(HDMI0_AUDIO_PACKET_CONTROL + offset, 0x0001, ~0x1001); - - } else { - /* enable audio workaround */ - WREG32_P(HDMI0_AUDIO_PACKET_CONTROL + offset, 0x1001, ~0x1001); - } + struct radeon_encoder_atom_dig *dig = radeon_encoder->enc_priv; + uint32_t offset = dig->afmt->offset; + bool hdmi_audio_workaround = false; /* FIXME */ + u32 value; + + if (!hdmi_audio_workaround || + r600_hdmi_is_audio_buffer_filled(encoder)) + value = 0; /* disable workaround */ + else + value = HDMI0_AUDIO_TEST_EN; /* enable workaround */ + WREG32_P(HDMI0_AUDIO_PACKET_CONTROL + offset, + value, ~HDMI0_AUDIO_TEST_EN); } @@ -319,29 +318,68 @@ void r600_hdmi_setmode(struct drm_encoder *encoder, struct drm_display_mode *mod { struct drm_device *dev = encoder->dev; struct radeon_device *rdev = dev->dev_private; - uint32_t offset = to_radeon_encoder(encoder)->hdmi_offset; + struct radeon_encoder *radeon_encoder = to_radeon_encoder(encoder); + struct radeon_encoder_atom_dig *dig = radeon_encoder->enc_priv; + uint32_t offset; if (ASIC_IS_DCE5(rdev)) return; - if (!to_radeon_encoder(encoder)->hdmi_enabled) + /* Silent, r600_hdmi_enable will raise WARN for us */ + if (!dig->afmt->enabled) return; + offset = dig->afmt->offset; r600_audio_set_clock(encoder, mode->clock); + WREG32(HDMI0_VBI_PACKET_CONTROL + offset, + HDMI0_NULL_SEND); /* send null packets when required */ + WREG32(HDMI0_AUDIO_CRC_CONTROL + offset, 0x1000); - WREG32(HDMI0_GC + offset, 0x0); - WREG32(HDMI0_ACR_PACKET_CONTROL + offset, 0x1000); - r600_hdmi_update_ACR(encoder, mode->clock); + if (ASIC_IS_DCE32(rdev)) { + WREG32(HDMI0_AUDIO_PACKET_CONTROL + offset, + HDMI0_AUDIO_DELAY_EN(1) | /* default audio delay */ + HDMI0_AUDIO_PACKETS_PER_LINE(3)); /* should be suffient for all audio modes and small enough for all hblanks */ + WREG32(AFMT_AUDIO_PACKET_CONTROL + offset, + AFMT_AUDIO_SAMPLE_SEND | /* send audio packets */ + AFMT_60958_CS_UPDATE); /* allow 60958 channel status fields to be updated */ + } else { + WREG32(HDMI0_AUDIO_PACKET_CONTROL + offset, + HDMI0_AUDIO_SAMPLE_SEND | /* send audio packets */ + HDMI0_AUDIO_DELAY_EN(1) | /* default audio delay */ + HDMI0_AUDIO_SEND_MAX_PACKETS | /* send NULL packets if no audio is available */ + HDMI0_AUDIO_PACKETS_PER_LINE(3) | /* should be suffient for all audio modes and small enough for all hblanks */ + HDMI0_60958_CS_UPDATE); /* allow 60958 channel status fields to be updated */ + } + + WREG32(HDMI0_ACR_PACKET_CONTROL + offset, + HDMI0_ACR_AUTO_SEND | /* allow hw to sent ACR packets when required */ + HDMI0_ACR_SOURCE); /* select SW CTS value */ - WREG32(HDMI0_INFOFRAME_CONTROL0 + offset, 0x13); + WREG32(HDMI0_VBI_PACKET_CONTROL + offset, + HDMI0_NULL_SEND | /* send null packets when required */ + HDMI0_GC_SEND | /* send general control packets */ + HDMI0_GC_CONT); /* send general control packets every frame */ - WREG32(HDMI0_INFOFRAME_CONTROL1 + offset, 0x202); + /* TODO: HDMI0_AUDIO_INFO_UPDATE */ + WREG32(HDMI0_INFOFRAME_CONTROL0 + offset, + HDMI0_AVI_INFO_SEND | /* enable AVI info frames */ + HDMI0_AVI_INFO_CONT | /* send AVI info frames every frame/field */ + HDMI0_AUDIO_INFO_SEND | /* enable audio info frames (frames won't be set until audio is enabled) */ + HDMI0_AUDIO_INFO_CONT); /* send audio info frames every frame/field */ + + WREG32(HDMI0_INFOFRAME_CONTROL1 + offset, + HDMI0_AVI_INFO_LINE(2) | /* anything other than 0 */ + HDMI0_AUDIO_INFO_LINE(2)); /* anything other than 0 */ + + WREG32(HDMI0_GC + offset, 0); /* unset HDMI0_GC_AVMUTE */ r600_hdmi_videoinfoframe(encoder, RGB, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0); + r600_hdmi_update_ACR(encoder, mode->clock); + /* it's unknown what these bits do excatly, but it's indeed quite useful for debugging */ WREG32(HDMI0_RAMP_CONTROL0 + offset, 0x00FFFFFF); WREG32(HDMI0_RAMP_CONTROL1 + offset, 0x007FFFFF); @@ -349,9 +387,6 @@ void r600_hdmi_setmode(struct drm_encoder *encoder, struct drm_display_mode *mod WREG32(HDMI0_RAMP_CONTROL3 + offset, 0x00000001); r600_hdmi_audio_workaround(encoder); - - /* audio packets per line, does anyone know how to calc this ? */ - WREG32_P(HDMI0_AUDIO_PACKET_CONTROL + offset, 0x00040000, ~0x001F0000); } /* @@ -361,115 +396,82 @@ void r600_hdmi_update_audio_settings(struct drm_encoder *encoder) { struct drm_device *dev = encoder->dev; struct radeon_device *rdev = dev->dev_private; - uint32_t offset = to_radeon_encoder(encoder)->hdmi_offset; - - int channels = r600_audio_channels(rdev); - int rate = r600_audio_rate(rdev); - int bps = r600_audio_bits_per_sample(rdev); - uint8_t status_bits = r600_audio_status_bits(rdev); - uint8_t category_code = r600_audio_category_code(rdev); - + struct radeon_encoder *radeon_encoder = to_radeon_encoder(encoder); + struct radeon_encoder_atom_dig *dig = radeon_encoder->enc_priv; + struct r600_audio audio = r600_audio_status(rdev); + uint32_t offset; uint32_t iec; - if (!to_radeon_encoder(encoder)->hdmi_enabled) + if (!dig->afmt || !dig->afmt->enabled) return; + offset = dig->afmt->offset; DRM_DEBUG("%s with %d channels, %d Hz sampling rate, %d bits per sample,\n", r600_hdmi_is_audio_buffer_filled(encoder) ? "playing" : "stopped", - channels, rate, bps); + audio.channels, audio.rate, audio.bits_per_sample); DRM_DEBUG("0x%02X IEC60958 status bits and 0x%02X category code\n", - (int)status_bits, (int)category_code); + (int)audio.status_bits, (int)audio.category_code); iec = 0; - if (status_bits & AUDIO_STATUS_PROFESSIONAL) + if (audio.status_bits & AUDIO_STATUS_PROFESSIONAL) iec |= 1 << 0; - if (status_bits & AUDIO_STATUS_NONAUDIO) + if (audio.status_bits & AUDIO_STATUS_NONAUDIO) iec |= 1 << 1; - if (status_bits & AUDIO_STATUS_COPYRIGHT) + if (audio.status_bits & AUDIO_STATUS_COPYRIGHT) iec |= 1 << 2; - if (status_bits & AUDIO_STATUS_EMPHASIS) + if (audio.status_bits & AUDIO_STATUS_EMPHASIS) iec |= 1 << 3; - iec |= category_code << 8; - - switch (rate) { - case 32000: iec |= 0x3 << 24; break; - case 44100: iec |= 0x0 << 24; break; - case 88200: iec |= 0x8 << 24; break; - case 176400: iec |= 0xc << 24; break; - case 48000: iec |= 0x2 << 24; break; - case 96000: iec |= 0xa << 24; break; - case 192000: iec |= 0xe << 24; break; + iec |= HDMI0_60958_CS_CATEGORY_CODE(audio.category_code); + + switch (audio.rate) { + case 32000: + iec |= HDMI0_60958_CS_SAMPLING_FREQUENCY(0x3); + break; + case 44100: + iec |= HDMI0_60958_CS_SAMPLING_FREQUENCY(0x0); + break; + case 48000: + iec |= HDMI0_60958_CS_SAMPLING_FREQUENCY(0x2); + break; + case 88200: + iec |= HDMI0_60958_CS_SAMPLING_FREQUENCY(0x8); + break; + case 96000: + iec |= HDMI0_60958_CS_SAMPLING_FREQUENCY(0xa); + break; + case 176400: + iec |= HDMI0_60958_CS_SAMPLING_FREQUENCY(0xc); + break; + case 192000: + iec |= HDMI0_60958_CS_SAMPLING_FREQUENCY(0xe); + break; } WREG32(HDMI0_60958_0 + offset, iec); iec = 0; - switch (bps) { - case 16: iec |= 0x2; break; - case 20: iec |= 0x3; break; - case 24: iec |= 0xb; break; + switch (audio.bits_per_sample) { + case 16: + iec |= HDMI0_60958_CS_WORD_LENGTH(0x2); + break; + case 20: + iec |= HDMI0_60958_CS_WORD_LENGTH(0x3); + break; + case 24: + iec |= HDMI0_60958_CS_WORD_LENGTH(0xb); + break; } - if (status_bits & AUDIO_STATUS_V) + if (audio.status_bits & AUDIO_STATUS_V) iec |= 0x5 << 16; - WREG32_P(HDMI0_60958_1 + offset, iec, ~0x5000f); - /* 0x021 or 0x031 sets the audio frame length */ - WREG32(HDMI0_VBI_PACKET_CONTROL + offset, 0x31); - r600_hdmi_audioinfoframe(encoder, channels-1, 0, 0, 0, 0, 0, 0, 0); + r600_hdmi_audioinfoframe(encoder, audio.channels - 1, 0, 0, 0, 0, 0, 0, + 0); r600_hdmi_audio_workaround(encoder); } -static void r600_hdmi_assign_block(struct drm_encoder *encoder) -{ - struct drm_device *dev = encoder->dev; - struct radeon_device *rdev = dev->dev_private; - struct radeon_encoder *radeon_encoder = to_radeon_encoder(encoder); - struct radeon_encoder_atom_dig *dig = radeon_encoder->enc_priv; - - u16 eg_offsets[] = { - EVERGREEN_CRTC0_REGISTER_OFFSET, - EVERGREEN_CRTC1_REGISTER_OFFSET, - EVERGREEN_CRTC2_REGISTER_OFFSET, - EVERGREEN_CRTC3_REGISTER_OFFSET, - EVERGREEN_CRTC4_REGISTER_OFFSET, - EVERGREEN_CRTC5_REGISTER_OFFSET, - }; - - if (!dig) { - dev_err(rdev->dev, "Enabling HDMI on non-dig encoder\n"); - return; - } - - if (ASIC_IS_DCE5(rdev)) { - /* TODO */ - } else if (ASIC_IS_DCE4(rdev)) { - if (dig->dig_encoder >= ARRAY_SIZE(eg_offsets)) { - dev_err(rdev->dev, "Enabling HDMI on unknown dig\n"); - return; - } - radeon_encoder->hdmi_offset = eg_offsets[dig->dig_encoder]; - /* Temp hack for Evergreen until we split r600_hdmi.c - * Evergreen first block is 0x7030 instead of 0x7400. - */ - radeon_encoder->hdmi_offset -= 0x3d0; - } else if (ASIC_IS_DCE3(rdev)) { - radeon_encoder->hdmi_offset = dig->dig_encoder ? - DCE3_HDMI_OFFSET1 : DCE3_HDMI_OFFSET0; - } else if (rdev->family >= CHIP_R600) { - /* 2 routable blocks, but using dig_encoder should be fine */ - radeon_encoder->hdmi_offset = dig->dig_encoder ? - DCE2_HDMI_OFFSET1 : DCE2_HDMI_OFFSET0; - } else if (rdev->family == CHIP_RS600 || rdev->family == CHIP_RS690 || - rdev->family == CHIP_RS740) { - /* Only 1 routable block */ - radeon_encoder->hdmi_offset = DCE2_HDMI_OFFSET0; - } - radeon_encoder->hdmi_enabled = true; -} - /* * enable the HDMI engine */ @@ -478,55 +480,57 @@ void r600_hdmi_enable(struct drm_encoder *encoder) struct drm_device *dev = encoder->dev; struct radeon_device *rdev = dev->dev_private; struct radeon_encoder *radeon_encoder = to_radeon_encoder(encoder); + struct radeon_encoder_atom_dig *dig = radeon_encoder->enc_priv; uint32_t offset; + u32 hdmi; if (ASIC_IS_DCE5(rdev)) return; - if (!radeon_encoder->hdmi_enabled) { - r600_hdmi_assign_block(encoder); - if (!radeon_encoder->hdmi_enabled) { - dev_warn(rdev->dev, "Could not find HDMI block for " - "0x%x encoder\n", radeon_encoder->encoder_id); - return; - } - } + /* Silent, r600_hdmi_enable will raise WARN for us */ + if (dig->afmt->enabled) + return; + offset = dig->afmt->offset; - offset = radeon_encoder->hdmi_offset; - if (ASIC_IS_DCE5(rdev)) { - /* TODO */ - } else if (ASIC_IS_DCE4(rdev)) { - WREG32_P(0x74fc + radeon_encoder->hdmi_offset, 0x1, ~0x1); - } else if (ASIC_IS_DCE32(rdev)) { - WREG32_P(AFMT_AUDIO_PACKET_CONTROL + radeon_encoder->hdmi_offset, 0x1, ~0x1); - } else if (ASIC_IS_DCE3(rdev)) { - /* TODO */ - } else if (rdev->family >= CHIP_R600) { + /* Older chipsets require setting HDMI and routing manually */ + if (rdev->family >= CHIP_R600 && !ASIC_IS_DCE3(rdev)) { + hdmi = HDMI0_ERROR_ACK | HDMI0_ENABLE; switch (radeon_encoder->encoder_id) { case ENCODER_OBJECT_ID_INTERNAL_KLDSCP_TMDS1: WREG32_P(AVIVO_TMDSA_CNTL, AVIVO_TMDSA_CNTL_HDMI_EN, ~AVIVO_TMDSA_CNTL_HDMI_EN); - WREG32(HDMI0_CONTROL + offset, 0x101); + hdmi |= HDMI0_STREAM(HDMI0_STREAM_TMDSA); break; case ENCODER_OBJECT_ID_INTERNAL_LVTM1: WREG32_P(AVIVO_LVTMA_CNTL, AVIVO_LVTMA_CNTL_HDMI_EN, ~AVIVO_LVTMA_CNTL_HDMI_EN); - WREG32(HDMI0_CONTROL + offset, 0x105); + hdmi |= HDMI0_STREAM(HDMI0_STREAM_LVTMA); + break; + case ENCODER_OBJECT_ID_INTERNAL_DDI: + WREG32_P(DDIA_CNTL, DDIA_HDMI_EN, ~DDIA_HDMI_EN); + hdmi |= HDMI0_STREAM(HDMI0_STREAM_DDIA); + break; + case ENCODER_OBJECT_ID_INTERNAL_KLDSCP_DVO1: + hdmi |= HDMI0_STREAM(HDMI0_STREAM_DVOA); break; default: - dev_err(rdev->dev, "Unknown HDMI output type\n"); + dev_err(rdev->dev, "Invalid encoder for HDMI: 0x%X\n", + radeon_encoder->encoder_id); break; } + WREG32(HDMI0_CONTROL + offset, hdmi); } if (rdev->irq.installed) { /* if irq is available use it */ - rdev->irq.afmt[offset == 0 ? 0 : 1] = true; + rdev->irq.afmt[dig->afmt->id] = true; radeon_irq_set(rdev); } + dig->afmt->enabled = true; + DRM_DEBUG("Enabling HDMI interface @ 0x%04X for encoder 0x%x\n", - radeon_encoder->hdmi_offset, radeon_encoder->encoder_id); + offset, radeon_encoder->encoder_id); } /* @@ -537,49 +541,51 @@ void r600_hdmi_disable(struct drm_encoder *encoder) struct drm_device *dev = encoder->dev; struct radeon_device *rdev = dev->dev_private; struct radeon_encoder *radeon_encoder = to_radeon_encoder(encoder); + struct radeon_encoder_atom_dig *dig = radeon_encoder->enc_priv; uint32_t offset; if (ASIC_IS_DCE5(rdev)) return; - offset = radeon_encoder->hdmi_offset; - if (!radeon_encoder->hdmi_enabled) { - dev_err(rdev->dev, "Disabling not enabled HDMI\n"); + /* Called for ATOM_ENCODER_MODE_HDMI only */ + if (!dig || !dig->afmt) { + WARN_ON(1); return; } + if (!dig->afmt->enabled) + return; + offset = dig->afmt->offset; DRM_DEBUG("Disabling HDMI interface @ 0x%04X for encoder 0x%x\n", - offset, radeon_encoder->encoder_id); + offset, radeon_encoder->encoder_id); /* disable irq */ - rdev->irq.afmt[offset == 0 ? 0 : 1] = false; + rdev->irq.afmt[dig->afmt->id] = false; radeon_irq_set(rdev); - - if (ASIC_IS_DCE5(rdev)) { - /* TODO */ - } else if (ASIC_IS_DCE4(rdev)) { - WREG32_P(0x74fc + radeon_encoder->hdmi_offset, 0, ~0x1); - } else if (ASIC_IS_DCE32(rdev)) { - WREG32_P(AFMT_AUDIO_PACKET_CONTROL + radeon_encoder->hdmi_offset, 0, ~0x1); - } else if (rdev->family >= CHIP_R600 && !ASIC_IS_DCE3(rdev)) { + /* Older chipsets not handled by AtomBIOS */ + if (rdev->family >= CHIP_R600 && !ASIC_IS_DCE3(rdev)) { switch (radeon_encoder->encoder_id) { case ENCODER_OBJECT_ID_INTERNAL_KLDSCP_TMDS1: WREG32_P(AVIVO_TMDSA_CNTL, 0, ~AVIVO_TMDSA_CNTL_HDMI_EN); - WREG32(HDMI0_CONTROL + offset, 0); break; case ENCODER_OBJECT_ID_INTERNAL_LVTM1: WREG32_P(AVIVO_LVTMA_CNTL, 0, ~AVIVO_LVTMA_CNTL_HDMI_EN); - WREG32(HDMI0_CONTROL + offset, 0); + break; + case ENCODER_OBJECT_ID_INTERNAL_DDI: + WREG32_P(DDIA_CNTL, 0, ~DDIA_HDMI_EN); + break; + case ENCODER_OBJECT_ID_INTERNAL_KLDSCP_DVO1: break; default: - dev_err(rdev->dev, "Unknown HDMI output type\n"); + dev_err(rdev->dev, "Invalid encoder for HDMI: 0x%X\n", + radeon_encoder->encoder_id); break; } + WREG32(HDMI0_CONTROL + offset, HDMI0_ERROR_ACK); } - radeon_encoder->hdmi_enabled = false; - radeon_encoder->hdmi_offset = 0; + dig->afmt->enabled = false; } diff --git a/drivers/gpu/drm/radeon/r600_reg.h b/drivers/gpu/drm/radeon/r600_reg.h index c44304a..2b960cb 100644 --- a/drivers/gpu/drm/radeon/r600_reg.h +++ b/drivers/gpu/drm/radeon/r600_reg.h @@ -156,4 +156,10 @@ #define R600_AUDIO_PIN_WIDGET_CNTL 0x73d4 #define R600_AUDIO_STATUS_BITS 0x73d8 +#define DCE2_HDMI_OFFSET0 (0x7400 - 0x7400) +#define DCE2_HDMI_OFFSET1 (0x7700 - 0x7400) +/* DCE3.2 second instance starts at 0x7800 */ +#define DCE3_HDMI_OFFSET0 (0x7400 - 0x7400) +#define DCE3_HDMI_OFFSET1 (0x7800 - 0x7400) + #endif diff --git a/drivers/gpu/drm/radeon/r600d.h b/drivers/gpu/drm/radeon/r600d.h index a9652be..15bd3b2 100644 --- a/drivers/gpu/drm/radeon/r600d.h +++ b/drivers/gpu/drm/radeon/r600d.h @@ -1057,12 +1057,6 @@ # define AFMT_AZ_FORMAT_WTRIG_ACK (1 << 29) # define AFMT_AZ_AUDIO_ENABLE_CHG_ACK (1 << 30) -#define DCE2_HDMI_OFFSET0 (0x7400 - 0x7400) -#define DCE2_HDMI_OFFSET1 (0x7700 - 0x7400) -/* DCE3.2 second instance starts at 0x7800 */ -#define DCE3_HDMI_OFFSET0 (0x7400 - 0x7400) -#define DCE3_HDMI_OFFSET1 (0x7800 - 0x7400) - /* * PM4 */ diff --git a/drivers/gpu/drm/radeon/radeon.h b/drivers/gpu/drm/radeon/radeon.h index 82ffa6a..1dc3a4a 100644 --- a/drivers/gpu/drm/radeon/radeon.h +++ b/drivers/gpu/drm/radeon/radeon.h @@ -100,28 +100,32 @@ extern int radeon_lockup_timeout; * Copy from radeon_drv.h so we don't have to include both and have conflicting * symbol; */ -#define RADEON_MAX_USEC_TIMEOUT 100000 /* 100 ms */ -#define RADEON_FENCE_JIFFIES_TIMEOUT (HZ / 2) +#define RADEON_MAX_USEC_TIMEOUT 100000 /* 100 ms */ +#define RADEON_FENCE_JIFFIES_TIMEOUT (HZ / 2) /* RADEON_IB_POOL_SIZE must be a power of 2 */ -#define RADEON_IB_POOL_SIZE 16 -#define RADEON_DEBUGFS_MAX_COMPONENTS 32 -#define RADEONFB_CONN_LIMIT 4 -#define RADEON_BIOS_NUM_SCRATCH 8 +#define RADEON_IB_POOL_SIZE 16 +#define RADEON_DEBUGFS_MAX_COMPONENTS 32 +#define RADEONFB_CONN_LIMIT 4 +#define RADEON_BIOS_NUM_SCRATCH 8 /* max number of rings */ -#define RADEON_NUM_RINGS 3 +#define RADEON_NUM_RINGS 3 + +/* fence seq are set to this number when signaled */ +#define RADEON_FENCE_SIGNALED_SEQ 0LL +#define RADEON_FENCE_NOTEMITED_SEQ (~0LL) /* internal ring indices */ /* r1xx+ has gfx CP ring */ -#define RADEON_RING_TYPE_GFX_INDEX 0 +#define RADEON_RING_TYPE_GFX_INDEX 0 /* cayman has 2 compute CP rings */ -#define CAYMAN_RING_TYPE_CP1_INDEX 1 -#define CAYMAN_RING_TYPE_CP2_INDEX 2 +#define CAYMAN_RING_TYPE_CP1_INDEX 1 +#define CAYMAN_RING_TYPE_CP2_INDEX 2 /* hardcode those limit for now */ -#define RADEON_VA_RESERVED_SIZE (8 << 20) -#define RADEON_IB_VM_MAX_SIZE (64 << 10) +#define RADEON_VA_RESERVED_SIZE (8 << 20) +#define RADEON_IB_VM_MAX_SIZE (64 << 10) /* * Errata workarounds. @@ -254,26 +258,20 @@ struct radeon_fence_driver { uint32_t scratch_reg; uint64_t gpu_addr; volatile uint32_t *cpu_addr; - atomic_t seq; - uint32_t last_seq; + /* seq is protected by ring emission lock */ + uint64_t seq; + atomic64_t last_seq; unsigned long last_activity; - wait_queue_head_t queue; - struct list_head emitted; - struct list_head signaled; bool initialized; }; struct radeon_fence { struct radeon_device *rdev; struct kref kref; - struct list_head list; /* protected by radeon_fence.lock */ - uint32_t seq; - bool emitted; - bool signaled; + uint64_t seq; /* RB, DMA, etc. */ - int ring; - struct radeon_semaphore *semaphore; + unsigned ring; }; int radeon_fence_driver_start_ring(struct radeon_device *rdev, int ring); @@ -284,11 +282,14 @@ int radeon_fence_emit(struct radeon_device *rdev, struct radeon_fence *fence); void radeon_fence_process(struct radeon_device *rdev, int ring); bool radeon_fence_signaled(struct radeon_fence *fence); int radeon_fence_wait(struct radeon_fence *fence, bool interruptible); -int radeon_fence_wait_next(struct radeon_device *rdev, int ring); -int radeon_fence_wait_empty(struct radeon_device *rdev, int ring); +int radeon_fence_wait_next_locked(struct radeon_device *rdev, int ring); +int radeon_fence_wait_empty_locked(struct radeon_device *rdev, int ring); +int radeon_fence_wait_any(struct radeon_device *rdev, + struct radeon_fence **fences, + bool intr); struct radeon_fence *radeon_fence_ref(struct radeon_fence *fence); void radeon_fence_unref(struct radeon_fence **fence); -int radeon_fence_count_emitted(struct radeon_device *rdev, int ring); +unsigned radeon_fence_count_emitted(struct radeon_device *rdev, int ring); /* * Tiling registers @@ -381,8 +382,11 @@ struct radeon_bo_list { * alignment). */ struct radeon_sa_manager { + spinlock_t lock; struct radeon_bo *bo; - struct list_head sa_bo; + struct list_head *hole; + struct list_head flist[RADEON_NUM_RINGS]; + struct list_head olist; unsigned size; uint64_t gpu_addr; void *cpu_ptr; @@ -393,10 +397,12 @@ struct radeon_sa_bo; /* sub-allocation buffer */ struct radeon_sa_bo { - struct list_head list; + struct list_head olist; + struct list_head flist; struct radeon_sa_manager *manager; - unsigned offset; - unsigned size; + unsigned soffset; + unsigned eoffset; + struct radeon_fence *fence; }; /* @@ -427,34 +433,13 @@ int radeon_mode_dumb_destroy(struct drm_file *file_priv, /* * Semaphores. */ -struct radeon_ring; - -#define RADEON_SEMAPHORE_BO_SIZE 256 - -struct radeon_semaphore_driver { - rwlock_t lock; - struct list_head bo; -}; - -struct radeon_semaphore_bo; - /* everything here is constant */ struct radeon_semaphore { - struct list_head list; + struct radeon_sa_bo *sa_bo; + signed waiters; uint64_t gpu_addr; - uint32_t *cpu_ptr; - struct radeon_semaphore_bo *bo; }; -struct radeon_semaphore_bo { - struct list_head list; - struct radeon_ib *ib; - struct list_head free; - struct radeon_semaphore semaphores[RADEON_SEMAPHORE_BO_SIZE/8]; - unsigned nused; -}; - -void radeon_semaphore_driver_fini(struct radeon_device *rdev); int radeon_semaphore_create(struct radeon_device *rdev, struct radeon_semaphore **semaphore); void radeon_semaphore_emit_signal(struct radeon_device *rdev, int ring, @@ -466,7 +451,8 @@ int radeon_semaphore_sync_rings(struct radeon_device *rdev, bool sync_to[RADEON_NUM_RINGS], int dst_ring); void radeon_semaphore_free(struct radeon_device *rdev, - struct radeon_semaphore *semaphore); + struct radeon_semaphore *semaphore, + struct radeon_fence *fence); /* * GART structures, functions & helpers @@ -637,26 +623,14 @@ void radeon_irq_kms_pflip_irq_put(struct radeon_device *rdev, int crtc); */ struct radeon_ib { - struct radeon_sa_bo sa_bo; - unsigned idx; - uint32_t length_dw; - uint64_t gpu_addr; - uint32_t *ptr; - struct radeon_fence *fence; - unsigned vm_id; - bool is_const_ib; -}; - -/* - * locking - - * mutex protects scheduled_ibs, ready, alloc_bm - */ -struct radeon_ib_pool { - struct radeon_mutex mutex; - struct radeon_sa_manager sa_manager; - struct radeon_ib ibs[RADEON_IB_POOL_SIZE]; - bool ready; - unsigned head_id; + struct radeon_sa_bo *sa_bo; + uint32_t length_dw; + uint64_t gpu_addr; + uint32_t *ptr; + struct radeon_fence *fence; + unsigned vm_id; + bool is_const_ib; + struct radeon_semaphore *semaphore; }; struct radeon_ring { @@ -676,7 +650,6 @@ struct radeon_ring { uint64_t gpu_addr; uint32_t align_mask; uint32_t ptr_mask; - struct mutex mutex; bool ready; u32 ptr_reg_shift; u32 ptr_reg_mask; @@ -693,7 +666,7 @@ struct radeon_vm { unsigned last_pfn; u64 pt_gpu_addr; u64 *pt; - struct radeon_sa_bo sa_bo; + struct radeon_sa_bo *sa_bo; struct mutex mutex; /* last fence for cs using this vm */ struct radeon_fence *fence; @@ -770,7 +743,6 @@ struct r600_blit_cp_primitives { }; struct r600_blit { - struct mutex mutex; struct radeon_bo *shader_obj; struct r600_blit_cp_primitives primitives; int max_dim; @@ -780,8 +752,6 @@ struct r600_blit { u32 vs_offset, ps_offset; u32 state_offset; u32 state_len; - u32 vb_used, vb_total; - struct radeon_ib *vb_ib; }; void r600_blit_suspend(struct radeon_device *rdev); @@ -799,9 +769,8 @@ struct si_rlc { }; int radeon_ib_get(struct radeon_device *rdev, int ring, - struct radeon_ib **ib, unsigned size); -void radeon_ib_free(struct radeon_device *rdev, struct radeon_ib **ib); -bool radeon_ib_try_free(struct radeon_device *rdev, struct radeon_ib *ib); + struct radeon_ib *ib, unsigned size); +void radeon_ib_free(struct radeon_device *rdev, struct radeon_ib *ib); int radeon_ib_schedule(struct radeon_device *rdev, struct radeon_ib *ib); int radeon_ib_pool_init(struct radeon_device *rdev); void radeon_ib_pool_fini(struct radeon_device *rdev); @@ -815,6 +784,7 @@ int radeon_ring_alloc(struct radeon_device *rdev, struct radeon_ring *cp, unsign int radeon_ring_lock(struct radeon_device *rdev, struct radeon_ring *cp, unsigned ndw); void radeon_ring_commit(struct radeon_device *rdev, struct radeon_ring *cp); void radeon_ring_unlock_commit(struct radeon_device *rdev, struct radeon_ring *cp); +void radeon_ring_undo(struct radeon_ring *ring); void radeon_ring_unlock_undo(struct radeon_device *rdev, struct radeon_ring *cp); int radeon_ring_test(struct radeon_device *rdev, struct radeon_ring *cp); void radeon_ring_force_activity(struct radeon_device *rdev, struct radeon_ring *ring); @@ -868,8 +838,8 @@ struct radeon_cs_parser { int chunk_relocs_idx; int chunk_flags_idx; int chunk_const_ib_idx; - struct radeon_ib *ib; - struct radeon_ib *const_ib; + struct radeon_ib ib; + struct radeon_ib const_ib; void *track; unsigned family; int parser_error; @@ -1124,7 +1094,6 @@ int radeon_pm_get_type_index(struct radeon_device *rdev, int instance); struct r600_audio { - bool enabled; int channels; int rate; int bits_per_sample; @@ -1531,11 +1500,12 @@ struct radeon_device { struct radeon_mode_info mode_info; struct radeon_scratch scratch; struct radeon_mman mman; - rwlock_t fence_lock; struct radeon_fence_driver fence_drv[RADEON_NUM_RINGS]; - struct radeon_semaphore_driver semaphore_drv; + wait_queue_head_t fence_queue; + struct mutex ring_lock; struct radeon_ring ring[RADEON_NUM_RINGS]; - struct radeon_ib_pool ib_pool; + bool ib_pool_ready; + struct radeon_sa_manager ring_tmp_bo; struct radeon_irq irq; struct radeon_asic *asic; struct radeon_gem gem; @@ -1564,7 +1534,8 @@ struct radeon_device { int num_crtc; /* number of crtcs */ struct mutex dc_hw_i2c_mutex; /* display controller hw i2c mutex */ struct mutex vram_mutex; - struct r600_audio audio; /* audio stuff */ + bool audio_enabled; + struct r600_audio audio_status; /* audio stuff */ struct notifier_block acpi_nb; /* only one userspace can use Hyperz features or CMASK at a time */ struct drm_file *hyperz_filp; @@ -1856,10 +1827,32 @@ int r600_fmt_get_nblocksy(u32 format, u32 h); /* * r600 functions used by radeon_encoder.c */ +struct radeon_hdmi_acr { + u32 clock; + + int n_32khz; + int cts_32khz; + + int n_44_1khz; + int cts_44_1khz; + + int n_48khz; + int cts_48khz; + +}; + +extern struct radeon_hdmi_acr r600_hdmi_acr(uint32_t clock); + extern void r600_hdmi_enable(struct drm_encoder *encoder); extern void r600_hdmi_disable(struct drm_encoder *encoder); extern void r600_hdmi_setmode(struct drm_encoder *encoder, struct drm_display_mode *mode); +/* + * evergreen functions used by radeon_encoder.c + */ + +extern void evergreen_hdmi_setmode(struct drm_encoder *encoder, struct drm_display_mode *mode); + extern int ni_init_microcode(struct radeon_device *rdev); extern int ni_mc_load_microcode(struct radeon_device *rdev); diff --git a/drivers/gpu/drm/radeon/radeon_asic.h b/drivers/gpu/drm/radeon/radeon_asic.h index 7830931..e76a941 100644 --- a/drivers/gpu/drm/radeon/radeon_asic.h +++ b/drivers/gpu/drm/radeon/radeon_asic.h @@ -356,23 +356,20 @@ void r600_disable_interrupts(struct radeon_device *rdev); void r600_rlc_stop(struct radeon_device *rdev); /* r600 audio */ int r600_audio_init(struct radeon_device *rdev); -int r600_audio_tmds_index(struct drm_encoder *encoder); void r600_audio_set_clock(struct drm_encoder *encoder, int clock); -int r600_audio_channels(struct radeon_device *rdev); -int r600_audio_bits_per_sample(struct radeon_device *rdev); -int r600_audio_rate(struct radeon_device *rdev); -uint8_t r600_audio_status_bits(struct radeon_device *rdev); -uint8_t r600_audio_category_code(struct radeon_device *rdev); +struct r600_audio r600_audio_status(struct radeon_device *rdev); void r600_audio_fini(struct radeon_device *rdev); -void r600_hdmi_init(struct drm_encoder *encoder); int r600_hdmi_buffer_status_changed(struct drm_encoder *encoder); void r600_hdmi_update_audio_settings(struct drm_encoder *encoder); /* r600 blit */ -int r600_blit_prepare_copy(struct radeon_device *rdev, unsigned num_gpu_pages); -void r600_blit_done_copy(struct radeon_device *rdev, struct radeon_fence *fence); +int r600_blit_prepare_copy(struct radeon_device *rdev, unsigned num_gpu_pages, + struct radeon_sa_bo **vb); +void r600_blit_done_copy(struct radeon_device *rdev, struct radeon_fence *fence, + struct radeon_sa_bo *vb); void r600_kms_blit_copy(struct radeon_device *rdev, u64 src_gpu_addr, u64 dst_gpu_addr, - unsigned num_gpu_pages); + unsigned num_gpu_pages, + struct radeon_sa_bo *vb); int r600_mc_wait_for_idle(struct radeon_device *rdev); /* diff --git a/drivers/gpu/drm/radeon/radeon_cs.c b/drivers/gpu/drm/radeon/radeon_cs.c index c66beb1..c7d64a7 100644 --- a/drivers/gpu/drm/radeon/radeon_cs.c +++ b/drivers/gpu/drm/radeon/radeon_cs.c @@ -122,15 +122,15 @@ static int radeon_cs_sync_rings(struct radeon_cs_parser *p) int i, r; for (i = 0; i < p->nrelocs; i++) { + struct radeon_fence *fence; + if (!p->relocs[i].robj || !p->relocs[i].robj->tbo.sync_obj) continue; - if (!(p->relocs[i].flags & RADEON_RELOC_DONT_SYNC)) { - struct radeon_fence *fence = p->relocs[i].robj->tbo.sync_obj; - if (fence->ring != p->ring && !radeon_fence_signaled(fence)) { - sync_to_ring[fence->ring] = true; - need_sync = true; - } + fence = p->relocs[i].robj->tbo.sync_obj; + if (fence->ring != p->ring && !radeon_fence_signaled(fence)) { + sync_to_ring[fence->ring] = true; + need_sync = true; } } @@ -138,12 +138,12 @@ static int radeon_cs_sync_rings(struct radeon_cs_parser *p) return 0; } - r = radeon_semaphore_create(p->rdev, &p->ib->fence->semaphore); + r = radeon_semaphore_create(p->rdev, &p->ib.semaphore); if (r) { return r; } - return radeon_semaphore_sync_rings(p->rdev, p->ib->fence->semaphore, + return radeon_semaphore_sync_rings(p->rdev, p->ib.semaphore, sync_to_ring, p->ring); } @@ -161,8 +161,10 @@ int radeon_cs_parser_init(struct radeon_cs_parser *p, void *data) /* get chunks */ INIT_LIST_HEAD(&p->validated); p->idx = 0; - p->ib = NULL; - p->const_ib = NULL; + p->ib.sa_bo = NULL; + p->ib.semaphore = NULL; + p->const_ib.sa_bo = NULL; + p->const_ib.semaphore = NULL; p->chunk_ib_idx = -1; p->chunk_relocs_idx = -1; p->chunk_flags_idx = -1; @@ -301,10 +303,9 @@ static void radeon_cs_parser_fini(struct radeon_cs_parser *parser, int error) { unsigned i; - - if (!error && parser->ib) + if (!error) ttm_eu_fence_buffer_objects(&parser->validated, - parser->ib->fence); + parser->ib.fence); else ttm_eu_backoff_reservation(&parser->validated); @@ -327,9 +328,7 @@ static void radeon_cs_parser_fini(struct radeon_cs_parser *parser, int error) kfree(parser->chunks); kfree(parser->chunks_array); radeon_ib_free(parser->rdev, &parser->ib); - if (parser->const_ib) { - radeon_ib_free(parser->rdev, &parser->const_ib); - } + radeon_ib_free(parser->rdev, &parser->const_ib); } static int radeon_cs_ib_chunk(struct radeon_device *rdev, @@ -355,7 +354,7 @@ static int radeon_cs_ib_chunk(struct radeon_device *rdev, DRM_ERROR("Failed to get ib !\n"); return r; } - parser->ib->length_dw = ib_chunk->length_dw; + parser->ib.length_dw = ib_chunk->length_dw; r = radeon_cs_parse(rdev, parser->ring, parser); if (r || parser->parser_error) { DRM_ERROR("Invalid command stream !\n"); @@ -370,8 +369,8 @@ static int radeon_cs_ib_chunk(struct radeon_device *rdev, if (r) { DRM_ERROR("Failed to synchronize rings !\n"); } - parser->ib->vm_id = 0; - r = radeon_ib_schedule(rdev, parser->ib); + parser->ib.vm_id = 0; + r = radeon_ib_schedule(rdev, &parser->ib); if (r) { DRM_ERROR("Failed to schedule IB !\n"); } @@ -422,14 +421,14 @@ static int radeon_cs_ib_vm_chunk(struct radeon_device *rdev, DRM_ERROR("Failed to get const ib !\n"); return r; } - parser->const_ib->is_const_ib = true; - parser->const_ib->length_dw = ib_chunk->length_dw; + parser->const_ib.is_const_ib = true; + parser->const_ib.length_dw = ib_chunk->length_dw; /* Copy the packet into the IB */ - if (DRM_COPY_FROM_USER(parser->const_ib->ptr, ib_chunk->user_ptr, + if (DRM_COPY_FROM_USER(parser->const_ib.ptr, ib_chunk->user_ptr, ib_chunk->length_dw * 4)) { return -EFAULT; } - r = radeon_ring_ib_parse(rdev, parser->ring, parser->const_ib); + r = radeon_ring_ib_parse(rdev, parser->ring, &parser->const_ib); if (r) { return r; } @@ -446,13 +445,13 @@ static int radeon_cs_ib_vm_chunk(struct radeon_device *rdev, DRM_ERROR("Failed to get ib !\n"); return r; } - parser->ib->length_dw = ib_chunk->length_dw; + parser->ib.length_dw = ib_chunk->length_dw; /* Copy the packet into the IB */ - if (DRM_COPY_FROM_USER(parser->ib->ptr, ib_chunk->user_ptr, + if (DRM_COPY_FROM_USER(parser->ib.ptr, ib_chunk->user_ptr, ib_chunk->length_dw * 4)) { return -EFAULT; } - r = radeon_ring_ib_parse(rdev, parser->ring, parser->ib); + r = radeon_ring_ib_parse(rdev, parser->ring, &parser->ib); if (r) { return r; } @@ -473,29 +472,29 @@ static int radeon_cs_ib_vm_chunk(struct radeon_device *rdev, if ((rdev->family >= CHIP_TAHITI) && (parser->chunk_const_ib_idx != -1)) { - parser->const_ib->vm_id = vm->id; + parser->const_ib.vm_id = vm->id; /* ib pool is bind at 0 in virtual address space to gpu_addr is the * offset inside the pool bo */ - parser->const_ib->gpu_addr = parser->const_ib->sa_bo.offset; - r = radeon_ib_schedule(rdev, parser->const_ib); + parser->const_ib.gpu_addr = parser->const_ib.sa_bo->soffset; + r = radeon_ib_schedule(rdev, &parser->const_ib); if (r) goto out; } - parser->ib->vm_id = vm->id; + parser->ib.vm_id = vm->id; /* ib pool is bind at 0 in virtual address space to gpu_addr is the * offset inside the pool bo */ - parser->ib->gpu_addr = parser->ib->sa_bo.offset; - parser->ib->is_const_ib = false; - r = radeon_ib_schedule(rdev, parser->ib); + parser->ib.gpu_addr = parser->ib.sa_bo->soffset; + parser->ib.is_const_ib = false; + r = radeon_ib_schedule(rdev, &parser->ib); out: if (!r) { if (vm->fence) { radeon_fence_unref(&vm->fence); } - vm->fence = radeon_fence_ref(parser->ib->fence); + vm->fence = radeon_fence_ref(parser->ib.fence); } mutex_unlock(&fpriv->vm.mutex); return r; @@ -573,7 +572,7 @@ int radeon_cs_finish_pages(struct radeon_cs_parser *p) size = PAGE_SIZE; } - if (DRM_COPY_FROM_USER(p->ib->ptr + (i * (PAGE_SIZE/4)), + if (DRM_COPY_FROM_USER(p->ib.ptr + (i * (PAGE_SIZE/4)), ibc->user_ptr + (i * PAGE_SIZE), size)) return -EFAULT; @@ -590,7 +589,7 @@ int radeon_cs_update_pages(struct radeon_cs_parser *p, int pg_idx) bool copy1 = (p->rdev->flags & RADEON_IS_AGP) ? false : true; for (i = ibc->last_copied_page + 1; i < pg_idx; i++) { - if (DRM_COPY_FROM_USER(p->ib->ptr + (i * (PAGE_SIZE/4)), + if (DRM_COPY_FROM_USER(p->ib.ptr + (i * (PAGE_SIZE/4)), ibc->user_ptr + (i * PAGE_SIZE), PAGE_SIZE)) { p->parser_error = -EFAULT; @@ -606,7 +605,7 @@ int radeon_cs_update_pages(struct radeon_cs_parser *p, int pg_idx) new_page = ibc->kpage_idx[0] < ibc->kpage_idx[1] ? 0 : 1; if (copy1) - ibc->kpage[new_page] = p->ib->ptr + (pg_idx * (PAGE_SIZE / 4)); + ibc->kpage[new_page] = p->ib.ptr + (pg_idx * (PAGE_SIZE / 4)); if (DRM_COPY_FROM_USER(ibc->kpage[new_page], ibc->user_ptr + (pg_idx * PAGE_SIZE), @@ -617,7 +616,7 @@ int radeon_cs_update_pages(struct radeon_cs_parser *p, int pg_idx) /* copy to IB for non single case */ if (!copy1) - memcpy((void *)(p->ib->ptr+(pg_idx*(PAGE_SIZE/4))), ibc->kpage[new_page], size); + memcpy((void *)(p->ib.ptr+(pg_idx*(PAGE_SIZE/4))), ibc->kpage[new_page], size); ibc->last_copied_page = pg_idx; ibc->kpage_idx[new_page] = pg_idx; diff --git a/drivers/gpu/drm/radeon/radeon_device.c b/drivers/gpu/drm/radeon/radeon_device.c index ff28210..5763701 100644 --- a/drivers/gpu/drm/radeon/radeon_device.c +++ b/drivers/gpu/drm/radeon/radeon_device.c @@ -225,9 +225,9 @@ int radeon_wb_init(struct radeon_device *rdev) /* disable event_write fences */ rdev->wb.use_event = false; /* disabled via module param */ - if (radeon_no_wb == 1) + if (radeon_no_wb == 1) { rdev->wb.enabled = false; - else { + } else { if (rdev->flags & RADEON_IS_AGP) { /* often unreliable on AGP */ rdev->wb.enabled = false; @@ -237,8 +237,9 @@ int radeon_wb_init(struct radeon_device *rdev) } else { rdev->wb.enabled = true; /* event_write fences are only available on r600+ */ - if (rdev->family >= CHIP_R600) + if (rdev->family >= CHIP_R600) { rdev->wb.use_event = true; + } } } /* always use writeback/events on NI, APUs */ @@ -696,6 +697,11 @@ static bool radeon_switcheroo_can_switch(struct pci_dev *pdev) return can_switch; } +static const struct vga_switcheroo_client_ops radeon_switcheroo_ops = { + .set_gpu_state = radeon_switcheroo_set_state, + .reprobe = NULL, + .can_switch = radeon_switcheroo_can_switch, +}; int radeon_device_init(struct radeon_device *rdev, struct drm_device *ddev, @@ -723,21 +729,18 @@ int radeon_device_init(struct radeon_device *rdev, /* mutex initialization are all done here so we * can recall function without having locking issues */ radeon_mutex_init(&rdev->cs_mutex); - radeon_mutex_init(&rdev->ib_pool.mutex); - for (i = 0; i < RADEON_NUM_RINGS; ++i) - mutex_init(&rdev->ring[i].mutex); + mutex_init(&rdev->ring_lock); mutex_init(&rdev->dc_hw_i2c_mutex); if (rdev->family >= CHIP_R600) spin_lock_init(&rdev->ih.lock); mutex_init(&rdev->gem.mutex); mutex_init(&rdev->pm.mutex); mutex_init(&rdev->vram_mutex); - rwlock_init(&rdev->fence_lock); - rwlock_init(&rdev->semaphore_drv.lock); - INIT_LIST_HEAD(&rdev->gem.objects); init_waitqueue_head(&rdev->irq.vblank_queue); init_waitqueue_head(&rdev->irq.idle_queue); - INIT_LIST_HEAD(&rdev->semaphore_drv.bo); + r = radeon_gem_init(rdev); + if (r) + return r; /* initialize vm here */ rdev->vm_manager.use_bitmap = 1; rdev->vm_manager.max_pfn = 1 << 20; @@ -813,10 +816,7 @@ int radeon_device_init(struct radeon_device *rdev, /* this will fail for cards that aren't VGA class devices, just * ignore it */ vga_client_register(rdev->pdev, rdev, NULL, radeon_vga_set_decode); - vga_switcheroo_register_client(rdev->pdev, - radeon_switcheroo_set_state, - NULL, - radeon_switcheroo_can_switch); + vga_switcheroo_register_client(rdev->pdev, &radeon_switcheroo_ops); r = radeon_init(rdev); if (r) @@ -913,9 +913,12 @@ int radeon_suspend_kms(struct drm_device *dev, pm_message_t state) } /* evict vram memory */ radeon_bo_evict_vram(rdev); + + mutex_lock(&rdev->ring_lock); /* wait for gpu to finish processing current batch */ for (i = 0; i < RADEON_NUM_RINGS; i++) - radeon_fence_wait_empty(rdev, i); + radeon_fence_wait_empty_locked(rdev, i); + mutex_unlock(&rdev->ring_lock); radeon_save_bios_scratch_regs(rdev); diff --git a/drivers/gpu/drm/radeon/radeon_display.c b/drivers/gpu/drm/radeon/radeon_display.c index 0a1d4bd..64a008d1 100644 --- a/drivers/gpu/drm/radeon/radeon_display.c +++ b/drivers/gpu/drm/radeon/radeon_display.c @@ -573,24 +573,6 @@ static const char *encoder_names[37] = { "INTERNAL_VCE" }; -static const char *connector_names[15] = { - "Unknown", - "VGA", - "DVI-I", - "DVI-D", - "DVI-A", - "Composite", - "S-video", - "LVDS", - "Component", - "DIN", - "DisplayPort", - "HDMI-A", - "HDMI-B", - "TV", - "eDP", -}; - static const char *hpd_names[6] = { "HPD1", "HPD2", @@ -613,7 +595,7 @@ static void radeon_print_display_setup(struct drm_device *dev) list_for_each_entry(connector, &dev->mode_config.connector_list, head) { radeon_connector = to_radeon_connector(connector); DRM_INFO("Connector %d:\n", i); - DRM_INFO(" %s\n", connector_names[connector->connector_type]); + DRM_INFO(" %s\n", drm_get_connector_name(connector)); if (radeon_connector->hpd.hpd != RADEON_HPD_NONE) DRM_INFO(" %s\n", hpd_names[radeon_connector->hpd.hpd]); if (radeon_connector->ddc_bus) { @@ -1243,6 +1225,93 @@ void radeon_update_display_priority(struct radeon_device *rdev) } +/* + * Allocate hdmi structs and determine register offsets + */ +static void radeon_afmt_init(struct radeon_device *rdev) +{ + int i; + + for (i = 0; i < RADEON_MAX_AFMT_BLOCKS; i++) + rdev->mode_info.afmt[i] = NULL; + + if (ASIC_IS_DCE6(rdev)) { + /* todo */ + } else if (ASIC_IS_DCE4(rdev)) { + /* DCE4/5 has 6 audio blocks tied to DIG encoders */ + /* DCE4.1 has 2 audio blocks tied to DIG encoders */ + rdev->mode_info.afmt[0] = kzalloc(sizeof(struct radeon_afmt), GFP_KERNEL); + if (rdev->mode_info.afmt[0]) { + rdev->mode_info.afmt[0]->offset = EVERGREEN_CRTC0_REGISTER_OFFSET; + rdev->mode_info.afmt[0]->id = 0; + } + rdev->mode_info.afmt[1] = kzalloc(sizeof(struct radeon_afmt), GFP_KERNEL); + if (rdev->mode_info.afmt[1]) { + rdev->mode_info.afmt[1]->offset = EVERGREEN_CRTC1_REGISTER_OFFSET; + rdev->mode_info.afmt[1]->id = 1; + } + if (!ASIC_IS_DCE41(rdev)) { + rdev->mode_info.afmt[2] = kzalloc(sizeof(struct radeon_afmt), GFP_KERNEL); + if (rdev->mode_info.afmt[2]) { + rdev->mode_info.afmt[2]->offset = EVERGREEN_CRTC2_REGISTER_OFFSET; + rdev->mode_info.afmt[2]->id = 2; + } + rdev->mode_info.afmt[3] = kzalloc(sizeof(struct radeon_afmt), GFP_KERNEL); + if (rdev->mode_info.afmt[3]) { + rdev->mode_info.afmt[3]->offset = EVERGREEN_CRTC3_REGISTER_OFFSET; + rdev->mode_info.afmt[3]->id = 3; + } + rdev->mode_info.afmt[4] = kzalloc(sizeof(struct radeon_afmt), GFP_KERNEL); + if (rdev->mode_info.afmt[4]) { + rdev->mode_info.afmt[4]->offset = EVERGREEN_CRTC4_REGISTER_OFFSET; + rdev->mode_info.afmt[4]->id = 4; + } + rdev->mode_info.afmt[5] = kzalloc(sizeof(struct radeon_afmt), GFP_KERNEL); + if (rdev->mode_info.afmt[5]) { + rdev->mode_info.afmt[5]->offset = EVERGREEN_CRTC5_REGISTER_OFFSET; + rdev->mode_info.afmt[5]->id = 5; + } + } + } else if (ASIC_IS_DCE3(rdev)) { + /* DCE3.x has 2 audio blocks tied to DIG encoders */ + rdev->mode_info.afmt[0] = kzalloc(sizeof(struct radeon_afmt), GFP_KERNEL); + if (rdev->mode_info.afmt[0]) { + rdev->mode_info.afmt[0]->offset = DCE3_HDMI_OFFSET0; + rdev->mode_info.afmt[0]->id = 0; + } + rdev->mode_info.afmt[1] = kzalloc(sizeof(struct radeon_afmt), GFP_KERNEL); + if (rdev->mode_info.afmt[1]) { + rdev->mode_info.afmt[1]->offset = DCE3_HDMI_OFFSET1; + rdev->mode_info.afmt[1]->id = 1; + } + } else if (ASIC_IS_DCE2(rdev)) { + /* DCE2 has at least 1 routable audio block */ + rdev->mode_info.afmt[0] = kzalloc(sizeof(struct radeon_afmt), GFP_KERNEL); + if (rdev->mode_info.afmt[0]) { + rdev->mode_info.afmt[0]->offset = DCE2_HDMI_OFFSET0; + rdev->mode_info.afmt[0]->id = 0; + } + /* r6xx has 2 routable audio blocks */ + if (rdev->family >= CHIP_R600) { + rdev->mode_info.afmt[1] = kzalloc(sizeof(struct radeon_afmt), GFP_KERNEL); + if (rdev->mode_info.afmt[1]) { + rdev->mode_info.afmt[1]->offset = DCE2_HDMI_OFFSET1; + rdev->mode_info.afmt[1]->id = 1; + } + } + } +} + +static void radeon_afmt_fini(struct radeon_device *rdev) +{ + int i; + + for (i = 0; i < RADEON_MAX_AFMT_BLOCKS; i++) { + kfree(rdev->mode_info.afmt[i]); + rdev->mode_info.afmt[i] = NULL; + } +} + int radeon_modeset_init(struct radeon_device *rdev) { int i; @@ -1251,7 +1320,7 @@ int radeon_modeset_init(struct radeon_device *rdev) drm_mode_config_init(rdev->ddev); rdev->mode_info.mode_config_initialized = true; - rdev->ddev->mode_config.funcs = (void *)&radeon_mode_funcs; + rdev->ddev->mode_config.funcs = &radeon_mode_funcs; if (ASIC_IS_DCE5(rdev)) { rdev->ddev->mode_config.max_width = 16384; @@ -1303,6 +1372,9 @@ int radeon_modeset_init(struct radeon_device *rdev) /* initialize hpd */ radeon_hpd_init(rdev); + /* setup afmt */ + radeon_afmt_init(rdev); + /* Initialize power management */ radeon_pm_init(rdev); @@ -1319,6 +1391,7 @@ void radeon_modeset_fini(struct radeon_device *rdev) radeon_pm_fini(rdev); if (rdev->mode_info.mode_config_initialized) { + radeon_afmt_fini(rdev); drm_kms_helper_poll_fini(rdev->ddev); radeon_hpd_fini(rdev); drm_mode_config_cleanup(rdev->ddev); diff --git a/drivers/gpu/drm/radeon/radeon_fence.c b/drivers/gpu/drm/radeon/radeon_fence.c index 5bb78bf..11f5f40 100644 --- a/drivers/gpu/drm/radeon/radeon_fence.c +++ b/drivers/gpu/drm/radeon/radeon_fence.c @@ -63,75 +63,82 @@ static u32 radeon_fence_read(struct radeon_device *rdev, int ring) int radeon_fence_emit(struct radeon_device *rdev, struct radeon_fence *fence) { - unsigned long irq_flags; - - write_lock_irqsave(&rdev->fence_lock, irq_flags); - if (fence->emitted) { - write_unlock_irqrestore(&rdev->fence_lock, irq_flags); + /* we are protected by the ring emission mutex */ + if (fence->seq && fence->seq < RADEON_FENCE_NOTEMITED_SEQ) { return 0; } - fence->seq = atomic_add_return(1, &rdev->fence_drv[fence->ring].seq); + fence->seq = ++rdev->fence_drv[fence->ring].seq; radeon_fence_ring_emit(rdev, fence->ring, fence); trace_radeon_fence_emit(rdev->ddev, fence->seq); - fence->emitted = true; - /* are we the first fence on a previusly idle ring? */ - if (list_empty(&rdev->fence_drv[fence->ring].emitted)) { - rdev->fence_drv[fence->ring].last_activity = jiffies; - } - list_move_tail(&fence->list, &rdev->fence_drv[fence->ring].emitted); - write_unlock_irqrestore(&rdev->fence_lock, irq_flags); return 0; } -static bool radeon_fence_poll_locked(struct radeon_device *rdev, int ring) +void radeon_fence_process(struct radeon_device *rdev, int ring) { - struct radeon_fence *fence; - struct list_head *i, *n; - uint32_t seq; + uint64_t seq, last_seq; + unsigned count_loop = 0; bool wake = false; - seq = radeon_fence_read(rdev, ring); - if (seq == rdev->fence_drv[ring].last_seq) - return false; - - rdev->fence_drv[ring].last_seq = seq; - rdev->fence_drv[ring].last_activity = jiffies; + /* Note there is a scenario here for an infinite loop but it's + * very unlikely to happen. For it to happen, the current polling + * process need to be interrupted by another process and another + * process needs to update the last_seq btw the atomic read and + * xchg of the current process. + * + * More over for this to go in infinite loop there need to be + * continuously new fence signaled ie radeon_fence_read needs + * to return a different value each time for both the currently + * polling process and the other process that xchg the last_seq + * btw atomic read and xchg of the current process. And the + * value the other process set as last seq must be higher than + * the seq value we just read. Which means that current process + * need to be interrupted after radeon_fence_read and before + * atomic xchg. + * + * To be even more safe we count the number of time we loop and + * we bail after 10 loop just accepting the fact that we might + * have temporarly set the last_seq not to the true real last + * seq but to an older one. + */ + last_seq = atomic64_read(&rdev->fence_drv[ring].last_seq); + do { + seq = radeon_fence_read(rdev, ring); + seq |= last_seq & 0xffffffff00000000LL; + if (seq < last_seq) { + seq += 0x100000000LL; + } - n = NULL; - list_for_each(i, &rdev->fence_drv[ring].emitted) { - fence = list_entry(i, struct radeon_fence, list); - if (fence->seq == seq) { - n = i; + if (seq == last_seq) { break; } - } - /* all fence previous to this one are considered as signaled */ - if (n) { - i = n; - do { - n = i->prev; - list_move_tail(i, &rdev->fence_drv[ring].signaled); - fence = list_entry(i, struct radeon_fence, list); - fence->signaled = true; - i = n; - } while (i != &rdev->fence_drv[ring].emitted); + /* If we loop over we don't want to return without + * checking if a fence is signaled as it means that the + * seq we just read is different from the previous on. + */ wake = true; + last_seq = seq; + if ((count_loop++) > 10) { + /* We looped over too many time leave with the + * fact that we might have set an older fence + * seq then the current real last seq as signaled + * by the hw. + */ + break; + } + } while (atomic64_xchg(&rdev->fence_drv[ring].last_seq, seq) > seq); + + if (wake) { + rdev->fence_drv[ring].last_activity = jiffies; + wake_up_all(&rdev->fence_queue); } - return wake; } static void radeon_fence_destroy(struct kref *kref) { - unsigned long irq_flags; - struct radeon_fence *fence; + struct radeon_fence *fence; fence = container_of(kref, struct radeon_fence, kref); - write_lock_irqsave(&fence->rdev->fence_lock, irq_flags); - list_del(&fence->list); - fence->emitted = false; - write_unlock_irqrestore(&fence->rdev->fence_lock, irq_flags); - if (fence->semaphore) - radeon_semaphore_free(fence->rdev, fence->semaphore); + fence->seq = RADEON_FENCE_NOTEMITED_SEQ; kfree(fence); } @@ -145,83 +152,84 @@ int radeon_fence_create(struct radeon_device *rdev, } kref_init(&((*fence)->kref)); (*fence)->rdev = rdev; - (*fence)->emitted = false; - (*fence)->signaled = false; - (*fence)->seq = 0; + (*fence)->seq = RADEON_FENCE_NOTEMITED_SEQ; (*fence)->ring = ring; - (*fence)->semaphore = NULL; - INIT_LIST_HEAD(&(*fence)->list); return 0; } -bool radeon_fence_signaled(struct radeon_fence *fence) +static bool radeon_fence_seq_signaled(struct radeon_device *rdev, + u64 seq, unsigned ring) { - unsigned long irq_flags; - bool signaled = false; - - if (!fence) + if (atomic64_read(&rdev->fence_drv[ring].last_seq) >= seq) { + return true; + } + /* poll new last sequence at least once */ + radeon_fence_process(rdev, ring); + if (atomic64_read(&rdev->fence_drv[ring].last_seq) >= seq) { return true; + } + return false; +} - write_lock_irqsave(&fence->rdev->fence_lock, irq_flags); - signaled = fence->signaled; - /* if we are shuting down report all fence as signaled */ - if (fence->rdev->shutdown) { - signaled = true; +bool radeon_fence_signaled(struct radeon_fence *fence) +{ + if (!fence) { + return true; } - if (!fence->emitted) { + if (fence->seq == RADEON_FENCE_NOTEMITED_SEQ) { WARN(1, "Querying an unemitted fence : %p !\n", fence); - signaled = true; + return true; } - if (!signaled) { - radeon_fence_poll_locked(fence->rdev, fence->ring); - signaled = fence->signaled; + if (fence->seq == RADEON_FENCE_SIGNALED_SEQ) { + return true; + } + if (radeon_fence_seq_signaled(fence->rdev, fence->seq, fence->ring)) { + fence->seq = RADEON_FENCE_SIGNALED_SEQ; + return true; } - write_unlock_irqrestore(&fence->rdev->fence_lock, irq_flags); - return signaled; + return false; } -int radeon_fence_wait(struct radeon_fence *fence, bool intr) +static int radeon_fence_wait_seq(struct radeon_device *rdev, u64 target_seq, + unsigned ring, bool intr, bool lock_ring) { - struct radeon_device *rdev; - unsigned long irq_flags, timeout; - u32 seq; - int i, r; + unsigned long timeout, last_activity; + uint64_t seq; + unsigned i; bool signaled; + int r; - if (fence == NULL) { - WARN(1, "Querying an invalid fence : %p !\n", fence); - return -EINVAL; - } + while (target_seq > atomic64_read(&rdev->fence_drv[ring].last_seq)) { + if (!rdev->ring[ring].ready) { + return -EBUSY; + } - rdev = fence->rdev; - signaled = radeon_fence_signaled(fence); - while (!signaled) { - read_lock_irqsave(&rdev->fence_lock, irq_flags); timeout = jiffies - RADEON_FENCE_JIFFIES_TIMEOUT; - if (time_after(rdev->fence_drv[fence->ring].last_activity, timeout)) { + if (time_after(rdev->fence_drv[ring].last_activity, timeout)) { /* the normal case, timeout is somewhere before last_activity */ - timeout = rdev->fence_drv[fence->ring].last_activity - timeout; + timeout = rdev->fence_drv[ring].last_activity - timeout; } else { /* either jiffies wrapped around, or no fence was signaled in the last 500ms - * anyway we will just wait for the minimum amount and then check for a lockup */ + * anyway we will just wait for the minimum amount and then check for a lockup + */ timeout = 1; } - /* save current sequence value used to check for GPU lockups */ - seq = rdev->fence_drv[fence->ring].last_seq; - read_unlock_irqrestore(&rdev->fence_lock, irq_flags); + seq = atomic64_read(&rdev->fence_drv[ring].last_seq); + /* Save current last activity valuee, used to check for GPU lockups */ + last_activity = rdev->fence_drv[ring].last_activity; trace_radeon_fence_wait_begin(rdev->ddev, seq); - radeon_irq_kms_sw_irq_get(rdev, fence->ring); + radeon_irq_kms_sw_irq_get(rdev, ring); if (intr) { - r = wait_event_interruptible_timeout( - rdev->fence_drv[fence->ring].queue, - (signaled = radeon_fence_signaled(fence)), timeout); - } else { - r = wait_event_timeout( - rdev->fence_drv[fence->ring].queue, - (signaled = radeon_fence_signaled(fence)), timeout); + r = wait_event_interruptible_timeout(rdev->fence_queue, + (signaled = radeon_fence_seq_signaled(rdev, target_seq, ring)), + timeout); + } else { + r = wait_event_timeout(rdev->fence_queue, + (signaled = radeon_fence_seq_signaled(rdev, target_seq, ring)), + timeout); } - radeon_irq_kms_sw_irq_put(rdev, fence->ring); + radeon_irq_kms_sw_irq_put(rdev, ring); if (unlikely(r < 0)) { return r; } @@ -234,81 +242,246 @@ int radeon_fence_wait(struct radeon_fence *fence, bool intr) continue; } - write_lock_irqsave(&rdev->fence_lock, irq_flags); /* check if sequence value has changed since last_activity */ - if (seq != rdev->fence_drv[fence->ring].last_seq) { - write_unlock_irqrestore(&rdev->fence_lock, irq_flags); + if (seq != atomic64_read(&rdev->fence_drv[ring].last_seq)) { continue; } - /* change sequence value on all rings, so nobody else things there is a lockup */ - for (i = 0; i < RADEON_NUM_RINGS; ++i) - rdev->fence_drv[i].last_seq -= 0x10000; - - rdev->fence_drv[fence->ring].last_activity = jiffies; - write_unlock_irqrestore(&rdev->fence_lock, irq_flags); + if (lock_ring) { + mutex_lock(&rdev->ring_lock); + } - if (radeon_ring_is_lockup(rdev, fence->ring, &rdev->ring[fence->ring])) { + /* test if somebody else has already decided that this is a lockup */ + if (last_activity != rdev->fence_drv[ring].last_activity) { + if (lock_ring) { + mutex_unlock(&rdev->ring_lock); + } + continue; + } + if (radeon_ring_is_lockup(rdev, ring, &rdev->ring[ring])) { /* good news we believe it's a lockup */ - printk(KERN_WARNING "GPU lockup (waiting for 0x%08X last fence id 0x%08X)\n", - fence->seq, seq); + dev_warn(rdev->dev, "GPU lockup (waiting for 0x%016llx last fence id 0x%016llx)\n", + target_seq, seq); + + /* change last activity so nobody else think there is a lockup */ + for (i = 0; i < RADEON_NUM_RINGS; ++i) { + rdev->fence_drv[i].last_activity = jiffies; + } /* mark the ring as not ready any more */ - rdev->ring[fence->ring].ready = false; + rdev->ring[ring].ready = false; + if (lock_ring) { + mutex_unlock(&rdev->ring_lock); + } return -EDEADLK; } + + if (lock_ring) { + mutex_unlock(&rdev->ring_lock); + } } } return 0; } -int radeon_fence_wait_next(struct radeon_device *rdev, int ring) +int radeon_fence_wait(struct radeon_fence *fence, bool intr) { - unsigned long irq_flags; - struct radeon_fence *fence; int r; - write_lock_irqsave(&rdev->fence_lock, irq_flags); - if (!rdev->ring[ring].ready) { - write_unlock_irqrestore(&rdev->fence_lock, irq_flags); - return -EBUSY; + if (fence == NULL) { + WARN(1, "Querying an invalid fence : %p !\n", fence); + return -EINVAL; } - if (list_empty(&rdev->fence_drv[ring].emitted)) { - write_unlock_irqrestore(&rdev->fence_lock, irq_flags); - return -ENOENT; + + r = radeon_fence_wait_seq(fence->rdev, fence->seq, + fence->ring, intr, true); + if (r) { + return r; } - fence = list_entry(rdev->fence_drv[ring].emitted.next, - struct radeon_fence, list); - radeon_fence_ref(fence); - write_unlock_irqrestore(&rdev->fence_lock, irq_flags); - r = radeon_fence_wait(fence, false); - radeon_fence_unref(&fence); - return r; + fence->seq = RADEON_FENCE_SIGNALED_SEQ; + return 0; } -int radeon_fence_wait_empty(struct radeon_device *rdev, int ring) +bool radeon_fence_any_seq_signaled(struct radeon_device *rdev, u64 *seq) { - unsigned long irq_flags; - struct radeon_fence *fence; + unsigned i; + + for (i = 0; i < RADEON_NUM_RINGS; ++i) { + if (seq[i] && radeon_fence_seq_signaled(rdev, seq[i], i)) { + return true; + } + } + return false; +} + +static int radeon_fence_wait_any_seq(struct radeon_device *rdev, + u64 *target_seq, bool intr) +{ + unsigned long timeout, last_activity, tmp; + unsigned i, ring = RADEON_NUM_RINGS; + bool signaled; int r; - write_lock_irqsave(&rdev->fence_lock, irq_flags); - if (!rdev->ring[ring].ready) { - write_unlock_irqrestore(&rdev->fence_lock, irq_flags); - return -EBUSY; + for (i = 0, last_activity = 0; i < RADEON_NUM_RINGS; ++i) { + if (!target_seq[i]) { + continue; + } + + /* use the most recent one as indicator */ + if (time_after(rdev->fence_drv[i].last_activity, last_activity)) { + last_activity = rdev->fence_drv[i].last_activity; + } + + /* For lockup detection just pick the lowest ring we are + * actively waiting for + */ + if (i < ring) { + ring = i; + } } - if (list_empty(&rdev->fence_drv[ring].emitted)) { - write_unlock_irqrestore(&rdev->fence_lock, irq_flags); + + /* nothing to wait for ? */ + if (ring == RADEON_NUM_RINGS) { return 0; } - fence = list_entry(rdev->fence_drv[ring].emitted.prev, - struct radeon_fence, list); - radeon_fence_ref(fence); - write_unlock_irqrestore(&rdev->fence_lock, irq_flags); - r = radeon_fence_wait(fence, false); - radeon_fence_unref(&fence); - return r; + + while (!radeon_fence_any_seq_signaled(rdev, target_seq)) { + timeout = jiffies - RADEON_FENCE_JIFFIES_TIMEOUT; + if (time_after(last_activity, timeout)) { + /* the normal case, timeout is somewhere before last_activity */ + timeout = last_activity - timeout; + } else { + /* either jiffies wrapped around, or no fence was signaled in the last 500ms + * anyway we will just wait for the minimum amount and then check for a lockup + */ + timeout = 1; + } + + trace_radeon_fence_wait_begin(rdev->ddev, target_seq[ring]); + for (i = 0; i < RADEON_NUM_RINGS; ++i) { + if (target_seq[i]) { + radeon_irq_kms_sw_irq_get(rdev, i); + } + } + if (intr) { + r = wait_event_interruptible_timeout(rdev->fence_queue, + (signaled = radeon_fence_any_seq_signaled(rdev, target_seq)), + timeout); + } else { + r = wait_event_timeout(rdev->fence_queue, + (signaled = radeon_fence_any_seq_signaled(rdev, target_seq)), + timeout); + } + for (i = 0; i < RADEON_NUM_RINGS; ++i) { + if (target_seq[i]) { + radeon_irq_kms_sw_irq_put(rdev, i); + } + } + if (unlikely(r < 0)) { + return r; + } + trace_radeon_fence_wait_end(rdev->ddev, target_seq[ring]); + + if (unlikely(!signaled)) { + /* we were interrupted for some reason and fence + * isn't signaled yet, resume waiting */ + if (r) { + continue; + } + + mutex_lock(&rdev->ring_lock); + for (i = 0, tmp = 0; i < RADEON_NUM_RINGS; ++i) { + if (time_after(rdev->fence_drv[i].last_activity, tmp)) { + tmp = rdev->fence_drv[i].last_activity; + } + } + /* test if somebody else has already decided that this is a lockup */ + if (last_activity != tmp) { + last_activity = tmp; + mutex_unlock(&rdev->ring_lock); + continue; + } + + if (radeon_ring_is_lockup(rdev, ring, &rdev->ring[ring])) { + /* good news we believe it's a lockup */ + dev_warn(rdev->dev, "GPU lockup (waiting for 0x%016llx)\n", + target_seq[ring]); + + /* change last activity so nobody else think there is a lockup */ + for (i = 0; i < RADEON_NUM_RINGS; ++i) { + rdev->fence_drv[i].last_activity = jiffies; + } + + /* mark the ring as not ready any more */ + rdev->ring[ring].ready = false; + mutex_unlock(&rdev->ring_lock); + return -EDEADLK; + } + mutex_unlock(&rdev->ring_lock); + } + } + return 0; +} + +int radeon_fence_wait_any(struct radeon_device *rdev, + struct radeon_fence **fences, + bool intr) +{ + uint64_t seq[RADEON_NUM_RINGS]; + unsigned i; + int r; + + for (i = 0; i < RADEON_NUM_RINGS; ++i) { + seq[i] = 0; + + if (!fences[i]) { + continue; + } + + if (fences[i]->seq == RADEON_FENCE_SIGNALED_SEQ) { + /* something was allready signaled */ + return 0; + } + + if (fences[i]->seq < RADEON_FENCE_NOTEMITED_SEQ) { + seq[i] = fences[i]->seq; + } + } + + r = radeon_fence_wait_any_seq(rdev, seq, intr); + if (r) { + return r; + } + return 0; +} + +int radeon_fence_wait_next_locked(struct radeon_device *rdev, int ring) +{ + uint64_t seq; + + /* We are not protected by ring lock when reading current seq but + * it's ok as worst case is we return to early while we could have + * wait. + */ + seq = atomic64_read(&rdev->fence_drv[ring].last_seq) + 1ULL; + if (seq >= rdev->fence_drv[ring].seq) { + /* nothing to wait for, last_seq is + already the last emited fence */ + return -ENOENT; + } + return radeon_fence_wait_seq(rdev, seq, ring, false, false); +} + +int radeon_fence_wait_empty_locked(struct radeon_device *rdev, int ring) +{ + /* We are not protected by ring lock when reading current seq + * but it's ok as wait empty is call from place where no more + * activity can be scheduled so there won't be concurrent access + * to seq value. + */ + return radeon_fence_wait_seq(rdev, rdev->fence_drv[ring].seq, + ring, false, false); } struct radeon_fence *radeon_fence_ref(struct radeon_fence *fence) @@ -327,49 +500,27 @@ void radeon_fence_unref(struct radeon_fence **fence) } } -void radeon_fence_process(struct radeon_device *rdev, int ring) -{ - unsigned long irq_flags; - bool wake; - - write_lock_irqsave(&rdev->fence_lock, irq_flags); - wake = radeon_fence_poll_locked(rdev, ring); - write_unlock_irqrestore(&rdev->fence_lock, irq_flags); - if (wake) { - wake_up_all(&rdev->fence_drv[ring].queue); - } -} - -int radeon_fence_count_emitted(struct radeon_device *rdev, int ring) +unsigned radeon_fence_count_emitted(struct radeon_device *rdev, int ring) { - unsigned long irq_flags; - int not_processed = 0; - - read_lock_irqsave(&rdev->fence_lock, irq_flags); - if (!rdev->fence_drv[ring].initialized) { - read_unlock_irqrestore(&rdev->fence_lock, irq_flags); - return 0; + uint64_t emitted; + + /* We are not protected by ring lock when reading the last sequence + * but it's ok to report slightly wrong fence count here. + */ + radeon_fence_process(rdev, ring); + emitted = rdev->fence_drv[ring].seq - atomic64_read(&rdev->fence_drv[ring].last_seq); + /* to avoid 32bits warp around */ + if (emitted > 0x10000000) { + emitted = 0x10000000; } - - if (!list_empty(&rdev->fence_drv[ring].emitted)) { - struct list_head *ptr; - list_for_each(ptr, &rdev->fence_drv[ring].emitted) { - /* count up to 3, that's enought info */ - if (++not_processed >= 3) - break; - } - } - read_unlock_irqrestore(&rdev->fence_lock, irq_flags); - return not_processed; + return (unsigned)emitted; } int radeon_fence_driver_start_ring(struct radeon_device *rdev, int ring) { - unsigned long irq_flags; uint64_t index; int r; - write_lock_irqsave(&rdev->fence_lock, irq_flags); radeon_scratch_free(rdev, rdev->fence_drv[ring].scratch_reg); if (rdev->wb.use_event) { rdev->fence_drv[ring].scratch_reg = 0; @@ -378,7 +529,6 @@ int radeon_fence_driver_start_ring(struct radeon_device *rdev, int ring) r = radeon_scratch_get(rdev, &rdev->fence_drv[ring].scratch_reg); if (r) { dev_err(rdev->dev, "fence failed to get scratch register\n"); - write_unlock_irqrestore(&rdev->fence_lock, irq_flags); return r; } index = RADEON_WB_SCRATCH_OFFSET + @@ -387,11 +537,10 @@ int radeon_fence_driver_start_ring(struct radeon_device *rdev, int ring) } rdev->fence_drv[ring].cpu_addr = &rdev->wb.wb[index/4]; rdev->fence_drv[ring].gpu_addr = rdev->wb.gpu_addr + index; - radeon_fence_write(rdev, atomic_read(&rdev->fence_drv[ring].seq), ring); + radeon_fence_write(rdev, rdev->fence_drv[ring].seq, ring); rdev->fence_drv[ring].initialized = true; - DRM_INFO("fence driver on ring %d use gpu addr 0x%08Lx and cpu addr 0x%p\n", + dev_info(rdev->dev, "fence driver on ring %d use gpu addr 0x%016llx and cpu addr 0x%p\n", ring, rdev->fence_drv[ring].gpu_addr, rdev->fence_drv[ring].cpu_addr); - write_unlock_irqrestore(&rdev->fence_lock, irq_flags); return 0; } @@ -400,23 +549,20 @@ static void radeon_fence_driver_init_ring(struct radeon_device *rdev, int ring) rdev->fence_drv[ring].scratch_reg = -1; rdev->fence_drv[ring].cpu_addr = NULL; rdev->fence_drv[ring].gpu_addr = 0; - atomic_set(&rdev->fence_drv[ring].seq, 0); - INIT_LIST_HEAD(&rdev->fence_drv[ring].emitted); - INIT_LIST_HEAD(&rdev->fence_drv[ring].signaled); - init_waitqueue_head(&rdev->fence_drv[ring].queue); + rdev->fence_drv[ring].seq = 0; + atomic64_set(&rdev->fence_drv[ring].last_seq, 0); + rdev->fence_drv[ring].last_activity = jiffies; rdev->fence_drv[ring].initialized = false; } int radeon_fence_driver_init(struct radeon_device *rdev) { - unsigned long irq_flags; int ring; - write_lock_irqsave(&rdev->fence_lock, irq_flags); + init_waitqueue_head(&rdev->fence_queue); for (ring = 0; ring < RADEON_NUM_RINGS; ring++) { radeon_fence_driver_init_ring(rdev, ring); } - write_unlock_irqrestore(&rdev->fence_lock, irq_flags); if (radeon_debugfs_fence_init(rdev)) { dev_err(rdev->dev, "fence debugfs file creation failed\n"); } @@ -425,19 +571,18 @@ int radeon_fence_driver_init(struct radeon_device *rdev) void radeon_fence_driver_fini(struct radeon_device *rdev) { - unsigned long irq_flags; int ring; + mutex_lock(&rdev->ring_lock); for (ring = 0; ring < RADEON_NUM_RINGS; ring++) { if (!rdev->fence_drv[ring].initialized) continue; - radeon_fence_wait_empty(rdev, ring); - wake_up_all(&rdev->fence_drv[ring].queue); - write_lock_irqsave(&rdev->fence_lock, irq_flags); + radeon_fence_wait_empty_locked(rdev, ring); + wake_up_all(&rdev->fence_queue); radeon_scratch_free(rdev, rdev->fence_drv[ring].scratch_reg); - write_unlock_irqrestore(&rdev->fence_lock, irq_flags); rdev->fence_drv[ring].initialized = false; } + mutex_unlock(&rdev->ring_lock); } @@ -450,7 +595,6 @@ static int radeon_debugfs_fence_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 radeon_device *rdev = dev->dev_private; - struct radeon_fence *fence; int i; for (i = 0; i < RADEON_NUM_RINGS; ++i) { @@ -458,14 +602,10 @@ static int radeon_debugfs_fence_info(struct seq_file *m, void *data) continue; seq_printf(m, "--- ring %d ---\n", i); - seq_printf(m, "Last signaled fence 0x%08X\n", - radeon_fence_read(rdev, i)); - if (!list_empty(&rdev->fence_drv[i].emitted)) { - fence = list_entry(rdev->fence_drv[i].emitted.prev, - struct radeon_fence, list); - seq_printf(m, "Last emitted fence %p with 0x%08X\n", - fence, fence->seq); - } + seq_printf(m, "Last signaled fence 0x%016llx\n", + (unsigned long long)atomic64_read(&rdev->fence_drv[i].last_seq)); + seq_printf(m, "Last emitted 0x%016llx\n", + rdev->fence_drv[i].seq); } return 0; } diff --git a/drivers/gpu/drm/radeon/radeon_gart.c b/drivers/gpu/drm/radeon/radeon_gart.c index c58a036..8e9ef34 100644 --- a/drivers/gpu/drm/radeon/radeon_gart.c +++ b/drivers/gpu/drm/radeon/radeon_gart.c @@ -326,7 +326,7 @@ static void radeon_vm_unbind_locked(struct radeon_device *rdev, rdev->vm_manager.use_bitmap &= ~(1 << vm->id); list_del_init(&vm->list); vm->id = -1; - radeon_sa_bo_free(rdev, &vm->sa_bo); + radeon_sa_bo_free(rdev, &vm->sa_bo, NULL); vm->pt = NULL; list_for_each_entry(bo_va, &vm->va, vm_list) { @@ -395,7 +395,7 @@ int radeon_vm_bind(struct radeon_device *rdev, struct radeon_vm *vm) retry: r = radeon_sa_bo_new(rdev, &rdev->vm_manager.sa_manager, &vm->sa_bo, RADEON_GPU_PAGE_ALIGN(vm->last_pfn * 8), - RADEON_GPU_PAGE_SIZE); + RADEON_GPU_PAGE_SIZE, false); if (r) { if (list_empty(&rdev->vm_manager.lru_vm)) { return r; @@ -404,10 +404,8 @@ retry: radeon_vm_unbind(rdev, vm_evict); goto retry; } - vm->pt = rdev->vm_manager.sa_manager.cpu_ptr; - vm->pt += (vm->sa_bo.offset >> 3); - vm->pt_gpu_addr = rdev->vm_manager.sa_manager.gpu_addr; - vm->pt_gpu_addr += vm->sa_bo.offset; + vm->pt = radeon_sa_bo_cpu_addr(vm->sa_bo); + vm->pt_gpu_addr = radeon_sa_bo_gpu_addr(vm->sa_bo); memset(vm->pt, 0, RADEON_GPU_PAGE_ALIGN(vm->last_pfn * 8)); retry_id: @@ -428,14 +426,14 @@ retry_id: /* do hw bind */ r = rdev->vm_manager.funcs->bind(rdev, vm, id); if (r) { - radeon_sa_bo_free(rdev, &vm->sa_bo); + radeon_sa_bo_free(rdev, &vm->sa_bo, NULL); return r; } rdev->vm_manager.use_bitmap |= 1 << id; vm->id = id; list_add_tail(&vm->list, &rdev->vm_manager.lru_vm); - return radeon_vm_bo_update_pte(rdev, vm, rdev->ib_pool.sa_manager.bo, - &rdev->ib_pool.sa_manager.bo->tbo.mem); + return radeon_vm_bo_update_pte(rdev, vm, rdev->ring_tmp_bo.bo, + &rdev->ring_tmp_bo.bo->tbo.mem); } /* object have to be reserved */ @@ -633,7 +631,7 @@ int radeon_vm_init(struct radeon_device *rdev, struct radeon_vm *vm) /* map the ib pool buffer at 0 in virtual address space, set * read only */ - r = radeon_vm_bo_add(rdev, vm, rdev->ib_pool.sa_manager.bo, 0, + r = radeon_vm_bo_add(rdev, vm, rdev->ring_tmp_bo.bo, 0, RADEON_VM_PAGE_READABLE | RADEON_VM_PAGE_SNOOPED); return r; } @@ -650,12 +648,12 @@ void radeon_vm_fini(struct radeon_device *rdev, struct radeon_vm *vm) radeon_mutex_unlock(&rdev->cs_mutex); /* remove all bo */ - r = radeon_bo_reserve(rdev->ib_pool.sa_manager.bo, false); + r = radeon_bo_reserve(rdev->ring_tmp_bo.bo, false); if (!r) { - bo_va = radeon_bo_va(rdev->ib_pool.sa_manager.bo, vm); + bo_va = radeon_bo_va(rdev->ring_tmp_bo.bo, vm); list_del_init(&bo_va->bo_list); list_del_init(&bo_va->vm_list); - radeon_bo_unreserve(rdev->ib_pool.sa_manager.bo); + radeon_bo_unreserve(rdev->ring_tmp_bo.bo); kfree(bo_va); } if (!list_empty(&vm->va)) { diff --git a/drivers/gpu/drm/radeon/radeon_legacy_encoders.c b/drivers/gpu/drm/radeon/radeon_legacy_encoders.c index 42db254..a0c8222 100644 --- a/drivers/gpu/drm/radeon/radeon_legacy_encoders.c +++ b/drivers/gpu/drm/radeon/radeon_legacy_encoders.c @@ -369,6 +369,7 @@ void radeon_legacy_backlight_init(struct radeon_encoder *radeon_encoder, goto error; } + memset(&props, 0, sizeof(props)); props.max_brightness = MAX_RADEON_LEVEL; props.type = BACKLIGHT_RAW; bd = backlight_device_register("radeon_bl", &drm_connector->kdev, diff --git a/drivers/gpu/drm/radeon/radeon_mode.h b/drivers/gpu/drm/radeon/radeon_mode.h index 499a5fe..5b10ffd 100644 --- a/drivers/gpu/drm/radeon/radeon_mode.h +++ b/drivers/gpu/drm/radeon/radeon_mode.h @@ -220,12 +220,20 @@ enum radeon_dvo_chip { struct radeon_fbdev; +struct radeon_afmt { + bool enabled; + int offset; + bool last_buffer_filled_status; + int id; +}; + struct radeon_mode_info { struct atom_context *atom_context; struct card_info *atom_card_info; enum radeon_connector_table connector_table; bool mode_config_initialized; struct radeon_crtc *crtcs[6]; + struct radeon_afmt *afmt[6]; /* DVI-I properties */ struct drm_property *coherent_mode_property; /* DAC enable load detect */ @@ -364,6 +372,7 @@ struct radeon_encoder_atom_dig { int dpms_mode; uint8_t backlight_level; int panel_mode; + struct radeon_afmt *afmt; }; struct radeon_encoder_atom_dac { @@ -385,10 +394,6 @@ struct radeon_encoder { struct drm_display_mode native_mode; void *enc_priv; int audio_polling_active; - bool hdmi_enabled; - int hdmi_offset; - int hdmi_audio_workaround; - int hdmi_buffer_status; bool is_ext_encoder; u16 caps; }; diff --git a/drivers/gpu/drm/radeon/radeon_object.h b/drivers/gpu/drm/radeon/radeon_object.h index f9104be..befec7d 100644 --- a/drivers/gpu/drm/radeon/radeon_object.h +++ b/drivers/gpu/drm/radeon/radeon_object.h @@ -146,6 +146,17 @@ extern struct radeon_bo_va *radeon_bo_va(struct radeon_bo *rbo, /* * sub allocation */ + +static inline uint64_t radeon_sa_bo_gpu_addr(struct radeon_sa_bo *sa_bo) +{ + return sa_bo->manager->gpu_addr + sa_bo->soffset; +} + +static inline void * radeon_sa_bo_cpu_addr(struct radeon_sa_bo *sa_bo) +{ + return sa_bo->manager->cpu_ptr + sa_bo->soffset; +} + extern int radeon_sa_bo_manager_init(struct radeon_device *rdev, struct radeon_sa_manager *sa_manager, unsigned size, u32 domain); @@ -157,9 +168,15 @@ extern int radeon_sa_bo_manager_suspend(struct radeon_device *rdev, struct radeon_sa_manager *sa_manager); extern int radeon_sa_bo_new(struct radeon_device *rdev, struct radeon_sa_manager *sa_manager, - struct radeon_sa_bo *sa_bo, - unsigned size, unsigned align); + struct radeon_sa_bo **sa_bo, + unsigned size, unsigned align, bool block); extern void radeon_sa_bo_free(struct radeon_device *rdev, - struct radeon_sa_bo *sa_bo); + struct radeon_sa_bo **sa_bo, + struct radeon_fence *fence); +#if defined(CONFIG_DEBUG_FS) +extern void radeon_sa_bo_dump_debug_info(struct radeon_sa_manager *sa_manager, + struct seq_file *m); +#endif + #endif diff --git a/drivers/gpu/drm/radeon/radeon_pm.c b/drivers/gpu/drm/radeon/radeon_pm.c index caa55d6..0882554 100644 --- a/drivers/gpu/drm/radeon/radeon_pm.c +++ b/drivers/gpu/drm/radeon/radeon_pm.c @@ -252,10 +252,7 @@ static void radeon_pm_set_clocks(struct radeon_device *rdev) mutex_lock(&rdev->ddev->struct_mutex); mutex_lock(&rdev->vram_mutex); - for (i = 0; i < RADEON_NUM_RINGS; ++i) { - if (rdev->ring[i].ring_obj) - mutex_lock(&rdev->ring[i].mutex); - } + mutex_lock(&rdev->ring_lock); /* gui idle int has issues on older chips it seems */ if (rdev->family >= CHIP_R600) { @@ -273,13 +270,7 @@ static void radeon_pm_set_clocks(struct radeon_device *rdev) } else { struct radeon_ring *ring = &rdev->ring[RADEON_RING_TYPE_GFX_INDEX]; if (ring->ready) { - struct radeon_fence *fence; - radeon_ring_alloc(rdev, ring, 64); - radeon_fence_create(rdev, &fence, radeon_ring_index(rdev, ring)); - radeon_fence_emit(rdev, fence); - radeon_ring_commit(rdev, ring); - radeon_fence_wait(fence, false); - radeon_fence_unref(&fence); + radeon_fence_wait_empty_locked(rdev, RADEON_RING_TYPE_GFX_INDEX); } } radeon_unmap_vram_bos(rdev); @@ -311,10 +302,7 @@ static void radeon_pm_set_clocks(struct radeon_device *rdev) rdev->pm.dynpm_planned_action = DYNPM_ACTION_NONE; - for (i = 0; i < RADEON_NUM_RINGS; ++i) { - if (rdev->ring[i].ring_obj) - mutex_unlock(&rdev->ring[i].mutex); - } + mutex_unlock(&rdev->ring_lock); mutex_unlock(&rdev->vram_mutex); mutex_unlock(&rdev->ddev->struct_mutex); } diff --git a/drivers/gpu/drm/radeon/radeon_ring.c b/drivers/gpu/drm/radeon/radeon_ring.c index 2eb4c6e..a5dee76 100644 --- a/drivers/gpu/drm/radeon/radeon_ring.c +++ b/drivers/gpu/drm/radeon/radeon_ring.c @@ -24,6 +24,7 @@ * Authors: Dave Airlie * Alex Deucher * Jerome Glisse + * Christian König */ #include <linux/seq_file.h> #include <linux/slab.h> @@ -33,8 +34,10 @@ #include "radeon.h" #include "atom.h" -int radeon_debugfs_ib_init(struct radeon_device *rdev); -int radeon_debugfs_ring_init(struct radeon_device *rdev, struct radeon_ring *ring); +/* + * IB. + */ +int radeon_debugfs_sa_init(struct radeon_device *rdev); u32 radeon_get_ib_value(struct radeon_cs_parser *p, int idx) { @@ -61,123 +64,37 @@ u32 radeon_get_ib_value(struct radeon_cs_parser *p, int idx) return idx_value; } -void radeon_ring_write(struct radeon_ring *ring, uint32_t v) -{ -#if DRM_DEBUG_CODE - if (ring->count_dw <= 0) { - DRM_ERROR("radeon: writting more dword to ring than expected !\n"); - } -#endif - ring->ring[ring->wptr++] = v; - ring->wptr &= ring->ptr_mask; - ring->count_dw--; - ring->ring_free_dw--; -} - -/* - * IB. - */ -bool radeon_ib_try_free(struct radeon_device *rdev, struct radeon_ib *ib) -{ - bool done = false; - - /* only free ib which have been emited */ - if (ib->fence && ib->fence->emitted) { - if (radeon_fence_signaled(ib->fence)) { - radeon_fence_unref(&ib->fence); - radeon_sa_bo_free(rdev, &ib->sa_bo); - done = true; - } - } - return done; -} - int radeon_ib_get(struct radeon_device *rdev, int ring, - struct radeon_ib **ib, unsigned size) + struct radeon_ib *ib, unsigned size) { - struct radeon_fence *fence; - unsigned cretry = 0; - int r = 0, i, idx; - - *ib = NULL; - /* align size on 256 bytes */ - size = ALIGN(size, 256); + int r; - r = radeon_fence_create(rdev, &fence, ring); + r = radeon_sa_bo_new(rdev, &rdev->ring_tmp_bo, &ib->sa_bo, size, 256, true); if (r) { - dev_err(rdev->dev, "failed to create fence for new IB\n"); + dev_err(rdev->dev, "failed to get a new IB (%d)\n", r); return r; } - - radeon_mutex_lock(&rdev->ib_pool.mutex); - idx = rdev->ib_pool.head_id; -retry: - if (cretry > 5) { - dev_err(rdev->dev, "failed to get an ib after 5 retry\n"); - radeon_mutex_unlock(&rdev->ib_pool.mutex); - radeon_fence_unref(&fence); - return -ENOMEM; - } - cretry++; - for (i = 0; i < RADEON_IB_POOL_SIZE; i++) { - radeon_ib_try_free(rdev, &rdev->ib_pool.ibs[idx]); - if (rdev->ib_pool.ibs[idx].fence == NULL) { - r = radeon_sa_bo_new(rdev, &rdev->ib_pool.sa_manager, - &rdev->ib_pool.ibs[idx].sa_bo, - size, 256); - if (!r) { - *ib = &rdev->ib_pool.ibs[idx]; - (*ib)->ptr = rdev->ib_pool.sa_manager.cpu_ptr; - (*ib)->ptr += ((*ib)->sa_bo.offset >> 2); - (*ib)->gpu_addr = rdev->ib_pool.sa_manager.gpu_addr; - (*ib)->gpu_addr += (*ib)->sa_bo.offset; - (*ib)->fence = fence; - (*ib)->vm_id = 0; - (*ib)->is_const_ib = false; - /* ib are most likely to be allocated in a ring fashion - * thus rdev->ib_pool.head_id should be the id of the - * oldest ib - */ - rdev->ib_pool.head_id = (1 + idx); - rdev->ib_pool.head_id &= (RADEON_IB_POOL_SIZE - 1); - radeon_mutex_unlock(&rdev->ib_pool.mutex); - return 0; - } - } - idx = (idx + 1) & (RADEON_IB_POOL_SIZE - 1); - } - /* this should be rare event, ie all ib scheduled none signaled yet. - */ - for (i = 0; i < RADEON_IB_POOL_SIZE; i++) { - if (rdev->ib_pool.ibs[idx].fence && rdev->ib_pool.ibs[idx].fence->emitted) { - r = radeon_fence_wait(rdev->ib_pool.ibs[idx].fence, false); - if (!r) { - goto retry; - } - /* an error happened */ - break; - } - idx = (idx + 1) & (RADEON_IB_POOL_SIZE - 1); + r = radeon_fence_create(rdev, &ib->fence, ring); + if (r) { + dev_err(rdev->dev, "failed to create fence for new IB (%d)\n", r); + radeon_sa_bo_free(rdev, &ib->sa_bo, NULL); + return r; } - radeon_mutex_unlock(&rdev->ib_pool.mutex); - radeon_fence_unref(&fence); - return r; + + ib->ptr = radeon_sa_bo_cpu_addr(ib->sa_bo); + ib->gpu_addr = radeon_sa_bo_gpu_addr(ib->sa_bo); + ib->vm_id = 0; + ib->is_const_ib = false; + ib->semaphore = NULL; + + return 0; } -void radeon_ib_free(struct radeon_device *rdev, struct radeon_ib **ib) +void radeon_ib_free(struct radeon_device *rdev, struct radeon_ib *ib) { - struct radeon_ib *tmp = *ib; - - *ib = NULL; - if (tmp == NULL) { - return; - } - radeon_mutex_lock(&rdev->ib_pool.mutex); - if (tmp->fence && !tmp->fence->emitted) { - radeon_sa_bo_free(rdev, &tmp->sa_bo); - radeon_fence_unref(&tmp->fence); - } - radeon_mutex_unlock(&rdev->ib_pool.mutex); + radeon_semaphore_free(rdev, ib->semaphore, ib->fence); + radeon_sa_bo_free(rdev, &ib->sa_bo, ib->fence); + radeon_fence_unref(&ib->fence); } int radeon_ib_schedule(struct radeon_device *rdev, struct radeon_ib *ib) @@ -187,14 +104,14 @@ int radeon_ib_schedule(struct radeon_device *rdev, struct radeon_ib *ib) if (!ib->length_dw || !ring->ready) { /* TODO: Nothings in the ib we should report. */ - DRM_ERROR("radeon: couldn't schedule IB(%u).\n", ib->idx); + dev_err(rdev->dev, "couldn't schedule ib\n"); return -EINVAL; } /* 64 dwords should be enough for fence too */ r = radeon_ring_lock(rdev, ring, 64); if (r) { - DRM_ERROR("radeon: scheduling IB failed (%d).\n", r); + dev_err(rdev->dev, "scheduling IB failed (%d).\n", r); return r; } radeon_ring_ib_execute(rdev, ib->fence->ring, ib); @@ -205,66 +122,40 @@ int radeon_ib_schedule(struct radeon_device *rdev, struct radeon_ib *ib) int radeon_ib_pool_init(struct radeon_device *rdev) { - struct radeon_sa_manager tmp; - int i, r; + int r; - r = radeon_sa_bo_manager_init(rdev, &tmp, + if (rdev->ib_pool_ready) { + return 0; + } + r = radeon_sa_bo_manager_init(rdev, &rdev->ring_tmp_bo, RADEON_IB_POOL_SIZE*64*1024, RADEON_GEM_DOMAIN_GTT); if (r) { return r; } - - radeon_mutex_lock(&rdev->ib_pool.mutex); - if (rdev->ib_pool.ready) { - radeon_mutex_unlock(&rdev->ib_pool.mutex); - radeon_sa_bo_manager_fini(rdev, &tmp); - return 0; + rdev->ib_pool_ready = true; + if (radeon_debugfs_sa_init(rdev)) { + dev_err(rdev->dev, "failed to register debugfs file for SA\n"); } - - rdev->ib_pool.sa_manager = tmp; - INIT_LIST_HEAD(&rdev->ib_pool.sa_manager.sa_bo); - for (i = 0; i < RADEON_IB_POOL_SIZE; i++) { - rdev->ib_pool.ibs[i].fence = NULL; - rdev->ib_pool.ibs[i].idx = i; - rdev->ib_pool.ibs[i].length_dw = 0; - INIT_LIST_HEAD(&rdev->ib_pool.ibs[i].sa_bo.list); - } - rdev->ib_pool.head_id = 0; - rdev->ib_pool.ready = true; - DRM_INFO("radeon: ib pool ready.\n"); - - if (radeon_debugfs_ib_init(rdev)) { - DRM_ERROR("Failed to register debugfs file for IB !\n"); - } - radeon_mutex_unlock(&rdev->ib_pool.mutex); return 0; } void radeon_ib_pool_fini(struct radeon_device *rdev) { - unsigned i; - - radeon_mutex_lock(&rdev->ib_pool.mutex); - if (rdev->ib_pool.ready) { - for (i = 0; i < RADEON_IB_POOL_SIZE; i++) { - radeon_sa_bo_free(rdev, &rdev->ib_pool.ibs[i].sa_bo); - radeon_fence_unref(&rdev->ib_pool.ibs[i].fence); - } - radeon_sa_bo_manager_fini(rdev, &rdev->ib_pool.sa_manager); - rdev->ib_pool.ready = false; + if (rdev->ib_pool_ready) { + radeon_sa_bo_manager_fini(rdev, &rdev->ring_tmp_bo); + rdev->ib_pool_ready = false; } - radeon_mutex_unlock(&rdev->ib_pool.mutex); } int radeon_ib_pool_start(struct radeon_device *rdev) { - return radeon_sa_bo_manager_start(rdev, &rdev->ib_pool.sa_manager); + return radeon_sa_bo_manager_start(rdev, &rdev->ring_tmp_bo); } int radeon_ib_pool_suspend(struct radeon_device *rdev) { - return radeon_sa_bo_manager_suspend(rdev, &rdev->ib_pool.sa_manager); + return radeon_sa_bo_manager_suspend(rdev, &rdev->ring_tmp_bo); } int radeon_ib_ring_tests(struct radeon_device *rdev) @@ -300,6 +191,21 @@ int radeon_ib_ring_tests(struct radeon_device *rdev) /* * Ring. */ +int radeon_debugfs_ring_init(struct radeon_device *rdev, struct radeon_ring *ring); + +void radeon_ring_write(struct radeon_ring *ring, uint32_t v) +{ +#if DRM_DEBUG_CODE + if (ring->count_dw <= 0) { + DRM_ERROR("radeon: writting more dword to ring than expected !\n"); + } +#endif + ring->ring[ring->wptr++] = v; + ring->wptr &= ring->ptr_mask; + ring->count_dw--; + ring->ring_free_dw--; +} + int radeon_ring_index(struct radeon_device *rdev, struct radeon_ring *ring) { /* r1xx-r5xx only has CP ring */ @@ -346,9 +252,7 @@ int radeon_ring_alloc(struct radeon_device *rdev, struct radeon_ring *ring, unsi if (ndw < ring->ring_free_dw) { break; } - mutex_unlock(&ring->mutex); - r = radeon_fence_wait_next(rdev, radeon_ring_index(rdev, ring)); - mutex_lock(&ring->mutex); + r = radeon_fence_wait_next_locked(rdev, radeon_ring_index(rdev, ring)); if (r) return r; } @@ -361,10 +265,10 @@ int radeon_ring_lock(struct radeon_device *rdev, struct radeon_ring *ring, unsig { int r; - mutex_lock(&ring->mutex); + mutex_lock(&rdev->ring_lock); r = radeon_ring_alloc(rdev, ring, ndw); if (r) { - mutex_unlock(&ring->mutex); + mutex_unlock(&rdev->ring_lock); return r; } return 0; @@ -389,20 +293,24 @@ void radeon_ring_commit(struct radeon_device *rdev, struct radeon_ring *ring) void radeon_ring_unlock_commit(struct radeon_device *rdev, struct radeon_ring *ring) { radeon_ring_commit(rdev, ring); - mutex_unlock(&ring->mutex); + mutex_unlock(&rdev->ring_lock); } -void radeon_ring_unlock_undo(struct radeon_device *rdev, struct radeon_ring *ring) +void radeon_ring_undo(struct radeon_ring *ring) { ring->wptr = ring->wptr_old; - mutex_unlock(&ring->mutex); +} + +void radeon_ring_unlock_undo(struct radeon_device *rdev, struct radeon_ring *ring) +{ + radeon_ring_undo(ring); + mutex_unlock(&rdev->ring_lock); } void radeon_ring_force_activity(struct radeon_device *rdev, struct radeon_ring *ring) { int r; - mutex_lock(&ring->mutex); radeon_ring_free_size(rdev, ring); if (ring->rptr == ring->wptr) { r = radeon_ring_alloc(rdev, ring, 1); @@ -411,7 +319,6 @@ void radeon_ring_force_activity(struct radeon_device *rdev, struct radeon_ring * radeon_ring_commit(rdev, ring); } } - mutex_unlock(&ring->mutex); } void radeon_ring_lockup_update(struct radeon_ring *ring) @@ -520,11 +427,12 @@ void radeon_ring_fini(struct radeon_device *rdev, struct radeon_ring *ring) int r; struct radeon_bo *ring_obj; - mutex_lock(&ring->mutex); + mutex_lock(&rdev->ring_lock); ring_obj = ring->ring_obj; + ring->ready = false; ring->ring = NULL; ring->ring_obj = NULL; - mutex_unlock(&ring->mutex); + mutex_unlock(&rdev->ring_lock); if (ring_obj) { r = radeon_bo_reserve(ring_obj, false); @@ -577,29 +485,22 @@ static struct drm_info_list radeon_debugfs_ring_info_list[] = { {"radeon_ring_cp2", radeon_debugfs_ring_info, 0, &cayman_ring_type_cp2_index}, }; -static int radeon_debugfs_ib_info(struct seq_file *m, void *data) +static int radeon_debugfs_sa_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 radeon_device *rdev = dev->dev_private; - struct radeon_ib *ib = &rdev->ib_pool.ibs[*((unsigned*)node->info_ent->data)]; - unsigned i; - if (ib == NULL) { - return 0; - } - seq_printf(m, "IB %04u\n", ib->idx); - seq_printf(m, "IB fence %p\n", ib->fence); - seq_printf(m, "IB size %05u dwords\n", ib->length_dw); - for (i = 0; i < ib->length_dw; i++) { - seq_printf(m, "[%05u]=0x%08X\n", i, ib->ptr[i]); - } + radeon_sa_bo_dump_debug_info(&rdev->ring_tmp_bo, m); + return 0; + } -static struct drm_info_list radeon_debugfs_ib_list[RADEON_IB_POOL_SIZE]; -static char radeon_debugfs_ib_names[RADEON_IB_POOL_SIZE][32]; -static unsigned radeon_debugfs_ib_idx[RADEON_IB_POOL_SIZE]; +static struct drm_info_list radeon_debugfs_sa_list[] = { + {"radeon_sa_info", &radeon_debugfs_sa_info, 0, NULL}, +}; + #endif int radeon_debugfs_ring_init(struct radeon_device *rdev, struct radeon_ring *ring) @@ -622,21 +523,10 @@ int radeon_debugfs_ring_init(struct radeon_device *rdev, struct radeon_ring *rin return 0; } -int radeon_debugfs_ib_init(struct radeon_device *rdev) +int radeon_debugfs_sa_init(struct radeon_device *rdev) { #if defined(CONFIG_DEBUG_FS) - unsigned i; - - for (i = 0; i < RADEON_IB_POOL_SIZE; i++) { - sprintf(radeon_debugfs_ib_names[i], "radeon_ib_%04u", i); - radeon_debugfs_ib_idx[i] = i; - radeon_debugfs_ib_list[i].name = radeon_debugfs_ib_names[i]; - radeon_debugfs_ib_list[i].show = &radeon_debugfs_ib_info; - radeon_debugfs_ib_list[i].driver_features = 0; - radeon_debugfs_ib_list[i].data = &radeon_debugfs_ib_idx[i]; - } - return radeon_debugfs_add_files(rdev, radeon_debugfs_ib_list, - RADEON_IB_POOL_SIZE); + return radeon_debugfs_add_files(rdev, radeon_debugfs_sa_list, 1); #else return 0; #endif diff --git a/drivers/gpu/drm/radeon/radeon_sa.c b/drivers/gpu/drm/radeon/radeon_sa.c index 8fbfe69..c3ac7f4 100644 --- a/drivers/gpu/drm/radeon/radeon_sa.c +++ b/drivers/gpu/drm/radeon/radeon_sa.c @@ -27,20 +27,42 @@ * Authors: * Jerome Glisse <glisse@freedesktop.org> */ +/* Algorithm: + * + * We store the last allocated bo in "hole", we always try to allocate + * after the last allocated bo. Principle is that in a linear GPU ring + * progression was is after last is the oldest bo we allocated and thus + * the first one that should no longer be in use by the GPU. + * + * If it's not the case we skip over the bo after last to the closest + * done bo if such one exist. If none exist and we are not asked to + * block we report failure to allocate. + * + * If we are asked to block we wait on all the oldest fence of all + * rings. We just wait for any of those fence to complete. + */ #include "drmP.h" #include "drm.h" #include "radeon.h" +static void radeon_sa_bo_remove_locked(struct radeon_sa_bo *sa_bo); +static void radeon_sa_bo_try_free(struct radeon_sa_manager *sa_manager); + int radeon_sa_bo_manager_init(struct radeon_device *rdev, struct radeon_sa_manager *sa_manager, unsigned size, u32 domain) { - int r; + int i, r; + spin_lock_init(&sa_manager->lock); sa_manager->bo = NULL; sa_manager->size = size; sa_manager->domain = domain; - INIT_LIST_HEAD(&sa_manager->sa_bo); + sa_manager->hole = &sa_manager->olist; + INIT_LIST_HEAD(&sa_manager->olist); + for (i = 0; i < RADEON_NUM_RINGS; ++i) { + INIT_LIST_HEAD(&sa_manager->flist[i]); + } r = radeon_bo_create(rdev, size, RADEON_GPU_PAGE_SIZE, true, RADEON_GEM_DOMAIN_CPU, &sa_manager->bo); @@ -57,11 +79,15 @@ void radeon_sa_bo_manager_fini(struct radeon_device *rdev, { struct radeon_sa_bo *sa_bo, *tmp; - if (!list_empty(&sa_manager->sa_bo)) { - dev_err(rdev->dev, "sa_manager is not empty, clearing anyway\n"); + if (!list_empty(&sa_manager->olist)) { + sa_manager->hole = &sa_manager->olist, + radeon_sa_bo_try_free(sa_manager); + if (!list_empty(&sa_manager->olist)) { + dev_err(rdev->dev, "sa_manager is not empty, clearing anyway\n"); + } } - list_for_each_entry_safe(sa_bo, tmp, &sa_manager->sa_bo, list) { - list_del_init(&sa_bo->list); + list_for_each_entry_safe(sa_bo, tmp, &sa_manager->olist, olist) { + radeon_sa_bo_remove_locked(sa_bo); } radeon_bo_unref(&sa_manager->bo); sa_manager->size = 0; @@ -113,77 +139,248 @@ int radeon_sa_bo_manager_suspend(struct radeon_device *rdev, return r; } -/* - * Principe is simple, we keep a list of sub allocation in offset - * order (first entry has offset == 0, last entry has the highest - * offset). - * - * When allocating new object we first check if there is room at - * the end total_size - (last_object_offset + last_object_size) >= - * alloc_size. If so we allocate new object there. - * - * When there is not enough room at the end, we start waiting for - * each sub object until we reach object_offset+object_size >= - * alloc_size, this object then become the sub object we return. - * - * Alignment can't be bigger than page size - */ +static void radeon_sa_bo_remove_locked(struct radeon_sa_bo *sa_bo) +{ + struct radeon_sa_manager *sa_manager = sa_bo->manager; + if (sa_manager->hole == &sa_bo->olist) { + sa_manager->hole = sa_bo->olist.prev; + } + list_del_init(&sa_bo->olist); + list_del_init(&sa_bo->flist); + radeon_fence_unref(&sa_bo->fence); + kfree(sa_bo); +} + +static void radeon_sa_bo_try_free(struct radeon_sa_manager *sa_manager) +{ + struct radeon_sa_bo *sa_bo, *tmp; + + if (sa_manager->hole->next == &sa_manager->olist) + return; + + sa_bo = list_entry(sa_manager->hole->next, struct radeon_sa_bo, olist); + list_for_each_entry_safe_from(sa_bo, tmp, &sa_manager->olist, olist) { + if (sa_bo->fence == NULL || !radeon_fence_signaled(sa_bo->fence)) { + return; + } + radeon_sa_bo_remove_locked(sa_bo); + } +} + +static inline unsigned radeon_sa_bo_hole_soffset(struct radeon_sa_manager *sa_manager) +{ + struct list_head *hole = sa_manager->hole; + + if (hole != &sa_manager->olist) { + return list_entry(hole, struct radeon_sa_bo, olist)->eoffset; + } + return 0; +} + +static inline unsigned radeon_sa_bo_hole_eoffset(struct radeon_sa_manager *sa_manager) +{ + struct list_head *hole = sa_manager->hole; + + if (hole->next != &sa_manager->olist) { + return list_entry(hole->next, struct radeon_sa_bo, olist)->soffset; + } + return sa_manager->size; +} + +static bool radeon_sa_bo_try_alloc(struct radeon_sa_manager *sa_manager, + struct radeon_sa_bo *sa_bo, + unsigned size, unsigned align) +{ + unsigned soffset, eoffset, wasted; + + soffset = radeon_sa_bo_hole_soffset(sa_manager); + eoffset = radeon_sa_bo_hole_eoffset(sa_manager); + wasted = (align - (soffset % align)) % align; + + if ((eoffset - soffset) >= (size + wasted)) { + soffset += wasted; + + sa_bo->manager = sa_manager; + sa_bo->soffset = soffset; + sa_bo->eoffset = soffset + size; + list_add(&sa_bo->olist, sa_manager->hole); + INIT_LIST_HEAD(&sa_bo->flist); + sa_manager->hole = &sa_bo->olist; + return true; + } + return false; +} + +static bool radeon_sa_bo_next_hole(struct radeon_sa_manager *sa_manager, + struct radeon_fence **fences, + unsigned *tries) +{ + struct radeon_sa_bo *best_bo = NULL; + unsigned i, soffset, best, tmp; + + /* if hole points to the end of the buffer */ + if (sa_manager->hole->next == &sa_manager->olist) { + /* try again with its beginning */ + sa_manager->hole = &sa_manager->olist; + return true; + } + + soffset = radeon_sa_bo_hole_soffset(sa_manager); + /* to handle wrap around we add sa_manager->size */ + best = sa_manager->size * 2; + /* go over all fence list and try to find the closest sa_bo + * of the current last + */ + for (i = 0; i < RADEON_NUM_RINGS; ++i) { + struct radeon_sa_bo *sa_bo; + + if (list_empty(&sa_manager->flist[i])) { + continue; + } + + sa_bo = list_first_entry(&sa_manager->flist[i], + struct radeon_sa_bo, flist); + + if (!radeon_fence_signaled(sa_bo->fence)) { + fences[i] = sa_bo->fence; + continue; + } + + /* limit the number of tries each ring gets */ + if (tries[i] > 2) { + continue; + } + + tmp = sa_bo->soffset; + if (tmp < soffset) { + /* wrap around, pretend it's after */ + tmp += sa_manager->size; + } + tmp -= soffset; + if (tmp < best) { + /* this sa bo is the closest one */ + best = tmp; + best_bo = sa_bo; + } + } + + if (best_bo) { + ++tries[best_bo->fence->ring]; + sa_manager->hole = best_bo->olist.prev; + + /* we knew that this one is signaled, + so it's save to remote it */ + radeon_sa_bo_remove_locked(best_bo); + return true; + } + return false; +} + int radeon_sa_bo_new(struct radeon_device *rdev, struct radeon_sa_manager *sa_manager, - struct radeon_sa_bo *sa_bo, - unsigned size, unsigned align) + struct radeon_sa_bo **sa_bo, + unsigned size, unsigned align, bool block) { - struct radeon_sa_bo *tmp; - struct list_head *head; - unsigned offset = 0, wasted = 0; + struct radeon_fence *fences[RADEON_NUM_RINGS]; + unsigned tries[RADEON_NUM_RINGS]; + int i, r = -ENOMEM; BUG_ON(align > RADEON_GPU_PAGE_SIZE); BUG_ON(size > sa_manager->size); - /* no one ? */ - head = sa_manager->sa_bo.prev; - if (list_empty(&sa_manager->sa_bo)) { - goto out; + *sa_bo = kmalloc(sizeof(struct radeon_sa_bo), GFP_KERNEL); + if ((*sa_bo) == NULL) { + return -ENOMEM; } + (*sa_bo)->manager = sa_manager; + (*sa_bo)->fence = NULL; + INIT_LIST_HEAD(&(*sa_bo)->olist); + INIT_LIST_HEAD(&(*sa_bo)->flist); - /* look for a hole big enough */ - offset = 0; - list_for_each_entry(tmp, &sa_manager->sa_bo, list) { - /* room before this object ? */ - if (offset < tmp->offset && (tmp->offset - offset) >= size) { - head = tmp->list.prev; - goto out; + spin_lock(&sa_manager->lock); + do { + for (i = 0; i < RADEON_NUM_RINGS; ++i) { + fences[i] = NULL; + tries[i] = 0; } - offset = tmp->offset + tmp->size; - wasted = offset % align; - if (wasted) { - wasted = align - wasted; + + do { + radeon_sa_bo_try_free(sa_manager); + + if (radeon_sa_bo_try_alloc(sa_manager, *sa_bo, + size, align)) { + spin_unlock(&sa_manager->lock); + return 0; + } + + /* see if we can skip over some allocations */ + } while (radeon_sa_bo_next_hole(sa_manager, fences, tries)); + + if (block) { + spin_unlock(&sa_manager->lock); + r = radeon_fence_wait_any(rdev, fences, false); + spin_lock(&sa_manager->lock); + if (r) { + /* if we have nothing to wait for we + are practically out of memory */ + if (r == -ENOENT) { + r = -ENOMEM; + } + goto out_err; + } } - offset += wasted; - } - /* room at the end ? */ - head = sa_manager->sa_bo.prev; - tmp = list_entry(head, struct radeon_sa_bo, list); - offset = tmp->offset + tmp->size; - wasted = offset % align; - if (wasted) { - wasted = align - wasted; - } - offset += wasted; - if ((sa_manager->size - offset) < size) { - /* failed to find somethings big enough */ - return -ENOMEM; + } while (block); + +out_err: + spin_unlock(&sa_manager->lock); + kfree(*sa_bo); + *sa_bo = NULL; + return r; +} + +void radeon_sa_bo_free(struct radeon_device *rdev, struct radeon_sa_bo **sa_bo, + struct radeon_fence *fence) +{ + struct radeon_sa_manager *sa_manager; + + if (sa_bo == NULL || *sa_bo == NULL) { + return; } -out: - sa_bo->manager = sa_manager; - sa_bo->offset = offset; - sa_bo->size = size; - list_add(&sa_bo->list, head); - return 0; + sa_manager = (*sa_bo)->manager; + spin_lock(&sa_manager->lock); + if (fence && fence->seq && fence->seq < RADEON_FENCE_NOTEMITED_SEQ) { + (*sa_bo)->fence = radeon_fence_ref(fence); + list_add_tail(&(*sa_bo)->flist, + &sa_manager->flist[fence->ring]); + } else { + radeon_sa_bo_remove_locked(*sa_bo); + } + spin_unlock(&sa_manager->lock); + *sa_bo = NULL; } -void radeon_sa_bo_free(struct radeon_device *rdev, struct radeon_sa_bo *sa_bo) +#if defined(CONFIG_DEBUG_FS) +void radeon_sa_bo_dump_debug_info(struct radeon_sa_manager *sa_manager, + struct seq_file *m) { - list_del_init(&sa_bo->list); + struct radeon_sa_bo *i; + + spin_lock(&sa_manager->lock); + list_for_each_entry(i, &sa_manager->olist, olist) { + if (&i->olist == sa_manager->hole) { + seq_printf(m, ">"); + } else { + seq_printf(m, " "); + } + seq_printf(m, "[0x%08x 0x%08x] size %8d", + i->soffset, i->eoffset, i->eoffset - i->soffset); + if (i->fence) { + seq_printf(m, " protected by 0x%016llx on ring %d", + i->fence->seq, i->fence->ring); + } + seq_printf(m, "\n"); + } + spin_unlock(&sa_manager->lock); } +#endif diff --git a/drivers/gpu/drm/radeon/radeon_semaphore.c b/drivers/gpu/drm/radeon/radeon_semaphore.c index 930a08a..e2ace5d 100644 --- a/drivers/gpu/drm/radeon/radeon_semaphore.c +++ b/drivers/gpu/drm/radeon/radeon_semaphore.c @@ -31,121 +31,40 @@ #include "drm.h" #include "radeon.h" -static int radeon_semaphore_add_bo(struct radeon_device *rdev) -{ - struct radeon_semaphore_bo *bo; - unsigned long irq_flags; - uint64_t gpu_addr; - uint32_t *cpu_ptr; - int r, i; - - - bo = kmalloc(sizeof(struct radeon_semaphore_bo), GFP_KERNEL); - if (bo == NULL) { - return -ENOMEM; - } - INIT_LIST_HEAD(&bo->free); - INIT_LIST_HEAD(&bo->list); - bo->nused = 0; - - r = radeon_ib_get(rdev, 0, &bo->ib, RADEON_SEMAPHORE_BO_SIZE); - if (r) { - dev_err(rdev->dev, "failed to get a bo after 5 retry\n"); - kfree(bo); - return r; - } - gpu_addr = rdev->ib_pool.sa_manager.gpu_addr; - gpu_addr += bo->ib->sa_bo.offset; - cpu_ptr = rdev->ib_pool.sa_manager.cpu_ptr; - cpu_ptr += (bo->ib->sa_bo.offset >> 2); - for (i = 0; i < (RADEON_SEMAPHORE_BO_SIZE/8); i++) { - bo->semaphores[i].gpu_addr = gpu_addr; - bo->semaphores[i].cpu_ptr = cpu_ptr; - bo->semaphores[i].bo = bo; - list_add_tail(&bo->semaphores[i].list, &bo->free); - gpu_addr += 8; - cpu_ptr += 2; - } - write_lock_irqsave(&rdev->semaphore_drv.lock, irq_flags); - list_add_tail(&bo->list, &rdev->semaphore_drv.bo); - write_unlock_irqrestore(&rdev->semaphore_drv.lock, irq_flags); - return 0; -} - -static void radeon_semaphore_del_bo_locked(struct radeon_device *rdev, - struct radeon_semaphore_bo *bo) -{ - radeon_sa_bo_free(rdev, &bo->ib->sa_bo); - radeon_fence_unref(&bo->ib->fence); - list_del(&bo->list); - kfree(bo); -} - -void radeon_semaphore_shrink_locked(struct radeon_device *rdev) -{ - struct radeon_semaphore_bo *bo, *n; - - if (list_empty(&rdev->semaphore_drv.bo)) { - return; - } - /* only shrink if first bo has free semaphore */ - bo = list_first_entry(&rdev->semaphore_drv.bo, struct radeon_semaphore_bo, list); - if (list_empty(&bo->free)) { - return; - } - list_for_each_entry_safe_continue(bo, n, &rdev->semaphore_drv.bo, list) { - if (bo->nused) - continue; - radeon_semaphore_del_bo_locked(rdev, bo); - } -} int radeon_semaphore_create(struct radeon_device *rdev, struct radeon_semaphore **semaphore) { - struct radeon_semaphore_bo *bo; - unsigned long irq_flags; - bool do_retry = true; int r; -retry: - *semaphore = NULL; - write_lock_irqsave(&rdev->semaphore_drv.lock, irq_flags); - list_for_each_entry(bo, &rdev->semaphore_drv.bo, list) { - if (list_empty(&bo->free)) - continue; - *semaphore = list_first_entry(&bo->free, struct radeon_semaphore, list); - (*semaphore)->cpu_ptr[0] = 0; - (*semaphore)->cpu_ptr[1] = 0; - list_del(&(*semaphore)->list); - bo->nused++; - break; - } - write_unlock_irqrestore(&rdev->semaphore_drv.lock, irq_flags); - + *semaphore = kmalloc(sizeof(struct radeon_semaphore), GFP_KERNEL); if (*semaphore == NULL) { - if (do_retry) { - do_retry = false; - r = radeon_semaphore_add_bo(rdev); - if (r) - return r; - goto retry; - } return -ENOMEM; } - + r = radeon_sa_bo_new(rdev, &rdev->ring_tmp_bo, + &(*semaphore)->sa_bo, 8, 8, true); + if (r) { + kfree(*semaphore); + *semaphore = NULL; + return r; + } + (*semaphore)->waiters = 0; + (*semaphore)->gpu_addr = radeon_sa_bo_gpu_addr((*semaphore)->sa_bo); + *((uint64_t*)radeon_sa_bo_cpu_addr((*semaphore)->sa_bo)) = 0; return 0; } void radeon_semaphore_emit_signal(struct radeon_device *rdev, int ring, struct radeon_semaphore *semaphore) { + --semaphore->waiters; radeon_semaphore_ring_emit(rdev, ring, &rdev->ring[ring], semaphore, false); } void radeon_semaphore_emit_wait(struct radeon_device *rdev, int ring, struct radeon_semaphore *semaphore) { + ++semaphore->waiters; radeon_semaphore_ring_emit(rdev, ring, &rdev->ring[ring], semaphore, true); } @@ -154,13 +73,17 @@ int radeon_semaphore_sync_rings(struct radeon_device *rdev, bool sync_to[RADEON_NUM_RINGS], int dst_ring) { - int i, r; + int i = 0, r; - for (i = 0; i < RADEON_NUM_RINGS; ++i) { - unsigned num_ops = i == dst_ring ? RADEON_NUM_RINGS : 1; + mutex_lock(&rdev->ring_lock); + r = radeon_ring_alloc(rdev, &rdev->ring[dst_ring], RADEON_NUM_RINGS * 8); + if (r) { + goto error; + } - /* don't lock unused rings */ - if (!sync_to[i] && i != dst_ring) + for (i = 0; i < RADEON_NUM_RINGS; ++i) { + /* no need to sync to our own or unused rings */ + if (!sync_to[i] || i == dst_ring) continue; /* prevent GPU deadlocks */ @@ -170,65 +93,45 @@ int radeon_semaphore_sync_rings(struct radeon_device *rdev, goto error; } - r = radeon_ring_lock(rdev, &rdev->ring[i], num_ops * 8); - if (r) + r = radeon_ring_alloc(rdev, &rdev->ring[i], 8); + if (r) { goto error; - } - - for (i = 0; i < RADEON_NUM_RINGS; ++i) { - /* no need to sync to our own or unused rings */ - if (!sync_to[i] || i == dst_ring) - continue; + } radeon_semaphore_emit_signal(rdev, i, semaphore); radeon_semaphore_emit_wait(rdev, dst_ring, semaphore); - } - - for (i = 0; i < RADEON_NUM_RINGS; ++i) { - /* don't unlock unused rings */ - if (!sync_to[i] && i != dst_ring) - continue; - - radeon_ring_unlock_commit(rdev, &rdev->ring[i]); + radeon_ring_commit(rdev, &rdev->ring[i]); } + radeon_ring_commit(rdev, &rdev->ring[dst_ring]); + mutex_unlock(&rdev->ring_lock); + return 0; error: /* unlock all locks taken so far */ for (--i; i >= 0; --i) { if (sync_to[i] || i == dst_ring) { - radeon_ring_unlock_undo(rdev, &rdev->ring[i]); + radeon_ring_undo(&rdev->ring[i]); } } + radeon_ring_undo(&rdev->ring[dst_ring]); + mutex_unlock(&rdev->ring_lock); return r; } void radeon_semaphore_free(struct radeon_device *rdev, - struct radeon_semaphore *semaphore) + struct radeon_semaphore *semaphore, + struct radeon_fence *fence) { - unsigned long irq_flags; - - write_lock_irqsave(&rdev->semaphore_drv.lock, irq_flags); - semaphore->bo->nused--; - list_add_tail(&semaphore->list, &semaphore->bo->free); - radeon_semaphore_shrink_locked(rdev); - write_unlock_irqrestore(&rdev->semaphore_drv.lock, irq_flags); -} - -void radeon_semaphore_driver_fini(struct radeon_device *rdev) -{ - struct radeon_semaphore_bo *bo, *n; - unsigned long irq_flags; - - write_lock_irqsave(&rdev->semaphore_drv.lock, irq_flags); - /* we force to free everything */ - list_for_each_entry_safe(bo, n, &rdev->semaphore_drv.bo, list) { - if (!list_empty(&bo->free)) { - dev_err(rdev->dev, "still in use semaphore\n"); - } - radeon_semaphore_del_bo_locked(rdev, bo); + if (semaphore == NULL) { + return; + } + if (semaphore->waiters > 0) { + dev_err(rdev->dev, "semaphore %p has more waiters than signalers," + " hardware lockup imminent!\n", semaphore); } - write_unlock_irqrestore(&rdev->semaphore_drv.lock, irq_flags); + radeon_sa_bo_free(rdev, &semaphore->sa_bo, fence); + kfree(semaphore); } diff --git a/drivers/gpu/drm/radeon/radeon_test.c b/drivers/gpu/drm/radeon/radeon_test.c index dc5dcf4..b057387 100644 --- a/drivers/gpu/drm/radeon/radeon_test.c +++ b/drivers/gpu/drm/radeon/radeon_test.c @@ -317,7 +317,7 @@ void radeon_test_ring_sync(struct radeon_device *rdev, out_cleanup: if (semaphore) - radeon_semaphore_free(rdev, semaphore); + radeon_semaphore_free(rdev, semaphore, NULL); if (fence1) radeon_fence_unref(&fence1); @@ -437,7 +437,7 @@ void radeon_test_ring_sync2(struct radeon_device *rdev, out_cleanup: if (semaphore) - radeon_semaphore_free(rdev, semaphore); + radeon_semaphore_free(rdev, semaphore, NULL); if (fenceA) radeon_fence_unref(&fenceA); diff --git a/drivers/gpu/drm/radeon/radeon_ttm.c b/drivers/gpu/drm/radeon/radeon_ttm.c index 5e3d54d..0f6aee8 100644 --- a/drivers/gpu/drm/radeon/radeon_ttm.c +++ b/drivers/gpu/drm/radeon/radeon_ttm.c @@ -223,6 +223,7 @@ static int radeon_move_blit(struct ttm_buffer_object *bo, struct radeon_device *rdev; uint64_t old_start, new_start; struct radeon_fence *fence, *old_fence; + struct radeon_semaphore *sem = NULL; int r; rdev = radeon_get_rdev(bo->bdev); @@ -272,15 +273,16 @@ static int radeon_move_blit(struct ttm_buffer_object *bo, bool sync_to_ring[RADEON_NUM_RINGS] = { }; sync_to_ring[old_fence->ring] = true; - r = radeon_semaphore_create(rdev, &fence->semaphore); + r = radeon_semaphore_create(rdev, &sem); if (r) { radeon_fence_unref(&fence); return r; } - r = radeon_semaphore_sync_rings(rdev, fence->semaphore, + r = radeon_semaphore_sync_rings(rdev, sem, sync_to_ring, fence->ring); if (r) { + radeon_semaphore_free(rdev, sem, NULL); radeon_fence_unref(&fence); return r; } @@ -292,6 +294,7 @@ static int radeon_move_blit(struct ttm_buffer_object *bo, /* FIXME: handle copy error */ r = ttm_bo_move_accel_cleanup(bo, (void *)fence, NULL, evict, no_wait_reserve, no_wait_gpu, new_mem); + radeon_semaphore_free(rdev, sem, fence); radeon_fence_unref(&fence); return r; } diff --git a/drivers/gpu/drm/radeon/rv770.c b/drivers/gpu/drm/radeon/rv770.c index a8b0016..c2f473b 100644 --- a/drivers/gpu/drm/radeon/rv770.c +++ b/drivers/gpu/drm/radeon/rv770.c @@ -1175,10 +1175,6 @@ int rv770_init(struct radeon_device *rdev) { int r; - /* This don't do much */ - r = radeon_gem_init(rdev); - if (r) - return r; /* Read BIOS */ if (!radeon_get_bios(rdev)) { if (ASIC_IS_AVIVO(rdev)) @@ -1278,7 +1274,6 @@ void rv770_fini(struct radeon_device *rdev) rv770_pcie_gart_fini(rdev); r600_vram_scratch_fini(rdev); radeon_gem_fini(rdev); - radeon_semaphore_driver_fini(rdev); radeon_fence_driver_fini(rdev); radeon_agp_fini(rdev); radeon_bo_fini(rdev); diff --git a/drivers/gpu/drm/radeon/si.c b/drivers/gpu/drm/radeon/si.c index 779f0b6..63ae41d 100644 --- a/drivers/gpu/drm/radeon/si.c +++ b/drivers/gpu/drm/radeon/si.c @@ -2265,6 +2265,7 @@ static int si_gpu_soft_reset(struct radeon_device *rdev) SOFT_RESET_GDS | SOFT_RESET_PA | SOFT_RESET_SC | + SOFT_RESET_BCI | SOFT_RESET_SPI | SOFT_RESET_SX | SOFT_RESET_TC | @@ -3986,10 +3987,6 @@ int si_init(struct radeon_device *rdev) struct radeon_ring *ring = &rdev->ring[RADEON_RING_TYPE_GFX_INDEX]; int r; - /* This don't do much */ - r = radeon_gem_init(rdev); - if (r) - return r; /* Read BIOS */ if (!radeon_get_bios(rdev)) { if (ASIC_IS_AVIVO(rdev)) @@ -4109,7 +4106,6 @@ void si_fini(struct radeon_device *rdev) si_pcie_gart_fini(rdev); r600_vram_scratch_fini(rdev); radeon_gem_fini(rdev); - radeon_semaphore_driver_fini(rdev); radeon_fence_driver_fini(rdev); radeon_bo_fini(rdev); radeon_atombios_fini(rdev); diff --git a/drivers/gpu/drm/savage/savage_bci.c b/drivers/gpu/drm/savage/savage_bci.c index cb1ee4e..6eb507a 100644 --- a/drivers/gpu/drm/savage/savage_bci.c +++ b/drivers/gpu/drm/savage/savage_bci.c @@ -735,7 +735,7 @@ static int savage_do_init_bci(struct drm_device * dev, drm_savage_init_t * init) return -EINVAL; } drm_core_ioremap(dev->agp_buffer_map, dev); - if (!dev->agp_buffer_map) { + if (!dev->agp_buffer_map->handle) { DRM_ERROR("failed to ioremap DMA buffer region!\n"); savage_do_cleanup_bci(dev); return -ENOMEM; diff --git a/drivers/gpu/drm/udl/udl_drv.c b/drivers/gpu/drm/udl/udl_drv.c index 5367390..82e6921 100644 --- a/drivers/gpu/drm/udl/udl_drv.c +++ b/drivers/gpu/drm/udl/udl_drv.c @@ -38,7 +38,7 @@ static void udl_usb_disconnect(struct usb_interface *interface) drm_unplug_dev(dev); } -static struct vm_operations_struct udl_gem_vm_ops = { +static const struct vm_operations_struct udl_gem_vm_ops = { .fault = udl_gem_fault, .open = drm_gem_vm_open, .close = drm_gem_vm_close, diff --git a/drivers/gpu/drm/udl/udl_modeset.c b/drivers/gpu/drm/udl/udl_modeset.c index b3ecb3d..0d78167 100644 --- a/drivers/gpu/drm/udl/udl_modeset.c +++ b/drivers/gpu/drm/udl/udl_modeset.c @@ -395,7 +395,7 @@ int udl_modeset_init(struct drm_device *dev) dev->mode_config.prefer_shadow = 0; dev->mode_config.preferred_depth = 24; - dev->mode_config.funcs = (void *)&udl_mode_funcs; + dev->mode_config.funcs = &udl_mode_funcs; drm_mode_create_dirty_info_property(dev); diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c b/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c index 2286d47..6b0078f 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c @@ -1178,7 +1178,7 @@ err_out: return &vfb->base; } -static struct drm_mode_config_funcs vmw_kms_funcs = { +static const struct drm_mode_config_funcs vmw_kms_funcs = { .fb_create = vmw_kms_fb_create, }; diff --git a/drivers/gpu/vga/vga_switcheroo.c b/drivers/gpu/vga/vga_switcheroo.c index 9d83028..38f9534 100644 --- a/drivers/gpu/vga/vga_switcheroo.c +++ b/drivers/gpu/vga/vga_switcheroo.c @@ -34,11 +34,10 @@ struct vga_switcheroo_client { struct pci_dev *pdev; struct fb_info *fb_info; int pwr_state; - void (*set_gpu_state)(struct pci_dev *pdev, enum vga_switcheroo_state); - void (*reprobe)(struct pci_dev *pdev); - bool (*can_switch)(struct pci_dev *pdev); + const struct vga_switcheroo_client_ops *ops; int id; bool active; + struct list_head list; }; static DEFINE_MUTEX(vgasr_mutex); @@ -53,16 +52,23 @@ struct vgasr_priv { struct dentry *switch_file; int registered_clients; - struct vga_switcheroo_client clients[VGA_SWITCHEROO_MAX_CLIENTS]; + struct list_head clients; struct vga_switcheroo_handler *handler; }; +#define ID_BIT_AUDIO 0x100 +#define client_is_audio(c) ((c)->id & ID_BIT_AUDIO) +#define client_is_vga(c) ((c)->id == -1 || !client_is_audio(c)) +#define client_id(c) ((c)->id & ~ID_BIT_AUDIO) + static int vga_switcheroo_debugfs_init(struct vgasr_priv *priv); static void vga_switcheroo_debugfs_fini(struct vgasr_priv *priv); /* only one switcheroo per system */ -static struct vgasr_priv vgasr_priv; +static struct vgasr_priv vgasr_priv = { + .clients = LIST_HEAD_INIT(vgasr_priv.clients), +}; int vga_switcheroo_register_handler(struct vga_switcheroo_handler *handler) { @@ -88,72 +94,119 @@ EXPORT_SYMBOL(vga_switcheroo_unregister_handler); static void vga_switcheroo_enable(void) { - int i; int ret; + struct vga_switcheroo_client *client; + /* call the handler to init */ vgasr_priv.handler->init(); - for (i = 0; i < VGA_SWITCHEROO_MAX_CLIENTS; i++) { - ret = vgasr_priv.handler->get_client_id(vgasr_priv.clients[i].pdev); + list_for_each_entry(client, &vgasr_priv.clients, list) { + if (client->id != -1) + continue; + ret = vgasr_priv.handler->get_client_id(client->pdev); if (ret < 0) return; - vgasr_priv.clients[i].id = ret; + client->id = ret; } vga_switcheroo_debugfs_init(&vgasr_priv); vgasr_priv.active = true; } -int vga_switcheroo_register_client(struct pci_dev *pdev, - void (*set_gpu_state)(struct pci_dev *pdev, enum vga_switcheroo_state), - void (*reprobe)(struct pci_dev *pdev), - bool (*can_switch)(struct pci_dev *pdev)) +static int register_client(struct pci_dev *pdev, + const struct vga_switcheroo_client_ops *ops, + int id, bool active) { - int index; + struct vga_switcheroo_client *client; + + client = kzalloc(sizeof(*client), GFP_KERNEL); + if (!client) + return -ENOMEM; + + client->pwr_state = VGA_SWITCHEROO_ON; + client->pdev = pdev; + client->ops = ops; + client->id = id; + client->active = active; mutex_lock(&vgasr_mutex); - /* don't do IGD vs DIS here */ - if (vgasr_priv.registered_clients & 1) - index = 1; - else - index = 0; - - vgasr_priv.clients[index].pwr_state = VGA_SWITCHEROO_ON; - vgasr_priv.clients[index].pdev = pdev; - vgasr_priv.clients[index].set_gpu_state = set_gpu_state; - vgasr_priv.clients[index].reprobe = reprobe; - vgasr_priv.clients[index].can_switch = can_switch; - vgasr_priv.clients[index].id = -1; - if (pdev == vga_default_device()) - vgasr_priv.clients[index].active = true; - - vgasr_priv.registered_clients |= (1 << index); + list_add_tail(&client->list, &vgasr_priv.clients); + if (client_is_vga(client)) + vgasr_priv.registered_clients++; /* if we get two clients + handler */ - if (vgasr_priv.registered_clients == 0x3 && vgasr_priv.handler) { + if (!vgasr_priv.active && + vgasr_priv.registered_clients == 2 && vgasr_priv.handler) { printk(KERN_INFO "vga_switcheroo: enabled\n"); vga_switcheroo_enable(); } mutex_unlock(&vgasr_mutex); return 0; } + +int vga_switcheroo_register_client(struct pci_dev *pdev, + const struct vga_switcheroo_client_ops *ops) +{ + return register_client(pdev, ops, -1, + pdev == vga_default_device()); +} EXPORT_SYMBOL(vga_switcheroo_register_client); +int vga_switcheroo_register_audio_client(struct pci_dev *pdev, + const struct vga_switcheroo_client_ops *ops, + int id, bool active) +{ + return register_client(pdev, ops, id | ID_BIT_AUDIO, active); +} +EXPORT_SYMBOL(vga_switcheroo_register_audio_client); + +static struct vga_switcheroo_client * +find_client_from_pci(struct list_head *head, struct pci_dev *pdev) +{ + struct vga_switcheroo_client *client; + list_for_each_entry(client, head, list) + if (client->pdev == pdev) + return client; + return NULL; +} + +static struct vga_switcheroo_client * +find_client_from_id(struct list_head *head, int client_id) +{ + struct vga_switcheroo_client *client; + list_for_each_entry(client, head, list) + if (client->id == client_id) + return client; + return NULL; +} + +static struct vga_switcheroo_client * +find_active_client(struct list_head *head) +{ + struct vga_switcheroo_client *client; + list_for_each_entry(client, head, list) + if (client->active && client_is_vga(client)) + return client; + return NULL; +} + void vga_switcheroo_unregister_client(struct pci_dev *pdev) { - int i; + struct vga_switcheroo_client *client; mutex_lock(&vgasr_mutex); - for (i = 0; i < VGA_SWITCHEROO_MAX_CLIENTS; i++) { - if (vgasr_priv.clients[i].pdev == pdev) { - vgasr_priv.registered_clients &= ~(1 << i); - break; - } + client = find_client_from_pci(&vgasr_priv.clients, pdev); + if (client) { + if (client_is_vga(client)) + vgasr_priv.registered_clients--; + list_del(&client->list); + kfree(client); + } + if (vgasr_priv.active && vgasr_priv.registered_clients < 2) { + printk(KERN_INFO "vga_switcheroo: disabled\n"); + vga_switcheroo_debugfs_fini(&vgasr_priv); + vgasr_priv.active = false; } - - printk(KERN_INFO "vga_switcheroo: disabled\n"); - vga_switcheroo_debugfs_fini(&vgasr_priv); - vgasr_priv.active = false; mutex_unlock(&vgasr_mutex); } EXPORT_SYMBOL(vga_switcheroo_unregister_client); @@ -161,29 +214,29 @@ EXPORT_SYMBOL(vga_switcheroo_unregister_client); void vga_switcheroo_client_fb_set(struct pci_dev *pdev, struct fb_info *info) { - int i; + struct vga_switcheroo_client *client; mutex_lock(&vgasr_mutex); - for (i = 0; i < VGA_SWITCHEROO_MAX_CLIENTS; i++) { - if (vgasr_priv.clients[i].pdev == pdev) { - vgasr_priv.clients[i].fb_info = info; - break; - } - } + client = find_client_from_pci(&vgasr_priv.clients, pdev); + if (client) + client->fb_info = info; mutex_unlock(&vgasr_mutex); } EXPORT_SYMBOL(vga_switcheroo_client_fb_set); static int vga_switcheroo_show(struct seq_file *m, void *v) { - int i; + struct vga_switcheroo_client *client; + int i = 0; mutex_lock(&vgasr_mutex); - for (i = 0; i < VGA_SWITCHEROO_MAX_CLIENTS; i++) { - seq_printf(m, "%d:%s:%c:%s:%s\n", i, - vgasr_priv.clients[i].id == VGA_SWITCHEROO_DIS ? "DIS" : "IGD", - vgasr_priv.clients[i].active ? '+' : ' ', - vgasr_priv.clients[i].pwr_state ? "Pwr" : "Off", - pci_name(vgasr_priv.clients[i].pdev)); + list_for_each_entry(client, &vgasr_priv.clients, list) { + seq_printf(m, "%d:%s%s:%c:%s:%s\n", i, + client_id(client) == VGA_SWITCHEROO_DIS ? "DIS" : "IGD", + client_is_vga(client) ? "" : "-Audio", + client->active ? '+' : ' ', + client->pwr_state ? "Pwr" : "Off", + pci_name(client->pdev)); + i++; } mutex_unlock(&vgasr_mutex); return 0; @@ -199,7 +252,7 @@ static int vga_switchon(struct vga_switcheroo_client *client) if (vgasr_priv.handler->power_state) vgasr_priv.handler->power_state(client->id, VGA_SWITCHEROO_ON); /* call the driver callback to turn on device */ - client->set_gpu_state(client->pdev, VGA_SWITCHEROO_ON); + client->ops->set_gpu_state(client->pdev, VGA_SWITCHEROO_ON); client->pwr_state = VGA_SWITCHEROO_ON; return 0; } @@ -207,25 +260,30 @@ static int vga_switchon(struct vga_switcheroo_client *client) static int vga_switchoff(struct vga_switcheroo_client *client) { /* call the driver callback to turn off device */ - client->set_gpu_state(client->pdev, VGA_SWITCHEROO_OFF); + client->ops->set_gpu_state(client->pdev, VGA_SWITCHEROO_OFF); if (vgasr_priv.handler->power_state) vgasr_priv.handler->power_state(client->id, VGA_SWITCHEROO_OFF); client->pwr_state = VGA_SWITCHEROO_OFF; return 0; } +static void set_audio_state(int id, int state) +{ + struct vga_switcheroo_client *client; + + client = find_client_from_id(&vgasr_priv.clients, id | ID_BIT_AUDIO); + if (client && client->pwr_state != state) { + client->ops->set_gpu_state(client->pdev, state); + client->pwr_state = state; + } +} + /* stage one happens before delay */ static int vga_switchto_stage1(struct vga_switcheroo_client *new_client) { - int i; - struct vga_switcheroo_client *active = NULL; + struct vga_switcheroo_client *active; - for (i = 0; i < VGA_SWITCHEROO_MAX_CLIENTS; i++) { - if (vgasr_priv.clients[i].active == true) { - active = &vgasr_priv.clients[i]; - break; - } - } + active = find_active_client(&vgasr_priv.clients); if (!active) return 0; @@ -233,6 +291,7 @@ static int vga_switchto_stage1(struct vga_switcheroo_client *new_client) vga_switchon(new_client); vga_set_default_device(new_client->pdev); + set_audio_state(new_client->id, VGA_SWITCHEROO_ON); return 0; } @@ -241,15 +300,9 @@ static int vga_switchto_stage1(struct vga_switcheroo_client *new_client) static int vga_switchto_stage2(struct vga_switcheroo_client *new_client) { int ret; - int i; - struct vga_switcheroo_client *active = NULL; + struct vga_switcheroo_client *active; - for (i = 0; i < VGA_SWITCHEROO_MAX_CLIENTS; i++) { - if (vgasr_priv.clients[i].active == true) { - active = &vgasr_priv.clients[i]; - break; - } - } + active = find_active_client(&vgasr_priv.clients); if (!active) return 0; @@ -265,8 +318,10 @@ static int vga_switchto_stage2(struct vga_switcheroo_client *new_client) if (ret) return ret; - if (new_client->reprobe) - new_client->reprobe(new_client->pdev); + if (new_client->ops->reprobe) + new_client->ops->reprobe(new_client->pdev); + + set_audio_state(active->id, VGA_SWITCHEROO_OFF); if (active->pwr_state == VGA_SWITCHEROO_ON) vga_switchoff(active); @@ -275,13 +330,26 @@ static int vga_switchto_stage2(struct vga_switcheroo_client *new_client) return 0; } +static bool check_can_switch(void) +{ + struct vga_switcheroo_client *client; + + list_for_each_entry(client, &vgasr_priv.clients, list) { + if (!client->ops->can_switch(client->pdev)) { + printk(KERN_ERR "vga_switcheroo: client %x refused switch\n", client->id); + return false; + } + } + return true; +} + static ssize_t vga_switcheroo_debugfs_write(struct file *filp, const char __user *ubuf, size_t cnt, loff_t *ppos) { char usercmd[64]; const char *pdev_name; - int i, ret; + int ret; bool delay = false, can_switch; bool just_mux = false; int client_id = -1; @@ -302,21 +370,21 @@ vga_switcheroo_debugfs_write(struct file *filp, const char __user *ubuf, /* pwr off the device not in use */ if (strncmp(usercmd, "OFF", 3) == 0) { - for (i = 0; i < VGA_SWITCHEROO_MAX_CLIENTS; i++) { - if (vgasr_priv.clients[i].active) + list_for_each_entry(client, &vgasr_priv.clients, list) { + if (client->active) continue; - if (vgasr_priv.clients[i].pwr_state == VGA_SWITCHEROO_ON) - vga_switchoff(&vgasr_priv.clients[i]); + if (client->pwr_state == VGA_SWITCHEROO_ON) + vga_switchoff(client); } goto out; } /* pwr on the device not in use */ if (strncmp(usercmd, "ON", 2) == 0) { - for (i = 0; i < VGA_SWITCHEROO_MAX_CLIENTS; i++) { - if (vgasr_priv.clients[i].active) + list_for_each_entry(client, &vgasr_priv.clients, list) { + if (client->active) continue; - if (vgasr_priv.clients[i].pwr_state == VGA_SWITCHEROO_OFF) - vga_switchon(&vgasr_priv.clients[i]); + if (client->pwr_state == VGA_SWITCHEROO_OFF) + vga_switchon(client); } goto out; } @@ -349,13 +417,9 @@ vga_switcheroo_debugfs_write(struct file *filp, const char __user *ubuf, if (client_id == -1) goto out; - - for (i = 0; i < VGA_SWITCHEROO_MAX_CLIENTS; i++) { - if (vgasr_priv.clients[i].id == client_id) { - client = &vgasr_priv.clients[i]; - break; - } - } + client = find_client_from_id(&vgasr_priv.clients, client_id); + if (!client) + goto out; vgasr_priv.delayed_switch_active = false; @@ -364,23 +428,16 @@ vga_switcheroo_debugfs_write(struct file *filp, const char __user *ubuf, goto out; } - if (client->active == true) + if (client->active) goto out; /* okay we want a switch - test if devices are willing to switch */ - can_switch = true; - for (i = 0; i < VGA_SWITCHEROO_MAX_CLIENTS; i++) { - can_switch = vgasr_priv.clients[i].can_switch(vgasr_priv.clients[i].pdev); - if (can_switch == false) { - printk(KERN_ERR "vga_switcheroo: client %d refused switch\n", i); - break; - } - } + can_switch = check_can_switch(); if (can_switch == false && delay == false) goto out; - if (can_switch == true) { + if (can_switch) { pdev_name = pci_name(client->pdev); ret = vga_switchto_stage1(client); if (ret) @@ -452,10 +509,8 @@ fail: int vga_switcheroo_process_delayed_switch(void) { - struct vga_switcheroo_client *client = NULL; + struct vga_switcheroo_client *client; const char *pdev_name; - bool can_switch = true; - int i; int ret; int err = -EINVAL; @@ -465,17 +520,9 @@ int vga_switcheroo_process_delayed_switch(void) printk(KERN_INFO "vga_switcheroo: processing delayed switch to %d\n", vgasr_priv.delayed_client_id); - for (i = 0; i < VGA_SWITCHEROO_MAX_CLIENTS; i++) { - if (vgasr_priv.clients[i].id == vgasr_priv.delayed_client_id) - client = &vgasr_priv.clients[i]; - can_switch = vgasr_priv.clients[i].can_switch(vgasr_priv.clients[i].pdev); - if (can_switch == false) { - printk(KERN_ERR "vga_switcheroo: client %d refused switch\n", i); - break; - } - } - - if (can_switch == false || client == NULL) + client = find_client_from_id(&vgasr_priv.clients, + vgasr_priv.delayed_client_id); + if (!client || !check_can_switch()) goto err; pdev_name = pci_name(client->pdev); diff --git a/drivers/staging/omapdrm/omap_crtc.c b/drivers/staging/omapdrm/omap_crtc.c index 490a7f1..8b864af 100644 --- a/drivers/staging/omapdrm/omap_crtc.c +++ b/drivers/staging/omapdrm/omap_crtc.c @@ -36,12 +36,6 @@ struct omap_crtc { struct drm_framebuffer *old_fb; }; -static void omap_crtc_gamma_set(struct drm_crtc *crtc, - u16 *red, u16 *green, u16 *blue, uint32_t start, uint32_t size) -{ - /* not supported.. at least not yet */ -} - static void omap_crtc_destroy(struct drm_crtc *crtc) { struct omap_crtc *omap_crtc = to_omap_crtc(crtc); @@ -198,7 +192,6 @@ static int omap_crtc_page_flip_locked(struct drm_crtc *crtc, } static const struct drm_crtc_funcs omap_crtc_funcs = { - .gamma_set = omap_crtc_gamma_set, .set_config = drm_crtc_helper_set_config, .destroy = omap_crtc_destroy, .page_flip = omap_crtc_page_flip_locked, diff --git a/drivers/staging/omapdrm/omap_drv.c b/drivers/staging/omapdrm/omap_drv.c index 620b8d5..1edbd77 100644 --- a/drivers/staging/omapdrm/omap_drv.c +++ b/drivers/staging/omapdrm/omap_drv.c @@ -58,7 +58,7 @@ static void omap_fb_output_poll_changed(struct drm_device *dev) } } -static struct drm_mode_config_funcs omap_mode_config_funcs = { +static const struct drm_mode_config_funcs omap_mode_config_funcs = { .fb_create = omap_framebuffer_create, .output_poll_changed = omap_fb_output_poll_changed, }; @@ -726,7 +726,7 @@ static void dev_irq_uninstall(struct drm_device *dev) DBG("irq_uninstall: dev=%p", dev); } -static struct vm_operations_struct omap_gem_vm_ops = { +static const struct vm_operations_struct omap_gem_vm_ops = { .fault = omap_gem_fault, .open = drm_gem_vm_open, .close = drm_gem_vm_close, diff --git a/include/drm/drm.h b/include/drm/drm.h index 64ff02d..e51035a 100644 --- a/include/drm/drm.h +++ b/include/drm/drm.h @@ -730,6 +730,8 @@ struct drm_prime_handle { #define DRM_IOCTL_MODE_GETPLANE DRM_IOWR(0xB6, struct drm_mode_get_plane) #define DRM_IOCTL_MODE_SETPLANE DRM_IOWR(0xB7, struct drm_mode_set_plane) #define DRM_IOCTL_MODE_ADDFB2 DRM_IOWR(0xB8, struct drm_mode_fb_cmd2) +#define DRM_IOCTL_MODE_OBJ_GETPROPERTIES DRM_IOWR(0xB9, struct drm_mode_obj_get_properties) +#define DRM_IOCTL_MODE_OBJ_SETPROPERTY DRM_IOWR(0xBA, struct drm_mode_obj_set_property) /** * Device specific ioctls should only be in their respective headers @@ -775,6 +777,10 @@ struct drm_event_vblank { #define DRM_CAP_VBLANK_HIGH_CRTC 0x2 #define DRM_CAP_DUMB_PREFERRED_DEPTH 0x3 #define DRM_CAP_DUMB_PREFER_SHADOW 0x4 +#define DRM_CAP_PRIME 0x5 + +#define DRM_PRIME_CAP_IMPORT 0x1 +#define DRM_PRIME_CAP_EXPORT 0x2 /* typedef area */ #ifndef __KERNEL__ diff --git a/include/drm/drmP.h b/include/drm/drmP.h index 10a5f37..15d9179 100644 --- a/include/drm/drmP.h +++ b/include/drm/drmP.h @@ -755,11 +755,11 @@ struct drm_driver { * @dev: DRM device * @crtc: counter to fetch * - * Driver callback for fetching a raw hardware vblank counter - * for @crtc. If a device doesn't have a hardware counter, the - * driver can simply return the value of drm_vblank_count and - * make the enable_vblank() and disable_vblank() hooks into no-ops, - * leaving interrupts enabled at all times. + * Driver callback for fetching a raw hardware vblank counter for @crtc. + * If a device doesn't have a hardware counter, the driver can simply + * return the value of drm_vblank_count. The DRM core will account for + * missed vblank events while interrupts where disabled based on system + * timestamps. * * Wraparound handling and loss of events due to modesetting is dealt * with in the DRM core code. @@ -941,7 +941,7 @@ struct drm_driver { uint32_t handle); /* Driver private ops for this object */ - struct vm_operations_struct *gem_vm_ops; + const struct vm_operations_struct *gem_vm_ops; int major; int minor; @@ -1309,8 +1309,8 @@ extern int drm_release(struct inode *inode, struct file *filp); /* Mapping support (drm_vm.h) */ extern int drm_mmap(struct file *filp, struct vm_area_struct *vma); extern int drm_mmap_locked(struct file *filp, struct vm_area_struct *vma); -extern void drm_vm_open_locked(struct vm_area_struct *vma); -extern void drm_vm_close_locked(struct vm_area_struct *vma); +extern void drm_vm_open_locked(struct drm_device *dev, struct vm_area_struct *vma); +extern void drm_vm_close_locked(struct drm_device *dev, struct vm_area_struct *vma); extern unsigned int drm_poll(struct file *filp, struct poll_table_struct *wait); /* Memory management support (drm_memory.h) */ diff --git a/include/drm/drm_crtc.h b/include/drm/drm_crtc.h index f35e7ed..b88b28f 100644 --- a/include/drm/drm_crtc.h +++ b/include/drm/drm_crtc.h @@ -36,6 +36,7 @@ struct drm_device; struct drm_mode_set; struct drm_framebuffer; +struct drm_object_properties; #define DRM_MODE_OBJECT_CRTC 0xcccccccc @@ -50,6 +51,14 @@ struct drm_framebuffer; struct drm_mode_object { uint32_t id; uint32_t type; + struct drm_object_properties *properties; +}; + +#define DRM_OBJECT_MAX_PROPERTY 16 +struct drm_object_properties { + int count; + uint32_t ids[DRM_OBJECT_MAX_PROPERTY]; + uint64_t values[DRM_OBJECT_MAX_PROPERTY]; }; /* @@ -285,19 +294,16 @@ struct drm_plane; /** * drm_crtc_funcs - control CRTCs for a given device - * @reset: reset CRTC after state has been invalidate (e.g. resume) - * @dpms: control display power levels * @save: save CRTC state - * @resore: restore CRTC state - * @lock: lock the CRTC - * @unlock: unlock the CRTC - * @shadow_allocate: allocate shadow pixmap - * @shadow_create: create shadow pixmap for rotation support - * @shadow_destroy: free shadow pixmap - * @mode_fixup: fixup proposed mode - * @mode_set: set the desired mode on the CRTC + * @restore: restore CRTC state + * @reset: reset CRTC after state has been invalidate (e.g. resume) + * @cursor_set: setup the cursor + * @cursor_move: move the cursor * @gamma_set: specify color ramp for CRTC - * @destroy: deinit and free object. + * @destroy: deinit and free object + * @set_property: called when a property is changed + * @set_config: apply a new CRTC configuration + * @page_flip: initiate a page flip * * The drm_crtc_funcs structure is the central CRTC management structure * in the DRM. Each CRTC controls one or more connectors (note that the name @@ -341,6 +347,9 @@ struct drm_crtc_funcs { int (*page_flip)(struct drm_crtc *crtc, struct drm_framebuffer *fb, struct drm_pending_vblank_event *event); + + int (*set_property)(struct drm_crtc *crtc, + struct drm_property *property, uint64_t val); }; /** @@ -360,6 +369,7 @@ struct drm_crtc_funcs { * @framedur_ns: precise line timing * @pixeldur_ns: precise pixel timing * @helper_private: mid-layer private data + * @properties: property tracking for this CRTC * * Each CRTC may have one or more connectors associated with it. This structure * allows the CRTC to be controlled. @@ -395,6 +405,8 @@ struct drm_crtc { /* if you are using the helper */ void *helper_private; + + struct drm_object_properties properties; }; @@ -404,11 +416,8 @@ struct drm_crtc { * @save: save connector state * @restore: restore connector state * @reset: reset connector after state has been invalidate (e.g. resume) - * @mode_valid: is this mode valid on the given connector? - * @mode_fixup: try to fixup proposed mode for this connector - * @mode_set: set this mode * @detect: is this connector active? - * @get_modes: get mode list for this connector + * @fill_modes: fill mode list for this connector * @set_property: property for this connector may need update * @destroy: make object go away * @force: notify the driver the connector is forced on @@ -451,7 +460,6 @@ struct drm_encoder_funcs { }; #define DRM_CONNECTOR_MAX_UMODES 16 -#define DRM_CONNECTOR_MAX_PROPERTY 16 #define DRM_CONNECTOR_LEN 32 #define DRM_CONNECTOR_MAX_ENCODER 3 @@ -520,8 +528,7 @@ enum drm_connector_force { * @funcs: connector control functions * @user_modes: user added mode list * @edid_blob_ptr: DRM property containing EDID if present - * @property_ids: property tracking for this connector - * @property_values: value pointers or data for properties + * @properties: property tracking for this connector * @polled: a %DRM_CONNECTOR_POLL_<foo> value for core driven polling * @dpms: current dpms state * @helper_private: mid-layer private data @@ -565,8 +572,7 @@ struct drm_connector { struct list_head user_modes; struct drm_property_blob *edid_blob_ptr; - u32 property_ids[DRM_CONNECTOR_MAX_PROPERTY]; - uint64_t property_values[DRM_CONNECTOR_MAX_PROPERTY]; + struct drm_object_properties properties; uint8_t polled; /* DRM_CONNECTOR_POLL_* */ @@ -761,7 +767,7 @@ struct drm_mode_config { int min_width, min_height; int max_width, max_height; - struct drm_mode_config_funcs *funcs; + const struct drm_mode_config_funcs *funcs; resource_size_t fb_base; /* output poll support */ @@ -898,6 +904,12 @@ extern int drm_connector_property_set_value(struct drm_connector *connector, extern int drm_connector_property_get_value(struct drm_connector *connector, struct drm_property *property, uint64_t *value); +extern int drm_object_property_set_value(struct drm_mode_object *obj, + struct drm_property *property, + uint64_t val); +extern int drm_object_property_get_value(struct drm_mode_object *obj, + struct drm_property *property, + uint64_t *value); extern struct drm_display_mode *drm_crtc_mode_create(struct drm_device *dev); extern void drm_framebuffer_set_object(struct drm_device *dev, unsigned long handle); @@ -910,8 +922,11 @@ extern int drmfb_remove(struct drm_device *dev, struct drm_framebuffer *fb); extern void drm_crtc_probe_connector_modes(struct drm_device *dev, int maxX, int maxY); extern bool drm_crtc_in_use(struct drm_crtc *crtc); -extern int drm_connector_attach_property(struct drm_connector *connector, - struct drm_property *property, uint64_t init_val); +extern void drm_connector_attach_property(struct drm_connector *connector, + struct drm_property *property, uint64_t init_val); +extern void drm_object_attach_property(struct drm_mode_object *obj, + struct drm_property *property, + uint64_t init_val); extern struct drm_property *drm_property_create(struct drm_device *dev, int flags, const char *name, int num_values); extern struct drm_property *drm_property_create_enum(struct drm_device *dev, int flags, @@ -1024,6 +1039,10 @@ extern int drm_mode_mmap_dumb_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv); extern int drm_mode_destroy_dumb_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv); +extern int drm_mode_obj_get_properties_ioctl(struct drm_device *dev, void *data, + struct drm_file *file_priv); +extern int drm_mode_obj_set_property_ioctl(struct drm_device *dev, void *data, + struct drm_file *file_priv); extern void drm_fb_get_bpp_depth(uint32_t format, unsigned int *depth, int *bpp); diff --git a/include/drm/drm_crtc_helper.h b/include/drm/drm_crtc_helper.h index 3add00e..7988e55 100644 --- a/include/drm/drm_crtc_helper.h +++ b/include/drm/drm_crtc_helper.h @@ -44,6 +44,13 @@ enum mode_set_atomic { ENTER_ATOMIC_MODE_SET, }; +/** + * drm_crtc_helper_funcs - helper operations for CRTCs + * @mode_fixup: try to fixup proposed mode for this connector + * @mode_set: set this mode + * + * The helper operations are called by the mid-layer CRTC helper. + */ struct drm_crtc_helper_funcs { /* * Control power levels on the CRTC. If the mode passed in is @@ -76,6 +83,13 @@ struct drm_crtc_helper_funcs { void (*disable)(struct drm_crtc *crtc); }; +/** + * drm_encoder_helper_funcs - helper operations for encoders + * @mode_fixup: try to fixup proposed mode for this connector + * @mode_set: set this mode + * + * The helper operations are called by the mid-layer CRTC helper. + */ struct drm_encoder_helper_funcs { void (*dpms)(struct drm_encoder *encoder, int mode); void (*save)(struct drm_encoder *encoder); @@ -97,6 +111,13 @@ struct drm_encoder_helper_funcs { void (*disable)(struct drm_encoder *encoder); }; +/** + * drm_connector_helper_funcs - helper operations for connectors + * @get_modes: get mode list for this connector + * @mode_valid: is this mode valid on the given connector? + * + * The helper operations are called by the mid-layer CRTC helper. + */ struct drm_connector_helper_funcs { int (*get_modes)(struct drm_connector *connector); int (*mode_valid)(struct drm_connector *connector, diff --git a/include/drm/drm_dp_helper.h b/include/drm/drm_dp_helper.h index 93df2d7..1744b18c 100644 --- a/include/drm/drm_dp_helper.h +++ b/include/drm/drm_dp_helper.h @@ -72,6 +72,10 @@ #define DP_MAIN_LINK_CHANNEL_CODING 0x006 +#define DP_DOWN_STREAM_PORT_COUNT 0x007 +#define DP_PORT_COUNT_MASK 0x0f +#define DP_OUI_SUPPORT (1 << 7) + #define DP_EDP_CONFIGURATION_CAP 0x00d #define DP_TRAINING_AUX_RD_INTERVAL 0x00e @@ -213,6 +217,10 @@ # define DP_TEST_NAK (1 << 1) # define DP_TEST_EDID_CHECKSUM_WRITE (1 << 2) +#define DP_SOURCE_OUI 0x300 +#define DP_SINK_OUI 0x400 +#define DP_BRANCH_OUI 0x500 + #define DP_SET_POWER 0x600 # define DP_SET_POWER_D0 0x1 # define DP_SET_POWER_D3 0x2 diff --git a/include/drm/drm_mode.h b/include/drm/drm_mode.h index 4a0aae3..326f2be 100644 --- a/include/drm/drm_mode.h +++ b/include/drm/drm_mode.h @@ -254,6 +254,21 @@ struct drm_mode_connector_set_property { __u32 connector_id; }; +struct drm_mode_obj_get_properties { + __u64 props_ptr; + __u64 prop_values_ptr; + __u32 count_props; + __u32 obj_id; + __u32 obj_type; +}; + +struct drm_mode_obj_set_property { + __u64 value; + __u32 prop_id; + __u32 obj_id; + __u32 obj_type; +}; + struct drm_mode_get_blob { __u32 blob_id; __u32 length; diff --git a/include/drm/radeon_drm.h b/include/drm/radeon_drm.h index 7c491b4..5805686 100644 --- a/include/drm/radeon_drm.h +++ b/include/drm/radeon_drm.h @@ -926,7 +926,6 @@ struct drm_radeon_cs_chunk { }; /* drm_radeon_cs_reloc.flags */ -#define RADEON_RELOC_DONT_SYNC 0x01 struct drm_radeon_cs_reloc { uint32_t handle; diff --git a/include/linux/vga_switcheroo.h b/include/linux/vga_switcheroo.h index 4b9a7f5..b455c7c 100644 --- a/include/linux/vga_switcheroo.h +++ b/include/linux/vga_switcheroo.h @@ -28,13 +28,19 @@ struct vga_switcheroo_handler { int (*get_client_id)(struct pci_dev *pdev); }; +struct vga_switcheroo_client_ops { + void (*set_gpu_state)(struct pci_dev *dev, enum vga_switcheroo_state); + void (*reprobe)(struct pci_dev *dev); + bool (*can_switch)(struct pci_dev *dev); +}; #if defined(CONFIG_VGA_SWITCHEROO) void vga_switcheroo_unregister_client(struct pci_dev *dev); int vga_switcheroo_register_client(struct pci_dev *dev, - void (*set_gpu_state)(struct pci_dev *dev, enum vga_switcheroo_state), - void (*reprobe)(struct pci_dev *dev), - bool (*can_switch)(struct pci_dev *dev)); + const struct vga_switcheroo_client_ops *ops); +int vga_switcheroo_register_audio_client(struct pci_dev *pdev, + const struct vga_switcheroo_client_ops *ops, + int id, bool active); void vga_switcheroo_client_fb_set(struct pci_dev *dev, struct fb_info *info); @@ -48,11 +54,12 @@ int vga_switcheroo_process_delayed_switch(void); static inline void vga_switcheroo_unregister_client(struct pci_dev *dev) {} static inline int vga_switcheroo_register_client(struct pci_dev *dev, - void (*set_gpu_state)(struct pci_dev *dev, enum vga_switcheroo_state), - void (*reprobe)(struct pci_dev *dev), - bool (*can_switch)(struct pci_dev *dev)) { return 0; } + const struct vga_switcheroo_client_ops *ops) { return 0; } static inline void vga_switcheroo_client_fb_set(struct pci_dev *dev, struct fb_info *info) {} static inline int vga_switcheroo_register_handler(struct vga_switcheroo_handler *handler) { return 0; } +static inline int vga_switcheroo_register_audio_client(struct pci_dev *pdev, + const struct vga_switcheroo_client_ops *ops, + int id, bool active) { return 0; } static inline void vga_switcheroo_unregister_handler(void) {} static inline int vga_switcheroo_process_delayed_switch(void) { return 0; } |