Merge tag 'drm-misc-next-2017-05-16' of git://anongit.freedesktop.org/git/drm-misc...
authorDave Airlie <airlied@redhat.com>
Thu, 18 May 2017 02:57:06 +0000 (12:57 +1000)
committerDave Airlie <airlied@redhat.com>
Thu, 18 May 2017 02:57:06 +0000 (12:57 +1000)
UAPI Changes:
- Return -ENODEV instead of -ENXIO when creating cma fb w/o valid gem (Daniel)
- Add aspect ratio and custom scaling propertis to connector state (Maarten)

Cross-subsystem Changes:
- None

Core Changes:
- Add Laurent as bridge reviewer and Andrzej as bridge maintainer (Archit)
- Maintain new STM driver through -misc (Yannick)
- Misc doc improvements (as is tradition) (Daniel)
- Add driver-private objects to atomic state (Dhinakaran)
- Deprecate preclose hook in modern drivers (use postclose) (Daniel)
- Add hwmode to vblank struct. This fixes mode access in irq context and reduced
  a bunch of boilerplate (Daniel)

Driver Changes:
- vc4: Add out-fence support to vc4 V3D rendering (Eric)
- stm: Add stm32f429 display hw and am-480272h3tmqw-t01h panel support (Yannick)
- vc4: Remove 256MB cma limit from vc4 (Eric)
- dw-hdmi: Disable audio when inactive, instead of always enabled (Romain)
- zte: Add support for VGA to the ZTE driver (Shawn)
- i915: Track DP MST bandwidth and check it in atomic_check (Dhinakaran)
- vgem: Enable gem dmabuf import iface to facilitate ion testing (Laura)
- vc4: Add support for Cygnus (new dt compat string + couple bug fixes) (Eric)
- pl111: Add driver for pl111 CLCD display controller (Eric/Tom)
- vgem: Subclass drm_device instead of standalone platform device (Chris)

Cc: Archit Taneja <architt@codeaurora.org>
Cc: Eric Anholt <eric@anholt.net>
Cc: Yannick Fertre <yannick.fertre@st.com>
Cc: Romain Perier <romain.perier@collabora.com>
Cc: Navare, Manasi D <manasi.d.navare@intel.com>
Cc: Shawn Guo <shawn.guo@linaro.org>
Cc: Dhinakaran Pandiyan <dhinakaran.pandiyan@intel.com>
Cc: Laura Abbott <labbott@redhat.com>
Cc: Maarten Lankhorst <maarten.lankhorst@linux.intel.com>
Cc: Tom Cooksey <tom.cooksey@arm.com>
Cc: Daniel Vetter <daniel.vetter@intel.com>
Cc: Chris Wilson <chris@chris-wilson.co.uk>
* tag 'drm-misc-next-2017-05-16' of git://anongit.freedesktop.org/git/drm-misc: (72 commits)
  drm: add missing declaration to drm_blend.h
  drm/dp: Wait up all outstanding tx waiters
  drm/dp: Read the tx msg state once after checking for an event
  drm/prime: Forward declare struct device
  drm/vblank: Lock down vblank->hwmode more
  drm/vblank: drop the mode argument from drm_calc_vbltimestamp_from_scanoutpos
  drm/vblank: Add FIXME comments about moving the vblank ts hooks
  drm/vblank: Switch to bool in_vblank_irq in get_vblank_timestamp
  drm/vblank: Switch drm_driver->get_vblank_timestamp to return a bool
  drm/vgem: Convert to a struct drm_device subclass
  gpu: drm: gma500: remove dead code
  drm/sti: Adjust two checks for null pointers in sti_hqvdp_probe()
  drm/sti: Fix typos in a comment line
  drm/sti: Fix a typo in a comment line
  drm/sti: Replace 17 seq_puts() calls by seq_putc()
  drm/sti: Reduce function calls for sequence output at five places
  drm/sti: use seq_puts to display a string
  drm: Nerf the preclose callback for modern drivers
  drm/exynos: Merge pre/postclose hooks
  drm/tegra: switch to postclose
  ...

109 files changed:
Documentation/devicetree/bindings/display/brcm,bcm-vc4.txt
Documentation/devicetree/bindings/display/st,stm32-ltdc.txt [new file with mode: 0644]
Documentation/devicetree/bindings/display/zte,vou.txt
Documentation/gpu/index.rst
Documentation/gpu/pl111.rst [new file with mode: 0644]
MAINTAINERS
drivers/dma-buf/dma-buf.c
drivers/dma-buf/dma-fence.c
drivers/dma-buf/sync_debug.c
drivers/dma-buf/sync_file.c
drivers/gpu/drm/Kconfig
drivers/gpu/drm/Makefile
drivers/gpu/drm/amd/amdgpu/amdgpu.h
drivers/gpu/drm/amd/amdgpu/amdgpu_drv.c
drivers/gpu/drm/amd/amdgpu/amdgpu_kms.c
drivers/gpu/drm/amd/amdgpu/amdgpu_mode.h
drivers/gpu/drm/bridge/sii902x.c
drivers/gpu/drm/bridge/synopsys/dw-hdmi.c
drivers/gpu/drm/drm_atomic.c
drivers/gpu/drm/drm_atomic_helper.c
drivers/gpu/drm/drm_color_mgmt.c
drivers/gpu/drm/drm_connector.c
drivers/gpu/drm/drm_dp_mst_topology.c
drivers/gpu/drm/drm_fb_cma_helper.c
drivers/gpu/drm/drm_file.c
drivers/gpu/drm/drm_irq.c
drivers/gpu/drm/drm_plane_helper.c
drivers/gpu/drm/drm_prime.c
drivers/gpu/drm/exynos/exynos_drm_drv.c
drivers/gpu/drm/gma500/mdfld_tpo_vid.c
drivers/gpu/drm/i915/i915_irq.c
drivers/gpu/drm/i915/intel_display.c
drivers/gpu/drm/i915/intel_dp_mst.c
drivers/gpu/drm/i915/intel_drv.h
drivers/gpu/drm/i915/intel_hdmi.c
drivers/gpu/drm/i915/intel_sdvo.c
drivers/gpu/drm/msm/mdp/mdp5/mdp5_kms.c
drivers/gpu/drm/nouveau/nouveau_display.c
drivers/gpu/drm/nouveau/nouveau_display.h
drivers/gpu/drm/nouveau/nouveau_drm.c
drivers/gpu/drm/pl111/Kconfig [new file with mode: 0644]
drivers/gpu/drm/pl111/Makefile [new file with mode: 0644]
drivers/gpu/drm/pl111/pl111_connector.c [new file with mode: 0644]
drivers/gpu/drm/pl111/pl111_display.c [new file with mode: 0644]
drivers/gpu/drm/pl111/pl111_drm.h [new file with mode: 0644]
drivers/gpu/drm/pl111/pl111_drv.c [new file with mode: 0644]
drivers/gpu/drm/radeon/radeon_drv.c
drivers/gpu/drm/radeon/radeon_kms.c
drivers/gpu/drm/radeon/radeon_mode.h
drivers/gpu/drm/rockchip/analogix_dp-rockchip.c
drivers/gpu/drm/rockchip/rockchip_drm_drv.h
drivers/gpu/drm/rockchip/rockchip_drm_vop.c
drivers/gpu/drm/selftests/test-drm_mm.c
drivers/gpu/drm/sti/sti_cursor.c
drivers/gpu/drm/sti/sti_dvo.c
drivers/gpu/drm/sti/sti_gdp.c
drivers/gpu/drm/sti/sti_hda.c
drivers/gpu/drm/sti/sti_hdmi.c
drivers/gpu/drm/sti/sti_hqvdp.c
drivers/gpu/drm/sti/sti_mixer.c
drivers/gpu/drm/sti/sti_tvout.c
drivers/gpu/drm/sti/sti_vid.c
drivers/gpu/drm/stm/Kconfig [new file with mode: 0644]
drivers/gpu/drm/stm/Makefile [new file with mode: 0644]
drivers/gpu/drm/stm/drv.c [new file with mode: 0644]
drivers/gpu/drm/stm/ltdc.c [new file with mode: 0644]
drivers/gpu/drm/stm/ltdc.h [new file with mode: 0644]
drivers/gpu/drm/tegra/drm.c
drivers/gpu/drm/vc4/Makefile
drivers/gpu/drm/vc4/vc4_bo.c
drivers/gpu/drm/vc4/vc4_crtc.c
drivers/gpu/drm/vc4/vc4_drv.c
drivers/gpu/drm/vc4/vc4_drv.h
drivers/gpu/drm/vc4/vc4_fence.c [new file with mode: 0644]
drivers/gpu/drm/vc4/vc4_gem.c
drivers/gpu/drm/vc4/vc4_hdmi.c
drivers/gpu/drm/vc4/vc4_irq.c
drivers/gpu/drm/vc4/vc4_kms.c
drivers/gpu/drm/vc4/vc4_render_cl.c
drivers/gpu/drm/vc4/vc4_v3d.c
drivers/gpu/drm/vc4/vc4_validate.c
drivers/gpu/drm/vgem/vgem_drv.c
drivers/gpu/drm/vgem/vgem_drv.h
drivers/gpu/drm/zte/Makefile
drivers/gpu/drm/zte/zx_common_regs.h [new file with mode: 0644]
drivers/gpu/drm/zte/zx_drm_drv.c
drivers/gpu/drm/zte/zx_drm_drv.h
drivers/gpu/drm/zte/zx_plane.c
drivers/gpu/drm/zte/zx_plane_regs.h
drivers/gpu/drm/zte/zx_vga.c [new file with mode: 0644]
drivers/gpu/drm/zte/zx_vga_regs.h [new file with mode: 0644]
drivers/gpu/drm/zte/zx_vou.c
drivers/gpu/drm/zte/zx_vou_regs.h
include/drm/drmP.h
include/drm/drm_atomic.h
include/drm/drm_blend.h
include/drm/drm_color_mgmt.h
include/drm/drm_connector.h
include/drm/drm_crtc.h
include/drm/drm_dp_helper.h
include/drm/drm_dp_mst_helper.h
include/drm/drm_drv.h
include/drm/drm_fb_cma_helper.h
include/drm/drm_gem_cma_helper.h
include/drm/drm_irq.h
include/drm/drm_prime.h
include/linux/amba/clcd-regs.h [new file with mode: 0644]
include/linux/amba/clcd.h
include/linux/sync_file.h

index ca02d3e4db91738ac5d87d7e235e0f9de8a56b5e..284e2b14cfbe0a291416973ce429ee4b9aa05300 100644 (file)
@@ -5,7 +5,7 @@ with HDMI output and the HVS (Hardware Video Scaler) for compositing
 display planes.
 
 Required properties for VC4:
-- compatible:  Should be "brcm,bcm2835-vc4"
+- compatible:  Should be "brcm,bcm2835-vc4" or "brcm,cygnus-vc4"
 
 Required properties for Pixel Valve:
 - compatible:  Should be one of "brcm,bcm2835-pixelvalve0",
@@ -54,11 +54,14 @@ Required properties for VEC:
                  See bindings/interrupt-controller/brcm,bcm2835-armctrl-ic.txt
 
 Required properties for V3D:
-- compatible:  Should be "brcm,bcm2835-v3d"
+- compatible:  Should be "brcm,bcm2835-v3d" or "brcm,cygnus-v3d"
 - reg:         Physical base address and length of the V3D's registers
 - interrupts:  The interrupt number
                  See bindings/interrupt-controller/brcm,bcm2835-armctrl-ic.txt
 
+Optional properties for V3D:
+- clocks:      The clock the unit runs on
+
 Required properties for DSI:
 - compatible:  Should be "brcm,bcm2835-dsi0" or "brcm,bcm2835-dsi1"
 - reg:         Physical base address and length of the DSI block's registers
diff --git a/Documentation/devicetree/bindings/display/st,stm32-ltdc.txt b/Documentation/devicetree/bindings/display/st,stm32-ltdc.txt
new file mode 100644 (file)
index 0000000..8e14769
--- /dev/null
@@ -0,0 +1,36 @@
+* STMicroelectronics STM32 lcd-tft display controller
+
+- ltdc: lcd-tft display controller host
+  must be a sub-node of st-display-subsystem
+  Required properties:
+  - compatible: "st,stm32-ltdc"
+  - reg: Physical base address of the IP registers and length of memory mapped region.
+  - clocks: A list of phandle + clock-specifier pairs, one for each
+    entry in 'clock-names'.
+  - clock-names: A list of clock names. For ltdc it should contain:
+      - "lcd" for the clock feeding the output pixel clock & IP clock.
+  - resets: reset to be used by the device (defined by use of RCC macro).
+  Required nodes:
+    - Video port for RGB output.
+
+Example:
+
+/ {
+       ...
+       soc {
+       ...
+               ltdc: display-controller@40016800 {
+                       compatible = "st,stm32-ltdc";
+                       reg = <0x40016800 0x200>;
+                       interrupts = <88>, <89>;
+                       resets = <&rcc STM32F4_APB2_RESET(LTDC)>;
+                       clocks = <&rcc 1 CLK_LCD>;
+                       clock-names = "lcd";
+
+                       port {
+                               ltdc_out_rgb: endpoint {
+                               };
+                       };
+               };
+       };
+};
index 9c356284232b0a6ef98d0a7e8d313ed757a342e5..38476475fd6031e803e0dec2b6094be1416702ad 100644 (file)
@@ -58,6 +58,18 @@ Required properties:
    integer cells.  The first cell is the offset of SYSCTRL register used
    to control TV Encoder DAC power, and the second cell is the bit mask.
 
+* VGA output device
+
+Required properties:
+ - compatible: should be "zte,zx296718-vga"
+ - reg: Physical base address and length of the VGA device IO region
+ - interrupts : VGA interrupt number to CPU
+ - clocks: Phandle with clock-specifier pointing to VGA I2C clock.
+ - clock-names: Must be "i2c_wclk".
+ - zte,vga-power-control: the phandle to SYSCTRL block followed by two
+   integer cells.  The first cell is the offset of SYSCTRL register used
+   to control VGA DAC power, and the second cell is the bit mask.
+
 Example:
 
 vou: vou@1440000 {
@@ -81,6 +93,15 @@ vou: vou@1440000 {
                              "main_wclk", "aux_wclk";
        };
 
+       vga: vga@8000 {
+               compatible = "zte,zx296718-vga";
+               reg = <0x8000 0x1000>;
+               interrupts = <GIC_SPI 86 IRQ_TYPE_LEVEL_HIGH>;
+               clocks = <&topcrm VGA_I2C_WCLK>;
+               clock-names = "i2c_wclk";
+               zte,vga-power-control = <&sysctrl 0x170 0xe0>;
+       };
+
        hdmi: hdmi@c000 {
                compatible = "zte,zx296718-hdmi";
                reg = <0xc000 0x4000>;
index c572f092739e04377aee355f80f914372ef8a67e..037a39ac18076322b3cca8aa949531a538f93942 100644 (file)
@@ -12,6 +12,7 @@ Linux GPU Driver Developer's Guide
    drm-uapi
    i915
    meson
+   pl111
    tinydrm
    vc4
    vga-switcheroo
diff --git a/Documentation/gpu/pl111.rst b/Documentation/gpu/pl111.rst
new file mode 100644 (file)
index 0000000..9b03736
--- /dev/null
@@ -0,0 +1,6 @@
+==========================================
+ drm/pl111 ARM PrimeCell PL111 CLCD Driver
+==========================================
+
+.. kernel-doc:: drivers/gpu/drm/pl111/pl111_drv.c
+   :doc: ARM PrimeCell PL111 CLCD Driver
index f7d568b8f133d9919e3823c102d7ac78f89c894a..81cdd03a6ad0b5ba00010c408c4d0f682e2f5a00 100644 (file)
@@ -4228,6 +4228,12 @@ F:       include/drm/drm*
 F:     include/uapi/drm/drm*
 F:     include/linux/vga*
 
+DRM DRIVER FOR ARM PL111 CLCD
+M:     Eric Anholt <eric@anholt.net>
+T:     git git://anongit.freedesktop.org/drm/drm-misc
+S:     Supported
+F:     drivers/gpu/drm/pl111/
+
 DRM DRIVER FOR AST SERVER GRAPHICS CHIPS
 M:     Dave Airlie <airlied@redhat.com>
 S:     Odd Fixes
@@ -4235,6 +4241,8 @@ F:        drivers/gpu/drm/ast/
 
 DRM DRIVERS FOR BRIDGE CHIPS
 M:     Archit Taneja <architt@codeaurora.org>
+M:     Andrzej Hajda <a.hajda@samsung.com>
+R:     Laurent Pinchart <Laurent.pinchart@ideasonboard.com>
 S:     Maintained
 T:     git git://anongit.freedesktop.org/drm/drm-misc
 F:     drivers/gpu/drm/bridge/
@@ -4491,6 +4499,15 @@ S:       Maintained
 F:     drivers/gpu/drm/sti
 F:     Documentation/devicetree/bindings/display/st,stih4xx.txt
 
+DRM DRIVERS FOR STM
+M:     Yannick Fertre <yannick.fertre@st.com>
+M:     Philippe Cornu <philippe.cornu@st.com>
+L:     dri-devel@lists.freedesktop.org
+T:     git git://anongit.freedesktop.org/drm/drm-misc
+S:     Maintained
+F:     drivers/gpu/drm/stm
+F:     Documentation/devicetree/bindings/display/st,stm32-ltdc.txt
+
 DRM DRIVER FOR TDFX VIDEO CARDS
 S:     Orphan / Obsolete
 F:     drivers/gpu/drm/tdfx/
index 512bdbc23bbb43b85e9da8badbe24bcd64a66872..4a038dcf53612c888cf55ec3668871964b8f8001 100644 (file)
@@ -558,8 +558,8 @@ struct dma_buf_attachment *dma_buf_attach(struct dma_buf *dmabuf,
        if (WARN_ON(!dmabuf || !dev))
                return ERR_PTR(-EINVAL);
 
-       attach = kzalloc(sizeof(struct dma_buf_attachment), GFP_KERNEL);
-       if (attach == NULL)
+       attach = kzalloc(sizeof(*attach), GFP_KERNEL);
+       if (!attach)
                return ERR_PTR(-ENOMEM);
 
        attach->dev = dev;
@@ -1122,9 +1122,7 @@ static int dma_buf_debug_show(struct seq_file *s, void *unused)
                attach_count = 0;
 
                list_for_each_entry(attach_obj, &buf_obj->attachments, node) {
-                       seq_puts(s, "\t");
-
-                       seq_printf(s, "%s\n", dev_name(attach_obj->dev));
+                       seq_printf(s, "\t%s\n", dev_name(attach_obj->dev));
                        attach_count++;
                }
 
index 0918d3f003d65d633a6e06a2c8d41a47fc42f9b5..57da14c15987fc019d1786ec0a4b0bd65077cf99 100644 (file)
@@ -402,6 +402,11 @@ dma_fence_default_wait(struct dma_fence *fence, bool intr, signed long timeout)
                }
        }
 
+       if (!timeout) {
+               ret = 0;
+               goto out;
+       }
+
        cb.base.func = dma_fence_default_wait_cb;
        cb.task = current;
        list_add(&cb.base.node, &fence->cb_list);
index c769dc653b344ac23fe5a99a36b99a31dc2fceba..a0d780ab68c3546dc3c5c06c7c5d4461b218bfdc 100644 (file)
@@ -110,7 +110,7 @@ static void sync_print_fence(struct seq_file *s,
                }
        }
 
-       seq_puts(s, "\n");
+       seq_putc(s, '\n');
 }
 
 static void sync_print_obj(struct seq_file *s, struct sync_timeline *obj)
@@ -161,7 +161,7 @@ static int sync_debugfs_show(struct seq_file *s, void *unused)
                                     sync_timeline_list);
 
                sync_print_obj(s, obj);
-               seq_puts(s, "\n");
+               seq_putc(s, '\n');
        }
        spin_unlock_irqrestore(&sync_timeline_list_lock, flags);
 
@@ -173,7 +173,7 @@ static int sync_debugfs_show(struct seq_file *s, void *unused)
                        container_of(pos, struct sync_file, sync_file_list);
 
                sync_print_sync_file(s, sync_file);
-               seq_puts(s, "\n");
+               seq_putc(s, '\n');
        }
        spin_unlock_irqrestore(&sync_file_list_lock, flags);
        return 0;
index 2321035f6204bc4e0f5c84980becb22bc534e328..dc89b1d484e815761afd033155bb0ae66d8f865d 100644 (file)
@@ -41,8 +41,6 @@ static struct sync_file *sync_file_alloc(void)
        if (IS_ERR(sync_file->file))
                goto err;
 
-       kref_init(&sync_file->kref);
-
        init_waitqueue_head(&sync_file->wq);
 
        INIT_LIST_HEAD(&sync_file->cb.node);
@@ -277,22 +275,15 @@ err:
 
 }
 
-static void sync_file_free(struct kref *kref)
+static int sync_file_release(struct inode *inode, struct file *file)
 {
-       struct sync_file *sync_file = container_of(kref, struct sync_file,
-                                                    kref);
+       struct sync_file *sync_file = file->private_data;
 
        if (test_bit(POLL_ENABLED, &sync_file->fence->flags))
                dma_fence_remove_callback(sync_file->fence, &sync_file->cb);
        dma_fence_put(sync_file->fence);
        kfree(sync_file);
-}
-
-static int sync_file_release(struct inode *inode, struct file *file)
-{
-       struct sync_file *sync_file = file->private_data;
 
-       kref_put(&sync_file->kref, sync_file_free);
        return 0;
 }
 
index 78d7fc0ebb57e69980059d1e01d02c5b988da95e..83cb2a88c204f7c6824396ceb296c7ea0a20577f 100644 (file)
@@ -246,6 +246,8 @@ source "drivers/gpu/drm/fsl-dcu/Kconfig"
 
 source "drivers/gpu/drm/tegra/Kconfig"
 
+source "drivers/gpu/drm/stm/Kconfig"
+
 source "drivers/gpu/drm/panel/Kconfig"
 
 source "drivers/gpu/drm/bridge/Kconfig"
@@ -274,6 +276,8 @@ source "drivers/gpu/drm/meson/Kconfig"
 
 source "drivers/gpu/drm/tinydrm/Kconfig"
 
+source "drivers/gpu/drm/pl111/Kconfig"
+
 # Keep legacy drivers last
 
 menuconfig DRM_LEGACY
index 59f0f9b696eb8f593d1d9d9d5a7bc09d8a1acd8b..c156fecfb3624e80e9bb4b7dc7a3bbc65733a873 100644 (file)
@@ -82,6 +82,7 @@ obj-$(CONFIG_DRM_BOCHS) += bochs/
 obj-$(CONFIG_DRM_VIRTIO_GPU) += virtio/
 obj-$(CONFIG_DRM_MSM) += msm/
 obj-$(CONFIG_DRM_TEGRA) += tegra/
+obj-$(CONFIG_DRM_STM) += stm/
 obj-$(CONFIG_DRM_STI) += sti/
 obj-$(CONFIG_DRM_IMX) += imx/
 obj-$(CONFIG_DRM_MEDIATEK) += mediatek/
@@ -96,3 +97,4 @@ obj-y                 += hisilicon/
 obj-$(CONFIG_DRM_ZTE)  += zte/
 obj-$(CONFIG_DRM_MXSFB)        += mxsfb/
 obj-$(CONFIG_DRM_TINYDRM) += tinydrm/
+obj-$(CONFIG_DRM_PL111) += pl111/
index 833c3c16501a0221da5161eaec189a5576292abc..67cdab9241a4c431f8a3b92af499a0f447644989 100644 (file)
@@ -1912,10 +1912,6 @@ int amdgpu_device_resume(struct drm_device *dev, bool resume, bool fbcon);
 u32 amdgpu_get_vblank_counter_kms(struct drm_device *dev, unsigned int pipe);
 int amdgpu_enable_vblank_kms(struct drm_device *dev, unsigned int pipe);
 void amdgpu_disable_vblank_kms(struct drm_device *dev, unsigned int pipe);
-int amdgpu_get_vblank_timestamp_kms(struct drm_device *dev, unsigned int pipe,
-                                   int *max_error,
-                                   struct timeval *vblank_time,
-                                   unsigned flags);
 long amdgpu_kms_compat_ioctl(struct file *filp, unsigned int cmd,
                             unsigned long arg);
 
index f2d705e6a75aa4f092d3d98ff739927e15b6f26b..5cb8f3e68447f1751f664dcfc90715cc88562ae6 100644 (file)
@@ -715,6 +715,16 @@ static const struct file_operations amdgpu_driver_kms_fops = {
 #endif
 };
 
+static bool
+amdgpu_get_crtc_scanout_position(struct drm_device *dev, unsigned int pipe,
+                                bool in_vblank_irq, int *vpos, int *hpos,
+                                ktime_t *stime, ktime_t *etime,
+                                const struct drm_display_mode *mode)
+{
+       return amdgpu_get_crtc_scanoutpos(dev, pipe, 0, vpos, hpos,
+                                         stime, etime, mode);
+}
+
 static struct drm_driver kms_driver = {
        .driver_features =
            DRIVER_USE_AGP |
@@ -729,8 +739,8 @@ static struct drm_driver kms_driver = {
        .get_vblank_counter = amdgpu_get_vblank_counter_kms,
        .enable_vblank = amdgpu_enable_vblank_kms,
        .disable_vblank = amdgpu_disable_vblank_kms,
-       .get_vblank_timestamp = amdgpu_get_vblank_timestamp_kms,
-       .get_scanout_position = amdgpu_get_crtc_scanoutpos,
+       .get_vblank_timestamp = drm_calc_vbltimestamp_from_scanoutpos,
+       .get_scanout_position = amdgpu_get_crtc_scanout_position,
 #if defined(CONFIG_DEBUG_FS)
        .debugfs_init = amdgpu_debugfs_init,
 #endif
index 96c34167078253b26d1ac0cfbc2f2036c07c487d..dca4be970d1361dce4b5a820f5faa6037897783e 100644 (file)
@@ -945,47 +945,6 @@ void amdgpu_disable_vblank_kms(struct drm_device *dev, unsigned int pipe)
        amdgpu_irq_put(adev, &adev->crtc_irq, idx);
 }
 
-/**
- * amdgpu_get_vblank_timestamp_kms - get vblank timestamp
- *
- * @dev: drm dev pointer
- * @crtc: crtc to get the timestamp for
- * @max_error: max error
- * @vblank_time: time value
- * @flags: flags passed to the driver
- *
- * Gets the timestamp on the requested crtc based on the
- * scanout position.  (all asics).
- * Returns postive status flags on success, negative error on failure.
- */
-int amdgpu_get_vblank_timestamp_kms(struct drm_device *dev, unsigned int pipe,
-                                   int *max_error,
-                                   struct timeval *vblank_time,
-                                   unsigned flags)
-{
-       struct drm_crtc *crtc;
-       struct amdgpu_device *adev = dev->dev_private;
-
-       if (pipe >= dev->num_crtcs) {
-               DRM_ERROR("Invalid crtc %u\n", pipe);
-               return -EINVAL;
-       }
-
-       /* Get associated drm_crtc: */
-       crtc = &adev->mode_info.crtcs[pipe]->base;
-       if (!crtc) {
-               /* This can occur on driver load if some component fails to
-                * initialize completely and driver is unloaded */
-               DRM_ERROR("Uninitialized crtc %d\n", pipe);
-               return -EINVAL;
-       }
-
-       /* Helper routine in DRM core does all the work: */
-       return drm_calc_vbltimestamp_from_scanoutpos(dev, pipe, max_error,
-                                                    vblank_time, flags,
-                                                    &crtc->hwmode);
-}
-
 const struct drm_ioctl_desc amdgpu_ioctls_kms[] = {
        DRM_IOCTL_DEF_DRV(AMDGPU_GEM_CREATE, amdgpu_gem_create_ioctl, DRM_AUTH|DRM_RENDER_ALLOW),
        DRM_IOCTL_DEF_DRV(AMDGPU_CTX, amdgpu_ctx_ioctl, DRM_AUTH|DRM_RENDER_ALLOW),
index dbd10618ec20859ca2699529708c1490354d85c4..43a9d3aec6c426e20bbff9bbb30084fe3cc523b2 100644 (file)
@@ -534,6 +534,9 @@ struct amdgpu_framebuffer {
                                ((em) == ATOM_ENCODER_MODE_DP_MST))
 
 /* Driver internal use only flags of amdgpu_get_crtc_scanoutpos() */
+#define DRM_SCANOUTPOS_VALID        (1 << 0)
+#define DRM_SCANOUTPOS_IN_VBLANK    (1 << 1)
+#define DRM_SCANOUTPOS_ACCURATE     (1 << 2)
 #define USE_REAL_VBLANKSTART           (1 << 30)
 #define GET_DISTANCE_TO_VBLANKSTART    (1 << 31)
 
index 9126d0306ab5240274f0a24d941185bf82617448..9b87067c022cd8f541d94310dd5266ab84cf7c9d 100644 (file)
@@ -160,7 +160,7 @@ static int sii902x_get_modes(struct drm_connector *connector)
                 time_before(jiffies, timeout));
 
        if (!(status & SII902X_SYS_CTRL_DDC_BUS_GRTD)) {
-               dev_err(&sii902x->i2c->dev, "failed to acquire the i2c bus");
+               dev_err(&sii902x->i2c->dev, "failed to acquire the i2c bus\n");
                return -ETIMEDOUT;
        }
 
@@ -202,7 +202,7 @@ static int sii902x_get_modes(struct drm_connector *connector)
 
        if (status & (SII902X_SYS_CTRL_DDC_BUS_REQ |
                      SII902X_SYS_CTRL_DDC_BUS_GRTD)) {
-               dev_err(&sii902x->i2c->dev, "failed to release the i2c bus");
+               dev_err(&sii902x->i2c->dev, "failed to release the i2c bus\n");
                return -ETIMEDOUT;
        }
 
@@ -298,7 +298,7 @@ static int sii902x_bridge_attach(struct drm_bridge *bridge)
 
        if (!drm_core_check_feature(drm, DRIVER_ATOMIC)) {
                dev_err(&sii902x->i2c->dev,
-                       "sii902x driver is only compatible with DRM devices supporting atomic updates");
+                       "sii902x driver is only compatible with DRM devices supporting atomic updates\n");
                return -ENOTSUPP;
        }
 
index 4e1f54a675d8def6c5f3f2ae8e261d802c17ccb2..8737de8c1c52163165d7da32da96e00a66c6fce1 100644 (file)
@@ -173,6 +173,8 @@ struct dw_hdmi {
 
        unsigned int reg_shift;
        struct regmap *regm;
+       void (*enable_audio)(struct dw_hdmi *hdmi);
+       void (*disable_audio)(struct dw_hdmi *hdmi);
 };
 
 #define HDMI_IH_PHY_STAT0_RX_SENSE \
@@ -542,13 +544,41 @@ void dw_hdmi_set_sample_rate(struct dw_hdmi *hdmi, unsigned int rate)
 }
 EXPORT_SYMBOL_GPL(dw_hdmi_set_sample_rate);
 
+static void hdmi_enable_audio_clk(struct dw_hdmi *hdmi, bool enable)
+{
+       hdmi_modb(hdmi, enable ? 0 : HDMI_MC_CLKDIS_AUDCLK_DISABLE,
+                 HDMI_MC_CLKDIS_AUDCLK_DISABLE, HDMI_MC_CLKDIS);
+}
+
+static void dw_hdmi_ahb_audio_enable(struct dw_hdmi *hdmi)
+{
+       hdmi_set_cts_n(hdmi, hdmi->audio_cts, hdmi->audio_n);
+}
+
+static void dw_hdmi_ahb_audio_disable(struct dw_hdmi *hdmi)
+{
+       hdmi_set_cts_n(hdmi, hdmi->audio_cts, 0);
+}
+
+static void dw_hdmi_i2s_audio_enable(struct dw_hdmi *hdmi)
+{
+       hdmi_set_cts_n(hdmi, hdmi->audio_cts, hdmi->audio_n);
+       hdmi_enable_audio_clk(hdmi, true);
+}
+
+static void dw_hdmi_i2s_audio_disable(struct dw_hdmi *hdmi)
+{
+       hdmi_enable_audio_clk(hdmi, false);
+}
+
 void dw_hdmi_audio_enable(struct dw_hdmi *hdmi)
 {
        unsigned long flags;
 
        spin_lock_irqsave(&hdmi->audio_lock, flags);
        hdmi->audio_enable = true;
-       hdmi_set_cts_n(hdmi, hdmi->audio_cts, hdmi->audio_n);
+       if (hdmi->enable_audio)
+               hdmi->enable_audio(hdmi);
        spin_unlock_irqrestore(&hdmi->audio_lock, flags);
 }
 EXPORT_SYMBOL_GPL(dw_hdmi_audio_enable);
@@ -559,7 +589,8 @@ void dw_hdmi_audio_disable(struct dw_hdmi *hdmi)
 
        spin_lock_irqsave(&hdmi->audio_lock, flags);
        hdmi->audio_enable = false;
-       hdmi_set_cts_n(hdmi, hdmi->audio_cts, 0);
+       if (hdmi->disable_audio)
+               hdmi->disable_audio(hdmi);
        spin_unlock_irqrestore(&hdmi->audio_lock, flags);
 }
 EXPORT_SYMBOL_GPL(dw_hdmi_audio_disable);
@@ -1573,11 +1604,6 @@ static void dw_hdmi_enable_video_path(struct dw_hdmi *hdmi)
                            HDMI_MC_FLOWCTRL);
 }
 
-static void hdmi_enable_audio_clk(struct dw_hdmi *hdmi)
-{
-       hdmi_modb(hdmi, 0, HDMI_MC_CLKDIS_AUDCLK_DISABLE, HDMI_MC_CLKDIS);
-}
-
 /* Workaround to clear the overflow condition */
 static void dw_hdmi_clear_overflow(struct dw_hdmi *hdmi)
 {
@@ -1691,7 +1717,7 @@ static int dw_hdmi_setup(struct dw_hdmi *hdmi, struct drm_display_mode *mode)
 
                /* HDMI Initialization Step E - Configure audio */
                hdmi_clk_regenerator_update_pixel_clock(hdmi);
-               hdmi_enable_audio_clk(hdmi);
+               hdmi_enable_audio_clk(hdmi, true);
        }
 
        /* not for DVI mode */
@@ -2403,6 +2429,8 @@ __dw_hdmi_probe(struct platform_device *pdev,
                audio.irq = irq;
                audio.hdmi = hdmi;
                audio.eld = hdmi->connector.eld;
+               hdmi->enable_audio = dw_hdmi_ahb_audio_enable;
+               hdmi->disable_audio = dw_hdmi_ahb_audio_disable;
 
                pdevinfo.name = "dw-hdmi-ahb-audio";
                pdevinfo.data = &audio;
@@ -2415,6 +2443,8 @@ __dw_hdmi_probe(struct platform_device *pdev,
                audio.hdmi      = hdmi;
                audio.write     = hdmi_writeb;
                audio.read      = hdmi_readb;
+               hdmi->enable_audio = dw_hdmi_i2s_audio_enable;
+               hdmi->disable_audio = dw_hdmi_i2s_audio_disable;
 
                pdevinfo.name = "dw-hdmi-i2s-audio";
                pdevinfo.data = &audio;
index f32506a7c1d61e91494e47f6a731fddaf1cc991c..cdec19a86af374c93cf028ad13e0e6276aa2665f 100644 (file)
@@ -57,6 +57,7 @@ void drm_atomic_state_default_release(struct drm_atomic_state *state)
        kfree(state->connectors);
        kfree(state->crtcs);
        kfree(state->planes);
+       kfree(state->private_objs);
 }
 EXPORT_SYMBOL(drm_atomic_state_default_release);
 
@@ -184,6 +185,17 @@ void drm_atomic_state_default_clear(struct drm_atomic_state *state)
                state->planes[i].ptr = NULL;
                state->planes[i].state = NULL;
        }
