Merge branch 'drm-next' of git://people.freedesktop.org/~airlied/linux
authorLinus Torvalds <torvalds@linux-foundation.org>
Thu, 22 Mar 2012 20:08:22 +0000 (13:08 -0700)
committerLinus Torvalds <torvalds@linux-foundation.org>
Thu, 22 Mar 2012 20:08:22 +0000 (13:08 -0700)
Pull drm main changes from Dave Airlie:
 "This is the main drm pull request, I'm probably going to send two more
  smaller ones, will explain below.

  This contains a patch that is also in the fbdev tree, but it should be
  the same patch, it added an API for hot unplugging framebuffer
  devices, and I need that API for a new driver.

  It also contains some changes to the i2c tree which Jean has acked,
  and one change to moorestown platform stuff in x86.

  Highlights:
   - new drivers: UDL driver for USB displaylink devices, kms only,
     should support correct hotplug operations.
   - core: i2c speedups + better hotplug support, EDID overriding via
     firmware interface - allows user to load a firmware for a broken
     monitor/kvm from userspace, it even has documentation for it.
   - exynos: new HDMI audio + hdmi 1.4 + virtual output driver
   - gma500: code cleanup
   - radeon: cleanups, CS optimisations, streamout support and pageflip
     fix
   - nouveau: NVD9 displayport support + more reclocking work
   - i915: re-enabling GMBUS, finish gpu patch (might help hibernation
     who knows), missed irq fixes, stencil tiling fixes, interlaced
     support, aliasesd PPGTT support for SNB/IVB, swizzling for SNB/IVB,
     semaphore fixes

  As well as the usual bunch of cleanups and fixes all over the place.

  I've got two things I'd like to merge a bit later:

   a) AMD support for all their new radeonhd 7000 series GPU and APUs.
      AMD dropped this a bit late due to insane internal review
      processes, (please AMD just follow Intel and let open source guys
      ship stuff early) however I don't want to penalise people who own
      this hardware (since its been on sale for 3-4 months and GPU hw
      doesn't exactly have a lifetime in years) and consign them to
      using closed drivers for longer than necessary.  The changes are
      well contained and just plug into the driver new gpu functionality
      so they should be fairly regression proof.  I just want to give
      them a bit of a run on the hw AMD kindly sent me.

   b) drm prime/dma-buf interface code.  This is just infrastructure
      code to expose the dma-buf stuff to drm drivers and to userspace.
      I'm not planning on pushing any driver support in this cycle
      (except maybe exynos), but I'd like to get the infrastructure code
      in so for the next cycle I can start getting the driver support
      into the individual drivers.  We have started driver support for
      i915, nouveau and udl along with I think exynos and omap in
      staging.  However this code relies on the dma-buf tree being
      pulled into your tree first since it needs the latest interfaces
      from that tree.  I'll push to get that tree sent asap.

  (oh and any warnings you see in i915 are gcc's fault from what anyone
  can see)."

Fix up trivial conflicts in arch/x86/platform/mrst/mrst.c due to the new
msic_thermal_platform_data() thermal function being added next to the
tc35876x_platform_data() i2c device function..

* 'drm-next' of git://people.freedesktop.org/~airlied/linux: (326 commits)
  drm/i915: use DDC_ADDR instead of hard-coding it
  drm/radeon: use DDC_ADDR instead of hard-coding it
  drm: remove unneeded redefinition of DDC_ADDR
  drm/exynos: added virtual display driver.
  drm: allow loading an EDID as firmware to override broken monitor
  drm/exynos: enable hdmi audio feature
  drm/exynos: add default pixel format for plane
  drm/exynos: cleanup exynos_hdmi.h
  drm/exynos: add is_local member in exynos_drm_subdrv struct
  drm/exynos: add subdrv open/close functions
  drm/exynos: remove module of exynos drm subdrv
  drm/exynos: release pending pageflip events when closed
  drm/exynos: added new funtion to get/put dma address.
  drm/exynos: update gem and buffer framework.
  drm/exynos: added mode_fixup feature and code clean.
  drm/exynos: add HDMI version 1.4 support
  drm/exynos: remove exynos_mixer.h
  gma500: Fix mmap frambuffer
  drm/radeon: Drop radeon_gem_object_(un)pin.
  drm/radeon: Restrict offset for legacy display engine.
  ...

271 files changed:
Documentation/EDID/1024x768.S [new file with mode: 0644]
Documentation/EDID/1280x1024.S [new file with mode: 0644]
Documentation/EDID/1680x1050.S [new file with mode: 0644]
Documentation/EDID/1920x1080.S [new file with mode: 0644]
Documentation/EDID/HOWTO.txt [new file with mode: 0644]
Documentation/EDID/Makefile [new file with mode: 0644]
Documentation/EDID/edid.S [new file with mode: 0644]
Documentation/EDID/hex [new file with mode: 0644]
Documentation/kernel-parameters.txt
arch/x86/platform/mrst/mrst.c
drivers/char/agp/intel-agp.c
drivers/char/agp/intel-gtt.c
drivers/gpu/drm/Kconfig
drivers/gpu/drm/Makefile
drivers/gpu/drm/drm_crtc.c
drivers/gpu/drm/drm_crtc_helper.c
drivers/gpu/drm/drm_drv.c
drivers/gpu/drm/drm_edid.c
drivers/gpu/drm/drm_edid_load.c [new file with mode: 0644]
drivers/gpu/drm/drm_fb_helper.c
drivers/gpu/drm/drm_fops.c
drivers/gpu/drm/drm_gem.c
drivers/gpu/drm/drm_ioctl.c
drivers/gpu/drm/drm_irq.c
drivers/gpu/drm/drm_memory.c
drivers/gpu/drm/drm_modes.c
drivers/gpu/drm/drm_pci.c
drivers/gpu/drm/drm_platform.c
drivers/gpu/drm/drm_stub.c
drivers/gpu/drm/drm_sysfs.c
drivers/gpu/drm/drm_usb.c
drivers/gpu/drm/drm_vm.c
drivers/gpu/drm/exynos/Kconfig
drivers/gpu/drm/exynos/Makefile
drivers/gpu/drm/exynos/exynos_ddc.c
drivers/gpu/drm/exynos/exynos_drm_buf.c
drivers/gpu/drm/exynos/exynos_drm_buf.h
drivers/gpu/drm/exynos/exynos_drm_connector.c
drivers/gpu/drm/exynos/exynos_drm_core.c
drivers/gpu/drm/exynos/exynos_drm_crtc.c
drivers/gpu/drm/exynos/exynos_drm_drv.c
drivers/gpu/drm/exynos/exynos_drm_drv.h
drivers/gpu/drm/exynos/exynos_drm_encoder.c
drivers/gpu/drm/exynos/exynos_drm_fb.c
drivers/gpu/drm/exynos/exynos_drm_fbdev.c
drivers/gpu/drm/exynos/exynos_drm_fimd.c
drivers/gpu/drm/exynos/exynos_drm_gem.c
drivers/gpu/drm/exynos/exynos_drm_gem.h
drivers/gpu/drm/exynos/exynos_drm_hdmi.c
drivers/gpu/drm/exynos/exynos_drm_hdmi.h
drivers/gpu/drm/exynos/exynos_drm_plane.c
drivers/gpu/drm/exynos/exynos_drm_vidi.c [new file with mode: 0644]
drivers/gpu/drm/exynos/exynos_drm_vidi.h [new file with mode: 0644]
drivers/gpu/drm/exynos/exynos_hdmi.c
drivers/gpu/drm/exynos/exynos_hdmi.h
drivers/gpu/drm/exynos/exynos_mixer.c
drivers/gpu/drm/exynos/exynos_mixer.h [deleted file]
drivers/gpu/drm/exynos/regs-hdmi.h
drivers/gpu/drm/gma500/Kconfig
drivers/gpu/drm/gma500/Makefile
drivers/gpu/drm/gma500/cdv_device.c
drivers/gpu/drm/gma500/cdv_device.h
drivers/gpu/drm/gma500/cdv_intel_crt.c
drivers/gpu/drm/gma500/cdv_intel_display.c
drivers/gpu/drm/gma500/cdv_intel_hdmi.c
drivers/gpu/drm/gma500/cdv_intel_lvds.c
drivers/gpu/drm/gma500/framebuffer.c
drivers/gpu/drm/gma500/gem_glue.c
drivers/gpu/drm/gma500/gtt.c
drivers/gpu/drm/gma500/intel_gmbus.c
drivers/gpu/drm/gma500/mdfld_device.c [new file with mode: 0644]
drivers/gpu/drm/gma500/mdfld_dsi_dpi.c [new file with mode: 0644]
drivers/gpu/drm/gma500/mdfld_dsi_dpi.h [new file with mode: 0644]
drivers/gpu/drm/gma500/mdfld_dsi_output.c [new file with mode: 0644]
drivers/gpu/drm/gma500/mdfld_dsi_output.h [new file with mode: 0644]
drivers/gpu/drm/gma500/mdfld_dsi_pkg_sender.c [new file with mode: 0644]
drivers/gpu/drm/gma500/mdfld_dsi_pkg_sender.h [new file with mode: 0644]
drivers/gpu/drm/gma500/mdfld_intel_display.c [new file with mode: 0644]
drivers/gpu/drm/gma500/mdfld_output.c [new file with mode: 0644]
drivers/gpu/drm/gma500/mdfld_output.h [new file with mode: 0644]
drivers/gpu/drm/gma500/mdfld_tmd_vid.c [new file with mode: 0644]
drivers/gpu/drm/gma500/mdfld_tpo_vid.c [new file with mode: 0644]
drivers/gpu/drm/gma500/mmu.c
drivers/gpu/drm/gma500/oaktrail_crtc.c
drivers/gpu/drm/gma500/oaktrail_device.c
drivers/gpu/drm/gma500/oaktrail_hdmi.c
drivers/gpu/drm/gma500/oaktrail_hdmi_i2c.c
drivers/gpu/drm/gma500/oaktrail_lvds.c
drivers/gpu/drm/gma500/power.c
drivers/gpu/drm/gma500/psb_device.c
drivers/gpu/drm/gma500/psb_drv.c
drivers/gpu/drm/gma500/psb_drv.h
drivers/gpu/drm/gma500/psb_intel_display.c
drivers/gpu/drm/gma500/psb_intel_lvds.c
drivers/gpu/drm/gma500/psb_intel_reg.h
drivers/gpu/drm/gma500/psb_intel_sdvo.c
drivers/gpu/drm/gma500/psb_irq.c
drivers/gpu/drm/gma500/psb_irq.h
drivers/gpu/drm/gma500/tc35876x-dsi-lvds.c [new file with mode: 0644]
drivers/gpu/drm/gma500/tc35876x-dsi-lvds.h [new file with mode: 0644]
drivers/gpu/drm/i2c/ch7006_drv.c
drivers/gpu/drm/i810/i810_dma.c
drivers/gpu/drm/i915/Makefile
drivers/gpu/drm/i915/i915_debugfs.c
drivers/gpu/drm/i915/i915_dma.c
drivers/gpu/drm/i915/i915_drv.c
drivers/gpu/drm/i915/i915_drv.h
drivers/gpu/drm/i915/i915_gem.c
drivers/gpu/drm/i915/i915_gem_evict.c
drivers/gpu/drm/i915/i915_gem_execbuffer.c
drivers/gpu/drm/i915/i915_gem_gtt.c
drivers/gpu/drm/i915/i915_gem_tiling.c
drivers/gpu/drm/i915/i915_irq.c
drivers/gpu/drm/i915/i915_mem.c [deleted file]
drivers/gpu/drm/i915/i915_reg.h
drivers/gpu/drm/i915/intel_acpi.c
drivers/gpu/drm/i915/intel_bios.c
drivers/gpu/drm/i915/intel_crt.c
drivers/gpu/drm/i915/intel_display.c
drivers/gpu/drm/i915/intel_dp.c
drivers/gpu/drm/i915/intel_drv.h
drivers/gpu/drm/i915/intel_dvo.c
drivers/gpu/drm/i915/intel_fb.c
drivers/gpu/drm/i915/intel_hdmi.c
drivers/gpu/drm/i915/intel_i2c.c
drivers/gpu/drm/i915/intel_lvds.c
drivers/gpu/drm/i915/intel_modes.c
drivers/gpu/drm/i915/intel_overlay.c
drivers/gpu/drm/i915/intel_panel.c
drivers/gpu/drm/i915/intel_ringbuffer.c
drivers/gpu/drm/i915/intel_ringbuffer.h
drivers/gpu/drm/i915/intel_sdvo.c
drivers/gpu/drm/i915/intel_sprite.c
drivers/gpu/drm/i915/intel_tv.c
drivers/gpu/drm/mga/mga_dma.c
drivers/gpu/drm/nouveau/Makefile
drivers/gpu/drm/nouveau/nouveau_bios.c
drivers/gpu/drm/nouveau/nouveau_bios.h
drivers/gpu/drm/nouveau/nouveau_connector.c
drivers/gpu/drm/nouveau/nouveau_crtc.h
drivers/gpu/drm/nouveau/nouveau_display.c
drivers/gpu/drm/nouveau/nouveau_dp.c
drivers/gpu/drm/nouveau/nouveau_drv.c
drivers/gpu/drm/nouveau/nouveau_drv.h
drivers/gpu/drm/nouveau/nouveau_encoder.h
drivers/gpu/drm/nouveau/nouveau_fbcon.c
drivers/gpu/drm/nouveau/nouveau_i2c.c
drivers/gpu/drm/nouveau/nouveau_mem.c
drivers/gpu/drm/nouveau/nouveau_mxm.c
drivers/gpu/drm/nouveau/nouveau_perf.c
drivers/gpu/drm/nouveau/nouveau_pm.c
drivers/gpu/drm/nouveau/nouveau_pm.h
drivers/gpu/drm/nouveau/nouveau_state.c
drivers/gpu/drm/nouveau/nv04_fb.c
drivers/gpu/drm/nouveau/nv10_fb.c
drivers/gpu/drm/nouveau/nv20_fb.c [new file with mode: 0644]
drivers/gpu/drm/nouveau/nv40_fb.c
drivers/gpu/drm/nouveau/nv50_crtc.c
drivers/gpu/drm/nouveau/nv50_dac.c
drivers/gpu/drm/nouveau/nv50_display.c
drivers/gpu/drm/nouveau/nv50_display.h
drivers/gpu/drm/nouveau/nv50_evo.h
drivers/gpu/drm/nouveau/nv50_pm.c
drivers/gpu/drm/nouveau/nv50_sor.c
drivers/gpu/drm/nouveau/nv50_vm.c
drivers/gpu/drm/nouveau/nv50_vram.c
drivers/gpu/drm/nouveau/nvc0_pm.c
drivers/gpu/drm/nouveau/nvc0_vm.c
drivers/gpu/drm/nouveau/nvc0_vram.c
drivers/gpu/drm/nouveau/nvd0_display.c
drivers/gpu/drm/r128/r128_drv.c
drivers/gpu/drm/radeon/Makefile
drivers/gpu/drm/radeon/atombios_crtc.c
drivers/gpu/drm/radeon/atombios_dp.c
drivers/gpu/drm/radeon/atombios_i2c.c [new file with mode: 0644]
drivers/gpu/drm/radeon/evergreen.c
drivers/gpu/drm/radeon/evergreen_blit_kms.c
drivers/gpu/drm/radeon/evergreen_cs.c
drivers/gpu/drm/radeon/evergreen_reg.h
drivers/gpu/drm/radeon/evergreend.h
drivers/gpu/drm/radeon/ni.c
drivers/gpu/drm/radeon/r100.c
drivers/gpu/drm/radeon/r200.c
drivers/gpu/drm/radeon/r300.c
drivers/gpu/drm/radeon/r420.c
drivers/gpu/drm/radeon/r500_reg.h
drivers/gpu/drm/radeon/r520.c
drivers/gpu/drm/radeon/r600.c
drivers/gpu/drm/radeon/r600_blit_kms.c
drivers/gpu/drm/radeon/r600_cs.c
drivers/gpu/drm/radeon/r600d.h
drivers/gpu/drm/radeon/radeon.h
drivers/gpu/drm/radeon/radeon_asic.c
drivers/gpu/drm/radeon/radeon_asic.h
drivers/gpu/drm/radeon/radeon_atombios.c
drivers/gpu/drm/radeon/radeon_benchmark.c
drivers/gpu/drm/radeon/radeon_blit_common.h [new file with mode: 0644]
drivers/gpu/drm/radeon/radeon_clocks.c
drivers/gpu/drm/radeon/radeon_connectors.c
drivers/gpu/drm/radeon/radeon_cp.c
drivers/gpu/drm/radeon/radeon_cs.c
drivers/gpu/drm/radeon/radeon_cursor.c
drivers/gpu/drm/radeon/radeon_display.c
drivers/gpu/drm/radeon/radeon_drv.c
drivers/gpu/drm/radeon/radeon_fb.c
drivers/gpu/drm/radeon/radeon_gem.c
drivers/gpu/drm/radeon/radeon_i2c.c
drivers/gpu/drm/radeon/radeon_kms.c
drivers/gpu/drm/radeon/radeon_legacy_crtc.c
drivers/gpu/drm/radeon/radeon_object.c
drivers/gpu/drm/radeon/radeon_object.h
drivers/gpu/drm/radeon/radeon_pm.c
drivers/gpu/drm/radeon/radeon_reg.h
drivers/gpu/drm/radeon/radeon_ring.c
drivers/gpu/drm/radeon/radeon_ttm.c
drivers/gpu/drm/radeon/reg_srcs/cayman
drivers/gpu/drm/radeon/reg_srcs/evergreen
drivers/gpu/drm/radeon/reg_srcs/r600
drivers/gpu/drm/radeon/rs400.c
drivers/gpu/drm/radeon/rs600.c
drivers/gpu/drm/radeon/rs690.c
drivers/gpu/drm/radeon/rv515.c
drivers/gpu/drm/radeon/rv770.c
drivers/gpu/drm/savage/savage_state.c
drivers/gpu/drm/sis/sis_drv.c
drivers/gpu/drm/ttm/ttm_agp_backend.c
drivers/gpu/drm/ttm/ttm_bo.c
drivers/gpu/drm/ttm/ttm_bo_vm.c
drivers/gpu/drm/ttm/ttm_memory.c
drivers/gpu/drm/ttm/ttm_object.c
drivers/gpu/drm/ttm/ttm_page_alloc.c
drivers/gpu/drm/ttm/ttm_page_alloc_dma.c
drivers/gpu/drm/ttm/ttm_tt.c
drivers/gpu/drm/udl/Kconfig [new file with mode: 0644]
drivers/gpu/drm/udl/Makefile [new file with mode: 0644]
drivers/gpu/drm/udl/udl_connector.c [new file with mode: 0644]
drivers/gpu/drm/udl/udl_drv.c [new file with mode: 0644]
drivers/gpu/drm/udl/udl_drv.h [new file with mode: 0644]
drivers/gpu/drm/udl/udl_encoder.c [new file with mode: 0644]
drivers/gpu/drm/udl/udl_fb.c [new file with mode: 0644]
drivers/gpu/drm/udl/udl_gem.c [new file with mode: 0644]
drivers/gpu/drm/udl/udl_main.c [new file with mode: 0644]
drivers/gpu/drm/udl/udl_modeset.c [new file with mode: 0644]
drivers/gpu/drm/udl/udl_transfer.c [new file with mode: 0644]
drivers/gpu/drm/via/via_map.c
drivers/gpu/drm/vmwgfx/vmwgfx_drv.c
drivers/gpu/drm/vmwgfx/vmwgfx_drv.h
drivers/gpu/drm/vmwgfx/vmwgfx_execbuf.c
drivers/gpu/drm/vmwgfx/vmwgfx_fb.c
drivers/gpu/drm/vmwgfx/vmwgfx_fence.c
drivers/gpu/drm/vmwgfx/vmwgfx_fence.h
drivers/gpu/drm/vmwgfx/vmwgfx_kms.c
drivers/gpu/drm/vmwgfx/vmwgfx_kms.h
drivers/gpu/drm/vmwgfx/vmwgfx_ldu.c
drivers/gpu/drm/vmwgfx/vmwgfx_scrn.c
drivers/i2c/algos/i2c-algo-bit.c
drivers/video/fbmem.c
drivers/video/udlfb.c
include/drm/drm.h
include/drm/drmP.h
include/drm/drm_crtc.h
include/drm/drm_edid.h
include/drm/drm_fb_helper.h
include/drm/exynos_drm.h
include/drm/gma_drm.h
include/drm/i915_drm.h
include/drm/intel-gtt.h
include/drm/radeon_drm.h
include/linux/fb.h
include/linux/i2c-algo-bit.h
include/linux/i2c/tc35876x.h [new file with mode: 0644]

diff --git a/Documentation/EDID/1024x768.S b/Documentation/EDID/1024x768.S
new file mode 100644 (file)
index 0000000..4b486fe
--- /dev/null
@@ -0,0 +1,44 @@
+/*
+   1024x768.S: EDID data set for standard 1024x768 60 Hz monitor
+
+   Copyright (C) 2011 Carsten Emde <C.Emde@osadl.org>
+
+   This program is free software; you can redistribute it and/or
+   modify it under the terms of the GNU General Public License
+   as published by the Free Software Foundation; either version 2
+   of the License, or (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA.
+*/
+
+/* EDID */
+#define VERSION 1
+#define REVISION 3
+
+/* Display */
+#define CLOCK 65000 /* kHz */
+#define XPIX 1024
+#define YPIX 768
+#define XY_RATIO XY_RATIO_4_3
+#define XBLANK 320
+#define YBLANK 38
+#define XOFFSET 8
+#define XPULSE 144
+#define YOFFSET (63+3)
+#define YPULSE (63+6)
+#define DPI 72
+#define VFREQ 60 /* Hz */
+#define TIMING_NAME "Linux XGA"
+#define ESTABLISHED_TIMINGS_BITS 0x08 /* Bit 3 -> 1024x768 @60 Hz */
+#define HSYNC_POL 0
+#define VSYNC_POL 0
+#define CRC 0x55
+
+#include "edid.S"
diff --git a/Documentation/EDID/1280x1024.S b/Documentation/EDID/1280x1024.S
new file mode 100644 (file)
index 0000000..a2799fe
--- /dev/null
@@ -0,0 +1,44 @@
+/*
+   1280x1024.S: EDID data set for standard 1280x1024 60 Hz monitor
+
+   Copyright (C) 2011 Carsten Emde <C.Emde@osadl.org>
+
+   This program is free software; you can redistribute it and/or
+   modify it under the terms of the GNU General Public License
+   as published by the Free Software Foundation; either version 2
+   of the License, or (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA.
+*/
+
+/* EDID */
+#define VERSION 1
+#define REVISION 3
+
+/* Display */
+#define CLOCK 108000 /* kHz */
+#define XPIX 1280
+#define YPIX 1024
+#define XY_RATIO XY_RATIO_5_4
+#define XBLANK 408
+#define YBLANK 42
+#define XOFFSET 48
+#define XPULSE 112
+#define YOFFSET (63+1)
+#define YPULSE (63+3)
+#define DPI 72
+#define VFREQ 60 /* Hz */
+#define TIMING_NAME "Linux SXGA"
+#define ESTABLISHED_TIMINGS_BITS 0x00 /* none */
+#define HSYNC_POL 1
+#define VSYNC_POL 1
+#define CRC 0xa0
+
+#include "edid.S"
diff --git a/Documentation/EDID/1680x1050.S b/Documentation/EDID/1680x1050.S
new file mode 100644 (file)
index 0000000..96f67ca
--- /dev/null
@@ -0,0 +1,44 @@
+/*
+   1680x1050.S: EDID data set for standard 1680x1050 60 Hz monitor
+
+   Copyright (C) 2012 Carsten Emde <C.Emde@osadl.org>
+
+   This program is free software; you can redistribute it and/or
+   modify it under the terms of the GNU General Public License
+   as published by the Free Software Foundation; either version 2
+   of the License, or (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA.
+*/
+
+/* EDID */
+#define VERSION 1
+#define REVISION 3
+
+/* Display */
+#define CLOCK 146250 /* kHz */
+#define XPIX 1680
+#define YPIX 1050
+#define XY_RATIO XY_RATIO_16_10
+#define XBLANK 560
+#define YBLANK 39
+#define XOFFSET 104
+#define XPULSE 176
+#define YOFFSET (63+3)
+#define YPULSE (63+6)
+#define DPI 96
+#define VFREQ 60 /* Hz */
+#define TIMING_NAME "Linux WSXGA"
+#define ESTABLISHED_TIMINGS_BITS 0x00 /* none */
+#define HSYNC_POL 1
+#define VSYNC_POL 1
+#define CRC 0x26
+
+#include "edid.S"
diff --git a/Documentation/EDID/1920x1080.S b/Documentation/EDID/1920x1080.S
new file mode 100644 (file)
index 0000000..36ed5d5
--- /dev/null
@@ -0,0 +1,44 @@
+/*
+   1920x1080.S: EDID data set for standard 1920x1080 60 Hz monitor
+
+   Copyright (C) 2012 Carsten Emde <C.Emde@osadl.org>
+
+   This program is free software; you can redistribute it and/or
+   modify it under the terms of the GNU General Public License
+   as published by the Free Software Foundation; either version 2
+   of the License, or (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA.
+*/
+
+/* EDID */
+#define VERSION 1
+#define REVISION 3
+
+/* Display */
+#define CLOCK 148500 /* kHz */
+#define XPIX 1920
+#define YPIX 1080
+#define XY_RATIO XY_RATIO_16_9
+#define XBLANK 280
+#define YBLANK 45
+#define XOFFSET 88
+#define XPULSE 44
+#define YOFFSET (63+4)
+#define YPULSE (63+5)
+#define DPI 96
+#define VFREQ 60 /* Hz */
+#define TIMING_NAME "Linux FHD"
+#define ESTABLISHED_TIMINGS_BITS 0x00 /* none */
+#define HSYNC_POL 1
+#define VSYNC_POL 1
+#define CRC 0x05
+
+#include "edid.S"
diff --git a/Documentation/EDID/HOWTO.txt b/Documentation/EDID/HOWTO.txt
new file mode 100644 (file)
index 0000000..75a9f2a
--- /dev/null
@@ -0,0 +1,39 @@
+In the good old days when graphics parameters were configured explicitly
+in a file called xorg.conf, even broken hardware could be managed.
+
+Today, with the advent of Kernel Mode Setting, a graphics board is
+either correctly working because all components follow the standards -
+or the computer is unusable, because the screen remains dark after
+booting or it displays the wrong area. Cases when this happens are:
+- The graphics board does not recognize the monitor.
+- The graphics board is unable to detect any EDID data.
+- The graphics board incorrectly forwards EDID data to the driver.
+- The monitor sends no or bogus EDID data.
+- A KVM sends its own EDID data instead of querying the connected monitor.
+Adding the kernel parameter "nomodeset" helps in most cases, but causes
+restrictions later on.
+
+As a remedy for such situations, the kernel configuration item
+CONFIG_DRM_LOAD_EDID_FIRMWARE was introduced. It allows to provide an
+individually prepared or corrected EDID data set in the /lib/firmware
+directory from where it is loaded via the firmware interface. The code
+(see drivers/gpu/drm/drm_edid_load.c) contains built-in data sets for
+commonly used screen resolutions (1024x768, 1280x1024, 1680x1050,
+1920x1080) as binary blobs, but the kernel source tree does not contain
+code to create these data. In order to elucidate the origin of the
+built-in binary EDID blobs and to facilitate the creation of individual
+data for a specific misbehaving monitor, commented sources and a
+Makefile environment are given here.
+
+To create binary EDID and C source code files from the existing data
+material, simply type "make".
+
+If you want to create your own EDID file, copy the file 1024x768.S and
+replace the settings with your own data. The CRC value in the last line
+  #define CRC 0x55
+is a bit tricky. After a first version of the binary data set is
+created, it must be be checked with the "edid-decode" utility which will
+most probably complain about a wrong CRC. Fortunately, the utility also
+displays the correct CRC which must then be inserted into the source
+file. After the make procedure is repeated, the EDID data set is ready
+to be used.
diff --git a/Documentation/EDID/Makefile b/Documentation/EDID/Makefile
new file mode 100644 (file)
index 0000000..17763ca
--- /dev/null
@@ -0,0 +1,26 @@
+
+SOURCES        := $(wildcard [0-9]*x[0-9]*.S)
+
+BIN    := $(patsubst %.S, %.bin, $(SOURCES))
+
+IHEX   := $(patsubst %.S, %.bin.ihex, $(SOURCES))
+
+CODE   := $(patsubst %.S, %.c, $(SOURCES))
+
+all:   $(BIN) $(IHEX) $(CODE)
+
+clean:
+       @rm -f *.o *.bin.ihex *.bin *.c
+
+%.o:   %.S
+       @cc -c $^
+
+%.bin: %.o
+       @objcopy -Obinary $^ $@
+
+%.bin.ihex:    %.o
+       @objcopy -Oihex $^ $@
+       @dos2unix $@ 2>/dev/null
+
+%.c:   %.bin
+       @echo "{" >$@; hexdump -f hex $^ >>$@; echo "};" >>$@
diff --git a/Documentation/EDID/edid.S b/Documentation/EDID/edid.S
new file mode 100644 (file)
index 0000000..ea97ae2
--- /dev/null
@@ -0,0 +1,261 @@
+/*
+   edid.S: EDID data template
+
+   Copyright (C) 2012 Carsten Emde <C.Emde@osadl.org>
+
+   This program is free software; you can redistribute it and/or
+   modify it under the terms of the GNU General Public License
+   as published by the Free Software Foundation; either version 2
+   of the License, or (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA.
+*/
+
+
+/* Manufacturer */
+#define MFG_LNX1 'L'
+#define MFG_LNX2 'N'
+#define MFG_LNX3 'X'
+#define SERIAL 0
+#define YEAR 2012
+#define WEEK 5
+
+/* EDID 1.3 standard definitions */
+#define XY_RATIO_16_10 0b00
+#define XY_RATIO_4_3   0b01
+#define XY_RATIO_5_4   0b10
+#define XY_RATIO_16_9  0b11
+
+#define mfgname2id(v1,v2,v3) \
+       ((((v1-'@')&0x1f)<<10)+(((v2-'@')&0x1f)<<5)+((v3-'@')&0x1f))
+#define swap16(v1) ((v1>>8)+((v1&0xff)<<8))
+#define msbs2(v1,v2) ((((v1>>8)&0x0f)<<4)+((v2>>8)&0x0f))
+#define msbs4(v1,v2,v3,v4) \
+       (((v1&0x03)>>2)+((v2&0x03)>>4)+((v3&0x03)>>6)+((v4&0x03)>>8))
+#define pixdpi2mm(pix,dpi) ((pix*25)/dpi)
+#define xsize pixdpi2mm(XPIX,DPI)
+#define ysize pixdpi2mm(YPIX,DPI)
+
+               .data
+
+/* Fixed header pattern */
+header:                .byte   0x00,0xff,0xff,0xff,0xff,0xff,0xff,0x00
+
+mfg_id:                .word   swap16(mfgname2id(MFG_LNX1, MFG_LNX2, MFG_LNX3))
+
+prod_code:     .word   0
+
+/* Serial number. 32 bits, little endian. */
+serial_number: .long   SERIAL
+
+/* Week of manufacture */
+week:          .byte   WEEK
+
+/* Year of manufacture, less 1990. (1990-2245)
+   If week=255, it is the model year instead */
+year:          .byte   YEAR-1990
+
+version:       .byte   VERSION         /* EDID version, usually 1 (for 1.3) */
+revision:      .byte   REVISION        /* EDID revision, usually 3 (for 1.3) */
+
+/* If Bit 7=1  Digital input. If set, the following bit definitions apply:
+     Bits 6-1  Reserved, must be 0
+     Bit 0     Signal is compatible with VESA DFP 1.x TMDS CRGB,
+                 1 pixel per clock, up to 8 bits per color, MSB aligned,
+   If Bit 7=0  Analog input. If clear, the following bit definitions apply:
+     Bits 6-5  Video white and sync levels, relative to blank
+                 00=+0.7/-0.3 V; 01=+0.714/-0.286 V;
+                 10=+1.0/-0.4 V; 11=+0.7/0 V
+   Bit 4       Blank-to-black setup (pedestal) expected
+   Bit 3       Separate sync supported
+   Bit 2       Composite sync (on HSync) supported
+   Bit 1       Sync on green supported
+   Bit 0       VSync pulse must be serrated when somposite or
+                 sync-on-green is used. */
+video_parms:   .byte   0x6d
+
+/* Maximum horizontal image size, in centimetres
+   (max 292 cm/115 in at 16:9 aspect ratio) */
+max_hor_size:  .byte   xsize/10
+
+/* Maximum vertical image size, in centimetres.
+   If either byte is 0, undefined (e.g. projector) */
+max_vert_size: .byte   ysize/10
+
+/* Display gamma, minus 1, times 100 (range 1.00-3.5 */
+gamma:         .byte   120
+
+/* Bit 7       DPMS standby supported
+   Bit 6       DPMS suspend supported
+   Bit 5       DPMS active-off supported
+   Bits 4-3    Display type: 00=monochrome; 01=RGB colour;
+                 10=non-RGB multicolour; 11=undefined
+   Bit 2       Standard sRGB colour space. Bytes 25-34 must contain
+                 sRGB standard values.
+   Bit 1       Preferred timing mode specified in descriptor block 1.
+   Bit 0       GTF supported with default parameter values. */
+dsp_features:  .byte   0xea
+
+/* Chromaticity coordinates. */
+/* Red and green least-significant bits
+   Bits 7-6    Red x value least-significant 2 bits
+   Bits 5-4    Red y value least-significant 2 bits
+   Bits 3-2    Green x value lst-significant 2 bits
+   Bits 1-0    Green y value least-significant 2 bits */
+red_green_lsb: .byte   0x5e
+
+/* Blue and white least-significant 2 bits */
+blue_white_lsb:        .byte   0xc0
+
+/* Red x value most significant 8 bits.
+   0-255 encodes 0-0.996 (255/256); 0-0.999 (1023/1024) with lsbits */
+red_x_msb:     .byte   0xa4
+
+/* Red y value most significant 8 bits */
+red_y_msb:     .byte   0x59
+
+/* Green x and y value most significant 8 bits */
+green_x_y_msb: .byte   0x4a,0x98
+
+/* Blue x and y value most significant 8 bits */
+blue_x_y_msb:  .byte   0x25,0x20
+
+/* Default white point x and y value most significant 8 bits */
+white_x_y_msb: .byte   0x50,0x54
+
+/* Established timings */
+/* Bit 7       720x400 @ 70 Hz
+   Bit 6       720x400 @ 88 Hz
+   Bit 5       640x480 @ 60 Hz
+   Bit 4       640x480 @ 67 Hz
+   Bit 3       640x480 @ 72 Hz
+   Bit 2       640x480 @ 75 Hz
+   Bit 1       800x600 @ 56 Hz
+   Bit 0       800x600 @ 60 Hz */
+estbl_timing1: .byte   0x00
+
+/* Bit 7       800x600 @ 72 Hz
+   Bit 6       800x600 @ 75 Hz
+   Bit 5       832x624 @ 75 Hz
+   Bit 4       1024x768 @ 87 Hz, interlaced (1024x768)
+   Bit 3       1024x768 @ 60 Hz
+   Bit 2       1024x768 @ 72 Hz
+   Bit 1       1024x768 @ 75 Hz
+   Bit 0       1280x1024 @ 75 Hz */
+estbl_timing2: .byte   ESTABLISHED_TIMINGS_BITS
+
+/* Bit 7       1152x870 @ 75 Hz (Apple Macintosh II)
+   Bits 6-0    Other manufacturer-specific display mod */
+estbl_timing3: .byte   0x00
+
+/* Standard timing */
+/* X resolution, less 31, divided by 8 (256-2288 pixels) */
+std_xres:      .byte   (XPIX/8)-31
+/* Y resolution, X:Y pixel ratio
+   Bits 7-6    X:Y pixel ratio: 00=16:10; 01=4:3; 10=5:4; 11=16:9.
+   Bits 5-0    Vertical frequency, less 60 (60-123 Hz) */
+std_vres:      .byte   (XY_RATIO<<6)+VFREQ-60
+               .fill   7,2,0x0101      /* Unused */
+
+descriptor1:
+/* Pixel clock in 10 kHz units. (0.-655.35 MHz, little-endian) */
+clock:         .word   CLOCK/10
+
+/* Horizontal active pixels 8 lsbits (0-4095) */
+x_act_lsb:     .byte   XPIX&0xff
+/* Horizontal blanking pixels 8 lsbits (0-4095)
+   End of active to start of next active. */
+x_blk_lsb:     .byte   XBLANK&0xff
+/* Bits 7-4    Horizontal active pixels 4 msbits
+   Bits 3-0    Horizontal blanking pixels 4 msbits */
+x_msbs:                .byte   msbs2(XPIX,XBLANK)
+
+/* Vertical active lines 8 lsbits (0-4095) */
+y_act_lsb:     .byte   YPIX&0xff
+/* Vertical blanking lines 8 lsbits (0-4095) */
+y_blk_lsb:     .byte   YBLANK&0xff
+/* Bits 7-4    Vertical active lines 4 msbits
+   Bits 3-0    Vertical blanking lines 4 msbits */
+y_msbs:                .byte   msbs2(YPIX,YBLANK)
+
+/* Horizontal sync offset pixels 8 lsbits (0-1023) From blanking start */
+x_snc_off_lsb: .byte   XOFFSET&0xff
+/* Horizontal sync pulse width pixels 8 lsbits (0-1023) */
+x_snc_pls_lsb: .byte   XPULSE&0xff
+/* Bits 7-4    Vertical sync offset lines 4 lsbits -63)
+   Bits 3-0    Vertical sync pulse width lines 4 lsbits -63) */
+y_snc_lsb:     .byte   ((YOFFSET-63)<<4)+(YPULSE-63)
+/* Bits 7-6    Horizontal sync offset pixels 2 msbits
+   Bits 5-4    Horizontal sync pulse width pixels 2 msbits
+   Bits 3-2    Vertical sync offset lines 2 msbits
+   Bits 1-0    Vertical sync pulse width lines 2 msbits */
+xy_snc_msbs:   .byte   msbs4(XOFFSET,XPULSE,YOFFSET,YPULSE)
+
+/* Horizontal display size, mm, 8 lsbits (0-4095 mm, 161 in) */
+x_dsp_size:    .byte   xsize&0xff
+
+/* Vertical display size, mm, 8 lsbits (0-4095 mm, 161 in) */
+y_dsp_size:    .byte   ysize&0xff
+
+/* Bits 7-4    Horizontal display size, mm, 4 msbits
+   Bits 3-0    Vertical display size, mm, 4 msbits */
+dsp_size_mbsb: .byte   msbs2(xsize,ysize)
+
+/* Horizontal border pixels (each side; total is twice this) */
+x_border:      .byte   0
+/* Vertical border lines (each side; total is twice this) */
+y_border:      .byte   0
+
+/* Bit 7       Interlaced
+   Bits 6-5    Stereo mode: 00=No stereo; other values depend on bit 0:
+   Bit 0=0: 01=Field sequential, sync=1 during right; 10=similar,
+     sync=1 during left; 11=4-way interleaved stereo
+   Bit 0=1 2-way interleaved stereo: 01=Right image on even lines;
+     10=Left image on even lines; 11=side-by-side
+   Bits 4-3    Sync type: 00=Analog composite; 01=Bipolar analog composite;
+     10=Digital composite (on HSync); 11=Digital separate
+   Bit 2       If digital separate: Vertical sync polarity (1=positive)
+   Other types: VSync serrated (HSync during VSync)
+   Bit 1       If analog sync: Sync on all 3 RGB lines (else green only)
+   Digital: HSync polarity (1=positive)
+   Bit 0       2-way line-interleaved stereo, if bits 4-3 are not 00. */
+features:      .byte   0x18+(VSYNC_POL<<2)+(HSYNC_POL<<1)
+
+descriptor2:   .byte   0,0     /* Not a detailed timing descriptor */
+               .byte   0       /* Must be zero */
+               .byte   0xff    /* Descriptor is monitor serial number (text) */
+               .byte   0       /* Must be zero */
+start1:                .ascii  "Linux #0"
+end1:          .byte   0x0a    /* End marker */
+               .fill   12-(end1-start1), 1, 0x20 /* Padded spaces */
+descriptor3:   .byte   0,0     /* Not a detailed timing descriptor */
+               .byte   0       /* Must be zero */
+               .byte   0xfd    /* Descriptor is monitor range limits */
+               .byte   0       /* Must be zero */
+start2:                .byte   VFREQ-1 /* Minimum vertical field rate (1-255 Hz) */
+               .byte   VFREQ+1 /* Maximum vertical field rate (1-255 Hz) */
+               .byte   (CLOCK/(XPIX+XBLANK))-1 /* Minimum horizontal line rate
+                                                   (1-255 kHz) */
+               .byte   (CLOCK/(XPIX+XBLANK))+1 /* Maximum horizontal line rate
+                                                   (1-255 kHz) */
+               .byte   (CLOCK/10000)+1 /* Maximum pixel clock rate, rounded up
+                                          to 10 MHz multiple (10-2550 MHz) */
+               .byte   0       /* No extended timing information type */
+end2:          .byte   0x0a    /* End marker */
+               .fill   12-(end2-start2), 1, 0x20 /* Padded spaces */
+descriptor4:   .byte   0,0     /* Not a detailed timing descriptor */
+               .byte   0       /* Must be zero */
+               .byte   0xfc    /* Descriptor is text */
+               .byte   0       /* Must be zero */
+start3:                .ascii  TIMING_NAME
+end3:          .byte   0x0a    /* End marker */
+               .fill   12-(end3-start3), 1, 0x20 /* Padded spaces */
+extensions:    .byte   0       /* Number of extensions to follow */
+checksum:      .byte   CRC     /* Sum of all bytes must be 0 */
diff --git a/Documentation/EDID/hex b/Documentation/EDID/hex
new file mode 100644 (file)
index 0000000..8873ebb
--- /dev/null
@@ -0,0 +1 @@
+"\t" 8/1 "0x%02x, " "\n"
index 7986d79d9d17030a73fb6efbe1ae02ddece06342..247dcfd62034612e09dce56d6d6fffff4f0c957a 100644 (file)
@@ -713,6 +713,21 @@ bytes respectively. Such letter suffixes can also be entirely omitted.
                        The filter can be disabled or changed to another
                        driver later using sysfs.
 