+
+       for (i = 0; i < state->num_private_objs; i++) {
+               void *obj_state = state->private_objs[i].obj_state;
+
+               state->private_objs[i].funcs->destroy_state(obj_state);
+               state->private_objs[i].obj = NULL;
+               state->private_objs[i].obj_state = NULL;
+               state->private_objs[i].funcs = NULL;
+       }
+       state->num_private_objs = 0;
+
 }
 EXPORT_SYMBOL(drm_atomic_state_default_clear);
 
@@ -425,7 +437,7 @@ drm_atomic_replace_property_blob(struct drm_property_blob **blob,
 }
 
 static int
-drm_atomic_replace_property_blob_from_id(struct drm_crtc *crtc,
+drm_atomic_replace_property_blob_from_id(struct drm_device *dev,
                                         struct drm_property_blob **blob,
                                         uint64_t blob_id,
                                         ssize_t expected_size,
@@ -434,7 +446,7 @@ drm_atomic_replace_property_blob_from_id(struct drm_crtc *crtc,
        struct drm_property_blob *new_blob = NULL;
 
        if (blob_id != 0) {
-               new_blob = drm_property_lookup_blob(crtc->dev, blob_id);
+               new_blob = drm_property_lookup_blob(dev, blob_id);
                if (new_blob == NULL)
                        return -EINVAL;
 
@@ -483,7 +495,7 @@ int drm_atomic_crtc_set_property(struct drm_crtc *crtc,
                drm_property_blob_put(mode);
                return ret;
        } else if (property == config->degamma_lut_property) {
-               ret = drm_atomic_replace_property_blob_from_id(crtc,
+               ret = drm_atomic_replace_property_blob_from_id(dev,
                                        &state->degamma_lut,
                                        val,
                                        -1,
@@ -491,7 +503,7 @@ int drm_atomic_crtc_set_property(struct drm_crtc *crtc,
                state->color_mgmt_changed |= replaced;
                return ret;
        } else if (property == config->ctm_property) {
-               ret = drm_atomic_replace_property_blob_from_id(crtc,
+               ret = drm_atomic_replace_property_blob_from_id(dev,
                                        &state->ctm,
                                        val,
                                        sizeof(struct drm_color_ctm),
@@ -499,7 +511,7 @@ int drm_atomic_crtc_set_property(struct drm_crtc *crtc,
                state->color_mgmt_changed |= replaced;
                return ret;
        } else if (property == config->gamma_lut_property) {
-               ret = drm_atomic_replace_property_blob_from_id(crtc,
+               ret = drm_atomic_replace_property_blob_from_id(dev,
                                        &state->gamma_lut,
                                        val,
                                        -1,
@@ -977,6 +989,59 @@ static void drm_atomic_plane_print_state(struct drm_printer *p,
                plane->funcs->atomic_print_state(p, state);
 }
 
+/**
+ * drm_atomic_get_private_obj_state - get private object state
+ * @state: global atomic state
+ * @obj: private object to get the state for
+ * @funcs: pointer to the struct of function pointers that identify the object
+ * type
+ *
+ * This function returns the private object state for the given private object,
+ * allocating the state if needed. It does not grab any locks as the caller is
+ * expected to care of any required locking.
+ *
+ * RETURNS:
+ *
+ * Either the allocated state or the error code encoded into a pointer.
+ */
+void *
+drm_atomic_get_private_obj_state(struct drm_atomic_state *state, void *obj,
+                             const struct drm_private_state_funcs *funcs)
+{
+       int index, num_objs, i;
+       size_t size;
+       struct __drm_private_objs_state *arr;
+
+       for (i = 0; i < state->num_private_objs; i++)
+               if (obj == state->private_objs[i].obj &&
+                   state->private_objs[i].obj_state)
+                       return state->private_objs[i].obj_state;
+
+       num_objs = state->num_private_objs + 1;
+       size = sizeof(*state->private_objs) * num_objs;
+       arr = krealloc(state->private_objs, size, GFP_KERNEL);
+       if (!arr)
+               return ERR_PTR(-ENOMEM);
+
+       state->private_objs = arr;
+       index = state->num_private_objs;
+       memset(&state->private_objs[index], 0, sizeof(*state->private_objs));
+
+       state->private_objs[index].obj_state = funcs->duplicate_state(state, obj);
+       if (!state->private_objs[index].obj_state)
+               return ERR_PTR(-ENOMEM);
+
+       state->private_objs[index].obj = obj;
+       state->private_objs[index].funcs = funcs;
+       state->num_private_objs = num_objs;
+
+       DRM_DEBUG_ATOMIC("Added new private object state %p to %p\n",
+                        state->private_objs[index].obj_state, state);
+
+       return state->private_objs[index].obj_state;
+}
+EXPORT_SYMBOL(drm_atomic_get_private_obj_state);
+
 /**
  * drm_atomic_get_connector_state - get connector state
  * @state: global atomic state object
@@ -1123,6 +1188,10 @@ int drm_atomic_connector_set_property(struct drm_connector *connector,
                 */
                if (state->link_status != DRM_LINK_STATUS_GOOD)
                        state->link_status = val;
+       } else if (property == config->aspect_ratio_property) {
+               state->picture_aspect_ratio = val;
+       } else if (property == connector->scaling_mode_property) {
+               state->scaling_mode = val;
        } else if (connector->funcs->atomic_set_property) {
                return connector->funcs->atomic_set_property(connector,
                                state, property, val);
@@ -1199,6 +1268,10 @@ drm_atomic_connector_get_property(struct drm_connector *connector,
                *val = state->tv.hue;
        } else if (property == config->link_status_property) {
                *val = state->link_status;
+       } else if (property == config->aspect_ratio_property) {
+               *val = state->picture_aspect_ratio;
+       } else if (property == connector->scaling_mode_property) {
+               *val = state->scaling_mode;
        } else if (connector->funcs->atomic_get_property) {
                return connector->funcs->atomic_get_property(connector,
                                state, property, val);
@@ -1618,7 +1691,7 @@ int drm_atomic_commit(struct drm_atomic_state *state)
        if (ret)
                return ret;
 
-       DRM_DEBUG_ATOMIC("commiting %p\n", state);
+       DRM_DEBUG_ATOMIC("committing %p\n", state);
 
        return config->funcs->atomic_commit(state->dev, state, false);
 }
@@ -1647,7 +1720,7 @@ int drm_atomic_nonblocking_commit(struct drm_atomic_state *state)
        if (ret)
                return ret;
 
-       DRM_DEBUG_ATOMIC("commiting %p nonblocking\n", state);
+       DRM_DEBUG_ATOMIC("committing %p nonblocking\n", state);
 
        return config->funcs->atomic_commit(state->dev, state, true);
 }
index 8be9719284b047f3d6046fd6a22c80bb617fa0f7..6426339427a43a00a818faaa395ab5a385d34e87 100644 (file)
@@ -1070,8 +1070,8 @@ EXPORT_SYMBOL(drm_atomic_helper_commit_modeset_enables);
  *
  * Note that @pre_swap is needed since the point where we block for fences moves
  * around depending upon whether an atomic commit is blocking or
- * non-blocking. For async commit all waiting needs to happen after
- * drm_atomic_helper_swap_state() is called, but for synchronous commits we want
+ * non-blocking. For non-blocking commit all waiting needs to happen after
+ * drm_atomic_helper_swap_state() is called, but for blocking commits we want
  * to wait **before** we do anything that can't be easily rolled back. That is
  * before we call drm_atomic_helper_swap_state().
  *
@@ -2032,6 +2032,8 @@ void drm_atomic_helper_swap_state(struct drm_atomic_state *state,
        struct drm_plane *plane;
        struct drm_plane_state *old_plane_state, *new_plane_state;
        struct drm_crtc_commit *commit;
+       void *obj, *obj_state;
+       const struct drm_private_state_funcs *funcs;
 
        if (stall) {
                for_each_new_crtc_in_state(state, crtc, new_crtc_state, i) {
@@ -2092,6 +2094,9 @@ void drm_atomic_helper_swap_state(struct drm_atomic_state *state,
                state->planes[i].state = old_plane_state;
                plane->state = new_plane_state;
        }
+
+       __for_each_private_obj(state, obj, obj_state, i, funcs)
+               funcs->swap_state(obj, &state->private_objs[i].obj_state);
 }
 EXPORT_SYMBOL(drm_atomic_helper_swap_state);
 
@@ -3517,7 +3522,8 @@ EXPORT_SYMBOL(drm_atomic_helper_connector_destroy_state);
  *
  * Implements support for legacy gamma correction table for drivers
  * that support color management through the DEGAMMA_LUT/GAMMA_LUT
- * properties.
+ * properties. See drm_crtc_enable_color_mgmt() and the containing chapter for
+ * how the atomic color management and gamma tables work.
  */
 int drm_atomic_helper_legacy_gamma_set(struct drm_crtc *crtc,
                                       u16 *red, u16 *green, u16 *blue,
index 533f3a3e687779499674249c1976a58df0a2d3dd..3eda500fc005ea485b4c5ed52bc19bead9a23814 100644 (file)
@@ -43,7 +43,8 @@
  *
  *     Setting this to NULL (blob property value set to 0) means a
  *     linear/pass-thru gamma table should be used. This is generally the
- *     driver boot-up state too.
+ *     driver boot-up state too. Drivers can access this blob through
+ *     &drm_crtc_state.degamma_lut.
  *
  * “DEGAMMA_LUT_SIZE”:
  *     Unsinged range property to give the size of the lookup table to be set
@@ -60,7 +61,8 @@
  *
  *     Setting this to NULL (blob property value set to 0) means a
  *     unit/pass-thru matrix should be used. This is generally the driver
- *     boot-up state too.
+ *     boot-up state too. Drivers can access the blob for the color conversion
+ *     matrix through &drm_crtc_state.ctm.
  *
  * “GAMMA_LUT”:
  *     Blob property to set the gamma lookup table (LUT) mapping pixel data
@@ -72,7 +74,8 @@
  *
  *     Setting this to NULL (blob property value set to 0) means a
  *     linear/pass-thru gamma table should be used. This is generally the
- *     driver boot-up state too.
+ *     driver boot-up state too. Drivers can access this blob through
+ *     &drm_crtc_state.gamma_lut.
  *
  * “GAMMA_LUT_SIZE”:
  *     Unsigned range property to give the size of the lookup table to be set
index 9f847615ac74ab012f6203a141627a5c6f5993e2..5cd61aff785767a0db6a0dafe517eee343442373 100644 (file)
@@ -941,6 +941,10 @@ EXPORT_SYMBOL(drm_mode_create_tv_properties);
  *
  * Called by a driver the first time it's needed, must be attached to desired
  * connectors.
+ *
+ * Atomic drivers should use drm_connector_attach_scaling_mode_property()
+ * instead to correctly assign &drm_connector_state.picture_aspect_ratio
+ * in the atomic state.
  */
 int drm_mode_create_scaling_mode_property(struct drm_device *dev)
 {
@@ -960,6 +964,66 @@ int drm_mode_create_scaling_mode_property(struct drm_device *dev)
 }
 EXPORT_SYMBOL(drm_mode_create_scaling_mode_property);
 
+/**
+ * drm_connector_attach_scaling_mode_property - attach atomic scaling mode property
+ * @connector: connector to attach scaling mode property on.
+ * @scaling_mode_mask: or'ed mask of BIT(%DRM_MODE_SCALE_\*).
+ *
+ * This is used to add support for scaling mode to atomic drivers.
+ * The scaling mode will be set to &drm_connector_state.picture_aspect_ratio
+ * and can be used from &drm_connector_helper_funcs->atomic_check for validation.
+ *
+ * This is the atomic version of drm_mode_create_scaling_mode_property().
+ *
+ * Returns:
+ * Zero on success, negative errno on failure.
+ */
+int drm_connector_attach_scaling_mode_property(struct drm_connector *connector,
+                                              u32 scaling_mode_mask)
+{
+       struct drm_device *dev = connector->dev;
+       struct drm_property *scaling_mode_property;
+       int i, j = 0;
+       const unsigned valid_scaling_mode_mask =
+               (1U << ARRAY_SIZE(drm_scaling_mode_enum_list)) - 1;
+
+       if (WARN_ON(hweight32(scaling_mode_mask) < 2 ||
+                   scaling_mode_mask & ~valid_scaling_mode_mask))
+               return -EINVAL;
+
+       scaling_mode_property =
+               drm_property_create(dev, DRM_MODE_PROP_ENUM, "scaling mode",
+                                   hweight32(scaling_mode_mask));
+
+       if (!scaling_mode_property)
+               return -ENOMEM;
+
+       for (i = 0; i < ARRAY_SIZE(drm_scaling_mode_enum_list); i++) {
+               int ret;
+
+               if (!(BIT(i) & scaling_mode_mask))
+                       continue;
+
+               ret = drm_property_add_enum(scaling_mode_property, j++,
+                                           drm_scaling_mode_enum_list[i].type,
+                                           drm_scaling_mode_enum_list[i].name);
+
+               if (ret) {
+                       drm_property_destroy(dev, scaling_mode_property);
+
+                       return ret;
+               }
+       }
+
+       drm_object_attach_property(&connector->base,
+                                  scaling_mode_property, 0);
+
+       connector->scaling_mode_property = scaling_mode_property;
+
+       return 0;
+}
+EXPORT_SYMBOL(drm_connector_attach_scaling_mode_property);
+
 /**
  * drm_mode_create_aspect_ratio_property - create aspect ratio property
  * @dev: DRM device
index d3fc7e4e85b7413eb586256540467cbd25f6090d..222eb1a8549b0182c70a58aca6f530e237814ec1 100644 (file)
@@ -737,16 +737,16 @@ static void drm_dp_mst_put_payload_id(struct drm_dp_mst_topology_mgr *mgr,
 static bool check_txmsg_state(struct drm_dp_mst_topology_mgr *mgr,
                              struct drm_dp_sideband_msg_tx *txmsg)
 {
-       bool ret;
+       unsigned int state;
 
        /*
         * All updates to txmsg->state are protected by mgr->qlock, and the two
         * cases we check here are terminal states. For those the barriers
         * provided by the wake_up/wait_event pair are enough.
         */
-       ret = (txmsg->state == DRM_DP_SIDEBAND_TX_RX ||
-              txmsg->state == DRM_DP_SIDEBAND_TX_TIMEOUT);
-       return ret;
+       state = READ_ONCE(txmsg->state);
+       return (state == DRM_DP_SIDEBAND_TX_RX ||
+               state == DRM_DP_SIDEBAND_TX_TIMEOUT);
 }
 
 static int drm_dp_mst_wait_tx_reply(struct drm_dp_mst_branch *mstb,
@@ -855,7 +855,7 @@ static void drm_dp_destroy_mst_branch_device(struct kref *kref)
        mutex_unlock(&mstb->mgr->qlock);
 
        if (wake_tx)
-               wake_up(&mstb->mgr->tx_waitq);
+               wake_up_all(&mstb->mgr->tx_waitq);
 
        kref_put(kref, drm_dp_free_mst_branch_device);
 }
@@ -1510,7 +1510,7 @@ static void process_single_down_tx_qlock(struct drm_dp_mst_topology_mgr *mgr)
                if (txmsg->seqno != -1)
                        txmsg->dst->tx_slots[txmsg->seqno] = NULL;
                txmsg->state = DRM_DP_SIDEBAND_TX_TIMEOUT;
-               wake_up(&mgr->tx_waitq);
+               wake_up_all(&mgr->tx_waitq);
        }
 }
 
@@ -2258,7 +2258,7 @@ static int drm_dp_mst_handle_down_rep(struct drm_dp_mst_topology_mgr *mgr)
                mstb->tx_slots[slot] = NULL;
                mutex_unlock(&mgr->qlock);
 
-               wake_up(&mgr->tx_waitq);
+               wake_up_all(&mgr->tx_waitq);
        }
        return ret;
 }
@@ -2497,6 +2497,81 @@ static int drm_dp_init_vcpi(struct drm_dp_mst_topology_mgr *mgr,
        return 0;
 }
 
+/**
+ * drm_dp_atomic_find_vcpi_slots() - Find and add vcpi slots to the state
+ * @state: global atomic state
+ * @mgr: MST topology manager for the port
+ * @port: port to find vcpi slots for
+ * @pbn: bandwidth required for the mode in PBN
+ *
+ * RETURNS:
+ * Total slots in the atomic state assigned for this port or error
+ */
+int drm_dp_atomic_find_vcpi_slots(struct drm_atomic_state *state,
+                                 struct drm_dp_mst_topology_mgr *mgr,
+                                 struct drm_dp_mst_port *port, int pbn)
+{
+       struct drm_dp_mst_topology_state *topology_state;
+       int req_slots;
+
+       topology_state = drm_atomic_get_mst_topology_state(state, mgr);
+       if (topology_state == NULL)
+               return -ENOMEM;
+
+       port = drm_dp_get_validated_port_ref(mgr, port);
+       if (port == NULL)
+               return -EINVAL;
+       req_slots = DIV_ROUND_UP(pbn, mgr->pbn_div);
+       DRM_DEBUG_KMS("vcpi slots req=%d, avail=%d\n",
+                       req_slots, topology_state->avail_slots);
+
+       if (req_slots > topology_state->avail_slots) {
+               drm_dp_put_port(port);
+               return -ENOSPC;
+       }
+
+       topology_state->avail_slots -= req_slots;
+       DRM_DEBUG_KMS("vcpi slots avail=%d", topology_state->avail_slots);
+
+       drm_dp_put_port(port);
+       return req_slots;
+}
+EXPORT_SYMBOL(drm_dp_atomic_find_vcpi_slots);
+
+/**
+ * drm_dp_atomic_release_vcpi_slots() - Release allocated vcpi slots
+ * @state: global atomic state
+ * @mgr: MST topology manager for the port
+ * @slots: number of vcpi slots to release
+ *
+ * RETURNS:
+ * 0 if @slots were added back to &drm_dp_mst_topology_state->avail_slots or
+ * negative error code
+ */
+int drm_dp_atomic_release_vcpi_slots(struct drm_atomic_state *state,
+                                    struct drm_dp_mst_topology_mgr *mgr,
+                                    int slots)
+{
+       struct drm_dp_mst_topology_state *topology_state;
+
+       topology_state = drm_atomic_get_mst_topology_state(state, mgr);
+       if (topology_state == NULL)
+               return -ENOMEM;
+
+       /* We cannot rely on port->vcpi.num_slots to update
+        * topology_state->avail_slots as the port may not exist if the parent
+        * branch device was unplugged. This should be fixed by tracking
+        * per-port slot allocation in drm_dp_mst_topology_state instead of
+        * depending on the caller to tell us how many slots to release.
+        */
+       topology_state->avail_slots += slots;
+       DRM_DEBUG_KMS("vcpi slots released=%d, avail=%d\n",
+                       slots, topology_state->avail_slots);
+
+       return 0;
+}
+EXPORT_SYMBOL(drm_dp_atomic_release_vcpi_slots);
+
 /**
  * drm_dp_mst_allocate_vcpi() - Allocate a virtual channel
  * @mgr: manager for this port
@@ -2936,6 +3011,69 @@ static void drm_dp_destroy_connector_work(struct work_struct *work)
                (*mgr->cbs->hotplug)(mgr);
 }
 
+void *drm_dp_mst_duplicate_state(struct drm_atomic_state *state, void *obj)
+{
+       struct drm_dp_mst_topology_mgr *mgr = obj;
+       struct drm_dp_mst_topology_state *new_mst_state;
+
+       if (WARN_ON(!mgr->state))
+               return NULL;
+
+       new_mst_state = kmemdup(mgr->state, sizeof(*new_mst_state), GFP_KERNEL);
+       if (new_mst_state)
+               new_mst_state->state = state;
+       return new_mst_state;
+}
+
+void drm_dp_mst_swap_state(void *obj, void **obj_state_ptr)
+{
+       struct drm_dp_mst_topology_mgr *mgr = obj;
+       struct drm_dp_mst_topology_state **topology_state_ptr;
+
+       topology_state_ptr = (struct drm_dp_mst_topology_state **)obj_state_ptr;
+
+       mgr->state->state = (*topology_state_ptr)->state;
+       swap(*topology_state_ptr, mgr->state);
+       mgr->state->state = NULL;
+}
+
+void drm_dp_mst_destroy_state(void *obj_state)
+{
+       kfree(obj_state);
+}
+
+static const struct drm_private_state_funcs mst_state_funcs = {
+       .duplicate_state = drm_dp_mst_duplicate_state,
+       .swap_state = drm_dp_mst_swap_state,
+       .destroy_state = drm_dp_mst_destroy_state,
+};
+
+/**
+ * drm_atomic_get_mst_topology_state: get MST topology state
+ *
+ * @state: global atomic state
+ * @mgr: MST topology manager, also the private object in this case
+ *
+ * This function wraps drm_atomic_get_priv_obj_state() passing in the MST atomic
+ * state vtable so that the private object state returned is that of a MST
+ * topology object. Also, drm_atomic_get_private_obj_state() expects the caller
+ * to care of the locking, so warn if don't hold the connection_mutex.
+ *
+ * RETURNS:
+ *
+ * The MST topology state or error pointer.
+ */
+struct drm_dp_mst_topology_state *drm_atomic_get_mst_topology_state(struct drm_atomic_state *state,
+                                                                   struct drm_dp_mst_topology_mgr *mgr)
+{
+       struct drm_device *dev = mgr->dev;
+
+       WARN_ON(!drm_modeset_is_locked(&dev->mode_config.connection_mutex));
+       return drm_atomic_get_private_obj_state(state, mgr,
+                                               &mst_state_funcs);
+}
+EXPORT_SYMBOL(drm_atomic_get_mst_topology_state);
+
 /**
  * drm_dp_mst_topology_mgr_init - initialise a topology manager
  * @mgr: manager struct to initialise
@@ -2980,6 +3118,15 @@ int drm_dp_mst_topology_mgr_init(struct drm_dp_mst_topology_mgr *mgr,
        if (test_calc_pbn_mode() < 0)
                DRM_ERROR("MST PBN self-test failed\n");
 
+       mgr->state = kzalloc(sizeof(*mgr->state), GFP_KERNEL);
+       if (mgr->state == NULL)
+               return -ENOMEM;
+       mgr->state->mgr = mgr;
+
+       /* max. time slots - one slot for MTP header */
+       mgr->state->avail_slots = 63;
+       mgr->funcs = &mst_state_funcs;
+
        return 0;
 }
 EXPORT_SYMBOL(drm_dp_mst_topology_mgr_init);
@@ -3000,6 +3147,9 @@ void drm_dp_mst_topology_mgr_destroy(struct drm_dp_mst_topology_mgr *mgr)
        mutex_unlock(&mgr->payload_lock);
        mgr->dev = NULL;
        mgr->aux = NULL;
+       kfree(mgr->state);
+       mgr->state = NULL;
+       mgr->funcs = NULL;
 }
 EXPORT_SYMBOL(drm_dp_mst_topology_mgr_destroy);
 
index 50abd1faf38f08681673cc09f2b290c6cf0d1aa3..53f9bdf470d77eed4f0c937ce98421cc8eacc043 100644 (file)
@@ -189,7 +189,7 @@ struct drm_framebuffer *drm_fb_cma_create_with_funcs(struct drm_device *dev,
                obj = drm_gem_object_lookup(file_priv, mode_cmd->handles[i]);
                if (!obj) {
                        dev_err(dev->dev, "Failed to lookup GEM object\n");
-                       ret = -ENXIO;
+                       ret = -ENOENT;
                        goto err_gem_object_put;
                }
 
@@ -259,6 +259,33 @@ struct drm_gem_cma_object *drm_fb_cma_get_gem_obj(struct drm_framebuffer *fb,
 }
 EXPORT_SYMBOL_GPL(drm_fb_cma_get_gem_obj);
 
+/**
+ * drm_fb_cma_get_gem_addr() - Get physical address for framebuffer
+ * @fb: The framebuffer
+ * @state: Which state of drm plane
+ * @plane: Which plane
+ * Return the CMA GEM address for given framebuffer.
+ *
+ * This function will usually be called from the PLANE callback functions.
+ */
+dma_addr_t drm_fb_cma_get_gem_addr(struct drm_framebuffer *fb,
+                                  struct drm_plane_state *state,
+                                  unsigned int plane)
+{
+       struct drm_fb_cma *fb_cma = to_fb_cma(fb);
+       dma_addr_t paddr;
+
+       if (plane >= 4)
+               return 0;
+
+       paddr = fb_cma->obj[plane]->paddr + fb->offsets[plane];
+       paddr += fb->format->cpp[plane] * (state->src_x >> 16);
+       paddr += fb->pitches[plane] * (state->src_y >> 16);
+
+       return paddr;
+}
+EXPORT_SYMBOL_GPL(drm_fb_cma_get_gem_addr);
+
 /**
  * drm_fb_cma_prepare_fb() - Prepare CMA framebuffer
  * @plane: Which plane
index 3783b659cd38a6b87f5418d8e2e6dccdb3e38800..caad93dab54bb3c72c9105af2025220aea47e1b1 100644 (file)
@@ -351,9 +351,8 @@ void drm_lastclose(struct drm_device * dev)
  *
  * This function must be used by drivers as their &file_operations.release
  * method. It frees any resources associated with the open file, and calls the
- * &drm_driver.preclose and &drm_driver.lastclose driver callbacks. If this is
- * the last open file for the DRM device also proceeds to call the
- * &drm_driver.lastclose driver callback.
+ * &drm_driver.postclose driver callback. If this is the last open file for the
+ * DRM device also proceeds to call the &drm_driver.lastclose driver callback.
  *
  * RETURNS:
  *
@@ -373,7 +372,8 @@ int drm_release(struct inode *inode, struct file *filp)
        list_del(&file_priv->lhead);
        mutex_unlock(&dev->filelist_mutex);
 
-       if (dev->driver->preclose)
+       if (drm_core_check_feature(dev, DRIVER_LEGACY) &&
+           dev->driver->preclose)
                dev->driver->preclose(dev, file_priv);
 
        /* ========================================================
index 8c866cac62dd27740823532ff87c192ecd6a47fa..c7debaad67f87afb9d6d620258afc6bb6c0f4823 100644 (file)
@@ -54,7 +54,7 @@
 
 static bool
 drm_get_last_vbltimestamp(struct drm_device *dev, unsigned int pipe,
-                         struct timeval *tvblank, unsigned flags);
+                         struct timeval *tvblank, bool in_vblank_irq);
 
 static unsigned int drm_timestamp_precision = 20;  /* Default to 20 usecs. */
 
@@ -138,7 +138,7 @@ static void drm_reset_vblank_timestamp(struct drm_device *dev, unsigned int pipe
         */
        do {
                cur_vblank = __get_vblank_counter(dev, pipe);
-               rc = drm_get_last_vbltimestamp(dev, pipe, &t_vblank, 0);
+               rc = drm_get_last_vbltimestamp(dev, pipe, &t_vblank, false);
        } while (cur_vblank != __get_vblank_counter(dev, pipe) && --count > 0);
 
        /*
@@ -171,7 +171,7 @@ static void drm_reset_vblank_timestamp(struct drm_device *dev, unsigned int pipe
  * device vblank fields.
  */
 static void drm_update_vblank_count(struct drm_device *dev, unsigned int pipe,
-                                   unsigned long flags)
+                                   bool in_vblank_irq)
 {
        struct drm_vblank_crtc *vblank = &dev->vblank[pipe];
        u32 cur_vblank, diff;
@@ -194,7 +194,7 @@ static void drm_update_vblank_count(struct drm_device *dev, unsigned int pipe,
         */
        do {
                cur_vblank = __get_vblank_counter(dev, pipe);
-               rc = drm_get_last_vbltimestamp(dev, pipe, &t_vblank, flags);
+               rc = drm_get_last_vbltimestamp(dev, pipe, &t_vblank, in_vblank_irq);
        } while (cur_vblank != __get_vblank_counter(dev, pipe) && --count > 0);
 
        if (dev->max_vblank_count != 0) {
@@ -214,13 +214,13 @@ static void drm_update_vblank_count(struct drm_device *dev, unsigned int pipe,
                 */
                diff = DIV_ROUND_CLOSEST_ULL(diff_ns, framedur_ns);
 
-               if (diff == 0 && flags & DRM_CALLED_FROM_VBLIRQ)
+               if (diff == 0 && in_vblank_irq)
                        DRM_DEBUG_VBL("crtc %u: Redundant vblirq ignored."
                                      " diff_ns = %lld, framedur_ns = %d)\n",
                                      pipe, (long long) diff_ns, framedur_ns);
        } else {
                /* some kind of default for drivers w/o accurate vbl timestamping */
-               diff = (flags & DRM_CALLED_FROM_VBLIRQ) != 0;
+               diff = in_vblank_irq ? 1 : 0;
        }
 
        /*
@@ -253,7 +253,7 @@ static void drm_update_vblank_count(struct drm_device *dev, unsigned int pipe,
         * Otherwise reinitialize delayed at next vblank interrupt and assign 0
         * for now, to mark the vblanktimestamp as invalid.
         */
-       if (!rc && (flags & DRM_CALLED_FROM_VBLIRQ) == 0)
+       if (!rc && in_vblank_irq)
                t_vblank = (struct timeval) {0, 0};
 
        store_vblank(dev, pipe, diff, &t_vblank, cur_vblank);
@@ -291,7 +291,7 @@ u32 drm_accurate_vblank_count(struct drm_crtc *crtc)
 
        spin_lock_irqsave(&dev->vblank_time_lock, flags);
 
-       drm_update_vblank_count(dev, pipe, 0);
+       drm_update_vblank_count(dev, pipe, false);
        vblank = drm_vblank_count(dev, pipe);
 
        spin_unlock_irqrestore(&dev->vblank_time_lock, flags);
@@ -349,7 +349,7 @@ static void vblank_disable_and_save(struct drm_device *dev, unsigned int pipe)
         * this time. This makes the count account for the entire time
         * between drm_crtc_vblank_on() and drm_crtc_vblank_off().
         */
-       drm_update_vblank_count(dev, pipe, 0);
+       drm_update_vblank_count(dev, pipe, false);
 
        spin_unlock_irqrestore(&dev->vblank_time_lock, irqflags);
 }
@@ -684,6 +684,7 @@ void drm_calc_timestamping_constants(struct drm_crtc *crtc,
 
        vblank->linedur_ns  = linedur_ns;
        vblank->framedur_ns = framedur_ns;
+       vblank->hwmode = *mode;
 
        DRM_DEBUG("crtc %u: hwmode: htotal %d, vtotal %d, vdisplay %d\n",
                  crtc->base.id, mode->crtc_htotal,
@@ -700,10 +701,10 @@ EXPORT_SYMBOL(drm_calc_timestamping_constants);
  * @max_error: Desired maximum allowable error in timestamps (nanosecs)
  *             On return contains true maximum error of timestamp
  * @vblank_time: Pointer to struct timeval which should receive the timestamp
- * @flags: Flags to pass to driver:
- *         0 = Default,
- *         DRM_CALLED_FROM_VBLIRQ = If function is called from vbl IRQ handler
- * @mode: mode which defines the scanout timings
+ * @in_vblank_irq:
+ *     True when called from drm_crtc_handle_vblank().  Some drivers
+ *     need to apply some workarounds for gpu-specific vblank irq quirks
+ *     if flag is set.
  *
  * Implements calculation of exact vblank timestamps from given drm_display_mode
  * timings and current video scanout position of a CRTC. This can be called from
@@ -723,52 +724,62 @@ EXPORT_SYMBOL(drm_calc_timestamping_constants);
  * returns as no operation if a doublescan or interlaced video mode is
  * active. Higher level code is expected to handle this.
  *
- * Returns:
- * Negative value on error, failure or if not supported in current
- * video mode:
- *
- * -EINVAL    Invalid CRTC.
- * -EAGAIN    Temporary unavailable, e.g., called before initial modeset.
- * -ENOTSUPP  Function not supported in current display mode.
- * -EIO       Failed, e.g., due to failed scanout position query.
+ * This function can be used to implement the &drm_driver.get_vblank_timestamp
+ * directly, if the driver implements the &drm_driver.get_scanout_position hook.
  *
- * Returns or'ed positive status flags on success:
+ * Note that atomic drivers must call drm_calc_timestamping_constants() before
+ * enabling a CRTC. The atomic helpers already take care of that in
+ * drm_atomic_helper_update_legacy_modeset_state().
  *
- * DRM_VBLANKTIME_SCANOUTPOS_METHOD - Signal this method used for timestamping.
- * DRM_VBLANKTIME_INVBL - Timestamp taken while scanout was in vblank interval.
+ * Returns:
  *
+ * Returns true on success, and false on failure, i.e. when no accurate
+ * timestamp could be acquired.
  */
-int drm_calc_vbltimestamp_from_scanoutpos(struct drm_device *dev,
-                                         unsigned int pipe,
-                                         int *max_error,
-                                         struct timeval *vblank_time,
-                                         unsigned flags,
-                                         const struct drm_display_mode *mode)
+bool drm_calc_vbltimestamp_from_scanoutpos(struct drm_device *dev,
+                                          unsigned int pipe,
+                                          int *max_error,
+                                          struct timeval *vblank_time,
+                                          bool in_vblank_irq)
 {
        struct timeval tv_etime;
        ktime_t stime, etime;
-       unsigned int vbl_status;
-       int ret = DRM_VBLANKTIME_SCANOUTPOS_METHOD;
+       bool vbl_status;
+       struct drm_crtc *crtc;
+       const struct drm_display_mode *mode;
+       struct drm_vblank_crtc *vblank = &dev->vblank[pipe];
        int vpos, hpos, i;
        int delta_ns, duration_ns;
 
-       if (pipe >= dev->num_crtcs) {
+       if (!drm_core_check_feature(dev, DRIVER_MODESET))
+               return false;
+
+       crtc = drm_crtc_from_index(dev, pipe);
+
+       if (pipe >= dev->num_crtcs || !crtc) {
                DRM_ERROR("Invalid crtc %u\n", pipe);
-               return -EINVAL;
+               return false;
        }
 
        /* Scanout position query not supported? Should not happen. */
        if (!dev->driver->get_scanout_position) {
                DRM_ERROR("Called from driver w/o get_scanout_position()!?\n");
-               return -EIO;
+               return false;
        }
 
+       if (drm_drv_uses_atomic_modeset(dev))
+               mode = &vblank->hwmode;
+       else
+               mode = &crtc->hwmode;
+
        /* If mode timing undefined, just return as no-op:
         * Happens during initial modesetting of a crtc.
         */
        if (mode->crtc_clock == 0) {
                DRM_DEBUG("crtc %u: Noop due to uninitialized mode.\n", pipe);
-               return -EAGAIN;
+               WARN_ON_ONCE(drm_drv_uses_atomic_modeset(dev));
+
+               return false;
        }
 
        /* Get current scanout position with system timestamp.
@@ -783,16 +794,17 @@ int drm_calc_vbltimestamp_from_scanoutpos(struct drm_device *dev,
                 * Get vertical and horizontal scanout position vpos, hpos,
                 * and bounding timestamps stime, etime, pre/post query.
                 */
-               vbl_status = dev->driver->get_scanout_position(dev, pipe, flags,
+               vbl_status = dev->driver->get_scanout_position(dev, pipe,
+                                                              in_vblank_irq,
                                                               &vpos, &hpos,
                                                               &stime, &etime,
                                                               mode);
 
                /* Return as no-op if scanout query unsupported or failed. */
-               if (!(vbl_status & DRM_SCANOUTPOS_VALID)) {
-                       DRM_DEBUG("crtc %u : scanoutpos query failed [0x%x].\n",
-                                 pipe, vbl_status);
-                       return -EIO;
+               if (!vbl_status) {
+                       DRM_DEBUG("crtc %u : scanoutpos query failed.\n",
+                                 pipe);
+                       return false;
                }
 
                /* Compute uncertainty in timestamp of scanout position query. */
@@ -830,13 +842,13 @@ int drm_calc_vbltimestamp_from_scanoutpos(struct drm_device *dev,
        etime = ktime_sub_ns(etime, delta_ns);
        *vblank_time = ktime_to_timeval(etime);
 
-       DRM_DEBUG_VBL("crtc %u : v 0x%x p(%d,%d)@ %ld.%ld -> %ld.%ld [e %d us, %d rep]\n",
-                     pipe, vbl_status, hpos, vpos,
+       DRM_DEBUG_VBL("crtc %u : v p(%d,%d)@ %ld.%ld -> %ld.%ld [e %d us, %d rep]\n",
+                     pipe, hpos, vpos,
                      (long)tv_etime.tv_sec, (long)tv_etime.tv_usec,
                      (long)vblank_time->tv_sec, (long)vblank_time->tv_usec,
                      duration_ns/1000, i);
 
-       return ret;
+       return true;
 }
 EXPORT_SYMBOL(drm_calc_vbltimestamp_from_scanoutpos);
 
@@ -854,9 +866,10 @@ static struct timeval get_drm_timestamp(void)
  * @dev: DRM device
  * @pipe: index of CRTC whose vblank timestamp to retrieve
  * @tvblank: Pointer to target struct timeval which should receive the timestamp
- * @flags: Flags to pass to driver:
- *         0 = Default,
- *         DRM_CALLED_FROM_VBLIRQ = If function is called from vbl IRQ handler
+ * @in_vblank_irq:
+ *     True when called from drm_crtc_handle_vblank().  Some drivers
+ *     need to apply some workarounds for gpu-specific vblank irq quirks
+ *     if flag is set.
  *
  * Fetches the system timestamp corresponding to the time of the most recent
  * vblank interval on specified CRTC. May call into kms-driver to
@@ -870,27 +883,25 @@ static struct timeval get_drm_timestamp(void)
  */
 static bool
 drm_get_last_vbltimestamp(struct drm_device *dev, unsigned int pipe,
-                         struct timeval *tvblank, unsigned flags)
+                         struct timeval *tvblank, bool in_vblank_irq)
 {
-       int ret;
+       bool ret = false;
 
        /* Define requested maximum error on timestamps (nanoseconds). */
        int max_error = (int) drm_timestamp_precision * 1000;
 
        /* Query driver if possible and precision timestamping enabled. */
-       if (dev->driver->get_vblank_timestamp && (max_error > 0)) {
+       if (dev->driver->get_vblank_timestamp && (max_error > 0))
                ret = dev->driver->get_vblank_timestamp(dev, pipe, &max_error,
-                                                       tvblank, flags);
-               if (ret > 0)
-                       return true;
-       }
+                                                       tvblank, in_vblank_irq);
 
        /* GPU high precision timestamp query unsupported or failed.
         * Return current monotonic/gettimeofday timestamp as best estimate.
         */
-       *tvblank = get_drm_timestamp();
+       if (!ret)
+               *tvblank = get_drm_timestamp();
 
-       return false;
+       return ret;
 }
 
 /**
@@ -1329,6 +1340,10 @@ void drm_crtc_vblank_off(struct drm_crtc *crtc)
                send_vblank_event(dev, e, seq, &now);
        }
        spin_unlock_irqrestore(&dev->event_lock, irqflags);
+
+       /* Will be reset by the modeset helpers when re-enabling the crtc by
+        * calling drm_calc_timestamping_constants(). */
+       vblank->hwmode.crtc_clock = 0;
 }
 EXPORT_SYMBOL(drm_crtc_vblank_off);
 
@@ -1760,7 +1775,7 @@ bool drm_handle_vblank(struct drm_device *dev, unsigned int pipe)
                return false;
        }
 
-       drm_update_vblank_count(dev, pipe, DRM_CALLED_FROM_VBLIRQ);
+       drm_update_vblank_count(dev, pipe, true);
 
        spin_unlock(&dev->vblank_time_lock);
 
index b84a295230fcd4ba7f7493a53d219e5c2ae93d4b..2c27f6f5a6689a98a646935be87fbed9cb10d24c 100644 (file)
@@ -381,6 +381,7 @@ EXPORT_SYMBOL(drm_primary_helper_update);
 /**
  * drm_primary_helper_disable() - Helper for primary plane disable
  * @plane: plane to disable
+ * @ctx: lock acquire context, not used here
  *
  * Provides a default plane disable handler for primary planes.  This is handler
  * is called in response to a userspace SetPlane operation on the plane with a
@@ -510,12 +511,10 @@ int drm_plane_helper_commit(struct drm_plane *plane,
        if (plane_funcs->cleanup_fb)
                plane_funcs->cleanup_fb(plane, plane_state);
 out:
-       if (plane_state) {
-               if (plane->funcs->atomic_destroy_state)
-                       plane->funcs->atomic_destroy_state(plane, plane_state);
-               else
-                       drm_atomic_helper_plane_destroy_state(plane, plane_state);
-       }
+       if (plane->funcs->atomic_destroy_state)
+               plane->funcs->atomic_destroy_state(plane, plane_state);
+       else
+               drm_atomic_helper_plane_destroy_state(plane, plane_state);
 
        return ret;
 }
index 954eb848b5e2125741fa025bc8892ba720a8aef7..22408badc617b0c43ad13ed7f838a2c458ea2162 100644 (file)
@@ -595,15 +595,18 @@ out_unlock:
 EXPORT_SYMBOL(drm_gem_prime_handle_to_fd);
 
 /**
- * drm_gem_prime_import - helper library implementation of the import callback
+ * drm_gem_prime_import_dev - core implementation of the import callback
  * @dev: drm_device to import into
  * @dma_buf: dma-buf object to import
+ * @attach_dev: struct device to dma_buf attach
  *
- * This is the implementation of the gem_prime_import functions for GEM drivers
- * using the PRIME helpers.
+ * This is the core of drm_gem_prime_import. It's designed to be called by
+ * drivers who want to use a different device structure than dev->dev for
+ * attaching via dma_buf.
  */
-struct drm_gem_object *drm_gem_prime_import(struct drm_device *dev,
-                                           struct dma_buf *dma_buf)
+struct drm_gem_object *drm_gem_prime_import_dev(struct drm_device *dev,
+                                           struct dma_buf *dma_buf,
+                                           struct device *attach_dev)
 {
        struct dma_buf_attachment *attach;
        struct sg_table *sgt;
@@ -625,7 +628,7 @@ struct drm_gem_object *drm_gem_prime_import(struct drm_device *dev,
        if (!dev->driver->gem_prime_import_sg_table)
                return ERR_PTR(-EINVAL);
 
-       attach = dma_buf_attach(dma_buf, dev->dev);
+       attach = dma_buf_attach(dma_buf, attach_dev);
        if (IS_ERR(attach))
                return ERR_CAST(attach);
 
@@ -655,6 +658,21 @@ fail_detach:
 
        return ERR_PTR(ret);
 }
+EXPORT_SYMBOL(drm_gem_prime_import_dev);
+
+/**
+ * drm_gem_prime_import - helper library implementation of the import callback
+ * @dev: drm_device to import into
+ * @dma_buf: dma-buf object to import
+ *
+ * This is the implementation of the gem_prime_import functions for GEM drivers
+ * using the PRIME helpers.
+ */
+struct drm_gem_object *drm_gem_prime_import(struct drm_device *dev,
+                                           struct dma_buf *dma_buf)
+{
+       return drm_gem_prime_import_dev(dev, dma_buf, dev->dev);
+}
 EXPORT_SYMBOL(drm_gem_prime_import);
 
 /**
index 09d3c4c3c858e8a05dcf4cbc9a14feb5910ff132..50294a7bd29da10f99e9dca59701b66c385bb66b 100644 (file)
@@ -82,14 +82,9 @@ err_file_priv_free:
        return ret;
 }
 
-static void exynos_drm_preclose(struct drm_device *dev,
-                                       struct drm_file *file)
-{
-       exynos_drm_subdrv_close(dev, file);
-}
-
 static void exynos_drm_postclose(struct drm_device *dev, struct drm_file *file)
 {
+       exynos_drm_subdrv_close(dev, file);
        kfree(file->driver_priv);
        file->driver_priv = NULL;
 }
@@ -145,7 +140,6 @@ static struct drm_driver exynos_drm_driver = {
        .driver_features        = DRIVER_MODESET | DRIVER_GEM | DRIVER_PRIME
                                  | DRIVER_ATOMIC | DRIVER_RENDER,
        .open                   = exynos_drm_open,
-       .preclose               = exynos_drm_preclose,
        .lastclose              = exynos_drm_lastclose,
        .postclose              = exynos_drm_postclose,
        .gem_free_object_unlocked = exynos_drm_gem_free_object,
index d8d4170725b293f50cf8d85f68dcc4b1a192463e..d40628e6810d2307ed135daca15dd4a295e272b4 100644 (file)
@@ -32,53 +32,20 @@ static struct drm_display_mode *tpo_vid_get_config_mode(struct drm_device *dev)
        struct drm_display_mode *mode;
        struct drm_psb_private *dev_priv = dev->dev_private;
        struct oaktrail_timing_info *ti = &dev_priv->gct_data.DTD;
-       bool use_gct = false;
 
        mode = kzalloc(sizeof(*mode), GFP_KERNEL);
        if (!mode)
                return NULL;
 
-       if (use_gct) {
-               mode->hdisplay = (ti->hactive_hi << 8) | ti->hactive_lo;
-               mode->vdisplay = (ti->vactive_hi << 8) | ti->vactive_lo;
-               mode->hsync_start = mode->hdisplay +
-                               ((ti->hsync_offset_hi << 8) |
-                               ti->hsync_offset_lo);
-               mode->hsync_end = mode->hsync_start +
-                               ((ti->hsync_pulse_width_hi << 8) |
-                               ti->hsync_pulse_width_lo);
-               mode->htotal = mode->hdisplay + ((ti->hblank_hi << 8) |
-                                                               ti->hblank_lo);
-               mode->vsync_start =
-                       mode->vdisplay + ((ti->vsync_offset_hi << 8) |
-                                               ti->vsync_offset_lo);
-               mode->vsync_end =
-                       mode->vsync_start + ((ti->vsync_pulse_width_hi << 8) |
-                                               ti->vsync_pulse_width_lo);
-               mode->vtotal = mode->vdisplay +
-                               ((ti->vblank_hi << 8) | ti->vblank_lo);
-               mode->clock = ti->pixel_clock * 10;
-
-               dev_dbg(dev->dev, "hdisplay is %d\n", mode->hdisplay);
-               dev_dbg(dev->dev, "vdisplay is %d\n", mode->vdisplay);
-               dev_dbg(dev->dev, "HSS is %d\n", mode->hsync_start);
-               dev_dbg(dev->dev, "HSE is %d\n", mode->hsync_end);
-               dev_dbg(dev->dev, "htotal is %d\n", mode->htotal);
-               dev_dbg(dev->dev, "VSS is %d\n", mode->vsync_start);
-               dev_dbg(dev->dev, "VSE is %d\n", mode->vsync_end);
-               dev_dbg(dev->dev, "vtotal is %d\n", mode->vtotal);
-               dev_dbg(dev->dev, "clock is %d\n", mode->clock);
-       } else {
-               mode->hdisplay = 864;
-               mode->vdisplay = 480;
-               mode->hsync_start = 873;
-               mode->hsync_end = 876;
-               mode->htotal = 887;
-               mode->vsync_start = 487;
-               mode->vsync_end = 490;
-               mode->vtotal = 499;
-               mode->clock = 33264;
-       }
+       mode->hdisplay = 864;
+       mode->vdisplay = 480;
+       mode->hsync_start = 873;
+       mode->hsync_end = 876;
+       mode->htotal = 887;
+       mode->vsync_start = 487;
+       mode->vsync_end = 490;
+       mode->vtotal = 499;
+       mode->clock = 33264;
 
        drm_mode_set_name(mode);
        drm_mode_set_crtcinfo(mode, 0);
index fd97fe00cd0d2ad00e1c7258eeb51ecf0f60d4c1..04493ef1d2f7a5c4108c697cfb4015b7e9c4ce33 100644 (file)
@@ -720,9 +720,7 @@ static u32 i915_get_vblank_counter(struct drm_device *dev, unsigned int pipe)
        struct drm_i915_private *dev_priv = to_i915(dev);
        i915_reg_t high_frame, low_frame;
        u32 high1, high2, low, pixel, vbl_start, hsync_start, htotal;
-       struct intel_crtc *intel_crtc = intel_get_crtc_for_pipe(dev_priv,
-                                                               pipe);
-       const struct drm_display_mode *mode = &intel_crtc->base.hwmode;
+       const struct drm_display_mode *mode = &dev->vblank[pipe].hwmode;
        unsigned long irqflags;
 
        htotal = mode->crtc_htotal;
@@ -779,13 +777,17 @@ static int __intel_get_crtc_scanline(struct intel_crtc *crtc)
 {
        struct drm_device *dev = crtc->base.dev;
        struct drm_i915_private *dev_priv = to_i915(dev);
-       const struct drm_display_mode *mode = &crtc->base.hwmode;
+       const struct drm_display_mode *mode;
+       struct drm_vblank_crtc *vblank;
        enum pipe pipe = crtc->pipe;
        int position, vtotal;
 
        if (!crtc->active)
                return -1;
 
+       vblank = &crtc->base.dev->vblank[drm_crtc_index(&crtc->base)];
+       mode = &vblank->hwmode;
+
        vtotal = mode->crtc_vtotal;
        if (mode->flags & DRM_MODE_FLAG_INTERLACE)
                vtotal /= 2;
@@ -827,10 +829,10 @@ static int __intel_get_crtc_scanline(struct intel_crtc *crtc)
        return (position + crtc->scanline_offset) % vtotal;
 }
 
-static int i915_get_crtc_scanoutpos(struct drm_device *dev, unsigned int pipe,
-                                   unsigned int flags, int *vpos, int *hpos,
-                                   ktime_t *stime, ktime_t *etime,
-                                   const struct drm_display_mode *mode)
+static bool i915_get_crtc_scanoutpos(struct drm_device *dev, unsigned int pipe,
+                                    bool in_vblank_irq, int *vpos, int *hpos,
+                                    ktime_t *stime, ktime_t *etime,
+                                    const struct drm_display_mode *mode)
 {
        struct drm_i915_private *dev_priv = to_i915(dev);
        struct intel_crtc *intel_crtc = intel_get_crtc_for_pipe(dev_priv,
@@ -838,13 +840,12 @@ static int i915_get_crtc_scanoutpos(struct drm_device *dev, unsigned int pipe,
        int position;
        int vbl_start, vbl_end, hsync_start, htotal, vtotal;
        bool in_vbl = true;
-       int ret = 0;
        unsigned long irqflags;
 
        if (WARN_ON(!mode->crtc_clock)) {
                DRM_DEBUG_DRIVER("trying to get scanoutpos for disabled "
                                 "pipe %c\n", pipe_name(pipe));
-               return 0;
+               return false;
        }
 
        htotal = mode->crtc_htotal;
@@ -859,8 +860,6 @@ static int i915_get_crtc_scanoutpos(struct drm_device *dev, unsigned int pipe,
                vtotal /= 2;
        }
 
-       ret |= DRM_SCANOUTPOS_VALID | DRM_SCANOUTPOS_ACCURATE;
-
        /*
         * Lock uncore.lock, as we will do multiple timing critical raw
         * register reads, potentially with preemption disabled, so the
@@ -944,11 +943,7 @@ static int i915_get_crtc_scanoutpos(struct drm_device *dev, unsigned int pipe,
                *hpos = position - (*vpos * htotal);
        }
 
-       /* In vblank? */
-       if (in_vbl)
-               ret |= DRM_SCANOUTPOS_IN_VBLANK;
-
-       return ret;
+       return true;
 }
 
 int intel_get_crtc_scanline(struct intel_crtc *crtc)
@@ -964,37 +959,6 @@ int intel_get_crtc_scanline(struct intel_crtc *crtc)
        return position;
 }
 
-static int i915_get_vblank_timestamp(struct drm_device *dev, unsigned int pipe,
-                             int *max_error,
-                             struct timeval *vblank_time,
-                             unsigned flags)
-{
-       struct drm_i915_private *dev_priv = to_i915(dev);
-       struct intel_crtc *crtc;
-
-       if (pipe >= INTEL_INFO(dev_priv)->num_pipes) {
-               DRM_ERROR("Invalid crtc %u\n", pipe);
-               return -EINVAL;
-       }
-
-       /* Get drm_crtc to timestamp: */
-       crtc = intel_get_crtc_for_pipe(dev_priv, pipe);
-       if (crtc == NULL) {
-               DRM_ERROR("Invalid crtc %u\n", pipe);
-               return -EINVAL;
-       }
-
-       if (!crtc->base.hwmode.crtc_clock) {
-               DRM_DEBUG_KMS("crtc %u is disabled\n", pipe);
-               return -EBUSY;
-       }
-
-       /* Helper routine in DRM core does all the work: */
-       return drm_calc_vbltimestamp_from_scanoutpos(dev, pipe, max_error,
-                                                    vblank_time, flags,
-                                                    &crtc->base.hwmode);
-}
-
 static void ironlake_rps_change_irq_handler(struct drm_i915_private *dev_priv)
 {
        u32 busy_up, busy_down, max_avg, min_avg;
@@ -4294,7 +4258,7 @@ void intel_irq_init(struct drm_i915_private *dev_priv)
 
        dev_priv->hotplug.hpd_storm_threshold = HPD_STORM_DEFAULT_THRESHOLD;
 
-       dev->driver->get_vblank_timestamp = i915_get_vblank_timestamp;
+       dev->driver->get_vblank_timestamp = drm_calc_vbltimestamp_from_scanoutpos;
        dev->driver->get_scanout_position = i915_get_crtc_scanoutpos;
 
        if (IS_CHERRYVIEW(dev_priv)) {
index 3617927af269afb9872b0d5d419873f0945f880c..2f2bb623cf5fbc909b2de34ba946a4194108ca9f 100644 (file)
@@ -11444,12 +11444,6 @@ intel_modeset_update_crtc_state(struct drm_atomic_state *state)
        for_each_new_crtc_in_state(state, crtc, new_crtc_state, i) {
                to_intel_crtc(crtc)->config = to_intel_crtc_state(new_crtc_state);
 
-               /* Update hwmode for vblank functions */
-               if (new_crtc_state->active)
-                       crtc->hwmode = new_crtc_state->adjusted_mode;
-               else
-                       crtc->hwmode.crtc_clock = 0;
-
                /*
                 * Update legacy state to satisfy fbc code. This can
                 * be removed when fbc uses the atomic state.
@@ -15425,8 +15419,6 @@ static void intel_modeset_readout_hw_state(struct drm_device *dev)
                        to_intel_crtc_state(crtc->base.state);
                int pixclk = 0;
 
-               crtc->base.hwmode = crtc_state->base.adjusted_mode;
-
                memset(&crtc->base.mode, 0, sizeof(crtc->base.mode));
                if (crtc_state->base.active) {
                        intel_mode_from_pipe_config(&crtc->base.mode, crtc_state);
@@ -15456,7 +15448,8 @@ static void intel_modeset_readout_hw_state(struct drm_device *dev)
                        if (IS_BROADWELL(dev_priv) && crtc_state->ips_enabled)
                                pixclk = DIV_ROUND_UP(pixclk * 100, 95);
 
-                       drm_calc_timestamping_constants(&crtc->base, &crtc->base.hwmode);
+                       drm_calc_timestamping_constants(&crtc->base,
+                                                       &crtc_state->base.adjusted_mode);
                        update_scanline_offset(crtc);
                }
 
index c1f62eb07c07a7ce49b3ed39e1e6ee2b23eb65e8..1dee9933005fdbc9860907d5a248d7063de1e482 100644 (file)
@@ -39,7 +39,7 @@ static bool intel_dp_mst_compute_config(struct intel_encoder *encoder,
        struct intel_dp *intel_dp = &intel_dig_port->dp;
        struct intel_connector *connector =
                to_intel_connector(conn_state->connector);
-       struct drm_atomic_state *state;
+       struct drm_atomic_state *state = pipe_config->base.state;
        int bpp;
        int lane_count, slots;
        const struct drm_display_mode *adjusted_mode = &pipe_config->base.adjusted_mode;
@@ -57,20 +57,24 @@ static bool intel_dp_mst_compute_config(struct intel_encoder *encoder,
         * seem to suggest we should do otherwise.
         */
        lane_count = drm_dp_max_lane_count(intel_dp->dpcd);
-
        pipe_config->lane_count = lane_count;
 
        pipe_config->pipe_bpp = bpp;
-       pipe_config->port_clock = intel_dp_max_link_rate(intel_dp);
 
-       state = pipe_config->base.state;
+       pipe_config->port_clock = intel_dp_max_link_rate(intel_dp);
 
        if (drm_dp_mst_port_has_audio(&intel_dp->mst_mgr, connector->port))
                pipe_config->has_audio = true;
-       mst_pbn = drm_dp_calc_pbn_mode(adjusted_mode->crtc_clock, bpp);
 
+       mst_pbn = drm_dp_calc_pbn_mode(adjusted_mode->crtc_clock, bpp);
        pipe_config->pbn = mst_pbn;
-       slots = drm_dp_find_vcpi_slots(&intel_dp->mst_mgr, mst_pbn);
+
+       slots = drm_dp_atomic_find_vcpi_slots(state, &intel_dp->mst_mgr,
+                                             connector->port, mst_pbn);
+       if (slots < 0) {
+               DRM_DEBUG_KMS("failed finding vcpi slots:%d\n", slots);
+               return false;
+       }
 
        intel_link_compute_m_n(bpp, lane_count,
                               adjusted_mode->crtc_clock,
@@ -80,7 +84,38 @@ static bool intel_dp_mst_compute_config(struct intel_encoder *encoder,
        pipe_config->dp_m_n.tu = slots;
 
        return true;
+}
 
+static int intel_dp_mst_atomic_check(struct drm_connector *connector,
+               struct drm_connector_state *new_conn_state)
+{
+       struct drm_atomic_state *state = new_conn_state->state;
+       struct drm_connector_state *old_conn_state;
+       struct drm_crtc *old_crtc;
+       struct drm_crtc_state *crtc_state;
+       int slots, ret = 0;
+
+       old_conn_state = drm_atomic_get_old_connector_state(state, connector);
+       old_crtc = old_conn_state->crtc;
+       if (!old_crtc)
+               return ret;
+
+       crtc_state = drm_atomic_get_new_crtc_state(state, old_crtc);
+       slots = to_intel_crtc_state(crtc_state)->dp_m_n.tu;
+       if (drm_atomic_crtc_needs_modeset(crtc_state) && slots > 0) {
+               struct drm_dp_mst_topology_mgr *mgr;
+               struct drm_encoder *old_encoder;
+
+               old_encoder = old_conn_state->best_encoder;
+               mgr = &enc_to_mst(old_encoder)->primary->dp.mst_mgr;
+
+               ret = drm_dp_atomic_release_vcpi_slots(state, mgr, slots);
+               if (ret)
+                       DRM_DEBUG_KMS("failed releasing %d vcpi slots:%d\n", slots, ret);
+               else
+                       to_intel_crtc_state(crtc_state)->dp_m_n.tu = 0;
+       }
+       return ret;
 }
 
 static void intel_mst_disable_dp(struct intel_encoder *encoder,
@@ -387,6 +422,7 @@ static const struct drm_connector_helper_funcs intel_dp_mst_connector_helper_fun
        .mode_valid = intel_dp_mst_mode_valid,
        .atomic_best_encoder = intel_mst_atomic_best_encoder,
        .best_encoder = intel_mst_best_encoder,
+       .atomic_check = intel_dp_mst_atomic_check,
 };
 
 static void intel_dp_mst_encoder_destroy(struct drm_encoder *encoder)
index aaee3949a42267603a5dfa9deb9be87dd0f7b2b4..48ea8d9d49fe27960ff4b8447fe3c6aa1a48250b 100644 (file)
@@ -869,7 +869,6 @@ struct intel_hdmi {
        bool has_audio;
        enum hdmi_force_audio force_audio;
        bool rgb_quant_range_selectable;
-       enum hdmi_picture_aspect aspect_ratio;
        struct intel_connector *attached_connector;
        void (*write_infoframe)(struct drm_encoder *encoder,
                                const struct intel_crtc_state *crtc_state,
index 1d623b5e09d62ca836a597d84738197df4b0eb03..c6b8207724fadf64d7c58e7ed8ec8fb3c8284c70 100644 (file)
@@ -1403,7 +1403,7 @@ bool intel_hdmi_compute_config(struct intel_encoder *encoder,
        }
 
        /* Set user selected PAR to incoming mode's member */
-       adjusted_mode->picture_aspect_ratio = intel_hdmi->aspect_ratio;
+       adjusted_mode->picture_aspect_ratio = conn_state->picture_aspect_ratio;
 
        pipe_config->lane_count = 4;
 
@@ -1649,19 +1649,7 @@ intel_hdmi_set_property(struct drm_connector *connector,
        }
 
        if (property == connector->dev->mode_config.aspect_ratio_property) {
-               switch (val) {
-               case DRM_MODE_PICTURE_ASPECT_NONE:
-                       intel_hdmi->aspect_ratio = HDMI_PICTURE_ASPECT_NONE;
-                       break;
-               case DRM_MODE_PICTURE_ASPECT_4_3:
-                       intel_hdmi->aspect_ratio = HDMI_PICTURE_ASPECT_4_3;
-                       break;
-               case DRM_MODE_PICTURE_ASPECT_16_9:
-                       intel_hdmi->aspect_ratio = HDMI_PICTURE_ASPECT_16_9;
-                       break;
-               default:
-                       return -EINVAL;
-               }
+               connector->state->picture_aspect_ratio = val;
                goto done;
        }
 
@@ -1823,7 +1811,7 @@ intel_hdmi_add_properties(struct intel_hdmi *intel_hdmi, struct drm_connector *c
        intel_attach_broadcast_rgb_property(connector);
        intel_hdmi->color_range_auto = true;
        intel_attach_aspect_ratio_property(connector);
-       intel_hdmi->aspect_ratio = HDMI_PICTURE_ASPECT_NONE;
+       connector->state->picture_aspect_ratio = HDMI_PICTURE_ASPECT_NONE;
 }
 
 /*
index 816a6f5a3fd948547ebcde09404351dab068d05a..ef6fa87b2f8aa85c96b55b33de90afbae06e2a6b 100644 (file)
@@ -106,11 +106,6 @@ struct intel_sdvo {
        uint32_t color_range;
        bool color_range_auto;
 
-       /**
-        * HDMI user specified aspect ratio
-        */
-       enum hdmi_picture_aspect aspect_ratio;
-
        /**
         * This is set if we're going to treat the device as TV-out.
         *
@@ -1186,7 +1181,7 @@ static bool intel_sdvo_compute_config(struct intel_encoder *encoder,
 
        /* Set user selected PAR to incoming mode's member */
        if (intel_sdvo->is_hdmi)
-               adjusted_mode->picture_aspect_ratio = intel_sdvo->aspect_ratio;
+               adjusted_mode->picture_aspect_ratio = conn_state->picture_aspect_ratio;
 
        return true;
 }
@@ -2067,19 +2062,7 @@ intel_sdvo_set_property(struct drm_connector *connector,
        }
 
        if (property == connector->dev->mode_config.aspect_ratio_property) {
-               switch (val) {
-               case DRM_MODE_PICTURE_ASPECT_NONE:
-                       intel_sdvo->aspect_ratio = HDMI_PICTURE_ASPECT_NONE;
-                       break;
-               case DRM_MODE_PICTURE_ASPECT_4_3:
-                       intel_sdvo->aspect_ratio = HDMI_PICTURE_ASPECT_4_3;
-                       break;
-               case DRM_MODE_PICTURE_ASPECT_16_9:
-                       intel_sdvo->aspect_ratio = HDMI_PICTURE_ASPECT_16_9;
-                       break;
-               default:
-                       return -EINVAL;
-               }
+               connector->state->picture_aspect_ratio = val;
                goto done;
        }
 
@@ -2418,7 +2401,7 @@ intel_sdvo_add_hdmi_properties(struct intel_sdvo *intel_sdvo,
                intel_sdvo->color_range_auto = true;
        }
        intel_attach_aspect_ratio_property(&connector->base.base);
-       intel_sdvo->aspect_ratio = HDMI_PICTURE_ASPECT_NONE;
+       connector->base.base.state->picture_aspect_ratio = HDMI_PICTURE_ASPECT_NONE;
 }
 
 static struct intel_sdvo_connector *intel_sdvo_connector_alloc(void)
index d3d6b4cae1e6c86f1fa461b16dda05d649c1c0e8..e2b3346ead48fb49aae2b278a49cd45c960afacb 100644 (file)
@@ -527,31 +527,28 @@ static struct drm_encoder *get_encoder_from_crtc(struct drm_crtc *crtc)
        return NULL;
 }
 
-static int mdp5_get_scanoutpos(struct drm_device *dev, unsigned int pipe,
-                              unsigned int flags, int *vpos, int *hpos,
-                              ktime_t *stime, ktime_t *etime,
-                              const struct drm_display_mode *mode)
+static bool mdp5_get_scanoutpos(struct drm_device *dev, unsigned int pipe,
+                               bool in_vblank_irq, int *vpos, int *hpos,
+                               ktime_t *stime, ktime_t *etime,
+                               const struct drm_display_mode *mode)
 {
        struct msm_drm_private *priv = dev->dev_private;
        struct drm_crtc *crtc;
        struct drm_encoder *encoder;
        int line, vsw, vbp, vactive_start, vactive_end, vfp_end;
-       int ret = 0;
 
        crtc = priv->crtcs[pipe];
        if (!crtc) {
                DRM_ERROR("Invalid crtc %d\n", pipe);
-               return 0;
+               return false;
        }
 
        encoder = get_encoder_from_crtc(crtc);
        if (!encoder) {
                DRM_ERROR("no encoder found for crtc %d\n", pipe);
-               return 0;
+               return false;
        }
 
-       ret |= DRM_SCANOUTPOS_VALID | DRM_SCANOUTPOS_ACCURATE;
-
        vsw = mode->crtc_vsync_end - mode->crtc_vsync_start;
        vbp = mode->crtc_vtotal - mode->crtc_vsync_end;
 
@@ -575,10 +572,8 @@ static int mdp5_get_scanoutpos(struct drm_device *dev, unsigned int pipe,
 
        if (line < vactive_start) {
                line -= vactive_start;
-               ret |= DRM_SCANOUTPOS_IN_VBLANK;
        } else if (line > vactive_end) {
                line = line - vfp_end - vactive_start;
-               ret |= DRM_SCANOUTPOS_IN_VBLANK;
        } else {
                line -= vactive_start;
        }
@@ -589,31 +584,7 @@ static int mdp5_get_scanoutpos(struct drm_device *dev, unsigned int pipe,
        if (etime)
                *etime = ktime_get();
 
-       return ret;
-}
-
-static int mdp5_get_vblank_timestamp(struct drm_device *dev, unsigned int pipe,
-                                    int *max_error,
-                                    struct timeval *vblank_time,
-                                    unsigned flags)
-{
-       struct msm_drm_private *priv = dev->dev_private;
-       struct drm_crtc *crtc;
-
-       if (pipe < 0 || pipe >= priv->num_crtcs) {
-               DRM_ERROR("Invalid crtc %d\n", pipe);
-               return -EINVAL;
-       }
-
-       crtc = priv->crtcs[pipe];
-       if (!crtc) {
-               DRM_ERROR("Invalid crtc %d\n", pipe);
-               return -EINVAL;
-       }
-
-       return drm_calc_vbltimestamp_from_scanoutpos(dev, pipe, max_error,
-                                                    vblank_time, flags,
-                                                    &crtc->mode);
+       return true;
 }
 
 static u32 mdp5_get_vblank_counter(struct drm_device *dev, unsigned int pipe)
@@ -725,7 +696,7 @@ struct msm_kms *mdp5_kms_init(struct drm_device *dev)
        dev->mode_config.max_width = 0xffff;
        dev->mode_config.max_height = 0xffff;
 
-       dev->driver->get_vblank_timestamp = mdp5_get_vblank_timestamp;
+       dev->driver->get_vblank_timestamp = drm_calc_vbltimestamp_from_scanoutpos;
        dev->driver->get_scanout_position = mdp5_get_scanoutpos;
        dev->driver->get_vblank_counter = mdp5_get_vblank_counter;
        dev->max_vblank_count = 0xffffffff;
index 21b10f9840c95e29cd177e9e36d93b8d6155b48d..6718c84fb86250f2bb68a4612a5209ed5725cee4 100644 (file)
@@ -98,7 +98,7 @@ calc(int blanks, int blanke, int total, int line)
        return line;
 }
 
-static int
+static bool
 nouveau_display_scanoutpos_head(struct drm_crtc *crtc, int *vpos, int *hpos,
                                ktime_t *stime, ktime_t *etime)
 {
@@ -111,16 +111,16 @@ nouveau_display_scanoutpos_head(struct drm_crtc *crtc, int *vpos, int *hpos,
        };
        struct nouveau_display *disp = nouveau_display(crtc->dev);
        struct drm_vblank_crtc *vblank = &crtc->dev->vblank[drm_crtc_index(crtc)];
-       int ret, retry = 20;
+       int retry = 20;
+       bool ret = false;
 
        do {
                ret = nvif_mthd(&disp->disp, 0, &args, sizeof(args));
                if (ret != 0)
-                       return 0;
+                       return false;
 
                if (args.scan.vline) {
-                       ret |= DRM_SCANOUTPOS_ACCURATE;
-                       ret |= DRM_SCANOUTPOS_VALID;
+                       ret = true;
                        break;
                }
 
@@ -133,14 +133,12 @@ nouveau_display_scanoutpos_head(struct drm_crtc *crtc, int *vpos, int *hpos,
        if (stime) *stime = ns_to_ktime(args.scan.time[0]);
        if (etime) *etime = ns_to_ktime(args.scan.time[1]);
 
-       if (*vpos < 0)
-               ret |= DRM_SCANOUTPOS_IN_VBLANK;
        return ret;
 }
 
-int
+bool
 nouveau_display_scanoutpos(struct drm_device *dev, unsigned int pipe,
-                          unsigned int flags, int *vpos, int *hpos,
+                          bool in_vblank_irq, int *vpos, int *hpos,
                           ktime_t *stime, ktime_t *etime,
                           const struct drm_display_mode *mode)
 {
@@ -153,28 +151,7 @@ nouveau_display_scanoutpos(struct drm_device *dev, unsigned int pipe,
                }
        }
 
-       return 0;
-}
-
-int
-nouveau_display_vblstamp(struct drm_device *dev, unsigned int pipe,
-                        int *max_error, struct timeval *time, unsigned flags)
-{
-       struct drm_crtc *crtc;
-
-       list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
-               if (nouveau_crtc(crtc)->index == pipe) {
-                       struct drm_display_mode *mode;
-                       if (drm_drv_uses_atomic_modeset(dev))
-                               mode = &crtc->state->adjusted_mode;
-                       else
-                               mode = &crtc->hwmode;
-                       return drm_calc_vbltimestamp_from_scanoutpos(dev,
-                                       pipe, max_error, time, flags, mode);
-               }
-       }
-
-       return -EINVAL;
+       return false;
 }
 
 static void
index e1d772d394887eb91cbc09672169ccd7becf2393..201aec2ea5b81c690e10c6765e0a406ec9af197c 100644 (file)
@@ -68,11 +68,9 @@ int  nouveau_display_suspend(struct drm_device *dev, bool runtime);
 void nouveau_display_resume(struct drm_device *dev, bool runtime);
 int  nouveau_display_vblank_enable(struct drm_device *, unsigned int);
 void nouveau_display_vblank_disable(struct drm_device *, unsigned int);
-int  nouveau_display_scanoutpos(struct drm_device *, unsigned int,
-                               unsigned int, int *, int *, ktime_t *,
-                               ktime_t *, const struct drm_display_mode *);
-int  nouveau_display_vblstamp(struct drm_device *, unsigned int, int *,
-                             struct timeval *, unsigned);
+bool  nouveau_display_scanoutpos(struct drm_device *, unsigned int,
+                                bool, int *, int *, ktime_t *,
+                                ktime_t *, const struct drm_display_mode *);
 
 int  nouveau_crtc_page_flip(struct drm_crtc *crtc, struct drm_framebuffer *fb,
                            struct drm_pending_vblank_event *event,
index 2b6ac24ce6901013fd1dc99b4b4e8b27de6125db..1f751a3f570ce5190d29556e418b3ae7e59804dc 100644 (file)
@@ -881,7 +881,7 @@ done:
 }
 
 static void
-nouveau_drm_preclose(struct drm_device *dev, struct drm_file *fpriv)
+nouveau_drm_postclose(struct drm_device *dev, struct drm_file *fpriv)
 {
        struct nouveau_cli *cli = nouveau_cli(fpriv);
        struct nouveau_drm *drm = nouveau_drm(dev);
@@ -897,12 +897,6 @@ nouveau_drm_preclose(struct drm_device *dev, struct drm_file *fpriv)
        list_del(&cli->head);
        mutex_unlock(&drm->client.mutex);
 
-}
-
-static void
-nouveau_drm_postclose(struct drm_device *dev, struct drm_file *fpriv)
-{
-       struct nouveau_cli *cli = nouveau_cli(fpriv);
        nouveau_cli_fini(cli);
        kfree(cli);
        pm_runtime_mark_last_busy(dev->dev);
@@ -974,7 +968,6 @@ driver_stub = {
        .load = nouveau_drm_load,
        .unload = nouveau_drm_unload,
        .open = nouveau_drm_open,
-       .preclose = nouveau_drm_preclose,
        .postclose = nouveau_drm_postclose,
        .lastclose = nouveau_vga_lastclose,
 
@@ -985,7 +978,7 @@ driver_stub = {
        .enable_vblank = nouveau_display_vblank_enable,
        .disable_vblank = nouveau_display_vblank_disable,
        .get_scanout_position = nouveau_display_scanoutpos,
-       .get_vblank_timestamp = nouveau_display_vblstamp,
+       .get_vblank_timestamp = drm_calc_vbltimestamp_from_scanoutpos,
 
        .ioctls = nouveau_ioctls,
        .num_ioctls = ARRAY_SIZE(nouveau_ioctls),
diff --git a/drivers/gpu/drm/pl111/Kconfig b/drivers/gpu/drm/pl111/Kconfig
new file mode 100644 (file)
index 0000000..ede49ef
--- /dev/null
@@ -0,0 +1,12 @@
+config DRM_PL111
+       tristate "DRM Support for PL111 CLCD Controller"
+       depends on DRM
+       depends on ARM || ARM64 || COMPILE_TEST
+       select DRM_KMS_HELPER
+       select DRM_KMS_CMA_HELPER
+       select DRM_GEM_CMA_HELPER
+       select VT_HW_CONSOLE_BINDING if FRAMEBUFFER_CONSOLE
+       help
+         Choose this option for DRM support for the PL111 CLCD controller.
+         If M is selected the module will be called pl111_drm.
+
diff --git a/drivers/gpu/drm/pl111/Makefile b/drivers/gpu/drm/pl111/Makefile
new file mode 100644 (file)
index 0000000..01caee7
--- /dev/null
@@ -0,0 +1,5 @@
+pl111_drm-y += pl111_connector.o \
+               pl111_display.o \
+               pl111_drv.o
+
+obj-$(CONFIG_DRM_PL111) += pl111_drm.o
diff --git a/drivers/gpu/drm/pl111/pl111_connector.c b/drivers/gpu/drm/pl111/pl111_connector.c
new file mode 100644 (file)
index 0000000..3f213d7
--- /dev/null
@@ -0,0 +1,127 @@
+/*
+ * (C) COPYRIGHT 2012-2013 ARM Limited. All rights reserved.
+ *
+ * Parts of this file were based on sources as follows:
+ *
+ * Copyright (c) 2006-2008 Intel Corporation
+ * Copyright (c) 2007 Dave Airlie <airlied@linux.ie>
+ * Copyright (C) 2011 Texas Instruments
+ *
+ * This program is free software and is provided to you under the terms of the
+ * GNU General Public License version 2 as published by the Free Software
+ * Foundation, and any use by you of this program is subject to the terms of
+ * such GNU licence.
+ *
+ */
+
+/**
+ * pl111_drm_connector.c
+ * Implementation of the connector functions for PL111 DRM
+ */
+#include <linux/amba/clcd-regs.h>
+#include <linux/version.h>
+#include <linux/shmem_fs.h>
+#include <linux/dma-buf.h>
+
+#include <drm/drmP.h>
+#include <drm/drm_atomic_helper.h>
+#include <drm/drm_crtc_helper.h>
+#include <drm/drm_of.h>
+#include <drm/drm_panel.h>
+
+#include "pl111_drm.h"
+
+static void pl111_connector_destroy(struct drm_connector *connector)
+{
+       struct pl111_drm_connector *pl111_connector =
+               to_pl111_connector(connector);
+
+       if (pl111_connector->panel)
+               drm_panel_detach(pl111_connector->panel);
+
+       drm_connector_unregister(connector);
+       drm_connector_cleanup(connector);
+}
+
+static enum drm_connector_status pl111_connector_detect(struct drm_connector
+                                                       *connector, bool force)
+{
+       struct pl111_drm_connector *pl111_connector =
+               to_pl111_connector(connector);
+
+       return (pl111_connector->panel ?
+               connector_status_connected :
+               connector_status_disconnected);
+}
+
+static int pl111_connector_helper_get_modes(struct drm_connector *connector)
+{
+       struct pl111_drm_connector *pl111_connector =
+               to_pl111_connector(connector);
+
+       if (!pl111_connector->panel)
+               return 0;
+
+       return drm_panel_get_modes(pl111_connector->panel);
+}
+
+const struct drm_connector_funcs connector_funcs = {
+       .fill_modes = drm_helper_probe_single_connector_modes,
+       .destroy = pl111_connector_destroy,
+       .detect = pl111_connector_detect,
+       .dpms = drm_atomic_helper_connector_dpms,
+       .reset = drm_atomic_helper_connector_reset,
+       .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
+       .atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
+};
+
+const struct drm_connector_helper_funcs connector_helper_funcs = {
+       .get_modes = pl111_connector_helper_get_modes,
+};
+
+/* Walks the OF graph to find the panel node and then asks DRM to look
+ * up the panel.
+ */
+static struct drm_panel *pl111_get_panel(struct device *dev)
+{
+       struct device_node *endpoint, *panel_node;
+       struct device_node *np = dev->of_node;
+       struct drm_panel *panel;
+
+       endpoint = of_graph_get_next_endpoint(np, NULL);
+       if (!endpoint) {
+               dev_err(dev, "no endpoint to fetch panel\n");
+               return NULL;
+       }
+
+       /* don't proceed if we have an endpoint but no panel_node tied to it */
+       panel_node = of_graph_get_remote_port_parent(endpoint);
+       of_node_put(endpoint);
+       if (!panel_node) {
+               dev_err(dev, "no valid panel node\n");
+               return NULL;
+       }
+
+       panel = of_drm_find_panel(panel_node);
+       of_node_put(panel_node);
+
+       return panel;
+}
+
+int pl111_connector_init(struct drm_device *dev)
+{
+       struct pl111_drm_dev_private *priv = dev->dev_private;
+       struct pl111_drm_connector *pl111_connector = &priv->connector;
+       struct drm_connector *connector = &pl111_connector->connector;
+
+       drm_connector_init(dev, connector, &connector_funcs,
+                          DRM_MODE_CONNECTOR_DPI);
+       drm_connector_helper_add(connector, &connector_helper_funcs);
+
+       pl111_connector->panel = pl111_get_panel(dev->dev);
+       if (pl111_connector->panel)
+               drm_panel_attach(pl111_connector->panel, connector);
+
+       return 0;
+}
+
diff --git a/drivers/gpu/drm/pl111/pl111_display.c b/drivers/gpu/drm/pl111/pl111_display.c
new file mode 100644 (file)
index 0000000..39a5c33
--- /dev/null
@@ -0,0 +1,344 @@
+/*
+ * (C) COPYRIGHT 2012-2013 ARM Limited. All rights reserved.
+ *
+ * Parts of this file were based on sources as follows:
+ *
+ * Copyright (c) 2006-2008 Intel Corporation
+ * Copyright (c) 2007 Dave Airlie <airlied@linux.ie>
+ * Copyright (C) 2011 Texas Instruments
+ *
+ * This program is free software and is provided to you under the terms of the
+ * GNU General Public License version 2 as published by the Free Software
+ * Foundation, and any use by you of this program is subject to the terms of
+ * such GNU licence.
+ *
+ */
+
+#include <linux/amba/clcd-regs.h>
+#include <linux/clk.h>
+#include <linux/version.h>
+#include <linux/dma-buf.h>
+#include <linux/of_graph.h>
+
+#include <drm/drmP.h>
+#include <drm/drm_panel.h>
+#include <drm/drm_gem_cma_helper.h>
+#include <drm/drm_fb_cma_helper.h>
+
+#include "pl111_drm.h"
+
+irqreturn_t pl111_irq(int irq, void *data)
+{
+       struct pl111_drm_dev_private *priv = data;
+       u32 irq_stat;
+       irqreturn_t status = IRQ_NONE;
+
+       irq_stat = readl(priv->regs + CLCD_PL111_MIS);
+
+       if (!irq_stat)
+               return IRQ_NONE;
+
+       if (irq_stat & CLCD_IRQ_NEXTBASE_UPDATE) {
+               drm_crtc_handle_vblank(&priv->pipe.crtc);
+
+               status = IRQ_HANDLED;
+       }
+
+       /* Clear the interrupt once done */
+       writel(irq_stat, priv->regs + CLCD_PL111_ICR);
+
+       return status;
+}
+
+static u32 pl111_get_fb_offset(struct drm_plane_state *pstate)
+{
+       struct drm_framebuffer *fb = pstate->fb;
+       struct drm_gem_cma_object *obj = drm_fb_cma_get_gem_obj(fb, 0);
+
+       return (obj->paddr +
+               fb->offsets[0] +
+               fb->format->cpp[0] * pstate->src_x +
+               fb->pitches[0] * pstate->src_y);
+}
+
+static int pl111_display_check(struct drm_simple_display_pipe *pipe,
+                              struct drm_plane_state *pstate,
+                              struct drm_crtc_state *cstate)
+{
+       const struct drm_display_mode *mode = &cstate->mode;
+       struct drm_framebuffer *old_fb = pipe->plane.state->fb;
+       struct drm_framebuffer *fb = pstate->fb;
+
+       if (mode->hdisplay % 16)
+               return -EINVAL;
+
+       if (fb) {
+               u32 offset = pl111_get_fb_offset(pstate);
+
+               /* FB base address must be dword aligned. */
+               if (offset & 3)
+                       return -EINVAL;
+
+               /* There's no pitch register -- the mode's hdisplay
+                * controls it.
+                */
+               if (fb->pitches[0] != mode->hdisplay * fb->format->cpp[0])
+                       return -EINVAL;
+
+               /* We can't change the FB format in a flicker-free
+                * manner (and only update it during CRTC enable).
+                */
+               if (old_fb && old_fb->format != fb->format)
+                       cstate->mode_changed = true;
+       }
+
+       return 0;
+}
+
+static void pl111_display_enable(struct drm_simple_display_pipe *pipe,
+                                struct drm_crtc_state *cstate)
+{
+       struct drm_crtc *crtc = &pipe->crtc;
+       struct drm_plane *plane = &pipe->plane;
+       struct drm_device *drm = crtc->dev;
+       struct pl111_drm_dev_private *priv = drm->dev_private;
+       const struct drm_display_mode *mode = &cstate->mode;
+       struct drm_framebuffer *fb = plane->state->fb;
+       struct drm_connector *connector = &priv->connector.connector;
+       u32 cntl;
+       u32 ppl, hsw, hfp, hbp;
+       u32 lpp, vsw, vfp, vbp;
+       u32 cpl;
+       int ret;
+
+       ret = clk_set_rate(priv->clk, mode->clock * 1000);
+       if (ret) {
+               dev_err(drm->dev,
+                       "Failed to set pixel clock rate to %d: %d\n",
+                       mode->clock * 1000, ret);
+       }
+
+       clk_prepare_enable(priv->clk);
+
+       ppl = (mode->hdisplay / 16) - 1;
+       hsw = mode->hsync_end - mode->hsync_start - 1;
+       hfp = mode->hsync_start - mode->hdisplay - 1;
+       hbp = mode->htotal - mode->hsync_end - 1;
+
+       lpp = mode->vdisplay - 1;
+       vsw = mode->vsync_end - mode->vsync_start - 1;
+       vfp = mode->vsync_start - mode->vdisplay;
+       vbp = mode->vtotal - mode->vsync_end;
+
+       cpl = mode->hdisplay - 1;
+
+       writel((ppl << 2) |
+              (hsw << 8) |
+              (hfp << 16) |
+              (hbp << 24),
+              priv->regs + CLCD_TIM0);
+       writel(lpp |
+              (vsw << 10) |
+              (vfp << 16) |
+              (vbp << 24),
+              priv->regs + CLCD_TIM1);
+       /* XXX: We currently always use CLCDCLK with no divisor.  We
+        * could probably reduce power consumption by using HCLK
+        * (apb_pclk) with a divisor when it gets us near our target
+        * pixel clock.
+        */
+       writel(((mode->flags & DRM_MODE_FLAG_NHSYNC) ? TIM2_IHS : 0) |
+              ((mode->flags & DRM_MODE_FLAG_NVSYNC) ? TIM2_IVS : 0) |
+              ((connector->display_info.bus_flags &
+                DRM_BUS_FLAG_DE_LOW) ? TIM2_IOE : 0) |
+              ((connector->display_info.bus_flags &
+                DRM_BUS_FLAG_PIXDATA_NEGEDGE) ? TIM2_IPC : 0) |
+              TIM2_BCD |
+              (cpl << 16),
+              priv->regs + CLCD_TIM2);
+       writel(0, priv->regs + CLCD_TIM3);
+
+       drm_panel_prepare(priv->connector.panel);
+
+       /* Enable and Power Up */
+       cntl = CNTL_LCDEN | CNTL_LCDTFT | CNTL_LCDPWR | CNTL_LCDVCOMP(1);
+
+       /* Note that the the hardware's format reader takes 'r' from
+        * the low bit, while DRM formats list channels from high bit
+        * to low bit as you read left to right.
+        */
+       switch (fb->format->format) {
+       case DRM_FORMAT_ABGR8888:
+       case DRM_FORMAT_XBGR8888:
+               cntl |= CNTL_LCDBPP24;
+               break;
+       case DRM_FORMAT_ARGB8888:
+       case DRM_FORMAT_XRGB8888:
+               cntl |= CNTL_LCDBPP24 | CNTL_BGR;
+               break;
+       case DRM_FORMAT_BGR565:
+               cntl |= CNTL_LCDBPP16_565;
+               break;
+       case DRM_FORMAT_RGB565:
+               cntl |= CNTL_LCDBPP16_565 | CNTL_BGR;
+               break;
+       case DRM_FORMAT_ABGR1555:
+       case DRM_FORMAT_XBGR1555:
+               cntl |= CNTL_LCDBPP16;
+               break;
+       case DRM_FORMAT_ARGB1555:
+       case DRM_FORMAT_XRGB1555:
+               cntl |= CNTL_LCDBPP16 | CNTL_BGR;
+               break;
+       case DRM_FORMAT_ABGR4444:
+       case DRM_FORMAT_XBGR4444:
+               cntl |= CNTL_LCDBPP16_444;
+               break;
+       case DRM_FORMAT_ARGB4444:
+       case DRM_FORMAT_XRGB4444:
+               cntl |= CNTL_LCDBPP16_444 | CNTL_BGR;
+               break;
+       default:
+               WARN_ONCE(true, "Unknown FB format 0x%08x\n",
+                         fb->format->format);
+               break;
+       }
+
+       writel(cntl, priv->regs + CLCD_PL111_CNTL);
+
+       drm_panel_enable(priv->connector.panel);
+
+       drm_crtc_vblank_on(crtc);
+}
+
+void pl111_display_disable(struct drm_simple_display_pipe *pipe)
+{
+       struct drm_crtc *crtc = &pipe->crtc;
+       struct drm_device *drm = crtc->dev;
+       struct pl111_drm_dev_private *priv = drm->dev_private;
+
+       drm_crtc_vblank_off(crtc);
+
+       drm_panel_disable(priv->connector.panel);
+
+       /* Disable and Power Down */
+       writel(0, priv->regs + CLCD_PL111_CNTL);
+
+       drm_panel_unprepare(priv->connector.panel);
+
+       clk_disable_unprepare(priv->clk);
+}
+
+static void pl111_display_update(struct drm_simple_display_pipe *pipe,
+                                struct drm_plane_state *old_pstate)
+{
+       struct drm_crtc *crtc = &pipe->crtc;
+       struct drm_device *drm = crtc->dev;
+       struct pl111_drm_dev_private *priv = drm->dev_private;
+       struct drm_pending_vblank_event *event = crtc->state->event;
+       struct drm_plane *plane = &pipe->plane;
+       struct drm_plane_state *pstate = plane->state;
+       struct drm_framebuffer *fb = pstate->fb;
+
+       if (fb) {
+               u32 addr = pl111_get_fb_offset(pstate);
+
+               writel(addr, priv->regs + CLCD_UBAS);
+       }
+
+       if (event) {
+               crtc->state->event = NULL;
+
+               spin_lock_irq(&crtc->dev->event_lock);
+               if (crtc->state->active && drm_crtc_vblank_get(crtc) == 0)
+                       drm_crtc_arm_vblank_event(crtc, event);
+               else
+                       drm_crtc_send_vblank_event(crtc, event);
+               spin_unlock_irq(&crtc->dev->event_lock);
+       }
+}
+
+int pl111_enable_vblank(struct drm_device *drm, unsigned int crtc)
+{
+       struct pl111_drm_dev_private *priv = drm->dev_private;
+
+       writel(CLCD_IRQ_NEXTBASE_UPDATE, priv->regs + CLCD_PL111_IENB);
+
+       return 0;
+}
+
+void pl111_disable_vblank(struct drm_device *drm, unsigned int crtc)
+{
+       struct pl111_drm_dev_private *priv = drm->dev_private;
+
+       writel(0, priv->regs + CLCD_PL111_IENB);
+}
+
+static int pl111_display_prepare_fb(struct drm_simple_display_pipe *pipe,
+                                   struct drm_plane_state *plane_state)
+{
+       return drm_fb_cma_prepare_fb(&pipe->plane, plane_state);
+}
+
+const struct drm_simple_display_pipe_funcs pl111_display_funcs = {
+       .check = pl111_display_check,
+       .enable = pl111_display_enable,
+       .disable = pl111_display_disable,
+       .update = pl111_display_update,
+       .prepare_fb = pl111_display_prepare_fb,
+};
+
+int pl111_display_init(struct drm_device *drm)
+{
+       struct pl111_drm_dev_private *priv = drm->dev_private;
+       struct device *dev = drm->dev;
+       struct device_node *endpoint;
+       u32 tft_r0b0g0[3];
+       int ret;
+       static const u32 formats[] = {
+               DRM_FORMAT_ABGR8888,
+               DRM_FORMAT_XBGR8888,
+               DRM_FORMAT_ARGB8888,
+               DRM_FORMAT_XRGB8888,
+               DRM_FORMAT_BGR565,
+               DRM_FORMAT_RGB565,
+               DRM_FORMAT_ABGR1555,
+               DRM_FORMAT_XBGR1555,
+               DRM_FORMAT_ARGB1555,
+               DRM_FORMAT_XRGB1555,
+               DRM_FORMAT_ABGR4444,
+               DRM_FORMAT_XBGR4444,
+               DRM_FORMAT_ARGB4444,
+               DRM_FORMAT_XRGB4444,
+       };
+
+       endpoint = of_graph_get_next_endpoint(dev->of_node, NULL);
+       if (!endpoint)
+               return -ENODEV;
+
+       if (of_property_read_u32_array(endpoint,
+                                      "arm,pl11x,tft-r0g0b0-pads",
+                                      tft_r0b0g0,
+                                      ARRAY_SIZE(tft_r0b0g0)) != 0) {
+               dev_err(dev, "arm,pl11x,tft-r0g0b0-pads should be 3 ints\n");
+               of_node_put(endpoint);
+               return -ENOENT;
+       }
+       of_node_put(endpoint);
+
+       if (tft_r0b0g0[0] != 0 ||
+           tft_r0b0g0[1] != 8 ||
+           tft_r0b0g0[2] != 16) {
+               dev_err(dev, "arm,pl11x,tft-r0g0b0-pads != [0,8,16] not yet supported\n");
+               return -EINVAL;
+       }
+
+       ret = drm_simple_display_pipe_init(drm, &priv->pipe,
+                                          &pl111_display_funcs,
+                                          formats, ARRAY_SIZE(formats),
+                                          &priv->connector.connector);
+       if (ret)
+               return ret;
+
+       return 0;
+}
diff --git a/drivers/gpu/drm/pl111/pl111_drm.h b/drivers/gpu/drm/pl111/pl111_drm.h
new file mode 100644 (file)
index 0000000..f381593
--- /dev/null
@@ -0,0 +1,56 @@
+/*
+ *
+ * (C) COPYRIGHT 2012-2013 ARM Limited. All rights reserved.
+ *
+ *
+ * Parts of this file were based on sources as follows:
+ *
+ * Copyright (c) 2006-2008 Intel Corporation
+ * Copyright (c) 2007 Dave Airlie <airlied@linux.ie>
+ * Copyright (C) 2011 Texas Instruments
+ *
+ * This program is free software and is provided to you under the terms of the
+ * GNU General Public License version 2 as published by the Free Software
+ * Foundation, and any use by you of this program is subject to the terms of
+ * such GNU licence.
+ *
+ */
+
+#ifndef _PL111_DRM_H_
+#define _PL111_DRM_H_
+
+#include <drm/drm_gem.h>
+#include <drm/drm_simple_kms_helper.h>
+
+#define CLCD_IRQ_NEXTBASE_UPDATE BIT(2)
+
+struct pl111_drm_connector {
+       struct drm_connector connector;
+       struct drm_panel *panel;
+};
+
+struct pl111_drm_dev_private {
+       struct drm_device *drm;
+
+       struct pl111_drm_connector connector;
+       struct drm_simple_display_pipe pipe;
+       struct drm_fbdev_cma *fbdev;
+
+       void *regs;
+       struct clk *clk;
+};
+
+#define to_pl111_connector(x) \
+       container_of(x, struct pl111_drm_connector, connector)
+
+int pl111_display_init(struct drm_device *dev);
+int pl111_enable_vblank(struct drm_device *drm, unsigned int crtc);
+void pl111_disable_vblank(struct drm_device *drm, unsigned int crtc);
+irqreturn_t pl111_irq(int irq, void *data);
+int pl111_connector_init(struct drm_device *dev);
+int pl111_encoder_init(struct drm_device *dev);
+int pl111_dumb_create(struct drm_file *file_priv,
+                     struct drm_device *dev,
+                     struct drm_mode_create_dumb *args);
+
+#endif /* _PL111_DRM_H_ */
diff --git a/drivers/gpu/drm/pl111/pl111_drv.c b/drivers/gpu/drm/pl111/pl111_drv.c
new file mode 100644 (file)
index 0000000..936403f
--- /dev/null
@@ -0,0 +1,272 @@
+/*
+ * (C) COPYRIGHT 2012-2013 ARM Limited. All rights reserved.
+ *
+ * Parts of this file were based on sources as follows:
+ *
+ * Copyright (c) 2006-2008 Intel Corporation
+ * Copyright (c) 2007 Dave Airlie <airlied@linux.ie>
+ * Copyright (C) 2011 Texas Instruments
+ *
+ * This program is free software and is provided to you under the terms of the
+ * GNU General Public License version 2 as published by the Free Software
+ * Foundation, and any use by you of this program is subject to the terms of
+ * such GNU licence.
+ *
+ */
+
+/**
+ * DOC: ARM PrimeCell PL111 CLCD Driver
+ *
+ * The PL111 is a simple LCD controller that can support TFT and STN
+ * displays.  This driver exposes a standard KMS interface for them.
+ *
+ * This driver uses the same Device Tree binding as the fbdev CLCD
+ * driver.  While the fbdev driver supports panels that may be
+ * connected to the CLCD internally to the CLCD driver, in DRM the
+ * panels get split out to drivers/gpu/drm/panels/.  This means that,
+ * in converting from using fbdev to using DRM, you also need to write
+ * a panel driver (which may be as simple as an entry in
+ * panel-simple.c).
+ *
+ * The driver currently doesn't expose the cursor.  The DRM API for
+ * cursors requires support for 64x64 ARGB8888 cursor images, while
+ * the hardware can only support 64x64 monochrome with masking
+ * cursors.  While one could imagine trying to hack something together
+ * to look at the ARGB8888 and program reasonable in monochrome, we
+ * just don't expose the cursor at all instead, and leave cursor
+ * support to the X11 software cursor layer.
+ *
+ * TODO:
+ *
+ * - Fix race between setting plane base address and getting IRQ for
+ *   vsync firing the pageflip completion.
+ *
+ * - Expose the correct set of formats we can support based on the
+ *   "arm,pl11x,tft-r0g0b0-pads" DT property.
+ *
+ * - Use the "max-memory-bandwidth" DT property to filter the
+ *   supported formats.
+ *
+ * - Read back hardware state at boot to skip reprogramming the
+ *   hardware when doing a no-op modeset.
+ *
+ * - Use the internal clock divisor to reduce power consumption by
+ *   using HCLK (apb_pclk) when appropriate.
+ */
+
+#include <linux/amba/bus.h>
+#include <linux/amba/clcd-regs.h>
+#include <linux/version.h>
+#include <linux/shmem_fs.h>
+#include <linux/dma-buf.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+
+#include <drm/drmP.h>
+#include <drm/drm_atomic_helper.h>
+#include <drm/drm_crtc_helper.h>
+#include <drm/drm_gem_cma_helper.h>
+#include <drm/drm_fb_cma_helper.h>
+
+#include "pl111_drm.h"
+
+#define DRIVER_DESC      "DRM module for PL111"
+
+struct drm_mode_config_funcs mode_config_funcs = {
+       .fb_create = drm_fb_cma_create,
+       .atomic_check = drm_atomic_helper_check,
+       .atomic_commit = drm_atomic_helper_commit,
+};
+
+static int pl111_modeset_init(struct drm_device *dev)
+{
+       struct drm_mode_config *mode_config;
+       struct pl111_drm_dev_private *priv = dev->dev_private;
+       int ret = 0;
+
+       drm_mode_config_init(dev);
+       mode_config = &dev->mode_config;
+       mode_config->funcs = &mode_config_funcs;
+       mode_config->min_width = 1;
+       mode_config->max_width = 1024;
+       mode_config->min_height = 1;
+       mode_config->max_height = 768;
+
+       ret = pl111_connector_init(dev);
+       if (ret) {
+               dev_err(dev->dev, "Failed to create pl111_drm_connector\n");
+               goto out_config;
+       }
+
+       /* Don't actually attach if we didn't find a drm_panel
+        * attached to us.  This will allow a kernel to include both
+        * the fbdev pl111 driver and this one, and choose between
+        * them based on which subsystem has support for the panel.
+        */
+       if (!priv->connector.panel) {
+               dev_info(dev->dev,
+                        "Disabling due to lack of DRM panel device.\n");
+               ret = -ENODEV;
+               goto out_config;
+       }
+
+       ret = pl111_display_init(dev);
+       if (ret != 0) {
+               dev_err(dev->dev, "Failed to init display\n");
+               goto out_config;
+       }
+
+       ret = drm_vblank_init(dev, 1);
+       if (ret != 0) {
+               dev_err(dev->dev, "Failed to init vblank\n");
+               goto out_config;
+       }
+
+       drm_mode_config_reset(dev);
+
+       priv->fbdev = drm_fbdev_cma_init(dev, 32,
+                                        dev->mode_config.num_connector);
+
+       drm_kms_helper_poll_init(dev);
+
+       goto finish;
+
+out_config:
+       drm_mode_config_cleanup(dev);
+finish:
+       return ret;
+}
+
+DEFINE_DRM_GEM_CMA_FOPS(drm_fops);
+
+static void pl111_lastclose(struct drm_device *dev)
+{
+       struct pl111_drm_dev_private *priv = dev->dev_private;
+
+       drm_fbdev_cma_restore_mode(priv->fbdev);
+}
+
+static struct drm_driver pl111_drm_driver = {
+       .driver_features =
+               DRIVER_MODESET | DRIVER_GEM | DRIVER_PRIME | DRIVER_ATOMIC,
+       .lastclose = pl111_lastclose,
+       .ioctls = NULL,
+       .fops = &drm_fops,
+       .name = "pl111",
+       .desc = DRIVER_DESC,
+       .date = "20170317",
+       .major = 1,
+       .minor = 0,
+       .patchlevel = 0,
+       .dumb_create = drm_gem_cma_dumb_create,
+       .dumb_destroy = drm_gem_dumb_destroy,
+       .dumb_map_offset = drm_gem_cma_dumb_map_offset,
+       .gem_free_object = drm_gem_cma_free_object,
+       .gem_vm_ops = &drm_gem_cma_vm_ops,
+
+       .enable_vblank = pl111_enable_vblank,
+       .disable_vblank = pl111_disable_vblank,
+
+       .prime_handle_to_fd = drm_gem_prime_handle_to_fd,
+       .prime_fd_to_handle = drm_gem_prime_fd_to_handle,
+       .gem_prime_import = drm_gem_prime_import,
+       .gem_prime_import_sg_table = drm_gem_cma_prime_import_sg_table,
+       .gem_prime_export = drm_gem_prime_export,
+       .gem_prime_get_sg_table = drm_gem_cma_prime_get_sg_table,
+};
+
+#ifdef CONFIG_ARM_AMBA
+static int pl111_amba_probe(struct amba_device *amba_dev,
+                           const struct amba_id *id)
+{
+       struct device *dev = &amba_dev->dev;
+       struct pl111_drm_dev_private *priv;
+       struct drm_device *drm;
+       int ret;
+
+       priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
+       if (!priv)
+               return -ENOMEM;
+
+       drm = drm_dev_alloc(&pl111_drm_driver, dev);
+       if (IS_ERR(drm))
+               return PTR_ERR(drm);
+       amba_set_drvdata(amba_dev, drm);
+       priv->drm = drm;
+       drm->dev_private = priv;
+
+       priv->clk = devm_clk_get(dev, "clcdclk");
+       if (IS_ERR(priv->clk)) {
+               dev_err(dev, "CLCD: unable to get clk.\n");
+               ret = PTR_ERR(priv->clk);
+               goto dev_unref;
+       }
+
+       priv->regs = devm_ioremap_resource(dev, &amba_dev->res);
+       if (!priv->regs) {
+               dev_err(dev, "%s failed mmio\n", __func__);
+               return -EINVAL;
+       }
+
+       /* turn off interrupts before requesting the irq */
+       writel(0, priv->regs + CLCD_PL111_IENB);
+
+       ret = devm_request_irq(dev, amba_dev->irq[0], pl111_irq, 0,
+                              "pl111", priv);
+       if (ret != 0) {
+               dev_err(dev, "%s failed irq %d\n", __func__, ret);
+               return ret;
+       }
+
+       ret = pl111_modeset_init(drm);
+       if (ret != 0)
+               goto dev_unref;
+
+       ret = drm_dev_register(drm, 0);
+       if (ret < 0)
+               goto dev_unref;
+
+       return 0;
+
+dev_unref:
+       drm_dev_unref(drm);
+       return ret;
+}
+
+static int pl111_amba_remove(struct amba_device *amba_dev)
+{
+       struct drm_device *drm = amba_get_drvdata(amba_dev);
+       struct pl111_drm_dev_private *priv = drm->dev_private;
+
+       drm_dev_unregister(drm);
+       if (priv->fbdev)
+               drm_fbdev_cma_fini(priv->fbdev);
+       drm_mode_config_cleanup(drm);
+       drm_dev_unref(drm);
+
+       return 0;
+}
+
+static struct amba_id pl111_id_table[] = {
+       {
+               .id = 0x00041111,
+               .mask = 0x000fffff,
+       },
+       {0, 0},
+};
+
+static struct amba_driver pl111_amba_driver = {
+       .drv = {
+               .name = "drm-clcd-pl111",
+       },
+       .probe = pl111_amba_probe,
+       .remove = pl111_amba_remove,
+       .id_table = pl111_id_table,
+};
+
+module_amba_driver(pl111_amba_driver);
+#endif /* CONFIG_ARM_AMBA */
+
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_AUTHOR("ARM Ltd.");
+MODULE_LICENSE("GPL");
index 93d45aa5c3d4f73bb421c5e4f2a844ef56f6752a..ef8a759409804bf74076986395fee5e536e674a5 100644 (file)
@@ -115,10 +115,6 @@ int radeon_resume_kms(struct drm_device *dev, bool resume, bool fbcon);
 u32 radeon_get_vblank_counter_kms(struct drm_device *dev, unsigned int pipe);
 int radeon_enable_vblank_kms(struct drm_device *dev, unsigned int pipe);
 void radeon_disable_vblank_kms(struct drm_device *dev, unsigned int pipe);
-int radeon_get_vblank_timestamp_kms(struct drm_device *dev, unsigned int pipe,
-                                   int *max_error,
-                                   struct timeval *vblank_time,
-                                   unsigned flags);
 void radeon_driver_irq_preinstall_kms(struct drm_device *dev);
 int radeon_driver_irq_postinstall_kms(struct drm_device *dev);
 void radeon_driver_irq_uninstall_kms(struct drm_device *dev);
@@ -530,6 +526,16 @@ static const struct file_operations radeon_driver_kms_fops = {
 #endif
 };
 
+static bool
+radeon_get_crtc_scanout_position(struct drm_device *dev, unsigned int pipe,
+                                bool in_vblank_irq, int *vpos, int *hpos,
+                                ktime_t *stime, ktime_t *etime,
+                                const struct drm_display_mode *mode)
+{
+       return radeon_get_crtc_scanoutpos(dev, pipe, 0, vpos, hpos,
+                                         stime, etime, mode);
+}
+
 static struct drm_driver kms_driver = {
        .driver_features =
            DRIVER_USE_AGP |
@@ -544,8 +550,8 @@ static struct drm_driver kms_driver = {
        .get_vblank_counter = radeon_get_vblank_counter_kms,
        .enable_vblank = radeon_enable_vblank_kms,
        .disable_vblank = radeon_disable_vblank_kms,
-       .get_vblank_timestamp = radeon_get_vblank_timestamp_kms,
-       .get_scanout_position = radeon_get_crtc_scanoutpos,
+       .get_vblank_timestamp = drm_calc_vbltimestamp_from_scanoutpos,
+       .get_scanout_position = radeon_get_crtc_scanout_position,
        .irq_preinstall = radeon_driver_irq_preinstall_kms,
        .irq_postinstall = radeon_driver_irq_postinstall_kms,
        .irq_uninstall = radeon_driver_irq_uninstall_kms,
index e3e7cb1d10a2941d1790d36a9f5250f70aeb78cd..6a68d440bc4486b73a8c3433dd5083c42fb8d2ef 100644 (file)
@@ -858,43 +858,6 @@ void radeon_disable_vblank_kms(struct drm_device *dev, int crtc)
        spin_unlock_irqrestore(&rdev->irq.lock, irqflags);
 }
 
-/**
- * radeon_get_vblank_timestamp_kms - get vblank timestamp
- *
- * @dev: drm dev pointer
- * @crtc: crtc to get the timestamp for
- * @max_error: max error
- * @vblank_time: time value
- * @flags: flags passed to the driver
- *
- * Gets the timestamp on the requested crtc based on the
- * scanout position.  (all asics).
- * Returns postive status flags on success, negative error on failure.
- */
-int radeon_get_vblank_timestamp_kms(struct drm_device *dev, int crtc,
-                                   int *max_error,
-                                   struct timeval *vblank_time,
-                                   unsigned flags)
-{
-       struct drm_crtc *drmcrtc;
-       struct radeon_device *rdev = dev->dev_private;
-
-       if (crtc < 0 || crtc >= dev->num_crtcs) {
-               DRM_ERROR("Invalid crtc %d\n", crtc);
-               return -EINVAL;
-       }
-
-       /* Get associated drm_crtc: */
-       drmcrtc = &rdev->mode_info.crtcs[crtc]->base;
-       if (!drmcrtc)
-               return -EINVAL;
-
-       /* Helper routine in DRM core does all the work: */
-       return drm_calc_vbltimestamp_from_scanoutpos(dev, crtc, max_error,
-                                                    vblank_time, flags,
-                                                    &drmcrtc->hwmode);
-}
-
 const struct drm_ioctl_desc radeon_ioctls_kms[] = {
        DRM_IOCTL_DEF_DRV(RADEON_CP_INIT, drm_invalid_op, DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY),
        DRM_IOCTL_DEF_DRV(RADEON_CP_START, drm_invalid_op, DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY),
index ad282648fc8b1e70adcd135b5618a56d818823ce..00f5ec5c12c7afba423876584648aa0ce7d05131 100644 (file)
@@ -691,6 +691,9 @@ struct atom_voltage_table
 };
 
 /* Driver internal use only flags of radeon_get_crtc_scanoutpos() */
+#define DRM_SCANOUTPOS_VALID        (1 << 0)
+#define DRM_SCANOUTPOS_IN_VBLANK    (1 << 1)
+#define DRM_SCANOUTPOS_ACCURATE     (1 << 2)
 #define USE_REAL_VBLANKSTART           (1 << 30)
 #define GET_DISTANCE_TO_VBLANKSTART    (1 << 31)
 
index d8fa7a9c9240bdf53f06d214f5952af7f85e5ee4..1bccd827d2e41da2721b4db7d6aad76b4abcc831 100644 (file)
@@ -104,26 +104,18 @@ static void analogix_dp_psr_work(struct work_struct *work)
 {
        struct rockchip_dp_device *dp =
                                container_of(work, typeof(*dp), psr_work);
-       struct drm_crtc *crtc = dp->encoder.crtc;
-       int psr_state = dp->psr_state;
-       int vact_end;
        int ret;
        unsigned long flags;
 
-       if (!crtc)
-               return;
-
-       vact_end = crtc->mode.vtotal - crtc->mode.vsync_start + crtc->mode.vdisplay;
-
-       ret = rockchip_drm_wait_line_flag(dp->encoder.crtc, vact_end,
-                                         PSR_WAIT_LINE_FLAG_TIMEOUT_MS);
+       ret = rockchip_drm_wait_vact_end(dp->encoder.crtc,
+                                        PSR_WAIT_LINE_FLAG_TIMEOUT_MS);
        if (ret) {
                dev_err(dp->dev, "line flag interrupt did not arrive\n");
                return;
        }
 
        spin_lock_irqsave(&dp->psr_lock, flags);
-       if (psr_state == EDP_VSC_PSR_STATE_ACTIVE)
+       if (dp->psr_state == EDP_VSC_PSR_STATE_ACTIVE)
                analogix_dp_enable_psr(dp->dev);
        else
                analogix_dp_disable_psr(dp->dev);
index a48fcce3f5f65edb70ca216583a87c7a1ae5a11c..47905faf55863f93b5768756f8f667ddcec5226a 100644 (file)
@@ -62,8 +62,7 @@ int rockchip_drm_dma_attach_device(struct drm_device *drm_dev,
                                   struct device *dev);
 void rockchip_drm_dma_detach_device(struct drm_device *drm_dev,
                                    struct device *dev);
-int rockchip_drm_wait_line_flag(struct drm_crtc *crtc, unsigned int line_num,
-                               unsigned int mstimeout);
+int rockchip_drm_wait_vact_end(struct drm_crtc *crtc, unsigned int mstimeout);
 
 extern struct platform_driver cdn_dp_driver;
 extern struct platform_driver dw_hdmi_rockchip_pltfm_driver;
index 3f7a82d1e0956e6a37e1478412210955db38aa19..40a5e6ef6f2c858fabe5908fb40a6f93dd0b9846 100644 (file)
@@ -468,7 +468,7 @@ static bool vop_line_flag_irq_is_enabled(struct vop *vop)
        return !!line_flag_irq;
 }
 
-static void vop_line_flag_irq_enable(struct vop *vop, int line_num)
+static void vop_line_flag_irq_enable(struct vop *vop)
 {
        unsigned long flags;
 
@@ -477,7 +477,6 @@ static void vop_line_flag_irq_enable(struct vop *vop, int line_num)
 
        spin_lock_irqsave(&vop->irq_lock, flags);
 
-       VOP_CTRL_SET(vop, line_flag_num[0], line_num);
        VOP_INTR_SET_TYPE(vop, clear, LINE_FLAG_INTR, 1);
        VOP_INTR_SET_TYPE(vop, enable, LINE_FLAG_INTR, 1);
 
@@ -981,6 +980,8 @@ static void vop_crtc_enable(struct drm_crtc *crtc)
        VOP_CTRL_SET(vop, vact_st_end, val);
        VOP_CTRL_SET(vop, vpost_st_end, val);
 
+       VOP_CTRL_SET(vop, line_flag_num[0], vact_end);
+
        clk_set_rate(vop->dclk, adjusted_mode->clock * 1000);
 
        VOP_CTRL_SET(vop, standby, 0);
@@ -1507,19 +1508,16 @@ static void vop_win_init(struct vop *vop)
 }
 
 /**
- * rockchip_drm_wait_line_flag - acqiure the give line flag event
+ * rockchip_drm_wait_vact_end
  * @crtc: CRTC to enable line flag
- * @line_num: interested line number
  * @mstimeout: millisecond for timeout
  *
- * Driver would hold here until the interested line flag interrupt have
- * happened or timeout to wait.
+ * Wait for vact_end line flag irq or timeout.
  *
  * Returns:
  * Zero on success, negative errno on failure.
  */
-int rockchip_drm_wait_line_flag(struct drm_crtc *crtc, unsigned int line_num,
-                               unsigned int mstimeout)
+int rockchip_drm_wait_vact_end(struct drm_crtc *crtc, unsigned int mstimeout)
 {
        struct vop *vop = to_vop(crtc);
        unsigned long jiffies_left;
@@ -1527,14 +1525,14 @@ int rockchip_drm_wait_line_flag(struct drm_crtc *crtc, unsigned int line_num,
        if (!crtc || !vop->is_enabled)
                return -ENODEV;
 
-       if (line_num > crtc->mode.vtotal || mstimeout <= 0)
+       if (mstimeout <= 0)
                return -EINVAL;
 
        if (vop_line_flag_irq_is_enabled(vop))
                return -EBUSY;
 
        reinit_completion(&vop->line_flag_completion);
-       vop_line_flag_irq_enable(vop, line_num);
+       vop_line_flag_irq_enable(vop);
 
        jiffies_left = wait_for_completion_timeout(&vop->line_flag_completion,
                                                   msecs_to_jiffies(mstimeout));
@@ -1547,7 +1545,7 @@ int rockchip_drm_wait_line_flag(struct drm_crtc *crtc, unsigned int line_num,
 
        return 0;
 }
-EXPORT_SYMBOL(rockchip_drm_wait_line_flag);
+EXPORT_SYMBOL(rockchip_drm_wait_vact_end);
 
 static int vop_bind(struct device *dev, struct device *master, void *data)
 {
index fa356f5dae27d309978424137236c6ac81a61436..dfdd858eda0a0640addf1511948d78c0e04678dd 100644 (file)
@@ -514,6 +514,8 @@ static int igt_reserve(void *ignored)
                ret = __igt_reserve(count, size + 1);
                if (ret)
                        return ret;
+
+               cond_resched();
        }
 
        return 0;
@@ -712,6 +714,10 @@ static int igt_insert(void *ignored)
                        return ret;
 
                ret = __igt_insert(count, size + 1, false);
+               if (ret)
+                       return ret;
+
+               cond_resched();
        }
 
        return 0;
@@ -741,6 +747,10 @@ static int igt_replace(void *ignored)
                        return ret;
 
                ret = __igt_insert(count, size + 1, true);
+               if (ret)
+                       return ret;
+
+               cond_resched();
        }
 
        return 0;
@@ -1011,6 +1021,8 @@ static int igt_insert_range(void *ignored)
                ret = __igt_insert_range(count, size, max/4+1, 3*max/4-1);
                if (ret)
                        return ret;
+
+               cond_resched();
        }
 
        return 0;
@@ -1056,6 +1068,7 @@ static int igt_align(void *ignored)
                drm_mm_for_each_node_safe(node, next, &mm)
                        drm_mm_remove_node(node);
                DRM_MM_BUG_ON(!drm_mm_clean(&mm));
+               cond_resched();
        }
 
        ret = 0;
@@ -1097,6 +1110,8 @@ static int igt_align_pot(int max)
                               align, bit);
                        goto out;
                }
+
+               cond_resched();
        }
 
        ret = 0;
@@ -1471,6 +1486,8 @@ static int igt_evict(void *ignored)
                                goto out;
                        }
                }
+
+               cond_resched();
        }
 
        ret = 0;
@@ -1566,6 +1583,8 @@ static int igt_evict_range(void *ignored)
                                goto out;
                        }
                }
+
+               cond_resched();
        }
 
        ret = 0;
@@ -1683,6 +1702,7 @@ static int igt_topdown(void *ignored)
                drm_mm_for_each_node_safe(node, next, &mm)
                        drm_mm_remove_node(node);
                DRM_MM_BUG_ON(!drm_mm_clean(&mm));
+               cond_resched();
        }
 
        ret = 0;
@@ -1783,6 +1803,7 @@ static int igt_bottomup(void *ignored)
                drm_mm_for_each_node_safe(node, next, &mm)
                        drm_mm_remove_node(node);
                DRM_MM_BUG_ON(!drm_mm_clean(&mm));
+               cond_resched();
        }
 
        ret = 0;
@@ -1970,6 +1991,8 @@ static int igt_color(void *ignored)
                        drm_mm_remove_node(node);
                        kfree(node);
                }
+
+               cond_resched();
        }
 
        ret = 0;
@@ -2047,6 +2070,7 @@ static int evict_color(struct drm_mm *mm,
                }
        }
 
+       cond_resched();
        return 0;
 }
 
@@ -2132,6 +2156,8 @@ static int igt_color_evict(void *ignored)
                                goto out;
                        }
                }
+
+               cond_resched();
        }
 
        ret = 0;
@@ -2231,6 +2257,8 @@ static int igt_color_evict_range(void *ignored)
                                goto out;
                        }
                }
+
+               cond_resched();
        }
 
        ret = 0;
index cca75bddb9ad1aa6d77cc317bfb3d56f36c79c6b..5b3a41f74f21b2da5d130ffba0d8d947d9b44de8 100644 (file)
@@ -33,7 +33,7 @@
 #define STI_CURS_MAX_SIZE   128
 
 /*
- * pixmap dma buffer stucture
+ * pixmap dma buffer structure
  *
  * @paddr:  physical address
  * @size:   buffer size
@@ -121,8 +121,7 @@ static int cursor_dbg_show(struct seq_file *s, void *data)
        cursor_dbg_cml(s, cursor, readl(cursor->regs + CUR_CML));
        DBGFS_DUMP(CUR_AWS);
        DBGFS_DUMP(CUR_AWE);
-       seq_puts(s, "\n");
-
+       seq_putc(s, '\n');
        return 0;
 }
 
index bb23318a44b714a3d5d98883fcdc00234ddd3f16..24ebc6b2f34d1db80fdd0645b34e71badcb1df68 100644 (file)
@@ -186,8 +186,7 @@ static int dvo_dbg_show(struct seq_file *s, void *data)
        DBGFS_DUMP(DVO_LUT_PROG_MID);
        DBGFS_DUMP(DVO_LUT_PROG_HIGH);
        dvo_dbg_awg_microcode(s, dvo->regs + DVO_DIGSYNC_INSTR_I);
-       seq_puts(s, "\n");
-
+       seq_putc(s, '\n');
        return 0;
 }
 
index 88f16cdf6a4bcee03adc0c1145eaae317eb7d2ef..5ee0503945c8bfb3a7224aa509e4fba4af2a2751 100644 (file)
@@ -149,7 +149,7 @@ static void gdp_dbg_ctl(struct seq_file *s, int val)
        seq_puts(s, "\tColor:");
        for (i = 0; i < ARRAY_SIZE(gdp_format_to_str); i++) {
                if (gdp_format_to_str[i].format == (val & 0x1F)) {
-                       seq_printf(s, gdp_format_to_str[i].name);
+                       seq_puts(s, gdp_format_to_str[i].name);
                        break;
                }
        }
@@ -266,8 +266,7 @@ static void gdp_node_dump_node(struct seq_file *s, struct sti_gdp_node *node)
        seq_printf(s, "\n\tKEY2 0x%08X", node->gam_gdp_key2);
        seq_printf(s, "\n\tPPT  0x%08X", node->gam_gdp_ppt);
        gdp_dbg_ppt(s, node->gam_gdp_ppt);
-       seq_printf(s, "\n\tCML  0x%08X", node->gam_gdp_cml);
-       seq_puts(s, "\n");
+       seq_printf(s, "\n\tCML  0x%08X\n", node->gam_gdp_cml);
 }
 
 static int gdp_node_dbg_show(struct seq_file *s, void *arg)
index 0c0a75bc8bc3f6ae4f60a8fe15826dc34b11ce84..d6ed909d9d75ffb45b61b364aea973d6b8751967 100644 (file)
@@ -320,8 +320,7 @@ static void hda_dbg_awg_microcode(struct seq_file *s, void __iomem *reg)
 {
        unsigned int i;
 
-       seq_puts(s, "\n\n");
-       seq_puts(s, "  HDA AWG microcode:");
+       seq_puts(s, "\n\n  HDA AWG microcode:");
        for (i = 0; i < AWG_MAX_INST; i++) {
                if (i % 8 == 0)
                        seq_printf(s, "\n  %04X:", i);
@@ -333,8 +332,7 @@ static void hda_dbg_video_dacs_ctrl(struct seq_file *s, void __iomem *reg)
 {
        u32 val = readl(reg);
 
-       seq_puts(s, "\n");
-       seq_printf(s, "\n  %-25s 0x%08X", "VIDEO_DACS_CONTROL", val);
+       seq_printf(s, "\n\n  %-25s 0x%08X", "VIDEO_DACS_CONTROL", val);
        seq_puts(s, "\tHD DACs ");
        seq_puts(s, val & DAC_CFG_HD_HZUVW_OFF_MASK ? "disabled" : "enabled");
 }
@@ -356,8 +354,7 @@ static int hda_dbg_show(struct seq_file *s, void *data)
        hda_dbg_awg_microcode(s, hda->regs + HDA_SYNC_AWGI);
        if (hda->video_dacs_ctrl)
                hda_dbg_video_dacs_ctrl(s, hda->video_dacs_ctrl);
-       seq_puts(s, "\n");
-
+       seq_putc(s, '\n');
        return 0;
 }
 
index 243905b6ae59034cf5ad4711f4703e63ba67a6bc..a59c95a8081b7acce1161b6dca34e027b8975825 100644 (file)
@@ -592,7 +592,7 @@ static void hdmi_dbg_cfg(struct seq_file *s, int val)
 {
        int tmp;
 
-       seq_puts(s, "\t");
+       seq_putc(s, '\t');
        tmp = val & HDMI_CFG_HDMI_NOT_DVI;
        DBGFS_PRINT_STR("mode:", tmp ? "HDMI" : "DVI");
        seq_puts(s, "\t\t\t\t\t");
@@ -616,7 +616,7 @@ static void hdmi_dbg_sta(struct seq_file *s, int val)
 {
        int tmp;
 
-       seq_puts(s, "\t");
+       seq_putc(s, '\t');
        tmp = (val & HDMI_STA_DLL_LCK);
        DBGFS_PRINT_STR("pll:", tmp ? "locked" : "not locked");
        seq_puts(s, "\t\t\t\t\t");
@@ -632,7 +632,7 @@ static void hdmi_dbg_sw_di_cfg(struct seq_file *s, int val)
                               "once every field",
                               "once every frame"};
 
-       seq_puts(s, "\t");
+       seq_putc(s, '\t');
        tmp = (val & HDMI_IFRAME_CFG_DI_N(HDMI_IFRAME_MASK, 1));
        DBGFS_PRINT_STR("Data island 1:", en_di[tmp]);
        seq_puts(s, "\t\t\t\t\t");
@@ -664,16 +664,16 @@ static int hdmi_dbg_show(struct seq_file *s, void *data)
        DBGFS_DUMP("\n", HDMI_STA);
        hdmi_dbg_sta(s, hdmi_read(hdmi, HDMI_STA));
        DBGFS_DUMP("", HDMI_ACTIVE_VID_XMIN);
-       seq_puts(s, "\t");
+       seq_putc(s, '\t');
        DBGFS_PRINT_INT("Xmin:", hdmi_read(hdmi, HDMI_ACTIVE_VID_XMIN));
        DBGFS_DUMP("", HDMI_ACTIVE_VID_XMAX);
-       seq_puts(s, "\t");
+       seq_putc(s, '\t');
        DBGFS_PRINT_INT("Xmax:", hdmi_read(hdmi, HDMI_ACTIVE_VID_XMAX));
        DBGFS_DUMP("", HDMI_ACTIVE_VID_YMIN);
-       seq_puts(s, "\t");
+       seq_putc(s, '\t');
        DBGFS_PRINT_INT("Ymin:", hdmi_read(hdmi, HDMI_ACTIVE_VID_YMIN));
        DBGFS_DUMP("", HDMI_ACTIVE_VID_YMAX);
-       seq_puts(s, "\t");
+       seq_putc(s, '\t');
        DBGFS_PRINT_INT("Ymax:", hdmi_read(hdmi, HDMI_ACTIVE_VID_YMAX));
        DBGFS_DUMP("", HDMI_SW_DI_CFG);
        hdmi_dbg_sw_di_cfg(s, hdmi_read(hdmi, HDMI_SW_DI_CFG));
@@ -692,8 +692,7 @@ static int hdmi_dbg_show(struct seq_file *s, void *data)
        DBGFS_DUMP_DI(HDMI_SW_DI_N_PKT_WORD4, HDMI_IFRAME_SLOT_AVI);
        DBGFS_DUMP_DI(HDMI_SW_DI_N_PKT_WORD5, HDMI_IFRAME_SLOT_AVI);
        DBGFS_DUMP_DI(HDMI_SW_DI_N_PKT_WORD6, HDMI_IFRAME_SLOT_AVI);
-       seq_puts(s, "\n");
-       seq_printf(s, "\n AUDIO Infoframe (Data Island slot N=%d):",
+       seq_printf(s, "\n\n AUDIO Infoframe (Data Island slot N=%d):",
                   HDMI_IFRAME_SLOT_AUDIO);
        DBGFS_DUMP_DI(HDMI_SW_DI_N_HEAD_WORD, HDMI_IFRAME_SLOT_AUDIO);
        DBGFS_DUMP_DI(HDMI_SW_DI_N_PKT_WORD0, HDMI_IFRAME_SLOT_AUDIO);
@@ -703,8 +702,7 @@ static int hdmi_dbg_show(struct seq_file *s, void *data)
        DBGFS_DUMP_DI(HDMI_SW_DI_N_PKT_WORD4, HDMI_IFRAME_SLOT_AUDIO);
        DBGFS_DUMP_DI(HDMI_SW_DI_N_PKT_WORD5, HDMI_IFRAME_SLOT_AUDIO);
        DBGFS_DUMP_DI(HDMI_SW_DI_N_PKT_WORD6, HDMI_IFRAME_SLOT_AUDIO);
-       seq_puts(s, "\n");
-       seq_printf(s, "\n VENDOR SPECIFIC Infoframe (Data Island slot N=%d):",
+       seq_printf(s, "\n\n VENDOR SPECIFIC Infoframe (Data Island slot N=%d):",
                   HDMI_IFRAME_SLOT_VENDOR);
        DBGFS_DUMP_DI(HDMI_SW_DI_N_HEAD_WORD, HDMI_IFRAME_SLOT_VENDOR);
        DBGFS_DUMP_DI(HDMI_SW_DI_N_PKT_WORD0, HDMI_IFRAME_SLOT_VENDOR);
@@ -714,8 +712,7 @@ static int hdmi_dbg_show(struct seq_file *s, void *data)
        DBGFS_DUMP_DI(HDMI_SW_DI_N_PKT_WORD4, HDMI_IFRAME_SLOT_VENDOR);
        DBGFS_DUMP_DI(HDMI_SW_DI_N_PKT_WORD5, HDMI_IFRAME_SLOT_VENDOR);
        DBGFS_DUMP_DI(HDMI_SW_DI_N_PKT_WORD6, HDMI_IFRAME_SLOT_VENDOR);
-       seq_puts(s, "\n");
-
+       seq_putc(s, '\n');
        return 0;
 }
 
index 66f843148ef7f5cd25e515fcc49ef4bfc4acba2f..a1c161f77804424e0014c93478f92847559ea484 100644 (file)
@@ -625,8 +625,7 @@ static int hqvdp_dbg_show(struct seq_file *s, void *data)
                hqvdp_dbg_dump_cmd(s, (struct sti_hqvdp_cmd *)virt);
        }
 
-       seq_puts(s, "\n");
-
+       seq_putc(s, '\n');
        return 0;
 }
 
@@ -1357,12 +1356,12 @@ static int sti_hqvdp_probe(struct platform_device *pdev)
 
        /* Get Memory resources */
        res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