+       drm_kms_helper.edid_firmware=[<connector>:]<file>
+                       Broken monitors, graphic adapters and KVMs may
+                       send no or incorrect EDID data sets. This parameter
+                       allows to specify an EDID data set in the
+                       /lib/firmware directory that is used instead.
+                       Generic built-in EDID data sets are used, if one of
+                       edid/1024x768.bin, edid/1280x1024.bin,
+                       edid/1680x1050.bin, or edid/1920x1080.bin is given
+                       and no file with the same name exists. Details and
+                       instructions how to build your own EDID data are
+                       available in Documentation/EDID/HOWTO.txt. An EDID
+                       data set will only be used for a particular connector,
+                       if its name and a colon are prepended to the EDID
+                       name.
+
        dscc4.setup=    [NET]
 
        earlycon=       [KNL] Output early console device and options.
index 721e65285dce0bdc3063ef0cadca27b1c8a1c38e..e0a37233c0af7ca57362d23b3aba75a4d8bca30e 100644 (file)
@@ -28,6 +28,8 @@
 #include <linux/module.h>
 #include <linux/notifier.h>
 #include <linux/mfd/intel_msic.h>
+#include <linux/gpio.h>
+#include <linux/i2c/tc35876x.h>
 
 #include <asm/setup.h>
 #include <asm/mpspec_def.h>
@@ -675,6 +677,19 @@ static void *msic_thermal_platform_data(void *info)
        return msic_generic_platform_data(info, INTEL_MSIC_BLOCK_THERMAL);
 }
 