-       if (res == NULL) {
+       if (!res) {
                DRM_ERROR("Get memory resource failed\n");
                return -ENXIO;
        }
        hqvdp->regs = devm_ioremap(dev, res->start, resource_size(res));
-       if (hqvdp->regs == NULL) {
+       if (!hqvdp->regs) {
                DRM_ERROR("Register mapping failed\n");
                return -ENXIO;
        }
index 4ddc58f7fe2e98bfb8e1f69123263ae22e5358c1..2bd1d46fe1cd32f5f8256fdc22688c5685f8185e 100644 (file)
@@ -162,8 +162,7 @@ static int mixer_dbg_show(struct seq_file *s, void *arg)
        DBGFS_DUMP(GAM_MIXER_MBP);
        DBGFS_DUMP(GAM_MIXER_MX0);
        mixer_dbg_mxn(s, mixer->regs + GAM_MIXER_MX0);
-       seq_puts(s, "\n");
-
+       seq_putc(s, '\n');
        return 0;
 }
 
index 8b8ea717c121f0ba89809bcb0aa4caff31c16305..8959fcc743a8edc0b71eded24192e7f483a143ff 100644 (file)
@@ -459,7 +459,7 @@ static void tvout_dbg_vip(struct seq_file *s, int val)
                                   "Aux (color matrix by-passed)",
                                   "", "", "", "", "", "Force value"};
 
-       seq_puts(s, "\t");
+       seq_putc(s, '\t');
        mask = TVO_VIP_REORDER_MASK << TVO_VIP_REORDER_R_SHIFT;
        r = (val & mask) >> TVO_VIP_REORDER_R_SHIFT;
        mask = TVO_VIP_REORDER_MASK << TVO_VIP_REORDER_G_SHIFT;
@@ -558,8 +558,7 @@ static int tvout_dbg_show(struct seq_file *s, void *data)
        DBGFS_DUMP(TVO_CSC_AUX_M6);
        DBGFS_DUMP(TVO_CSC_AUX_M7);
        DBGFS_DUMP(TVO_AUX_IN_VID_FORMAT);
-       seq_puts(s, "\n");
-
+       seq_putc(s, '\n');
        return 0;
 }
 
@@ -847,7 +846,7 @@ static int sti_tvout_probe(struct platform_device *pdev)
 
        tvout->dev = dev;
 
-       /* get Memory ressources */
+       /* get memory resources */
        res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "tvout-reg");
        if (!res) {
                DRM_ERROR("Invalid glue resource\n");
index 2ad59892b57e254fffa4467aebe8039d8260181f..577a3341d3c13f18cba9943a68b9fdb72fd70fb3 100644 (file)
@@ -61,7 +61,7 @@
 static void vid_dbg_ctl(struct seq_file *s, int val)
 {
        val = val >> 30;
-       seq_puts(s, "\t");
+       seq_putc(s, '\t');
 
        if (!(val & 1))
                seq_puts(s, "NOT ");
@@ -114,8 +114,7 @@ static int vid_dbg_show(struct seq_file *s, void *arg)
        DBGFS_DUMP(VID_BC);
        DBGFS_DUMP(VID_TINT);
        DBGFS_DUMP(VID_CSAT);
-       seq_puts(s, "\n");
-
+       seq_putc(s, '\n');
        return 0;
 }
 
diff --git a/drivers/gpu/drm/stm/Kconfig b/drivers/gpu/drm/stm/Kconfig
new file mode 100644 (file)
index 0000000..2c4817f
--- /dev/null
@@ -0,0 +1,16 @@
+config DRM_STM
+       tristate "DRM Support for STMicroelectronics SoC Series"
+       depends on DRM && (ARCH_STM32 || ARCH_MULTIPLATFORM)
+       select DRM_KMS_HELPER
+       select DRM_GEM_CMA_HELPER
+       select DRM_KMS_CMA_HELPER
+       select DRM_PANEL
+       select VIDEOMODE_HELPERS
+       select FB_PROVIDE_GET_FB_UNMAPPED_AREA
+       default y
+
+       help
+         Enable support for the on-chip display controller on
+         STMicroelectronics STM32 MCUs.
+         To compile this driver as a module, choose M here: the module
+         will be called stm-drm.
diff --git a/drivers/gpu/drm/stm/Makefile b/drivers/gpu/drm/stm/Makefile
new file mode 100644 (file)
index 0000000..e114d45
--- /dev/null
@@ -0,0 +1,7 @@
+ccflags-y := -Iinclude/drm
+
+stm-drm-y := \
+       drv.o \
+       ltdc.o
+
+obj-$(CONFIG_DRM_STM) += stm-drm.o
diff --git a/drivers/gpu/drm/stm/drv.c b/drivers/gpu/drm/stm/drv.c
new file mode 100644 (file)
index 0000000..83ab48f
--- /dev/null
@@ -0,0 +1,221 @@
+/*
+ * Copyright (C) STMicroelectronics SA 2017
+ *
+ * Authors: Philippe Cornu <philippe.cornu@st.com>
+ *          Yannick Fertre <yannick.fertre@st.com>
+ *          Fabien Dessenne <fabien.dessenne@st.com>
+ *          Mickael Reulier <mickael.reulier@st.com>
+ *
+ * License terms:  GNU General Public License (GPL), version 2
+ */
+
+#include <linux/component.h>
+#include <linux/of_platform.h>
+
+#include <drm/drm_atomic.h>
+#include <drm/drm_atomic_helper.h>
+#include <drm/drm_crtc_helper.h>
+#include <drm/drm_fb_cma_helper.h>
+#include <drm/drm_gem_cma_helper.h>
+
+#include "ltdc.h"
+
+#define DRIVER_NAME            "stm"
+#define DRIVER_DESC            "STMicroelectronics SoC DRM"
+#define DRIVER_DATE            "20170330"
+#define DRIVER_MAJOR           1
+#define DRIVER_MINOR           0
+#define DRIVER_PATCH_LEVEL     0
+
+#define STM_MAX_FB_WIDTH       2048
+#define STM_MAX_FB_HEIGHT      2048 /* same as width to handle orientation */
+
+static void drv_output_poll_changed(struct drm_device *ddev)
+{
+       struct ltdc_device *ldev = ddev->dev_private;
+
+       drm_fbdev_cma_hotplug_event(ldev->fbdev);
+}
+
+static const struct drm_mode_config_funcs drv_mode_config_funcs = {
+       .fb_create = drm_fb_cma_create,
+       .output_poll_changed = drv_output_poll_changed,
+       .atomic_check = drm_atomic_helper_check,
+       .atomic_commit = drm_atomic_helper_commit,
+};
+
+static void drv_lastclose(struct drm_device *ddev)
+{
+       struct ltdc_device *ldev = ddev->dev_private;
+
+       DRM_DEBUG("%s\n", __func__);
+
+       drm_fbdev_cma_restore_mode(ldev->fbdev);
+}
+
+DEFINE_DRM_GEM_CMA_FOPS(drv_driver_fops);
+
+static struct drm_driver drv_driver = {
+       .driver_features = DRIVER_MODESET | DRIVER_GEM | DRIVER_PRIME |
+                          DRIVER_ATOMIC,
+       .lastclose = drv_lastclose,
+       .name = DRIVER_NAME,
+       .desc = DRIVER_DESC,
+       .date = DRIVER_DATE,
+       .major = DRIVER_MAJOR,
+       .minor = DRIVER_MINOR,
+       .patchlevel = DRIVER_PATCH_LEVEL,
+       .fops = &drv_driver_fops,
+       .dumb_create = drm_gem_cma_dumb_create,
+       .dumb_map_offset = drm_gem_cma_dumb_map_offset,
+       .dumb_destroy = drm_gem_dumb_destroy,
+       .prime_handle_to_fd = drm_gem_prime_handle_to_fd,
+       .prime_fd_to_handle = drm_gem_prime_fd_to_handle,
+       .gem_free_object_unlocked = drm_gem_cma_free_object,
+       .gem_vm_ops = &drm_gem_cma_vm_ops,
+       .gem_prime_export = drm_gem_prime_export,
+       .gem_prime_import = drm_gem_prime_import,
+       .gem_prime_get_sg_table = drm_gem_cma_prime_get_sg_table,
+       .gem_prime_import_sg_table = drm_gem_cma_prime_import_sg_table,
+       .gem_prime_vmap = drm_gem_cma_prime_vmap,
+       .gem_prime_vunmap = drm_gem_cma_prime_vunmap,
+       .gem_prime_mmap = drm_gem_cma_prime_mmap,
+       .enable_vblank = ltdc_crtc_enable_vblank,
+       .disable_vblank = ltdc_crtc_disable_vblank,
+};
+
+static int drv_load(struct drm_device *ddev)
+{
+       struct platform_device *pdev = to_platform_device(ddev->dev);
+       struct drm_fbdev_cma *fbdev;
+       struct ltdc_device *ldev;
+       int ret;
+
+       DRM_DEBUG("%s\n", __func__);
+
+       ldev = devm_kzalloc(ddev->dev, sizeof(*ldev), GFP_KERNEL);
+       if (!ldev)
+               return -ENOMEM;
+
+       ddev->dev_private = (void *)ldev;
+
+       drm_mode_config_init(ddev);
+
+       /*
+        * set max width and height as default value.
+        * this value would be used to check framebuffer size limitation
+        * at drm_mode_addfb().
+        */
+       ddev->mode_config.min_width = 0;
+       ddev->mode_config.min_height = 0;
+       ddev->mode_config.max_width = STM_MAX_FB_WIDTH;
+       ddev->mode_config.max_height = STM_MAX_FB_HEIGHT;
+       ddev->mode_config.funcs = &drv_mode_config_funcs;
+
+       ret = ltdc_load(ddev);
+       if (ret)
+               goto err;
+
+       drm_mode_config_reset(ddev);
+       drm_kms_helper_poll_init(ddev);
+
+       if (ddev->mode_config.num_connector) {
+               ldev = ddev->dev_private;
+               fbdev = drm_fbdev_cma_init(ddev, 16,
+                                          ddev->mode_config.num_connector);
+               if (IS_ERR(fbdev)) {
+                       DRM_DEBUG("Warning: fails to create fbdev\n");
+                       fbdev = NULL;
+               }
+               ldev->fbdev = fbdev;
+       }
+
+       platform_set_drvdata(pdev, ddev);
+
+       return 0;
+err:
+       drm_mode_config_cleanup(ddev);
+       return ret;
+}
+
+static void drv_unload(struct drm_device *ddev)
+{
+       struct ltdc_device *ldev = ddev->dev_private;
+
+       DRM_DEBUG("%s\n", __func__);
+
+       if (ldev->fbdev) {
+               drm_fbdev_cma_fini(ldev->fbdev);
+               ldev->fbdev = NULL;
+       }
+       drm_kms_helper_poll_fini(ddev);
+       ltdc_unload(ddev);
+       drm_mode_config_cleanup(ddev);
+}
+
+static int stm_drm_platform_probe(struct platform_device *pdev)
+{
+       struct device *dev = &pdev->dev;
+       struct drm_device *ddev;
+       int ret;
+
+       DRM_DEBUG("%s\n", __func__);
+
+       dma_set_coherent_mask(dev, DMA_BIT_MASK(32));
+
+       ddev = drm_dev_alloc(&drv_driver, dev);
+       if (IS_ERR(ddev))
+               return PTR_ERR(ddev);
+
+       ret = drv_load(ddev);
+       if (ret)
+               goto err_unref;
+
+       ret = drm_dev_register(ddev, 0);
+       if (ret)
+               goto err_unref;
+
+       return 0;
+
+err_unref:
+       drm_dev_unref(ddev);
+
+       return ret;
+}
+
+static int stm_drm_platform_remove(struct platform_device *pdev)
+{
+       struct drm_device *ddev = platform_get_drvdata(pdev);
+
+       DRM_DEBUG("%s\n", __func__);
+
+       drm_dev_unregister(ddev);
+       drv_unload(ddev);
+       drm_dev_unref(ddev);
+
+       return 0;
+}
+
+static const struct of_device_id drv_dt_ids[] = {
+       { .compatible = "st,stm32-ltdc"},
+       { /* end node */ },
+};
+MODULE_DEVICE_TABLE(of, drv_dt_ids);
+
+static struct platform_driver stm_drm_platform_driver = {
+       .probe = stm_drm_platform_probe,
+       .remove = stm_drm_platform_remove,
+       .driver = {
+               .name = DRIVER_NAME,
+               .of_match_table = drv_dt_ids,
+       },
+};
+
+module_platform_driver(stm_drm_platform_driver);
+
+MODULE_AUTHOR("Philippe Cornu <philippe.cornu@st.com>");
+MODULE_AUTHOR("Yannick Fertre <yannick.fertre@st.com>");
+MODULE_AUTHOR("Fabien Dessenne <fabien.dessenne@st.com>");
+MODULE_AUTHOR("Mickael Reulier <mickael.reulier@st.com>");
+MODULE_DESCRIPTION("STMicroelectronics ST DRM LTDC driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/gpu/drm/stm/ltdc.c b/drivers/gpu/drm/stm/ltdc.c
new file mode 100644 (file)
index 0000000..a40418c
--- /dev/null
@@ -0,0 +1,1160 @@
+/*
+ * Copyright (C) STMicroelectronics SA 2017
+ *
+ * Authors: Philippe Cornu <philippe.cornu@st.com>
+ *          Yannick Fertre <yannick.fertre@st.com>
+ *          Fabien Dessenne <fabien.dessenne@st.com>
+ *          Mickael Reulier <mickael.reulier@st.com>
+ *
+ * License terms:  GNU General Public License (GPL), version 2
+ */
+
+#include <linux/clk.h>
+#include <linux/component.h>
+#include <linux/of_address.h>
+#include <linux/of_graph.h>
+#include <linux/reset.h>
+
+#include <drm/drm_atomic.h>
+#include <drm/drm_atomic_helper.h>
+#include <drm/drm_crtc_helper.h>
+#include <drm/drm_fb_cma_helper.h>
+#include <drm/drm_gem_cma_helper.h>
+#include <drm/drm_of.h>
+#include <drm/drm_panel.h>
+#include <drm/drm_plane_helper.h>
+
+#include <video/videomode.h>
+
+#include "ltdc.h"
+
+#define NB_CRTC 1
+#define CRTC_MASK GENMASK(NB_CRTC - 1, 0)
+
+#define MAX_IRQ 4
+
+#define HWVER_10200 0x010200
+#define HWVER_10300 0x010300
+#define HWVER_20101 0x020101
+
+/*
+ * The address of some registers depends on the HW version: such registers have
+ * an extra offset specified with reg_ofs.
+ */
+#define REG_OFS_NONE   0
+#define REG_OFS_4      4 /* Insertion of "Layer Configuration 2" reg */
+#define REG_OFS                (ldev->caps.reg_ofs)
+#define LAY_OFS                0x80    /* Register Offset between 2 layers */
+
+/* Global register offsets */
+#define LTDC_IDR       0x0000 /* IDentification */
+#define LTDC_LCR       0x0004 /* Layer Count */
+#define LTDC_SSCR      0x0008 /* Synchronization Size Configuration */
+#define LTDC_BPCR      0x000C /* Back Porch Configuration */
+#define LTDC_AWCR      0x0010 /* Active Width Configuration */
+#define LTDC_TWCR      0x0014 /* Total Width Configuration */
+#define LTDC_GCR       0x0018 /* Global Control */
+#define LTDC_GC1R      0x001C /* Global Configuration 1 */
+#define LTDC_GC2R      0x0020 /* Global Configuration 2 */
+#define LTDC_SRCR      0x0024 /* Shadow Reload Configuration */
+#define LTDC_GACR      0x0028 /* GAmma Correction */
+#define LTDC_BCCR      0x002C /* Background Color Configuration */
+#define LTDC_IER       0x0034 /* Interrupt Enable */
+#define LTDC_ISR       0x0038 /* Interrupt Status */
+#define LTDC_ICR       0x003C /* Interrupt Clear */
+#define LTDC_LIPCR     0x0040 /* Line Interrupt Position Configuration */
+#define LTDC_CPSR      0x0044 /* Current Position Status */
+#define LTDC_CDSR      0x0048 /* Current Display Status */
+
+/* Layer register offsets */
+#define LTDC_L1LC1R    (0x0080)           /* L1 Layer Configuration 1 */
+#define LTDC_L1LC2R    (0x0084)           /* L1 Layer Configuration 2 */
+#define LTDC_L1CR      (0x0084 + REG_OFS) /* L1 Control */
+#define LTDC_L1WHPCR   (0x0088 + REG_OFS) /* L1 Window Hor Position Config */
+#define LTDC_L1WVPCR   (0x008C + REG_OFS) /* L1 Window Vert Position Config */
+#define LTDC_L1CKCR    (0x0090 + REG_OFS) /* L1 Color Keying Configuration */
+#define LTDC_L1PFCR    (0x0094 + REG_OFS) /* L1 Pixel Format Configuration */
+#define LTDC_L1CACR    (0x0098 + REG_OFS) /* L1 Constant Alpha Config */
+#define LTDC_L1DCCR    (0x009C + REG_OFS) /* L1 Default Color Configuration */
+#define LTDC_L1BFCR    (0x00A0 + REG_OFS) /* L1 Blend Factors Configuration */
+#define LTDC_L1FBBCR   (0x00A4 + REG_OFS) /* L1 FrameBuffer Bus Control */
+#define LTDC_L1AFBCR   (0x00A8 + REG_OFS) /* L1 AuxFB Control */
+#define LTDC_L1CFBAR   (0x00AC + REG_OFS) /* L1 Color FrameBuffer Address */
+#define LTDC_L1CFBLR   (0x00B0 + REG_OFS) /* L1 Color FrameBuffer Length */
+#define LTDC_L1CFBLNR  (0x00B4 + REG_OFS) /* L1 Color FrameBuffer Line Nb */
+#define LTDC_L1AFBAR   (0x00B8 + REG_OFS) /* L1 AuxFB Address */
+#define LTDC_L1AFBLR   (0x00BC + REG_OFS) /* L1 AuxFB Length */
+#define LTDC_L1AFBLNR  (0x00C0 + REG_OFS) /* L1 AuxFB Line Number */
+#define LTDC_L1CLUTWR  (0x00C4 + REG_OFS) /* L1 CLUT Write */
+#define LTDC_L1YS1R    (0x00E0 + REG_OFS) /* L1 YCbCr Scale 1 */
+#define LTDC_L1YS2R    (0x00E4 + REG_OFS) /* L1 YCbCr Scale 2 */
+
+/* Bit definitions */
+#define SSCR_VSH       GENMASK(10, 0)  /* Vertical Synchronization Height */
+#define SSCR_HSW       GENMASK(27, 16) /* Horizontal Synchronization Width */
+
+#define BPCR_AVBP      GENMASK(10, 0)  /* Accumulated Vertical Back Porch */
+#define BPCR_AHBP      GENMASK(27, 16) /* Accumulated Horizontal Back Porch */
+
+#define AWCR_AAH       GENMASK(10, 0)  /* Accumulated Active Height */
+#define AWCR_AAW       GENMASK(27, 16) /* Accumulated Active Width */
+
+#define TWCR_TOTALH    GENMASK(10, 0)  /* TOTAL Height */
+#define TWCR_TOTALW    GENMASK(27, 16) /* TOTAL Width */
+
+#define GCR_LTDCEN     BIT(0)          /* LTDC ENable */
+#define GCR_DEN                BIT(16)         /* Dither ENable */
+#define GCR_PCPOL      BIT(28)         /* Pixel Clock POLarity */
+#define GCR_DEPOL      BIT(29)         /* Data Enable POLarity */
+#define GCR_VSPOL      BIT(30)         /* Vertical Synchro POLarity */
+#define GCR_HSPOL      BIT(31)         /* Horizontal Synchro POLarity */
+
+#define GC1R_WBCH      GENMASK(3, 0)   /* Width of Blue CHannel output */
+#define GC1R_WGCH      GENMASK(7, 4)   /* Width of Green Channel output */
+#define GC1R_WRCH      GENMASK(11, 8)  /* Width of Red Channel output */
+#define GC1R_PBEN      BIT(12)         /* Precise Blending ENable */
+#define GC1R_DT                GENMASK(15, 14) /* Dithering Technique */
+#define GC1R_GCT       GENMASK(19, 17) /* Gamma Correction Technique */
+#define GC1R_SHREN     BIT(21)         /* SHadow Registers ENabled */
+#define GC1R_BCP       BIT(22)         /* Background Colour Programmable */
+#define GC1R_BBEN      BIT(23)         /* Background Blending ENabled */
+#define GC1R_LNIP      BIT(24)         /* Line Number IRQ Position */
+#define GC1R_TP                BIT(25)         /* Timing Programmable */
+#define GC1R_IPP       BIT(26)         /* IRQ Polarity Programmable */
+#define GC1R_SPP       BIT(27)         /* Sync Polarity Programmable */
+#define GC1R_DWP       BIT(28)         /* Dither Width Programmable */
+#define GC1R_STREN     BIT(29)         /* STatus Registers ENabled */
+#define GC1R_BMEN      BIT(31)         /* Blind Mode ENabled */
+
+#define GC2R_EDCA      BIT(0)          /* External Display Control Ability  */
+#define GC2R_STSAEN    BIT(1)          /* Slave Timing Sync Ability ENabled */
+#define GC2R_DVAEN     BIT(2)          /* Dual-View Ability ENabled */
+#define GC2R_DPAEN     BIT(3)          /* Dual-Port Ability ENabled */
+#define GC2R_BW                GENMASK(6, 4)   /* Bus Width (log2 of nb of bytes) */
+#define GC2R_EDCEN     BIT(7)          /* External Display Control ENabled */
+
+#define SRCR_IMR       BIT(0)          /* IMmediate Reload */
+#define SRCR_VBR       BIT(1)          /* Vertical Blanking Reload */
+
+#define BCCR_BCBLACK   0x00            /* Background Color BLACK */
+#define BCCR_BCBLUE    GENMASK(7, 0)   /* Background Color BLUE */
+#define BCCR_BCGREEN   GENMASK(15, 8)  /* Background Color GREEN */
+#define BCCR_BCRED     GENMASK(23, 16) /* Background Color RED */
+#define BCCR_BCWHITE   GENMASK(23, 0)  /* Background Color WHITE */
+
+#define IER_LIE                BIT(0)          /* Line Interrupt Enable */
+#define IER_FUIE       BIT(1)          /* Fifo Underrun Interrupt Enable */
+#define IER_TERRIE     BIT(2)          /* Transfer ERRor Interrupt Enable */
+#define IER_RRIE       BIT(3)          /* Register Reload Interrupt enable */
+
+#define ISR_LIF                BIT(0)          /* Line Interrupt Flag */
+#define ISR_FUIF       BIT(1)          /* Fifo Underrun Interrupt Flag */
+#define ISR_TERRIF     BIT(2)          /* Transfer ERRor Interrupt Flag */
+#define ISR_RRIF       BIT(3)          /* Register Reload Interrupt Flag */
+
+#define LXCR_LEN       BIT(0)          /* Layer ENable */
+#define LXCR_COLKEN    BIT(1)          /* Color Keying Enable */
+#define LXCR_CLUTEN    BIT(4)          /* Color Look-Up Table ENable */
+
+#define LXWHPCR_WHSTPOS        GENMASK(11, 0)  /* Window Horizontal StarT POSition */
+#define LXWHPCR_WHSPPOS        GENMASK(27, 16) /* Window Horizontal StoP POSition */
+
+#define LXWVPCR_WVSTPOS        GENMASK(10, 0)  /* Window Vertical StarT POSition */
+#define LXWVPCR_WVSPPOS        GENMASK(26, 16) /* Window Vertical StoP POSition */
+
+#define LXPFCR_PF      GENMASK(2, 0)   /* Pixel Format */
+
+#define LXCACR_CONSTA  GENMASK(7, 0)   /* CONSTant Alpha */
+
+#define LXBFCR_BF2     GENMASK(2, 0)   /* Blending Factor 2 */
+#define LXBFCR_BF1     GENMASK(10, 8)  /* Blending Factor 1 */
+
+#define LXCFBLR_CFBLL  GENMASK(12, 0)  /* Color Frame Buffer Line Length */
+#define LXCFBLR_CFBP   GENMASK(28, 16) /* Color Frame Buffer Pitch in bytes */
+
+#define LXCFBLNR_CFBLN GENMASK(10, 0)   /* Color Frame Buffer Line Number */
+
+#define HSPOL_AL   0           /* Horizontal Sync POLarity Active Low */
+#define VSPOL_AL   0           /* Vertical Sync POLarity Active Low */
+#define DEPOL_AL   0           /* Data Enable POLarity Active Low */
+#define PCPOL_IPC  0           /* Input Pixel Clock */
+#define HSPOL_AH   GCR_HSPOL   /* Horizontal Sync POLarity Active High */
+#define VSPOL_AH   GCR_VSPOL   /* Vertical Sync POLarity Active High */
+#define DEPOL_AH   GCR_DEPOL   /* Data Enable POLarity Active High */
+#define PCPOL_IIPC GCR_PCPOL   /* Inverted Input Pixel Clock */
+#define CONSTA_MAX 0xFF                /* CONSTant Alpha MAX= 1.0 */
+#define BF1_PAXCA  0x600       /* Pixel Alpha x Constant Alpha */
+#define BF1_CA     0x400       /* Constant Alpha */
+#define BF2_1PAXCA 0x007       /* 1 - (Pixel Alpha x Constant Alpha) */
+#define BF2_1CA           0x005        /* 1 - Constant Alpha */
+
+#define NB_PF           8       /* Max nb of HW pixel format */
+
+enum ltdc_pix_fmt {
+       PF_NONE,
+       /* RGB formats */
+       PF_ARGB8888,    /* ARGB [32 bits] */
+       PF_RGBA8888,    /* RGBA [32 bits] */
+       PF_RGB888,      /* RGB [24 bits] */
+       PF_RGB565,      /* RGB [16 bits] */
+       PF_ARGB1555,    /* ARGB A:1 bit RGB:15 bits [16 bits] */
+       PF_ARGB4444,    /* ARGB A:4 bits R/G/B: 4 bits each [16 bits] */
+       /* Indexed formats */
+       PF_L8,          /* Indexed 8 bits [8 bits] */
+       PF_AL44,        /* Alpha:4 bits + indexed 4 bits [8 bits] */
+       PF_AL88         /* Alpha:8 bits + indexed 8 bits [16 bits] */
+};
+
+/* The index gives the encoding of the pixel format for an HW version */
+static const enum ltdc_pix_fmt ltdc_pix_fmt_a0[NB_PF] = {
+       PF_ARGB8888,    /* 0x00 */
+       PF_RGB888,      /* 0x01 */
+       PF_RGB565,      /* 0x02 */
+       PF_ARGB1555,    /* 0x03 */
+       PF_ARGB4444,    /* 0x04 */
+       PF_L8,          /* 0x05 */
+       PF_AL44,        /* 0x06 */
+       PF_AL88         /* 0x07 */
+};
+
+static const enum ltdc_pix_fmt ltdc_pix_fmt_a1[NB_PF] = {
+       PF_ARGB8888,    /* 0x00 */
+       PF_RGB888,      /* 0x01 */
+       PF_RGB565,      /* 0x02 */
+       PF_RGBA8888,    /* 0x03 */
+       PF_AL44,        /* 0x04 */
+       PF_L8,          /* 0x05 */
+       PF_ARGB1555,    /* 0x06 */
+       PF_ARGB4444     /* 0x07 */
+};
+
+static inline u32 reg_read(void __iomem *base, u32 reg)
+{
+       return readl_relaxed(base + reg);
+}
+
+static inline void reg_write(void __iomem *base, u32 reg, u32 val)
+{
+       writel_relaxed(val, base + reg);
+}
+
+static inline void reg_set(void __iomem *base, u32 reg, u32 mask)
+{
+       reg_write(base, reg, reg_read(base, reg) | mask);
+}
+
+static inline void reg_clear(void __iomem *base, u32 reg, u32 mask)
+{
+       reg_write(base, reg, reg_read(base, reg) & ~mask);
+}
+
+static inline void reg_update_bits(void __iomem *base, u32 reg, u32 mask,
+                                  u32 val)
+{
+       reg_write(base, reg, (reg_read(base, reg) & ~mask) | val);
+}
+
+static inline struct ltdc_device *crtc_to_ltdc(struct drm_crtc *crtc)
+{
+       return (struct ltdc_device *)crtc->dev->dev_private;
+}
+
+static inline struct ltdc_device *plane_to_ltdc(struct drm_plane *plane)
+{
+       return (struct ltdc_device *)plane->dev->dev_private;
+}
+
+static inline struct ltdc_device *encoder_to_ltdc(struct drm_encoder *enc)
+{
+       return (struct ltdc_device *)enc->dev->dev_private;
+}
+
+static inline struct ltdc_device *connector_to_ltdc(struct drm_connector *con)
+{
+       return (struct ltdc_device *)con->dev->dev_private;
+}
+
+static inline enum ltdc_pix_fmt to_ltdc_pixelformat(u32 drm_fmt)
+{
+       enum ltdc_pix_fmt pf;
+
+       switch (drm_fmt) {
+       case DRM_FORMAT_ARGB8888:
+       case DRM_FORMAT_XRGB8888:
+               pf = PF_ARGB8888;
+               break;
+       case DRM_FORMAT_RGBA8888:
+       case DRM_FORMAT_RGBX8888:
+               pf = PF_RGBA8888;
+               break;
+       case DRM_FORMAT_RGB888:
+               pf = PF_RGB888;
+               break;
+       case DRM_FORMAT_RGB565:
+               pf = PF_RGB565;
+               break;
+       case DRM_FORMAT_ARGB1555:
+       case DRM_FORMAT_XRGB1555:
+               pf = PF_ARGB1555;
+               break;
+       case DRM_FORMAT_ARGB4444:
+       case DRM_FORMAT_XRGB4444:
+               pf = PF_ARGB4444;
+               break;
+       case DRM_FORMAT_C8:
+               pf = PF_L8;
+               break;
+       default:
+               pf = PF_NONE;
+               break;
+       /* Note: There are no DRM_FORMAT for AL44 and AL88 */
+       }
+
+       return pf;
+}
+
+static inline u32 to_drm_pixelformat(enum ltdc_pix_fmt pf)
+{
+       switch (pf) {
+       case PF_ARGB8888:
+               return DRM_FORMAT_ARGB8888;
+       case PF_RGBA8888:
+               return DRM_FORMAT_RGBA8888;
+       case PF_RGB888:
+               return DRM_FORMAT_RGB888;
+       case PF_RGB565:
+               return DRM_FORMAT_RGB565;
+       case PF_ARGB1555:
+               return DRM_FORMAT_ARGB1555;
+       case PF_ARGB4444:
+               return DRM_FORMAT_ARGB4444;
+       case PF_L8:
+               return DRM_FORMAT_C8;
+       case PF_AL44: /* No DRM support */
+       case PF_AL88: /* No DRM support */
+       case PF_NONE:
+       default:
+               return 0;
+       }
+}
+
+static irqreturn_t ltdc_irq_thread(int irq, void *arg)
+{
+       struct drm_device *ddev = arg;
+       struct ltdc_device *ldev = ddev->dev_private;
+       struct drm_crtc *crtc = drm_crtc_from_index(ddev, 0);
+
+       /* Line IRQ : trigger the vblank event */
+       if (ldev->irq_status & ISR_LIF)
+               drm_crtc_handle_vblank(crtc);
+
+       /* Save FIFO Underrun & Transfer Error status */
+       mutex_lock(&ldev->err_lock);
+       if (ldev->irq_status & ISR_FUIF)
+               ldev->error_status |= ISR_FUIF;
+       if (ldev->irq_status & ISR_TERRIF)
+               ldev->error_status |= ISR_TERRIF;
+       mutex_unlock(&ldev->err_lock);
+
+       return IRQ_HANDLED;
+}
+
+static irqreturn_t ltdc_irq(int irq, void *arg)
+{
+       struct drm_device *ddev = arg;
+       struct ltdc_device *ldev = ddev->dev_private;
+
+       /* Read & Clear the interrupt status */
+       ldev->irq_status = reg_read(ldev->regs, LTDC_ISR);
+       reg_write(ldev->regs, LTDC_ICR, ldev->irq_status);
+
+       return IRQ_WAKE_THREAD;
+}
+
+/*
+ * DRM_CRTC
+ */
+
+static void ltdc_crtc_load_lut(struct drm_crtc *crtc)
+{
+       struct ltdc_device *ldev = crtc_to_ltdc(crtc);
+       unsigned int i, lay;
+
+       for (lay = 0; lay < ldev->caps.nb_layers; lay++)
+               for (i = 0; i < 256; i++)
+                       reg_write(ldev->regs, LTDC_L1CLUTWR + lay * LAY_OFS,
+                                 ldev->clut[i]);
+}
+
+static void ltdc_crtc_enable(struct drm_crtc *crtc)
+{
+       struct ltdc_device *ldev = crtc_to_ltdc(crtc);
+
+       DRM_DEBUG_DRIVER("\n");
+
+       /* Sets the background color value */
+       reg_write(ldev->regs, LTDC_BCCR, BCCR_BCBLACK);
+
+       /* Enable IRQ */
+       reg_set(ldev->regs, LTDC_IER, IER_RRIE | IER_FUIE | IER_TERRIE);
+
+       /* Immediately commit the planes */
+       reg_set(ldev->regs, LTDC_SRCR, SRCR_IMR);
+
+       /* Enable LTDC */
+       reg_set(ldev->regs, LTDC_GCR, GCR_LTDCEN);
+
+       drm_crtc_vblank_on(crtc);
+}
+
+static void ltdc_crtc_disable(struct drm_crtc *crtc)
+{
+       struct ltdc_device *ldev = crtc_to_ltdc(crtc);
+
+       DRM_DEBUG_DRIVER("\n");
+
+       drm_crtc_vblank_off(crtc);
+
+       /* disable LTDC */
+       reg_clear(ldev->regs, LTDC_GCR, GCR_LTDCEN);
+
+       /* disable IRQ */
+       reg_clear(ldev->regs, LTDC_IER, IER_RRIE | IER_FUIE | IER_TERRIE);
+
+       /* immediately commit disable of layers before switching off LTDC */
+       reg_set(ldev->regs, LTDC_SRCR, SRCR_IMR);
+}
+
+static void ltdc_crtc_mode_set_nofb(struct drm_crtc *crtc)
+{
+       struct ltdc_device *ldev = crtc_to_ltdc(crtc);
+       struct drm_display_mode *mode = &crtc->state->adjusted_mode;
+       struct videomode vm;
+       int rate = mode->clock * 1000;
+       u32 hsync, vsync, accum_hbp, accum_vbp, accum_act_w, accum_act_h;
+       u32 total_width, total_height;
+       u32 val;
+
+       drm_display_mode_to_videomode(mode, &vm);
+
+       DRM_DEBUG_DRIVER("CRTC:%d mode:%s\n", crtc->base.id, mode->name);
+       DRM_DEBUG_DRIVER("Video mode: %dx%d", vm.hactive, vm.vactive);
+       DRM_DEBUG_DRIVER(" hfp %d hbp %d hsl %d vfp %d vbp %d vsl %d\n",
+                        vm.hfront_porch, vm.hback_porch, vm.hsync_len,
+                        vm.vfront_porch, vm.vback_porch, vm.vsync_len);
+
+       /* Convert video timings to ltdc timings */
+       hsync = vm.hsync_len - 1;
+       vsync = vm.vsync_len - 1;
+       accum_hbp = hsync + vm.hback_porch;
+       accum_vbp = vsync + vm.vback_porch;
+       accum_act_w = accum_hbp + vm.hactive;
+       accum_act_h = accum_vbp + vm.vactive;
+       total_width = accum_act_w + vm.hfront_porch;
+       total_height = accum_act_h + vm.vfront_porch;
+
+       clk_disable(ldev->pixel_clk);
+
+       if (clk_set_rate(ldev->pixel_clk, rate) < 0) {
+               DRM_ERROR("Cannot set rate (%dHz) for pixel clk\n", rate);
+               return;
+       }
+
+       clk_enable(ldev->pixel_clk);
+
+       /* Configures the HS, VS, DE and PC polarities. */
+       val = HSPOL_AL | HSPOL_AL | DEPOL_AL | PCPOL_IPC;
+
+       if (vm.flags & DISPLAY_FLAGS_HSYNC_HIGH)
+               val |= HSPOL_AH;
+
+       if (vm.flags & DISPLAY_FLAGS_VSYNC_HIGH)
+               val |= VSPOL_AH;
+
+       if (vm.flags & DISPLAY_FLAGS_DE_HIGH)
+               val |= DEPOL_AH;
+
+       if (vm.flags & DISPLAY_FLAGS_PIXDATA_NEGEDGE)
+               val |= PCPOL_IIPC;
+
+       reg_update_bits(ldev->regs, LTDC_GCR,
+                       GCR_HSPOL | GCR_VSPOL | GCR_DEPOL | GCR_PCPOL, val);
+
+       /* Set Synchronization size */
+       val = (hsync << 16) | vsync;
+       reg_update_bits(ldev->regs, LTDC_SSCR, SSCR_VSH | SSCR_HSW, val);
+
+       /* Set Accumulated Back porch */
+       val = (accum_hbp << 16) | accum_vbp;
+       reg_update_bits(ldev->regs, LTDC_BPCR, BPCR_AVBP | BPCR_AHBP, val);
+
+       /* Set Accumulated Active Width */
+       val = (accum_act_w << 16) | accum_act_h;
+       reg_update_bits(ldev->regs, LTDC_AWCR, AWCR_AAW | AWCR_AAH, val);
+
+       /* Set total width & height */
+       val = (total_width << 16) | total_height;
+       reg_update_bits(ldev->regs, LTDC_TWCR, TWCR_TOTALH | TWCR_TOTALW, val);
+
+       reg_write(ldev->regs, LTDC_LIPCR, (accum_act_h + 1));
+}
+
+static void ltdc_crtc_atomic_flush(struct drm_crtc *crtc,
+                                  struct drm_crtc_state *old_crtc_state)
+{
+       struct ltdc_device *ldev = crtc_to_ltdc(crtc);
+       struct drm_pending_vblank_event *event = crtc->state->event;
+
+       DRM_DEBUG_ATOMIC("\n");
+
+       /* Commit shadow registers = update planes at next vblank */
+       reg_set(ldev->regs, LTDC_SRCR, SRCR_VBR);
+
+       if (event) {
+               crtc->state->event = NULL;
+
+               spin_lock_irq(&crtc->dev->event_lock);
+               if (drm_crtc_vblank_get(crtc) == 0)
+                       drm_crtc_arm_vblank_event(crtc, event);
+               else
+                       drm_crtc_send_vblank_event(crtc, event);
+               spin_unlock_irq(&crtc->dev->event_lock);
+       }
+}
+
+static struct drm_crtc_helper_funcs ltdc_crtc_helper_funcs = {
+       .load_lut = ltdc_crtc_load_lut,
+       .enable = ltdc_crtc_enable,
+       .disable = ltdc_crtc_disable,
+       .mode_set_nofb = ltdc_crtc_mode_set_nofb,
+       .atomic_flush = ltdc_crtc_atomic_flush,
+};
+
+int ltdc_crtc_enable_vblank(struct drm_device *ddev, unsigned int pipe)
+{
+       struct ltdc_device *ldev = ddev->dev_private;
+
+       DRM_DEBUG_DRIVER("\n");
+       reg_set(ldev->regs, LTDC_IER, IER_LIE);
+
+       return 0;
+}
+
+void ltdc_crtc_disable_vblank(struct drm_device *ddev, unsigned int pipe)
+{
+       struct ltdc_device *ldev = ddev->dev_private;
+
+       DRM_DEBUG_DRIVER("\n");
+       reg_clear(ldev->regs, LTDC_IER, IER_LIE);
+}
+
+static struct drm_crtc_funcs ltdc_crtc_funcs = {
+       .destroy = drm_crtc_cleanup,
+       .set_config = drm_atomic_helper_set_config,
+       .page_flip = drm_atomic_helper_page_flip,
+       .reset = drm_atomic_helper_crtc_reset,
+       .atomic_duplicate_state = drm_atomic_helper_crtc_duplicate_state,
+       .atomic_destroy_state = drm_atomic_helper_crtc_destroy_state,
+};
+
+/*
+ * DRM_PLANE
+ */
+
+static int ltdc_plane_atomic_check(struct drm_plane *plane,
+                                  struct drm_plane_state *state)
+{
+       struct drm_framebuffer *fb = state->fb;
+       u32 src_x, src_y, src_w, src_h;
+
+       DRM_DEBUG_DRIVER("\n");
+
+       if (!fb)
+               return 0;
+
+       /* convert src_ from 16:16 format */
+       src_x = state->src_x >> 16;
+       src_y = state->src_y >> 16;
+       src_w = state->src_w >> 16;
+       src_h = state->src_h >> 16;
+
+       /* Reject scaling */
+       if ((src_w != state->crtc_w) || (src_h != state->crtc_h)) {
+               DRM_ERROR("Scaling is not supported");
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
+static void ltdc_plane_atomic_update(struct drm_plane *plane,
+                                    struct drm_plane_state *oldstate)
+{
+       struct ltdc_device *ldev = plane_to_ltdc(plane);
+       struct drm_plane_state *state = plane->state;
+       struct drm_framebuffer *fb = state->fb;
+       u32 lofs = plane->index * LAY_OFS;
+       u32 x0 = state->crtc_x;
+       u32 x1 = state->crtc_x + state->crtc_w - 1;
+       u32 y0 = state->crtc_y;
+       u32 y1 = state->crtc_y + state->crtc_h - 1;
+       u32 src_x, src_y, src_w, src_h;
+       u32 val, pitch_in_bytes, line_length, paddr, ahbp, avbp, bpcr;
+       enum ltdc_pix_fmt pf;
+
+       if (!state->crtc || !fb) {
+               DRM_DEBUG_DRIVER("fb or crtc NULL");
+               return;
+       }
+
+       /* convert src_ from 16:16 format */
+       src_x = state->src_x >> 16;
+       src_y = state->src_y >> 16;
+       src_w = state->src_w >> 16;
+       src_h = state->src_h >> 16;
+
+       DRM_DEBUG_DRIVER(
+               "plane:%d fb:%d (%dx%d)@(%d,%d) -> (%dx%d)@(%d,%d)\n",
+               plane->base.id, fb->base.id,
+               src_w, src_h, src_x, src_y,
+               state->crtc_w, state->crtc_h, state->crtc_x, state->crtc_y);
+
+       bpcr = reg_read(ldev->regs, LTDC_BPCR);
+       ahbp = (bpcr & BPCR_AHBP) >> 16;
+       avbp = bpcr & BPCR_AVBP;
+
+       /* Configures the horizontal start and stop position */
+       val = ((x1 + 1 + ahbp) << 16) + (x0 + 1 + ahbp);
+       reg_update_bits(ldev->regs, LTDC_L1WHPCR + lofs,
+                       LXWHPCR_WHSTPOS | LXWHPCR_WHSPPOS, val);
+
+       /* Configures the vertical start and stop position */
+       val = ((y1 + 1 + avbp) << 16) + (y0 + 1 + avbp);
+       reg_update_bits(ldev->regs, LTDC_L1WVPCR + lofs,
+                       LXWVPCR_WVSTPOS | LXWVPCR_WVSPPOS, val);
+
+       /* Specifies the pixel format */
+       pf = to_ltdc_pixelformat(fb->format->format);
+       for (val = 0; val < NB_PF; val++)
+               if (ldev->caps.pix_fmt_hw[val] == pf)
+                       break;
+
+       if (val == NB_PF) {
+               DRM_ERROR("Pixel format %.4s not supported\n",
+                         (char *)&fb->format->format);
+               val = 0; /* set by default ARGB 32 bits */
+       }
+       reg_update_bits(ldev->regs, LTDC_L1PFCR + lofs, LXPFCR_PF, val);
+
+       /* Configures the color frame buffer pitch in bytes & line length */
+       pitch_in_bytes = fb->pitches[0];
+       line_length = drm_format_plane_cpp(fb->format->format, 0) *
+                     (x1 - x0 + 1) + (ldev->caps.bus_width >> 3) - 1;
+       val = ((pitch_in_bytes << 16) | line_length);
+       reg_update_bits(ldev->regs, LTDC_L1CFBLR + lofs,
+                       LXCFBLR_CFBLL | LXCFBLR_CFBP, val);
+
+       /* Specifies the constant alpha value */
+       val = CONSTA_MAX;
+       reg_update_bits(ldev->regs, LTDC_L1CACR + lofs,
+                       LXCACR_CONSTA, val);
+
+       /* Specifies the blending factors */
+       val = BF1_PAXCA | BF2_1PAXCA;
+       reg_update_bits(ldev->regs, LTDC_L1BFCR + lofs,
+                       LXBFCR_BF2 | LXBFCR_BF1, val);
+
+       /* Configures the frame buffer line number */
+       val = y1 - y0 + 1;
+       reg_update_bits(ldev->regs, LTDC_L1CFBLNR + lofs,
+                       LXCFBLNR_CFBLN, val);
+
+       /* Sets the FB address */
+       paddr = (u32)drm_fb_cma_get_gem_addr(fb, state, 0);
+
+       DRM_DEBUG_DRIVER("fb: phys 0x%08x", paddr);
+       reg_write(ldev->regs, LTDC_L1CFBAR + lofs, paddr);
+
+       /* Enable layer and CLUT if needed */
+       val = fb->format->format == DRM_FORMAT_C8 ? LXCR_CLUTEN : 0;
+       val |= LXCR_LEN;
+       reg_update_bits(ldev->regs, LTDC_L1CR + lofs,
+                       LXCR_LEN | LXCR_CLUTEN, val);
+
+       mutex_lock(&ldev->err_lock);
+       if (ldev->error_status & ISR_FUIF) {
+               DRM_DEBUG_DRIVER("Fifo underrun\n");
+               ldev->error_status &= ~ISR_FUIF;
+       }
+       if (ldev->error_status & ISR_TERRIF) {
+               DRM_DEBUG_DRIVER("Transfer error\n");
+               ldev->error_status &= ~ISR_TERRIF;
+       }
+       mutex_unlock(&ldev->err_lock);
+}
+
+static void ltdc_plane_atomic_disable(struct drm_plane *plane,
+                                     struct drm_plane_state *oldstate)
+{
+       struct ltdc_device *ldev = plane_to_ltdc(plane);
+       u32 lofs = plane->index * LAY_OFS;
+
+       /* disable layer */
+       reg_clear(ldev->regs, LTDC_L1CR + lofs, LXCR_LEN);
+
+       DRM_DEBUG_DRIVER("CRTC:%d plane:%d\n",
+                        oldstate->crtc->base.id, plane->base.id);
+}
+
+static struct drm_plane_funcs ltdc_plane_funcs = {
+       .update_plane = drm_atomic_helper_update_plane,
+       .disable_plane = drm_atomic_helper_disable_plane,
+       .destroy = drm_plane_cleanup,
+       .set_property = drm_atomic_helper_plane_set_property,
+       .reset = drm_atomic_helper_plane_reset,
+       .atomic_duplicate_state = drm_atomic_helper_plane_duplicate_state,
+       .atomic_destroy_state = drm_atomic_helper_plane_destroy_state,
+};
+
+static const struct drm_plane_helper_funcs ltdc_plane_helper_funcs = {
+       .atomic_check = ltdc_plane_atomic_check,
+       .atomic_update = ltdc_plane_atomic_update,
+       .atomic_disable = ltdc_plane_atomic_disable,
+};
+
+static struct drm_plane *ltdc_plane_create(struct drm_device *ddev,
+                                          enum drm_plane_type type)
+{
+       unsigned long possible_crtcs = CRTC_MASK;
+       struct ltdc_device *ldev = ddev->dev_private;
+       struct device *dev = ddev->dev;
+       struct drm_plane *plane;
+       unsigned int i, nb_fmt = 0;
+       u32 formats[NB_PF];
+       u32 drm_fmt;
+       int ret;
+
+       /* Get supported pixel formats */
+       for (i = 0; i < NB_PF; i++) {
+               drm_fmt = to_drm_pixelformat(ldev->caps.pix_fmt_hw[i]);
+               if (!drm_fmt)
+                       continue;
+               formats[nb_fmt++] = drm_fmt;
+       }
+
+       plane = devm_kzalloc(dev, sizeof(*plane), GFP_KERNEL);
+       if (!plane)
+               return 0;
+
+       ret = drm_universal_plane_init(ddev, plane, possible_crtcs,
+                                      &ltdc_plane_funcs, formats, nb_fmt,
+                                      type, NULL);
+       if (ret < 0)
+               return 0;
+
+       drm_plane_helper_add(plane, &ltdc_plane_helper_funcs);
+
+       DRM_DEBUG_DRIVER("plane:%d created\n", plane->base.id);
+
+       return plane;
+}
+
+static void ltdc_plane_destroy_all(struct drm_device *ddev)
+{
+       struct drm_plane *plane, *plane_temp;
+
+       list_for_each_entry_safe(plane, plane_temp,
+                                &ddev->mode_config.plane_list, head)
+               drm_plane_cleanup(plane);
+}
+
+static int ltdc_crtc_init(struct drm_device *ddev, struct drm_crtc *crtc)
+{
+       struct ltdc_device *ldev = ddev->dev_private;
+       struct drm_plane *primary, *overlay;
+       unsigned int i;
+       int res;
+
+       primary = ltdc_plane_create(ddev, DRM_PLANE_TYPE_PRIMARY);
+       if (!primary) {
+               DRM_ERROR("Can not create primary plane\n");
+               return -EINVAL;
+       }
+
+       res = drm_crtc_init_with_planes(ddev, crtc, primary, NULL,
+                                       &ltdc_crtc_funcs, NULL);
+       if (res) {
+               DRM_ERROR("Can not initialize CRTC\n");
+               goto cleanup;
+       }
+
+       drm_crtc_helper_add(crtc, &ltdc_crtc_helper_funcs);
+
+       DRM_DEBUG_DRIVER("CRTC:%d created\n", crtc->base.id);
+
+       /* Add planes. Note : the first layer is used by primary plane */
+       for (i = 1; i < ldev->caps.nb_layers; i++) {
+               overlay = ltdc_plane_create(ddev, DRM_PLANE_TYPE_OVERLAY);
+               if (!overlay) {
+                       res = -ENOMEM;
+                       DRM_ERROR("Can not create overlay plane %d\n", i);
+                       goto cleanup;
+               }
+       }
+
+       return 0;
+
+cleanup:
+       ltdc_plane_destroy_all(ddev);
+       return res;
+}
+
+/*
+ * DRM_ENCODER
+ */
+
+static void ltdc_rgb_encoder_enable(struct drm_encoder *encoder)
+{
+       struct ltdc_device *ldev = encoder_to_ltdc(encoder);
+
+       DRM_DEBUG_DRIVER("\n");
+
+       drm_panel_prepare(ldev->panel);
+       drm_panel_enable(ldev->panel);
+}
+
+static void ltdc_rgb_encoder_disable(struct drm_encoder *encoder)
+{
+       struct ltdc_device *ldev = encoder_to_ltdc(encoder);
+
+       DRM_DEBUG_DRIVER("\n");
+
+       drm_panel_disable(ldev->panel);
+       drm_panel_unprepare(ldev->panel);
+}
+
+static const struct drm_encoder_helper_funcs ltdc_rgb_encoder_helper_funcs = {
+       .enable = ltdc_rgb_encoder_enable,
+       .disable = ltdc_rgb_encoder_disable,
+};
+
+static const struct drm_encoder_funcs ltdc_rgb_encoder_funcs = {
+       .destroy = drm_encoder_cleanup,
+};
+
+static struct drm_encoder *ltdc_rgb_encoder_create(struct drm_device *ddev)
+{
+       struct drm_encoder *encoder;
+
+       encoder = devm_kzalloc(ddev->dev, sizeof(*encoder), GFP_KERNEL);
+       if (!encoder)
+               return NULL;
+
+       encoder->possible_crtcs = CRTC_MASK;
+       encoder->possible_clones = 0; /* No cloning support */
+
+       drm_encoder_init(ddev, encoder, &ltdc_rgb_encoder_funcs,
+                        DRM_MODE_ENCODER_DPI, NULL);
+
+       drm_encoder_helper_add(encoder, &ltdc_rgb_encoder_helper_funcs);
+
+       DRM_DEBUG_DRIVER("RGB encoder:%d created\n", encoder->base.id);
+
+       return encoder;
+}
+
+/*
+ * DRM_CONNECTOR
+ */
+
+static int ltdc_rgb_connector_get_modes(struct drm_connector *connector)
+{
+       struct drm_device *ddev = connector->dev;
+       struct ltdc_device *ldev = ddev->dev_private;
+       int ret = 0;
+
+       DRM_DEBUG_DRIVER("\n");
+
+       if (ldev->panel)
+               ret = drm_panel_get_modes(ldev->panel);
+
+       return ret < 0 ? 0 : ret;
+}
+
+static struct drm_connector_helper_funcs ltdc_rgb_connector_helper_funcs = {
+       .get_modes = ltdc_rgb_connector_get_modes,
+};
+
+static enum drm_connector_status
+ltdc_rgb_connector_detect(struct drm_connector *connector, bool force)
+{
+       struct ltdc_device *ldev = connector_to_ltdc(connector);
+
+       return ldev->panel ? connector_status_connected :
+              connector_status_disconnected;
+}
+
+static void ltdc_rgb_connector_destroy(struct drm_connector *connector)
+{
+       DRM_DEBUG_DRIVER("\n");
+
+       drm_connector_unregister(connector);
+       drm_connector_cleanup(connector);
+}
+
+static const struct drm_connector_funcs ltdc_rgb_connector_funcs = {
+       .dpms = drm_atomic_helper_connector_dpms,
+       .fill_modes = drm_helper_probe_single_connector_modes,
+       .detect = ltdc_rgb_connector_detect,
+       .destroy = ltdc_rgb_connector_destroy,
+       .reset = drm_atomic_helper_connector_reset,
+       .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
+       .atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
+};
+
+struct drm_connector *ltdc_rgb_connector_create(struct drm_device *ddev)
+{
+       struct drm_connector *connector;
+       int err;
+
+       connector = devm_kzalloc(ddev->dev, sizeof(*connector), GFP_KERNEL);
+       if (!connector) {
+               DRM_ERROR("Failed to allocate connector\n");
+               return NULL;
+       }
+
+       connector->polled = DRM_CONNECTOR_POLL_HPD;
+
+       err = drm_connector_init(ddev, connector, &ltdc_rgb_connector_funcs,
+                                DRM_MODE_CONNECTOR_DPI);
+       if (err) {
+               DRM_ERROR("Failed to initialize connector\n");
+               return NULL;
+       }
+
+       drm_connector_helper_add(connector, &ltdc_rgb_connector_helper_funcs);
+
+       DRM_DEBUG_DRIVER("RGB connector:%d created\n", connector->base.id);
+
+       return connector;
+}
+
+static int ltdc_get_caps(struct drm_device *ddev)
+{
+       struct ltdc_device *ldev = ddev->dev_private;
+       u32 bus_width_log2, lcr, gc2r;
+
+       /* at least 1 layer must be managed */
+       lcr = reg_read(ldev->regs, LTDC_LCR);
+
+       ldev->caps.nb_layers = max_t(int, lcr, 1);
+
+       /* set data bus width */
+       gc2r = reg_read(ldev->regs, LTDC_GC2R);
+       bus_width_log2 = (gc2r & GC2R_BW) >> 4;
+       ldev->caps.bus_width = 8 << bus_width_log2;
+       ldev->caps.hw_version = reg_read(ldev->regs, LTDC_IDR);
+
+       switch (ldev->caps.hw_version) {
+       case HWVER_10200:
+       case HWVER_10300:
+               ldev->caps.reg_ofs = REG_OFS_NONE;
+               ldev->caps.pix_fmt_hw = ltdc_pix_fmt_a0;
+               break;
+       case HWVER_20101:
+               ldev->caps.reg_ofs = REG_OFS_4;
+               ldev->caps.pix_fmt_hw = ltdc_pix_fmt_a1;
+               break;
+       default:
+               return -ENODEV;
+       }
+
+       return 0;
+}
+
+static struct drm_panel *ltdc_get_panel(struct drm_device *ddev)
+{
+       struct device *dev = ddev->dev;
+       struct device_node *np = dev->of_node;
+       struct device_node *entity, *port = NULL;
+       struct drm_panel *panel = NULL;
+
+       DRM_DEBUG_DRIVER("\n");
+
+       /*
+        * Parse ltdc node to get remote port and find RGB panel / HDMI slave
+        * If a dsi or a bridge (hdmi, lvds...) is connected to ltdc,
+        * a remote port & RGB panel will not be found.
+        */
+       for_each_endpoint_of_node(np, entity) {
+               if (!of_device_is_available(entity))
+                       continue;
+
+               port = of_graph_get_remote_port_parent(entity);
+               if (port) {
+                       panel = of_drm_find_panel(port);
+                       of_node_put(port);
+                       if (panel) {
+                               DRM_DEBUG_DRIVER("remote panel %s\n",
+                                                port->full_name);
+                       } else {
+                               DRM_DEBUG_DRIVER("panel missing\n");
+                               of_node_put(entity);
+                       }
+               }
+       }
+
+       return panel;
+}
+
+int ltdc_load(struct drm_device *ddev)
+{
+       struct platform_device *pdev = to_platform_device(ddev->dev);
+       struct ltdc_device *ldev = ddev->dev_private;
+       struct device *dev = ddev->dev;
+       struct device_node *np = dev->of_node;
+       struct drm_encoder *encoder;
+       struct drm_connector *connector = NULL;
+       struct drm_crtc *crtc;
+       struct reset_control *rstc;
+       struct resource res;
+       int irq, ret, i;
+
+       DRM_DEBUG_DRIVER("\n");
+
+       ldev->panel = ltdc_get_panel(ddev);
+       if (!ldev->panel)
+               return -EPROBE_DEFER;
+
+       rstc = of_reset_control_get(np, NULL);
+
+       mutex_init(&ldev->err_lock);
+
+       ldev->pixel_clk = devm_clk_get(dev, "lcd");
+       if (IS_ERR(ldev->pixel_clk)) {
+               DRM_ERROR("Unable to get lcd clock\n");
+               return -ENODEV;
+       }
+
+       if (clk_prepare_enable(ldev->pixel_clk)) {
+               DRM_ERROR("Unable to prepare pixel clock\n");
+               return -ENODEV;
+       }
+
+       if (of_address_to_resource(np, 0, &res)) {
+               DRM_ERROR("Unable to get resource\n");
+               return -ENODEV;
+       }
+
+       ldev->regs = devm_ioremap_resource(dev, &res);
+       if (IS_ERR(ldev->regs)) {
+               DRM_ERROR("Unable to get ltdc registers\n");
+               return PTR_ERR(ldev->regs);
+       }
+
+       for (i = 0; i < MAX_IRQ; i++) {
+               irq = platform_get_irq(pdev, i);
+               if (irq < 0)
+                       continue;
+
+               ret = devm_request_threaded_irq(dev, irq, ltdc_irq,
+                                               ltdc_irq_thread, IRQF_ONESHOT,
+                                               dev_name(dev), ddev);
+               if (ret) {
+                       DRM_ERROR("Failed to register LTDC interrupt\n");
+                       return ret;
+               }
+       }
+
+       if (!IS_ERR(rstc))
+               reset_control_deassert(rstc);
+
+       /* Disable interrupts */
+       reg_clear(ldev->regs, LTDC_IER,
+                 IER_LIE | IER_RRIE | IER_FUIE | IER_TERRIE);
+
+       ret = ltdc_get_caps(ddev);
+       if (ret) {
+               DRM_ERROR("hardware identifier (0x%08x) not supported!\n",
+                         ldev->caps.hw_version);
+               return ret;
+       }
+
+       DRM_INFO("ltdc hw version 0x%08x - ready\n", ldev->caps.hw_version);
+
+       if (ldev->panel) {
+               encoder = ltdc_rgb_encoder_create(ddev);
+               if (!encoder) {
+                       DRM_ERROR("Failed to create RGB encoder\n");
+                       ret = -EINVAL;
+                       goto err;
+               }
+
+               connector = ltdc_rgb_connector_create(ddev);
+               if (!connector) {
+                       DRM_ERROR("Failed to create RGB connector\n");
+                       ret = -EINVAL;
+                       goto err;
+               }
+
+               ret = drm_mode_connector_attach_encoder(connector, encoder);
+               if (ret) {
+                       DRM_ERROR("Failed to attach connector to encoder\n");
+                       goto err;
+               }
+
+               drm_panel_attach(ldev->panel, connector);
+       }
+
+       crtc = devm_kzalloc(dev, sizeof(*crtc), GFP_KERNEL);
+       if (!crtc) {
+               DRM_ERROR("Failed to allocate crtc\n");
+               ret = -ENOMEM;
+               goto err;
+       }
+
+       ret = ltdc_crtc_init(ddev, crtc);
+       if (ret) {
+               DRM_ERROR("Failed to init crtc\n");
+               goto err;
+       }
+
+       ret = drm_vblank_init(ddev, NB_CRTC);
+       if (ret) {
+               DRM_ERROR("Failed calling drm_vblank_init()\n");
+               goto err;
+       }
+
+       /* Allow usage of vblank without having to call drm_irq_install */
+       ddev->irq_enabled = 1;
+
+       return 0;
+err:
+       if (ldev->panel)
+               drm_panel_detach(ldev->panel);
+
+       clk_disable_unprepare(ldev->pixel_clk);
+
+       return ret;
+}
+
+void ltdc_unload(struct drm_device *ddev)
+{
+       struct ltdc_device *ldev = ddev->dev_private;
+
+       DRM_DEBUG_DRIVER("\n");
+
+       drm_vblank_cleanup(ddev);
+
+       if (ldev->panel)
+               drm_panel_detach(ldev->panel);
+
+       clk_disable_unprepare(ldev->pixel_clk);
+}
+
+MODULE_AUTHOR("Philippe Cornu <philippe.cornu@st.com>");
+MODULE_AUTHOR("Yannick Fertre <yannick.fertre@st.com>");
+MODULE_AUTHOR("Fabien Dessenne <fabien.dessenne@st.com>");
+MODULE_AUTHOR("Mickael Reulier <mickael.reulier@st.com>");
+MODULE_DESCRIPTION("STMicroelectronics ST DRM LTDC driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/gpu/drm/stm/ltdc.h b/drivers/gpu/drm/stm/ltdc.h
new file mode 100644 (file)
index 0000000..d7a9c73
--- /dev/null
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) STMicroelectronics SA 2017
+ *
+ * Authors: Philippe Cornu <philippe.cornu@st.com>
+ *          Yannick Fertre <yannick.fertre@st.com>
+ *          Fabien Dessenne <fabien.dessenne@st.com>
+ *          Mickael Reulier <mickael.reulier@st.com>
+ *
+ * License terms:  GNU General Public License (GPL), version 2
+ */
+
+#ifndef _LTDC_H_
+#define _LTDC_H_
+
+struct ltdc_caps {
+       u32 hw_version;         /* hardware version */
+       u32 nb_layers;          /* number of supported layers */
+       u32 reg_ofs;            /* register offset for applicable regs */
+       u32 bus_width;          /* bus width (32 or 64 bits) */
+       const u32 *pix_fmt_hw;  /* supported pixel formats */
+};
+
+struct ltdc_device {
+       struct drm_fbdev_cma *fbdev;
+       void __iomem *regs;
+       struct clk *pixel_clk;  /* lcd pixel clock */
+       struct drm_panel *panel;
+       struct mutex err_lock;  /* protecting error_status */
+       struct ltdc_caps caps;
+       u32 clut[256];          /* color look up table */
+       u32 error_status;
+       u32 irq_status;
+};
+
+int ltdc_crtc_enable_vblank(struct drm_device *dev, unsigned int pipe);
+void ltdc_crtc_disable_vblank(struct drm_device *dev, unsigned int pipe);
+int ltdc_load(struct drm_device *ddev);
+void ltdc_unload(struct drm_device *ddev);
+
+#endif
index 9a1e34e48f64fb0ab684a20811a0be0ff0a09ab6..51c48a8e00ec018fc5a21a340ed9144d07d1a198 100644 (file)
@@ -892,7 +892,7 @@ static int tegra_drm_context_cleanup(int id, void *p, void *data)
        return 0;
 }
 
-static void tegra_drm_preclose(struct drm_device *drm, struct drm_file *file)
+static void tegra_drm_postclose(struct drm_device *drm, struct drm_file *file)
 {
        struct tegra_drm_file *fpriv = file->driver_priv;
 
@@ -960,7 +960,7 @@ static struct drm_driver tegra_drm_driver = {
        .load = tegra_drm_load,
        .unload = tegra_drm_unload,
        .open = tegra_drm_open,
-       .preclose = tegra_drm_preclose,
+       .postclose = tegra_drm_postclose,
        .lastclose = tegra_drm_lastclose,
 
 #if defined(CONFIG_DEBUG_FS)
index 61f45d122bd0f73b04d4e6aefd0b7ec6f607d04e..ab687fba491629c63d004327c4d74a27b16060cf 100644 (file)
@@ -9,6 +9,7 @@ vc4-y := \
        vc4_drv.o \
        vc4_dpi.o \
        vc4_dsi.o \
+       vc4_fence.o \
        vc4_kms.o \
        vc4_gem.o \
        vc4_hdmi.o \
index af29432a6471a1063da5e0611430052c754a8a16..80b2f9e55c5c46d1c6bd319bc7c461e50b9ecaa0 100644 (file)
@@ -19,6 +19,8 @@
  * rendering can return quickly.
  */
 
+#include <linux/dma-buf.h>
+
 #include "vc4_drv.h"
 #include "uapi/drm/vc4_drm.h"
 
@@ -88,6 +90,10 @@ static void vc4_bo_destroy(struct vc4_bo *bo)
 
        vc4->bo_stats.num_allocated--;
        vc4->bo_stats.size_allocated -= obj->size;
+
+       if (bo->resv == &bo->_resv)
+               reservation_object_fini(bo->resv);
+
        drm_gem_cma_free_object(obj);
 }
 
@@ -244,8 +250,12 @@ struct vc4_bo *vc4_bo_create(struct drm_device *dev, size_t unaligned_size,
                        return ERR_PTR(-ENOMEM);
                }
        }
+       bo = to_vc4_bo(&cma_obj->base);
 
-       return to_vc4_bo(&cma_obj->base);
+       bo->resv = &bo->_resv;
+       reservation_object_init(bo->resv);
+
+       return bo;
 }
 
 int vc4_dumb_create(struct drm_file *file_priv,
@@ -369,6 +379,13 @@ static void vc4_bo_cache_time_timer(unsigned long data)
        schedule_work(&vc4->bo_cache.time_work);
 }
 
+struct reservation_object *vc4_prime_res_obj(struct drm_gem_object *obj)
+{
+       struct vc4_bo *bo = to_vc4_bo(obj);
+
+       return bo->resv;
+}
+
 struct dma_buf *
 vc4_prime_export(struct drm_device *dev, struct drm_gem_object *obj, int flags)
 {
@@ -440,6 +457,24 @@ void *vc4_prime_vmap(struct drm_gem_object *obj)
        return drm_gem_cma_prime_vmap(obj);
 }
 
+struct drm_gem_object *
+vc4_prime_import_sg_table(struct drm_device *dev,
+                         struct dma_buf_attachment *attach,
+                         struct sg_table *sgt)
+{
+       struct drm_gem_object *obj;
+       struct vc4_bo *bo;
+
+       obj = drm_gem_cma_prime_import_sg_table(dev, attach, sgt);
+       if (IS_ERR(obj))
+               return obj;
+
+       bo = to_vc4_bo(obj);
+       bo->resv = attach->dmabuf->resv;
+
+       return obj;
+}
+
 int vc4_create_bo_ioctl(struct drm_device *dev, void *data,
                        struct drm_file *file_priv)
 {
index d86c8cce318270a0e34b09c3a1713d48e1a4ff0b..1b4dbe9e1c6d5816bb6f02e42aeccd6f42a23c19 100644 (file)
@@ -151,10 +151,10 @@ int vc4_crtc_debugfs_regs(struct seq_file *m, void *unused)
 }
 #endif
 
-int vc4_crtc_get_scanoutpos(struct drm_device *dev, unsigned int crtc_id,
-                           unsigned int flags, int *vpos, int *hpos,
-                           ktime_t *stime, ktime_t *etime,
-                           const struct drm_display_mode *mode)
+bool vc4_crtc_get_scanoutpos(struct drm_device *dev, unsigned int crtc_id,
+                            bool in_vblank_irq, int *vpos, int *hpos,
+                            ktime_t *stime, ktime_t *etime,
+                            const struct drm_display_mode *mode)
 {
        struct vc4_dev *vc4 = to_vc4_dev(dev);
        struct drm_crtc *crtc = drm_crtc_from_index(dev, crtc_id);
@@ -162,7 +162,7 @@ int vc4_crtc_get_scanoutpos(struct drm_device *dev, unsigned int crtc_id,
        u32 val;
        int fifo_lines;
        int vblank_lines;
-       int ret = 0;
+       bool ret = false;
 
        /* preempt_disable_rt() should go right here in PREEMPT_RT patchset. */
 
@@ -198,7 +198,7 @@ int vc4_crtc_get_scanoutpos(struct drm_device *dev, unsigned int crtc_id,
        fifo_lines = vc4_crtc->cob_size / mode->crtc_hdisplay;
 
        if (fifo_lines > 0)
-               ret |= DRM_SCANOUTPOS_VALID;
+               ret = true;
 
        /* HVS more than fifo_lines into frame for compositing? */
        if (*vpos > fifo_lines) {
@@ -216,7 +216,6 @@ int vc4_crtc_get_scanoutpos(struct drm_device *dev, unsigned int crtc_id,
                 */
                *vpos -= fifo_lines + 1;
 
-               ret |= DRM_SCANOUTPOS_ACCURATE;
                return ret;
        }
 
@@ -229,10 +228,9 @@ int vc4_crtc_get_scanoutpos(struct drm_device *dev, unsigned int crtc_id,
         * We can't get meaningful readings wrt. scanline position of the PV
         * and need to make things up in a approximative but consistent way.
         */
-       ret |= DRM_SCANOUTPOS_IN_VBLANK;
        vblank_lines = mode->vtotal - mode->vdisplay;
 
-       if (flags & DRM_CALLED_FROM_VBLIRQ) {
+       if (in_vblank_irq) {
                /*
                 * Assume the irq handler got called close to first
                 * line of vblank, so PV has about a full vblank
@@ -254,9 +252,10 @@ int vc4_crtc_get_scanoutpos(struct drm_device *dev, unsigned int crtc_id,
                 * we are at the very beginning of vblank, as the hvs just
                 * started refilling, and the stime and etime timestamps
                 * truly correspond to start of vblank.
+                *
+                * Unfortunately there's no way to report this to upper levels
+                * and make it more useful.
                 */
-               if ((val & SCALER_DISPSTATX_FULL) != SCALER_DISPSTATX_FULL)
-                       ret |= DRM_SCANOUTPOS_ACCURATE;
        } else {
                /*
                 * No clue where we are inside vblank. Return a vpos of zero,
@@ -270,19 +269,6 @@ int vc4_crtc_get_scanoutpos(struct drm_device *dev, unsigned int crtc_id,
        return ret;
 }
 
-int vc4_crtc_get_vblank_timestamp(struct drm_device *dev, unsigned int crtc_id,
-                                 int *max_error, struct timeval *vblank_time,
-                                 unsigned flags)
-{
-       struct drm_crtc *crtc = drm_crtc_from_index(dev, crtc_id);
-       struct drm_crtc_state *state = crtc->state;
-
-       /* Helper routine in DRM core does all the work: */
-       return drm_calc_vbltimestamp_from_scanoutpos(dev, crtc_id, max_error,
-                                                    vblank_time, flags,
-                                                    &state->adjusted_mode);
-}
-
 static void vc4_crtc_destroy(struct drm_crtc *crtc)
 {
        drm_crtc_cleanup(crtc);
index 61e674baf3a6f6cd0c0356d245d9879f7fdd1b67..863974942c662f5f3300c8637b4daa3324765ecf 100644 (file)
@@ -154,7 +154,7 @@ static struct drm_driver vc4_drm_driver = {
        .irq_uninstall = vc4_irq_uninstall,
 
        .get_scanout_position = vc4_crtc_get_scanoutpos,
-       .get_vblank_timestamp = vc4_crtc_get_vblank_timestamp,
+       .get_vblank_timestamp = drm_calc_vbltimestamp_from_scanoutpos,
 
 #if defined(CONFIG_DEBUG_FS)
        .debugfs_init = vc4_debugfs_init,
@@ -168,8 +168,9 @@ static struct drm_driver vc4_drm_driver = {
        .prime_fd_to_handle = drm_gem_prime_fd_to_handle,
        .gem_prime_import = drm_gem_prime_import,
        .gem_prime_export = vc4_prime_export,
+       .gem_prime_res_obj = vc4_prime_res_obj,
        .gem_prime_get_sg_table = drm_gem_cma_prime_get_sg_table,
-       .gem_prime_import_sg_table = drm_gem_cma_prime_import_sg_table,
+       .gem_prime_import_sg_table = vc4_prime_import_sg_table,
        .gem_prime_vmap = vc4_prime_vmap,
        .gem_prime_vunmap = drm_gem_cma_prime_vunmap,
        .gem_prime_mmap = vc4_prime_mmap,
@@ -334,6 +335,7 @@ static int vc4_platform_drm_remove(struct platform_device *pdev)
 
 static const struct of_device_id vc4_of_match[] = {
        { .compatible = "brcm,bcm2835-vc4", },
+       { .compatible = "brcm,cygnus-vc4", },
        {},
 };
 MODULE_DEVICE_TABLE(of, vc4_of_match);
index dffce6293d875416e38a7c722a1e26dc925f4bf5..5ba281361fb7fbb1b2af87ded378721c6793e291 100644 (file)
@@ -8,7 +8,9 @@
 
 #include "drmP.h"
 #include "drm_gem_cma_helper.h"
+#include "drm_gem_cma_helper.h"
 
+#include <linux/reservation.h>
 #include <drm/drm_encoder.h>
 
 struct vc4_dev {
@@ -56,6 +58,8 @@ struct vc4_dev {
        /* Protects bo_cache and the BO stats. */
        struct mutex bo_lock;
 
+       uint64_t dma_fence_context;
+
        /* Sequence number for the last job queued in bin_job_list.
         * Starts at 0 (no jobs emitted).
         */
@@ -95,12 +99,23 @@ struct vc4_dev {
         */
        struct list_head seqno_cb_list;
 
-       /* The binner overflow memory that's currently set up in
-        * BPOA/BPOS registers.  When overflow occurs and a new one is
-        * allocated, the previous one will be moved to
-        * vc4->current_exec's free list.
+       /* The memory used for storing binner tile alloc, tile state,
+        * and overflow memory allocations.  This is freed when V3D
+        * powers down.
+        */
+       struct vc4_bo *bin_bo;
+
+       /* Size of blocks allocated within bin_bo. */
+       uint32_t bin_alloc_size;
+
+       /* Bitmask of the bin_alloc_size chunks in bin_bo that are
+        * used.
         */
-       struct vc4_bo *overflow_mem;
+       uint32_t bin_alloc_used;
+
+       /* Bitmask of the current bin_alloc used for overflow memory. */
+       uint32_t bin_alloc_overflow;
+
        struct work_struct overflow_mem_work;
 
        int power_refcount;
@@ -150,6 +165,10 @@ struct vc4_bo {
         * DRM_IOCTL_VC4_CREATE_SHADER_BO.
         */
        struct vc4_validated_shader_info *validated_shader;
+
+       /* normally (resv == &_resv) except for imported bo's */
+       struct reservation_object *resv;
+       struct reservation_object _resv;
 };
 
 static inline struct vc4_bo *
@@ -158,6 +177,19 @@ to_vc4_bo(struct drm_gem_object *bo)
        return (struct vc4_bo *)bo;
 }
 
+struct vc4_fence {
+       struct dma_fence base;
+       struct drm_device *dev;
+       /* vc4 seqno for signaled() test */
+       uint64_t seqno;
+};
+
+static inline struct vc4_fence *
+to_vc4_fence(struct dma_fence *fence)
+{
+       return (struct vc4_fence *)fence;
+}
+
 struct vc4_seqno_cb {
        struct work_struct work;
        uint64_t seqno;
@@ -168,6 +200,7 @@ struct vc4_v3d {
        struct vc4_dev *vc4;
        struct platform_device *pdev;
        void __iomem *regs;
+       struct clk *clk;
 };
 
 struct vc4_hvs {
@@ -230,6 +263,8 @@ struct vc4_exec_info {
        /* Latest write_seqno of any BO that binning depends on. */
        uint64_t bin_dep_seqno;
 
+       struct dma_fence *fence;
+
        /* Last current addresses the hardware was processing when the
         * hangcheck timer checked on us.
         */
@@ -293,8 +328,12 @@ struct vc4_exec_info {
        bool found_increment_semaphore_packet;
        bool found_flush;
        uint8_t bin_tiles_x, bin_tiles_y;
-       struct drm_gem_cma_object *tile_bo;
+       /* Physical address of the start of the tile alloc array
+        * (where each tile's binned CL will start)
+        */
        uint32_t tile_alloc_offset;
+       /* Bitmask of which binner slots are freed when this job completes. */
+       uint32_t bin_slots;
 
        /**
         * Computed addresses pointing into exec_bo where we start the
@@ -436,7 +475,11 @@ int vc4_mmap_bo_ioctl(struct drm_device *dev, void *data,
 int vc4_get_hang_state_ioctl(struct drm_device *dev, void *data,
                             struct drm_file *file_priv);
 int vc4_mmap(struct file *filp, struct vm_area_struct *vma);
+struct reservation_object *vc4_prime_res_obj(struct drm_gem_object *obj);
 int vc4_prime_mmap(struct drm_gem_object *obj, struct vm_area_struct *vma);
+struct drm_gem_object *vc4_prime_import_sg_table(struct drm_device *dev,
+                                                struct dma_buf_attachment *attach,
+                                                struct sg_table *sgt);
 void *vc4_prime_vmap(struct drm_gem_object *obj);
 void vc4_bo_cache_init(struct drm_device *dev);
 void vc4_bo_cache_destroy(struct drm_device *dev);
@@ -446,13 +489,10 @@ int vc4_bo_stats_debugfs(struct seq_file *m, void *arg);
 extern struct platform_driver vc4_crtc_driver;
 bool vc4_event_pending(struct drm_crtc *crtc);
 int vc4_crtc_debugfs_regs(struct seq_file *m, void *arg);
-int vc4_crtc_get_scanoutpos(struct drm_device *dev, unsigned int crtc_id,
-                           unsigned int flags, int *vpos, int *hpos,
-                           ktime_t *stime, ktime_t *etime,
-                           const struct drm_display_mode *mode);
-int vc4_crtc_get_vblank_timestamp(struct drm_device *dev, unsigned int crtc_id,
-                                 int *max_error, struct timeval *vblank_time,
-                                 unsigned flags);
+bool vc4_crtc_get_scanoutpos(struct drm_device *dev, unsigned int crtc_id,
+                            bool in_vblank_irq, int *vpos, int *hpos,
+                            ktime_t *stime, ktime_t *etime,
+                            const struct drm_display_mode *mode);
 
 /* vc4_debugfs.c */
 int vc4_debugfs_init(struct drm_minor *minor);
@@ -468,6 +508,9 @@ int vc4_dpi_debugfs_regs(struct seq_file *m, void *unused);
 extern struct platform_driver vc4_dsi_driver;
 int vc4_dsi_debugfs_regs(struct seq_file *m, void *unused);
 
+/* vc4_fence.c */
+extern const struct dma_fence_ops vc4_fence_ops;
+
 /* vc4_gem.c */
 void vc4_gem_init(struct drm_device *dev);
 void vc4_gem_destroy(struct drm_device *dev);
@@ -522,6 +565,7 @@ void vc4_plane_async_set_fb(struct drm_plane *plane,
 extern struct platform_driver vc4_v3d_driver;
 int vc4_v3d_debugfs_ident(struct seq_file *m, void *unused);
 int vc4_v3d_debugfs_regs(struct seq_file *m, void *unused);
+int vc4_v3d_get_bin_slot(struct vc4_dev *vc4);
 
 /* vc4_validate.c */
 int
diff --git a/drivers/gpu/drm/vc4/vc4_fence.c b/drivers/gpu/drm/vc4/vc4_fence.c
new file mode 100644 (file)
index 0000000..dbf5a5a
--- /dev/null
@@ -0,0 +1,56 @@
+/*
+ * Copyright © 2017 Broadcom
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ */
+
+#include "vc4_drv.h"
+
+static const char *vc4_fence_get_driver_name(struct dma_fence *fence)
+{
+       return "vc4";
+}
+
+static const char *vc4_fence_get_timeline_name(struct dma_fence *fence)
+{
+       return "vc4-v3d";
+}
+
+static bool vc4_fence_enable_signaling(struct dma_fence *fence)
+{
+       return true;
+}
+
+static bool vc4_fence_signaled(struct dma_fence *fence)
+{
+       struct vc4_fence *f = to_vc4_fence(fence);
+       struct vc4_dev *vc4 = to_vc4_dev(f->dev);
+
+       return vc4->finished_seqno >= f->seqno;
+}
+
+const struct dma_fence_ops vc4_fence_ops = {
+       .get_driver_name = vc4_fence_get_driver_name,
+       .get_timeline_name = vc4_fence_get_timeline_name,
+       .enable_signaling = vc4_fence_enable_signaling,
+       .signaled = vc4_fence_signaled,
+       .wait = dma_fence_default_wait,
+       .release = dma_fence_free,
+};
index e9c381c42139573ff0d62c1d14318a05386842e6..735412e3725a39a02d8df0bd01f5a3b27c30fe96 100644 (file)
@@ -463,6 +463,8 @@ vc4_update_bo_seqnos(struct vc4_exec_info *exec, uint64_t seqno)
        for (i = 0; i < exec->bo_count; i++) {
                bo = to_vc4_bo(&exec->bo[i]->base);
                bo->seqno = seqno;
+
+               reservation_object_add_shared_fence(bo->resv, exec->fence);
        }
 
        list_for_each_entry(bo, &exec->unref_list, unref_head) {
@@ -472,7 +474,103 @@ vc4_update_bo_seqnos(struct vc4_exec_info *exec, uint64_t seqno)
        for (i = 0; i < exec->rcl_write_bo_count; i++) {
                bo = to_vc4_bo(&exec->rcl_write_bo[i]->base);
                bo->write_seqno = seqno;
+
+               reservation_object_add_excl_fence(bo->resv, exec->fence);
+       }
+}
+
+static void
+vc4_unlock_bo_reservations(struct drm_device *dev,
+                          struct vc4_exec_info *exec,
+                          struct ww_acquire_ctx *acquire_ctx)
+{
+       int i;
+
+       for (i = 0; i < exec->bo_count; i++) {
+               struct vc4_bo *bo = to_vc4_bo(&exec->bo[i]->base);
+
+               ww_mutex_unlock(&bo->resv->lock);
+       }
+
+       ww_acquire_fini(acquire_ctx);
+}
+
+/* Takes the reservation lock on all the BOs being referenced, so that
+ * at queue submit time we can update the reservations.
+ *
+ * We don't lock the RCL the tile alloc/state BOs, or overflow memory
+ * (all of which are on exec->unref_list).  They're entirely private
+ * to vc4, so we don't attach dma-buf fences to them.
+ */
+static int
+vc4_lock_bo_reservations(struct drm_device *dev,
+                        struct vc4_exec_info *exec,
+                        struct ww_acquire_ctx *acquire_ctx)
+{
+       int contended_lock = -1;
+       int i, ret;
+       struct vc4_bo *bo;
+
+       ww_acquire_init(acquire_ctx, &reservation_ww_class);
+
+retry:
+       if (contended_lock != -1) {
+               bo = to_vc4_bo(&exec->bo[contended_lock]->base);
+               ret = ww_mutex_lock_slow_interruptible(&bo->resv->lock,
+                                                      acquire_ctx);
+               if (ret) {
+                       ww_acquire_done(acquire_ctx);
+                       return ret;
+               }
+       }
+
+       for (i = 0; i < exec->bo_count; i++) {
+               if (i == contended_lock)
+                       continue;
+
+               bo = to_vc4_bo(&exec->bo[i]->base);
+
+               ret = ww_mutex_lock_interruptible(&bo->resv->lock, acquire_ctx);
+               if (ret) {
+                       int j;
+
+                       for (j = 0; j < i; j++) {
+                               bo = to_vc4_bo(&exec->bo[j]->base);
+                               ww_mutex_unlock(&bo->resv->lock);
+                       }
+
+                       if (contended_lock != -1 && contended_lock >= i) {
+                               bo = to_vc4_bo(&exec->bo[contended_lock]->base);
+
+                               ww_mutex_unlock(&bo->resv->lock);
+                       }
+
+                       if (ret == -EDEADLK) {
+                               contended_lock = i;
+                               goto retry;
+                       }
+
+                       ww_acquire_done(acquire_ctx);
+                       return ret;
+               }
        }
+
+       ww_acquire_done(acquire_ctx);
+
+       /* Reserve space for our shared (read-only) fence references,
+        * before we commit the CL to the hardware.
+        */
+       for (i = 0; i < exec->bo_count; i++) {
+               bo = to_vc4_bo(&exec->bo[i]->base);
+
+               ret = reservation_object_reserve_shared(bo->resv);
+               if (ret) {
+                       vc4_unlock_bo_reservations(dev, exec, acquire_ctx);
+                       return ret;
+               }
+       }
+
+       return 0;
 }
 
 /* Queues a struct vc4_exec_info for execution.  If no job is
@@ -484,19 +582,34 @@ vc4_update_bo_seqnos(struct vc4_exec_info *exec, uint64_t seqno)
  * then bump the end address.  That's a change for a later date,
  * though.
  */
-static void
-vc4_queue_submit(struct drm_device *dev, struct vc4_exec_info *exec)
+static int
+vc4_queue_submit(struct drm_device *dev, struct vc4_exec_info *exec,
+                struct ww_acquire_ctx *acquire_ctx)
 {
        struct vc4_dev *vc4 = to_vc4_dev(dev);
        uint64_t seqno;
        unsigned long irqflags;
+       struct vc4_fence *fence;
+
+       fence = kzalloc(sizeof(*fence), GFP_KERNEL);
+       if (!fence)
+               return -ENOMEM;
+       fence->dev = dev;
 
        spin_lock_irqsave(&vc4->job_lock, irqflags);
 
        seqno = ++vc4->emit_seqno;
        exec->seqno = seqno;
+
+       dma_fence_init(&fence->base, &vc4_fence_ops, &vc4->job_lock,
+                      vc4->dma_fence_context, exec->seqno);
+       fence->seqno = exec->seqno;
+       exec->fence = &fence->base;
+
        vc4_update_bo_seqnos(exec, seqno);
 
+       vc4_unlock_bo_reservations(dev, exec, acquire_ctx);
+
        list_add_tail(&exec->head, &vc4->bin_job_list);
 
        /* If no job was executing, kick ours off.  Otherwise, it'll
@@ -509,6 +622,8 @@ vc4_queue_submit(struct drm_device *dev, struct vc4_exec_info *exec)
        }
 
        spin_unlock_irqrestore(&vc4->job_lock, irqflags);
+
+       return 0;
 }
 
 /**
@@ -705,8 +820,15 @@ static void
 vc4_complete_exec(struct drm_device *dev, struct vc4_exec_info *exec)
 {
        struct vc4_dev *vc4 = to_vc4_dev(dev);
+       unsigned long irqflags;
        unsigned i;
 
+       /* If we got force-completed because of GPU reset rather than
+        * through our IRQ handler, signal the fence now.
+        */
+       if (exec->fence)
+               dma_fence_signal(exec->fence);
+
        if (exec->bo) {
                for (i = 0; i < exec->bo_count; i++)
                        drm_gem_object_unreference_unlocked(&exec->bo[i]->base);
@@ -720,6 +842,11 @@ vc4_complete_exec(struct drm_device *dev, struct vc4_exec_info *exec)
                drm_gem_object_unreference_unlocked(&bo->base.base);
        }
 
+       /* Free up the allocation of any bin slots we used. */
+       spin_lock_irqsave(&vc4->job_lock, irqflags);
+       vc4->bin_alloc_used &= ~exec->bin_slots;
+       spin_unlock_irqrestore(&vc4->job_lock, irqflags);
+
        mutex_lock(&vc4->power_lock);
        if (--vc4->power_refcount == 0) {
                pm_runtime_mark_last_busy(&vc4->v3d->pdev->dev);
@@ -874,6 +1001,7 @@ vc4_submit_cl_ioctl(struct drm_device *dev, void *data,
        struct vc4_dev *vc4 = to_vc4_dev(dev);
        struct drm_vc4_submit_cl *args = data;
        struct vc4_exec_info *exec;
+       struct ww_acquire_ctx acquire_ctx;
        int ret = 0;
 
        if ((args->flags & ~VC4_SUBMIT_CL_USE_CLEAR_COLOR) != 0) {
@@ -888,13 +1016,16 @@ vc4_submit_cl_ioctl(struct drm_device *dev, void *data,
        }
 
        mutex_lock(&vc4->power_lock);
-       if (vc4->power_refcount++ == 0)
+       if (vc4->power_refcount++ == 0) {
                ret = pm_runtime_get_sync(&vc4->v3d->pdev->dev);
-       mutex_unlock(&vc4->power_lock);
-       if (ret < 0) {
-               kfree(exec);
-               return ret;
+               if (ret < 0) {
+                       mutex_unlock(&vc4->power_lock);
+                       vc4->power_refcount--;
+                       kfree(exec);
+                       return ret;
+               }
        }
+       mutex_unlock(&vc4->power_lock);
 
        exec->args = args;
        INIT_LIST_HEAD(&exec->unref_list);
@@ -916,12 +1047,18 @@ vc4_submit_cl_ioctl(struct drm_device *dev, void *data,
        if (ret)
                goto fail;
 
+       ret = vc4_lock_bo_reservations(dev, exec, &acquire_ctx);
+       if (ret)
+               goto fail;
+
        /* Clear this out of the struct we'll be putting in the queue,
         * since it's part of our stack.
         */
        exec->args = NULL;
 
-       vc4_queue_submit(dev, exec);
+       ret = vc4_queue_submit(dev, exec, &acquire_ctx);
+       if (ret)
+               goto fail;
 
        /* Return the seqno for our job. */
        args->seqno = vc4->emit_seqno;
@@ -939,6 +1076,8 @@ vc4_gem_init(struct drm_device *dev)
 {
        struct vc4_dev *vc4 = to_vc4_dev(dev);
 
+       vc4->dma_fence_context = dma_fence_context_alloc(1);
+
        INIT_LIST_HEAD(&vc4->bin_job_list);
        INIT_LIST_HEAD(&vc4->render_job_list);
        INIT_LIST_HEAD(&vc4->job_done_list);
@@ -968,9 +1107,9 @@ vc4_gem_destroy(struct drm_device *dev)
        /* V3D should already have disabled its interrupt and cleared
         * the overflow allocation registers.  Now free the object.
         */
-       if (vc4->overflow_mem) {
-               drm_gem_object_unreference_unlocked(&vc4->overflow_mem->base.base);
-               vc4->overflow_mem = NULL;
+       if (vc4->bin_bo) {
+               drm_gem_object_put_unlocked(&vc4->bin_bo->base.base);
+               vc4->bin_bo = NULL;
        }
 
        if (vc4->hang_state)
index e9cbe269710bc194d7caab8031e0d71ec3d61b9e..3c2723f6345c36a66c595b495c73dec3ddaafde5 100644 (file)
@@ -51,6 +51,7 @@
 #include "linux/of_address.h"
 #include "linux/of_gpio.h"
 #include "linux/of_platform.h"
+#include "linux/pm_runtime.h"
 #include "linux/rational.h"
 #include "sound/dmaengine_pcm.h"
 #include "sound/pcm_drm_eld.h"
@@ -449,13 +450,38 @@ static void vc4_hdmi_set_infoframes(struct drm_encoder *encoder)
        vc4_hdmi_set_spd_infoframe(encoder);
 }
 
-static void vc4_hdmi_encoder_mode_set(struct drm_encoder *encoder,
-                                     struct drm_display_mode *unadjusted_mode,
-                                     struct drm_display_mode *mode)
+static void vc4_hdmi_encoder_disable(struct drm_encoder *encoder)
+{
+       struct drm_device *dev = encoder->dev;
+       struct vc4_dev *vc4 = to_vc4_dev(dev);
+       struct vc4_hdmi *hdmi = vc4->hdmi;
+       int ret;
+
+       HDMI_WRITE(VC4_HDMI_RAM_PACKET_CONFIG, 0);
+
+       HDMI_WRITE(VC4_HDMI_TX_PHY_RESET_CTL, 0xf << 16);
+       HD_WRITE(VC4_HD_VID_CTL,
+                HD_READ(VC4_HD_VID_CTL) & ~VC4_HD_VID_CTL_ENABLE);
+
+       HD_WRITE(VC4_HD_M_CTL, VC4_HD_M_SW_RST);
+       udelay(1);
+       HD_WRITE(VC4_HD_M_CTL, 0);
+
+       clk_disable_unprepare(hdmi->hsm_clock);
+       clk_disable_unprepare(hdmi->pixel_clock);
+
+       ret = pm_runtime_put(&hdmi->pdev->dev);
+       if (ret < 0)
+               DRM_ERROR("Failed to release power domain: %d\n", ret);
+}
+
+static void vc4_hdmi_encoder_enable(struct drm_encoder *encoder)
 {
+       struct drm_display_mode *mode = &encoder->crtc->state->adjusted_mode;
        struct vc4_hdmi_encoder *vc4_encoder = to_vc4_hdmi_encoder(encoder);
        struct drm_device *dev = encoder->dev;
        struct vc4_dev *vc4 = to_vc4_dev(dev);
+       struct vc4_hdmi *hdmi = vc4->hdmi;
        bool debug_dump_regs = false;
        bool hsync_pos = mode->flags & DRM_MODE_FLAG_PHSYNC;
        bool vsync_pos = mode->flags & DRM_MODE_FLAG_PVSYNC;
@@ -475,6 +501,64 @@ static void vc4_hdmi_encoder_mode_set(struct drm_encoder *encoder,
                                        interlaced,
                                        VC4_HDMI_VERTB_VBP));
        u32 csc_ctl;
+       int ret;
+
+       ret = pm_runtime_get_sync(&hdmi->pdev->dev);
+       if (ret < 0) {
+               DRM_ERROR("Failed to retain power domain: %d\n", ret);
+               return;
+       }
+
+       /* This is the rate that is set by the firmware.  The number
+        * needs to be a bit higher than the pixel clock rate
+        * (generally 148.5Mhz).
+        */
+       ret = clk_set_rate(hdmi->hsm_clock, 163682864);
+       if (ret) {
+               DRM_ERROR("Failed to set HSM clock rate: %d\n", ret);
+               return;
+       }
+
+       ret = clk_set_rate(hdmi->pixel_clock,
+                          mode->clock * 1000 *
+                          ((mode->flags & DRM_MODE_FLAG_DBLCLK) ? 2 : 1));
+       if (ret) {
+               DRM_ERROR("Failed to set pixel clock rate: %d\n", ret);
+               return;
+       }
+
+       ret = clk_prepare_enable(hdmi->pixel_clock);
+       if (ret) {
+               DRM_ERROR("Failed to turn on pixel clock: %d\n", ret);
+               return;
+       }
+
+       ret = clk_prepare_enable(hdmi->hsm_clock);
+       if (ret) {
+               DRM_ERROR("Failed to turn on HDMI state machine clock: %d\n",
+                         ret);
+               clk_disable_unprepare(hdmi->pixel_clock);
+               return;
+       }
+
+       HD_WRITE(VC4_HD_M_CTL, VC4_HD_M_SW_RST);
+       udelay(1);
+       HD_WRITE(VC4_HD_M_CTL, 0);
+
+       HD_WRITE(VC4_HD_M_CTL, VC4_HD_M_ENABLE);
+
+       HDMI_WRITE(VC4_HDMI_SW_RESET_CONTROL,
+                  VC4_HDMI_SW_RESET_HDMI |
+                  VC4_HDMI_SW_RESET_FORMAT_DETECT);
+
+       HDMI_WRITE(VC4_HDMI_SW_RESET_CONTROL, 0);
+
+       /* PHY should be in reset, like
+        * vc4_hdmi_encoder_disable() does.
+        */
+       HDMI_WRITE(VC4_HDMI_TX_PHY_RESET_CTL, 0xf << 16);
+
+       HDMI_WRITE(VC4_HDMI_TX_PHY_RESET_CTL, 0);
 
        if (debug_dump_regs) {
                DRM_INFO("HDMI regs before:\n");
@@ -483,9 +567,6 @@ static void vc4_hdmi_encoder_mode_set(struct drm_encoder *encoder,
 
        HD_WRITE(VC4_HD_VID_CTL, 0);
 
-       clk_set_rate(vc4->hdmi->pixel_clock, mode->clock * 1000 *
-                    ((mode->flags & DRM_MODE_FLAG_DBLCLK) ? 2 : 1));
-
        HDMI_WRITE(VC4_HDMI_SCHEDULER_CONTROL,
                   HDMI_READ(VC4_HDMI_SCHEDULER_CONTROL) |
                   VC4_HDMI_SCHEDULER_CONTROL_MANUAL_FORMAT |
@@ -559,28 +640,6 @@ static void vc4_hdmi_encoder_mode_set(struct drm_encoder *encoder,
                DRM_INFO("HDMI regs after:\n");
                vc4_hdmi_dump_regs(dev);
        }
-}
-
-static void vc4_hdmi_encoder_disable(struct drm_encoder *encoder)
-{
-       struct drm_device *dev = encoder->dev;
-       struct vc4_dev *vc4 = to_vc4_dev(dev);
-
-       HDMI_WRITE(VC4_HDMI_RAM_PACKET_CONFIG, 0);
-
-       HDMI_WRITE(VC4_HDMI_TX_PHY_RESET_CTL, 0xf << 16);
-       HD_WRITE(VC4_HD_VID_CTL,
-                HD_READ(VC4_HD_VID_CTL) & ~VC4_HD_VID_CTL_ENABLE);
-}
-
-static void vc4_hdmi_encoder_enable(struct drm_encoder *encoder)
-{
-       struct vc4_hdmi_encoder *vc4_encoder = to_vc4_hdmi_encoder(encoder);
-       struct drm_device *dev = encoder->dev;
-       struct vc4_dev *vc4 = to_vc4_dev(dev);
-       int ret;
-
-       HDMI_WRITE(VC4_HDMI_TX_PHY_RESET_CTL, 0);
 
        HD_WRITE(VC4_HD_VID_CTL,
                 HD_READ(VC4_HD_VID_CTL) |
@@ -646,7 +705,6 @@ static void vc4_hdmi_encoder_enable(struct drm_encoder *encoder)
 }
 
 static const struct drm_encoder_helper_funcs vc4_hdmi_encoder_helper_funcs = {
-       .mode_set = vc4_hdmi_encoder_mode_set,
        .disable = vc4_hdmi_encoder_disable,
        .enable = vc4_hdmi_encoder_enable,
 };
@@ -1147,33 +1205,6 @@ static int vc4_hdmi_bind(struct device *dev, struct device *master, void *data)
                return -EPROBE_DEFER;
        }
 
-       /* Enable the clocks at startup.  We can't quite recover from
-        * turning off the pixel clock during disable/enables yet, so
-        * it's always running.
-        */
-       ret = clk_prepare_enable(hdmi->pixel_clock);
-       if (ret) {
-               DRM_ERROR("Failed to turn on pixel clock: %d\n", ret);
-               goto err_put_i2c;
-       }
-
-       /* This is the rate that is set by the firmware.  The number
-        * needs to be a bit higher than the pixel clock rate
-        * (generally 148.5Mhz).
-        */
-       ret = clk_set_rate(hdmi->hsm_clock, 163682864);
-       if (ret) {
-               DRM_ERROR("Failed to set HSM clock rate: %d\n", ret);
-               goto err_unprepare_pix;
-       }
-
-       ret = clk_prepare_enable(hdmi->hsm_clock);
-       if (ret) {
-               DRM_ERROR("Failed to turn on HDMI state machine clock: %d\n",
-                         ret);
-               goto err_unprepare_pix;
-       }
-
        /* Only use the GPIO HPD pin if present in the DT, otherwise
         * we'll use the HDMI core's register.
         */
@@ -1185,7 +1216,7 @@ static int vc4_hdmi_bind(struct device *dev, struct device *master, void *data)
                                                         &hpd_gpio_flags);
                if (hdmi->hpd_gpio < 0) {
                        ret = hdmi->hpd_gpio;
-                       goto err_unprepare_hsm;
+                       goto err_put_i2c;
                }
 
                hdmi->hpd_active_low = hpd_gpio_flags & OF_GPIO_ACTIVE_LOW;
@@ -1193,25 +1224,7 @@ static int vc4_hdmi_bind(struct device *dev, struct device *master, void *data)
 
        vc4->hdmi = hdmi;
 
-       /* HDMI core must be enabled. */
-       if (!(HD_READ(VC4_HD_M_CTL) & VC4_HD_M_ENABLE)) {
-               HD_WRITE(VC4_HD_M_CTL, VC4_HD_M_SW_RST);
-               udelay(1);
-               HD_WRITE(VC4_HD_M_CTL, 0);
-
-               HD_WRITE(VC4_HD_M_CTL, VC4_HD_M_ENABLE);
-
-               HDMI_WRITE(VC4_HDMI_SW_RESET_CONTROL,
-                          VC4_HDMI_SW_RESET_HDMI |
-                          VC4_HDMI_SW_RESET_FORMAT_DETECT);
-
-               HDMI_WRITE(VC4_HDMI_SW_RESET_CONTROL, 0);
-
-               /* PHY should be in reset, like
-                * vc4_hdmi_encoder_disable() does.
-                */
-               HDMI_WRITE(VC4_HDMI_TX_PHY_RESET_CTL, 0xf << 16);
-       }
+       pm_runtime_enable(dev);
 
        drm_encoder_init(drm, hdmi->encoder, &vc4_hdmi_encoder_funcs,
                         DRM_MODE_ENCODER_TMDS, NULL);
@@ -1231,10 +1244,7 @@ static int vc4_hdmi_bind(struct device *dev, struct device *master, void *data)
 
 err_destroy_encoder:
        vc4_hdmi_encoder_destroy(hdmi->encoder);
-err_unprepare_hsm:
-       clk_disable_unprepare(hdmi->hsm_clock);
-err_unprepare_pix:
-       clk_disable_unprepare(hdmi->pixel_clock);
+       pm_runtime_disable(dev);
 err_put_i2c:
        put_device(&hdmi->ddc->dev);
 
@@ -1253,8 +1263,8 @@ static void vc4_hdmi_unbind(struct device *dev, struct device *master,
        vc4_hdmi_connector_destroy(hdmi->connector);
        vc4_hdmi_encoder_destroy(hdmi->encoder);
 
-       clk_disable_unprepare(hdmi->pixel_clock);
-       clk_disable_unprepare(hdmi->hsm_clock);
+       pm_runtime_disable(dev);
+
        put_device(&hdmi->ddc->dev);
 
        vc4->hdmi = NULL;
index cdc6e67607056f06e5730cac05f46a19a4516ae1..7d7af3a93d941bb9552afc1140c408c8da86be37 100644 (file)
@@ -59,50 +59,45 @@ vc4_overflow_mem_work(struct work_struct *work)
 {
        struct vc4_dev *vc4 =
                container_of(work, struct vc4_dev, overflow_mem_work);
-       struct drm_device *dev = vc4->dev;
-       struct vc4_bo *bo;
+       struct vc4_bo *bo = vc4->bin_bo;
+       int bin_bo_slot;
+       struct vc4_exec_info *exec;
+       unsigned long irqflags;
 
-       bo = vc4_bo_create(dev, 256 * 1024, true);
-       if (IS_ERR(bo)) {
+       bin_bo_slot = vc4_v3d_get_bin_slot(vc4);
+       if (bin_bo_slot < 0) {
                DRM_ERROR("Couldn't allocate binner overflow mem\n");
                return;
        }
 
-       /* If there's a job executing currently, then our previous
-        * overflow allocation is getting used in that job and we need
-        * to queue it to be released when the job is done.  But if no
-        * job is executing at all, then we can free the old overflow
-        * object direcctly.
-        *
-        * No lock necessary for this pointer since we're the only
-        * ones that update the pointer, and our workqueue won't
-        * reenter.
-        */
-       if (vc4->overflow_mem) {
-               struct vc4_exec_info *current_exec;
-               unsigned long irqflags;
-
-               spin_lock_irqsave(&vc4->job_lock, irqflags);
-               current_exec = vc4_first_bin_job(vc4);
-               if (!current_exec)
-                       current_exec = vc4_last_render_job(vc4);
-               if (current_exec) {
-                       vc4->overflow_mem->seqno = current_exec->seqno;
-                       list_add_tail(&vc4->overflow_mem->unref_head,
-                                     &current_exec->unref_list);
-                       vc4->overflow_mem = NULL;
+       spin_lock_irqsave(&vc4->job_lock, irqflags);
+
+       if (vc4->bin_alloc_overflow) {
+               /* If we had overflow memory allocated previously,
+                * then that chunk will free when the current bin job
+                * is done.  If we don't have a bin job running, then
+                * the chunk will be done whenever the list of render
+                * jobs has drained.
+                */
+               exec = vc4_first_bin_job(vc4);
+               if (!exec)
+                       exec = vc4_last_render_job(vc4);
+               if (exec) {
+                       exec->bin_slots |= vc4->bin_alloc_overflow;
+               } else {
+                       /* There's nothing queued in the hardware, so
+                        * the old slot is free immediately.
+                        */
+                       vc4->bin_alloc_used &= ~vc4->bin_alloc_overflow;
                }
-               spin_unlock_irqrestore(&vc4->job_lock, irqflags);
        }
+       vc4->bin_alloc_overflow = BIT(bin_bo_slot);
 
-       if (vc4->overflow_mem)
-               drm_gem_object_unreference_unlocked(&vc4->overflow_mem->base.base);
-       vc4->overflow_mem = bo;
-
-       V3D_WRITE(V3D_BPOA, bo->base.paddr);
+       V3D_WRITE(V3D_BPOA, bo->base.paddr + bin_bo_slot * vc4->bin_alloc_size);
        V3D_WRITE(V3D_BPOS, bo->base.base.size);
        V3D_WRITE(V3D_INTCTL, V3D_INT_OUTOMEM);
        V3D_WRITE(V3D_INTENA, V3D_INT_OUTOMEM);
+       spin_unlock_irqrestore(&vc4->job_lock, irqflags);
 }
 
 static void
@@ -142,6 +137,10 @@ vc4_irq_finish_render_job(struct drm_device *dev)
 
        vc4->finished_seqno++;
        list_move_tail(&exec->head, &vc4->job_done_list);
+       if (exec->fence) {
+               dma_fence_signal_locked(exec->fence);
+               exec->fence = NULL;
+       }
        vc4_submit_next_render_job(dev);
 
        wake_up_all(&vc4->job_wait_queue);
index ad7925a9e0ea4c331bdad8474388e8e6058af1c8..237a504f11f0da3dc71301564a4bac752b64db98 100644 (file)
@@ -230,10 +230,12 @@ int vc4_kms_load(struct drm_device *dev)
 
        drm_mode_config_reset(dev);
 
-       vc4->fbdev = drm_fbdev_cma_init(dev, 32,
-                                       dev->mode_config.num_connector);
-       if (IS_ERR(vc4->fbdev))
-               vc4->fbdev = NULL;
+       if (dev->mode_config.num_connector) {
+               vc4->fbdev = drm_fbdev_cma_init(dev, 32,
+                                               dev->mode_config.num_connector);
+               if (IS_ERR(vc4->fbdev))
+                       vc4->fbdev = NULL;
+       }
 
        drm_kms_helper_poll_init(dev);
 
index 4339471f517f9503b3d8617dce12b6bbd74b03fb..5dc19429d4ae65b316eed20472145ed3232bd0ff 100644 (file)
@@ -182,8 +182,7 @@ static void emit_tile(struct vc4_exec_info *exec,
 
        if (has_bin) {
                rcl_u8(setup, VC4_PACKET_BRANCH_TO_SUB_LIST);
-               rcl_u32(setup, (exec->tile_bo->paddr +
-                               exec->tile_alloc_offset +
+               rcl_u32(setup, (exec->tile_alloc_offset +
                                (y * exec->bin_tiles_x + x) * 32));
        }
 
index 7cc346ad9b0baed63701d1fae8f0306aa7713129..c53afec34586bcdf44ef7d9e713283a3a816373b 100644 (file)
@@ -16,6 +16,7 @@
  * this program.  If not, see <http://www.gnu.org/licenses/>.
  */
 
+#include "linux/clk.h"
 #include "linux/component.h"
 #include "linux/pm_runtime.h"
 #include "vc4_drv.h"
@@ -156,6 +157,144 @@ static void vc4_v3d_init_hw(struct drm_device *dev)
        V3D_WRITE(V3D_VPMBASE, 0);
 }
 
+int vc4_v3d_get_bin_slot(struct vc4_dev *vc4)
+{
+       struct drm_device *dev = vc4->dev;
+       unsigned long irqflags;
+       int slot;
+       uint64_t seqno = 0;
+       struct vc4_exec_info *exec;
+
+try_again:
+       spin_lock_irqsave(&vc4->job_lock, irqflags);
+       slot = ffs(~vc4->bin_alloc_used);
+       if (slot != 0) {
+               /* Switch from ffs() bit index to a 0-based index. */
+               slot--;
+               vc4->bin_alloc_used |= BIT(slot);
+               spin_unlock_irqrestore(&vc4->job_lock, irqflags);
+               return slot;
+       }
+
+       /* Couldn't find an open slot.  Wait for render to complete
+        * and try again.
+        */
+       exec = vc4_last_render_job(vc4);
+       if (exec)
+               seqno = exec->seqno;
+       spin_unlock_irqrestore(&vc4->job_lock, irqflags);
+
+       if (seqno) {
+               int ret = vc4_wait_for_seqno(dev, seqno, ~0ull, true);
+
+               if (ret == 0)
+                       goto try_again;
+
+               return ret;
+       }
+
+       return -ENOMEM;
+}
+
+/**
+ * vc4_allocate_bin_bo() - allocates the memory that will be used for
+ * tile binning.
+ *
+ * The binner has a limitation that the addresses in the tile state
+ * buffer that point into the tile alloc buffer or binner overflow
+ * memory only have 28 bits (256MB), and the top 4 on the bus for
+ * tile alloc references end up coming from the tile state buffer's
+ * address.
+ *
+ * To work around this, we allocate a single large buffer while V3D is
+ * in use, make sure that it has the top 4 bits constant across its
+ * entire extent, and then put the tile state, tile alloc, and binner
+ * overflow memory inside that buffer.
+ *
+ * This creates a limitation where we may not be able to execute a job
+ * if it doesn't fit within the buffer that we allocated up front.
+ * However, it turns out that 16MB is "enough for anybody", and
+ * real-world applications run into allocation failures from the
+ * overall CMA pool before they make scenes complicated enough to run
+ * out of bin space.
+ */
+int
+vc4_allocate_bin_bo(struct drm_device *drm)
+{
+       struct vc4_dev *vc4 = to_vc4_dev(drm);
+       struct vc4_v3d *v3d = vc4->v3d;
+       uint32_t size = 16 * 1024 * 1024;
+       int ret = 0;
+       struct list_head list;
+
+       /* We may need to try allocating more than once to get a BO
+        * that doesn't cross 256MB.  Track the ones we've allocated
+        * that failed so far, so that we can free them when we've got
+        * one that succeeded (if we freed them right away, our next
+        * allocation would probably be the same chunk of memory).
+        */
+       INIT_LIST_HEAD(&list);
+
+       while (true) {
+               struct vc4_bo *bo = vc4_bo_create(drm, size, true);
+
+               if (IS_ERR(bo)) {
+                       ret = PTR_ERR(bo);
+
+                       dev_err(&v3d->pdev->dev,
+                               "Failed to allocate memory for tile binning: "
+                               "%d. You may need to enable CMA or give it "
+                               "more memory.",
+                               ret);
+                       break;
+               }
+
+               /* Check if this BO won't trigger the addressing bug. */
+               if ((bo->base.paddr & 0xf0000000) ==
+                   ((bo->base.paddr + bo->base.base.size - 1) & 0xf0000000)) {
+                       vc4->bin_bo = bo;
+
+                       /* Set up for allocating 512KB chunks of
+                        * binner memory.  The biggest allocation we
+                        * need to do is for the initial tile alloc +
+                        * tile state buffer.  We can render to a
+                        * maximum of ((2048*2048) / (32*32) = 4096
+                        * tiles in a frame (until we do floating
+                        * point rendering, at which point it would be
+                        * 8192).  Tile state is 48b/tile (rounded to
+                        * a page), and tile alloc is 32b/tile
+                        * (rounded to a page), plus a page of extra,
+                        * for a total of 320kb for our worst-case.
+                        * We choose 512kb so that it divides evenly
+                        * into our 16MB, and the rest of the 512kb
+                        * will be used as storage for the overflow
+                        * from the initial 32b CL per bin.
+                        */
+                       vc4->bin_alloc_size = 512 * 1024;
+                       vc4->bin_alloc_used = 0;
+                       vc4->bin_alloc_overflow = 0;
+                       WARN_ON_ONCE(sizeof(vc4->bin_alloc_used) * 8 !=
+                                    bo->base.base.size / vc4->bin_alloc_size);
+
+                       break;
+               }
+
+               /* Put it on the list to free later, and try again. */
+               list_add(&bo->unref_head, &list);
+       }
+
+       /* Free all the BOs we allocated but didn't choose. */
+       while (!list_empty(&list)) {
+               struct vc4_bo *bo = list_last_entry(&list,
+                                                   struct vc4_bo, unref_head);
+
+               list_del(&bo->unref_head);
+               drm_gem_object_put_unlocked(&bo->base.base);
+       }
+
+       return ret;
+}
+
 #ifdef CONFIG_PM
 static int vc4_v3d_runtime_suspend(struct device *dev)
 {
@@ -164,6 +303,11 @@ static int vc4_v3d_runtime_suspend(struct device *dev)
 
        vc4_irq_uninstall(vc4->dev);
 
+       drm_gem_object_put_unlocked(&vc4->bin_bo->base.base);
+       vc4->bin_bo = NULL;
+
+       clk_disable_unprepare(v3d->clk);
+
        return 0;
 }
 
@@ -171,6 +315,15 @@ static int vc4_v3d_runtime_resume(struct device *dev)
 {
        struct vc4_v3d *v3d = dev_get_drvdata(dev);
        struct vc4_dev *vc4 = v3d->vc4;
+       int ret;
+
+       ret = vc4_allocate_bin_bo(vc4->dev);
+       if (ret)
+               return ret;
+
+       ret = clk_prepare_enable(v3d->clk);
+       if (ret != 0)
+               return ret;
 
        vc4_v3d_init_hw(vc4->dev);
        vc4_irq_postinstall(vc4->dev);
@@ -202,12 +355,38 @@ static int vc4_v3d_bind(struct device *dev, struct device *master, void *data)
        vc4->v3d = v3d;
        v3d->vc4 = vc4;
 
+       v3d->clk = devm_clk_get(dev, NULL);
+       if (IS_ERR(v3d->clk)) {
+               int ret = PTR_ERR(v3d->clk);
+
+               if (ret == -ENOENT) {
+                       /* bcm2835 didn't have a clock reference in the DT. */
+                       ret = 0;
+                       v3d->clk = NULL;
+               } else {
+                       if (ret != -EPROBE_DEFER)
+                               dev_err(dev, "Failed to get V3D clock: %d\n",
+                                       ret);
+                       return ret;
+               }
+       }
+
        if (V3D_READ(V3D_IDENT0) != V3D_EXPECTED_IDENT0) {
                DRM_ERROR("V3D_IDENT0 read 0x%08x instead of 0x%08x\n",
                          V3D_READ(V3D_IDENT0), V3D_EXPECTED_IDENT0);
                return -EINVAL;
        }
 
+       ret = clk_prepare_enable(v3d->clk);
+       if (ret != 0)
+               return ret;
+
+       ret = vc4_allocate_bin_bo(drm);
+       if (ret) {
+               clk_disable_unprepare(v3d->clk);
+               return ret;
+       }
+
        /* Reset the binner overflow address/size at setup, to be sure
         * we don't reuse an old one.
         */
@@ -271,6 +450,7 @@ static int vc4_v3d_dev_remove(struct platform_device *pdev)
 
 static const struct of_device_id vc4_v3d_dt_match[] = {
        { .compatible = "brcm,bcm2835-v3d" },
+       { .compatible = "brcm,cygnus-v3d" },
        { .compatible = "brcm,vc4-v3d" },
        {}
 };
index da6f1e138e8d1252180e9fe1e70d9c1483524636..3de8f11595c08b72a9ef3eb0b46e379627e2c39c 100644 (file)
@@ -348,10 +348,11 @@ static int
 validate_tile_binning_config(VALIDATE_ARGS)
 {
        struct drm_device *dev = exec->exec_bo->base.dev;
-       struct vc4_bo *tile_bo;
+       struct vc4_dev *vc4 = to_vc4_dev(dev);
        uint8_t flags;
-       uint32_t tile_state_size, tile_alloc_size;
-       uint32_t tile_count;
+       uint32_t tile_state_size;
+       uint32_t tile_count, bin_addr;
+       int bin_slot;
 
        if (exec->found_tile_binning_mode_config_packet) {
                DRM_ERROR("Duplicate VC4_PACKET_TILE_BINNING_MODE_CONFIG\n");
@@ -377,13 +378,28 @@ validate_tile_binning_config(VALIDATE_ARGS)
                return -EINVAL;
        }
 
+       bin_slot = vc4_v3d_get_bin_slot(vc4);
+       if (bin_slot < 0) {
+               if (bin_slot != -EINTR && bin_slot != -ERESTARTSYS) {
+                       DRM_ERROR("Failed to allocate binner memory: %d\n",
+                                 bin_slot);
+               }
+               return bin_slot;
+       }
+
+       /* The slot we allocated will only be used by this job, and is
+        * free when the job completes rendering.
+        */
+       exec->bin_slots |= BIT(bin_slot);
+       bin_addr = vc4->bin_bo->base.paddr + bin_slot * vc4->bin_alloc_size;
+
        /* The tile state data array is 48 bytes per tile, and we put it at
         * the start of a BO containing both it and the tile alloc.
         */
        tile_state_size = 48 * tile_count;
 
        /* Since the tile alloc array will follow us, align. */
-       exec->tile_alloc_offset = roundup(tile_state_size, 4096);
+       exec->tile_alloc_offset = bin_addr + roundup(tile_state_size, 4096);
 
        *(uint8_t *)(validated + 14) =
                ((flags & ~(VC4_BIN_CONFIG_ALLOC_INIT_BLOCK_SIZE_MASK |
@@ -394,35 +410,13 @@ validate_tile_binning_config(VALIDATE_ARGS)
                 VC4_SET_FIELD(VC4_BIN_CONFIG_ALLOC_BLOCK_SIZE_128,
                               VC4_BIN_CONFIG_ALLOC_BLOCK_SIZE));
 
-       /* Initial block size. */
-       tile_alloc_size = 32 * tile_count;
-
-       /*
-        * The initial allocation gets rounded to the next 256 bytes before
-        * the hardware starts fulfilling further allocations.
-        */
-       tile_alloc_size = roundup(tile_alloc_size, 256);
-
-       /* Add space for the extra allocations.  This is what gets used first,
-        * before overflow memory.  It must have at least 4096 bytes, but we
-        * want to avoid overflow memory usage if possible.
-        */
-       tile_alloc_size += 1024 * 1024;
-
-       tile_bo = vc4_bo_create(dev, exec->tile_alloc_offset + tile_alloc_size,
-                               true);
-       exec->tile_bo = &tile_bo->base;
-       if (IS_ERR(exec->tile_bo))
-               return PTR_ERR(exec->tile_bo);
-       list_add_tail(&tile_bo->unref_head, &exec->unref_list);
-
        /* tile alloc address. */
-       *(uint32_t *)(validated + 0) = (exec->tile_bo->paddr +
-                                       exec->tile_alloc_offset);
+       *(uint32_t *)(validated + 0) = exec->tile_alloc_offset;
        /* tile alloc size. */
-       *(uint32_t *)(validated + 4) = tile_alloc_size;
+       *(uint32_t *)(validated + 4) = (bin_addr + vc4->bin_alloc_size -
+                                       exec->tile_alloc_offset);
        /* tile state address. */
-       *(uint32_t *)(validated + 8) = exec->tile_bo->paddr;
+       *(uint32_t *)(validated + 8) = bin_addr;
 
        return 0;
 }
index 9fee38a942c43666fc41f6ce1de9f5a98c78ab5c..4b23ba0496322280c03f5697ae759c63e783e1ac 100644 (file)
 #define DRIVER_MAJOR   1
 #define DRIVER_MINOR   0
 
+static struct vgem_device {
+       struct drm_device drm;
+       struct platform_device *platform;
+} *vgem_device;
+
 static void vgem_gem_free_object(struct drm_gem_object *obj)
 {
        struct drm_vgem_gem_object *vgem_obj = to_vgem_bo(obj);
 
+       drm_free_large(vgem_obj->pages);
+
+       if (obj->import_attach)
+               drm_prime_gem_destroy(obj, vgem_obj->table);
+
        drm_gem_object_release(obj);
        kfree(vgem_obj);
 }
@@ -56,26 +66,49 @@ static int vgem_gem_fault(struct vm_fault *vmf)
        struct drm_vgem_gem_object *obj = vma->vm_private_data;
        /* We don't use vmf->pgoff since that has the fake offset */
        unsigned long vaddr = vmf->address;
-       struct page *page;
-
-       page = shmem_read_mapping_page(file_inode(obj->base.filp)->i_mapping,
-                                      (vaddr - vma->vm_start) >> PAGE_SHIFT);
-       if (!IS_ERR(page)) {
-