+/* tc35876x DSI-LVDS bridge chip and panel platform data */
+static void *tc35876x_platform_data(void *data)
+{
+       static struct tc35876x_platform_data pdata;
+
+       /* gpio pins set to -1 will not be used by the driver */
+       pdata.gpio_bridge_reset = get_gpio_by_name("LCMB_RXEN");
+       pdata.gpio_panel_bl_en = get_gpio_by_name("6S6P_BL_EN");
+       pdata.gpio_panel_vadd = get_gpio_by_name("EN_VREG_LCD_V3P3");
+
+       return &pdata;
+}
+
 static const struct devs_id __initconst device_ids[] = {
        {"bma023", SFI_DEV_TYPE_I2C, 1, &no_platform_data},
        {"pmic_gpio", SFI_DEV_TYPE_SPI, 1, &pmic_gpio_platform_data},
@@ -687,6 +702,7 @@ static const struct devs_id __initconst device_ids[] = {
        {"i2c_accel", SFI_DEV_TYPE_I2C, 0, &lis331dl_platform_data},
        {"pmic_audio", SFI_DEV_TYPE_IPC, 1, &no_platform_data},
        {"mpu3050", SFI_DEV_TYPE_I2C, 1, &mpu3050_platform_data},
+       {"i2c_disp_brig", SFI_DEV_TYPE_I2C, 0, &tc35876x_platform_data},
 
        /* MSIC subdevices */
        {"msic_battery", SFI_DEV_TYPE_IPC, 1, &msic_battery_platform_data},
index b427711be4be385c2704d4c971dc5cd512ed85c4..962e75dc47810a0c7d0eac323d596b2be507189c 100644 (file)
@@ -850,6 +850,7 @@ static struct pci_device_id agp_intel_pci_table[] = {
        .subvendor      = PCI_ANY_ID,                   \
        .subdevice      = PCI_ANY_ID,                   \
        }
+       ID(PCI_DEVICE_ID_INTEL_82441), /* for HAS2 support */
        ID(PCI_DEVICE_ID_INTEL_82443LX_0),
        ID(PCI_DEVICE_ID_INTEL_82443BX_0),
        ID(PCI_DEVICE_ID_INTEL_82443GX_0),
index c92424ca1a55370dcdaa7798cb6eb1ae6fbdd90c..5cf47ac2d401d2551661355d2d5fbaccfccdb72e 100644 (file)
@@ -76,7 +76,6 @@ static struct _intel_private {
        struct resource ifp_resource;
        int resource_valid;
        struct page *scratch_page;
-       dma_addr_t scratch_page_dma;
 } intel_private;
 
 #define INTEL_GTT_GEN  intel_private.driver->gen
@@ -306,9 +305,9 @@ static int intel_gtt_setup_scratch_page(void)
                if (pci_dma_mapping_error(intel_private.pcidev, dma_addr))
                        return -EINVAL;
 
-               intel_private.scratch_page_dma = dma_addr;
+               intel_private.base.scratch_page_dma = dma_addr;
        } else
-               intel_private.scratch_page_dma = page_to_phys(page);
+               intel_private.base.scratch_page_dma = page_to_phys(page);
 
        intel_private.scratch_page = page;
 
@@ -631,7 +630,7 @@ static unsigned int intel_gtt_mappable_entries(void)
 static void intel_gtt_teardown_scratch_page(void)
 {
        set_pages_wb(intel_private.scratch_page, 1);
-       pci_unmap_page(intel_private.pcidev, intel_private.scratch_page_dma,
+       pci_unmap_page(intel_private.pcidev, intel_private.base.scratch_page_dma,
                       PAGE_SIZE, PCI_DMA_BIDIRECTIONAL);
        put_page(intel_private.scratch_page);
        __free_page(intel_private.scratch_page);
@@ -681,6 +680,7 @@ static int intel_gtt_init(void)
                iounmap(intel_private.registers);
                return -ENOMEM;
        }
+       intel_private.base.gtt = intel_private.gtt;
 
        global_cache_flush();   /* FIXME: ? */
 
@@ -975,7 +975,7 @@ void intel_gtt_clear_range(unsigned int first_entry, unsigned int num_entries)
        unsigned int i;
 
        for (i = first_entry; i < (first_entry + num_entries); i++) {
-               intel_private.driver->write_entry(intel_private.scratch_page_dma,
+               intel_private.driver->write_entry(intel_private.base.scratch_page_dma,
                                                  i, 0);
        }
        readl(intel_private.gtt+i-1);
index 2418429a98360fd996888e5ccce6189bad9d9a86..87ca18b82e153188f497c09aee122c852f0f9c84 100644 (file)
@@ -18,6 +18,11 @@ menuconfig DRM
          details.  You should also select and configure AGP
          (/dev/agpgart) support if it is available for your platform.
 
+config DRM_USB
+       tristate
+       depends on DRM
+       select USB
+
 config DRM_KMS_HELPER
        tristate
        depends on DRM
@@ -27,6 +32,18 @@ config DRM_KMS_HELPER
        help
          FB and CRTC helpers for KMS drivers.
 
+config DRM_LOAD_EDID_FIRMWARE
+       bool "Allow to specify an EDID data set instead of probing for it"
+       depends on DRM_KMS_HELPER
+       help
+         Say Y here, if you want to use EDID data to be loaded from the
+         /lib/firmware directory or one of the provided built-in
+         data sets. This may be necessary, if the graphics adapter or
+         monitor are unable to provide appropriate EDID data. Since this
+         feature is provided as a workaround for broken hardware, the
+         default case is N. Details and instructions how to build your own
+         EDID data are given in Documentation/EDID/HOWTO.txt.
+
 config DRM_TTM
        tristate
        depends on DRM
@@ -165,3 +182,4 @@ source "drivers/gpu/drm/vmwgfx/Kconfig"
 
 source "drivers/gpu/drm/gma500/Kconfig"
 
+source "drivers/gpu/drm/udl/Kconfig"
index 0cde1b80fdb198a074d1b30e7cf8d330852c6fa8..a858532806ae34ae341c608d5c985a577992739e 100644 (file)
@@ -12,17 +12,21 @@ drm-y       :=      drm_auth.o drm_buffer.o drm_bufs.o drm_cache.o \
                drm_platform.o drm_sysfs.o drm_hashtab.o drm_mm.o \
                drm_crtc.o drm_modes.o drm_edid.o \
                drm_info.o drm_debugfs.o drm_encoder_slave.o \
-               drm_trace_points.o drm_global.o drm_usb.o
+               drm_trace_points.o drm_global.o
 
 drm-$(CONFIG_COMPAT) += drm_ioc32.o
 
+drm-usb-y   := drm_usb.o
+
 drm_kms_helper-y := drm_fb_helper.o drm_crtc_helper.o drm_dp_i2c_helper.o
+drm_kms_helper-$(CONFIG_DRM_LOAD_EDID_FIRMWARE) += drm_edid_load.o
 
 obj-$(CONFIG_DRM_KMS_HELPER) += drm_kms_helper.o
 
 CFLAGS_drm_trace_points.o := -I$(src)
 
 obj-$(CONFIG_DRM)      += drm.o
+obj-$(CONFIG_DRM_USB)   += drm_usb.o
 obj-$(CONFIG_DRM_TTM)  += ttm/
 obj-$(CONFIG_DRM_TDFX) += tdfx/
 obj-$(CONFIG_DRM_R128) += r128/
@@ -37,4 +41,5 @@ obj-$(CONFIG_DRM_VIA) +=via/
 obj-$(CONFIG_DRM_NOUVEAU) +=nouveau/
 obj-$(CONFIG_DRM_EXYNOS) +=exynos/
 obj-$(CONFIG_DRM_GMA500) += gma500/
+obj-$(CONFIG_DRM_UDL) += udl/
 obj-y                  += i2c/
index 5e818a808acee995cc2f0e73e0abb84f6bb60894..d3aaeb6ae2362167f360d3a8d1aa846028714fdd 100644 (file)
 #include "drm_edid.h"
 #include "drm_fourcc.h"
 
-struct drm_prop_enum_list {
-       int type;
-       char *name;
-};
-
 /* Avoid boilerplate.  I'm tired of typing. */
 #define DRM_ENUM_NAME_FN(fnname, list)                         \
        char *fnname(int val)                                   \
@@ -298,9 +293,8 @@ int drm_framebuffer_init(struct drm_device *dev, struct drm_framebuffer *fb,
        int ret;
 
        ret = drm_mode_object_get(dev, &fb->base, DRM_MODE_OBJECT_FB);
-       if (ret) {
+       if (ret)
                return ret;
-       }
 
        fb->dev = dev;
        fb->funcs = funcs;
@@ -370,19 +364,31 @@ EXPORT_SYMBOL(drm_framebuffer_cleanup);
  * Caller must hold mode config lock.
  *
  * Inits a new object created as base part of an driver crtc object.
+ *
+ * RETURNS:
+ * Zero on success, error code on failure.
  */
-void drm_crtc_init(struct drm_device *dev, struct drm_crtc *crtc,
+int drm_crtc_init(struct drm_device *dev, struct drm_crtc *crtc,
                   const struct drm_crtc_funcs *funcs)
 {
+       int ret;
+
        crtc->dev = dev;
        crtc->funcs = funcs;
 
        mutex_lock(&dev->mode_config.mutex);
-       drm_mode_object_get(dev, &crtc->base, DRM_MODE_OBJECT_CRTC);
+
+       ret = drm_mode_object_get(dev, &crtc->base, DRM_MODE_OBJECT_CRTC);
+       if (ret)
+               goto out;
 
        list_add_tail(&crtc->head, &dev->mode_config.crtc_list);
        dev->mode_config.num_crtc++;
+
+ out:
        mutex_unlock(&dev->mode_config.mutex);
+
+       return ret;
 }
 EXPORT_SYMBOL(drm_crtc_init);
 
@@ -442,7 +448,7 @@ void drm_mode_remove(struct drm_connector *connector,
                     struct drm_display_mode *mode)
 {
        list_del(&mode->head);
-       kfree(mode);
+       drm_mode_destroy(connector->dev, mode);
 }
 EXPORT_SYMBOL(drm_mode_remove);
 
@@ -454,21 +460,29 @@ EXPORT_SYMBOL(drm_mode_remove);
  * @name: user visible name of the connector
  *
  * LOCKING:
- * Caller must hold @dev's mode_config lock.
+ * Takes mode config lock.
  *
  * Initialises a preallocated connector. Connectors should be
  * subclassed as part of driver connector objects.
+ *
+ * RETURNS:
+ * Zero on success, error code on failure.
  */
-void drm_connector_init(struct drm_device *dev,
-                    struct drm_connector *connector,
-                    const struct drm_connector_funcs *funcs,
-                    int connector_type)
+int drm_connector_init(struct drm_device *dev,
+                      struct drm_connector *connector,
+                      const struct drm_connector_funcs *funcs,
+                      int connector_type)
 {
+       int ret;
+
        mutex_lock(&dev->mode_config.mutex);
 
+       ret = drm_mode_object_get(dev, &connector->base, DRM_MODE_OBJECT_CONNECTOR);
+       if (ret)
+               goto out;
+
        connector->dev = dev;
        connector->funcs = funcs;
-       drm_mode_object_get(dev, &connector->base, DRM_MODE_OBJECT_CONNECTOR);
        connector->connector_type = connector_type;
        connector->connector_type_id =
                ++drm_connector_enum_list[connector_type].count; /* TODO */
@@ -488,7 +502,10 @@ void drm_connector_init(struct drm_device *dev,
        drm_connector_attach_property(connector,
                                      dev->mode_config.dpms_property, 0);
 
+ out:
        mutex_unlock(&dev->mode_config.mutex);
+
+       return ret;
 }
 EXPORT_SYMBOL(drm_connector_init);
 
@@ -497,7 +514,7 @@ EXPORT_SYMBOL(drm_connector_init);
  * @connector: connector to cleanup
  *
  * LOCKING:
- * Caller must hold @dev's mode_config lock.
+ * Takes mode config lock.
  *
  * Cleans up the connector but doesn't free the object.
  */
@@ -523,23 +540,41 @@ void drm_connector_cleanup(struct drm_connector *connector)
 }
 EXPORT_SYMBOL(drm_connector_cleanup);
 
-void drm_encoder_init(struct drm_device *dev,
+void drm_connector_unplug_all(struct drm_device *dev)
+{
+       struct drm_connector *connector;
+
+       /* taking the mode config mutex ends up in a clash with sysfs */
+       list_for_each_entry(connector, &dev->mode_config.connector_list, head)
+               drm_sysfs_connector_remove(connector);
+
+}
+EXPORT_SYMBOL(drm_connector_unplug_all);
+
+int drm_encoder_init(struct drm_device *dev,
                      struct drm_encoder *encoder,
                      const struct drm_encoder_funcs *funcs,
                      int encoder_type)
 {
+       int ret;
+
        mutex_lock(&dev->mode_config.mutex);
 
-       encoder->dev = dev;
+       ret = drm_mode_object_get(dev, &encoder->base, DRM_MODE_OBJECT_ENCODER);
+       if (ret)
+               goto out;
 
-       drm_mode_object_get(dev, &encoder->base, DRM_MODE_OBJECT_ENCODER);
+       encoder->dev = dev;
        encoder->encoder_type = encoder_type;
        encoder->funcs = funcs;
 
        list_add_tail(&encoder->head, &dev->mode_config.encoder_list);
        dev->mode_config.num_encoder++;
 
+ out:
        mutex_unlock(&dev->mode_config.mutex);
+
+       return ret;
 }
 EXPORT_SYMBOL(drm_encoder_init);
 
@@ -560,18 +595,23 @@ int drm_plane_init(struct drm_device *dev, struct drm_plane *plane,
                   const uint32_t *formats, uint32_t format_count,
                   bool priv)
 {
+       int ret;
+
        mutex_lock(&dev->mode_config.mutex);
 
+       ret = drm_mode_object_get(dev, &plane->base, DRM_MODE_OBJECT_PLANE);
+       if (ret)
+               goto out;
+
        plane->dev = dev;
-       drm_mode_object_get(dev, &plane->base, DRM_MODE_OBJECT_PLANE);
        plane->funcs = funcs;
        plane->format_types = kmalloc(sizeof(uint32_t) * format_count,
                                      GFP_KERNEL);
        if (!plane->format_types) {
                DRM_DEBUG_KMS("out of memory when allocating plane\n");
                drm_mode_object_put(dev, &plane->base);
-               mutex_unlock(&dev->mode_config.mutex);
-               return -ENOMEM;
+               ret = -ENOMEM;
+               goto out;
        }
 
        memcpy(plane->format_types, formats, format_count * sizeof(uint32_t));
@@ -589,9 +629,10 @@ int drm_plane_init(struct drm_device *dev, struct drm_plane *plane,
                INIT_LIST_HEAD(&plane->head);
        }
 
+ out:
        mutex_unlock(&dev->mode_config.mutex);
 
-       return 0;
+       return ret;
 }
 EXPORT_SYMBOL(drm_plane_init);
 
@@ -631,7 +672,11 @@ struct drm_display_mode *drm_mode_create(struct drm_device *dev)
        if (!nmode)
                return NULL;
 
-       drm_mode_object_get(dev, &nmode->base, DRM_MODE_OBJECT_MODE);
+       if (drm_mode_object_get(dev, &nmode->base, DRM_MODE_OBJECT_MODE)) {
+               kfree(nmode);
+               return NULL;
+       }
+
        return nmode;
 }
 EXPORT_SYMBOL(drm_mode_create);
@@ -648,6 +693,9 @@ EXPORT_SYMBOL(drm_mode_create);
  */
 void drm_mode_destroy(struct drm_device *dev, struct drm_display_mode *mode)
 {
+       if (!mode)
+               return;
+
        drm_mode_object_put(dev, &mode->base);
 
        kfree(mode);
@@ -658,7 +706,6 @@ static int drm_mode_create_standard_connector_properties(struct drm_device *dev)
 {
        struct drm_property *edid;
        struct drm_property *dpms;
-       int i;
 
        /*
         * Standard properties (apply to all connectors)
@@ -668,11 +715,9 @@ static int drm_mode_create_standard_connector_properties(struct drm_device *dev)
                                   "EDID", 0);
        dev->mode_config.edid_property = edid;
 
-       dpms = drm_property_create(dev, DRM_MODE_PROP_ENUM,
-                                  "DPMS", ARRAY_SIZE(drm_dpms_enum_list));
-       for (i = 0; i < ARRAY_SIZE(drm_dpms_enum_list); i++)
-               drm_property_add_enum(dpms, i, drm_dpms_enum_list[i].type,
-                                     drm_dpms_enum_list[i].name);
+       dpms = drm_property_create_enum(dev, 0,
+                                  "DPMS", drm_dpms_enum_list,
+                                  ARRAY_SIZE(drm_dpms_enum_list));
        dev->mode_config.dpms_property = dpms;
 
        return 0;
@@ -688,30 +733,21 @@ int drm_mode_create_dvi_i_properties(struct drm_device *dev)
 {
        struct drm_property *dvi_i_selector;
        struct drm_property *dvi_i_subconnector;
-       int i;
 
        if (dev->mode_config.dvi_i_select_subconnector_property)
                return 0;
 
        dvi_i_selector =
-               drm_property_create(dev, DRM_MODE_PROP_ENUM,
+               drm_property_create_enum(dev, 0,
                                    "select subconnector",
+                                   drm_dvi_i_select_enum_list,
                                    ARRAY_SIZE(drm_dvi_i_select_enum_list));
-       for (i = 0; i < ARRAY_SIZE(drm_dvi_i_select_enum_list); i++)
-               drm_property_add_enum(dvi_i_selector, i,
-                                     drm_dvi_i_select_enum_list[i].type,
-                                     drm_dvi_i_select_enum_list[i].name);
        dev->mode_config.dvi_i_select_subconnector_property = dvi_i_selector;
 
-       dvi_i_subconnector =
-               drm_property_create(dev, DRM_MODE_PROP_ENUM |
-                                   DRM_MODE_PROP_IMMUTABLE,
+       dvi_i_subconnector = drm_property_create_enum(dev, DRM_MODE_PROP_IMMUTABLE,
                                    "subconnector",
+                                   drm_dvi_i_subconnector_enum_list,
                                    ARRAY_SIZE(drm_dvi_i_subconnector_enum_list));
-       for (i = 0; i < ARRAY_SIZE(drm_dvi_i_subconnector_enum_list); i++)
-               drm_property_add_enum(dvi_i_subconnector, i,
-                                     drm_dvi_i_subconnector_enum_list[i].type,
-                                     drm_dvi_i_subconnector_enum_list[i].name);
        dev->mode_config.dvi_i_subconnector_property = dvi_i_subconnector;
 
        return 0;
@@ -742,51 +778,33 @@ int drm_mode_create_tv_properties(struct drm_device *dev, int num_modes,
        /*
         * Basic connector properties
         */
-       tv_selector = drm_property_create(dev, DRM_MODE_PROP_ENUM,
+       tv_selector = drm_property_create_enum(dev, 0,
                                          "select subconnector",
+                                         drm_tv_select_enum_list,
                                          ARRAY_SIZE(drm_tv_select_enum_list));
-       for (i = 0; i < ARRAY_SIZE(drm_tv_select_enum_list); i++)
-               drm_property_add_enum(tv_selector, i,
-                                     drm_tv_select_enum_list[i].type,
-                                     drm_tv_select_enum_list[i].name);
        dev->mode_config.tv_select_subconnector_property = tv_selector;
 
        tv_subconnector =
-               drm_property_create(dev, DRM_MODE_PROP_ENUM |
-                                   DRM_MODE_PROP_IMMUTABLE, "subconnector",
+               drm_property_create_enum(dev, DRM_MODE_PROP_IMMUTABLE,
+                                   "subconnector",
+                                   drm_tv_subconnector_enum_list,
                                    ARRAY_SIZE(drm_tv_subconnector_enum_list));
-       for (i = 0; i < ARRAY_SIZE(drm_tv_subconnector_enum_list); i++)
-               drm_property_add_enum(tv_subconnector, i,
-                                     drm_tv_subconnector_enum_list[i].type,
-                                     drm_tv_subconnector_enum_list[i].name);
        dev->mode_config.tv_subconnector_property = tv_subconnector;
 
        /*
         * Other, TV specific properties: margins & TV modes.
         */
        dev->mode_config.tv_left_margin_property =
-               drm_property_create(dev, DRM_MODE_PROP_RANGE,
-                                   "left margin", 2);
-       dev->mode_config.tv_left_margin_property->values[0] = 0;
-       dev->mode_config.tv_left_margin_property->values[1] = 100;
+               drm_property_create_range(dev, 0, "left margin", 0, 100);
 
        dev->mode_config.tv_right_margin_property =
-               drm_property_create(dev, DRM_MODE_PROP_RANGE,
-                                   "right margin", 2);
-       dev->mode_config.tv_right_margin_property->values[0] = 0;
-       dev->mode_config.tv_right_margin_property->values[1] = 100;
+               drm_property_create_range(dev, 0, "right margin", 0, 100);
 
        dev->mode_config.tv_top_margin_property =
-               drm_property_create(dev, DRM_MODE_PROP_RANGE,
-                                   "top margin", 2);
-       dev->mode_config.tv_top_margin_property->values[0] = 0;
-       dev->mode_config.tv_top_margin_property->values[1] = 100;
+               drm_property_create_range(dev, 0, "top margin", 0, 100);
 
        dev->mode_config.tv_bottom_margin_property =
-               drm_property_create(dev, DRM_MODE_PROP_RANGE,
-                                   "bottom margin", 2);
-       dev->mode_config.tv_bottom_margin_property->values[0] = 0;
-       dev->mode_config.tv_bottom_margin_property->values[1] = 100;
+               drm_property_create_range(dev, 0, "bottom margin", 0, 100);
 
        dev->mode_config.tv_mode_property =
                drm_property_create(dev, DRM_MODE_PROP_ENUM,
@@ -796,40 +814,22 @@ int drm_mode_create_tv_properties(struct drm_device *dev, int num_modes,
                                      i, modes[i]);
 
        dev->mode_config.tv_brightness_property =
-               drm_property_create(dev, DRM_MODE_PROP_RANGE,
-                                   "brightness", 2);
-       dev->mode_config.tv_brightness_property->values[0] = 0;
-       dev->mode_config.tv_brightness_property->values[1] = 100;
+               drm_property_create_range(dev, 0, "brightness", 0, 100);
 
        dev->mode_config.tv_contrast_property =
-               drm_property_create(dev, DRM_MODE_PROP_RANGE,
-                                   "contrast", 2);
-       dev->mode_config.tv_contrast_property->values[0] = 0;
-       dev->mode_config.tv_contrast_property->values[1] = 100;
+               drm_property_create_range(dev, 0, "contrast", 0, 100);
 
        dev->mode_config.tv_flicker_reduction_property =
-               drm_property_create(dev, DRM_MODE_PROP_RANGE,
-                                   "flicker reduction", 2);
-       dev->mode_config.tv_flicker_reduction_property->values[0] = 0;
-       dev->mode_config.tv_flicker_reduction_property->values[1] = 100;
+               drm_property_create_range(dev, 0, "flicker reduction", 0, 100);
 
        dev->mode_config.tv_overscan_property =
-               drm_property_create(dev, DRM_MODE_PROP_RANGE,
-                                   "overscan", 2);
-       dev->mode_config.tv_overscan_property->values[0] = 0;
-       dev->mode_config.tv_overscan_property->values[1] = 100;
+               drm_property_create_range(dev, 0, "overscan", 0, 100);
 
        dev->mode_config.tv_saturation_property =
-               drm_property_create(dev, DRM_MODE_PROP_RANGE,
-                                   "saturation", 2);
-       dev->mode_config.tv_saturation_property->values[0] = 0;
-       dev->mode_config.tv_saturation_property->values[1] = 100;
+               drm_property_create_range(dev, 0, "saturation", 0, 100);
 
        dev->mode_config.tv_hue_property =
-               drm_property_create(dev, DRM_MODE_PROP_RANGE,
-                                   "hue", 2);
-       dev->mode_config.tv_hue_property->values[0] = 0;
-       dev->mode_config.tv_hue_property->values[1] = 100;
+               drm_property_create_range(dev, 0, "hue", 0, 100);
 
        return 0;
 }
@@ -845,18 +845,14 @@ EXPORT_SYMBOL(drm_mode_create_tv_properties);
 int drm_mode_create_scaling_mode_property(struct drm_device *dev)
 {
        struct drm_property *scaling_mode;
-       int i;
 
        if (dev->mode_config.scaling_mode_property)
                return 0;
 
        scaling_mode =
-               drm_property_create(dev, DRM_MODE_PROP_ENUM, "scaling mode",
+               drm_property_create_enum(dev, 0, "scaling mode",
+                               drm_scaling_mode_enum_list,
                                    ARRAY_SIZE(drm_scaling_mode_enum_list));
-       for (i = 0; i < ARRAY_SIZE(drm_scaling_mode_enum_list); i++)
-               drm_property_add_enum(scaling_mode, i,
-                                     drm_scaling_mode_enum_list[i].type,
-                                     drm_scaling_mode_enum_list[i].name);
 
        dev->mode_config.scaling_mode_property = scaling_mode;
 
@@ -874,18 +870,14 @@ EXPORT_SYMBOL(drm_mode_create_scaling_mode_property);
 int drm_mode_create_dithering_property(struct drm_device *dev)
 {
        struct drm_property *dithering_mode;
-       int i;
 
        if (dev->mode_config.dithering_mode_property)
                return 0;
 
        dithering_mode =
-               drm_property_create(dev, DRM_MODE_PROP_ENUM, "dithering",
+               drm_property_create_enum(dev, 0, "dithering",
+                               drm_dithering_mode_enum_list,
                                    ARRAY_SIZE(drm_dithering_mode_enum_list));
-       for (i = 0; i < ARRAY_SIZE(drm_dithering_mode_enum_list); i++)
-               drm_property_add_enum(dithering_mode, i,
-                                     drm_dithering_mode_enum_list[i].type,
-                                     drm_dithering_mode_enum_list[i].name);
        dev->mode_config.dithering_mode_property = dithering_mode;
 
        return 0;
@@ -902,20 +894,15 @@ EXPORT_SYMBOL(drm_mode_create_dithering_property);
 int drm_mode_create_dirty_info_property(struct drm_device *dev)
 {
        struct drm_property *dirty_info;
-       int i;
 
        if (dev->mode_config.dirty_info_property)
                return 0;
 
        dirty_info =
-               drm_property_create(dev, DRM_MODE_PROP_ENUM |
-                                   DRM_MODE_PROP_IMMUTABLE,
+               drm_property_create_enum(dev, DRM_MODE_PROP_IMMUTABLE,
                                    "dirty",
+                                   drm_dirty_info_enum_list,
                                    ARRAY_SIZE(drm_dirty_info_enum_list));
-       for (i = 0; i < ARRAY_SIZE(drm_dirty_info_enum_list); i++)
-               drm_property_add_enum(dirty_info, i,
-                                     drm_dirty_info_enum_list[i].type,
-                                     drm_dirty_info_enum_list[i].name);
        dev->mode_config.dirty_info_property = dirty_info;
 
        return 0;
@@ -999,6 +986,7 @@ int drm_mode_group_init_legacy_group(struct drm_device *dev,
 
        return 0;
 }
+EXPORT_SYMBOL(drm_mode_group_init_legacy_group);
 
 /**
  * drm_mode_config_cleanup - free up DRM mode_config info
@@ -1048,6 +1036,9 @@ void drm_mode_config_cleanup(struct drm_device *dev)
                                 head) {
                plane->funcs->destroy(plane);
        }
+
+       idr_remove_all(&dev->mode_config.crtc_idr);
+       idr_destroy(&dev->mode_config.crtc_idr);
 }
 EXPORT_SYMBOL(drm_mode_config_cleanup);
 
@@ -1062,9 +1053,16 @@ EXPORT_SYMBOL(drm_mode_config_cleanup);
  * Convert a drm_display_mode into a drm_mode_modeinfo structure to return to
  * the user.
  */
-void drm_crtc_convert_to_umode(struct drm_mode_modeinfo *out,
-                              struct drm_display_mode *in)
+static void drm_crtc_convert_to_umode(struct drm_mode_modeinfo *out,
+                                     const struct drm_display_mode *in)
 {
+       WARN(in->hdisplay > USHRT_MAX || in->hsync_start > USHRT_MAX ||
+            in->hsync_end > USHRT_MAX || in->htotal > USHRT_MAX ||
+            in->hskew > USHRT_MAX || in->vdisplay > USHRT_MAX ||
+            in->vsync_start > USHRT_MAX || in->vsync_end > USHRT_MAX ||
+            in->vtotal > USHRT_MAX || in->vscan > USHRT_MAX,
+            "timing values too large for mode info\n");
+
        out->clock = in->clock;
        out->hdisplay = in->hdisplay;
        out->hsync_start = in->hsync_start;
@@ -1093,10 +1091,16 @@ void drm_crtc_convert_to_umode(struct drm_mode_modeinfo *out,
  *
  * Convert a drm_mode_modeinfo into a drm_display_mode structure to return to
  * the caller.
+ *
+ * RETURNS:
+ * Zero on success, errno on failure.
  */
-void drm_crtc_convert_umode(struct drm_display_mode *out,
-                           struct drm_mode_modeinfo *in)
+static int drm_crtc_convert_umode(struct drm_display_mode *out,
+                                 const struct drm_mode_modeinfo *in)
 {
+       if (in->clock > INT_MAX || in->vrefresh > INT_MAX)
+               return -ERANGE;
+
        out->clock = in->clock;
        out->hdisplay = in->hdisplay;
        out->hsync_start = in->hsync_start;
@@ -1113,6 +1117,8 @@ void drm_crtc_convert_umode(struct drm_display_mode *out,
        out->type = in->type;
        strncpy(out->name, in->name, DRM_DISPLAY_MODE_LEN);
        out->name[DRM_DISPLAY_MODE_LEN-1] = 0;
+
+       return 0;
 }
 
 /**
@@ -1311,7 +1317,7 @@ out:
  * @arg: arg from ioctl
  *
  * LOCKING:
- * Caller? (FIXME)
+ * Takes mode config lock.
  *
  * Construct a CRTC configuration structure to return to the user.
  *
@@ -1371,7 +1377,7 @@ out:
  * @arg: arg from ioctl
  *
  * LOCKING:
- * Caller? (FIXME)
+ * Takes mode config lock.
  *
  * Construct a connector configuration structure to return to the user.
  *
@@ -1553,6 +1559,9 @@ out:
  * @data: ioctl data
  * @file_priv: DRM file info
  *
+ * LOCKING:
+ * Takes mode config lock.
+ *
  * Return an plane count and set of IDs.
  */
 int drm_mode_getplane_res(struct drm_device *dev, void *data,
@@ -1599,6 +1608,9 @@ out:
  * @data: ioctl data
  * @file_priv: DRM file info
  *
+ * LOCKING:
+ * Takes mode config lock.
+ *
  * Return plane info, including formats supported, gamma size, any
  * current fb, etc.
  */
@@ -1664,6 +1676,9 @@ out:
  * @data: ioctl data*
  * @file_prive: DRM file info
  *
+ * LOCKING:
+ * Takes mode config lock.
+ *
  * Set plane info, including placement, fb, scaling, and other factors.
  * Or pass a NULL fb to disable.
  */
@@ -1794,7 +1809,7 @@ out:
  * @arg: arg from ioctl
  *
  * LOCKING:
- * Caller? (FIXME)
+ * Takes mode config lock.
  *
  * Build a new CRTC configuration based on user request.
  *
@@ -1809,7 +1824,7 @@ int drm_mode_setcrtc(struct drm_device *dev, void *data,
        struct drm_mode_config *config = &dev->mode_config;
        struct drm_mode_crtc *crtc_req = data;
        struct drm_mode_object *obj;
-       struct drm_crtc *crtc, *crtcfb;
+       struct drm_crtc *crtc;
        struct drm_connector **connector_set = NULL, *connector;
        struct drm_framebuffer *fb = NULL;
        struct drm_display_mode *mode = NULL;
@@ -1821,6 +1836,10 @@ int drm_mode_setcrtc(struct drm_device *dev, void *data,
        if (!drm_core_check_feature(dev, DRIVER_MODESET))
                return -EINVAL;
 
+       /* For some reason crtc x/y offsets are signed internally. */
+       if (crtc_req->x > INT_MAX || crtc_req->y > INT_MAX)
+               return -ERANGE;
+
        mutex_lock(&dev->mode_config.mutex);
        obj = drm_mode_object_find(dev, crtc_req->crtc_id,
                                   DRM_MODE_OBJECT_CRTC);
@@ -1836,14 +1855,12 @@ int drm_mode_setcrtc(struct drm_device *dev, void *data,
                /* If we have a mode we need a framebuffer. */
                /* If we pass -1, set the mode with the currently bound fb */
                if (crtc_req->fb_id == -1) {
-                       list_for_each_entry(crtcfb,
-                                           &dev->mode_config.crtc_list, head) {
-                               if (crtcfb == crtc) {
-                                       DRM_DEBUG_KMS("Using current fb for "
-                                                       "setmode\n");
-                                       fb = crtc->fb;
-                               }
+                       if (!crtc->fb) {
+                               DRM_DEBUG_KMS("CRTC doesn't have current FB\n");
+                               ret = -EINVAL;
+                               goto out;
                        }
+                       fb = crtc->fb;
                } else {
                        obj = drm_mode_object_find(dev, crtc_req->fb_id,
                                                   DRM_MODE_OBJECT_FB);
@@ -1857,8 +1874,30 @@ int drm_mode_setcrtc(struct drm_device *dev, void *data,
                }
 
                mode = drm_mode_create(dev);
-               drm_crtc_convert_umode(mode, &crtc_req->mode);
+               if (!mode) {
+                       ret = -ENOMEM;
+                       goto out;
+               }
+
+               ret = drm_crtc_convert_umode(mode, &crtc_req->mode);
+               if (ret) {
+                       DRM_DEBUG_KMS("Invalid mode\n");
+                       goto out;
+               }
+
                drm_mode_set_crtcinfo(mode, CRTC_INTERLACE_HALVE_V);
+
+               if (mode->hdisplay > fb->width ||
+                   mode->vdisplay > fb->height ||
+                   crtc_req->x > fb->width - mode->hdisplay ||
+                   crtc_req->y > fb->height - mode->vdisplay) {
+                       DRM_DEBUG_KMS("Invalid CRTC viewport %ux%u+%u+%u for fb size %ux%u.\n",
+                                     mode->hdisplay, mode->vdisplay,
+                                     crtc_req->x, crtc_req->y,
+                                     fb->width, fb->height);
+                       ret = -ENOSPC;
+                       goto out;
+               }
        }
 
        if (crtc_req->count_connectors == 0 && mode) {
@@ -1926,6 +1965,7 @@ int drm_mode_setcrtc(struct drm_device *dev, void *data,
 
 out:
        kfree(connector_set);
+       drm_mode_destroy(dev, mode);
        mutex_unlock(&dev->mode_config.mutex);
        return ret;
 }
@@ -2275,7 +2315,7 @@ out:
  * @arg: arg from ioctl
  *
  * LOCKING:
- * Caller? (FIXME)
+ * Takes mode config lock.
  *
  * Lookup the FB given its ID and return info about it.
  *
@@ -2424,38 +2464,48 @@ void drm_fb_release(struct drm_file *priv)
  *
  * Add @mode to @connector's user mode list.
  */
-static int drm_mode_attachmode(struct drm_device *dev,
-                              struct drm_connector *connector,
-                              struct drm_display_mode *mode)
+static void drm_mode_attachmode(struct drm_device *dev,
+                               struct drm_connector *connector,
+                               struct drm_display_mode *mode)
 {
-       int ret = 0;
-
        list_add_tail(&mode->head, &connector->user_modes);
-       return ret;
 }
 
 int drm_mode_attachmode_crtc(struct drm_device *dev, struct drm_crtc *crtc,
-                            struct drm_display_mode *mode)
+                            const struct drm_display_mode *mode)
 {
        struct drm_connector *connector;
        int ret = 0;
-       struct drm_display_mode *dup_mode;
-       int need_dup = 0;
+       struct drm_display_mode *dup_mode, *next;
+       LIST_HEAD(list);
+
        list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
                if (!connector->encoder)
-                       break;
+                       continue;
                if (connector->encoder->crtc == crtc) {
-                       if (need_dup)
-                               dup_mode = drm_mode_duplicate(dev, mode);
-                       else
-                               dup_mode = mode;
-                       ret = drm_mode_attachmode(dev, connector, dup_mode);
-                       if (ret)
-                               return ret;
-                       need_dup = 1;
+                       dup_mode = drm_mode_duplicate(dev, mode);
+                       if (!dup_mode) {
+                               ret = -ENOMEM;
+                               goto out;
+                       }
+                       list_add_tail(&dup_mode->head, &list);
                }
        }
-       return 0;
+
+       list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
+               if (!connector->encoder)
+                       continue;
+               if (connector->encoder->crtc == crtc)
+                       list_move_tail(list.next, &connector->user_modes);
+       }
+
+       WARN_ON(!list_empty(&list));
+
+ out:
+       list_for_each_entry_safe(dup_mode, next, &list, head)
+               drm_mode_destroy(dev, dup_mode);
+
+       return ret;
 }
 EXPORT_SYMBOL(drm_mode_attachmode_crtc);
 
@@ -2534,9 +2584,14 @@ int drm_mode_attachmode_ioctl(struct drm_device *dev,
                goto out;
        }
 
-       drm_crtc_convert_umode(mode, umode);
+       ret = drm_crtc_convert_umode(mode, umode);
+       if (ret) {
+               DRM_DEBUG_KMS("Invalid mode\n");
+               drm_mode_destroy(dev, mode);
+               goto out;
+       }
 
-       ret = drm_mode_attachmode(dev, connector, mode);
+       drm_mode_attachmode(dev, connector, mode);
 out:
        mutex_unlock(&dev->mode_config.mutex);
        return ret;
@@ -2577,7 +2632,12 @@ int drm_mode_detachmode_ioctl(struct drm_device *dev,
        }
        connector = obj_to_connector(obj);
 
-       drm_crtc_convert_umode(&mode, umode);
+       ret = drm_crtc_convert_umode(&mode, umode);
+       if (ret) {
+               DRM_DEBUG_KMS("Invalid mode\n");
+               goto out;
+       }
+
        ret = drm_mode_detachmode(dev, connector, &mode);
 out:
        mutex_unlock(&dev->mode_config.mutex);
@@ -2588,6 +2648,7 @@ struct drm_property *drm_property_create(struct drm_device *dev, int flags,
                                         const char *name, int num_values)
 {
        struct drm_property *property = NULL;
+       int ret;
 
        property = kzalloc(sizeof(struct drm_property), GFP_KERNEL);
        if (!property)
@@ -2599,7 +2660,10 @@ struct drm_property *drm_property_create(struct drm_device *dev, int flags,
                        goto fail;
        }
 
-       drm_mode_object_get(dev, &property->base, DRM_MODE_OBJECT_PROPERTY);
+       ret = drm_mode_object_get(dev, &property->base, DRM_MODE_OBJECT_PROPERTY);
+       if (ret)
+               goto fail;
+
        property->flags = flags;
        property->num_values = num_values;
        INIT_LIST_HEAD(&property->enum_blob_list);
@@ -2612,11 +2676,59 @@ struct drm_property *drm_property_create(struct drm_device *dev, int flags,
        list_add_tail(&property->head, &dev->mode_config.property_list);
        return property;
 fail:
+       kfree(property->values);
        kfree(property);
        return NULL;
 }
 EXPORT_SYMBOL(drm_property_create);
 
+struct drm_property *drm_property_create_enum(struct drm_device *dev, int flags,
+                                        const char *name,
+                                        const struct drm_prop_enum_list *props,
+                                        int num_values)
+{
+       struct drm_property *property;
+       int i, ret;
+
+       flags |= DRM_MODE_PROP_ENUM;
+
+       property = drm_property_create(dev, flags, name, num_values);
+       if (!property)
+               return NULL;
+
+       for (i = 0; i < num_values; i++) {
+               ret = drm_property_add_enum(property, i,
+                                     props[i].type,
+                                     props[i].name);
+               if (ret) {
+                       drm_property_destroy(dev, property);
+                       return NULL;
+               }
+       }
+
+       return property;
+}
+EXPORT_SYMBOL(drm_property_create_enum);
+
+struct drm_property *drm_property_create_range(struct drm_device *dev, int flags,
+                                        const char *name,
+                                        uint64_t min, uint64_t max)
+{
+       struct drm_property *property;
+
+       flags |= DRM_MODE_PROP_RANGE;
+
+       property = drm_property_create(dev, flags, name, 2);
+       if (!property)
+               return NULL;
+
+       property->values[0] = min;
+       property->values[1] = max;
+
+       return property;
+}
+EXPORT_SYMBOL(drm_property_create_range);
+
 int drm_property_add_enum(struct drm_property *property, int index,
                          uint64_t value, const char *name)
 {
@@ -2828,6 +2940,7 @@ static struct drm_property_blob *drm_property_create_blob(struct drm_device *dev
                                                          void *data)
 {
        struct drm_property_blob *blob;
+       int ret;
 
        if (!length || !data)
                return NULL;
@@ -2836,13 +2949,16 @@ static struct drm_property_blob *drm_property_create_blob(struct drm_device *dev
        if (!blob)
                return NULL;
 
-       blob->data = (void *)((char *)blob + sizeof(struct drm_property_blob));
+       ret = drm_mode_object_get(dev, &blob->base, DRM_MODE_OBJECT_BLOB);
+       if (ret) {
+               kfree(blob);
+               return NULL;
+       }
+
        blob->length = length;
 
        memcpy(blob->data, data, length);
 
-       drm_mode_object_get(dev, &blob->base, DRM_MODE_OBJECT_BLOB);
-
        list_add_tail(&blob->head, &dev->mode_config.property_blob_list);
        return blob;
 }
@@ -3021,7 +3137,7 @@ void drm_mode_connector_detach_encoder(struct drm_connector *connector,
 }
 EXPORT_SYMBOL(drm_mode_connector_detach_encoder);
 
-bool drm_mode_crtc_set_gamma_size(struct drm_crtc *crtc,
+int drm_mode_crtc_set_gamma_size(struct drm_crtc *crtc,
                                  int gamma_size)
 {
        crtc->gamma_size = gamma_size;
@@ -3029,10 +3145,10 @@ bool drm_mode_crtc_set_gamma_size(struct drm_crtc *crtc,
        crtc->gamma_store = kzalloc(gamma_size * sizeof(uint16_t) * 3, GFP_KERNEL);
        if (!crtc->gamma_store) {
                crtc->gamma_size = 0;
-               return false;
+               return -ENOMEM;
        }
 
-       return true;
+       return 0;
 }
 EXPORT_SYMBOL(drm_mode_crtc_set_gamma_size);
 
@@ -3178,6 +3294,18 @@ int drm_mode_page_flip_ioctl(struct drm_device *dev,
                goto out;
        fb = obj_to_fb(obj);
 
+       if (crtc->mode.hdisplay > fb->width ||
+           crtc->mode.vdisplay > fb->height ||
+           crtc->x > fb->width - crtc->mode.hdisplay ||
+           crtc->y > fb->height - crtc->mode.vdisplay) {
+               DRM_DEBUG_KMS("Invalid fb size %ux%u for CRTC viewport %ux%u+%d+%d.\n",
+                             fb->width, fb->height,
+                             crtc->mode.hdisplay, crtc->mode.vdisplay,
+                             crtc->x, crtc->y);
+               ret = -ENOSPC;
+               goto out;
+       }
+
        if (page_flip->flags & DRM_MODE_PAGE_FLIP_EVENT) {
                ret = -ENOMEM;
                spin_lock_irqsave(&dev->event_lock, flags);
index 84a4a809793ff7bcf7fdf68e9d28555eb5e5b8ba..81118893264c26e3cd472827dee324e6570dad04 100644 (file)
@@ -37,6 +37,7 @@
 #include "drm_fourcc.h"
 #include "drm_crtc_helper.h"
 #include "drm_fb_helper.h"
+#include "drm_edid.h"
 
 static bool drm_kms_helper_poll = true;
 module_param_named(poll, drm_kms_helper_poll, bool, 0600);
@@ -44,12 +45,12 @@ module_param_named(poll, drm_kms_helper_poll, bool, 0600);
 static void drm_mode_validate_flag(struct drm_connector *connector,
                                   int flags)
 {
-       struct drm_display_mode *mode, *t;
+       struct drm_display_mode *mode;
 
        if (flags == (DRM_MODE_FLAG_DBLSCAN | DRM_MODE_FLAG_INTERLACE))
                return;
 
-       list_for_each_entry_safe(mode, t, &connector->modes, head) {
+       list_for_each_entry(mode, &connector->modes, head) {
                if ((mode->flags & DRM_MODE_FLAG_INTERLACE) &&
                                !(flags & DRM_MODE_FLAG_INTERLACE))
                        mode->status = MODE_NO_INTERLACE;
@@ -87,7 +88,7 @@ int drm_helper_probe_single_connector_modes(struct drm_connector *connector,
                                            uint32_t maxX, uint32_t maxY)
 {
        struct drm_device *dev = connector->dev;
-       struct drm_display_mode *mode, *t;
+       struct drm_display_mode *mode;
        struct drm_connector_helper_funcs *connector_funcs =
                connector->helper_private;
        int count = 0;
@@ -96,7 +97,7 @@ int drm_helper_probe_single_connector_modes(struct drm_connector *connector,
        DRM_DEBUG_KMS("[CONNECTOR:%d:%s]\n", connector->base.id,
                        drm_get_connector_name(connector));
        /* set all modes to the unverified state */
-       list_for_each_entry_safe(mode, t, &connector->modes, head)
+       list_for_each_entry(mode, &connector->modes, head)
                mode->status = MODE_UNVERIFIED;
 
        if (connector->force) {
@@ -118,7 +119,12 @@ int drm_helper_probe_single_connector_modes(struct drm_connector *connector,
                goto prune;
        }
 
-       count = (*connector_funcs->get_modes)(connector);
+#ifdef CONFIG_DRM_LOAD_EDID_FIRMWARE
+       count = drm_load_edid_firmware(connector);
+       if (count == 0)
+#endif
+               count = (*connector_funcs->get_modes)(connector);
+
        if (count == 0 && connector->status == connector_status_connected)
                count = drm_add_modes_noedid(connector, 1024, 768);
        if (count == 0)
@@ -136,7 +142,7 @@ int drm_helper_probe_single_connector_modes(struct drm_connector *connector,
                mode_flags |= DRM_MODE_FLAG_DBLSCAN;
        drm_mode_validate_flag(connector, mode_flags);
 
-       list_for_each_entry_safe(mode, t, &connector->modes, head) {
+       list_for_each_entry(mode, &connector->modes, head) {
                if (mode->status == MODE_OK)
                        mode->status = connector_funcs->mode_valid(connector,
                                                                   mode);
@@ -152,7 +158,7 @@ prune:
 
        DRM_DEBUG_KMS("[CONNECTOR:%d:%s] probed modes :\n", connector->base.id,
                        drm_get_connector_name(connector));
-       list_for_each_entry_safe(mode, t, &connector->modes, head) {
+       list_for_each_entry(mode, &connector->modes, head) {
                mode->vrefresh = drm_mode_vrefresh(mode);
 
                drm_mode_set_crtcinfo(mode, CRTC_INTERLACE_HALVE_V);
@@ -352,6 +358,8 @@ bool drm_crtc_helper_set_mode(struct drm_crtc *crtc,
                return true;
 
        adjusted_mode = drm_mode_duplicate(dev, mode);
+       if (!adjusted_mode)
+               return false;
 
        saved_hwmode = crtc->hwmode;
        saved_mode = crtc->mode;
index ebf7d3f68fc4c33446897be3bdee3dedde0038e6..0b65fbc8a6308c9b1f7aae823e7d10149d75c380 100644 (file)
@@ -135,23 +135,23 @@ static struct drm_ioctl_desc drm_ioctls[] = {
        DRM_IOCTL_DEF(DRM_IOCTL_GEM_FLINK, drm_gem_flink_ioctl, DRM_AUTH|DRM_UNLOCKED),
        DRM_IOCTL_DEF(DRM_IOCTL_GEM_OPEN, drm_gem_open_ioctl, DRM_AUTH|DRM_UNLOCKED),
 
-       DRM_IOCTL_DEF(DRM_IOCTL_MODE_GETRESOURCES, drm_mode_getresources, DRM_MASTER|DRM_CONTROL_ALLOW|DRM_UNLOCKED),
+       DRM_IOCTL_DEF(DRM_IOCTL_MODE_GETRESOURCES, drm_mode_getresources, DRM_CONTROL_ALLOW|DRM_UNLOCKED),
        DRM_IOCTL_DEF(DRM_IOCTL_MODE_GETPLANERESOURCES, drm_mode_getplane_res, DRM_MASTER|DRM_CONTROL_ALLOW|DRM_UNLOCKED),
-       DRM_IOCTL_DEF(DRM_IOCTL_MODE_GETCRTC, drm_mode_getcrtc, DRM_MASTER|DRM_CONTROL_ALLOW|DRM_UNLOCKED),
+       DRM_IOCTL_DEF(DRM_IOCTL_MODE_GETCRTC, drm_mode_getcrtc, DRM_CONTROL_ALLOW|DRM_UNLOCKED),
        DRM_IOCTL_DEF(DRM_IOCTL_MODE_SETCRTC, drm_mode_setcrtc, DRM_MASTER|DRM_CONTROL_ALLOW|DRM_UNLOCKED),
        DRM_IOCTL_DEF(DRM_IOCTL_MODE_GETPLANE, drm_mode_getplane, DRM_MASTER|DRM_CONTROL_ALLOW|DRM_UNLOCKED),
        DRM_IOCTL_DEF(DRM_IOCTL_MODE_SETPLANE, drm_mode_setplane, DRM_MASTER|DRM_CONTROL_ALLOW|DRM_UNLOCKED),
        DRM_IOCTL_DEF(DRM_IOCTL_MODE_CURSOR, drm_mode_cursor_ioctl, DRM_MASTER|DRM_CONTROL_ALLOW|DRM_UNLOCKED),
-       DRM_IOCTL_DEF(DRM_IOCTL_MODE_GETGAMMA, drm_mode_gamma_get_ioctl, DRM_MASTER|DRM_UNLOCKED),
+       DRM_IOCTL_DEF(DRM_IOCTL_MODE_GETGAMMA, drm_mode_gamma_get_ioctl, DRM_UNLOCKED),
        DRM_IOCTL_DEF(DRM_IOCTL_MODE_SETGAMMA, drm_mode_gamma_set_ioctl, DRM_MASTER|DRM_UNLOCKED),
-       DRM_IOCTL_DEF(DRM_IOCTL_MODE_GETENCODER, drm_mode_getencoder, DRM_MASTER|DRM_CONTROL_ALLOW|DRM_UNLOCKED),
-       DRM_IOCTL_DEF(DRM_IOCTL_MODE_GETCONNECTOR, drm_mode_getconnector, DRM_MASTER|DRM_CONTROL_ALLOW|DRM_UNLOCKED),
+       DRM_IOCTL_DEF(DRM_IOCTL_MODE_GETENCODER, drm_mode_getencoder, DRM_CONTROL_ALLOW|DRM_UNLOCKED),
+       DRM_IOCTL_DEF(DRM_IOCTL_MODE_GETCONNECTOR, drm_mode_getconnector, DRM_CONTROL_ALLOW|DRM_UNLOCKED),
        DRM_IOCTL_DEF(DRM_IOCTL_MODE_ATTACHMODE, drm_mode_attachmode_ioctl, DRM_MASTER|DRM_CONTROL_ALLOW|DRM_UNLOCKED),
        DRM_IOCTL_DEF(DRM_IOCTL_MODE_DETACHMODE, drm_mode_detachmode_ioctl, DRM_MASTER|DRM_CONTROL_ALLOW|DRM_UNLOCKED),
        DRM_IOCTL_DEF(DRM_IOCTL_MODE_GETPROPERTY, drm_mode_getproperty_ioctl, DRM_MASTER | DRM_CONTROL_ALLOW|DRM_UNLOCKED),
        DRM_IOCTL_DEF(DRM_IOCTL_MODE_SETPROPERTY, drm_mode_connector_property_set_ioctl, DRM_MASTER|DRM_CONTROL_ALLOW|DRM_UNLOCKED),
        DRM_IOCTL_DEF(DRM_IOCTL_MODE_GETPROPBLOB, drm_mode_getblob_ioctl, DRM_MASTER|DRM_CONTROL_ALLOW|DRM_UNLOCKED),
-       DRM_IOCTL_DEF(DRM_IOCTL_MODE_GETFB, drm_mode_getfb, DRM_MASTER|DRM_CONTROL_ALLOW|DRM_UNLOCKED),
+       DRM_IOCTL_DEF(DRM_IOCTL_MODE_GETFB, drm_mode_getfb, DRM_CONTROL_ALLOW|DRM_UNLOCKED),
        DRM_IOCTL_DEF(DRM_IOCTL_MODE_ADDFB, drm_mode_addfb, DRM_MASTER|DRM_CONTROL_ALLOW|DRM_UNLOCKED),
        DRM_IOCTL_DEF(DRM_IOCTL_MODE_ADDFB2, drm_mode_addfb2, DRM_MASTER|DRM_CONTROL_ALLOW|DRM_UNLOCKED),
        DRM_IOCTL_DEF(DRM_IOCTL_MODE_RMFB, drm_mode_rmfb, DRM_MASTER|DRM_CONTROL_ALLOW|DRM_UNLOCKED),
@@ -390,6 +390,10 @@ long drm_ioctl(struct file *filp,
        unsigned int usize, asize;
 
        dev = file_priv->minor->dev;
+
+       if (drm_device_is_unplugged(dev))
+               return -ENODEV;
+
        atomic_inc(&dev->ioctl_count);
        atomic_inc(&dev->counts[_DRM_STAT_IOCTLS]);
        ++file_priv->ioctl_count;
index ece03fc2d3864aa25ac7bebccfa916cfacc6fd07..5a18b0df82850a046608c57c7fedb4cf75d03e4b 100644 (file)
@@ -149,8 +149,7 @@ EXPORT_SYMBOL(drm_edid_header_is_valid);
  * Sanity check the EDID block (base or extension).  Return 0 if the block
  * doesn't check out, or 1 if it's valid.
  */
-static bool
-drm_edid_block_valid(u8 *raw_edid)
+bool drm_edid_block_valid(u8 *raw_edid)
 {
        int i;
        u8 csum = 0;
@@ -203,6 +202,7 @@ bad:
        }
        return 0;
 }
+EXPORT_SYMBOL(drm_edid_block_valid);
 
 /**
  * drm_edid_is_valid - sanity check EDID data
@@ -226,7 +226,6 @@ bool drm_edid_is_valid(struct edid *edid)
 }
 EXPORT_SYMBOL(drm_edid_is_valid);
 
-#define DDC_ADDR 0x50
 #define DDC_SEGMENT_ADDR 0x30
 /**
  * Get EDID information via I2C.
@@ -266,6 +265,11 @@ drm_do_probe_ddc_edid(struct i2c_adapter *adapter, unsigned char *buf,
                        }
                };
                ret = i2c_transfer(adapter, msgs, 2);
+               if (ret == -ENXIO) {
+                       DRM_DEBUG_KMS("drm: skipping non-existent adapter %s\n",
+                                       adapter->name);
+                       break;
+               }
        } while (ret != 2 && --retries);
 
        return ret == 2 ? 0 : -1;
@@ -745,7 +749,7 @@ drm_mode_std(struct drm_connector *connector, struct edid *edid,
                 */
                mode = drm_gtf_mode(dev, hsize, vsize, vrefresh_rate, 0, 0);
                if (drm_mode_hsync(mode) > drm_gtf2_hbreak(edid)) {
-                       kfree(mode);
+                       drm_mode_destroy(dev, mode);
                        mode = drm_gtf_mode_complex(dev, hsize, vsize,
                                                    vrefresh_rate, 0, 0,
                                                    drm_gtf2_m(edid),
diff --git a/drivers/gpu/drm/drm_edid_load.c b/drivers/gpu/drm/drm_edid_load.c
new file mode 100644 (file)
index 0000000..da9acba
--- /dev/null
@@ -0,0 +1,250 @@
+/*
+   drm_edid_load.c: use a built-in EDID data set or load it via the firmware
+                   interface
+
+   Copyright (C) 2012 Carsten Emde <C.Emde@osadl.org>
+
+   This program is free software; you can redistribute it and/or
+   modify it under the terms of the GNU General Public License
+   as published by the Free Software Foundation; either version 2
+   of the License, or (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA.
+*/
+
+#include <linux/module.h>
+#include <linux/firmware.h>
+#include "drmP.h"
+#include "drm_crtc.h"
+#include "drm_crtc_helper.h"
+#include "drm_edid.h"
+
+static char edid_firmware[PATH_MAX];
+module_param_string(edid_firmware, edid_firmware, sizeof(edid_firmware), 0644);
+MODULE_PARM_DESC(edid_firmware, "Do not probe monitor, use specified EDID blob "
+       "from built-in data or /lib/firmware instead. ");
+
+#define GENERIC_EDIDS 4
+static char *generic_edid_name[GENERIC_EDIDS] = {
+       "edid/1024x768.bin",
+       "edid/1280x1024.bin",
+       "edid/1680x1050.bin",
+       "edid/1920x1080.bin",
+};
+
+static u8 generic_edid[GENERIC_EDIDS][128] = {
+       {
+       0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00,
+       0x31, 0xd8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+       0x05, 0x16, 0x01, 0x03, 0x6d, 0x23, 0x1a, 0x78,
+       0xea, 0x5e, 0xc0, 0xa4, 0x59, 0x4a, 0x98, 0x25,
+       0x20, 0x50, 0x54, 0x00, 0x08, 0x00, 0x61, 0x40,
+       0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+       0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x64, 0x19,
+       0x00, 0x40, 0x41, 0x00, 0x26, 0x30, 0x08, 0x90,
+       0x36, 0x00, 0x63, 0x0a, 0x11, 0x00, 0x00, 0x18,
+       0x00, 0x00, 0x00, 0xff, 0x00, 0x4c, 0x69, 0x6e,
+       0x75, 0x78, 0x20, 0x23, 0x30, 0x0a, 0x20, 0x20,
+       0x20, 0x20, 0x00, 0x00, 0x00, 0xfd, 0x00, 0x3b,
+       0x3d, 0x2f, 0x31, 0x07, 0x00, 0x0a, 0x20, 0x20,
+       0x20, 0x20, 0x20, 0x20, 0x00, 0x00, 0x00, 0xfc,
+       0x00, 0x4c, 0x69, 0x6e, 0x75, 0x78, 0x20, 0x58,
+       0x47, 0x41, 0x0a, 0x20, 0x20, 0x20, 0x00, 0x55,
+       },
+       {
+       0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00,
+       0x31, 0xd8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+       0x05, 0x16, 0x01, 0x03, 0x6d, 0x2c, 0x23, 0x78,
+       0xea, 0x5e, 0xc0, 0xa4, 0x59, 0x4a, 0x98, 0x25,
+       0x20, 0x50, 0x54, 0x00, 0x00, 0x00, 0x81, 0x80,
+       0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+       0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x30, 0x2a,
+       0x00, 0x98, 0x51, 0x00, 0x2a, 0x40, 0x30, 0x70,
+       0x13, 0x00, 0xbc, 0x63, 0x11, 0x00, 0x00, 0x1e,
+       0x00, 0x00, 0x00, 0xff, 0x00, 0x4c, 0x69, 0x6e,
+       0x75, 0x78, 0x20, 0x23, 0x30, 0x0a, 0x20, 0x20,
+       0x20, 0x20, 0x00, 0x00, 0x00, 0xfd, 0x00, 0x3b,
+       0x3d, 0x3e, 0x40, 0x0b, 0x00, 0x0a, 0x20, 0x20,
+       0x20, 0x20, 0x20, 0x20, 0x00, 0x00, 0x00, 0xfc,
+       0x00, 0x4c, 0x69, 0x6e, 0x75, 0x78, 0x20, 0x53,
+       0x58, 0x47, 0x41, 0x0a, 0x20, 0x20, 0x00, 0xa0,
+       },
+       {
+       0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00,
+       0x31, 0xd8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+       0x05, 0x16, 0x01, 0x03, 0x6d, 0x2b, 0x1b, 0x78,
+       0xea, 0x5e, 0xc0, 0xa4, 0x59, 0x4a, 0x98, 0x25,
+       0x20, 0x50, 0x54, 0x00, 0x00, 0x00, 0xb3, 0x00,
+       0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+       0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x21, 0x39,
+       0x90, 0x30, 0x62, 0x1a, 0x27, 0x40, 0x68, 0xb0,
+       0x36, 0x00, 0xb5, 0x11, 0x11, 0x00, 0x00, 0x1e,
+       0x00, 0x00, 0x00, 0xff, 0x00, 0x4c, 0x69, 0x6e,
+       0x75, 0x78, 0x20, 0x23, 0x30, 0x0a, 0x20, 0x20,
+       0x20, 0x20, 0x00, 0x00, 0x00, 0xfd, 0x00, 0x3b,
+       0x3d, 0x40, 0x42, 0x0f, 0x00, 0x0a, 0x20, 0x20,
+       0x20, 0x20, 0x20, 0x20, 0x00, 0x00, 0x00, 0xfc,
+       0x00, 0x4c, 0x69, 0x6e, 0x75, 0x78, 0x20, 0x57,
+       0x53, 0x58, 0x47, 0x41, 0x0a, 0x20, 0x00, 0x26,
+       },
+       {
+       0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00,
+       0x31, 0xd8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+       0x05, 0x16, 0x01, 0x03, 0x6d, 0x32, 0x1c, 0x78,
+       0xea, 0x5e, 0xc0, 0xa4, 0x59, 0x4a, 0x98, 0x25,
+       0x20, 0x50, 0x54, 0x00, 0x00, 0x00, 0xd1, 0xc0,
+       0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+       0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x3a,
+       0x80, 0x18, 0x71, 0x38, 0x2d, 0x40, 0x58, 0x2c,
+       0x45, 0x00, 0xf4, 0x19, 0x11, 0x00, 0x00, 0x1e,
+       0x00, 0x00, 0x00, 0xff, 0x00, 0x4c, 0x69, 0x6e,
+       0x75, 0x78, 0x20, 0x23, 0x30, 0x0a, 0x20, 0x20,
+       0x20, 0x20, 0x00, 0x00, 0x00, 0xfd, 0x00, 0x3b,
+       0x3d, 0x42, 0x44, 0x0f, 0x00, 0x0a, 0x20, 0x20,
+       0x20, 0x20, 0x20, 0x20, 0x00, 0x00, 0x00, 0xfc,
+       0x00, 0x4c, 0x69, 0x6e, 0x75, 0x78, 0x20, 0x46,
+       0x48, 0x44, 0x0a, 0x20, 0x20, 0x20, 0x00, 0x05,
+       },
+};
+
+static int edid_load(struct drm_connector *connector, char *name,
+                    char *connector_name)
+{
+       const struct firmware *fw;
+       struct platform_device *pdev;
+       u8 *fwdata = NULL, *edid;
+       int fwsize, expected;
+       int builtin = 0, err = 0;
+       int i, valid_extensions = 0;
+
+       pdev = platform_device_register_simple(connector_name, -1, NULL, 0);
+       if (IS_ERR(pdev)) {
+               DRM_ERROR("Failed to register EDID firmware platform device "
+                   "for connector \"%s\"\n", connector_name);
+               err = -EINVAL;
+               goto out;
+       }
+
+       err = request_firmware(&fw, name, &pdev->dev);
+       platform_device_unregister(pdev);
+
+       if (err) {
+               i = 0;
+               while (i < GENERIC_EDIDS && strcmp(name, generic_edid_name[i]))
+                       i++;
+               if (i < GENERIC_EDIDS) {
+                       err = 0;
+                       builtin = 1;
+                       fwdata = generic_edid[i];
+                       fwsize = sizeof(generic_edid[i]);
+               }
+       }
+
+       if (err) {
+               DRM_ERROR("Requesting EDID firmware \"%s\" failed (err=%d)\n",
+                   name, err);
+               goto out;
+       }
+
+       if (fwdata == NULL) {
+               fwdata = (u8 *) fw->data;
+               fwsize = fw->size;
+       }
+
+       expected = (fwdata[0x7e] + 1) * EDID_LENGTH;
+       if (expected != fwsize) {
+               DRM_ERROR("Size of EDID firmware \"%s\" is invalid "
+                   "(expected %d, got %d)\n", name, expected, (int) fwsize);
+               err = -EINVAL;
+               goto relfw_out;
+       }
+
+       edid = kmalloc(fwsize, GFP_KERNEL);
+       if (edid == NULL) {
+               err = -ENOMEM;
+               goto relfw_out;
+       }
+       memcpy(edid, fwdata, fwsize);
+
+       if (!drm_edid_block_valid(edid)) {
+               DRM_ERROR("Base block of EDID firmware \"%s\" is invalid ",
+                   name);
+               kfree(edid);
+               err = -EINVAL;
+               goto relfw_out;
+       }
+
+       for (i = 1; i <= edid[0x7e]; i++) {
+               if (i != valid_extensions + 1)
+                       memcpy(edid + (valid_extensions + 1) * EDID_LENGTH,
+                           edid + i * EDID_LENGTH, EDID_LENGTH);
+               if (drm_edid_block_valid(edid + i * EDID_LENGTH))
+                       valid_extensions++;
+       }
+
+       if (valid_extensions != edid[0x7e]) {
+               edid[EDID_LENGTH-1] += edid[0x7e] - valid_extensions;
+               DRM_INFO("Found %d valid extensions instead of %d in EDID data "
+                   "\"%s\" for connector \"%s\"\n", valid_extensions,
+                   edid[0x7e], name, connector_name);
+               edid[0x7e] = valid_extensions;
+               edid = krealloc(edid, (valid_extensions + 1) * EDID_LENGTH,
+                   GFP_KERNEL);
+               if (edid == NULL) {
+                       err = -ENOMEM;
+                       goto relfw_out;
+               }
+       }
+
+       connector->display_info.raw_edid = edid;
+       DRM_INFO("Got %s EDID base block and %d extension%s from "
+           "\"%s\" for connector \"%s\"\n", builtin ? "built-in" :
+           "external", valid_extensions, valid_extensions == 1 ? "" : "s",
+           name, connector_name);
+
+relfw_out:
+       release_firmware(fw);
+
+out:
+       return err;
+}
+
+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;
+
+       if (*edidname == '\0')
+               return ret;
+
+       colon = strchr(edidname, ':');
+       if (colon != NULL) {
+               if (strncmp(connector_name, edidname, colon - edidname))
+                       return ret;
+               edidname = colon + 1;
+               if (*edidname == '\0')
+                       return ret;
+       }
+
+       last = edidname + strlen(edidname) - 1;
+       if (*last == '\n')
+               *last = '\0';
+
+       ret = edid_load(connector, edidname, connector_name);
+       if (ret)
+               return 0;
+
+       drm_mode_connector_update_edid_property(connector,
+           (struct edid *) connector->display_info.raw_edid);
+
+       return drm_add_edid_modes(connector, (struct edid *)
+           connector->display_info.raw_edid);
+}
index aada26f63decc7a91d2803079dc9795d053ecdc2..7740dd26f00706d3b2059f475d10399f8f66d068 100644 (file)
@@ -306,91 +306,31 @@ static struct sysrq_key_op sysrq_drm_fb_helper_restore_op = {
 static struct sysrq_key_op sysrq_drm_fb_helper_restore_op = { };
 #endif
 
-static void drm_fb_helper_on(struct fb_info *info)
+static void drm_fb_helper_dpms(struct fb_info *info, int dpms_mode)
 {
        struct drm_fb_helper *fb_helper = info->par;
        struct drm_device *dev = fb_helper->dev;
        struct drm_crtc *crtc;
-       struct drm_crtc_helper_funcs *crtc_funcs;
-       struct drm_connector *connector;
-       struct drm_encoder *encoder;
-       int i, j;
-
-       /*
-        * For each CRTC in this fb, turn the crtc on then,
-        * find all associated encoders and turn them on.
-        */
-       mutex_lock(&dev->mode_config.mutex);
-       for (i = 0; i < fb_helper->crtc_count; i++) {
-               crtc = fb_helper->crtc_info[i].mode_set.crtc;
-               crtc_funcs = crtc->helper_private;
-
-               if (!crtc->enabled)
-                       continue;
-
-               crtc_funcs->dpms(crtc, DRM_MODE_DPMS_ON);
-
-               /* Walk the connectors & encoders on this fb turning them on */
-               for (j = 0; j < fb_helper->connector_count; j++) {
-                       connector = fb_helper->connector_info[j]->connector;
-                       connector->dpms = DRM_MODE_DPMS_ON;
-                       drm_connector_property_set_value(connector,
-                                                        dev->mode_config.dpms_property,
-                                                        DRM_MODE_DPMS_ON);
-               }
-               /* Found a CRTC on this fb, now find encoders */
-               list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) {
-                       if (encoder->crtc == crtc) {
-                               struct drm_encoder_helper_funcs *encoder_funcs;
-
-                               encoder_funcs = encoder->helper_private;
-                               encoder_funcs->dpms(encoder, DRM_MODE_DPMS_ON);
-                       }
-               }
-       }
-       mutex_unlock(&dev->mode_config.mutex);
-}
-
-static void drm_fb_helper_off(struct fb_info *info, int dpms_mode)
-{
-       struct drm_fb_helper *fb_helper = info->par;
-       struct drm_device *dev = fb_helper->dev;
-       struct drm_crtc *crtc;
-       struct drm_crtc_helper_funcs *crtc_funcs;
        struct drm_connector *connector;
-       struct drm_encoder *encoder;
        int i, j;
 
        /*
-        * For each CRTC in this fb, find all associated encoders
-        * and turn them off, then turn off the CRTC.
+        * For each CRTC in this fb, turn the connectors on/off.
         */
        mutex_lock(&dev->mode_config.mutex);
        for (i = 0; i < fb_helper->crtc_count; i++) {
                crtc = fb_helper->crtc_info[i].mode_set.crtc;
-               crtc_funcs = crtc->helper_private;
 
                if (!crtc->enabled)
                        continue;
 
-               /* Walk the connectors on this fb and mark them off */
+               /* Walk the connectors & encoders on this fb turning them on/off */
                for (j = 0; j < fb_helper->connector_count; j++) {
                        connector = fb_helper->connector_info[j]->connector;
-                       connector->dpms = dpms_mode;
+                       drm_helper_connector_dpms(connector, dpms_mode);
                        drm_connector_property_set_value(connector,
-                                                        dev->mode_config.dpms_property,
-                                                        dpms_mode);
-               }
-               /* Found a CRTC on this fb, now find encoders */
-               list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) {
-                       if (encoder->crtc == crtc) {
-                               struct drm_encoder_helper_funcs *encoder_funcs;
-
-                               encoder_funcs = encoder->helper_private;
-                               encoder_funcs->dpms(encoder, dpms_mode);
-                       }
+                               dev->mode_config.dpms_property, dpms_mode);
                }
-               crtc_funcs->dpms(crtc, DRM_MODE_DPMS_OFF);
        }
        mutex_unlock(&dev->mode_config.mutex);
 }
@@ -400,23 +340,23 @@ int drm_fb_helper_blank(int blank, struct fb_info *info)
        switch (blank) {
        /* Display: On; HSync: On, VSync: On */
        case FB_BLANK_UNBLANK:
-               drm_fb_helper_on(info);
+               drm_fb_helper_dpms(info, DRM_MODE_DPMS_ON);
                break;
        /* Display: Off; HSync: On, VSync: On */
        case FB_BLANK_NORMAL:
-               drm_fb_helper_off(info, DRM_MODE_DPMS_STANDBY);
+               drm_fb_helper_dpms(info, DRM_MODE_DPMS_STANDBY);
                break;
        /* Display: Off; HSync: Off, VSync: On */
        case FB_BLANK_HSYNC_SUSPEND:
-               drm_fb_helper_off(info, DRM_MODE_DPMS_STANDBY);
+               drm_fb_helper_dpms(info, DRM_MODE_DPMS_STANDBY);
                break;
        /* Display: Off; HSync: On, VSync: Off */
        case FB_BLANK_VSYNC_SUSPEND:
-               drm_fb_helper_off(info, DRM_MODE_DPMS_SUSPEND);
+               drm_fb_helper_dpms(info, DRM_MODE_DPMS_SUSPEND);
                break;
        /* Display: Off; HSync: Off, VSync: Off */
        case FB_BLANK_POWERDOWN:
-               drm_fb_helper_off(info, DRM_MODE_DPMS_OFF);
+               drm_fb_helper_dpms(info, DRM_MODE_DPMS_OFF);
                break;
        }
        return 0;
@@ -430,8 +370,11 @@ static void drm_fb_helper_crtc_free(struct drm_fb_helper *helper)
        for (i = 0; i < helper->connector_count; i++)
                kfree(helper->connector_info[i]);
        kfree(helper->connector_info);
-       for (i = 0; i < helper->crtc_count; i++)
+       for (i = 0; i < helper->crtc_count; i++) {
                kfree(helper->crtc_info[i].mode_set.connectors);
+               if (helper->crtc_info[i].mode_set.mode)
+                       drm_mode_destroy(helper->dev, helper->crtc_info[i].mode_set.mode);
+       }
        kfree(helper->crtc_info);
 }
 
@@ -474,11 +417,10 @@ int drm_fb_helper_init(struct drm_device *dev,
 
        i = 0;
        list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
-               fb_helper->crtc_info[i].crtc_id = crtc->base.id;
                fb_helper->crtc_info[i].mode_set.crtc = crtc;
                i++;
        }
-       fb_helper->conn_limit = max_conn_count;
+
        return 0;
 out_free:
        drm_fb_helper_crtc_free(fb_helper);
index 6263b0147598de9688fcdfefb8a0dadc841f9ad9..7348a3dab250a47046eb1f025f12fb3365a5df16 100644 (file)
@@ -133,6 +133,9 @@ int drm_open(struct inode *inode, struct file *filp)
        if (!(dev = minor->dev))
                return -ENODEV;
 
+       if (drm_device_is_unplugged(dev))
+               return -ENODEV;
+
        retcode = drm_open_helper(inode, filp, dev);
        if (!retcode) {
                atomic_inc(&dev->counts[_DRM_STAT_OPENS]);
@@ -181,6 +184,9 @@ int drm_stub_open(struct inode *inode, struct file *filp)
        if (!(dev = minor->dev))
                goto out;
 
+       if (drm_device_is_unplugged(dev))
+               goto out;
+
        old_fops = filp->f_op;
        filp->f_op = fops_get(dev->driver->fops);
        if (filp->f_op == NULL) {
@@ -579,6 +585,8 @@ int drm_release(struct inode *inode, struct file *filp)
                        retcode = -EBUSY;
                } else
                        retcode = drm_lastclose(dev);
+               if (drm_device_is_unplugged(dev))
+                       drm_put_dev(dev);
        }
        mutex_unlock(&drm_global_mutex);
 
index f8625e2907288f590552183ff579a9c7aa756d40..0ef358e5324542a6be54751527e889c8eb8f3eca 100644 (file)
@@ -661,6 +661,9 @@ int drm_gem_mmap(struct file *filp, struct vm_area_struct *vma)
        struct drm_hash_item *hash;
        int ret = 0;
 
+       if (drm_device_is_unplugged(dev))
+               return -ENODEV;
+
        mutex_lock(&dev->struct_mutex);
 
        if (drm_ht_find_item(&mm->offset_hash, vma->vm_pgoff, &hash)) {
@@ -700,7 +703,6 @@ int drm_gem_mmap(struct file *filp, struct vm_area_struct *vma)
         */
        drm_gem_object_reference(obj);
 
-       vma->vm_file = filp;    /* Needed for drm_vm_open() */
        drm_vm_open_locked(vma);
 
 out_unlock:
index 956fd38d7c9ed6da526479cf8b1d91ad2038c22e..cf85155da2a00be52738f4ef9c2e2002b6bf378c 100644 (file)
@@ -37,6 +37,7 @@
 #include "drm_core.h"
 
 #include "linux/pci.h"
+#include "linux/export.h"
 
 /**
  * Get the bus id.
@@ -276,6 +277,12 @@ int drm_getcap(struct drm_device *dev, void *data, struct drm_file *file_priv)
        case DRM_CAP_VBLANK_HIGH_CRTC:
                req->value = 1;
                break;
+       case DRM_CAP_DUMB_PREFERRED_DEPTH:
+               req->value = dev->mode_config.preferred_depth;
+               break;
+       case DRM_CAP_DUMB_PREFER_SHADOW:
+               req->value = dev->mode_config.prefer_shadow;
+               break;
        default:
                return -EINVAL;
        }
@@ -346,3 +353,4 @@ int drm_noop(struct drm_device *dev, void *data,
        DRM_DEBUG("\n");
        return 0;
 }
+EXPORT_SYMBOL(drm_noop);
index 44a5d0ad8b7c56e99c9ce85f2dfcf14d19e855e0..c869436e238a1b0fd375af236c57df29fb4be7a2 100644 (file)
@@ -305,7 +305,7 @@ static void drm_irq_vgaarb_nokms(void *cookie, bool state)
  * \param dev DRM device.
  *
  * Initializes the IRQ related data. Installs the handler, calling the driver
- * \c drm_driver_irq_preinstall() and \c drm_driver_irq_postinstall() functions
+ * \c irq_preinstall() and \c irq_postinstall() functions
  * before and after the installation.
  */
 int drm_irq_install(struct drm_device *dev)
@@ -385,7 +385,7 @@ EXPORT_SYMBOL(drm_irq_install);
  *
  * \param dev DRM device.
  *
- * Calls the driver's \c drm_driver_irq_uninstall() function, and stops the irq.
+ * Calls the driver's \c irq_uninstall() function, and stops the irq.
  */
 int drm_irq_uninstall(struct drm_device *dev)
 {
index c8b6b66d428da7ae167425b8d01f60acebfc30e5..c86a0f1a435c8c1c4d805e94613f39d759c2b83a 100644 (file)
 #include <linux/export.h>
 #include "drmP.h"
 
-/**
- * Called when "/proc/dri/%dev%/mem" is read.
- *
- * \param buf output buffer.
- * \param start start of output data.
- * \param offset requested start offset.
- * \param len requested number of bytes.
- * \param eof whether there is no more data to return.
- * \param data private data.
- * \return number of written bytes.
- *
- * No-op.
- */
-int drm_mem_info(char *buf, char **start, off_t offset,
-                int len, int *eof, void *data)
-{
-       return 0;
-}
-
 #if __OS_HAS_AGP
 static void *agp_remap(unsigned long offset, unsigned long size,
                       struct drm_device * dev)
index fb8e46b4e8bc9b7f2a4b0e05ae38a49c99b82b2b..b7adb4a967fd02b80eee0f0bd6e78b2d76c7e9b1 100644 (file)
@@ -686,8 +686,6 @@ void drm_mode_set_crtcinfo(struct drm_display_mode *p, int adjust_flags)
                        p->crtc_vsync_end /= 2;
                        p->crtc_vtotal /= 2;
                }
-
-               p->crtc_vtotal |= 1;
        }
 
        if (p->flags & DRM_MODE_FLAG_DBLSCAN) {
@@ -715,6 +713,27 @@ void drm_mode_set_crtcinfo(struct drm_display_mode *p, int adjust_flags)
 EXPORT_SYMBOL(drm_mode_set_crtcinfo);
 
 
+/**
+ * drm_mode_copy - copy the mode
+ * @dst: mode to overwrite
+ * @src: mode to copy
+ *
+ * LOCKING:
+ * None.
+ *
+ * Copy an existing mode into another mode, preserving the object id
+ * of the destination mode.
+ */
+void drm_mode_copy(struct drm_display_mode *dst, const struct drm_display_mode *src)
+{
+       int id = dst->base.id;
+
+       *dst = *src;
+       dst->base.id = id;
+       INIT_LIST_HEAD(&dst->head);
+}
+EXPORT_SYMBOL(drm_mode_copy);
+
 /**
  * drm_mode_duplicate - allocate and duplicate an existing mode
  * @m: mode to duplicate
@@ -729,16 +748,13 @@ struct drm_display_mode *drm_mode_duplicate(struct drm_device *dev,
                                            const struct drm_display_mode *mode)
 {
        struct drm_display_mode *nmode;
-       int new_id;
 
        nmode = drm_mode_create(dev);
        if (!nmode)
                return NULL;
 
-       new_id = nmode->base.id;
-       *nmode = *mode;
-       nmode->base.id = new_id;
-       INIT_LIST_HEAD(&nmode->head);
+       drm_mode_copy(nmode, mode);
+
        return nmode;
 }
 EXPORT_SYMBOL(drm_mode_duplicate);
index d4d10b7880cf8b96303d6c539ff09a08299241f2..13f3d936472f3aadeed78ca465b2cf4feef617ff 100644 (file)
@@ -324,8 +324,6 @@ int drm_get_pci_dev(struct pci_dev *pdev, const struct pci_device_id *ent,
        if (ret)
                goto err_g1;
 
-       pci_set_master(pdev);
-
        dev->pdev = pdev;
        dev->dev = &pdev->dev;
 
index ae9db5e2b27c2449b04150be14eee439a608c0db..82431dcae37bdb6c2f97b3c2954d96c62a2e5e35 100644 (file)
@@ -122,7 +122,7 @@ static const char *drm_platform_get_name(struct drm_device *dev)
 
 static int drm_platform_set_busid(struct drm_device *dev, struct drm_master *master)
 {
-       int len, ret;
+       int len, ret, id;
 
        master->unique_len = 13 + strlen(dev->platformdev->name);
        master->unique_size = master->unique_len;
@@ -131,8 +131,16 @@ static int drm_platform_set_busid(struct drm_device *dev, struct drm_master *mas
        if (master->unique == NULL)
                return -ENOMEM;
 
+       id = dev->platformdev->id;
+
+       /* if only a single instance of the platform device, id will be
+        * set to -1.. use 0 instead to avoid a funny looking bus-id:
+        */
+       if (id == -1)
+               id = 0;
+
        len = snprintf(master->unique, master->unique_len,
-                       "platform:%s:%02d", dev->platformdev->name, dev->platformdev->id);
+                       "platform:%s:%02d", dev->platformdev->name, id);
 
        if (len > master->unique_len) {
                DRM_ERROR("Unique buffer overflowed\n");
index 6d7b083c5b776d482d3b0a21f60a43adebd8f66d..aa454f80e1098c641fa82834bc9034df001c578f 100644 (file)
@@ -319,6 +319,7 @@ int drm_fill_in_dev(struct drm_device *dev,
        drm_lastclose(dev);
        return retcode;
 }
+EXPORT_SYMBOL(drm_fill_in_dev);
 
 
 /**
@@ -397,6 +398,7 @@ err_idr:
        *minor = NULL;
        return ret;
 }
+EXPORT_SYMBOL(drm_get_minor);
 
 /**
  * Put a secondary minor number.
@@ -428,6 +430,12 @@ int drm_put_minor(struct drm_minor **minor_p)
        *minor_p = NULL;
        return 0;
 }
+EXPORT_SYMBOL(drm_put_minor);
+
+static void drm_unplug_minor(struct drm_minor *minor)
+{
+       drm_sysfs_device_remove(minor);
+}
 
 /**
  * Called via drm_exit() at module unload time or when pci device is
@@ -492,3 +500,21 @@ void drm_put_dev(struct drm_device *dev)
        kfree(dev);
 }
 EXPORT_SYMBOL(drm_put_dev);
+
+void drm_unplug_dev(struct drm_device *dev)
+{
+       /* for a USB device */
+       if (drm_core_check_feature(dev, DRIVER_MODESET))
+               drm_unplug_minor(dev->control);
+       drm_unplug_minor(dev->primary);
+
+       mutex_lock(&drm_global_mutex);
+
+       drm_device_set_unplugged(dev);
+
+       if (dev->open_count == 0) {
+               drm_put_dev(dev);
+       }
+       mutex_unlock(&drm_global_mutex);
+}
+EXPORT_SYMBOL(drm_unplug_dev);
index 62c3675045ac72462eedc6a18b375a1b33eb498d..5a7bd51fc3d842be78c6fd07f72a84a554e3ae12 100644 (file)
@@ -454,6 +454,8 @@ void drm_sysfs_connector_remove(struct drm_connector *connector)
 {
        int i;
 
+       if (!connector->kdev.parent)
+               return;
        DRM_DEBUG("removing \"%s\" from sysfs\n",
                  drm_get_connector_name(connector));
 
@@ -461,6 +463,7 @@ void drm_sysfs_connector_remove(struct drm_connector *connector)
                device_remove_file(&connector->kdev, &connector_attrs[i]);
        sysfs_remove_bin_file(&connector->kdev.kobj, &edid_attr);
        device_unregister(&connector->kdev);
+       connector->kdev.parent = NULL;
 }
 EXPORT_SYMBOL(drm_sysfs_connector_remove);
 
@@ -533,7 +536,9 @@ err_out:
  */
 void drm_sysfs_device_remove(struct drm_minor *minor)
 {
-       device_unregister(&minor->kdev);
+       if (minor->kdev.parent)
+               device_unregister(&minor->kdev);
+       minor->kdev.parent = NULL;
 }
 
 
index 445003f4dc939cbc731a915551b509633f2f6769..c8c83dad2ce1443d67782ef94fd3ae70b0505983 100644 (file)
@@ -2,7 +2,6 @@
 #include <linux/usb.h>
 #include <linux/export.h>
 
-#ifdef CONFIG_USB
 int drm_get_usb_dev(struct usb_interface *interface,
                    const struct usb_device_id *id,
                    struct drm_driver *driver)
@@ -115,4 +114,3 @@ void drm_usb_exit(struct drm_driver *driver,
        usb_deregister(udriver);
 }
 EXPORT_SYMBOL(drm_usb_exit);
-#endif
index 8c03eaf414486c9590c830d2e551369406afff84..149561818349e602e39dc469e60a385394c5bdf7 100644 (file)
@@ -519,7 +519,6 @@ 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;
 
-       vma->vm_file = filp;    /* Needed for drm_vm_open() */
        drm_vm_open_locked(vma);
        return 0;
 }
@@ -671,7 +670,6 @@ 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;
 
-       vma->vm_file = filp;    /* Needed for drm_vm_open() */
        drm_vm_open_locked(vma);
        return 0;
 }
@@ -682,6 +680,9 @@ int drm_mmap(struct file *filp, struct vm_area_struct *vma)
        struct drm_device *dev = priv->minor->dev;
        int ret;
 
+       if (drm_device_is_unplugged(dev))
+               return -ENODEV;
+
        mutex_lock(&dev->struct_mutex);
        ret = drm_mmap_locked(filp, vma);
        mutex_unlock(&dev->struct_mutex);
index b9e5266c341baae491412e1fe75887c270b1fdb7..3343ac437fe5c886e6dc1879130d67c4340b8601 100644 (file)
@@ -1,7 +1,6 @@
 config DRM_EXYNOS
        tristate "DRM Support for Samsung SoC EXYNOS Series"
        depends on DRM && PLAT_SAMSUNG
-       default n
        select DRM_KMS_HELPER
        select FB_CFB_FILLRECT
        select FB_CFB_COPYAREA
@@ -12,16 +11,19 @@ config DRM_EXYNOS
          If M is selected the module will be called exynosdrm.
 
 config DRM_EXYNOS_FIMD
-       tristate "Exynos DRM FIMD"
+       bool "Exynos DRM FIMD"
        depends on DRM_EXYNOS && !FB_S3C
-       default n
        help
          Choose this option if you want to use Exynos FIMD for DRM.
-         If M is selected, the module will be called exynos_drm_fimd
 
 config DRM_EXYNOS_HDMI
-       tristate "Exynos DRM HDMI"
+       bool "Exynos DRM HDMI"
        depends on DRM_EXYNOS && !VIDEO_SAMSUNG_S5P_TV
        help
          Choose this option if you want to use Exynos HDMI for DRM.
-         If M is selected, the module will be called exynos_drm_hdmi
+
+config DRM_EXYNOS_VIDI
+       bool "Exynos DRM Virtual Display"
+       depends on DRM_EXYNOS
+       help
+         Choose this option if you want to use Exynos VIDI for DRM.
index 395e69c9a96e63daea257add8befa40acdc80640..9e0bff8badf9501c8d4d0d5ca49b1b15e7a68c8c 100644 (file)
@@ -8,7 +8,10 @@ exynosdrm-y := exynos_drm_drv.o exynos_drm_encoder.o exynos_drm_connector.o \
                exynos_drm_buf.o exynos_drm_gem.o exynos_drm_core.o \
                exynos_drm_plane.o
 
-obj-$(CONFIG_DRM_EXYNOS) += exynosdrm.o
-obj-$(CONFIG_DRM_EXYNOS_FIMD) += exynos_drm_fimd.o
-obj-$(CONFIG_DRM_EXYNOS_HDMI) += exynos_hdmi.o exynos_mixer.o exynos_ddc.o \
-                                exynos_hdmiphy.o exynos_drm_hdmi.o
+exynosdrm-$(CONFIG_DRM_EXYNOS_FIMD)    += exynos_drm_fimd.o
+exynosdrm-$(CONFIG_DRM_EXYNOS_HDMI)    += exynos_hdmi.o exynos_mixer.o \
+                                          exynos_ddc.o exynos_hdmiphy.o \
+                                          exynos_drm_hdmi.o
+exynosdrm-$(CONFIG_DRM_EXYNOS_VIDI)    += exynos_drm_vidi.o
+
+obj-$(CONFIG_DRM_EXYNOS)               += exynosdrm.o
index 84b614fe26fd2f4e88a0f6e68a245ab929a1e01e..7e1051d07f1f85b3ee8827f006093a2a10d66413 100644 (file)
@@ -55,4 +55,3 @@ struct i2c_driver ddc_driver = {
        .remove         = __devexit_p(s5p_ddc_remove),
        .command                = NULL,
 };
-EXPORT_SYMBOL(ddc_driver);
index 3cf785c58186da818eb1757584ef8e00e14efa38..4a3a5f72ed4a42938dd38b998752c63baf953304 100644 (file)
 
 #include "drmP.h"
 #include "drm.h"
+#include "exynos_drm.h"
 
 #include "exynos_drm_drv.h"
 #include "exynos_drm_gem.h"
 #include "exynos_drm_buf.h"
 
 static int lowlevel_buffer_allocate(struct drm_device *dev,
-               struct exynos_drm_gem_buf *buffer)
+               unsigned int flags, struct exynos_drm_gem_buf *buf)
 {
+       dma_addr_t start_addr, end_addr;
+       unsigned int npages, page_size, i = 0;
+       struct scatterlist *sgl;
+       int ret = 0;
+
        DRM_DEBUG_KMS("%s\n", __FILE__);
 
-       buffer->kvaddr = dma_alloc_writecombine(dev->dev, buffer->size,
-                       &buffer->dma_addr, GFP_KERNEL);
-       if (!buffer->kvaddr) {
-               DRM_ERROR("failed to allocate buffer.\n");
+       if (flags & EXYNOS_BO_NONCONTIG) {
+               DRM_DEBUG_KMS("not support allocation type.\n");
+               return -EINVAL;
+       }
+
+       if (buf->dma_addr) {
+               DRM_DEBUG_KMS("already allocated.\n");
+               return 0;
+       }
+
+       if (buf->size >= SZ_1M) {
+               npages = (buf->size >> SECTION_SHIFT) + 1;
+               page_size = SECTION_SIZE;
+       } else if (buf->size >= SZ_64K) {
+               npages = (buf->size >> 16) + 1;
+               page_size = SZ_64K;
+       } else {
+               npages = (buf->size >> PAGE_SHIFT) + 1;
+               page_size = PAGE_SIZE;
+       }
+
+       buf->sgt = kzalloc(sizeof(struct sg_table), GFP_KERNEL);
+       if (!buf->sgt) {
+               DRM_ERROR("failed to allocate sg table.\n");
                return -ENOMEM;
        }
 
-       DRM_DEBUG_KMS("vaddr(0x%lx), dma_addr(0x%lx), size(0x%lx)\n",
-                       (unsigned long)buffer->kvaddr,
-                       (unsigned long)buffer->dma_addr,
-                       buffer->size);
+       ret = sg_alloc_table(buf->sgt, npages, GFP_KERNEL);
+       if (ret < 0) {
+               DRM_ERROR("failed to initialize sg table.\n");
+               kfree(buf->sgt);
+               buf->sgt = NULL;
+               return -ENOMEM;
+       }
 
-       return 0;
+               buf->kvaddr = dma_alloc_writecombine(dev->dev, buf->size,
+                               &buf->dma_addr, GFP_KERNEL);
+               if (!buf->kvaddr) {
+                       DRM_ERROR("failed to allocate buffer.\n");
+                       ret = -ENOMEM;
+                       goto err1;
+               }
+
+               start_addr = buf->dma_addr;
+               end_addr = buf->dma_addr + buf->size;
+
+               buf->pages = kzalloc(sizeof(struct page) * npages, GFP_KERNEL);
+               if (!buf->pages) {
+                       DRM_ERROR("failed to allocate pages.\n");
+                       ret = -ENOMEM;
+                       goto err2;
+               }
+
+       start_addr = buf->dma_addr;
+       end_addr = buf->dma_addr + buf->size;
+
+       buf->pages = kzalloc(sizeof(struct page) * npages, GFP_KERNEL);
+       if (!buf->pages) {
+               DRM_ERROR("failed to allocate pages.\n");
+               ret = -ENOMEM;
+               goto err2;
+       }
+
+       sgl = buf->sgt->sgl;
+
+       while (i < npages) {
+               buf->pages[i] = phys_to_page(start_addr);
+               sg_set_page(sgl, buf->pages[i], page_size, 0);
+               sg_dma_address(sgl) = start_addr;
+               start_addr += page_size;
+               if (end_addr - start_addr < page_size)
+                       break;
+               sgl = sg_next(sgl);
+               i++;
+       }
+
+       buf->pages[i] = phys_to_page(start_addr);
+
+       sgl = sg_next(sgl);
+       sg_set_page(sgl, buf->pages[i+1], end_addr - start_addr, 0);
+
+       DRM_DEBUG_KMS("vaddr(0x%lx), dma_addr(0x%lx), size(0x%lx)\n",
+                       (unsigned long)buf->kvaddr,
+                       (unsigned long)buf->dma_addr,
+                       buf->size);
+
+       return ret;
+err2:
+       dma_free_writecombine(dev->dev, buf->size, buf->kvaddr,
+                       (dma_addr_t)buf->dma_addr);
+       buf->dma_addr = (dma_addr_t)NULL;
+err1:
+       sg_free_table(buf->sgt);
+       kfree(buf->sgt);
+       buf->sgt = NULL;
+
+       return ret;
 }
 
 static void lowlevel_buffer_deallocate(struct drm_device *dev,
-               struct exynos_drm_gem_buf *buffer)
+               unsigned int flags, struct exynos_drm_gem_buf *buf)
 {
        DRM_DEBUG_KMS("%s.\n", __FILE__);
 
-       if (buffer->dma_addr && buffer->size)
-               dma_free_writecombine(dev->dev, buffer->size, buffer->kvaddr,
-                               (dma_addr_t)buffer->dma_addr);
-       else
-               DRM_DEBUG_KMS("buffer data are invalid.\n");
+       /*
+        * release only physically continuous memory and
+        * non-continuous memory would be released by exynos
+        * gem framework.
+        */
+       if (flags & EXYNOS_BO_NONCONTIG) {
+               DRM_DEBUG_KMS("not support allocation type.\n");
+               return;
+       }
+
+       if (!buf->dma_addr) {
+               DRM_DEBUG_KMS("dma_addr is invalid.\n");
+               return;
+       }
+
+       DRM_DEBUG_KMS("vaddr(0x%lx), dma_addr(0x%lx), size(0x%lx)\n",
+                       (unsigned long)buf->kvaddr,
+                       (unsigned long)buf->dma_addr,
+                       buf->size);
+
+       sg_free_table(buf->sgt);
+
+       kfree(buf->sgt);
+       buf->sgt = NULL;
+
+       kfree(buf->pages);
+       buf->pages = NULL;
+
+       dma_free_writecombine(dev->dev, buf->size, buf->kvaddr,
+                               (dma_addr_t)buf->dma_addr);
+       buf->dma_addr = (dma_addr_t)NULL;
 }
 
-struct exynos_drm_gem_buf *exynos_drm_buf_create(struct drm_device *dev,
-               unsigned int size)
+struct exynos_drm_gem_buf *exynos_drm_init_buf(struct drm_device *dev,
+                                               unsigned int size)
 {
        struct exynos_drm_gem_buf *buffer;
 
@@ -77,21 +193,11 @@ struct exynos_drm_gem_buf *exynos_drm_buf_create(struct drm_device *dev,
        }
 
        buffer->size = size;
-
-       /*
-        * allocate memory region with size and set the memory information
-        * to vaddr and dma_addr of a buffer object.
-        */
-       if (lowlevel_buffer_allocate(dev, buffer) < 0) {
-               kfree(buffer);
-               return NULL;
-       }
-
        return buffer;
 }
 
-void exynos_drm_buf_destroy(struct drm_device *dev,
-               struct exynos_drm_gem_buf *buffer)
+void exynos_drm_fini_buf(struct drm_device *dev,
+                               struct exynos_drm_gem_buf *buffer)
 {
        DRM_DEBUG_KMS("%s.\n", __FILE__);
 
@@ -100,12 +206,27 @@ void exynos_drm_buf_destroy(struct drm_device *dev,
                return;
        }
 
-       lowlevel_buffer_deallocate(dev, buffer);
-
        kfree(buffer);
        buffer = NULL;
 }
 
-MODULE_AUTHOR("Inki Dae <inki.dae@samsung.com>");
-MODULE_DESCRIPTION("Samsung SoC DRM Buffer Management Module");
-MODULE_LICENSE("GPL");
+int exynos_drm_alloc_buf(struct drm_device *dev,
+               struct exynos_drm_gem_buf *buf, unsigned int flags)
+{
+
+       /*
+        * allocate memory region and set the memory information
+        * to vaddr and dma_addr of a buffer object.
+        */
+       if (lowlevel_buffer_allocate(dev, flags, buf) < 0)
+               return -ENOMEM;
+
+       return 0;
+}
+
+void exynos_drm_free_buf(struct drm_device *dev,
+               unsigned int flags, struct exynos_drm_gem_buf *buffer)
+{
+
+       lowlevel_buffer_deallocate(dev, flags, buffer);
+}
index c913f2bad760f6235f86e2953b3c6d131daae243..3388e4eb4ba21f8c3ba42d2da05d592a0d24b3df 100644 (file)
 #ifndef _EXYNOS_DRM_BUF_H_
 #define _EXYNOS_DRM_BUF_H_
 
-/* allocate physical memory. */
-struct exynos_drm_gem_buf *exynos_drm_buf_create(struct drm_device *dev,
-               unsigned int size);
+/* create and initialize buffer object. */
+struct exynos_drm_gem_buf *exynos_drm_init_buf(struct drm_device *dev,
+                                               unsigned int size);
 
-/* remove allocated physical memory. */
-void exynos_drm_buf_destroy(struct drm_device *dev,
-               struct exynos_drm_gem_buf *buffer);
+/* destroy buffer object. */
+void exynos_drm_fini_buf(struct drm_device *dev,
+                               struct exynos_drm_gem_buf *buffer);
+
+/* allocate physical memory region and setup sgt and pages. */
+int exynos_drm_alloc_buf(struct drm_device *dev,
+                               struct exynos_drm_gem_buf *buf,
+                               unsigned int flags);
+
+/* release physical memory region, sgt and pages. */
+void exynos_drm_free_buf(struct drm_device *dev,
+                               unsigned int flags,
+                               struct exynos_drm_gem_buf *buffer);
 
 #endif
index 99d5527b2ca6a61f4e036b7d840f5a1110d63c4e..bf791fa0e50d06311b98379aa550a2444051b191 100644 (file)
@@ -225,6 +225,29 @@ static struct drm_connector_helper_funcs exynos_connector_helper_funcs = {
        .best_encoder   = exynos_drm_best_encoder,
 };
 
+static int exynos_drm_connector_fill_modes(struct drm_connector *connector,
+                               unsigned int max_width, unsigned int max_height)
+{
+       struct exynos_drm_connector *exynos_connector =
+                                       to_exynos_connector(connector);
+       struct exynos_drm_manager *manager = exynos_connector->manager;
+       struct exynos_drm_manager_ops *ops = manager->ops;
+       unsigned int width, height;
+
+       width = max_width;
+       height = max_height;
+
+       /*
+        * if specific driver want to find desired_mode using maxmum
+        * resolution then get max width and height from that driver.
+        */
+       if (ops && ops->get_max_resol)
+               ops->get_max_resol(manager->dev, &width, &height);
+
+       return drm_helper_probe_single_connector_modes(connector, width,
+                                                       height);
+}
+
 /* get detection status of display device. */
 static enum drm_connector_status
 exynos_drm_connector_detect(struct drm_connector *connector, bool force)
@@ -262,7 +285,7 @@ static void exynos_drm_connector_destroy(struct drm_connector *connector)
 
 static struct drm_connector_funcs exynos_connector_funcs = {
        .dpms           = drm_helper_connector_dpms,
-       .fill_modes     = drm_helper_probe_single_connector_modes,
+       .fill_modes     = exynos_drm_connector_fill_modes,
        .detect         = exynos_drm_connector_detect,
        .destroy        = exynos_drm_connector_destroy,
 };
@@ -292,6 +315,10 @@ struct drm_connector *exynos_drm_connector_create(struct drm_device *dev,
                connector->interlace_allowed = true;
                connector->polled = DRM_CONNECTOR_POLL_HPD;
                break;
+       case EXYNOS_DISPLAY_TYPE_VIDI:
+               type = DRM_MODE_CONNECTOR_VIRTUAL;
+               connector->polled = DRM_CONNECTOR_POLL_HPD;
+               break;
        default:
                type = DRM_MODE_CONNECTOR_Unknown;
                break;
@@ -325,9 +352,3 @@ err_connector:
        kfree(exynos_connector);
        return NULL;
 }
-
-MODULE_AUTHOR("Inki Dae <inki.dae@samsung.com>");
-MODULE_AUTHOR("Joonyoung Shim <jy0922.shim@samsung.com>");
-MODULE_AUTHOR("Seung-Woo Kim <sw0312.kim@samsung.com>");
-MODULE_DESCRIPTION("Samsung SoC DRM Connector Driver");
-MODULE_LICENSE("GPL");
index d08a55896d50adce1345902bbe8072c399179378..411832e8e17aa9fcd9eed13fc614be878f781403 100644 (file)
@@ -32,7 +32,6 @@
 #include "exynos_drm_connector.h"
 #include "exynos_drm_fbdev.h"
 
-static DEFINE_MUTEX(exynos_drm_mutex);
 static LIST_HEAD(exynos_drm_subdrv_list);
 static struct drm_device *drm_dev;
 
@@ -60,6 +59,9 @@ static int exynos_drm_subdrv_probe(struct drm_device *dev,
                        return ret;
        }
 
+       if (subdrv->is_local)
+               return 0;
+
        /* create and initialize a encoder for this sub driver. */
        encoder = exynos_drm_encoder_create(dev, &subdrv->manager,
                        (1 << MAX_CRTC) - 1);
@@ -116,13 +118,10 @@ int exynos_drm_device_register(struct drm_device *dev)
        if (!dev)
                return -EINVAL;
 
-       if (drm_dev) {
-               DRM_ERROR("Already drm device were registered\n");
-               return -EBUSY;
-       }
+       drm_dev = dev;
 
-       mutex_lock(&exynos_drm_mutex);
        list_for_each_entry_safe(subdrv, n, &exynos_drm_subdrv_list, list) {
+               subdrv->drm_dev = dev;
                err = exynos_drm_subdrv_probe(dev, subdrv);
                if (err) {
                        DRM_DEBUG("exynos drm subdrv probe failed.\n");
@@ -130,9 +129,6 @@ int exynos_drm_device_register(struct drm_device *dev)
                }
        }
 
-       drm_dev = dev;
-       mutex_unlock(&exynos_drm_mutex);
-
        return 0;
 }
 EXPORT_SYMBOL_GPL(exynos_drm_device_register);
@@ -143,86 +139,28 @@ int exynos_drm_device_unregister(struct drm_device *dev)
 
        DRM_DEBUG_DRIVER("%s\n", __FILE__);
 
-       if (!dev || dev != drm_dev) {
+       if (!dev) {
                WARN(1, "Unexpected drm device unregister!\n");
                return -EINVAL;
        }
 
-       mutex_lock(&exynos_drm_mutex);
        list_for_each_entry(subdrv, &exynos_drm_subdrv_list, list)
                exynos_drm_subdrv_remove(dev, subdrv);
 
        drm_dev = NULL;
-       mutex_unlock(&exynos_drm_mutex);
 
        return 0;
 }
 EXPORT_SYMBOL_GPL(exynos_drm_device_unregister);
 
-static int exynos_drm_mode_group_reinit(struct drm_device *dev)
-{
-       struct drm_mode_group *group = &dev->primary->mode_group;
-       uint32_t *id_list = group->id_list;
-       int ret;
-
-       DRM_DEBUG_DRIVER("%s\n", __FILE__);
-
-       ret = drm_mode_group_init_legacy_group(dev, group);
-       if (ret < 0)
-               return ret;
-
-       kfree(id_list);
-       return 0;
-}
-
 int exynos_drm_subdrv_register(struct exynos_drm_subdrv *subdrv)
 {
-       int err;
-
        DRM_DEBUG_DRIVER("%s\n", __FILE__);
 
        if (!subdrv)
                return -EINVAL;
 
-       mutex_lock(&exynos_drm_mutex);
-       if (drm_dev) {
-               err = exynos_drm_subdrv_probe(drm_dev, subdrv);
-               if (err) {
-                       DRM_ERROR("failed to probe exynos drm subdrv\n");
-                       mutex_unlock(&exynos_drm_mutex);
-                       return err;
-               }
-
-               /* setup possible_clones. */
-               exynos_drm_encoder_setup(drm_dev);
-
-               /*
-                * if any specific driver such as fimd or hdmi driver called
-                * exynos_drm_subdrv_register() later than drm_load(),
-                * the fb helper should be re-initialized and re-configured.
-                */
-               err = exynos_drm_fbdev_reinit(drm_dev);
-               if (err) {
-                       DRM_ERROR("failed to reinitialize exynos drm fbdev\n");
-                       exynos_drm_subdrv_remove(drm_dev, subdrv);
-                       mutex_unlock(&exynos_drm_mutex);
-                       return err;
-               }
-
-               err = exynos_drm_mode_group_reinit(drm_dev);
-               if (err) {
-                       DRM_ERROR("failed to reinitialize mode group\n");
-                       exynos_drm_fbdev_fini(drm_dev);
-                       exynos_drm_subdrv_remove(drm_dev, subdrv);
-                       mutex_unlock(&exynos_drm_mutex);
-                       return err;
-               }
-       }
-
-       subdrv->drm_dev = drm_dev;
-
        list_add_tail(&subdrv->list, &exynos_drm_subdrv_list);
-       mutex_unlock(&exynos_drm_mutex);
 
        return 0;
 }
@@ -230,46 +168,48 @@ EXPORT_SYMBOL_GPL(exynos_drm_subdrv_register);
 
 int exynos_drm_subdrv_unregister(struct exynos_drm_subdrv *subdrv)
 {
-       int ret = -EFAULT;
-
        DRM_DEBUG_DRIVER("%s\n", __FILE__);
 
-       if (!subdrv) {
-               DRM_DEBUG("Unexpected exynos drm subdrv unregister!\n");
-               return ret;
-       }
+       if (!subdrv)
+               return -EINVAL;
 
-       mutex_lock(&exynos_drm_mutex);
-       if (drm_dev) {
-               exynos_drm_subdrv_remove(drm_dev, subdrv);
-               list_del(&subdrv->list);
+       list_del(&subdrv->list);
 
-               /*
-                * fb helper should be updated once a sub driver is released
-                * to re-configure crtc and connector and also to re-setup
-                * drm framebuffer.
-                */
-               ret = exynos_drm_fbdev_reinit(drm_dev);
-               if (ret < 0) {
-                       DRM_ERROR("failed fb helper reinit.\n");
-                       goto fail;
-               }
+       return 0;
+}
+EXPORT_SYMBOL_GPL(exynos_drm_subdrv_unregister);
 
-               ret = exynos_drm_mode_group_reinit(drm_dev);
-               if (ret < 0) {
-                       DRM_ERROR("failed drm mode group reinit.\n");
-                       goto fail;
+int exynos_drm_subdrv_open(struct drm_device *dev, struct drm_file *file)
+{
+       struct exynos_drm_subdrv *subdrv;
+       int ret;
+
+       list_for_each_entry(subdrv, &exynos_drm_subdrv_list, list) {
+               if (subdrv->open) {
+                       ret = subdrv->open(dev, subdrv->manager.dev, file);
+                       if (ret)
+                               goto err;
                }
        }
 
-fail:
-       mutex_unlock(&exynos_drm_mutex);
+       return 0;
+
+err:
+       list_for_each_entry_reverse(subdrv, &subdrv->list, list) {
+               if (subdrv->close)
+                       subdrv->close(dev, subdrv->manager.dev, file);
+       }
        return ret;
 }
-EXPORT_SYMBOL_GPL(exynos_drm_subdrv_unregister);
+EXPORT_SYMBOL_GPL(exynos_drm_subdrv_open);
+
+void exynos_drm_subdrv_close(struct drm_device *dev, struct drm_file *file)
+{
+       struct exynos_drm_subdrv *subdrv;
 
-MODULE_AUTHOR("Inki Dae <inki.dae@samsung.com>");
-MODULE_AUTHOR("Joonyoung Shim <jy0922.shim@samsung.com>");
-MODULE_AUTHOR("Seung-Woo Kim <sw0312.kim@samsung.com>");
-MODULE_DESCRIPTION("Samsung SoC DRM Core Driver");
-MODULE_LICENSE("GPL");
+       list_for_each_entry(subdrv, &exynos_drm_subdrv_list, list) {
+               if (subdrv->close)
+                       subdrv->close(dev, subdrv->manager.dev, file);
+       }
+}
+EXPORT_SYMBOL_GPL(exynos_drm_subdrv_close);
index de818831a51144393a6f237feb5d81077a1776bc..3486ffed0bf05935082965327c11eb3b679a55af 100644 (file)
@@ -249,7 +249,11 @@ exynos_drm_crtc_mode_set(struct drm_crtc *crtc, struct drm_display_mode *mode,
 {
        DRM_DEBUG_KMS("%s\n", __FILE__);
 
-       mode = adjusted_mode;
+       /*
+        * copy the mode data adjusted by mode_fixup() into crtc->mode
+        * so that hardware can be seet to proper mode.
+        */
+       memcpy(&crtc->mode, adjusted_mode, sizeof(*adjusted_mode));
 
        return exynos_drm_crtc_update(crtc);
 }
@@ -426,9 +430,3 @@ void exynos_drm_crtc_disable_vblank(struct drm_device *dev, int crtc)
        exynos_drm_fn_encoder(private->crtc[crtc], &crtc,
                        exynos_drm_disable_vblank);
 }
-
-MODULE_AUTHOR("Inki Dae <inki.dae@samsung.com>");
-MODULE_AUTHOR("Joonyoung Shim <jy0922.shim@samsung.com>");
-MODULE_AUTHOR("Seung-Woo Kim <sw0312.kim@samsung.com>");
-MODULE_DESCRIPTION("Samsung SoC DRM CRTC Driver");
-MODULE_LICENSE("GPL");
index 09cc13f791b3d005eca2f4c2a9189773d9bd0275..a6819b5f8428f4edf6af24420f0ffb784cb24641 100644 (file)
@@ -38,6 +38,7 @@
 #include "exynos_drm_fb.h"
 #include "exynos_drm_gem.h"
 #include "exynos_drm_plane.h"
+#include "exynos_drm_vidi.h"
 
 #define DRIVER_NAME    "exynos"
 #define DRIVER_DESC    "Samsung SoC DRM"
@@ -144,11 +145,34 @@ static int exynos_drm_unload(struct drm_device *dev)
        return 0;
 }
 
+static int exynos_drm_open(struct drm_device *dev, struct drm_file *file)
+{
+       DRM_DEBUG_DRIVER("%s\n", __FILE__);
+
+       return exynos_drm_subdrv_open(dev, file);
+}
+
 static void exynos_drm_preclose(struct drm_device *dev,
                                        struct drm_file *file)
 {
+       struct exynos_drm_private *private = dev->dev_private;
+       struct drm_pending_vblank_event *e, *t;
+       unsigned long flags;
+
        DRM_DEBUG_DRIVER("%s\n", __FILE__);
 
+       /* release events of current file */
+       spin_lock_irqsave(&dev->event_lock, flags);
+       list_for_each_entry_safe(e, t, &private->pageflip_event_list,
+                       base.link) {
+               if (e->base.file_priv == file) {
+                       list_del(&e->base.link);
+                       e->base.destroy(&e->base);
+               }
+       }
+       spin_unlock_irqrestore(&dev->event_lock, flags);
+
+       exynos_drm_subdrv_close(dev, file);
 }
 
 static void exynos_drm_postclose(struct drm_device *dev, struct drm_file *file)
@@ -185,6 +209,8 @@ static struct drm_ioctl_desc exynos_ioctls[] = {
                        exynos_drm_gem_mmap_ioctl, DRM_UNLOCKED | DRM_AUTH),
        DRM_IOCTL_DEF_DRV(EXYNOS_PLANE_SET_ZPOS, exynos_plane_set_zpos_ioctl,
                        DRM_UNLOCKED | DRM_AUTH),
+       DRM_IOCTL_DEF_DRV(EXYNOS_VIDI_CONNECTION,
+                       vidi_connection_ioctl, DRM_UNLOCKED | DRM_AUTH),
 };
 
 static const struct file_operations exynos_drm_driver_fops = {
@@ -202,6 +228,7 @@ static struct drm_driver exynos_drm_driver = {
                                  DRIVER_MODESET | DRIVER_GEM,
        .load                   = exynos_drm_load,
        .unload                 = exynos_drm_unload,
+       .open                   = exynos_drm_open,
        .preclose               = exynos_drm_preclose,
        .lastclose              = exynos_drm_lastclose,
        .postclose              = exynos_drm_postclose,
@@ -252,9 +279,60 @@ static struct platform_driver exynos_drm_platform_driver = {
 
 static int __init exynos_drm_init(void)
 {
+       int ret;
+
        DRM_DEBUG_DRIVER("%s\n", __FILE__);
 
-       return platform_driver_register(&exynos_drm_platform_driver);
+#ifdef CONFIG_DRM_EXYNOS_FIMD
+       ret = platform_driver_register(&fimd_driver);
+       if (ret < 0)
+               goto out_fimd;
+#endif
+
+#ifdef CONFIG_DRM_EXYNOS_HDMI
+       ret = platform_driver_register(&hdmi_driver);
+       if (ret < 0)
+               goto out_hdmi;
+       ret = platform_driver_register(&mixer_driver);
+       if (ret < 0)
+               goto out_mixer;
+       ret = platform_driver_register(&exynos_drm_common_hdmi_driver);
+       if (ret < 0)
+               goto out_common_hdmi;
+#endif
+
+#ifdef CONFIG_DRM_EXYNOS_VIDI
+       ret = platform_driver_register(&vidi_driver);
+       if (ret < 0)
+               goto out_vidi;
+#endif
+
+       ret = platform_driver_register(&exynos_drm_platform_driver);
+       if (ret < 0)
+               goto out;
+
+       return 0;
+
+out:
+#ifdef CONFIG_DRM_EXYNOS_VIDI
+out_vidi:
+       platform_driver_unregister(&vidi_driver);
+#endif
+
+#ifdef CONFIG_DRM_EXYNOS_HDMI
+       platform_driver_unregister(&exynos_drm_common_hdmi_driver);
+out_common_hdmi:
+       platform_driver_unregister(&mixer_driver);
+out_mixer:
+       platform_driver_unregister(&hdmi_driver);
+out_hdmi:
+#endif
+
+#ifdef CONFIG_DRM_EXYNOS_FIMD
+       platform_driver_unregister(&fimd_driver);
+out_fimd:
+#endif
+       return ret;
 }
 
 static void __exit exynos_drm_exit(void)
@@ -262,6 +340,20 @@ static void __exit exynos_drm_exit(void)
        DRM_DEBUG_DRIVER("%s\n", __FILE__);
 
        platform_driver_unregister(&exynos_drm_platform_driver);
+
+#ifdef CONFIG_DRM_EXYNOS_HDMI
+       platform_driver_unregister(&exynos_drm_common_hdmi_driver);
+       platform_driver_unregister(&mixer_driver);
+       platform_driver_unregister(&hdmi_driver);
+#endif
+
+#ifdef CONFIG_DRM_EXYNOS_VIDI
+       platform_driver_unregister(&vidi_driver);
+#endif
+
+#ifdef CONFIG_DRM_EXYNOS_FIMD
+       platform_driver_unregister(&fimd_driver);
+#endif
 }
 
 module_init(exynos_drm_init);
index 13540de90bfc428101afe15c39258ee9623868a1..fbd0a232c93dea173fc4582ebf9aa98c3c620f90 100644 (file)
@@ -32,9 +32,9 @@
 #include <linux/module.h>
 #include "drm.h"
 
-#define MAX_CRTC       2
+#define MAX_CRTC       3
 #define MAX_PLANE      5
-#define MAX_FB_BUFFER  3
+#define MAX_FB_BUFFER  4
 #define DEFAULT_ZPOS   -1
 
 struct drm_device;
@@ -50,6 +50,8 @@ enum exynos_drm_output_type {
        EXYNOS_DISPLAY_TYPE_LCD,
        /* HDMI Interface. */
        EXYNOS_DISPLAY_TYPE_HDMI,
+       /* Virtual Display Interface. */
+       EXYNOS_DISPLAY_TYPE_VIDI,
 };
 
 /*
@@ -155,8 +157,10 @@ struct exynos_drm_display_ops {
  *
  * @dpms: control device power.
  * @apply: set timing, vblank and overlay data to registers.
+ * @mode_fixup: fix mode data comparing to hw specific display mode.
  * @mode_set: convert drm_display_mode to hw specific display mode and
  *           would be called by encoder->mode_set().
+ * @get_max_resol: get maximum resolution to specific hardware.
  * @commit: set current hw specific display mode to hw.
  * @enable_vblank: specific driver callback for enabling vblank interrupt.
  * @disable_vblank: specific driver callback for disabling vblank interrupt.
@@ -164,7 +168,13 @@ struct exynos_drm_display_ops {
 struct exynos_drm_manager_ops {
        void (*dpms)(struct device *subdrv_dev, int mode);
        void (*apply)(struct device *subdrv_dev);
+       void (*mode_fixup)(struct device *subdrv_dev,
+                               struct drm_connector *connector,
+                               struct drm_display_mode *mode,
+                               struct drm_display_mode *adjusted_mode);
        void (*mode_set)(struct device *subdrv_dev, void *mode);
+       void (*get_max_resol)(struct device *subdrv_dev, unsigned int *width,
+                               unsigned int *height);
        void (*commit)(struct device *subdrv_dev);
        int (*enable_vblank)(struct device *subdrv_dev);
        void (*disable_vblank)(struct device *subdrv_dev);
@@ -217,10 +227,13 @@ struct exynos_drm_private {
  * @list: sub driver has its own list object to register to exynos drm driver.
  * @drm_dev: pointer to drm_device and this pointer would be set
  *     when sub driver calls exynos_drm_subdrv_register().
+ * @is_local: appear encoder and connector disrelated device.
  * @probe: this callback would be called by exynos drm driver after
  *     subdrv is registered to it.
  * @remove: this callback is used to release resources created
  *     by probe callback.
+ * @open: this would be called with drm device file open.
+ * @close: this would be called with drm device file close.
  * @manager: subdrv has its own manager to control a hardware appropriately
  *     and we can access a hardware drawing on this manager.
  * @encoder: encoder object owned by this sub driver.
@@ -229,9 +242,14 @@ struct exynos_drm_private {
 struct exynos_drm_subdrv {
        struct list_head list;
        struct drm_device *drm_dev;
+       bool is_local;
 
        int (*probe)(struct drm_device *drm_dev, struct device *dev);
        void (*remove)(struct drm_device *dev);
+       int (*open)(struct drm_device *drm_dev, struct device *dev,
+                       struct drm_file *file);
+       void (*close)(struct drm_device *drm_dev, struct device *dev,
+                       struct drm_file *file);
 
        struct exynos_drm_manager manager;
        struct drm_encoder *encoder;
@@ -254,15 +272,19 @@ int exynos_drm_device_unregister(struct drm_device *dev);
  * this function would be called by sub drivers such as display controller
  * or hdmi driver to register this sub driver object to exynos drm driver
  * and when a sub driver is registered to exynos drm driver a probe callback
- * of the sub driver is called and creates its own encoder and connector
- * and then fb helper and drm mode group would be re-initialized.
+ * of the sub driver is called and creates its own encoder and connector.
  */
 int exynos_drm_subdrv_register(struct exynos_drm_subdrv *drm_subdrv);
 
-/*
- * this function removes subdrv list from exynos drm driver and fb helper
- * and drm mode group would be re-initialized.
- */
+/* this function removes subdrv list from exynos drm driver */
 int exynos_drm_subdrv_unregister(struct exynos_drm_subdrv *drm_subdrv);
 
+int exynos_drm_subdrv_open(struct drm_device *dev, struct drm_file *file);
+void exynos_drm_subdrv_close(struct drm_device *dev, struct drm_file *file);
+
+extern struct platform_driver fimd_driver;
+extern struct platform_driver hdmi_driver;
+extern struct platform_driver mixer_driver;
+extern struct platform_driver exynos_drm_common_hdmi_driver;
+extern struct platform_driver vidi_driver;
 #endif
index ef4754f1519bfbb4334b71c1372cc8d594526bd9..6e9ac7bd1dcf7bb2c2ad2883ea17c53c61459cfd 100644 (file)
@@ -111,9 +111,19 @@ exynos_drm_encoder_mode_fixup(struct drm_encoder *encoder,
                               struct drm_display_mode *mode,
                               struct drm_display_mode *adjusted_mode)
 {
+       struct drm_device *dev = encoder->dev;
+       struct drm_connector *connector;
+       struct exynos_drm_manager *manager = exynos_drm_get_manager(encoder);
+       struct exynos_drm_manager_ops *manager_ops = manager->ops;
+
        DRM_DEBUG_KMS("%s\n", __FILE__);
 
-       /* drm framework doesn't check NULL. */
+       list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
+               if (connector->encoder == encoder)
+                       if (manager_ops && manager_ops->mode_fixup)
+                               manager_ops->mode_fixup(manager->dev, connector,
+                                                       mode, adjusted_mode);
+       }
 
        return true;
 }
@@ -132,12 +142,11 @@ static void exynos_drm_encoder_mode_set(struct drm_encoder *encoder,
 
        DRM_DEBUG_KMS("%s\n", __FILE__);
 
-       mode = adjusted_mode;
-
        list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
                if (connector->encoder == encoder) {
                        if (manager_ops && manager_ops->mode_set)
-                               manager_ops->mode_set(manager->dev, mode);
+                               manager_ops->mode_set(manager->dev,
+                                                       adjusted_mode);
 
                        if (overlay_ops && overlay_ops->mode_set)
                                overlay_ops->mode_set(manager->dev, overlay);
@@ -209,6 +218,7 @@ static unsigned int exynos_drm_encoder_clones(struct drm_encoder *encoder)
                switch (display_ops->type) {
                case EXYNOS_DISPLAY_TYPE_LCD:
                case EXYNOS_DISPLAY_TYPE_HDMI:
+               case EXYNOS_DISPLAY_TYPE_VIDI:
                        clone_mask |= (1 << (cnt++));
                        break;
                default:
@@ -433,9 +443,3 @@ void exynos_drm_encoder_crtc_disable(struct drm_encoder *encoder, void *data)
        if (overlay_ops && overlay_ops->disable)
                overlay_ops->disable(manager->dev, zpos);
 }
-
-MODULE_AUTHOR("Inki Dae <inki.dae@samsung.com>");
-MODULE_AUTHOR("Joonyoung Shim <jy0922.shim@samsung.com>");
-MODULE_AUTHOR("Seung-Woo Kim <sw0312.kim@samsung.com>");
-MODULE_DESCRIPTION("Samsung SoC DRM Encoder Driver");
-MODULE_LICENSE("GPL");
index 3733fe6723d32cee58091135dad7abfd28672843..c38c8f468fa35351d89a1f3309cee0fa358a960c 100644 (file)
@@ -211,9 +211,3 @@ void exynos_drm_mode_config_init(struct drm_device *dev)
 
        dev->mode_config.funcs = &exynos_drm_mode_config_funcs;
 }
-
-MODULE_AUTHOR("Inki Dae <inki.dae@samsung.com>");
-MODULE_AUTHOR("Joonyoung Shim <jy0922.shim@samsung.com>");
-MODULE_AUTHOR("Seung-Woo Kim <sw0312.kim@samsung.com>");
-MODULE_DESCRIPTION("Samsung SoC DRM FB Driver");
-MODULE_LICENSE("GPL");
index 54f8f074822f6fd24bd16948fb2c3fa7210a22a4..d5586cc751635e9ab858d2b3880e39b2c672e1e4 100644 (file)
@@ -125,7 +125,9 @@ static int exynos_drm_fbdev_create(struct drm_fb_helper *helper,
        }
 
        size = mode_cmd.pitches[0] * mode_cmd.height;
-       exynos_gem_obj = exynos_drm_gem_create(dev, size);
+
+       /* 0 means to allocate physically continuous memory */
+       exynos_gem_obj = exynos_drm_gem_create(dev, 0, size);
        if (IS_ERR(exynos_gem_obj)) {
                ret = PTR_ERR(exynos_gem_obj);
                goto out;
@@ -314,89 +316,3 @@ void exynos_drm_fbdev_restore_mode(struct drm_device *dev)
 
        drm_fb_helper_restore_fbdev_mode(private->fb_helper);
 }
-
-int exynos_drm_fbdev_reinit(struct drm_device *dev)
-{
-       struct exynos_drm_private *private = dev->dev_private;
-       struct drm_fb_helper *fb_helper;
-       int ret;
-
-       if (!private)
-               return -EINVAL;
-
-       /*
-        * if all sub drivers were unloaded then num_connector is 0
-        * so at this time, the framebuffers also should be destroyed.
-        */
-       if (!dev->mode_config.num_connector) {
-               exynos_drm_fbdev_fini(dev);
-               return 0;
-       }
-
-       fb_helper = private->fb_helper;
-
-       if (fb_helper) {
-               struct list_head temp_list;
-
-               INIT_LIST_HEAD(&temp_list);
-
-               /*
-                * fb_helper is reintialized but kernel fb is reused
-                * so kernel_fb_list need to be backuped and restored
-                */
-               if (!list_empty(&fb_helper->kernel_fb_list))
-                       list_replace_init(&fb_helper->kernel_fb_list,
-                                       &temp_list);
-
-               drm_fb_helper_fini(fb_helper);
-
-               ret = drm_fb_helper_init(dev, fb_helper,
-                               dev->mode_config.num_crtc, MAX_CONNECTOR);
-               if (ret < 0) {
-                       DRM_ERROR("failed to initialize drm fb helper\n");
-                       return ret;
-               }
-
-               if (!list_empty(&temp_list))
-                       list_replace(&temp_list, &fb_helper->kernel_fb_list);
-
-               ret = drm_fb_helper_single_add_all_connectors(fb_helper);
-               if (ret < 0) {
-                       DRM_ERROR("failed to add fb helper to connectors\n");
-                       goto err;
-               }
-
-               ret = drm_fb_helper_initial_config(fb_helper, PREFERRED_BPP);
-               if (ret < 0) {
-                       DRM_ERROR("failed to set up hw configuration.\n");
-                       goto err;
-               }
-       } else {
-               /*
-                * if drm_load() failed whem drm load() was called prior
-                * to specific drivers, fb_helper must be NULL and so
-                * this fuction should be called again to re-initialize and
-                * re-configure the fb helper. it means that this function
-                * has been called by the specific drivers.
-                */
-               ret = exynos_drm_fbdev_init(dev);
-       }
-
-       return ret;
-
-err:
-       /*
-        * if drm_load() failed when drm load() was called prior
-        * to specific drivers, the fb_helper must be NULL and so check it.
-        */
-       if (fb_helper)
-               drm_fb_helper_fini(fb_helper);
-
-       return ret;
-}
-
-MODULE_AUTHOR("Inki Dae <inki.dae@samsung.com>");
-MODULE_AUTHOR("Joonyoung Shim <jy0922.shim@samsung.com>");
-MODULE_AUTHOR("Seung-Woo Kim <sw0312.kim@samsung.com>");
-MODULE_DESCRIPTION("Samsung SoC DRM FBDEV Driver");
-MODULE_LICENSE("GPL");
index 56458eea05010932c5a96228723c3c8c40321c9b..ecb6db2297008f420ebb7b05e9e19b333bd37286 100644 (file)
@@ -1007,7 +1007,7 @@ static const struct dev_pm_ops fimd_pm_ops = {
        SET_RUNTIME_PM_OPS(fimd_runtime_suspend, fimd_runtime_resume, NULL)
 };
 
-static struct platform_driver fimd_driver = {
+struct platform_driver fimd_driver = {
        .probe          = fimd_probe,
        .remove         = __devexit_p(fimd_remove),
        .driver         = {
@@ -1016,21 +1016,3 @@ static struct platform_driver fimd_driver = {
                .pm     = &fimd_pm_ops,
        },
 };
-
-static int __init fimd_init(void)
-{
-       return platform_driver_register(&fimd_driver);
-}
-
-static void __exit fimd_exit(void)
-{
-       platform_driver_unregister(&fimd_driver);
-}
-
-module_init(fimd_init);
-module_exit(fimd_exit);
-
-MODULE_AUTHOR("Joonyoung Shim <jy0922.shim@samsung.com>");
-MODULE_AUTHOR("Inki Dae <inki.dae@samsung.com>");
-MODULE_DESCRIPTION("Samsung DRM FIMD Driver");
-MODULE_LICENSE("GPL");
index 025abb3e3b67906948c1c68cf684333d915d9d75..fa1aa94a3d8e3066f5abf21fdd02331b1d9b5219 100644 (file)
@@ -26,6 +26,7 @@
 #include "drmP.h"
 #include "drm.h"
 
+#include <linux/shmem_fs.h>
 #include <drm/exynos_drm.h>
 
 #include "exynos_drm_drv.h"
@@ -55,6 +56,178 @@ static unsigned int convert_to_vm_err_msg(int msg)
        return out_msg;
 }
 
+static unsigned int mask_gem_flags(unsigned int flags)
+{
+       return flags &= EXYNOS_BO_NONCONTIG;
+}
+
+static struct page **exynos_gem_get_pages(struct drm_gem_object *obj,
+                                               gfp_t gfpmask)
+{
+       struct inode *inode;
+       struct address_space *mapping;
+       struct page *p, **pages;
+       int i, npages;
+
+       /* This is the shared memory object that backs the GEM resource */
+       inode = obj->filp->f_path.dentry->d_inode;
+       mapping = inode->i_mapping;
+
+       npages = obj->size >> PAGE_SHIFT;
+
+       pages = drm_malloc_ab(npages, sizeof(struct page *));
+       if (pages == NULL)
+               return ERR_PTR(-ENOMEM);
+
+       gfpmask |= mapping_gfp_mask(mapping);
+
+       for (i = 0; i < npages; i++) {
+               p = shmem_read_mapping_page_gfp(mapping, i, gfpmask);
+               if (IS_ERR(p))
+                       goto fail;
+               pages[i] = p;
+       }
+
+       return pages;
+
+fail:
+       while (i--)
+               page_cache_release(pages[i]);
+
+       drm_free_large(pages);
+       return ERR_PTR(PTR_ERR(p));
+}
+
+static void exynos_gem_put_pages(struct drm_gem_object *obj,
+                                       struct page **pages,
+                                       bool dirty, bool accessed)
+{
+       int i, npages;
+
+       npages = obj->size >> PAGE_SHIFT;
+
+       for (i = 0; i < npages; i++) {
+               if (dirty)
+                       set_page_dirty(pages[i]);
+
+               if (accessed)
+                       mark_page_accessed(pages[i]);
+
+               /* Undo the reference we took when populating the table */
+               page_cache_release(pages[i]);
+       }
+
+       drm_free_large(pages);
+}
+
+static int exynos_drm_gem_map_pages(struct drm_gem_object *obj,
+                                       struct vm_area_struct *vma,
+                                       unsigned long f_vaddr,
+                                       pgoff_t page_offset)
+{
+       struct exynos_drm_gem_obj *exynos_gem_obj = to_exynos_gem_obj(obj);
+       struct exynos_drm_gem_buf *buf = exynos_gem_obj->buffer;
+       unsigned long pfn;
+
+       if (exynos_gem_obj->flags & EXYNOS_BO_NONCONTIG) {
+               unsigned long usize = buf->size;
+
+               if (!buf->pages)
+                       return -EINTR;
+
+               while (usize > 0) {
+                       pfn = page_to_pfn(buf->pages[page_offset++]);
+                       vm_insert_mixed(vma, f_vaddr, pfn);
+                       f_vaddr += PAGE_SIZE;
+                       usize -= PAGE_SIZE;
+               }
+
+               return 0;
+       }
+
+       pfn = (buf->dma_addr >> PAGE_SHIFT) + page_offset;
+
+       return vm_insert_mixed(vma, f_vaddr, pfn);
+}
+
+static int exynos_drm_gem_get_pages(struct drm_gem_object *obj)
+{
+       struct exynos_drm_gem_obj *exynos_gem_obj = to_exynos_gem_obj(obj);
+       struct exynos_drm_gem_buf *buf = exynos_gem_obj->buffer;
+       struct scatterlist *sgl;
+       struct page **pages;
+       unsigned int npages, i = 0;
+       int ret;
+
+       if (buf->pages) {
+               DRM_DEBUG_KMS("already allocated.\n");
+               return -EINVAL;
+       }
+
+       pages = exynos_gem_get_pages(obj, GFP_KERNEL);
+       if (IS_ERR(pages)) {
+               DRM_ERROR("failed to get pages.\n");
+               return PTR_ERR(pages);
+       }
+
+       npages = obj->size >> PAGE_SHIFT;
+
+       buf->sgt = kzalloc(sizeof(struct sg_table), GFP_KERNEL);
+       if (!buf->sgt) {
+               DRM_ERROR("failed to allocate sg table.\n");
+               ret = -ENOMEM;
+               goto err;
+       }
+
+       ret = sg_alloc_table(buf->sgt, npages, GFP_KERNEL);
+       if (ret < 0) {
+               DRM_ERROR("failed to initialize sg table.\n");
+               ret = -EFAULT;
+               goto err1;
+       }
+
+       sgl = buf->sgt->sgl;
+
+       /* set all pages to sg list. */
+       while (i < npages) {
+               sg_set_page(sgl, pages[i], PAGE_SIZE, 0);
+               sg_dma_address(sgl) = page_to_phys(pages[i]);
+               i++;
+               sgl = sg_next(sgl);
+       }
+
+       /* add some codes for UNCACHED type here. TODO */
+
+       buf->pages = pages;
+       return ret;
+err1:
+       kfree(buf->sgt);
+       buf->sgt = NULL;
+err:
+       exynos_gem_put_pages(obj, pages, true, false);
+       return ret;
+
+}
+
+static void exynos_drm_gem_put_pages(struct drm_gem_object *obj)
+{
+       struct exynos_drm_gem_obj *exynos_gem_obj = to_exynos_gem_obj(obj);
+       struct exynos_drm_gem_buf *buf = exynos_gem_obj->buffer;
+
+       /*
+        * if buffer typs is EXYNOS_BO_NONCONTIG then release all pages
+        * allocated at gem fault handler.
+        */
+       sg_free_table(buf->sgt);
+       kfree(buf->sgt);
+       buf->sgt = NULL;
+
+       exynos_gem_put_pages(obj, buf->pages, true, false);
+       buf->pages = NULL;
+
+       /* add some codes for UNCACHED type here. TODO */
+}
+
 static int exynos_drm_gem_handle_create(struct drm_gem_object *obj,
                                        struct drm_file *file_priv,
                                        unsigned int *handle)
@@ -90,7 +263,15 @@ void exynos_drm_gem_destroy(struct exynos_drm_gem_obj *exynos_gem_obj)
 
        DRM_DEBUG_KMS("handle count = %d\n", atomic_read(&obj->handle_count));
 
-       exynos_drm_buf_destroy(obj->dev, exynos_gem_obj->buffer);
+       if ((exynos_gem_obj->flags & EXYNOS_BO_NONCONTIG) &&
+                       exynos_gem_obj->buffer->pages)
+               exynos_drm_gem_put_pages(obj);
+       else
+               exynos_drm_free_buf(obj->dev, exynos_gem_obj->flags,
+                                       exynos_gem_obj->buffer);
+
+       exynos_drm_fini_buf(obj->dev, exynos_gem_obj->buffer);
+       exynos_gem_obj->buffer = NULL;
 
        if (obj->map_list.map)
                drm_gem_free_mmap_offset(obj);
@@ -99,6 +280,7 @@ void exynos_drm_gem_destroy(struct exynos_drm_gem_obj *exynos_gem_obj)
        drm_gem_object_release(obj);
 
        kfree(exynos_gem_obj);
+       exynos_gem_obj = NULL;
 }
 
 static struct exynos_drm_gem_obj *exynos_drm_gem_init(struct drm_device *dev,
@@ -114,6 +296,7 @@ static struct exynos_drm_gem_obj *exynos_drm_gem_init(struct drm_device *dev,
                return NULL;
        }
 
+       exynos_gem_obj->size = size;
        obj = &exynos_gem_obj->base;
 
        ret = drm_gem_object_init(dev, obj, size);
@@ -129,27 +312,55 @@ static struct exynos_drm_gem_obj *exynos_drm_gem_init(struct drm_device *dev,
 }
 
 struct exynos_drm_gem_obj *exynos_drm_gem_create(struct drm_device *dev,
-                                                unsigned long size)
+                                               unsigned int flags,
+                                               unsigned long size)
 {
-       struct exynos_drm_gem_buf *buffer;
        struct exynos_drm_gem_obj *exynos_gem_obj;
+       struct exynos_drm_gem_buf *buf;
+       int ret;
 
        size = roundup(size, PAGE_SIZE);
        DRM_DEBUG_KMS("%s: size = 0x%lx\n", __FILE__, size);
 
-       buffer = exynos_drm_buf_create(dev, size);
-       if (!buffer)
+       flags = mask_gem_flags(flags);
+
+       buf = exynos_drm_init_buf(dev, size);
+       if (!buf)
                return ERR_PTR(-ENOMEM);
 
        exynos_gem_obj = exynos_drm_gem_init(dev, size);
        if (!exynos_gem_obj) {
-               exynos_drm_buf_destroy(dev, buffer);
-               return ERR_PTR(-ENOMEM);
+               ret = -ENOMEM;
+               goto err;
        }
 
-       exynos_gem_obj->buffer = buffer;
+       exynos_gem_obj->buffer = buf;
+
+       /* set memory type and cache attribute from user side. */
+       exynos_gem_obj->flags = flags;
+
+       /*
+        * allocate all pages as desired size if user wants to allocate
+        * physically non-continuous memory.
+        */
+       if (flags & EXYNOS_BO_NONCONTIG) {
+               ret = exynos_drm_gem_get_pages(&exynos_gem_obj->base);
+               if (ret < 0) {
+                       drm_gem_object_release(&exynos_gem_obj->base);
+                       goto err;
+               }
+       } else {
+               ret = exynos_drm_alloc_buf(dev, buf, flags);
+               if (ret < 0) {
+                       drm_gem_object_release(&exynos_gem_obj->base);
+                       goto err;
+               }
+       }
 
        return exynos_gem_obj;
+err:
+       exynos_drm_fini_buf(dev, buf);
+       return ERR_PTR(ret);
 }
 
 int exynos_drm_gem_create_ioctl(struct drm_device *dev, void *data,
@@ -161,7 +372,7 @@ int exynos_drm_gem_create_ioctl(struct drm_device *dev, void *data,
 
        DRM_DEBUG_KMS("%s\n", __FILE__);
 
-       exynos_gem_obj = exynos_drm_gem_create(dev, args->size);
+       exynos_gem_obj = exynos_drm_gem_create(dev, args->flags, args->size);
        if (IS_ERR(exynos_gem_obj))
                return PTR_ERR(exynos_gem_obj);
 
@@ -175,6 +386,64 @@ int exynos_drm_gem_create_ioctl(struct drm_device *dev, void *data,
        return 0;
 }
 
+void *exynos_drm_gem_get_dma_addr(struct drm_device *dev,
+                                       unsigned int gem_handle,
+                                       struct drm_file *file_priv)
+{
+       struct exynos_drm_gem_obj *exynos_gem_obj;
+       struct drm_gem_object *obj;
+
+       obj = drm_gem_object_lookup(dev, file_priv, gem_handle);
+       if (!obj) {
+               DRM_ERROR("failed to lookup gem object.\n");
+               return ERR_PTR(-EINVAL);
+       }
+
+       exynos_gem_obj = to_exynos_gem_obj(obj);
+
+       if (exynos_gem_obj->flags & EXYNOS_BO_NONCONTIG) {
+               DRM_DEBUG_KMS("not support NONCONTIG type.\n");
+               drm_gem_object_unreference_unlocked(obj);
+
+               /* TODO */
+               return ERR_PTR(-EINVAL);
+       }
+
+       return &exynos_gem_obj->buffer->dma_addr;
+}
+
+void exynos_drm_gem_put_dma_addr(struct drm_device *dev,
+                                       unsigned int gem_handle,
+                                       struct drm_file *file_priv)
+{
+       struct exynos_drm_gem_obj *exynos_gem_obj;
+       struct drm_gem_object *obj;
+
+       obj = drm_gem_object_lookup(dev, file_priv, gem_handle);
+       if (!obj) {
+               DRM_ERROR("failed to lookup gem object.\n");
+               return;
+       }
+
+       exynos_gem_obj = to_exynos_gem_obj(obj);
+
+       if (exynos_gem_obj->flags & EXYNOS_BO_NONCONTIG) {
+               DRM_DEBUG_KMS("not support NONCONTIG type.\n");
+               drm_gem_object_unreference_unlocked(obj);
+
+               /* TODO */
+               return;
+       }
+
+       drm_gem_object_unreference_unlocked(obj);
+
+       /*
+        * decrease obj->refcount one more time because we has already
+        * increased it at exynos_drm_gem_get_dma_addr().
+        */
+       drm_gem_object_unreference_unlocked(obj);
+}
+
 int exynos_drm_gem_map_offset_ioctl(struct drm_device *dev, void *data,
                                    struct drm_file *file_priv)
 {
@@ -200,7 +469,8 @@ static int exynos_drm_gem_mmap_buffer(struct file *filp,
        struct drm_gem_object *obj = filp->private_data;
        struct exynos_drm_gem_obj *exynos_gem_obj = to_exynos_gem_obj(obj);
        struct exynos_drm_gem_buf *buffer;
-       unsigned long pfn, vm_size;
+       unsigned long pfn, vm_size, usize, uaddr = vma->vm_start;
+       int ret;
 
        DRM_DEBUG_KMS("%s\n", __FILE__);
 
@@ -208,9 +478,9 @@ static int exynos_drm_gem_mmap_buffer(struct file *filp,
 
        /* in case of direct mapping, always having non-cachable attribute */
        vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
-       vma->vm_file = filp;
 
-       vm_size = vma->vm_end - vma->vm_start;
+       vm_size = usize = vma->vm_end - vma->vm_start;
+
        /*
         * a buffer contains information to physically continuous memory
         * allocated by user request or at framebuffer creation.
@@ -221,18 +491,37 @@ static int exynos_drm_gem_mmap_buffer(struct file *filp,
        if (vm_size > buffer->size)
                return -EINVAL;
 
-       /*
-        * get page frame number to physical memory to be mapped
-        * to user space.
-        */
-       pfn = ((unsigned long)exynos_gem_obj->buffer->dma_addr) >> PAGE_SHIFT;
-
-       DRM_DEBUG_KMS("pfn = 0x%lx\n", pfn);
-
-       if (remap_pfn_range(vma, vma->vm_start, pfn, vm_size,
-                               vma->vm_page_prot)) {
-               DRM_ERROR("failed to remap pfn range.\n");
-               return -EAGAIN;
+       if (exynos_gem_obj->flags & EXYNOS_BO_NONCONTIG) {
+               int i = 0;
+
+               if (!buffer->pages)
+                       return -EINVAL;
+
+               do {
+                       ret = vm_insert_page(vma, uaddr, buffer->pages[i++]);
+                       if (ret) {
+                               DRM_ERROR("failed to remap user space.\n");
+                               return ret;
+                       }
+
+                       uaddr += PAGE_SIZE;
+                       usize -= PAGE_SIZE;
+               } while (usize > 0);
+       } else {
+               /*
+                * get page frame number to physical memory to be mapped
+                * to user space.
+                */
+               pfn = ((unsigned long)exynos_gem_obj->buffer->dma_addr) >>
+                                                               PAGE_SHIFT;
+
+               DRM_DEBUG_KMS("pfn = 0x%lx\n", pfn);
+
+               if (remap_pfn_range(vma, vma->vm_start, pfn, vm_size,
+                                       vma->vm_page_prot)) {
+                       DRM_ERROR("failed to remap pfn range.\n");
+                       return -EAGAIN;
+               }
        }
 
        return 0;
@@ -312,9 +601,9 @@ int exynos_drm_gem_dumb_create(struct drm_file *file_priv,
         */
 
        args->pitch = args->width * args->bpp >> 3;
-       args->size = args->pitch * args->height;
+       args->size = PAGE_ALIGN(args->pitch * args->height);
 
-       exynos_gem_obj = exynos_drm_gem_create(dev, args->size);
+       exynos_gem_obj = exynos_drm_gem_create(dev, args->flags, args->size);
        if (IS_ERR(exynos_gem_obj))
                return PTR_ERR(exynos_gem_obj);
 
@@ -398,20 +687,31 @@ int exynos_drm_gem_fault(struct vm_area_struct *vma, struct vm_fault *vmf)
        struct drm_gem_object *obj = vma->vm_private_data;
        struct exynos_drm_gem_obj *exynos_gem_obj = to_exynos_gem_obj(obj);
        struct drm_device *dev = obj->dev;
-       unsigned long pfn;
+       unsigned long f_vaddr;
        pgoff_t page_offset;
        int ret;
 
        page_offset = ((unsigned long)vmf->virtual_address -
                        vma->vm_start) >> PAGE_SHIFT;
+       f_vaddr = (unsigned long)vmf->virtual_address;
 
        mutex_lock(&dev->struct_mutex);
 
-       pfn = (((unsigned long)exynos_gem_obj->buffer->dma_addr) >>
-                       PAGE_SHIFT) + page_offset;
+       /*
+        * allocate all pages as desired size if user wants to allocate
+        * physically non-continuous memory.
+        */
+       if (exynos_gem_obj->flags & EXYNOS_BO_NONCONTIG) {
+               ret = exynos_drm_gem_get_pages(obj);
+               if (ret < 0)
+                       goto err;
+       }
 
-       ret = vm_insert_mixed(vma, (unsigned long)vmf->virtual_address, pfn);
+       ret = exynos_drm_gem_map_pages(obj, vma, f_vaddr, page_offset);
+       if (ret < 0)
+               DRM_ERROR("failed to map pages.\n");
 
+err:
        mutex_unlock(&dev->struct_mutex);
 
        return convert_to_vm_err_msg(ret);
@@ -435,7 +735,3 @@ int exynos_drm_gem_mmap(struct file *filp, struct vm_area_struct *vma)
 
        return ret;
 }
-
-MODULE_AUTHOR("Inki Dae <inki.dae@samsung.com>");
-MODULE_DESCRIPTION("Samsung SoC DRM GEM Module");
-MODULE_LICENSE("GPL");
index 67cdc9168708b30eac9bd1af01f9cb552b23d46f..e40fbad8b70552abf83d2cf86ccd2cd4e10a5630 100644 (file)
  * @dma_addr: bus address(accessed by dma) to allocated memory region.
  *     - this address could be physical address without IOMMU and
  *     device address with IOMMU.
+ * @sgt: sg table to transfer page data.
+ * @pages: contain all pages to allocated memory region.
  * @size: size of allocated memory region.
  */
 struct exynos_drm_gem_buf {
        void __iomem            *kvaddr;
        dma_addr_t              dma_addr;
+       struct sg_table         *sgt;
+       struct page             **pages;
        unsigned long           size;
 };
 
@@ -55,6 +59,8 @@ struct exynos_drm_gem_buf {
  *     by user request or at framebuffer creation.
  *     continuous memory region allocated by user request
  *     or at framebuffer creation.
+ * @size: total memory size to physically non-continuous memory region.
+ * @flags: indicate memory type to allocated buffer and cache attruibute.
  *
  * P.S. this object would be transfered to user as kms_bo.handle so
  *     user can access the buffer through kms_bo.handle.
@@ -62,6 +68,8 @@ struct exynos_drm_gem_buf {
 struct exynos_drm_gem_obj {
        struct drm_gem_object           base;
        struct exynos_drm_gem_buf       *buffer;
+       unsigned long                   size;
+       unsigned int                    flags;
 };
 
 /* destroy a buffer with gem object */
@@ -69,7 +77,8 @@ void exynos_drm_gem_destroy(struct exynos_drm_gem_obj *exynos_gem_obj);
 
 /* create a new buffer with gem object */
 struct exynos_drm_gem_obj *exynos_drm_gem_create(struct drm_device *dev,
-                                                unsigned long size);
+                                               unsigned int flags,
+                                               unsigned long size);
 
 /*
  * request gem object creation and buffer allocation as the size
@@ -79,6 +88,24 @@ struct exynos_drm_gem_obj *exynos_drm_gem_create(struct drm_device *dev,
 int exynos_drm_gem_create_ioctl(struct drm_device *dev, void *data,
                                struct drm_file *file_priv);
 
+/*
+ * get dma address from gem handle and this function could be used for
+ * other drivers such as 2d/3d acceleration drivers.
+ * with this function call, gem object reference count would be increased.
+ */
+void *exynos_drm_gem_get_dma_addr(struct drm_device *dev,
+                                       unsigned int gem_handle,
+                                       struct drm_file *file_priv);
+
+/*
+ * put dma address from gem handle and this function could be used for
+ * other drivers such as 2d/3d acceleration drivers.
+ * with this function call, gem object reference count would be decreased.
+ */
+void exynos_drm_gem_put_dma_addr(struct drm_device *dev,
+                                       unsigned int gem_handle,
+                                       struct drm_file *file_priv);
+
 /* get buffer offset to map to user space. */
 int exynos_drm_gem_map_offset_ioctl(struct drm_device *dev, void *data,
                                    struct drm_file *file_priv);
index ed8a319ed84b63d7c34556c32128dc5974488285..14eb26b0ba1cb1fc70fb533be2adb38fd935ac71 100644 (file)
@@ -38,7 +38,6 @@ struct drm_hdmi_context {
        struct exynos_drm_subdrv        subdrv;
        struct exynos_drm_hdmi_context  *hdmi_ctx;
        struct exynos_drm_hdmi_context  *mixer_ctx;
-       struct work_struct              work;
 };
 
 void exynos_drm_display_ops_register(struct exynos_hdmi_display_ops
@@ -49,7 +48,6 @@ void exynos_drm_display_ops_register(struct exynos_hdmi_display_ops
        if (display_ops)
                hdmi_display_ops = display_ops;
 }
-EXPORT_SYMBOL(exynos_drm_display_ops_register);
 
 void exynos_drm_manager_ops_register(struct exynos_hdmi_manager_ops
                                        *manager_ops)
@@ -59,7 +57,6 @@ void exynos_drm_manager_ops_register(struct exynos_hdmi_manager_ops
        if (manager_ops)
                hdmi_manager_ops = manager_ops;
 }
-EXPORT_SYMBOL(exynos_drm_manager_ops_register);
 
 void exynos_drm_overlay_ops_register(struct exynos_hdmi_overlay_ops
                                        *overlay_ops)
@@ -69,7 +66,6 @@ void exynos_drm_overlay_ops_register(struct exynos_hdmi_overlay_ops
        if (overlay_ops)
                hdmi_overlay_ops = overlay_ops;
 }
-EXPORT_SYMBOL(exynos_drm_overlay_ops_register);
 
 static bool drm_hdmi_is_connected(struct device *dev)
 {
@@ -155,6 +151,20 @@ static void drm_hdmi_disable_vblank(struct device *subdrv_dev)
                return hdmi_overlay_ops->disable_vblank(ctx->mixer_ctx->ctx);
 }
 
+static void drm_hdmi_mode_fixup(struct device *subdrv_dev,
+                               struct drm_connector *connector,
+                               struct drm_display_mode *mode,
+                               struct drm_display_mode *adjusted_mode)
+{
+       struct drm_hdmi_context *ctx = to_context(subdrv_dev);
+
+       DRM_DEBUG_KMS("%s\n", __FILE__);
+
+       if (hdmi_manager_ops && hdmi_manager_ops->mode_fixup)
+               hdmi_manager_ops->mode_fixup(ctx->hdmi_ctx->ctx, connector,
+                                               mode, adjusted_mode);
+}
+
 static void drm_hdmi_mode_set(struct device *subdrv_dev, void *mode)
 {
        struct drm_hdmi_context *ctx = to_context(subdrv_dev);
@@ -165,6 +175,18 @@ static void drm_hdmi_mode_set(struct device *subdrv_dev, void *mode)
                hdmi_manager_ops->mode_set(ctx->hdmi_ctx->ctx, mode);
 }
 
+static void drm_hdmi_get_max_resol(struct device *subdrv_dev,
+                               unsigned int *width, unsigned int *height)
+{
+       struct drm_hdmi_context *ctx = to_context(subdrv_dev);
+
+       DRM_DEBUG_KMS("%s\n", __FILE__);
+
+       if (hdmi_manager_ops && hdmi_manager_ops->get_max_resol)
+               hdmi_manager_ops->get_max_resol(ctx->hdmi_ctx->ctx, width,
+                                                       height);
+}
+
 static void drm_hdmi_commit(struct device *subdrv_dev)
 {
        struct drm_hdmi_context *ctx = to_context(subdrv_dev);
@@ -200,7 +222,9 @@ static struct exynos_drm_manager_ops drm_hdmi_manager_ops = {
        .dpms = drm_hdmi_dpms,
        .enable_vblank = drm_hdmi_enable_vblank,
        .disable_vblank = drm_hdmi_disable_vblank,
+       .mode_fixup = drm_hdmi_mode_fixup,
        .mode_set = drm_hdmi_mode_set,
+       .get_max_resol = drm_hdmi_get_max_resol,
        .commit = drm_hdmi_commit,
 };
 
@@ -249,7 +273,6 @@ static int hdmi_subdrv_probe(struct drm_device *drm_dev,
        struct drm_hdmi_context *ctx;
        struct platform_device *pdev = to_platform_device(dev);
        struct exynos_drm_common_hdmi_pd *pd;
-       int ret;
 
        DRM_DEBUG_KMS("%s\n", __FILE__);
 
@@ -270,26 +293,13 @@ static int hdmi_subdrv_probe(struct drm_device *drm_dev,
                return -EFAULT;
        }
 
-       ret = platform_driver_register(&hdmi_driver);
-       if (ret) {
-               DRM_DEBUG_KMS("failed to register hdmi driver.\n");
-               return ret;
-       }
-
-       ret = platform_driver_register(&mixer_driver);
-       if (ret) {
-               DRM_DEBUG_KMS("failed to register mixer driver.\n");
-               goto err_hdmidrv;
-       }
-
        ctx = get_ctx_from_subdrv(subdrv);
 
        ctx->hdmi_ctx = (struct exynos_drm_hdmi_context *)
                                to_context(pd->hdmi_dev);
        if (!ctx->hdmi_ctx) {
                DRM_DEBUG_KMS("hdmi context is null.\n");
-               ret = -EFAULT;
-               goto err_mixerdrv;
+               return -EFAULT;
        }
 
        ctx->hdmi_ctx->drm_dev = drm_dev;
@@ -298,42 +308,12 @@ static int hdmi_subdrv_probe(struct drm_device *drm_dev,
                                to_context(pd->mixer_dev);
        if (!ctx->mixer_ctx) {
                DRM_DEBUG_KMS("mixer context is null.\n");
-               ret = -EFAULT;
-               goto err_mixerdrv;
+               return -EFAULT;
        }
 
        ctx->mixer_ctx->drm_dev = drm_dev;
 
        return 0;
-
-err_mixerdrv:
-       platform_driver_unregister(&mixer_driver);
-err_hdmidrv:
-       platform_driver_unregister(&hdmi_driver);
-       return ret;
-}
-
-static void hdmi_subdrv_remove(struct drm_device *drm_dev)
-{
-       DRM_DEBUG_KMS("%s\n", __FILE__);
-
-       platform_driver_unregister(&hdmi_driver);
-       platform_driver_unregister(&mixer_driver);
-}
-
-static void exynos_drm_hdmi_late_probe(struct work_struct *work)
-{
-       struct drm_hdmi_context *ctx = container_of(work,
-                               struct drm_hdmi_context, work);
-
-       /*
-        * this function calls subdrv->probe() so this must be called
-        * after probe context.
-        *
-        * PS. subdrv->probe() will call platform_driver_register() to probe
-        * hdmi and mixer driver.
-        */
-       exynos_drm_subdrv_register(&ctx->subdrv);
 }
 
 static int __devinit exynos_drm_hdmi_probe(struct platform_device *pdev)
@@ -353,7 +333,6 @@ static int __devinit exynos_drm_hdmi_probe(struct platform_device *pdev)
        subdrv = &ctx->subdrv;
 
        subdrv->probe = hdmi_subdrv_probe;
-       subdrv->remove = hdmi_subdrv_remove;
        subdrv->manager.pipe = -1;
        subdrv->manager.ops = &drm_hdmi_manager_ops;
        subdrv->manager.overlay_ops = &drm_hdmi_overlay_ops;
@@ -362,9 +341,7 @@ static int __devinit exynos_drm_hdmi_probe(struct platform_device *pdev)
 
        platform_set_drvdata(pdev, subdrv);
 
-       INIT_WORK(&ctx->work, exynos_drm_hdmi_late_probe);
-
-       schedule_work(&ctx->work);
+       exynos_drm_subdrv_register(subdrv);
 
        return 0;
 }
@@ -400,7 +377,7 @@ static int __devexit exynos_drm_hdmi_remove(struct platform_device *pdev)
        return 0;
 }
 
-static struct platform_driver exynos_drm_common_hdmi_driver = {
+struct platform_driver exynos_drm_common_hdmi_driver = {
        .probe          = exynos_drm_hdmi_probe,
        .remove         = __devexit_p(exynos_drm_hdmi_remove),
        .driver         = {
@@ -409,31 +386,3 @@ static struct platform_driver exynos_drm_common_hdmi_driver = {
                .pm = &hdmi_pm_ops,
        },
 };
-
-static int __init exynos_drm_hdmi_init(void)
-{
-       int ret;
-
-       DRM_DEBUG_KMS("%s\n", __FILE__);
-
-       ret = platform_driver_register(&exynos_drm_common_hdmi_driver);
-       if (ret) {
-               DRM_DEBUG_KMS("failed to register hdmi common driver.\n");
-               return ret;
-       }
-
-       return ret;
-}
-
-static void __exit exynos_drm_hdmi_exit(void)
-{
-       platform_driver_unregister(&exynos_drm_common_hdmi_driver);
-}
-
-module_init(exynos_drm_hdmi_init);
-module_exit(exynos_drm_hdmi_exit);
-
-MODULE_AUTHOR("Inki Dae <inki.dae@samsung.com>");
-MODULE_AUTHOR("Seung-Woo Kim, <sw0312.kim@samsung.com>");
-MODULE_DESCRIPTION("Samsung SoC DRM HDMI Driver");
-MODULE_LICENSE("GPL");
index 3c29f790ee451da566f3f07e9c59ba35c015bad8..44497cfb6c74174ebb6faab6f5f0729ad712c43c 100644 (file)
@@ -47,7 +47,12 @@ struct exynos_hdmi_display_ops {
 };
 
 struct exynos_hdmi_manager_ops {
+       void (*mode_fixup)(void *ctx, struct drm_connector *connector,
+                               struct drm_display_mode *mode,
+                               struct drm_display_mode *adjusted_mode);
        void (*mode_set)(void *ctx, void *mode);
+       void (*get_max_resol)(void *ctx, unsigned int *width,
+                               unsigned int *height);
        void (*commit)(void *ctx);
        void (*disable)(void *ctx);
 };
index bdcf770aa22ef18fe6af51ec63411b2d7c1220bb..c277a3a445f5a206209dc898ded6f15500b084a7 100644 (file)
@@ -22,6 +22,10 @@ struct exynos_plane {
        bool                            enabled;
 };
 
+static const uint32_t formats[] = {
+       DRM_FORMAT_XRGB8888,
+};
+
 static int
 exynos_update_plane(struct drm_plane *plane, struct drm_crtc *crtc,
                     struct drm_framebuffer *fb, int crtc_x, int crtc_y,
@@ -115,9 +119,9 @@ int exynos_plane_init(struct drm_device *dev, unsigned int nr)
 
        exynos_plane->overlay.zpos = DEFAULT_ZPOS;
 
-       /* TODO: format */
        return drm_plane_init(dev, &exynos_plane->base, possible_crtcs,
-                             &exynos_plane_funcs, NULL, 0, false);
+                             &exynos_plane_funcs, formats, ARRAY_SIZE(formats),
+                             false);
 }
 
 int exynos_plane_set_zpos_ioctl(struct drm_device *dev, void *data,
diff --git a/drivers/gpu/drm/exynos/exynos_drm_vidi.c b/drivers/gpu/drm/exynos/exynos_drm_vidi.c
new file mode 100644 (file)
index 0000000..8e1339f
--- /dev/null
@@ -0,0 +1,676 @@
+/* exynos_drm_vidi.c
+ *
+ * Copyright (C) 2012 Samsung Electronics Co.Ltd
+ * Authors:
+ *     Inki Dae <inki.dae@samsung.com>
+ *
+ * This program is free software; you can redistribute  it and/or modify it
+ * under  the terms of  the GNU General  Public License as published by the
+ * Free Software Foundation;  either version 2 of the  License, or (at your
+ * option) any later version.
+ *
+ */
+#include "drmP.h"
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+
+#include <drm/exynos_drm.h>
+
+#include "drm_edid.h"
+#include "drm_crtc_helper.h"
+
+#include "exynos_drm_drv.h"
+#include "exynos_drm_crtc.h"
+#include "exynos_drm_encoder.h"
+
+/* vidi has totally three virtual windows. */
+#define WINDOWS_NR             3
+
+#define get_vidi_context(dev)  platform_get_drvdata(to_platform_device(dev))
+
+struct vidi_win_data {
+       unsigned int            offset_x;
+       unsigned int            offset_y;
+       unsigned int            ovl_width;
+       unsigned int            ovl_height;
+       unsigned int            fb_width;
+       unsigned int            fb_height;
+       unsigned int            bpp;
+       dma_addr_t              dma_addr;
+       void __iomem            *vaddr;
+       unsigned int            buf_offsize;
+       unsigned int            line_size;      /* bytes */
+       bool                    enabled;
+};
+
+struct vidi_context {
+       struct exynos_drm_subdrv        subdrv;
+       struct drm_crtc                 *crtc;
+       struct vidi_win_data            win_data[WINDOWS_NR];
+       struct edid                     *raw_edid;
+       unsigned int                    clkdiv;
+       unsigned int                    default_win;
+       unsigned long                   irq_flags;
+       unsigned int                    connected;
+       bool                            vblank_on;
+       bool                            suspended;
+       struct work_struct              work;
+       struct mutex                    lock;
+};
+
+static const char fake_edid_info[] = {
+       0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x4c, 0x2d, 0x05, 0x05,
+       0x00, 0x00, 0x00, 0x00, 0x30, 0x12, 0x01, 0x03, 0x80, 0x10, 0x09, 0x78,
+       0x0a, 0xee, 0x91, 0xa3, 0x54, 0x4c, 0x99, 0x26, 0x0f, 0x50, 0x54, 0xbd,
+       0xee, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+       0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x66, 0x21, 0x50, 0xb0, 0x51, 0x00,
+       0x1b, 0x30, 0x40, 0x70, 0x36, 0x00, 0xa0, 0x5a, 0x00, 0x00, 0x00, 0x1e,
+       0x01, 0x1d, 0x00, 0x72, 0x51, 0xd0, 0x1e, 0x20, 0x6e, 0x28, 0x55, 0x00,
+       0xa0, 0x5a, 0x00, 0x00, 0x00, 0x1e, 0x00, 0x00, 0x00, 0xfd, 0x00, 0x18,
+       0x4b, 0x1a, 0x44, 0x17, 0x00, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+       0x00, 0x00, 0x00, 0xfc, 0x00, 0x53, 0x41, 0x4d, 0x53, 0x55, 0x4e, 0x47,
+       0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x01, 0xbc, 0x02, 0x03, 0x1e, 0xf1,
+       0x46, 0x84, 0x05, 0x03, 0x10, 0x20, 0x22, 0x23, 0x09, 0x07, 0x07, 0x83,
+       0x01, 0x00, 0x00, 0xe2, 0x00, 0x0f, 0x67, 0x03, 0x0c, 0x00, 0x10, 0x00,
+       0xb8, 0x2d, 0x01, 0x1d, 0x80, 0x18, 0x71, 0x1c, 0x16, 0x20, 0x58, 0x2c,
+       0x25, 0x00, 0xa0, 0x5a, 0x00, 0x00, 0x00, 0x9e, 0x8c, 0x0a, 0xd0, 0x8a,
+       0x20, 0xe0, 0x2d, 0x10, 0x10, 0x3e, 0x96, 0x00, 0xa0, 0x5a, 0x00, 0x00,
+       0x00, 0x18, 0x02, 0x3a, 0x80, 0x18, 0x71, 0x38, 0x2d, 0x40, 0x58, 0x2c,
+       0x45, 0x00, 0xa0, 0x5a, 0x00, 0x00, 0x00, 0x1e, 0x00, 0x00, 0x00, 0x00,
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+       0x00, 0x00, 0x00, 0x06
+};
+
+static void vidi_fake_vblank_handler(struct work_struct *work);
+
+static bool vidi_display_is_connected(struct device *dev)
+{
+       struct vidi_context *ctx = get_vidi_context(dev);
+
+       DRM_DEBUG_KMS("%s\n", __FILE__);
+
+       /*
+        * connection request would come from user side
+        * to do hotplug through specific ioctl.
+        */
+       return ctx->connected ? true : false;
+}
+
+static int vidi_get_edid(struct device *dev, struct drm_connector *connector,
+                               u8 *edid, int len)
+{
+       struct vidi_context *ctx = get_vidi_context(dev);
+       struct edid *raw_edid;
+
+       DRM_DEBUG_KMS("%s\n", __FILE__);
+
+       /*
+        * the edid data comes from user side and it would be set
+        * to ctx->raw_edid through specific ioctl.
+        */
+       if (!ctx->raw_edid) {
+               DRM_DEBUG_KMS("raw_edid is null.\n");
+               return -EFAULT;
+       }
+
+       raw_edid = kzalloc(len, GFP_KERNEL);
+       if (!raw_edid) {
+               DRM_DEBUG_KMS("failed to allocate raw_edid.\n");
+               return -ENOMEM;
+       }
+
+       memcpy(raw_edid, ctx->raw_edid, min((1 + ctx->raw_edid->extensions)
+                                               * EDID_LENGTH, len));
+
+       /* attach the edid data to connector. */
+       connector->display_info.raw_edid = (char *)raw_edid;
+
+       memcpy(edid, ctx->raw_edid, min((1 + ctx->raw_edid->extensions)
+                                       * EDID_LENGTH, len));
+
+       return 0;
+}
+
+static void *vidi_get_panel(struct device *dev)
+{
+       DRM_DEBUG_KMS("%s\n", __FILE__);
+
+       /* TODO. */
+
+       return NULL;
+}
+
+static int vidi_check_timing(struct device *dev, void *timing)
+{
+       DRM_DEBUG_KMS("%s\n", __FILE__);
+
+       /* TODO. */
+
+       return 0;
+}
+
+static int vidi_display_power_on(struct device *dev, int mode)
+{
+       DRM_DEBUG_KMS("%s\n", __FILE__);
+
+       /* TODO */
+
+       return 0;
+}
+
+static struct exynos_drm_display_ops vidi_display_ops = {
+       .type = EXYNOS_DISPLAY_TYPE_VIDI,
+       .is_connected = vidi_display_is_connected,
+       .get_edid = vidi_get_edid,
+       .get_panel = vidi_get_panel,
+       .check_timing = vidi_check_timing,
+       .power_on = vidi_display_power_on,
+};
+
+static void vidi_dpms(struct device *subdrv_dev, int mode)
+{
+       struct vidi_context *ctx = get_vidi_context(subdrv_dev);
+
+       DRM_DEBUG_KMS("%s, %d\n", __FILE__, mode);
+
+       mutex_lock(&ctx->lock);
+
+       switch (mode) {
+       case DRM_MODE_DPMS_ON:
+               /* TODO. */
+               break;
+       case DRM_MODE_DPMS_STANDBY:
+       case DRM_MODE_DPMS_SUSPEND:
+       case DRM_MODE_DPMS_OFF:
+               /* TODO. */
+               break;
+       default:
+               DRM_DEBUG_KMS("unspecified mode %d\n", mode);
+               break;
+       }
+
+       mutex_unlock(&ctx->lock);
+}
+
+static void vidi_apply(struct device *subdrv_dev)
+{
+       struct vidi_context *ctx = get_vidi_context(subdrv_dev);
+       struct exynos_drm_manager *mgr = &ctx->subdrv.manager;
+       struct exynos_drm_manager_ops *mgr_ops = mgr->ops;
+       struct exynos_drm_overlay_ops *ovl_ops = mgr->overlay_ops;
+       struct vidi_win_data *win_data;
+       int i;
+
+       DRM_DEBUG_KMS("%s\n", __FILE__);
+
+       for (i = 0; i < WINDOWS_NR; i++) {
+               win_data = &ctx->win_data[i];
+               if (win_data->enabled && (ovl_ops && ovl_ops->commit))
+                       ovl_ops->commit(subdrv_dev, i);
+       }
+
+       if (mgr_ops && mgr_ops->commit)
+               mgr_ops->commit(subdrv_dev);
+}
+
+static void vidi_commit(struct device *dev)
+{
+       struct vidi_context *ctx = get_vidi_context(dev);
+
+       DRM_DEBUG_KMS("%s\n", __FILE__);
+
+       if (ctx->suspended)
+               return;
+}
+
+static int vidi_enable_vblank(struct device *dev)
+{
+       struct vidi_context *ctx = get_vidi_context(dev);
+
+       DRM_DEBUG_KMS("%s\n", __FILE__);
+
+       if (ctx->suspended)
+               return -EPERM;
+
+       if (!test_and_set_bit(0, &ctx->irq_flags))
+               ctx->vblank_on = true;
+
+       return 0;
+}
+
+static void vidi_disable_vblank(struct device *dev)
+{
+       struct vidi_context *ctx = get_vidi_context(dev);
+
+       DRM_DEBUG_KMS("%s\n", __FILE__);
+
+       if (ctx->suspended)
+               return;
+
+       if (test_and_clear_bit(0, &ctx->irq_flags))
+               ctx->vblank_on = false;
+}
+
+static struct exynos_drm_manager_ops vidi_manager_ops = {
+       .dpms = vidi_dpms,
+       .apply = vidi_apply,
+       .commit = vidi_commit,
+       .enable_vblank = vidi_enable_vblank,
+       .disable_vblank = vidi_disable_vblank,
+};
+
+static void vidi_win_mode_set(struct device *dev,
+                             struct exynos_drm_overlay *overlay)
+{
+       struct vidi_context *ctx = get_vidi_context(dev);
+       struct vidi_win_data *win_data;
+       int win;
+       unsigned long offset;
+
+       DRM_DEBUG_KMS("%s\n", __FILE__);
+
+       if (!overlay) {
+               dev_err(dev, "overlay is NULL\n");
+               return;
+       }
+
+       win = overlay->zpos;
+       if (win == DEFAULT_ZPOS)
+               win = ctx->default_win;
+
+       if (win < 0 || win > WINDOWS_NR)
+               return;
+
+       offset = overlay->fb_x * (overlay->bpp >> 3);
+       offset += overlay->fb_y * overlay->pitch;
+
+       DRM_DEBUG_KMS("offset = 0x%lx, pitch = %x\n", offset, overlay->pitch);
+
+       win_data = &ctx->win_data[win];
+
+       win_data->offset_x = overlay->crtc_x;
+       win_data->offset_y = overlay->crtc_y;
+       win_data->ovl_width = overlay->crtc_width;
+       win_data->ovl_height = overlay->crtc_height;
+       win_data->fb_width = overlay->fb_width;
+       win_data->fb_height = overlay->fb_height;
+       win_data->dma_addr = overlay->dma_addr[0] + offset;
+       win_data->vaddr = overlay->vaddr[0] + offset;
+       win_data->bpp = overlay->bpp;
+       win_data->buf_offsize = (overlay->fb_width - overlay->crtc_width) *
+                               (overlay->bpp >> 3);
+       win_data->line_size = overlay->crtc_width * (overlay->bpp >> 3);
+
+       /*
+        * some parts of win_data should be transferred to user side
+        * through specific ioctl.
+        */
+
+       DRM_DEBUG_KMS("offset_x = %d, offset_y = %d\n",
+                       win_data->offset_x, win_data->offset_y);
+       DRM_DEBUG_KMS("ovl_width = %d, ovl_height = %d\n",
+                       win_data->ovl_width, win_data->ovl_height);
+       DRM_DEBUG_KMS("paddr = 0x%lx, vaddr = 0x%lx\n",
+                       (unsigned long)win_data->dma_addr,
+                       (unsigned long)win_data->vaddr);
+       DRM_DEBUG_KMS("fb_width = %d, crtc_width = %d\n",
+                       overlay->fb_width, overlay->crtc_width);
+}
+
+static void vidi_win_commit(struct device *dev, int zpos)
+{
+       struct vidi_context *ctx = get_vidi_context(dev);
+       struct vidi_win_data *win_data;
+       int win = zpos;
+
+       DRM_DEBUG_KMS("%s\n", __FILE__);
+
+       if (ctx->suspended)
+               return;
+
+       if (win == DEFAULT_ZPOS)
+               win = ctx->default_win;
+
+       if (win < 0 || win > WINDOWS_NR)
+               return;
+
+       win_data = &ctx->win_data[win];
+
+       win_data->enabled = true;
+
+       DRM_DEBUG_KMS("dma_addr = 0x%x\n", win_data->dma_addr);
+
+       if (ctx->vblank_on)
+               schedule_work(&ctx->work);
+}
+
+static void vidi_win_disable(struct device *dev, int zpos)
+{
+       struct vidi_context *ctx = get_vidi_context(dev);
+       struct vidi_win_data *win_data;
+       int win = zpos;
+
+       DRM_DEBUG_KMS("%s\n", __FILE__);
+
+       if (win == DEFAULT_ZPOS)
+               win = ctx->default_win;
+
+       if (win < 0 || win > WINDOWS_NR)
+               return;
+
+       win_data = &ctx->win_data[win];
+       win_data->enabled = false;
+
+       /* TODO. */
+}
+
+static struct exynos_drm_overlay_ops vidi_overlay_ops = {
+       .mode_set = vidi_win_mode_set,
+       .commit = vidi_win_commit,
+       .disable = vidi_win_disable,
+};
+
+static void vidi_finish_pageflip(struct drm_device *drm_dev, int crtc)
+{
+       struct exynos_drm_private *dev_priv = drm_dev->dev_private;
+       struct drm_pending_vblank_event *e, *t;
+       struct timeval now;
+       unsigned long flags;
+       bool is_checked = false;
+
+       spin_lock_irqsave(&drm_dev->event_lock, flags);
+
+       list_for_each_entry_safe(e, t, &dev_priv->pageflip_event_list,
+                       base.link) {
+               /* if event's pipe isn't same as crtc then ignore it. */
+               if (crtc != e->pipe)
+                       continue;
+
+               is_checked = true;
+
+               do_gettimeofday(&now);
+               e->event.sequence = 0;
+               e->event.tv_sec = now.tv_sec;
+               e->event.tv_usec = now.tv_usec;
+
+               list_move_tail(&e->base.link, &e->base.file_priv->event_list);
+               wake_up_interruptible(&e->base.file_priv->event_wait);
+       }
+
+       if (is_checked) {
+               /*
+                * call drm_vblank_put only in case that drm_vblank_get was
+                * called.
+                */
+               if (atomic_read(&drm_dev->vblank_refcount[crtc]) > 0)
+                       drm_vblank_put(drm_dev, crtc);
+
+               /*
+                * don't off vblank if vblank_disable_allowed is 1,
+                * because vblank would be off by timer handler.
+                */
+               if (!drm_dev->vblank_disable_allowed)
+                       drm_vblank_off(drm_dev, crtc);
+       }
+
+       spin_unlock_irqrestore(&drm_dev->event_lock, flags);
+}
+
+static void vidi_fake_vblank_handler(struct work_struct *work)
+{
+       struct vidi_context *ctx = container_of(work, struct vidi_context,
+                                       work);
+       struct exynos_drm_subdrv *subdrv = &ctx->subdrv;
+       struct exynos_drm_manager *manager = &subdrv->manager;
+
+       if (manager->pipe < 0)
+               return;
+
+       /* refresh rate is about 50Hz. */
+       usleep_range(16000, 20000);
+
+       drm_handle_vblank(subdrv->drm_dev, manager->pipe);
+       vidi_finish_pageflip(subdrv->drm_dev, manager->pipe);
+}
+
+static int vidi_subdrv_probe(struct drm_device *drm_dev, struct device *dev)
+{
+       DRM_DEBUG_KMS("%s\n", __FILE__);
+
+       /*
+        * enable drm irq mode.
+        * - with irq_enabled = 1, we can use the vblank feature.
+        *
+        * P.S. note that we wouldn't use drm irq handler but
+        *      just specific driver own one instead because
+        *      drm framework supports only one irq handler.
+        */
+       drm_dev->irq_enabled = 1;
+
+       /*
+        * with vblank_disable_allowed = 1, vblank interrupt will be disabled
+        * by drm timer once a current process gives up ownership of
+        * vblank event.(after drm_vblank_put function is called)
+        */
+       drm_dev->vblank_disable_allowed = 1;
+
+       return 0;
+}
+
+static void vidi_subdrv_remove(struct drm_device *drm_dev)
+{
+       DRM_DEBUG_KMS("%s\n", __FILE__);
+
+       /* TODO. */
+}
+
+static int vidi_power_on(struct vidi_context *ctx, bool enable)
+{
+       struct exynos_drm_subdrv *subdrv = &ctx->subdrv;
+       struct device *dev = subdrv->manager.dev;
+
+       DRM_DEBUG_KMS("%s\n", __FILE__);
+
+       if (enable != false && enable != true)
+               return -EINVAL;
+
+       if (enable) {
+               ctx->suspended = false;
+
+               /* if vblank was enabled status, enable it again. */
+               if (test_and_clear_bit(0, &ctx->irq_flags))
+                       vidi_enable_vblank(dev);
+
+               vidi_apply(dev);
+       } else {
+               ctx->suspended = true;
+       }
+
+       return 0;
+}
+
+static int vidi_show_connection(struct device *dev,
+                               struct device_attribute *attr, char *buf)
+{
+       int rc;
+       struct vidi_context *ctx = get_vidi_context(dev);
+
+       mutex_lock(&ctx->lock);
+
+       rc = sprintf(buf, "%d\n", ctx->connected);
+
+       mutex_unlock(&ctx->lock);
+
+       return rc;
+}
+
+static int vidi_store_connection(struct device *dev,
+                               struct device_attribute *attr,
+                               const char *buf, size_t len)
+{
+       struct vidi_context *ctx = get_vidi_context(dev);
+       int ret;
+
+       DRM_DEBUG_KMS("%s\n", __FILE__);
+
+       ret = kstrtoint(buf, 0, &ctx->connected);
+       if (ret)
+               return ret;
+
+       if (ctx->connected > 1)
+               return -EINVAL;
+
+       DRM_DEBUG_KMS("requested connection.\n");
+
+       drm_helper_hpd_irq_event(ctx->subdrv.drm_dev);
+
+       return len;
+}
+
+static DEVICE_ATTR(connection, 0644, vidi_show_connection,
+                       vidi_store_connection);
+
+int vidi_connection_ioctl(struct drm_device *drm_dev, void *data,
+                               struct drm_file *file_priv)
+{
+       struct vidi_context *ctx = NULL;
+       struct drm_encoder *encoder;
+       struct exynos_drm_manager *manager;
+       struct exynos_drm_display_ops *display_ops;
+       struct drm_exynos_vidi_connection *vidi = data;
+
+       DRM_DEBUG_KMS("%s\n", __FILE__);
+
+       if (!vidi) {
+               DRM_DEBUG_KMS("user data for vidi is null.\n");
+               return -EINVAL;
+       }
+
+       if (!vidi->edid) {
+               DRM_DEBUG_KMS("edid data is null.\n");
+               return -EINVAL;
+       }
+
+       if (vidi->connection > 1) {
+               DRM_DEBUG_KMS("connection should be 0 or 1.\n");
+               return -EINVAL;
+       }
+
+       list_for_each_entry(encoder, &drm_dev->mode_config.encoder_list,
+                                                               head) {
+               manager = exynos_drm_get_manager(encoder);
+               display_ops = manager->display_ops;
+
+               if (display_ops->type == EXYNOS_DISPLAY_TYPE_VIDI) {
+                       ctx = get_vidi_context(manager->dev);
+                       break;
+               }
+       }
+
+       if (!ctx) {
+               DRM_DEBUG_KMS("not found virtual device type encoder.\n");
+               return -EINVAL;
+       }
+
+       if (ctx->connected == vidi->connection) {
+               DRM_DEBUG_KMS("same connection request.\n");
+               return -EINVAL;
+       }
+
+       if (vidi->connection)
+               ctx->raw_edid = (struct edid *)vidi->edid;
+
+       ctx->connected = vidi->connection;
+       drm_helper_hpd_irq_event(ctx->subdrv.drm_dev);
+
+       return 0;
+}
+
+static int __devinit vidi_probe(struct platform_device *pdev)
+{
+       struct device *dev = &pdev->dev;
+       struct vidi_context *ctx;
+       struct exynos_drm_subdrv *subdrv;
+       int ret;
+
+       DRM_DEBUG_KMS("%s\n", __FILE__);
+
+       ctx = kzalloc(sizeof(*ctx), GFP_KERNEL);
+       if (!ctx)
+               return -ENOMEM;
+
+       ctx->default_win = 0;
+
+       INIT_WORK(&ctx->work, vidi_fake_vblank_handler);
+
+       /* for test */
+       ctx->raw_edid = (struct edid *)fake_edid_info;
+
+       subdrv = &ctx->subdrv;
+       subdrv->probe = vidi_subdrv_probe;
+       subdrv->remove = vidi_subdrv_remove;
+       subdrv->manager.pipe = -1;
+       subdrv->manager.ops = &vidi_manager_ops;
+       subdrv->manager.overlay_ops = &vidi_overlay_ops;
+       subdrv->manager.display_ops = &vidi_display_ops;
+       subdrv->manager.dev = dev;
+
+       mutex_init(&ctx->lock);
+
+       platform_set_drvdata(pdev, ctx);
+
+       ret = device_create_file(&pdev->dev, &dev_attr_connection);
+       if (ret < 0)
+               DRM_INFO("failed to create connection sysfs.\n");
+
+       exynos_drm_subdrv_register(subdrv);
+
+       return 0;
+}
+
+static int __devexit vidi_remove(struct platform_device *pdev)
+{
+       struct vidi_context *ctx = platform_get_drvdata(pdev);
+
+       DRM_DEBUG_KMS("%s\n", __FILE__);
+
+       exynos_drm_subdrv_unregister(&ctx->subdrv);
+
+       kfree(ctx);
+
+       return 0;
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int vidi_suspend(struct device *dev)
+{
+       struct vidi_context *ctx = get_vidi_context(dev);
+
+       return vidi_power_on(ctx, false);
+}
+
+static int vidi_resume(struct device *dev)
+{
+       struct vidi_context *ctx = get_vidi_context(dev);
+
+       return vidi_power_on(ctx, true);
+}
+#endif
+
+static const struct dev_pm_ops vidi_pm_ops = {
+       SET_SYSTEM_SLEEP_PM_OPS(vidi_suspend, vidi_resume)
+};
+
+struct platform_driver vidi_driver = {
+       .probe          = vidi_probe,
+       .remove         = __devexit_p(vidi_remove),
+       .driver         = {
+               .name   = "exynos-drm-vidi",
+               .owner  = THIS_MODULE,
+               .pm     = &vidi_pm_ops,
+       },
+};
diff --git a/drivers/gpu/drm/exynos/exynos_drm_vidi.h b/drivers/gpu/drm/exynos/exynos_drm_vidi.h
new file mode 100644 (file)
index 0000000..a4babe4
--- /dev/null
@@ -0,0 +1,36 @@
+/* exynos_drm_vidi.h
+ *
+ * Copyright (c) 2012 Samsung Electronics Co., Ltd.
+ * Author: Inki Dae <inki.dae@samsung.com>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * VA LINUX SYSTEMS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+#ifndef _EXYNOS_DRM_VIDI_H_
+#define _EXYNOS_DRM_VIDI_H_
+
+#ifdef CONFIG_DRM_EXYNOS_VIDI
+int vidi_connection_ioctl(struct drm_device *drm_dev, void *data,
+                               struct drm_file *file_priv);
+#else
+#define vidi_connection_ioctl  NULL
+#endif
+
+#endif
index 3429d3fd93f3265d31cb3cad101b87eccc74964d..575a8cbd35337b2719195b733485c4d04aee580c 100644 (file)
 #include "exynos_hdmi.h"
 
 #define HDMI_OVERLAY_NUMBER    3
+#define MAX_WIDTH              1920
+#define MAX_HEIGHT             1080
 #define get_hdmi_context(dev)  platform_get_drvdata(to_platform_device(dev))
 
-static const u8 hdmiphy_conf27[32] = {
+struct hdmi_resources {
+       struct clk                      *hdmi;
+       struct clk                      *sclk_hdmi;
+       struct clk                      *sclk_pixel;
+       struct clk                      *sclk_hdmiphy;
+       struct clk                      *hdmiphy;
+       struct regulator_bulk_data      *regul_bulk;
+       int                             regul_count;
+};
+
+struct hdmi_context {
+       struct device                   *dev;
+       struct drm_device               *drm_dev;
+       struct fb_videomode             *default_timing;
+       unsigned int                    is_v13:1;
+       unsigned int                    default_win;
+       unsigned int                    default_bpp;
+       bool                            hpd_handle;
+       bool                            enabled;
+
+       struct resource                 *regs_res;
+       void __iomem                    *regs;
+       unsigned int                    irq;
+       struct workqueue_struct         *wq;
+       struct work_struct              hotplug_work;
+
+       struct i2c_client               *ddc_port;
+       struct i2c_client               *hdmiphy_port;
+
+       /* current hdmiphy conf index */
+       int cur_conf;
+
+       struct hdmi_resources           res;
+       void                            *parent_ctx;
+};
+
+/* HDMI Version 1.3 */
+static const u8 hdmiphy_v13_conf27[32] = {
        0x01, 0x05, 0x00, 0xD8, 0x10, 0x1C, 0x30, 0x40,
        0x6B, 0x10, 0x02, 0x51, 0xDF, 0xF2, 0x54, 0x87,
        0x84, 0x00, 0x30, 0x38, 0x00, 0x08, 0x10, 0xE0,
        0x22, 0x40, 0xE3, 0x26, 0x00, 0x00, 0x00, 0x00,
 };
 
-static const u8 hdmiphy_conf27_027[32] = {
+static const u8 hdmiphy_v13_conf27_027[32] = {
        0x01, 0x05, 0x00, 0xD4, 0x10, 0x9C, 0x09, 0x64,
        0x6B, 0x10, 0x02, 0x51, 0xDF, 0xF2, 0x54, 0x87,
        0x84, 0x00, 0x30, 0x38, 0x00, 0x08, 0x10, 0xE0,
        0x22, 0x40, 0xE3, 0x26, 0x00, 0x00, 0x00, 0x00,
 };
 
-static const u8 hdmiphy_conf74_175[32] = {
+static const u8 hdmiphy_v13_conf74_175[32] = {
        0x01, 0x05, 0x00, 0xD8, 0x10, 0x9C, 0xef, 0x5B,
        0x6D, 0x10, 0x01, 0x51, 0xef, 0xF3, 0x54, 0xb9,
        0x84, 0x00, 0x30, 0x38, 0x00, 0x08, 0x10, 0xE0,
        0x22, 0x40, 0xa5, 0x26, 0x01, 0x00, 0x00, 0x00,
 };
 
-static const u8 hdmiphy_conf74_25[32] = {
+static const u8 hdmiphy_v13_conf74_25[32] = {
        0x01, 0x05, 0x00, 0xd8, 0x10, 0x9c, 0xf8, 0x40,
        0x6a, 0x10, 0x01, 0x51, 0xff, 0xf1, 0x54, 0xba,
        0x84, 0x00, 0x10, 0x38, 0x00, 0x08, 0x10, 0xe0,
        0x22, 0x40, 0xa4, 0x26, 0x01, 0x00, 0x00, 0x00,
 };
 
-static const u8 hdmiphy_conf148_5[32] = {
+static const u8 hdmiphy_v13_conf148_5[32] = {
        0x01, 0x05, 0x00, 0xD8, 0x10, 0x9C, 0xf8, 0x40,
        0x6A, 0x18, 0x00, 0x51, 0xff, 0xF1, 0x54, 0xba,
        0x84, 0x00, 0x10, 0x38, 0x00, 0x08, 0x10, 0xE0,
        0x22, 0x40, 0xa4, 0x26, 0x02, 0x00, 0x00, 0x00,
 };
 
-struct hdmi_tg_regs {
+struct hdmi_v13_tg_regs {
        u8 cmd;
        u8 h_fsz_l;
        u8 h_fsz_h;
@@ -110,7 +149,7 @@ struct hdmi_tg_regs {
        u8 field_bot_hdmi_h;
 };
 
-struct hdmi_core_regs {
+struct hdmi_v13_core_regs {
        u8 h_blank[2];
        u8 v_blank[3];
        u8 h_v_line[3];
@@ -123,12 +162,21 @@ struct hdmi_core_regs {
        u8 v_sync_gen3[3];
 };
 
-struct hdmi_preset_conf {
-       struct hdmi_core_regs core;
-       struct hdmi_tg_regs tg;
+struct hdmi_v13_preset_conf {
+       struct hdmi_v13_core_regs core;
+       struct hdmi_v13_tg_regs tg;
 };
 
-static const struct hdmi_preset_conf hdmi_conf_480p = {
+struct hdmi_v13_conf {
+       int width;
+       int height;
+       int vrefresh;
+       bool interlace;
+       const u8 *hdmiphy_data;
+       const struct hdmi_v13_preset_conf *conf;
+};
+
+static const struct hdmi_v13_preset_conf hdmi_v13_conf_480p = {
        .core = {
                .h_blank = {0x8a, 0x00},
                .v_blank = {0x0d, 0x6a, 0x01},
@@ -154,7 +202,7 @@ static const struct hdmi_preset_conf hdmi_conf_480p = {
        },
 };
 
-static const struct hdmi_preset_conf hdmi_conf_720p60 = {
+static const struct hdmi_v13_preset_conf hdmi_v13_conf_720p60 = {
        .core = {
                .h_blank = {0x72, 0x01},
                .v_blank = {0xee, 0xf2, 0x00},
@@ -182,7 +230,7 @@ static const struct hdmi_preset_conf hdmi_conf_720p60 = {
        },
 };
 
-static const struct hdmi_preset_conf hdmi_conf_1080i50 = {
+static const struct hdmi_v13_preset_conf hdmi_v13_conf_1080i50 = {
        .core = {
                .h_blank = {0xd0, 0x02},
                .v_blank = {0x32, 0xB2, 0x00},
@@ -210,7 +258,7 @@ static const struct hdmi_preset_conf hdmi_conf_1080i50 = {
        },
 };
 
-static const struct hdmi_preset_conf hdmi_conf_1080p50 = {
+static const struct hdmi_v13_preset_conf hdmi_v13_conf_1080p50 = {
        .core = {
                .h_blank = {0xd0, 0x02},
                .v_blank = {0x65, 0x6c, 0x01},
@@ -238,7 +286,7 @@ static const struct hdmi_preset_conf hdmi_conf_1080p50 = {
        },
 };
 
-static const struct hdmi_preset_conf hdmi_conf_1080i60 = {
+static const struct hdmi_v13_preset_conf hdmi_v13_conf_1080i60 = {
        .core = {
                .h_blank = {0x18, 0x01},
                .v_blank = {0x32, 0xB2, 0x00},
@@ -266,7 +314,7 @@ static const struct hdmi_preset_conf hdmi_conf_1080i60 = {
        },
 };
 
-static const struct hdmi_preset_conf hdmi_conf_1080p60 = {
+static const struct hdmi_v13_preset_conf hdmi_v13_conf_1080p60 = {
        .core = {
                .h_blank = {0x18, 0x01},
                .v_blank = {0x65, 0x6c, 0x01},
@@ -294,13 +342,530 @@ static const struct hdmi_preset_conf hdmi_conf_1080p60 = {
        },
 };
 
+static const struct hdmi_v13_conf hdmi_v13_confs[] = {
+       { 1280, 720, 60, false, hdmiphy_v13_conf74_25, &hdmi_v13_conf_720p60 },
+       { 1280, 720, 50, false, hdmiphy_v13_conf74_25, &hdmi_v13_conf_720p60 },
+       { 720, 480, 60, false, hdmiphy_v13_conf27_027, &hdmi_v13_conf_480p },
+       { 1920, 1080, 50, true, hdmiphy_v13_conf74_25, &hdmi_v13_conf_1080i50 },
+       { 1920, 1080, 50, false, hdmiphy_v13_conf148_5,
+                                &hdmi_v13_conf_1080p50 },
+       { 1920, 1080, 60, true, hdmiphy_v13_conf74_25, &hdmi_v13_conf_1080i60 },
+       { 1920, 1080, 60, false, hdmiphy_v13_conf148_5,
+                                &hdmi_v13_conf_1080p60 },
+};
+
+/* HDMI Version 1.4 */
+static const u8 hdmiphy_conf27_027[32] = {
+       0x01, 0xd1, 0x2d, 0x72, 0x40, 0x64, 0x12, 0x08,
+       0x43, 0xa0, 0x0e, 0xd9, 0x45, 0xa0, 0xac, 0x80,
+       0x08, 0x80, 0x11, 0x04, 0x02, 0x22, 0x44, 0x86,
+       0x54, 0xe3, 0x24, 0x00, 0x00, 0x00, 0x01, 0x00,
+};
+
+static const u8 hdmiphy_conf74_25[32] = {
+       0x01, 0xd1, 0x1f, 0x10, 0x40, 0x40, 0xf8, 0x08,
+       0x81, 0xa0, 0xba, 0xd8, 0x45, 0xa0, 0xac, 0x80,
+       0x3c, 0x80, 0x11, 0x04, 0x02, 0x22, 0x44, 0x86,
+       0x54, 0xa5, 0x24, 0x01, 0x00, 0x00, 0x01, 0x00,
+};
+
+static const u8 hdmiphy_conf148_5[32] = {
+       0x01, 0xd1, 0x1f, 0x00, 0x40, 0x40, 0xf8, 0x08,
+       0x81, 0xa0, 0xba, 0xd8, 0x45, 0xa0, 0xac, 0x80,
+       0x3c, 0x80, 0x11, 0x04, 0x02, 0x22, 0x44, 0x86,
+       0x54, 0x4b, 0x25, 0x03, 0x00, 0x00, 0x01, 0x00,
+};
+
+struct hdmi_tg_regs {
+       u8 cmd;
+       u8 h_fsz_l;
+       u8 h_fsz_h;
+       u8 hact_st_l;
+       u8 hact_st_h;
+       u8 hact_sz_l;
+       u8 hact_sz_h;
+       u8 v_fsz_l;
+       u8 v_fsz_h;
+       u8 vsync_l;
+       u8 vsync_h;
+       u8 vsync2_l;
+       u8 vsync2_h;
+       u8 vact_st_l;
+       u8 vact_st_h;
+       u8 vact_sz_l;
+       u8 vact_sz_h;
+       u8 field_chg_l;
+       u8 field_chg_h;
+       u8 vact_st2_l;
+       u8 vact_st2_h;
+       u8 vact_st3_l;
+       u8 vact_st3_h;
+       u8 vact_st4_l;
+       u8 vact_st4_h;
+       u8 vsync_top_hdmi_l;
+       u8 vsync_top_hdmi_h;
+       u8 vsync_bot_hdmi_l;
+       u8 vsync_bot_hdmi_h;
+       u8 field_top_hdmi_l;
+       u8 field_top_hdmi_h;
+       u8 field_bot_hdmi_l;
+       u8 field_bot_hdmi_h;
+       u8 tg_3d;
+};
+
+struct hdmi_core_regs {
+       u8 h_blank[2];
+       u8 v2_blank[2];
+       u8 v1_blank[2];
+       u8 v_line[2];
+       u8 h_line[2];
+       u8 hsync_pol[1];
+       u8 vsync_pol[1];
+       u8 int_pro_mode[1];
+       u8 v_blank_f0[2];
+       u8 v_blank_f1[2];
+       u8 h_sync_start[2];
+       u8 h_sync_end[2];
+       u8 v_sync_line_bef_2[2];
+       u8 v_sync_line_bef_1[2];
+       u8 v_sync_line_aft_2[2];
+       u8 v_sync_line_aft_1[2];
+       u8 v_sync_line_aft_pxl_2[2];
+       u8 v_sync_line_aft_pxl_1[2];
+       u8 v_blank_f2[2]; /* for 3D mode */
+       u8 v_blank_f3[2]; /* for 3D mode */
+       u8 v_blank_f4[2]; /* for 3D mode */
+       u8 v_blank_f5[2]; /* for 3D mode */
+       u8 v_sync_line_aft_3[2];
+       u8 v_sync_line_aft_4[2];
+       u8 v_sync_line_aft_5[2];
+       u8 v_sync_line_aft_6[2];
+       u8 v_sync_line_aft_pxl_3[2];
+       u8 v_sync_line_aft_pxl_4[2];
+       u8 v_sync_line_aft_pxl_5[2];
+       u8 v_sync_line_aft_pxl_6[2];
+       u8 vact_space_1[2];
+       u8 vact_space_2[2];
+       u8 vact_space_3[2];
+       u8 vact_space_4[2];
+       u8 vact_space_5[2];
+       u8 vact_space_6[2];
+};
+
+struct hdmi_preset_conf {
+       struct hdmi_core_regs core;
+       struct hdmi_tg_regs tg;
+};
+
+struct hdmi_conf {
+       int width;
+       int height;
+       int vrefresh;
+       bool interlace;
+       const u8 *hdmiphy_data;
+       const struct hdmi_preset_conf *conf;
+};
+
+static const struct hdmi_preset_conf hdmi_conf_480p60 = {
+       .core = {
+               .h_blank = {0x8a, 0x00},
+               .v2_blank = {0x0d, 0x02},
+               .v1_blank = {0x2d, 0x00},
+               .v_line = {0x0d, 0x02},
+               .h_line = {0x5a, 0x03},
+               .hsync_pol = {0x01},
+               .vsync_pol = {0x01},
+               .int_pro_mode = {0x00},
+               .v_blank_f0 = {0xff, 0xff},
+               .v_blank_f1 = {0xff, 0xff},
+               .h_sync_start = {0x0e, 0x00},
+               .h_sync_end = {0x4c, 0x00},
+               .v_sync_line_bef_2 = {0x0f, 0x00},
+               .v_sync_line_bef_1 = {0x09, 0x00},
+               .v_sync_line_aft_2 = {0xff, 0xff},
+               .v_sync_line_aft_1 = {0xff, 0xff},
+               .v_sync_line_aft_pxl_2 = {0xff, 0xff},
+               .v_sync_line_aft_pxl_1 = {0xff, 0xff},
+               .v_blank_f2 = {0xff, 0xff},
+               .v_blank_f3 = {0xff, 0xff},
+               .v_blank_f4 = {0xff, 0xff},
+               .v_blank_f5 = {0xff, 0xff},
+               .v_sync_line_aft_3 = {0xff, 0xff},
+               .v_sync_line_aft_4 = {0xff, 0xff},
+               .v_sync_line_aft_5 = {0xff, 0xff},
+               .v_sync_line_aft_6 = {0xff, 0xff},
+               .v_sync_line_aft_pxl_3 = {0xff, 0xff},
+               .v_sync_line_aft_pxl_4 = {0xff, 0xff},
+               .v_sync_line_aft_pxl_5 = {0xff, 0xff},
+               .v_sync_line_aft_pxl_6 = {0xff, 0xff},
+               .vact_space_1 = {0xff, 0xff},
+               .vact_space_2 = {0xff, 0xff},
+               .vact_space_3 = {0xff, 0xff},
+               .vact_space_4 = {0xff, 0xff},
+               .vact_space_5 = {0xff, 0xff},
+               .vact_space_6 = {0xff, 0xff},
+               /* other don't care */
+       },
+       .tg = {
+               0x00, /* cmd */
+               0x5a, 0x03, /* h_fsz */
+               0x8a, 0x00, 0xd0, 0x02, /* hact */
+               0x0d, 0x02, /* v_fsz */
+               0x01, 0x00, 0x33, 0x02, /* vsync */
+               0x2d, 0x00, 0xe0, 0x01, /* vact */
+               0x33, 0x02, /* field_chg */
+               0x48, 0x02, /* vact_st2 */
+               0x00, 0x00, /* vact_st3 */
+               0x00, 0x00, /* vact_st4 */
+               0x01, 0x00, 0x01, 0x00, /* vsync top/bot */
+               0x01, 0x00, 0x33, 0x02, /* field top/bot */
+               0x00, /* 3d FP */
+       },
+};
+
+static const struct hdmi_preset_conf hdmi_conf_720p50 = {
+       .core = {
+               .h_blank = {0xbc, 0x02},
+               .v2_blank = {0xee, 0x02},
+               .v1_blank = {0x1e, 0x00},
+               .v_line = {0xee, 0x02},
+               .h_line = {0xbc, 0x07},
+               .hsync_pol = {0x00},
+               .vsync_pol = {0x00},
+               .int_pro_mode = {0x00},
+               .v_blank_f0 = {0xff, 0xff},
+               .v_blank_f1 = {0xff, 0xff},
+               .h_sync_start = {0xb6, 0x01},
+               .h_sync_end = {0xde, 0x01},
+               .v_sync_line_bef_2 = {0x0a, 0x00},
+               .v_sync_line_bef_1 = {0x05, 0x00},
+               .v_sync_line_aft_2 = {0xff, 0xff},
+               .v_sync_line_aft_1 = {0xff, 0xff},
+               .v_sync_line_aft_pxl_2 = {0xff, 0xff},
+               .v_sync_line_aft_pxl_1 = {0xff, 0xff},
+               .v_blank_f2 = {0xff, 0xff},
+               .v_blank_f3 = {0xff, 0xff},
+               .v_blank_f4 = {0xff, 0xff},
+               .v_blank_f5 = {0xff, 0xff},
+               .v_sync_line_aft_3 = {0xff, 0xff},
+               .v_sync_line_aft_4 = {0xff, 0xff},
+               .v_sync_line_aft_5 = {0xff, 0xff},
+               .v_sync_line_aft_6 = {0xff, 0xff},
+               .v_sync_line_aft_pxl_3 = {0xff, 0xff},
+               .v_sync_line_aft_pxl_4 = {0xff, 0xff},
+               .v_sync_line_aft_pxl_5 = {0xff, 0xff},
+               .v_sync_line_aft_pxl_6 = {0xff, 0xff},
+               .vact_space_1 = {0xff, 0xff},
+               .vact_space_2 = {0xff, 0xff},
+               .vact_space_3 = {0xff, 0xff},
+               .vact_space_4 = {0xff, 0xff},
+               .vact_space_5 = {0xff, 0xff},
+               .vact_space_6 = {0xff, 0xff},
+               /* other don't care */
+       },
+       .tg = {
+               0x00, /* cmd */
+               0xbc, 0x07, /* h_fsz */
+               0xbc, 0x02, 0x00, 0x05, /* hact */
+               0xee, 0x02, /* v_fsz */
+               0x01, 0x00, 0x33, 0x02, /* vsync */
+               0x1e, 0x00, 0xd0, 0x02, /* vact */
+               0x33, 0x02, /* field_chg */
+               0x48, 0x02, /* vact_st2 */
+               0x00, 0x00, /* vact_st3 */
+               0x00, 0x00, /* vact_st4 */
+               0x01, 0x00, 0x01, 0x00, /* vsync top/bot */
+               0x01, 0x00, 0x33, 0x02, /* field top/bot */
+               0x00, /* 3d FP */
+       },
+};
+
+static const struct hdmi_preset_conf hdmi_conf_720p60 = {
+       .core = {
+               .h_blank = {0x72, 0x01},
+               .v2_blank = {0xee, 0x02},
+               .v1_blank = {0x1e, 0x00},
+               .v_line = {0xee, 0x02},
+               .h_line = {0x72, 0x06},
+               .hsync_pol = {0x00},
+               .vsync_pol = {0x00},
+               .int_pro_mode = {0x00},
+               .v_blank_f0 = {0xff, 0xff},
+               .v_blank_f1 = {0xff, 0xff},
+               .h_sync_start = {0x6c, 0x00},
+               .h_sync_end = {0x94, 0x00},
+               .v_sync_line_bef_2 = {0x0a, 0x00},
+               .v_sync_line_bef_1 = {0x05, 0x00},
+               .v_sync_line_aft_2 = {0xff, 0xff},
+               .v_sync_line_aft_1 = {0xff, 0xff},
+               .v_sync_line_aft_pxl_2 = {0xff, 0xff},
+               .v_sync_line_aft_pxl_1 = {0xff, 0xff},
+               .v_blank_f2 = {0xff, 0xff},
+               .v_blank_f3 = {0xff, 0xff},
+               .v_blank_f4 = {0xff, 0xff},
+               .v_blank_f5 = {0xff, 0xff},
+               .v_sync_line_aft_3 = {0xff, 0xff},
+               .v_sync_line_aft_4 = {0xff, 0xff},
+               .v_sync_line_aft_5 = {0xff, 0xff},
+               .v_sync_line_aft_6 = {0xff, 0xff},
+               .v_sync_line_aft_pxl_3 = {0xff, 0xff},
+               .v_sync_line_aft_pxl_4 = {0xff, 0xff},
+               .v_sync_line_aft_pxl_5 = {0xff, 0xff},
+               .v_sync_line_aft_pxl_6 = {0xff, 0xff},
+               .vact_space_1 = {0xff, 0xff},
+               .vact_space_2 = {0xff, 0xff},
+               .vact_space_3 = {0xff, 0xff},
+               .vact_space_4 = {0xff, 0xff},
+               .vact_space_5 = {0xff, 0xff},
+               .vact_space_6 = {0xff, 0xff},
+               /* other don't care */
+       },
+       .tg = {
+               0x00, /* cmd */
+               0x72, 0x06, /* h_fsz */
+               0x72, 0x01, 0x00, 0x05, /* hact */
+               0xee, 0x02, /* v_fsz */
+               0x01, 0x00, 0x33, 0x02, /* vsync */
+               0x1e, 0x00, 0xd0, 0x02, /* vact */
+               0x33, 0x02, /* field_chg */
+               0x48, 0x02, /* vact_st2 */
+               0x00, 0x00, /* vact_st3 */
+               0x00, 0x00, /* vact_st4 */
+               0x01, 0x00, 0x01, 0x00, /* vsync top/bot */
+               0x01, 0x00, 0x33, 0x02, /* field top/bot */
+               0x00, /* 3d FP */
+       },
+};
+
+static const struct hdmi_preset_conf hdmi_conf_1080i50 = {
+       .core = {
+               .h_blank = {0xd0, 0x02},
+               .v2_blank = {0x32, 0x02},
+               .v1_blank = {0x16, 0x00},
+               .v_line = {0x65, 0x04},
+               .h_line = {0x50, 0x0a},
+               .hsync_pol = {0x00},
+               .vsync_pol = {0x00},
+               .int_pro_mode = {0x01},
+               .v_blank_f0 = {0x49, 0x02},
+               .v_blank_f1 = {0x65, 0x04},
+               .h_sync_start = {0x0e, 0x02},
+               .h_sync_end = {0x3a, 0x02},
+               .v_sync_line_bef_2 = {0x07, 0x00},
+               .v_sync_line_bef_1 = {0x02, 0x00},
+               .v_sync_line_aft_2 = {0x39, 0x02},
+               .v_sync_line_aft_1 = {0x34, 0x02},
+               .v_sync_line_aft_pxl_2 = {0x38, 0x07},
+               .v_sync_line_aft_pxl_1 = {0x38, 0x07},
+               .v_blank_f2 = {0xff, 0xff},
+               .v_blank_f3 = {0xff, 0xff},
+               .v_blank_f4 = {0xff, 0xff},
+               .v_blank_f5 = {0xff, 0xff},
+               .v_sync_line_aft_3 = {0xff, 0xff},
+               .v_sync_line_aft_4 = {0xff, 0xff},
+               .v_sync_line_aft_5 = {0xff, 0xff},
+               .v_sync_line_aft_6 = {0xff, 0xff},
+               .v_sync_line_aft_pxl_3 = {0xff, 0xff},
+               .v_sync_line_aft_pxl_4 = {0xff, 0xff},
+               .v_sync_line_aft_pxl_5 = {0xff, 0xff},
+               .v_sync_line_aft_pxl_6 = {0xff, 0xff},
+               .vact_space_1 = {0xff, 0xff},
+               .vact_space_2 = {0xff, 0xff},
+               .vact_space_3 = {0xff, 0xff},
+               .vact_space_4 = {0xff, 0xff},
+               .vact_space_5 = {0xff, 0xff},
+               .vact_space_6 = {0xff, 0xff},
+               /* other don't care */
+       },
+       .tg = {
+               0x00, /* cmd */
+               0x50, 0x0a, /* h_fsz */
+               0xd0, 0x02, 0x80, 0x07, /* hact */
+               0x65, 0x04, /* v_fsz */
+               0x01, 0x00, 0x33, 0x02, /* vsync */
+               0x16, 0x00, 0x1c, 0x02, /* vact */
+               0x33, 0x02, /* field_chg */
+               0x49, 0x02, /* vact_st2 */
+               0x00, 0x00, /* vact_st3 */
+               0x00, 0x00, /* vact_st4 */
+               0x01, 0x00, 0x33, 0x02, /* vsync top/bot */
+               0x01, 0x00, 0x33, 0x02, /* field top/bot */
+               0x00, /* 3d FP */
+       },
+};
+
+static const struct hdmi_preset_conf hdmi_conf_1080i60 = {
+       .core = {
+               .h_blank = {0x18, 0x01},
+               .v2_blank = {0x32, 0x02},
+               .v1_blank = {0x16, 0x00},
+               .v_line = {0x65, 0x04},
+               .h_line = {0x98, 0x08},
+               .hsync_pol = {0x00},
+               .vsync_pol = {0x00},
+               .int_pro_mode = {0x01},
+               .v_blank_f0 = {0x49, 0x02},
+               .v_blank_f1 = {0x65, 0x04},
+               .h_sync_start = {0x56, 0x00},
+               .h_sync_end = {0x82, 0x00},
+               .v_sync_line_bef_2 = {0x07, 0x00},
+               .v_sync_line_bef_1 = {0x02, 0x00},
+               .v_sync_line_aft_2 = {0x39, 0x02},
+               .v_sync_line_aft_1 = {0x34, 0x02},
+               .v_sync_line_aft_pxl_2 = {0xa4, 0x04},
+               .v_sync_line_aft_pxl_1 = {0xa4, 0x04},
+               .v_blank_f2 = {0xff, 0xff},
+               .v_blank_f3 = {0xff, 0xff},
+               .v_blank_f4 = {0xff, 0xff},
+               .v_blank_f5 = {0xff, 0xff},
+               .v_sync_line_aft_3 = {0xff, 0xff},
+               .v_sync_line_aft_4 = {0xff, 0xff},
+               .v_sync_line_aft_5 = {0xff, 0xff},
+               .v_sync_line_aft_6 = {0xff, 0xff},
+               .v_sync_line_aft_pxl_3 = {0xff, 0xff},
+               .v_sync_line_aft_pxl_4 = {0xff, 0xff},
+               .v_sync_line_aft_pxl_5 = {0xff, 0xff},
+               .v_sync_line_aft_pxl_6 = {0xff, 0xff},
+               .vact_space_1 = {0xff, 0xff},
+               .vact_space_2 = {0xff, 0xff},
+               .vact_space_3 = {0xff, 0xff},
+               .vact_space_4 = {0xff, 0xff},
+               .vact_space_5 = {0xff, 0xff},
+               .vact_space_6 = {0xff, 0xff},
+               /* other don't care */
+       },
+       .tg = {
+               0x00, /* cmd */
+               0x98, 0x08, /* h_fsz */
+               0x18, 0x01, 0x80, 0x07, /* hact */
+               0x65, 0x04, /* v_fsz */
+               0x01, 0x00, 0x33, 0x02, /* vsync */
+               0x16, 0x00, 0x1c, 0x02, /* vact */
+               0x33, 0x02, /* field_chg */
+               0x49, 0x02, /* vact_st2 */
+               0x00, 0x00, /* vact_st3 */
+               0x00, 0x00, /* vact_st4 */
+               0x01, 0x00, 0x33, 0x02, /* vsync top/bot */
+               0x01, 0x00, 0x33, 0x02, /* field top/bot */
+               0x00, /* 3d FP */
+       },
+};
+
+static const struct hdmi_preset_conf hdmi_conf_1080p50 = {
+       .core = {
+               .h_blank = {0xd0, 0x02},
+               .v2_blank = {0x65, 0x04},
+               .v1_blank = {0x2d, 0x00},
+               .v_line = {0x65, 0x04},
+               .h_line = {0x50, 0x0a},
+               .hsync_pol = {0x00},
+               .vsync_pol = {0x00},
+               .int_pro_mode = {0x00},
+               .v_blank_f0 = {0xff, 0xff},
+               .v_blank_f1 = {0xff, 0xff},
+               .h_sync_start = {0x0e, 0x02},
+               .h_sync_end = {0x3a, 0x02},
+               .v_sync_line_bef_2 = {0x09, 0x00},
+               .v_sync_line_bef_1 = {0x04, 0x00},
+               .v_sync_line_aft_2 = {0xff, 0xff},
+               .v_sync_line_aft_1 = {0xff, 0xff},
+               .v_sync_line_aft_pxl_2 = {0xff, 0xff},
+               .v_sync_line_aft_pxl_1 = {0xff, 0xff},
+               .v_blank_f2 = {0xff, 0xff},
+               .v_blank_f3 = {0xff, 0xff},
+               .v_blank_f4 = {0xff, 0xff},
+               .v_blank_f5 = {0xff, 0xff},
+               .v_sync_line_aft_3 = {0xff, 0xff},
+               .v_sync_line_aft_4 = {0xff, 0xff},
+               .v_sync_line_aft_5 = {0xff, 0xff},
+               .v_sync_line_aft_6 = {0xff, 0xff},
+               .v_sync_line_aft_pxl_3 = {0xff, 0xff},
+               .v_sync_line_aft_pxl_4 = {0xff, 0xff},
+               .v_sync_line_aft_pxl_5 = {0xff, 0xff},
+               .v_sync_line_aft_pxl_6 = {0xff, 0xff},
+               .vact_space_1 = {0xff, 0xff},
+               .vact_space_2 = {0xff, 0xff},
+               .vact_space_3 = {0xff, 0xff},
+               .vact_space_4 = {0xff, 0xff},
+               .vact_space_5 = {0xff, 0xff},
+               .vact_space_6 = {0xff, 0xff},
+               /* other don't care */
+       },
+       .tg = {
+               0x00, /* cmd */
+               0x50, 0x0a, /* h_fsz */
+               0xd0, 0x02, 0x80, 0x07, /* hact */
+               0x65, 0x04, /* v_fsz */
+               0x01, 0x00, 0x33, 0x02, /* vsync */
+               0x2d, 0x00, 0x38, 0x04, /* vact */
+               0x33, 0x02, /* field_chg */
+               0x48, 0x02, /* vact_st2 */
+               0x00, 0x00, /* vact_st3 */
+               0x00, 0x00, /* vact_st4 */
+               0x01, 0x00, 0x01, 0x00, /* vsync top/bot */
+               0x01, 0x00, 0x33, 0x02, /* field top/bot */
+               0x00, /* 3d FP */
+       },
+};
+
+static const struct hdmi_preset_conf hdmi_conf_1080p60 = {
+       .core = {
+               .h_blank = {0x18, 0x01},
+               .v2_blank = {0x65, 0x04},
+               .v1_blank = {0x2d, 0x00},
+               .v_line = {0x65, 0x04},
+               .h_line = {0x98, 0x08},
+               .hsync_pol = {0x00},
+               .vsync_pol = {0x00},
+               .int_pro_mode = {0x00},
+               .v_blank_f0 = {0xff, 0xff},
+               .v_blank_f1 = {0xff, 0xff},
+               .h_sync_start = {0x56, 0x00},
+               .h_sync_end = {0x82, 0x00},
+               .v_sync_line_bef_2 = {0x09, 0x00},
+               .v_sync_line_bef_1 = {0x04, 0x00},
+               .v_sync_line_aft_2 = {0xff, 0xff},
+               .v_sync_line_aft_1 = {0xff, 0xff},
+               .v_sync_line_aft_pxl_2 = {0xff, 0xff},
+               .v_sync_line_aft_pxl_1 = {0xff, 0xff},
+               .v_blank_f2 = {0xff, 0xff},
+               .v_blank_f3 = {0xff, 0xff},
+               .v_blank_f4 = {0xff, 0xff},
+               .v_blank_f5 = {0xff, 0xff},
+               .v_sync_line_aft_3 = {0xff, 0xff},
+               .v_sync_line_aft_4 = {0xff, 0xff},
+               .v_sync_line_aft_5 = {0xff, 0xff},
+               .v_sync_line_aft_6 = {0xff, 0xff},
+               .v_sync_line_aft_pxl_3 = {0xff, 0xff},
+               .v_sync_line_aft_pxl_4 = {0xff, 0xff},
+               .v_sync_line_aft_pxl_5 = {0xff, 0xff},
+               .v_sync_line_aft_pxl_6 = {0xff, 0xff},
+               /* other don't care */
+       },
+       .tg = {
+               0x00, /* cmd */
+               0x98, 0x08, /* h_fsz */
+               0x18, 0x01, 0x80, 0x07, /* hact */
+               0x65, 0x04, /* v_fsz */
+               0x01, 0x00, 0x33, 0x02, /* vsync */
+               0x2d, 0x00, 0x38, 0x04, /* vact */
+               0x33, 0x02, /* field_chg */
+               0x48, 0x02, /* vact_st2 */
+               0x00, 0x00, /* vact_st3 */
+               0x00, 0x00, /* vact_st4 */
+               0x01, 0x00, 0x01, 0x00, /* vsync top/bot */
+               0x01, 0x00, 0x33, 0x02, /* field top/bot */
+               0x00, /* 3d FP */
+       },
+};
+
 static const struct hdmi_conf hdmi_confs[] = {
+       { 720, 480, 60, false, hdmiphy_conf27_027, &hdmi_conf_480p60 },
+       { 1280, 720, 50, false, hdmiphy_conf74_25, &hdmi_conf_720p50 },
        { 1280, 720, 60, false, hdmiphy_conf74_25, &hdmi_conf_720p60 },
-       { 1280, 720, 50, false, hdmiphy_conf74_25, &hdmi_conf_720p60 },
-       { 720, 480, 60, false, hdmiphy_conf27_027, &hdmi_conf_480p },
        { 1920, 1080, 50, true, hdmiphy_conf74_25, &hdmi_conf_1080i50 },
-       { 1920, 1080, 50, false, hdmiphy_conf148_5, &hdmi_conf_1080p50 },
        { 1920, 1080, 60, true, hdmiphy_conf74_25, &hdmi_conf_1080i60 },
+       { 1920, 1080, 50, false, hdmiphy_conf148_5, &hdmi_conf_1080p50 },
        { 1920, 1080, 60, false, hdmiphy_conf148_5, &hdmi_conf_1080p60 },
 };
 
@@