OSDN Git Service

Merge tag 'drm-misc-next-2019-03-21' of git://anongit.freedesktop.org/drm/drm-misc...
authorDaniel Vetter <daniel.vetter@ffwll.ch>
Mon, 25 Mar 2019 10:05:11 +0000 (11:05 +0100)
committerDaniel Vetter <daniel.vetter@ffwll.ch>
Mon, 25 Mar 2019 10:05:12 +0000 (11:05 +0100)
drm-misc-next for 5.2:

UAPI Changes:
- Add Colorspace connector property (Uma)
- fourcc: Several new YUV formats from ARM (Brian & Ayan)
- fourcc: Fix merge conflicts between new formats above and Swati's that
  went in via topic/hdr-formats-2019-03-07 branch (Maarten)

Cross-subsystem Changes:
- Typed component support via topic/component-typed-2019-02-11 (Maxime/Daniel)

Core Changes:
- Improve component helper documentation (Daniel)
- Avoid calling drm_dev_unregister() twice on unplugged devices (Noralf)
- Add device managed (devm) drm_device init function (Noralf)
- Graduate TINYDRM_MODE to DRM_SIMPLE_MODE in core (Noralf)
- Move MIPI/DSI rate control params computation into core from i915 (David)
- Add support for shmem backed gem objects (Noralf)

Driver Changes:
- various: Use of_node_name_eq for node name comparisons (Rob Herring)
- sun4i: Add DSI burst mode support (Konstantin)
- panel: Add Ronbo RB070D30 MIPI/DSI panel support (Konstantin)
- virtio: A few prime improvements (Gerd)
- tinydrm: Remove tinydrm_device (Noralf)
- vc4: Add load tracker to driver to detect underflow in atomic check (Boris)
- vboxvideo: Move it out of staging \o/ (Hans)
- v3d: Add support for V3D v4.2 (Eric)

Cc: Konstantin Sudakov <k.sudakov@integrasources.com>
Cc: Rob Herring <robh@kernel.org>
Cc: Daniel Vetter <daniel.vetter@ffwll.ch>
Cc: Maxime Ripard <maxime.ripard@bootlin.com>
Cc: Uma Shankar <uma.shankar@intel.com>
Cc: Noralf Trønnes <noralf@tronnes.org>
Cc: Gerd Hoffmann <kraxel@redhat.com>
Cc: David Francis <David.Francis@amd.com>
Cc: Boris Brezillon <boris.brezillon@bootlin.com>
Cc: Eric Anholt <eric@anholt.net>
Cc: Hans de Goede <hdegoede@redhat.com>
Cc: Brian Starkey <brian.starkey@arm.com>
Cc: Ayan Kumar Halder <ayan.halder@arm.com>
Cc: Maarten Lankhorst <maarten.lankhorst@linux.intel.com>
Signed-off-by: Daniel Vetter <daniel.vetter@ffwll.ch>
From: Sean Paul <sean@poorly.run>
Link: https://patchwork.freedesktop.org/patch/msgid/20190321170805.GA50145@art_vandelay
170 files changed:
Documentation/devicetree/bindings/display/amlogic,meson-vpu.txt
Documentation/devicetree/bindings/display/panel/ronbo,rb070d30.yaml [new file with mode: 0644]
Documentation/devicetree/bindings/gpu/brcm,bcm-v3d.txt
Documentation/devicetree/bindings/vendor-prefixes.txt
Documentation/driver-api/component.rst
Documentation/driver-model/devres.txt
Documentation/gpu/drm-internals.rst
Documentation/gpu/drm-kms-helpers.rst
Documentation/gpu/tinydrm.rst
Documentation/gpu/todo.rst
MAINTAINERS
drivers/dma-buf/reservation.c
drivers/gpu/drm/Kconfig
drivers/gpu/drm/Makefile
drivers/gpu/drm/amd/amdgpu/amdgpu_drv.c
drivers/gpu/drm/amd/amdgpu/gmc_v6_0.c
drivers/gpu/drm/amd/amdgpu/gmc_v7_0.c
drivers/gpu/drm/amd/amdgpu/gmc_v8_0.c
drivers/gpu/drm/amd/amdgpu/gmc_v9_0.c
drivers/gpu/drm/arm/malidp_drv.c
drivers/gpu/drm/arm/malidp_drv.h
drivers/gpu/drm/arm/malidp_hw.c
drivers/gpu/drm/arm/malidp_hw.h
drivers/gpu/drm/arm/malidp_mw.c
drivers/gpu/drm/arm/malidp_planes.c
drivers/gpu/drm/arm/malidp_regs.h
drivers/gpu/drm/bochs/bochs_kms.c
drivers/gpu/drm/drm_atomic_helper.c
drivers/gpu/drm/drm_atomic_uapi.c
drivers/gpu/drm/drm_connector.c
drivers/gpu/drm/drm_dp_mst_topology.c
drivers/gpu/drm/drm_drv.c
drivers/gpu/drm/drm_dsc.c
drivers/gpu/drm/drm_edid.c
drivers/gpu/drm/drm_fb_helper.c
drivers/gpu/drm/drm_file.c
drivers/gpu/drm/drm_fourcc.c
drivers/gpu/drm/drm_gem.c
drivers/gpu/drm/drm_gem_shmem_helper.c [new file with mode: 0644]
drivers/gpu/drm/drm_kms_helper_common.c
drivers/gpu/drm/drm_memory.c
drivers/gpu/drm/drm_panel_orientation_quirks.c
drivers/gpu/drm/drm_prime.c
drivers/gpu/drm/drm_syncobj.c
drivers/gpu/drm/drm_vm.c
drivers/gpu/drm/etnaviv/etnaviv_drv.c
drivers/gpu/drm/etnaviv/etnaviv_drv.h
drivers/gpu/drm/etnaviv/etnaviv_gem.c
drivers/gpu/drm/etnaviv/etnaviv_gem.h
drivers/gpu/drm/etnaviv/etnaviv_gem_prime.c
drivers/gpu/drm/etnaviv/etnaviv_gem_submit.c
drivers/gpu/drm/i915/i915_reg.h
drivers/gpu/drm/i915/intel_atomic.c
drivers/gpu/drm/i915/intel_atomic_plane.c
drivers/gpu/drm/i915/intel_connector.c
drivers/gpu/drm/i915/intel_display.c
drivers/gpu/drm/i915/intel_drv.h
drivers/gpu/drm/i915/intel_hdmi.c
drivers/gpu/drm/i915/intel_pm.c
drivers/gpu/drm/i915/intel_sprite.c
drivers/gpu/drm/i915/intel_vdsc.c
drivers/gpu/drm/imx/imx-drm-core.c
drivers/gpu/drm/meson/Makefile
drivers/gpu/drm/meson/meson_canvas.c [deleted file]
drivers/gpu/drm/meson/meson_canvas.h [deleted file]
drivers/gpu/drm/meson/meson_crtc.c
drivers/gpu/drm/meson/meson_drv.c
drivers/gpu/drm/meson/meson_drv.h
drivers/gpu/drm/meson/meson_overlay.c
drivers/gpu/drm/meson/meson_plane.c
drivers/gpu/drm/meson/meson_viu.c
drivers/gpu/drm/msm/msm_drv.c
drivers/gpu/drm/msm/msm_drv.h
drivers/gpu/drm/msm/msm_gem.c
drivers/gpu/drm/msm/msm_gem_prime.c
drivers/gpu/drm/msm/msm_gem_submit.c
drivers/gpu/drm/panel/Kconfig
drivers/gpu/drm/panel/Makefile
drivers/gpu/drm/panel/panel-ronbo-rb070d30.c [new file with mode: 0644]
drivers/gpu/drm/qxl/qxl_display.c
drivers/gpu/drm/radeon/radeon_device.c
drivers/gpu/drm/selftests/test-drm_mm.c
drivers/gpu/drm/stm/Kconfig
drivers/gpu/drm/sun4i/sun4i_backend.c
drivers/gpu/drm/sun4i/sun4i_lvds.c
drivers/gpu/drm/sun4i/sun4i_rgb.c
drivers/gpu/drm/sun4i/sun4i_tcon.c
drivers/gpu/drm/sun4i/sun4i_tcon.h
drivers/gpu/drm/sun4i/sun6i_mipi_dsi.c
drivers/gpu/drm/sun4i/sun6i_mipi_dsi.h
drivers/gpu/drm/sun4i/sun8i_mixer.c
drivers/gpu/drm/sun4i/sun8i_mixer.h
drivers/gpu/drm/sun4i/sun8i_vi_layer.c
drivers/gpu/drm/sun4i/sun8i_vi_layer.h
drivers/gpu/drm/tinydrm/core/Makefile
drivers/gpu/drm/tinydrm/core/tinydrm-core.c [deleted file]
drivers/gpu/drm/tinydrm/core/tinydrm-helpers.c
drivers/gpu/drm/tinydrm/core/tinydrm-pipe.c
drivers/gpu/drm/tinydrm/hx8357d.c
drivers/gpu/drm/tinydrm/ili9225.c
drivers/gpu/drm/tinydrm/ili9341.c
drivers/gpu/drm/tinydrm/mi0283qt.c
drivers/gpu/drm/tinydrm/mipi-dbi.c
drivers/gpu/drm/tinydrm/repaper.c
drivers/gpu/drm/tinydrm/st7586.c
drivers/gpu/drm/tinydrm/st7735r.c
drivers/gpu/drm/udl/udl_drv.c
drivers/gpu/drm/v3d/Kconfig
drivers/gpu/drm/v3d/v3d_bo.c
drivers/gpu/drm/v3d/v3d_debugfs.c
drivers/gpu/drm/v3d/v3d_drv.c
drivers/gpu/drm/v3d/v3d_drv.h
drivers/gpu/drm/v3d/v3d_gem.c
drivers/gpu/drm/v3d/v3d_irq.c
drivers/gpu/drm/v3d/v3d_mmu.c
drivers/gpu/drm/v3d/v3d_regs.h
drivers/gpu/drm/v3d/v3d_sched.c
drivers/gpu/drm/vboxvideo/Kconfig [moved from drivers/staging/vboxvideo/Kconfig with 100% similarity]
drivers/gpu/drm/vboxvideo/Makefile [moved from drivers/staging/vboxvideo/Makefile with 100% similarity]
drivers/gpu/drm/vboxvideo/hgsmi_base.c [moved from drivers/staging/vboxvideo/hgsmi_base.c with 100% similarity]
drivers/gpu/drm/vboxvideo/hgsmi_ch_setup.h [moved from drivers/staging/vboxvideo/hgsmi_ch_setup.h with 100% similarity]
drivers/gpu/drm/vboxvideo/hgsmi_channels.h [moved from drivers/staging/vboxvideo/hgsmi_channels.h with 100% similarity]
drivers/gpu/drm/vboxvideo/hgsmi_defs.h [moved from drivers/staging/vboxvideo/hgsmi_defs.h with 100% similarity]
drivers/gpu/drm/vboxvideo/modesetting.c [moved from drivers/staging/vboxvideo/modesetting.c with 100% similarity]
drivers/gpu/drm/vboxvideo/vbox_drv.c [moved from drivers/staging/vboxvideo/vbox_drv.c with 89% similarity]
drivers/gpu/drm/vboxvideo/vbox_drv.h [moved from drivers/staging/vboxvideo/vbox_drv.h with 97% similarity]
drivers/gpu/drm/vboxvideo/vbox_fb.c [moved from drivers/staging/vboxvideo/vbox_fb.c with 100% similarity]
drivers/gpu/drm/vboxvideo/vbox_hgsmi.c [moved from drivers/staging/vboxvideo/vbox_hgsmi.c with 100% similarity]
drivers/gpu/drm/vboxvideo/vbox_irq.c [moved from drivers/staging/vboxvideo/vbox_irq.c with 93% similarity]
drivers/gpu/drm/vboxvideo/vbox_main.c [moved from drivers/staging/vboxvideo/vbox_main.c with 98% similarity]
drivers/gpu/drm/vboxvideo/vbox_mode.c [moved from drivers/staging/vboxvideo/vbox_mode.c with 97% similarity]
drivers/gpu/drm/vboxvideo/vbox_prime.c [moved from drivers/staging/vboxvideo/vbox_prime.c with 100% similarity]
drivers/gpu/drm/vboxvideo/vbox_ttm.c [moved from drivers/staging/vboxvideo/vbox_ttm.c with 100% similarity]
drivers/gpu/drm/vboxvideo/vboxvideo.h [moved from drivers/staging/vboxvideo/vboxvideo.h with 100% similarity]
drivers/gpu/drm/vboxvideo/vboxvideo_guest.h [moved from drivers/staging/vboxvideo/vboxvideo_guest.h with 100% similarity]
drivers/gpu/drm/vboxvideo/vboxvideo_vbe.h [moved from drivers/staging/vboxvideo/vboxvideo_vbe.h with 100% similarity]
drivers/gpu/drm/vboxvideo/vbva_base.c [moved from drivers/staging/vboxvideo/vbva_base.c with 100% similarity]
drivers/gpu/drm/vc4/vc4_bo.c
drivers/gpu/drm/vc4/vc4_crtc.c
drivers/gpu/drm/vc4/vc4_debugfs.c
drivers/gpu/drm/vc4/vc4_drv.c
drivers/gpu/drm/vc4/vc4_drv.h
drivers/gpu/drm/vc4/vc4_gem.c
drivers/gpu/drm/vc4/vc4_hvs.c
drivers/gpu/drm/vc4/vc4_kms.c
drivers/gpu/drm/vc4/vc4_plane.c
drivers/gpu/drm/vc4/vc4_regs.h
drivers/gpu/drm/vc4/vc4_txp.c
drivers/gpu/drm/virtio/virtgpu_drv.c
drivers/gpu/drm/virtio/virtgpu_drv.h
drivers/gpu/drm/virtio/virtgpu_prime.c
drivers/gpu/drm/xen/xen_drm_front.c
drivers/staging/Kconfig
drivers/staging/Makefile
drivers/staging/vboxvideo/TODO [deleted file]
include/drm/drm_cache.h
include/drm/drm_connector.h
include/drm/drm_crtc.h
include/drm/drm_drv.h
include/drm/drm_dsc.h
include/drm/drm_edid.h
include/drm/drm_gem.h
include/drm/drm_gem_shmem_helper.h [new file with mode: 0644]
include/drm/drm_modes.h
include/drm/drm_utils.h
include/drm/tinydrm/mipi-dbi.h
include/drm/tinydrm/tinydrm-helpers.h
include/drm/tinydrm/tinydrm.h [deleted file]
include/linux/reservation.h
include/uapi/drm/drm_fourcc.h

index c65fd7a..419444e 100644 (file)
@@ -60,15 +60,14 @@ Required properties:
 - reg: base address and size of he following memory-mapped regions :
        - vpu
        - hhi
-       - dmc
 - reg-names: should contain the names of the previous memory regions
 - interrupts: should contain the VENC Vsync interrupt number
+- amlogic,canvas: phandle to canvas provider node as described in the file
+       ../soc/amlogic/amlogic,canvas.txt
 
 Optional properties:
 - power-domains: Optional phandle to associated power domain as described in
        the file ../power/power_domain.txt
-- amlogic,canvas: phandle to canvas provider node as described in the file
-       ../soc/amlogic/amlogic,canvas.txt
 
 Required nodes:
 
diff --git a/Documentation/devicetree/bindings/display/panel/ronbo,rb070d30.yaml b/Documentation/devicetree/bindings/display/panel/ronbo,rb070d30.yaml
new file mode 100644 (file)
index 0000000..0e7987f
--- /dev/null
@@ -0,0 +1,51 @@
+# SPDX-License-Identifier: (GPL-2.0+ OR X11)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/display/panel/ronbo,rb070d30.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Ronbo RB070D30 DSI Display Panel
+
+maintainers:
+  - Maxime Ripard <maxime.ripard@bootlin.com>
+
+properties:
+  compatible:
+    const: ronbo,rb070d30
+
+  reg:
+    description: MIPI-DSI virtual channel
+
+  power-gpios:
+    description: GPIO used for the power pin
+    maxItems: 1
+
+  reset-gpios:
+    description: GPIO used for the reset pin
+    maxItems: 1
+
+  shlr-gpios:
+    description: GPIO used for the shlr pin (horizontal flip)
+    maxItems: 1
+
+  updn-gpios:
+    description: GPIO used for the updn pin (vertical flip)
+    maxItems: 1
+
+  vcc-lcd-supply:
+    description: Power regulator
+
+  backlight:
+    description: Backlight used by the panel
+    $ref: "/schemas/types.yaml#/definitions/phandle"
+
+required:
+  - compatible
+  - power-gpios
+  - reg
+  - reset-gpios
+  - shlr-gpios
+  - updn-gpios
+  - vcc-lcd-supply
+
+additionalProperties: false
index c907aa8..b2df82b 100644 (file)
@@ -6,15 +6,20 @@ For V3D 2.x, see brcm,bcm-vc4.txt.
 Required properties:
 - compatible:  Should be "brcm,7268-v3d" or "brcm,7278-v3d"
 - reg:         Physical base addresses and lengths of the register areas
-- reg-names:   Names for the register areas.  The "hub", "bridge", and "core0"
+- reg-names:   Names for the register areas.  The "hub" and "core0"
                  register areas are always required.  The "gca" register area
-                 is required if the GCA cache controller is present.
+                 is required if the GCA cache controller is present.  The
+                 "bridge" register area is required if an external reset
+                 controller is not present.
 - interrupts:  The interrupt numbers.  The first interrupt is for the hub,
-                 while the following interrupts are for the cores.
+                 while the following interrupts are separate interrupt lines
+                 for the cores (if they don't share the hub's interrupt).
                  See bindings/interrupt-controller/interrupts.txt
 
 Optional properties:
 - clocks:      The core clock the unit runs on
+- resets:      The reset line for v3d, if not using a mapping of the bridge
+                 See bindings/reset/reset.txt
 
 v3d {
        compatible = "brcm,7268-v3d";
index 7db1945..5e6ea1f 100644 (file)
@@ -346,6 +346,7 @@ rikomagic   Rikomagic Tech Corp. Ltd
 riscv  RISC-V Foundation
 rockchip       Fuzhou Rockchip Electronics Co., Ltd
 rohm   ROHM Semiconductor Co., Ltd
+ronbo   Ronbo Electronics
 roofull        Shenzhen Roofull Technology Co, Ltd
 samsung        Samsung Semiconductor
 samtec Samtec/Softing company
index 2da4a8f..57e3759 100644 (file)
@@ -1,3 +1,5 @@
+.. _component:
+
 ======================================
 Component Helper for Aggregate Drivers
 ======================================
index d7d6f01..99994a4 100644 (file)
@@ -256,6 +256,9 @@ DMA
   dmam_pool_create()
   dmam_pool_destroy()
 
+DRM
+  devm_drm_dev_init()
+
 GPIO
   devm_gpiod_get()
   devm_gpiod_get_index()
index 3ae23a5..966bd2d 100644 (file)
@@ -93,6 +93,11 @@ Device Instance and Driver Handling
 Driver Load
 -----------
 
+Component Helper Usage
+~~~~~~~~~~~~~~~~~~~~~~
+
+.. kernel-doc:: drivers/gpu/drm/drm_drv.c
+   :doc: component helper usage recommendations
 
 IRQ Helper Library
 ~~~~~~~~~~~~~~~~~~
index 17ca7f8..58b375e 100644 (file)
@@ -369,3 +369,15 @@ Legacy CRTC/Modeset Helper Functions Reference
 
 .. kernel-doc:: drivers/gpu/drm/drm_crtc_helper.c
    :export:
+
+SHMEM GEM Helper Reference
+==========================
+
+.. kernel-doc:: drivers/gpu/drm/drm_gem_shmem_helper.c
+   :doc: overview
+
+.. kernel-doc:: include/drm/drm_gem_shmem_helper.h
+   :internal:
+
+.. kernel-doc:: drivers/gpu/drm/drm_gem_shmem_helper.c
+   :export:
index a913644..33a4154 100644 (file)
@@ -1,34 +1,22 @@
-==========================
-drm/tinydrm Driver library
-==========================
+============================
+drm/tinydrm Tiny DRM drivers
+============================
 
-.. kernel-doc:: drivers/gpu/drm/tinydrm/core/tinydrm-core.c
-   :doc: overview
-
-Core functionality
-==================
+tinydrm is a collection of DRM drivers that are so small they can fit in a
+single source file.
 
-.. kernel-doc:: drivers/gpu/drm/tinydrm/core/tinydrm-core.c
-   :doc: core
+Helpers
+=======
 
-.. kernel-doc:: include/drm/tinydrm/tinydrm.h
+.. kernel-doc:: include/drm/tinydrm/tinydrm-helpers.h
    :internal:
 
-.. kernel-doc:: drivers/gpu/drm/tinydrm/core/tinydrm-core.c
+.. kernel-doc:: drivers/gpu/drm/tinydrm/core/tinydrm-helpers.c
    :export:
 
 .. kernel-doc:: drivers/gpu/drm/tinydrm/core/tinydrm-pipe.c
    :export:
 
-Additional helpers
-==================
-
-.. kernel-doc:: include/drm/tinydrm/tinydrm-helpers.h
-   :internal:
-
-.. kernel-doc:: drivers/gpu/drm/tinydrm/core/tinydrm-helpers.c
-   :export:
-
 MIPI DBI Compatible Controllers
 ===============================
 
index 159a4ab..1528ad2 100644 (file)
@@ -215,12 +215,12 @@ Might be good to also have some igt testcases for this.
 
 Contact: Daniel Vetter, Noralf Tronnes
 
-Put a reservation_object into drm_gem_object
+Remove the ->gem_prime_res_obj callback
 --------------------------------------------
 
-This would remove the need for the ->gem_prime_res_obj callback. It would also
-allow us to implement generic helpers for waiting for a bo, allowing for quite a
-bit of refactoring in the various wait ioctl implementations.
+The ->gem_prime_res_obj callback can be removed from drivers by using the
+reservation_object in the drm_gem_object. It may also be possible to use the
+generic drm_gem_reservation_object_wait helper for waiting for a bo.
 
 Contact: Daniel Vetter
 
@@ -469,10 +469,6 @@ those drivers as simple as possible, so lots of room for refactoring:
   one of the ideas for having a shared dsi/dbi helper, abstracting away the
   transport details more.
 
-- Quick aside: The unregister devm stuff is kinda getting the lifetimes of
-  a drm_device wrong. Doesn't matter, since everyone else gets it wrong
-  too :-)
-
 Contact: Noralf Trønnes, Daniel Vetter
 
 AMD DC Display Driver
index 3e5a5d2..dcf0b4f 100644 (file)
@@ -5045,6 +5045,13 @@ S:       Odd Fixes
 F:     drivers/gpu/drm/udl/
 T:     git git://anongit.freedesktop.org/drm/drm-misc
 
+DRM DRIVER FOR VIRTUALBOX VIRTUAL GPU
+M:     Hans de Goede <hdegoede@redhat.com>
+L:     dri-devel@lists.freedesktop.org
+S:     Maintained
+F:     drivers/gpu/drm/vboxvideo/
+T:     git git://anongit.freedesktop.org/drm/drm-misc
+
 DRM DRIVER FOR VIRTUAL KERNEL MODESETTING (VKMS)
 M:     Rodrigo Siqueira <rodrigosiqueiramelo@gmail.com>
 R:     Haneen Mohammed <hamohammed.sa@gmail.com>
index c161833..4d32e2c 100644 (file)
@@ -73,6 +73,8 @@ int reservation_object_reserve_shared(struct reservation_object *obj,
        struct reservation_object_list *old, *new;
        unsigned int i, j, k, max;
 
+       reservation_object_assert_held(obj);
+
        old = reservation_object_get_list(obj);
 
        if (old && old->shared_max) {
@@ -151,6 +153,8 @@ void reservation_object_add_shared_fence(struct reservation_object *obj,
 
        dma_fence_get(fence);
 
+       reservation_object_assert_held(obj);
+
        fobj = reservation_object_get_list(obj);
        count = fobj->shared_count;
 
@@ -196,6 +200,8 @@ void reservation_object_add_excl_fence(struct reservation_object *obj,
        struct reservation_object_list *old;
        u32 i = 0;
 
+       reservation_object_assert_held(obj);
+
        old = reservation_object_get_list(obj);
        if (old)
                i = old->shared_count;
@@ -236,6 +242,8 @@ int reservation_object_copy_fences(struct reservation_object *dst,
        size_t size;
        unsigned i;
 
+       reservation_object_assert_held(dst);
+
        rcu_read_lock();
        src_list = rcu_dereference(src->fence);
 
index bd943a7..5e1bc63 100644 (file)
@@ -173,6 +173,12 @@ config DRM_KMS_CMA_HELPER
        help
          Choose this if you need the KMS CMA helper functions
 
+config DRM_GEM_SHMEM_HELPER
+       bool
+       depends on DRM
+       help
+         Choose this if you need the GEM shmem helper functions
+
 config DRM_VM
        bool
        depends on DRM && MMU
@@ -329,6 +335,8 @@ source "drivers/gpu/drm/tve200/Kconfig"
 
 source "drivers/gpu/drm/xen/Kconfig"
 
+source "drivers/gpu/drm/vboxvideo/Kconfig"
+
 # Keep legacy drivers last
 
 menuconfig DRM_LEGACY
index 1ac55c6..e630ecc 100644 (file)
@@ -25,6 +25,7 @@ drm-$(CONFIG_DRM_LIB_RANDOM) += lib/drm_random.o
 drm-$(CONFIG_DRM_VM) += drm_vm.o
 drm-$(CONFIG_COMPAT) += drm_ioc32.o
 drm-$(CONFIG_DRM_GEM_CMA_HELPER) += drm_gem_cma_helper.o
+drm-$(CONFIG_DRM_GEM_SHMEM_HELPER) += drm_gem_shmem_helper.o
 drm-$(CONFIG_PCI) += ati_pcigart.o
 drm-$(CONFIG_DRM_PANEL) += drm_panel.o
 drm-$(CONFIG_OF) += drm_of.o
@@ -109,3 +110,4 @@ obj-$(CONFIG_DRM_TINYDRM) += tinydrm/
 obj-$(CONFIG_DRM_PL111) += pl111/
 obj-$(CONFIG_DRM_TVE200) += tve200/
 obj-$(CONFIG_DRM_XEN) += xen/
+obj-$(CONFIG_DRM_VBOXVIDEO) += vboxvideo/
index 7419ea8..8a07320 100644 (file)
@@ -974,6 +974,7 @@ amdgpu_pci_remove(struct pci_dev *pdev)
 
        DRM_ERROR("Device removal is currently not supported outside of fbcon\n");
        drm_dev_unplug(dev);
+       drm_dev_put(dev);
        pci_disable_device(pdev);
        pci_set_drvdata(pdev, NULL);
 }
index 9fc3296..98fd920 100644 (file)
@@ -886,7 +886,7 @@ static int gmc_v6_0_sw_init(void *handle)
                pci_set_consistent_dma_mask(adev->pdev, DMA_BIT_MASK(32));
                dev_warn(adev->dev, "amdgpu: No coherent DMA available.\n");
        }
-       adev->need_swiotlb = drm_get_max_iomem() > ((u64)1 << dma_bits);
+       adev->need_swiotlb = drm_need_swiotlb(dma_bits);
 
        r = gmc_v6_0_init_microcode(adev);
        if (r) {
index 761dcfb..3e9c503 100644 (file)
@@ -1030,7 +1030,7 @@ static int gmc_v7_0_sw_init(void *handle)
                pci_set_consistent_dma_mask(adev->pdev, DMA_BIT_MASK(32));
                pr_warn("amdgpu: No coherent DMA available\n");
        }
-       adev->need_swiotlb = drm_get_max_iomem() > ((u64)1 << dma_bits);
+       adev->need_swiotlb = drm_need_swiotlb(dma_bits);
 
        r = gmc_v7_0_init_microcode(adev);
        if (r) {
index 3444067..29dde64 100644 (file)
@@ -1155,7 +1155,7 @@ static int gmc_v8_0_sw_init(void *handle)
                pci_set_consistent_dma_mask(adev->pdev, DMA_BIT_MASK(32));
                pr_warn("amdgpu: No coherent DMA available\n");
        }
-       adev->need_swiotlb = drm_get_max_iomem() > ((u64)1 << dma_bits);
+       adev->need_swiotlb = drm_need_swiotlb(dma_bits);
 
        r = gmc_v8_0_init_microcode(adev);
        if (r) {
index 2fe8397..5332749 100644 (file)
@@ -1011,7 +1011,7 @@ static int gmc_v9_0_sw_init(void *handle)
                pci_set_consistent_dma_mask(adev->pdev, DMA_BIT_MASK(32));
                printk(KERN_WARNING "amdgpu: No coherent DMA available.\n");
        }
-       adev->need_swiotlb = drm_get_max_iomem() > ((u64)1 << dma_bits);
+       adev->need_swiotlb = drm_need_swiotlb(dma_bits);
 
        if (adev->gmc.xgmi.supported) {
                r = gfxhub_v1_1_get_xgmi_info(adev);
index ab50ad0..21725c9 100644 (file)
@@ -264,37 +264,17 @@ static bool
 malidp_verify_afbc_framebuffer_caps(struct drm_device *dev,
                                    const struct drm_mode_fb_cmd2 *mode_cmd)
 {
-       const struct drm_format_info *info;
-
-       if ((mode_cmd->modifier[0] >> 56) != DRM_FORMAT_MOD_VENDOR_ARM) {
-               DRM_DEBUG_KMS("Unknown modifier (not Arm)\n");
-               return false;
-       }
-
-       if (mode_cmd->modifier[0] &
-           ~DRM_FORMAT_MOD_ARM_AFBC(AFBC_MOD_VALID_BITS)) {
-               DRM_DEBUG_KMS("Unsupported modifiers\n");
-               return false;
-       }
-
-       info = drm_get_format_info(dev, mode_cmd);
-       if (!info) {
-               DRM_DEBUG_KMS("Unable to get the format information\n");
+       if (malidp_format_mod_supported(dev, mode_cmd->pixel_format,
+                                       mode_cmd->modifier[0]) == false)
                return false;
-       }
-
-       if (info->num_planes != 1) {
-               DRM_DEBUG_KMS("AFBC buffers expect one plane\n");
-               return false;
-       }
 
        if (mode_cmd->offsets[0] != 0) {
                DRM_DEBUG_KMS("AFBC buffers' plane offset should be 0\n");
                return false;
        }
 
-       switch (mode_cmd->modifier[0] & AFBC_FORMAT_MOD_BLOCK_SIZE_MASK) {
-       case AFBC_FORMAT_MOD_BLOCK_SIZE_16x16:
+       switch (mode_cmd->modifier[0] & AFBC_SIZE_MASK) {
+       case AFBC_SIZE_16X16:
                if ((mode_cmd->width % 16) || (mode_cmd->height % 16)) {
                        DRM_DEBUG_KMS("AFBC buffers must be aligned to 16 pixels\n");
                        return false;
@@ -318,9 +298,10 @@ malidp_verify_afbc_framebuffer_size(struct drm_device *dev,
        struct drm_gem_object *objs = NULL;
        u32 afbc_superblock_size = 0, afbc_superblock_height = 0;
        u32 afbc_superblock_width = 0, afbc_size = 0;
+       int bpp = 0;
 
-       switch (mode_cmd->modifier[0] & AFBC_FORMAT_MOD_BLOCK_SIZE_MASK) {
-       case AFBC_FORMAT_MOD_BLOCK_SIZE_16x16:
+       switch (mode_cmd->modifier[0] & AFBC_SIZE_MASK) {
+       case AFBC_SIZE_16X16:
                afbc_superblock_height = 16;
                afbc_superblock_width = 16;
                break;
@@ -334,15 +315,19 @@ malidp_verify_afbc_framebuffer_size(struct drm_device *dev,
        n_superblocks = (mode_cmd->width / afbc_superblock_width) *
                (mode_cmd->height / afbc_superblock_height);
 
-       afbc_superblock_size = info->cpp[0] * afbc_superblock_width *
-               afbc_superblock_height;
+       bpp = malidp_format_get_bpp(info->format);
+
+       afbc_superblock_size = (bpp * afbc_superblock_width * afbc_superblock_height)
+                               / BITS_PER_BYTE;
 
        afbc_size = ALIGN(n_superblocks * AFBC_HEADER_SIZE, AFBC_SUPERBLK_ALIGNMENT);
        afbc_size += n_superblocks * ALIGN(afbc_superblock_size, AFBC_SUPERBLK_ALIGNMENT);
 
-       if (mode_cmd->width * info->cpp[0] != mode_cmd->pitches[0]) {
-               DRM_DEBUG_KMS("Invalid value of pitch (=%u) should be same as width (=%u) * cpp (=%u)\n",
-                             mode_cmd->pitches[0], mode_cmd->width, info->cpp[0]);
+       if ((mode_cmd->width * bpp) != (mode_cmd->pitches[0] * BITS_PER_BYTE)) {
+               DRM_DEBUG_KMS("Invalid value of (pitch * BITS_PER_BYTE) (=%u) "
+                             "should be same as width (=%u) * bpp (=%u)\n",
+                             (mode_cmd->pitches[0] * BITS_PER_BYTE),
+                             mode_cmd->width, bpp);
                return false;
        }
 
@@ -406,6 +391,7 @@ static int malidp_init(struct drm_device *drm)
        drm->mode_config.max_height = hwdev->max_line_size;
        drm->mode_config.funcs = &malidp_mode_config_funcs;
        drm->mode_config.helper_private = &malidp_mode_config_helpers;
+       drm->mode_config.allow_fb_modifiers = true;
 
        ret = malidp_crtc_init(drm);
        if (ret)
index b76c86f..019a682 100644 (file)
@@ -90,6 +90,12 @@ struct malidp_crtc_state {
 int malidp_de_planes_init(struct drm_device *drm);
 int malidp_crtc_init(struct drm_device *drm);
 
+bool malidp_hw_format_is_linear_only(u32 format);
+bool malidp_hw_format_is_afbc_only(u32 format);
+
+bool malidp_format_mod_supported(struct drm_device *drm,
+                                u32 format, u64 modifier);
+
 #ifdef CONFIG_DEBUG_FS
 void malidp_error(struct malidp_drm *malidp,
                  struct malidp_error_stats *error_stats, u32 status,
index b9bed11..8df12e9 100644 (file)
@@ -49,11 +49,19 @@ static const struct malidp_format_id malidp500_de_formats[] = {
        { DRM_FORMAT_YUYV, DE_VIDEO1, 13 },
        { DRM_FORMAT_NV12, DE_VIDEO1 | SE_MEMWRITE, 14 },
        { DRM_FORMAT_YUV420, DE_VIDEO1, 15 },
+       { DRM_FORMAT_XYUV8888, DE_VIDEO1, 16 },
+       /* These are supported with AFBC only */
+       { DRM_FORMAT_YUV420_8BIT, DE_VIDEO1, 14 },
+       { DRM_FORMAT_VUY888, DE_VIDEO1, 16 },
+       { DRM_FORMAT_VUY101010, DE_VIDEO1, 17 },
+       { DRM_FORMAT_YUV420_10BIT, DE_VIDEO1, 18 }
 };
 
 #define MALIDP_ID(__group, __format) \
        ((((__group) & 0x7) << 3) | ((__format) & 0x7))
 
+#define AFBC_YUV_422_FORMAT_ID MALIDP_ID(5, 1)
+
 #define MALIDP_COMMON_FORMATS \
        /*    fourcc,   layers supporting the format,      internal id   */ \
        { DRM_FORMAT_ARGB2101010, DE_VIDEO1 | DE_GRAPHICS1 | DE_VIDEO2 | SE_MEMWRITE, MALIDP_ID(0, 0) }, \
@@ -74,11 +82,25 @@ static const struct malidp_format_id malidp500_de_formats[] = {
        { DRM_FORMAT_ABGR1555, DE_VIDEO1 | DE_GRAPHICS1 | DE_VIDEO2, MALIDP_ID(4, 1) }, \
        { DRM_FORMAT_RGB565, DE_VIDEO1 | DE_GRAPHICS1 | DE_VIDEO2, MALIDP_ID(4, 2) }, \
        { DRM_FORMAT_BGR565, DE_VIDEO1 | DE_GRAPHICS1 | DE_VIDEO2, MALIDP_ID(4, 3) }, \
+       /* This is only supported with linear modifier */       \
+       { DRM_FORMAT_XYUV8888, DE_VIDEO1 | DE_VIDEO2, MALIDP_ID(5, 0) },\
+       /* This is only supported with AFBC modifier */         \
+       { DRM_FORMAT_VUY888, DE_VIDEO1 | DE_VIDEO2, MALIDP_ID(5, 0) }, \
        { DRM_FORMAT_YUYV, DE_VIDEO1 | DE_VIDEO2, MALIDP_ID(5, 2) },    \
+       /* This is only supported with linear modifier */ \
        { DRM_FORMAT_UYVY, DE_VIDEO1 | DE_VIDEO2, MALIDP_ID(5, 3) },    \
        { DRM_FORMAT_NV12, DE_VIDEO1 | DE_VIDEO2 | SE_MEMWRITE, MALIDP_ID(5, 6) },      \
+       /* This is only supported with AFBC modifier */ \
+       { DRM_FORMAT_YUV420_8BIT, DE_VIDEO1 | DE_VIDEO2, MALIDP_ID(5, 6) }, \
        { DRM_FORMAT_YUV420, DE_VIDEO1 | DE_VIDEO2, MALIDP_ID(5, 7) }, \
-       { DRM_FORMAT_X0L2, DE_VIDEO1 | DE_VIDEO2, MALIDP_ID(6, 6)}
+       /* This is only supported with linear modifier */ \
+       { DRM_FORMAT_XVYU2101010, DE_VIDEO1 | DE_VIDEO2, MALIDP_ID(6, 0)}, \
+       /* This is only supported with AFBC modifier */ \
+       { DRM_FORMAT_VUY101010, DE_VIDEO1 | DE_VIDEO2, MALIDP_ID(6, 0)}, \
+       { DRM_FORMAT_X0L2, DE_VIDEO1 | DE_VIDEO2, MALIDP_ID(6, 6)}, \
+       /* This is only supported with AFBC modifier */ \
+       { DRM_FORMAT_YUV420_10BIT, DE_VIDEO1 | DE_VIDEO2, MALIDP_ID(6, 7)}, \
+       { DRM_FORMAT_P010, DE_VIDEO1 | DE_VIDEO2, MALIDP_ID(6, 7)}
 
 static const struct malidp_format_id malidp550_de_formats[] = {
        MALIDP_COMMON_FORMATS,
@@ -94,11 +116,14 @@ static const struct malidp_layer malidp500_layers[] = {
         *      yuv2rgb matrix offset, mmu control register offset, rotation_features
         */
        { DE_VIDEO1, MALIDP500_DE_LV_BASE, MALIDP500_DE_LV_PTR_BASE,
-               MALIDP_DE_LV_STRIDE0, MALIDP500_LV_YUV2RGB, 0, ROTATE_ANY },
+               MALIDP_DE_LV_STRIDE0, MALIDP500_LV_YUV2RGB, 0, ROTATE_ANY,
+               MALIDP500_DE_LV_AD_CTRL },
        { DE_GRAPHICS1, MALIDP500_DE_LG1_BASE, MALIDP500_DE_LG1_PTR_BASE,
-               MALIDP_DE_LG_STRIDE, 0, 0, ROTATE_ANY },
+               MALIDP_DE_LG_STRIDE, 0, 0, ROTATE_ANY,
+               MALIDP500_DE_LG1_AD_CTRL },
        { DE_GRAPHICS2, MALIDP500_DE_LG2_BASE, MALIDP500_DE_LG2_PTR_BASE,
-               MALIDP_DE_LG_STRIDE, 0, 0, ROTATE_ANY },
+               MALIDP_DE_LG_STRIDE, 0, 0, ROTATE_ANY,
+               MALIDP500_DE_LG2_AD_CTRL },
 };
 
 static const struct malidp_layer malidp550_layers[] = {
@@ -106,13 +131,16 @@ static const struct malidp_layer malidp550_layers[] = {
         *      yuv2rgb matrix offset, mmu control register offset, rotation_features
         */
        { DE_VIDEO1, MALIDP550_DE_LV1_BASE, MALIDP550_DE_LV1_PTR_BASE,
-               MALIDP_DE_LV_STRIDE0, MALIDP550_LV_YUV2RGB, 0, ROTATE_ANY },
+               MALIDP_DE_LV_STRIDE0, MALIDP550_LV_YUV2RGB, 0, ROTATE_ANY,
+               MALIDP550_DE_LV1_AD_CTRL },
        { DE_GRAPHICS1, MALIDP550_DE_LG_BASE, MALIDP550_DE_LG_PTR_BASE,
-               MALIDP_DE_LG_STRIDE, 0, 0, ROTATE_ANY },
+               MALIDP_DE_LG_STRIDE, 0, 0, ROTATE_ANY,
+               MALIDP550_DE_LG_AD_CTRL },
        { DE_VIDEO2, MALIDP550_DE_LV2_BASE, MALIDP550_DE_LV2_PTR_BASE,
-               MALIDP_DE_LV_STRIDE0, MALIDP550_LV_YUV2RGB, 0, ROTATE_ANY },
+               MALIDP_DE_LV_STRIDE0, MALIDP550_LV_YUV2RGB, 0, ROTATE_ANY,
+               MALIDP550_DE_LV2_AD_CTRL },
        { DE_SMART, MALIDP550_DE_LS_BASE, MALIDP550_DE_LS_PTR_BASE,
-               MALIDP550_DE_LS_R1_STRIDE, 0, 0, ROTATE_NONE },
+               MALIDP550_DE_LS_R1_STRIDE, 0, 0, ROTATE_NONE, 0 },
 };
 
 static const struct malidp_layer malidp650_layers[] = {
@@ -122,16 +150,44 @@ static const struct malidp_layer malidp650_layers[] = {
         */
        { DE_VIDEO1, MALIDP550_DE_LV1_BASE, MALIDP550_DE_LV1_PTR_BASE,
                MALIDP_DE_LV_STRIDE0, MALIDP550_LV_YUV2RGB,
-               MALIDP650_DE_LV_MMU_CTRL, ROTATE_ANY },
+               MALIDP650_DE_LV_MMU_CTRL, ROTATE_ANY,
+               MALIDP550_DE_LV1_AD_CTRL },
        { DE_GRAPHICS1, MALIDP550_DE_LG_BASE, MALIDP550_DE_LG_PTR_BASE,
                MALIDP_DE_LG_STRIDE, 0, MALIDP650_DE_LG_MMU_CTRL,
-               ROTATE_COMPRESSED },
+               ROTATE_COMPRESSED, MALIDP550_DE_LG_AD_CTRL },
        { DE_VIDEO2, MALIDP550_DE_LV2_BASE, MALIDP550_DE_LV2_PTR_BASE,
                MALIDP_DE_LV_STRIDE0, MALIDP550_LV_YUV2RGB,
-               MALIDP650_DE_LV_MMU_CTRL, ROTATE_ANY },
+               MALIDP650_DE_LV_MMU_CTRL, ROTATE_ANY,
+               MALIDP550_DE_LV2_AD_CTRL },
        { DE_SMART, MALIDP550_DE_LS_BASE, MALIDP550_DE_LS_PTR_BASE,
                MALIDP550_DE_LS_R1_STRIDE, 0, MALIDP650_DE_LS_MMU_CTRL,
-               ROTATE_NONE },
+               ROTATE_NONE, 0 },
+};
+
+const u64 malidp_format_modifiers[] = {
+       /* All RGB formats (except XRGB, RGBX, XBGR, BGRX) */
+       DRM_FORMAT_MOD_ARM_AFBC(AFBC_SIZE_16X16 | AFBC_YTR | AFBC_SPARSE),
+       DRM_FORMAT_MOD_ARM_AFBC(AFBC_SIZE_16X16 | AFBC_YTR),
+
+       /* All RGB formats > 16bpp (except XRGB, RGBX, XBGR, BGRX) */
+       DRM_FORMAT_MOD_ARM_AFBC(AFBC_SIZE_16X16 | AFBC_YTR | AFBC_SPARSE | AFBC_SPLIT),
+
+       /* All 8 or 10 bit YUV 444 formats. */
+       /* In DP550, 10 bit YUV 420 format also supported */
+       DRM_FORMAT_MOD_ARM_AFBC(AFBC_SIZE_16X16 | AFBC_SPARSE | AFBC_SPLIT),
+
+       /* YUV 420, 422 P1 8 bit and YUV 444 8 bit/10 bit formats */
+       DRM_FORMAT_MOD_ARM_AFBC(AFBC_SIZE_16X16 | AFBC_SPARSE),
+       DRM_FORMAT_MOD_ARM_AFBC(AFBC_SIZE_16X16),
+
+       /* YUV 420, 422 P1 8, 10 bit formats */
+       DRM_FORMAT_MOD_ARM_AFBC(AFBC_SIZE_16X16 | AFBC_CBR | AFBC_SPARSE),
+       DRM_FORMAT_MOD_ARM_AFBC(AFBC_SIZE_16X16 | AFBC_CBR),
+
+       /* All formats */
+       DRM_FORMAT_MOD_LINEAR,
+
+       DRM_FORMAT_MOD_INVALID
 };
 
 #define SE_N_SCALING_COEFFS    96
@@ -324,14 +380,39 @@ static void malidp500_modeset(struct malidp_hw_device *hwdev, struct videomode *
                malidp_hw_clearbits(hwdev, MALIDP_DISP_FUNC_ILACED, MALIDP_DE_DISPLAY_FUNC);
 }
 
-static int malidp500_rotmem_required(struct malidp_hw_device *hwdev, u16 w, u16 h, u32 fmt)
+int malidp_format_get_bpp(u32 fmt)
+{
+       int bpp = drm_format_plane_cpp(fmt, 0) * 8;
+
+       if (bpp == 0) {
+               switch (fmt) {
+               case DRM_FORMAT_VUY101010:
+                       bpp = 30;
+               case DRM_FORMAT_YUV420_10BIT:
+                       bpp = 15;
+                       break;
+               case DRM_FORMAT_YUV420_8BIT:
+                       bpp = 12;
+                       break;
+               default:
+                       bpp = 0;
+               }
+       }
+
+       return bpp;
+}
+
+static int malidp500_rotmem_required(struct malidp_hw_device *hwdev, u16 w,
+                                    u16 h, u32 fmt, bool has_modifier)
 {
        /*
         * Each layer needs enough rotation memory to fit 8 lines
         * worth of pixel data. Required size is then:
         *    size = rotated_width * (bpp / 8) * 8;
         */
-       return w * drm_format_plane_cpp(fmt, 0) * 8;
+       int bpp = malidp_format_get_bpp(fmt);
+
+       return w * bpp;
 }
 
 static void malidp500_se_write_pp_coefftab(struct malidp_hw_device *hwdev,
@@ -609,9 +690,9 @@ static void malidp550_modeset(struct malidp_hw_device *hwdev, struct videomode *
                malidp_hw_clearbits(hwdev, MALIDP_DISP_FUNC_ILACED, MALIDP_DE_DISPLAY_FUNC);
 }
 
-static int malidp550_rotmem_required(struct malidp_hw_device *hwdev, u16 w, u16 h, u32 fmt)
+static int malidpx50_get_bytes_per_column(u32 fmt)
 {
-       u32 bytes_per_col;
+       u32 bytes_per_column;
 
        switch (fmt) {
        /* 8 lines at 4 bytes per pixel */
@@ -637,19 +718,77 @@ static int malidp550_rotmem_required(struct malidp_hw_device *hwdev, u16 w, u16
        case DRM_FORMAT_UYVY:
        case DRM_FORMAT_YUYV:
        case DRM_FORMAT_X0L0:
-       case DRM_FORMAT_X0L2:
-               bytes_per_col = 32;
+               bytes_per_column = 32;
                break;
        /* 16 lines at 1.5 bytes per pixel */
        case DRM_FORMAT_NV12:
        case DRM_FORMAT_YUV420:
-               bytes_per_col = 24;
+       /* 8 lines at 3 bytes per pixel */
+       case DRM_FORMAT_VUY888:
+       /* 16 lines at 12 bits per pixel */
+       case DRM_FORMAT_YUV420_8BIT:
+       /* 8 lines at 3 bytes per pixel */
+       case DRM_FORMAT_P010:
+               bytes_per_column = 24;
+               break;
+       /* 8 lines at 30 bits per pixel */
+       case DRM_FORMAT_VUY101010:
+       /* 16 lines at 15 bits per pixel */
+       case DRM_FORMAT_YUV420_10BIT:
+               bytes_per_column = 30;
                break;
        default:
                return -EINVAL;
        }
 
-       return w * bytes_per_col;
+       return bytes_per_column;
+}
+
+static int malidp550_rotmem_required(struct malidp_hw_device *hwdev, u16 w,
+                                    u16 h, u32 fmt, bool has_modifier)
+{
+       int bytes_per_column = 0;
+
+       switch (fmt) {
+       /* 8 lines at 15 bits per pixel */
+       case DRM_FORMAT_YUV420_10BIT:
+               bytes_per_column = 15;
+               break;
+       /* Uncompressed YUV 420 10 bit single plane cannot be rotated */
+       case DRM_FORMAT_X0L2:
+               if (has_modifier)
+                       bytes_per_column = 8;
+               else
+                       return -EINVAL;
+               break;
+       default:
+               bytes_per_column = malidpx50_get_bytes_per_column(fmt);
+       }
+
+       if (bytes_per_column == -EINVAL)
+               return bytes_per_column;
+
+       return w * bytes_per_column;
+}
+
+static int malidp650_rotmem_required(struct malidp_hw_device *hwdev, u16 w,
+                                    u16 h, u32 fmt, bool has_modifier)
+{
+       int bytes_per_column = 0;
+
+       switch (fmt) {
+       /* 16 lines at 2 bytes per pixel */
+       case DRM_FORMAT_X0L2:
+               bytes_per_column = 32;
+               break;
+       default:
+               bytes_per_column = malidpx50_get_bytes_per_column(fmt);
+       }
+
+       if (bytes_per_column == -EINVAL)
+               return bytes_per_column;
+
+       return w * bytes_per_column;
 }
 
 static int malidp550_se_set_scaling_coeffs(struct malidp_hw_device *hwdev,
@@ -838,7 +977,10 @@ const struct malidp_hw malidp_device[MALIDP_MAX_DEVICES] = {
                        .se_base = MALIDP550_SE_BASE,
                        .dc_base = MALIDP550_DC_BASE,
                        .out_depth_base = MALIDP550_DE_OUTPUT_DEPTH,
-                       .features = MALIDP_REGMAP_HAS_CLEARIRQ,
+                       .features = MALIDP_REGMAP_HAS_CLEARIRQ |
+                                   MALIDP_DEVICE_AFBC_SUPPORT_SPLIT |
+                                   MALIDP_DEVICE_AFBC_YUV_420_10_SUPPORT_SPLIT |
+                                   MALIDP_DEVICE_AFBC_YUYV_USE_422_P2,
                        .n_layers = ARRAY_SIZE(malidp550_layers),
                        .layers = malidp550_layers,
                        .de_irq_map = {
@@ -884,7 +1026,9 @@ const struct malidp_hw malidp_device[MALIDP_MAX_DEVICES] = {
                        .se_base = MALIDP550_SE_BASE,
                        .dc_base = MALIDP550_DC_BASE,
                        .out_depth_base = MALIDP550_DE_OUTPUT_DEPTH,
-                       .features = MALIDP_REGMAP_HAS_CLEARIRQ,
+                       .features = MALIDP_REGMAP_HAS_CLEARIRQ |
+                                   MALIDP_DEVICE_AFBC_SUPPORT_SPLIT |
+                                   MALIDP_DEVICE_AFBC_YUYV_USE_422_P2,
                        .n_layers = ARRAY_SIZE(malidp650_layers),
                        .layers = malidp650_layers,
                        .de_irq_map = {
@@ -923,7 +1067,7 @@ const struct malidp_hw malidp_device[MALIDP_MAX_DEVICES] = {
                .in_config_mode = malidp550_in_config_mode,
                .set_config_valid = malidp550_set_config_valid,
                .modeset = malidp550_modeset,
-               .rotmem_required = malidp550_rotmem_required,
+               .rotmem_required = malidp650_rotmem_required,
                .se_set_scaling_coeffs = malidp550_se_set_scaling_coeffs,
                .se_calc_mclk = malidp550_se_calc_mclk,
                .enable_memwrite = malidp550_enable_memwrite,
@@ -933,19 +1077,72 @@ const struct malidp_hw malidp_device[MALIDP_MAX_DEVICES] = {
 };
 
 u8 malidp_hw_get_format_id(const struct malidp_hw_regmap *map,
-                          u8 layer_id, u32 format)
+                          u8 layer_id, u32 format, bool has_modifier)
 {
        unsigned int i;
 
        for (i = 0; i < map->n_pixel_formats; i++) {
                if (((map->pixel_formats[i].layer & layer_id) == layer_id) &&
-                   (map->pixel_formats[i].format == format))
-                       return map->pixel_formats[i].id;
+                   (map->pixel_formats[i].format == format)) {
+                       /*
+                        * In some DP550 and DP650, DRM_FORMAT_YUYV + AFBC modifier
+                        * is supported by a different h/w format id than
+                        * DRM_FORMAT_YUYV (only).
+                        */
+                       if (format == DRM_FORMAT_YUYV &&
+                           (has_modifier) &&
+                           (map->features & MALIDP_DEVICE_AFBC_YUYV_USE_422_P2))
+                               return AFBC_YUV_422_FORMAT_ID;
+                       else
+                               return map->pixel_formats[i].id;
+               }
        }
 
        return MALIDP_INVALID_FORMAT_ID;
 }
 
+bool malidp_hw_format_is_linear_only(u32 format)
+{
+       switch (format) {
+       case DRM_FORMAT_ARGB2101010:
+       case DRM_FORMAT_RGBA1010102:
+       case DRM_FORMAT_BGRA1010102:
+       case DRM_FORMAT_ARGB8888:
+       case DRM_FORMAT_RGBA8888:
+       case DRM_FORMAT_BGRA8888:
+       case DRM_FORMAT_XBGR8888:
+       case DRM_FORMAT_XRGB8888:
+       case DRM_FORMAT_RGBX8888:
+       case DRM_FORMAT_BGRX8888:
+       case DRM_FORMAT_RGB888:
+       case DRM_FORMAT_RGB565:
+       case DRM_FORMAT_ARGB1555:
+       case DRM_FORMAT_RGBA5551:
+       case DRM_FORMAT_BGRA5551:
+       case DRM_FORMAT_UYVY:
+       case DRM_FORMAT_XYUV8888:
+       case DRM_FORMAT_XVYU2101010:
+       case DRM_FORMAT_X0L2:
+       case DRM_FORMAT_X0L0:
+               return true;
+       default:
+               return false;
+       }
+}
+
+bool malidp_hw_format_is_afbc_only(u32 format)
+{
+       switch (format) {
+       case DRM_FORMAT_VUY888:
+       case DRM_FORMAT_VUY101010:
+       case DRM_FORMAT_YUV420_8BIT:
+       case DRM_FORMAT_YUV420_10BIT:
+               return true;
+       default:
+               return false;
+       }
+}
+
 static void malidp_hw_clear_irq(struct malidp_hw_device *hwdev, u8 block, u32 irq)
 {
        u32 base = malidp_get_block_base(hwdev, block);
index 40155e2..207c3ce 100644 (file)
@@ -70,6 +70,8 @@ struct malidp_layer {
        s16 yuv2rgb_offset;     /* offset to the YUV->RGB matrix entries */
        u16 mmu_ctrl_offset;    /* offset to the MMU control register */
        enum rotation_features rot;     /* type of rotation supported */
+       /* address offset for the AFBC decoder registers */
+       u16 afbc_decoder_offset;
 };
 
 enum malidp_scaling_coeff_set {
@@ -93,7 +95,10 @@ struct malidp_se_config {
 };
 
 /* regmap features */
-#define MALIDP_REGMAP_HAS_CLEARIRQ     (1 << 0)
+#define MALIDP_REGMAP_HAS_CLEARIRQ                             BIT(0)
+#define MALIDP_DEVICE_AFBC_SUPPORT_SPLIT                       BIT(1)
+#define MALIDP_DEVICE_AFBC_YUV_420_10_SUPPORT_SPLIT            BIT(2)
+#define MALIDP_DEVICE_AFBC_YUYV_USE_422_P2                     BIT(3)
 
 struct malidp_hw_regmap {
        /* address offset of the DE register bank */
@@ -179,7 +184,8 @@ struct malidp_hw {
         * Calculate the required rotation memory given the active area
         * and the buffer format.
         */
-       int (*rotmem_required)(struct malidp_hw_device *hwdev, u16 w, u16 h, u32 fmt);
+       int (*rotmem_required)(struct malidp_hw_device *hwdev, u16 w, u16 h,
+                              u32 fmt, bool has_modifier);
 
        int (*se_set_scaling_coeffs)(struct malidp_hw_device *hwdev,
                                     struct malidp_se_config *se_config,
@@ -319,7 +325,9 @@ int malidp_se_irq_init(struct drm_device *drm, int irq);
 void malidp_se_irq_fini(struct malidp_hw_device *hwdev);
 
 u8 malidp_hw_get_format_id(const struct malidp_hw_regmap *map,
-                          u8 layer_id, u32 format);
+                          u8 layer_id, u32 format, bool has_modifier);
+
+int malidp_format_get_bpp(u32 fmt);
 
 static inline u8 malidp_hw_get_pitch_align(struct malidp_hw_device *hwdev, bool rotated)
 {
@@ -388,9 +396,18 @@ static inline void malidp_se_set_enh_coeffs(struct malidp_hw_device *hwdev)
 
 #define MALIDP_GAMMA_LUT_SIZE          4096
 
-#define AFBC_MOD_VALID_BITS (AFBC_FORMAT_MOD_BLOCK_SIZE_MASK | \
-                       AFBC_FORMAT_MOD_YTR | AFBC_FORMAT_MOD_SPLIT | \
-                       AFBC_FORMAT_MOD_SPARSE | AFBC_FORMAT_MOD_CBR | \
-                       AFBC_FORMAT_MOD_TILED | AFBC_FORMAT_MOD_SC)
+#define AFBC_SIZE_MASK         AFBC_FORMAT_MOD_BLOCK_SIZE_MASK
+#define AFBC_SIZE_16X16                AFBC_FORMAT_MOD_BLOCK_SIZE_16x16
+#define AFBC_YTR               AFBC_FORMAT_MOD_YTR
+#define AFBC_SPARSE            AFBC_FORMAT_MOD_SPARSE
+#define AFBC_CBR               AFBC_FORMAT_MOD_CBR
+#define AFBC_SPLIT             AFBC_FORMAT_MOD_SPLIT
+#define AFBC_TILED             AFBC_FORMAT_MOD_TILED
+#define AFBC_SC                        AFBC_FORMAT_MOD_SC
+
+#define AFBC_MOD_VALID_BITS    (AFBC_SIZE_MASK | AFBC_YTR | AFBC_SPLIT | \
+                                AFBC_SPARSE | AFBC_CBR | AFBC_TILED | AFBC_SC)
+
+extern const u64 malidp_format_modifiers[];
 
 #endif  /* __MALIDP_HW_H__ */
index 8762721..5f102bd 100644 (file)
@@ -141,9 +141,14 @@ malidp_mw_encoder_atomic_check(struct drm_encoder *encoder,
                return -EINVAL;
        }
 
+       if (fb->modifier) {
+               DRM_DEBUG_KMS("Writeback framebuffer does not support modifiers\n");
+               return -EINVAL;
+       }
+
        mw_state->format =
                malidp_hw_get_format_id(&malidp->dev->hw->map, SE_MEMWRITE,
-                                       fb->format->format);
+                                       fb->format->format, !!fb->modifier);
        if (mw_state->format == MALIDP_INVALID_FORMAT_ID) {
                struct drm_format_name_buf format_name;
 
index c9a6d3e..d42e0ea 100644 (file)
@@ -52,6 +52,8 @@
 #define MALIDP550_LS_ENABLE            0x01c
 #define MALIDP550_LS_R1_IN_SIZE                0x020
 
+#define MODIFIERS_COUNT_MAX            15
+
 /*
  * This 4-entry look-up-table is used to determine the full 8-bit alpha value
  * for formats with 1- or 2-bit alpha channels.
@@ -145,6 +147,119 @@ static void malidp_plane_atomic_print_state(struct drm_printer *p,
        drm_printf(p, "\tmmu_prefetch_pgsize=%d\n", ms->mmu_prefetch_pgsize);
 }
 
+bool malidp_format_mod_supported(struct drm_device *drm,
+                                u32 format, u64 modifier)
+{
+       const struct drm_format_info *info;
+       const u64 *modifiers;
+       struct malidp_drm *malidp = drm->dev_private;
+       const struct malidp_hw_regmap *map = &malidp->dev->hw->map;
+
+       if (WARN_ON(modifier == DRM_FORMAT_MOD_INVALID))
+               return false;
+
+       /* Some pixel formats are supported without any modifier */
+       if (modifier == DRM_FORMAT_MOD_LINEAR) {
+               /*
+                * However these pixel formats need to be supported with
+                * modifiers only
+                */
+               return !malidp_hw_format_is_afbc_only(format);
+       }
+
+       if ((modifier >> 56) != DRM_FORMAT_MOD_VENDOR_ARM) {
+               DRM_ERROR("Unknown modifier (not Arm)\n");
+               return false;
+       }
+
+       if (modifier &
+           ~DRM_FORMAT_MOD_ARM_AFBC(AFBC_MOD_VALID_BITS)) {
+               DRM_DEBUG_KMS("Unsupported modifiers\n");
+               return false;
+       }
+
+       modifiers = malidp_format_modifiers;
+
+       /* SPLIT buffers must use SPARSE layout */
+       if (WARN_ON_ONCE((modifier & AFBC_SPLIT) && !(modifier & AFBC_SPARSE)))
+               return false;
+
+       /* CBR only applies to YUV formats, where YTR should be always 0 */
+       if (WARN_ON_ONCE((modifier & AFBC_CBR) && (modifier & AFBC_YTR)))
+               return false;
+
+       while (*modifiers != DRM_FORMAT_MOD_INVALID) {
+               if (*modifiers == modifier)
+                       break;
+
+               modifiers++;
+       }
+
+       /* return false, if the modifier was not found */
+       if (*modifiers == DRM_FORMAT_MOD_INVALID) {
+               DRM_DEBUG_KMS("Unsupported modifier\n");
+               return false;
+       }
+
+       info = drm_format_info(format);
+
+       if (info->num_planes != 1) {
+               DRM_DEBUG_KMS("AFBC buffers expect one plane\n");
+               return false;
+       }
+
+       if (malidp_hw_format_is_linear_only(format) == true) {
+               DRM_DEBUG_KMS("Given format (0x%x) is supported is linear mode only\n",
+                             format);
+               return false;
+       }
+
+       /*
+        * RGB formats need to provide YTR modifier and YUV formats should not
+        * provide YTR modifier.
+        */
+       if (!(info->is_yuv) != !!(modifier & AFBC_FORMAT_MOD_YTR)) {
+               DRM_DEBUG_KMS("AFBC_FORMAT_MOD_YTR is %s for %s formats\n",
+                             info->is_yuv ? "disallowed" : "mandatory",
+                             info->is_yuv ? "YUV" : "RGB");
+               return false;
+       }
+
+       if (modifier & AFBC_SPLIT) {
+               if (!info->is_yuv) {
+                       if (drm_format_plane_cpp(format, 0) <= 2) {
+                               DRM_DEBUG_KMS("RGB formats <= 16bpp are not supported with SPLIT\n");
+                               return false;
+                       }
+               }
+
+               if ((drm_format_horz_chroma_subsampling(format) != 1) ||
+                   (drm_format_vert_chroma_subsampling(format) != 1)) {
+                       if (!(format == DRM_FORMAT_YUV420_10BIT &&
+                             (map->features & MALIDP_DEVICE_AFBC_YUV_420_10_SUPPORT_SPLIT))) {
+                               DRM_DEBUG_KMS("Formats which are sub-sampled should never be split\n");
+                               return false;
+                       }
+               }
+       }
+
+       if (modifier & AFBC_CBR) {
+               if ((drm_format_horz_chroma_subsampling(format) == 1) ||
+                   (drm_format_vert_chroma_subsampling(format) == 1)) {
+                       DRM_DEBUG_KMS("Formats which are not sub-sampled should not have CBR set\n");
+                       return false;
+               }
+       }
+
+       return true;
+}
+
+static bool malidp_format_mod_supported_per_plane(struct drm_plane *plane,
+                                                 u32 format, u64 modifier)
+{
+       return malidp_format_mod_supported(plane->dev, format, modifier);
+}
+
 static const struct drm_plane_funcs malidp_de_plane_funcs = {
        .update_plane = drm_atomic_helper_update_plane,
        .disable_plane = drm_atomic_helper_disable_plane,
@@ -153,6 +268,7 @@ static const struct drm_plane_funcs malidp_de_plane_funcs = {
        .atomic_duplicate_state = malidp_duplicate_plane_state,
        .atomic_destroy_state = malidp_destroy_plane_state,
        .atomic_print_state = malidp_plane_atomic_print_state,
+       .format_mod_supported = malidp_format_mod_supported_per_plane,
 };
 
 static int malidp_se_check_scaling(struct malidp_plane *mp,
@@ -406,8 +522,8 @@ static int malidp_de_plane_check(struct drm_plane *plane,
        fb = state->fb;
 
        ms->format = malidp_hw_get_format_id(&mp->hwdev->hw->map,
-                                            mp->layer->id,
-                                            fb->format->format);
+                                            mp->layer->id, fb->format->format,
+                                            !!fb->modifier);
        if (ms->format == MALIDP_INVALID_FORMAT_ID)
                return -EINVAL;
 
@@ -415,8 +531,8 @@ static int malidp_de_plane_check(struct drm_plane *plane,
        for (i = 0; i < ms->n_planes; i++) {
                u8 alignment = malidp_hw_get_pitch_align(mp->hwdev, rotated);
 
-               if ((fb->pitches[i] * drm_format_info_block_height(fb->format, i))
-                               & (alignment - 1)) {
+               if (((fb->pitches[i] * drm_format_info_block_height(fb->format, i))
+                               & (alignment - 1)) && !(fb->modifier)) {
                        DRM_DEBUG_KMS("Invalid pitch %u for plane %d\n",
                                      fb->pitches[i], i);
                        return -EINVAL;
@@ -469,13 +585,20 @@ static int malidp_de_plane_check(struct drm_plane *plane,
                        return -EINVAL;
        }
 
+       /* SMART layer does not support AFBC */
+       if (mp->layer->id == DE_SMART && fb->modifier) {
+               DRM_ERROR("AFBC framebuffer not supported in SMART layer");
+               return -EINVAL;
+       }
+
        ms->rotmem_size = 0;
        if (state->rotation & MALIDP_ROTATED_MASK) {
                int val;
 
                val = mp->hwdev->hw->rotmem_required(mp->hwdev, state->crtc_w,
                                                     state->crtc_h,
-                                                    fb->format->format);
+                                                    fb->format->format,
+                                                    !!(fb->modifier));
                if (val < 0)
                        return val;
 
@@ -592,6 +715,83 @@ static void malidp_de_set_mmu_control(struct malidp_plane *mp,
                        mp->layer->base + mp->layer->mmu_ctrl_offset);
 }
 
+static void malidp_set_plane_base_addr(struct drm_framebuffer *fb,
+                                      struct malidp_plane *mp,
+                                      int plane_index)
+{
+       dma_addr_t paddr;
+       u16 ptr;
+       struct drm_plane *plane = &mp->base;
+       bool afbc = fb->modifier ? true : false;
+
+       ptr = mp->layer->ptr + (plane_index << 4);
+
+       /*
+        * drm_fb_cma_get_gem_addr() alters the physical base address of the
+        * framebuffer as per the plane's src_x, src_y co-ordinates (ie to
+        * take care of source cropping).
+        * For AFBC, this is not needed as the cropping is handled by _AD_CROP_H
+        * and _AD_CROP_V registers.
+        */
+       if (!afbc) {
+               paddr = drm_fb_cma_get_gem_addr(fb, plane->state,
+                                               plane_index);
+       } else {
+               struct drm_gem_cma_object *obj;
+
+               obj = drm_fb_cma_get_gem_obj(fb, plane_index);
+
+               if (WARN_ON(!obj))
+                       return;
+               paddr = obj->paddr;
+       }
+
+       malidp_hw_write(mp->hwdev, lower_32_bits(paddr), ptr);
+       malidp_hw_write(mp->hwdev, upper_32_bits(paddr), ptr + 4);
+}
+
+static void malidp_de_set_plane_afbc(struct drm_plane *plane)
+{
+       struct malidp_plane *mp;
+       u32 src_w, src_h, val = 0, src_x, src_y;
+       struct drm_framebuffer *fb = plane->state->fb;
+
+       mp = to_malidp_plane(plane);
+
+       /* no afbc_decoder_offset means AFBC is not supported on this plane */
+       if (!mp->layer->afbc_decoder_offset)
+               return;
+
+       if (!fb->modifier) {
+               malidp_hw_write(mp->hwdev, 0, mp->layer->afbc_decoder_offset);
+               return;
+       }
+
+       /* convert src values from Q16 fixed point to integer */
+       src_w = plane->state->src_w >> 16;
+       src_h = plane->state->src_h >> 16;
+       src_x = plane->state->src_x >> 16;
+       src_y = plane->state->src_y >> 16;
+
+       val = ((fb->width - (src_x + src_w)) << MALIDP_AD_CROP_RIGHT_OFFSET) |
+                  src_x;
+       malidp_hw_write(mp->hwdev, val,
+                       mp->layer->afbc_decoder_offset + MALIDP_AD_CROP_H);
+
+       val = ((fb->height - (src_y + src_h)) << MALIDP_AD_CROP_BOTTOM_OFFSET) |
+                  src_y;
+       malidp_hw_write(mp->hwdev, val,
+                       mp->layer->afbc_decoder_offset + MALIDP_AD_CROP_V);
+
+       val = MALIDP_AD_EN;
+       if (fb->modifier & AFBC_FORMAT_MOD_SPLIT)
+               val |= MALIDP_AD_BS;
+       if (fb->modifier & AFBC_FORMAT_MOD_YTR)
+               val |= MALIDP_AD_YTR;
+
+       malidp_hw_write(mp->hwdev, val, mp->layer->afbc_decoder_offset);
+}
+
 static void malidp_de_plane_update(struct drm_plane *plane,
                                   struct drm_plane_state *old_state)
 {
@@ -602,12 +802,23 @@ static void malidp_de_plane_update(struct drm_plane *plane,
        u8 plane_alpha = state->alpha >> 8;
        u32 src_w, src_h, dest_w, dest_h, val;
        int i;
+       struct drm_framebuffer *fb = plane->state->fb;
 
        mp = to_malidp_plane(plane);
 
-       /* convert src values from Q16 fixed point to integer */
-       src_w = state->src_w >> 16;
-       src_h = state->src_h >> 16;
+       /*
+        * For AFBC framebuffer, use the framebuffer width and height for
+        * configuring layer input size register.
+        */
+       if (fb->modifier) {
+               src_w = fb->width;
+               src_h = fb->height;
+       } else {
+               /* convert src values from Q16 fixed point to integer */
+               src_w = state->src_w >> 16;
+               src_h = state->src_h >> 16;
+       }
+
        dest_w = state->crtc_w;
        dest_h = state->crtc_h;
 
@@ -615,15 +826,8 @@ static void malidp_de_plane_update(struct drm_plane *plane,
        val = (val & ~LAYER_FORMAT_MASK) | ms->format;
        malidp_hw_write(mp->hwdev, val, mp->layer->base);
 
-       for (i = 0; i < ms->n_planes; i++) {
-               /* calculate the offset for the layer's plane registers */
-               u16 ptr = mp->layer->ptr + (i << 4);
-               dma_addr_t fb_addr = drm_fb_cma_get_gem_addr(state->fb,
-                                                            state, i);
-
-               malidp_hw_write(mp->hwdev, lower_32_bits(fb_addr), ptr);
-               malidp_hw_write(mp->hwdev, upper_32_bits(fb_addr), ptr + 4);
-       }
+       for (i = 0; i < ms->n_planes; i++)
+               malidp_set_plane_base_addr(fb, mp, i);
 
        malidp_de_set_mmu_control(mp, ms);
 
@@ -657,6 +861,8 @@ static void malidp_de_plane_update(struct drm_plane *plane,
                                mp->layer->base + MALIDP550_LS_R1_IN_SIZE);
        }
 
+       malidp_de_set_plane_afbc(plane);
+
        /* first clear the rotation bits */
        val = malidp_hw_read(mp->hwdev, mp->layer->base + MALIDP_LAYER_CONTROL);
        val &= ~LAYER_ROT_MASK;
@@ -733,7 +939,26 @@ int malidp_de_planes_init(struct drm_device *drm)
                                  BIT(DRM_MODE_BLEND_PREMULTI)   |
                                  BIT(DRM_MODE_BLEND_COVERAGE);
        u32 *formats;
-       int ret, i, j, n;
+       int ret, i = 0, j = 0, n;
+       u64 supported_modifiers[MODIFIERS_COUNT_MAX];
+       const u64 *modifiers;
+
+       modifiers = malidp_format_modifiers;
+
+       if (!(map->features & MALIDP_DEVICE_AFBC_SUPPORT_SPLIT)) {
+               /*
+                * Since our hardware does not support SPLIT, so build the list
+                * of supported modifiers excluding SPLIT ones.
+                */
+               while (*modifiers != DRM_FORMAT_MOD_INVALID) {
+                       if (!(*modifiers & AFBC_SPLIT))
+                               supported_modifiers[j++] = *modifiers;
+
+                       modifiers++;
+               }
+               supported_modifiers[j++] = DRM_FORMAT_MOD_INVALID;
+               modifiers = supported_modifiers;
+       }
 
        formats = kcalloc(map->n_pixel_formats, sizeof(*formats), GFP_KERNEL);
        if (!formats) {
@@ -758,9 +983,15 @@ int malidp_de_planes_init(struct drm_device *drm)
 
                plane_type = (i == 0) ? DRM_PLANE_TYPE_PRIMARY :
                                        DRM_PLANE_TYPE_OVERLAY;
+
+               /*
+                * All the layers except smart layer supports AFBC modifiers.
+                */
                ret = drm_universal_plane_init(drm, &plane->base, crtcs,
-                                              &malidp_de_plane_funcs, formats,
-                                              n, NULL, plane_type, NULL);
+                               &malidp_de_plane_funcs, formats, n,
+                               (id == DE_SMART) ? NULL : modifiers, plane_type,
+                               NULL);
+
                if (ret < 0)
                        goto cleanup;
 
index 7ce3e14..a0dd6e1 100644 (file)
 #define MALIDP500_LV_YUV2RGB           ((s16)(-0xB8))
 #define MALIDP500_DE_LV_BASE           0x00100
 #define MALIDP500_DE_LV_PTR_BASE       0x00124
+#define MALIDP500_DE_LV_AD_CTRL                0x00400
 #define MALIDP500_DE_LG1_BASE          0x00200
 #define MALIDP500_DE_LG1_PTR_BASE      0x0021c
+#define MALIDP500_DE_LG1_AD_CTRL       0x0040c
 #define MALIDP500_DE_LG2_BASE          0x00300
 #define MALIDP500_DE_LG2_PTR_BASE      0x0031c
+#define MALIDP500_DE_LG2_AD_CTRL       0x00418
 #define MALIDP500_SE_BASE              0x00c00
 #define MALIDP500_SE_CONTROL           0x00c0c
 #define MALIDP500_SE_MEMWRITE_OUT_SIZE 0x00c2c
 #define MALIDP550_LV_YUV2RGB           0x00084
 #define MALIDP550_DE_LV1_BASE          0x00100
 #define MALIDP550_DE_LV1_PTR_BASE      0x00124
+#define MALIDP550_DE_LV1_AD_CTRL       0x001B8
 #define MALIDP550_DE_LV2_BASE          0x00200
 #define MALIDP550_DE_LV2_PTR_BASE      0x00224
+#define MALIDP550_DE_LV2_AD_CTRL       0x002B8
 #define MALIDP550_DE_LG_BASE           0x00300
 #define MALIDP550_DE_LG_PTR_BASE       0x0031c
+#define MALIDP550_DE_LG_AD_CTRL                0x00330
 #define MALIDP550_DE_LS_BASE           0x00400
 #define MALIDP550_DE_LS_PTR_BASE       0x0042c
 #define MALIDP550_DE_PERF_BASE         0x00500
 #define MALIDP_MMU_CTRL_PX_PS(x)       (1 << (8 + (x)))
 #define MALIDP_MMU_CTRL_PP_NUM_REQ(x)  (((x) & 0x7f) << 12)
 
+/* AFBC register offsets relative to MALIDPXXX_DE_LX_AD_CTRL */
+/* The following register offsets are common for DP500, DP550 and DP650 */
+#define MALIDP_AD_CROP_H                0x4
+#define MALIDP_AD_CROP_V                0x8
+#define MALIDP_AD_END_PTR_LOW           0xc
+#define MALIDP_AD_END_PTR_HIGH          0x10
+
+/* AFBC decoder Registers */
+#define MALIDP_AD_EN                    BIT(0)
+#define MALIDP_AD_YTR                   BIT(4)
+#define MALIDP_AD_BS                    BIT(8)
+#define MALIDP_AD_CROP_RIGHT_OFFSET     16
+#define MALIDP_AD_CROP_BOTTOM_OFFSET    16
+
 /*
  * Starting with DP550 the register map blocks has been standardised to the
  * following layout:
index 9cd82e3..9e7cd6b 100644 (file)
@@ -214,20 +214,9 @@ static enum drm_mode_status bochs_connector_mode_valid(struct drm_connector *con
        return MODE_OK;
 }
 
-static struct drm_encoder *
-bochs_connector_best_encoder(struct drm_connector *connector)
-{
-       int enc_id = connector->encoder_ids[0];
-       /* pick the encoder ids */
-       if (enc_id)
-               return drm_encoder_find(connector->dev, NULL, enc_id);
-       return NULL;
-}
-
 static const struct drm_connector_helper_funcs bochs_connector_connector_helper_funcs = {
        .get_modes = bochs_connector_get_modes,
        .mode_valid = bochs_connector_mode_valid,
-       .best_encoder = bochs_connector_best_encoder,
 };
 
 static const struct drm_connector_funcs bochs_connector_connector_funcs = {
index dc8ae98..86efd2d 100644 (file)
@@ -1752,7 +1752,7 @@ int drm_atomic_helper_commit(struct drm_device *dev,
         *
         * NOTE: Commit work has multiple phases, first hardware commit, then
         * cleanup. We want them to overlap, hence need system_unbound_wq to
-        * make sure work items don't artifically stall on each another.
+        * make sure work items don't artificially stall on each another.
         */
 
        drm_atomic_state_get(state);
@@ -1786,7 +1786,7 @@ EXPORT_SYMBOL(drm_atomic_helper_commit);
  *
  * Asynchronous workers need to have sufficient parallelism to be able to run
  * different atomic commits on different CRTCs in parallel. The simplest way to
- * achive this is by running them on the &system_unbound_wq work queue. Note
+ * achieve this is by running them on the &system_unbound_wq work queue. Note
  * that drivers are not required to split up atomic commits and run an
  * individual commit in parallel - userspace is supposed to do that if it cares.
  * But it might be beneficial to do that for modesets, since those necessarily
index 8fa77de..ea797d4 100644 (file)
@@ -733,6 +733,8 @@ static int drm_atomic_connector_set_property(struct drm_connector *connector,
                        return -EINVAL;
                }
                state->content_protection = val;
+       } else if (property == connector->colorspace_property) {
+               state->colorspace = val;
        } else if (property == config->writeback_fb_id_property) {
                struct drm_framebuffer *fb = drm_framebuffer_lookup(dev, NULL, val);
                int ret = drm_atomic_set_writeback_fb_for_connector(state, fb);
@@ -801,6 +803,8 @@ drm_atomic_connector_get_property(struct drm_connector *connector,
                *val = state->picture_aspect_ratio;
        } else if (property == config->content_type_property) {
                *val = state->content_type;
+       } else if (property == connector->colorspace_property) {
+               *val = state->colorspace;
        } else if (property == connector->scaling_mode_property) {
                *val = state->scaling_mode;
        } else if (property == connector->content_protection_property) {
index dd40eff..2355124 100644 (file)
@@ -245,6 +245,7 @@ int drm_connector_init(struct drm_device *dev,
        INIT_LIST_HEAD(&connector->modes);
        mutex_init(&connector->mutex);
        connector->edid_blob_ptr = NULL;
+       connector->tile_blob_ptr = NULL;
        connector->status = connector_status_unknown;
        connector->display_info.panel_orientation =
                DRM_MODE_PANEL_ORIENTATION_UNKNOWN;
@@ -272,6 +273,9 @@ int drm_connector_init(struct drm_device *dev,
        drm_object_attach_property(&connector->base,
                                   config->non_desktop_property,
                                   0);
+       drm_object_attach_property(&connector->base,
+                                  config->tile_property,
+                                  0);
 
        if (drm_core_check_feature(dev, DRIVER_ATOMIC)) {
                drm_object_attach_property(&connector->base, config->prop_crtc_id, 0);
@@ -826,6 +830,33 @@ static struct drm_prop_enum_list drm_cp_enum_list[] = {
 };
 DRM_ENUM_NAME_FN(drm_get_content_protection_name, drm_cp_enum_list)
 
+static const struct drm_prop_enum_list hdmi_colorspaces[] = {
+       /* For Default case, driver will set the colorspace */
+       { DRM_MODE_COLORIMETRY_DEFAULT, "Default" },
+       /* Standard Definition Colorimetry based on CEA 861 */
+       { DRM_MODE_COLORIMETRY_SMPTE_170M_YCC, "SMPTE_170M_YCC" },
+       { DRM_MODE_COLORIMETRY_BT709_YCC, "BT709_YCC" },
+       /* Standard Definition Colorimetry based on IEC 61966-2-4 */
+       { DRM_MODE_COLORIMETRY_XVYCC_601, "XVYCC_601" },
+       /* High Definition Colorimetry based on IEC 61966-2-4 */
+       { DRM_MODE_COLORIMETRY_XVYCC_709, "XVYCC_709" },
+       /* Colorimetry based on IEC 61966-2-1/Amendment 1 */
+       { DRM_MODE_COLORIMETRY_SYCC_601, "SYCC_601" },
+       /* Colorimetry based on IEC 61966-2-5 [33] */
+       { DRM_MODE_COLORIMETRY_OPYCC_601, "opYCC_601" },
+       /* Colorimetry based on IEC 61966-2-5 */
+       { DRM_MODE_COLORIMETRY_OPRGB, "opRGB" },
+       /* Colorimetry based on ITU-R BT.2020 */
+       { DRM_MODE_COLORIMETRY_BT2020_CYCC, "BT2020_CYCC" },
+       /* Colorimetry based on ITU-R BT.2020 */
+       { DRM_MODE_COLORIMETRY_BT2020_RGB, "BT2020_RGB" },
+       /* Colorimetry based on ITU-R BT.2020 */
+       { DRM_MODE_COLORIMETRY_BT2020_YCC, "BT2020_YCC" },
+       /* Added as part of Additional Colorimetry Extension in 861.G */
+       { DRM_MODE_COLORIMETRY_DCI_P3_RGB_D65, "DCI-P3_RGB_D65" },
+       { DRM_MODE_COLORIMETRY_DCI_P3_RGB_THEATER, "DCI-P3_RGB_Theater" },
+};
+
 /**
  * DOC: standard connector properties
  *
@@ -1548,6 +1579,57 @@ int drm_mode_create_aspect_ratio_property(struct drm_device *dev)
 EXPORT_SYMBOL(drm_mode_create_aspect_ratio_property);
 
 /**
+ * DOC: standard connector properties
+ *
+ * Colorspace:
+ *     drm_mode_create_colorspace_property - create colorspace property
+ *     This property helps select a suitable colorspace based on the sink
+ *     capability. Modern sink devices support wider gamut like BT2020.
+ *     This helps switch to BT2020 mode if the BT2020 encoded video stream
+ *     is being played by the user, same for any other colorspace. Thereby
+ *     giving a good visual experience to users.
+ *
+ *     The expectation from userspace is that it should parse the EDID
+ *     and get supported colorspaces. Use this property and switch to the
+ *     one supported. Sink supported colorspaces should be retrieved by
+ *     userspace from EDID and driver will not explicitly expose them.
+ *
+ *     Basically the expectation from userspace is:
+ *      - Set up CRTC DEGAMMA/CTM/GAMMA to convert to some sink
+ *        colorspace
+ *      - Set this new property to let the sink know what it
+ *        converted the CRTC output to.
+ *      - This property is just to inform sink what colorspace
+ *        source is trying to drive.
+ *
+ * Called by a driver the first time it's needed, must be attached to desired
+ * connectors.
+ */
+int drm_mode_create_colorspace_property(struct drm_connector *connector)
+{
+       struct drm_device *dev = connector->dev;
+       struct drm_property *prop;
+
+       if (connector->connector_type == DRM_MODE_CONNECTOR_HDMIA ||
+           connector->connector_type == DRM_MODE_CONNECTOR_HDMIB) {
+               prop = drm_property_create_enum(dev, DRM_MODE_PROP_ENUM,
+                                               "Colorspace",
+                                               hdmi_colorspaces,
+                                               ARRAY_SIZE(hdmi_colorspaces));
+               if (!prop)
+                       return -ENOMEM;
+       } else {
+               DRM_DEBUG_KMS("Colorspace property not supported\n");
+               return 0;
+       }
+
+       connector->colorspace_property = prop;
+
+       return 0;
+}
+EXPORT_SYMBOL(drm_mode_create_colorspace_property);
+
+/**
  * drm_mode_create_content_type_property - create content type property
  * @dev: DRM device
  *
@@ -1634,6 +1716,8 @@ EXPORT_SYMBOL(drm_connector_set_path_property);
  * This looks up the tile information for a connector, and creates a
  * property for userspace to parse if it exists. The property is of
  * the form of 8 integers using ':' as a separator.
+ * This is used for dual port tiled displays with DisplayPort SST
+ * or DisplayPort MST connectors.
  *
  * Returns:
  * Zero on success, errno on failure.
@@ -1677,6 +1761,9 @@ EXPORT_SYMBOL(drm_connector_set_tile_property);
  *
  * This function creates a new blob modeset object and assigns its id to the
  * connector's edid property.
+ * Since we also parse tile information from EDID's displayID block, we also
+ * set the connector's tile property here. See drm_connector_set_tile_property()
+ * for more details.
  *
  * Returns:
  * Zero on success, negative errno on failure.
@@ -1718,7 +1805,9 @@ int drm_connector_update_edid_property(struct drm_connector *connector,
                                               edid,
                                               &connector->base,
                                               dev->mode_config.edid_property);
-       return ret;
+       if (ret)
+               return ret;
+       return drm_connector_set_tile_property(connector);
 }
 EXPORT_SYMBOL(drm_connector_update_edid_property);
 
index dc7ac0c..c630ed1 100644 (file)
@@ -3022,7 +3022,6 @@ struct edid *drm_dp_mst_get_edid(struct drm_connector *connector, struct drm_dp_
                edid = drm_edid_duplicate(port->cached_edid);
        else {
                edid = drm_get_edid(connector, &port->aux.ddc);
-               drm_connector_set_tile_property(connector);
        }
        port->has_audio = drm_detect_monitor_audio(edid);
        drm_dp_mst_topology_put_port(port);
index 381581b..50d849d 100644 (file)
@@ -286,6 +286,138 @@ void drm_minor_release(struct drm_minor *minor)
  * Note that the lifetime rules for &drm_device instance has still a lot of
  * historical baggage. Hence use the reference counting provided by
  * drm_dev_get() and drm_dev_put() only carefully.
+ *
+ * Display driver example
+ * ~~~~~~~~~~~~~~~~~~~~~~
+ *
+ * The following example shows a typical structure of a DRM display driver.
+ * The example focus on the probe() function and the other functions that is
+ * almost always present and serves as a demonstration of devm_drm_dev_init()
+ * usage with its accompanying drm_driver->release callback.
+ *
+ * .. code-block:: c
+ *
+ *     struct driver_device {
+ *             struct drm_device drm;
+ *             void *userspace_facing;
+ *             struct clk *pclk;
+ *     };
+ *
+ *     static void driver_drm_release(struct drm_device *drm)
+ *     {
+ *             struct driver_device *priv = container_of(...);
+ *
+ *             drm_mode_config_cleanup(drm);
+ *             drm_dev_fini(drm);
+ *             kfree(priv->userspace_facing);
+ *             kfree(priv);
+ *     }
+ *
+ *     static struct drm_driver driver_drm_driver = {
+ *             [...]
+ *             .release = driver_drm_release,
+ *     };
+ *
+ *     static int driver_probe(struct platform_device *pdev)
+ *     {
+ *             struct driver_device *priv;
+ *             struct drm_device *drm;
+ *             int ret;
+ *
+ *             [
+ *               devm_kzalloc() can't be used here because the drm_device
+ *               lifetime can exceed the device lifetime if driver unbind
+ *               happens when userspace still has open file descriptors.
+ *             ]
+ *             priv = kzalloc(sizeof(*priv), GFP_KERNEL);
+ *             if (!priv)
+ *                     return -ENOMEM;
+ *
+ *             drm = &priv->drm;
+ *
+ *             ret = devm_drm_dev_init(&pdev->dev, drm, &driver_drm_driver);
+ *             if (ret) {
+ *                     kfree(drm);
+ *                     return ret;
+ *             }
+ *
+ *             drm_mode_config_init(drm);
+ *
+ *             priv->userspace_facing = kzalloc(..., GFP_KERNEL);
+ *             if (!priv->userspace_facing)
+ *                     return -ENOMEM;
+ *
+ *             priv->pclk = devm_clk_get(dev, "PCLK");
+ *             if (IS_ERR(priv->pclk))
+ *                     return PTR_ERR(priv->pclk);
+ *
+ *             [ Further setup, display pipeline etc ]
+ *
+ *             platform_set_drvdata(pdev, drm);
+ *
+ *             drm_mode_config_reset(drm);
+ *
+ *             ret = drm_dev_register(drm);
+ *             if (ret)
+ *                     return ret;
+ *
+ *             drm_fbdev_generic_setup(drm, 32);
+ *
+ *             return 0;
+ *     }
+ *
+ *     [ This function is called before the devm_ resources are released ]
+ *     static int driver_remove(struct platform_device *pdev)
+ *     {
+ *             struct drm_device *drm = platform_get_drvdata(pdev);
+ *
+ *             drm_dev_unregister(drm);
+ *             drm_atomic_helper_shutdown(drm)
+ *
+ *             return 0;
+ *     }
+ *
+ *     [ This function is called on kernel restart and shutdown ]
+ *     static void driver_shutdown(struct platform_device *pdev)
+ *     {
+ *             drm_atomic_helper_shutdown(platform_get_drvdata(pdev));
+ *     }
+ *
+ *     static int __maybe_unused driver_pm_suspend(struct device *dev)
+ *     {
+ *             return drm_mode_config_helper_suspend(dev_get_drvdata(dev));
+ *     }
+ *
+ *     static int __maybe_unused driver_pm_resume(struct device *dev)
+ *     {
+ *             drm_mode_config_helper_resume(dev_get_drvdata(dev));
+ *
+ *             return 0;
+ *     }
+ *
+ *     static const struct dev_pm_ops driver_pm_ops = {
+ *             SET_SYSTEM_SLEEP_PM_OPS(driver_pm_suspend, driver_pm_resume)
+ *     };
+ *
+ *     static struct platform_driver driver_driver = {
+ *             .driver = {
+ *                     [...]
+ *                     .pm = &driver_pm_ops,
+ *             },
+ *             .probe = driver_probe,
+ *             .remove = driver_remove,
+ *             .shutdown = driver_shutdown,
+ *     };
+ *     module_platform_driver(driver_driver);
+ *
+ * Drivers that want to support device unplugging (USB, DT overlay unload) should
+ * use drm_dev_unplug() instead of drm_dev_unregister(). The driver must protect
+ * regions that is accessing device resources to prevent use after they're
+ * released. This is done using drm_dev_enter() and drm_dev_exit(). There is one
+ * shortcoming however, drm_dev_unplug() marks the drm_device as unplugged before
+ * drm_atomic_helper_shutdown() is called. This means that if the disable code
+ * paths are protected, they will not run on regular driver module unload,
+ * possibily leaving the hardware enabled.
  */
 
 /**
@@ -376,11 +508,6 @@ void drm_dev_unplug(struct drm_device *dev)
        synchronize_srcu(&drm_unplug_srcu);
 
        drm_dev_unregister(dev);
-
-       mutex_lock(&drm_global_mutex);
-       if (dev->open_count == 0)
-               drm_dev_put(dev);
-       mutex_unlock(&drm_global_mutex);
 }
 EXPORT_SYMBOL(drm_dev_unplug);
 
@@ -457,6 +584,31 @@ static void drm_fs_inode_free(struct inode *inode)
 }
 
 /**
+ * DOC: component helper usage recommendations
+ *
+ * DRM drivers that drive hardware where a logical device consists of a pile of
+ * independent hardware blocks are recommended to use the :ref:`component helper
+ * library<component>`. For consistency and better options for code reuse the
+ * following guidelines apply:
+ *
+ *  - The entire device initialization procedure should be run from the
+ *    &component_master_ops.master_bind callback, starting with drm_dev_init(),
+ *    then binding all components with component_bind_all() and finishing with
+ *    drm_dev_register().
+ *
+ *  - The opaque pointer passed to all components through component_bind_all()
+ *    should point at &struct drm_device of the device instance, not some driver
+ *    specific private structure.
+ *
+ *  - The component helper fills the niche where further standardization of
+ *    interfaces is not practical. When there already is, or will be, a
+ *    standardized interface like &drm_bridge or &drm_panel, providing its own
+ *    functions to find such components at driver load time, like
+ *    drm_of_find_panel_or_bridge(), then the component helper should not be
+ *    used.
+ */
+
+/**
  * drm_dev_init - Initialise new DRM device
  * @dev: DRM device
  * @driver: DRM driver
@@ -501,7 +653,7 @@ int drm_dev_init(struct drm_device *dev,
        BUG_ON(!parent);
 
        kref_init(&dev->ref);
-       dev->dev = parent;
+       dev->dev = get_device(parent);
        dev->driver = driver;
 
        /* no per-device feature limits by default */
@@ -571,6 +723,7 @@ err_minors:
        drm_minor_free(dev, DRM_MINOR_RENDER);
        drm_fs_inode_free(dev->anon_inode);
 err_free:
+       put_device(dev->dev);
        mutex_destroy(&dev->master_mutex);
        mutex_destroy(&dev->ctxlist_mutex);
        mutex_destroy(&dev->clientlist_mutex);
@@ -580,6 +733,45 @@ err_free:
 }
 EXPORT_SYMBOL(drm_dev_init);
 
+static void devm_drm_dev_init_release(void *data)
+{
+       drm_dev_put(data);
+}
+
+/**
+ * devm_drm_dev_init - Resource managed drm_dev_init()
+ * @parent: Parent device object
+ * @dev: DRM device
+ * @driver: DRM driver
+ *
+ * Managed drm_dev_init(). The DRM device initialized with this function is
+ * automatically put on driver detach using drm_dev_put(). You must supply a
+ * &drm_driver.release callback to control the finalization explicitly.
+ *
+ * RETURNS:
+ * 0 on success, or error code on failure.
+ */
+int devm_drm_dev_init(struct device *parent,
+                     struct drm_device *dev,
+                     struct drm_driver *driver)
+{
+       int ret;
+
+       if (WARN_ON(!parent || !driver->release))
+               return -EINVAL;
+
+       ret = drm_dev_init(dev, driver, parent);
+       if (ret)
+               return ret;
+
+       ret = devm_add_action(parent, devm_drm_dev_init_release, dev);
+       if (ret)
+               devm_drm_dev_init_release(dev);
+
+       return ret;
+}
+EXPORT_SYMBOL(devm_drm_dev_init);
+
 /**
  * drm_dev_fini - Finalize a dead DRM device
  * @dev: DRM device
@@ -606,6 +798,8 @@ void drm_dev_fini(struct drm_device *dev)
        drm_minor_free(dev, DRM_MINOR_PRIMARY);
        drm_minor_free(dev, DRM_MINOR_RENDER);
 
+       put_device(dev->dev);
+
        mutex_destroy(&dev->master_mutex);
        mutex_destroy(&dev->ctxlist_mutex);
        mutex_destroy(&dev->clientlist_mutex);
index bce99f9..77f4e5a 100644 (file)
@@ -11,6 +11,7 @@
 #include <linux/init.h>
 #include <linux/errno.h>
 #include <linux/byteorder/generic.h>
+#include <drm/drm_print.h>
 #include <drm/drm_dp_helper.h>
 #include <drm/drm_dsc.h>
 
 /**
  * drm_dsc_dp_pps_header_init() - Initializes the PPS Header
  * for DisplayPort as per the DP 1.4 spec.
- * @pps_sdp: Secondary data packet for DSC Picture Parameter Set
- *           as defined in &struct drm_dsc_pps_infoframe
+ * @pps_header: Secondary data packet header for DSC Picture
+ *              Parameter Set as defined in &struct dp_sdp_header
  *
  * DP 1.4 spec defines the secondary data packet for sending the
  * picture parameter infoframes from the source to the sink.
- * This function populates the pps header defined in
- * &struct drm_dsc_pps_infoframe as per the header bytes defined
- * in &struct dp_sdp_header.
+ * This function populates the SDP header defined in
+ * &struct dp_sdp_header.
  */
-void drm_dsc_dp_pps_header_init(struct drm_dsc_pps_infoframe *pps_sdp)
+void drm_dsc_dp_pps_header_init(struct dp_sdp_header *pps_header)
 {
-       memset(&pps_sdp->pps_header, 0, sizeof(pps_sdp->pps_header));
+       memset(pps_header, 0, sizeof(*pps_header));
 
-       pps_sdp->pps_header.HB1 = DP_SDP_PPS;
-       pps_sdp->pps_header.HB2 = DP_SDP_PPS_HEADER_PAYLOAD_BYTES_MINUS_1;
+       pps_header->HB1 = DP_SDP_PPS;
+       pps_header->HB2 = DP_SDP_PPS_HEADER_PAYLOAD_BYTES_MINUS_1;
 }
 EXPORT_SYMBOL(drm_dsc_dp_pps_header_init);
 
 /**
- * drm_dsc_pps_infoframe_pack() - Populates the DSC PPS infoframe
+ * drm_dsc_pps_payload_pack() - Populates the DSC PPS
  *
- * @pps_sdp:
- * Secondary data packet for DSC Picture Parameter Set. This is defined
- * by &struct drm_dsc_pps_infoframe
+ * @pps_payload:
+ * Bitwise struct for DSC Picture Parameter Set. This is defined
+ * by &struct drm_dsc_picture_parameter_set
  * @dsc_cfg:
  * DSC Configuration data filled by driver as defined by
  * &struct drm_dsc_config
  *
- * DSC source device sends a secondary data packet filled with all the
- * picture parameter set (PPS) information required by the sink to decode
- * the compressed frame. Driver populates the dsC PPS infoframe using the DSC
- * configuration parameters in the order expected by the DSC Display Sink
- * device. For the DSC, the sink device expects the PPS payload in the big
- * endian format for the fields that span more than 1 byte.
+ * DSC source device sends a picture parameter set (PPS) containing the
+ * information required by the sink to decode the compressed frame. Driver
+ * populates the DSC PPS struct using the DSC configuration parameters in
+ * the order expected by the DSC Display Sink device. For the DSC, the sink
+ * device expects the PPS payload in big endian format for fields
+ * that span more than 1 byte.
  */
-void drm_dsc_pps_infoframe_pack(struct drm_dsc_pps_infoframe *pps_sdp,
+void drm_dsc_pps_payload_pack(struct drm_dsc_picture_parameter_set *pps_payload,
                                const struct drm_dsc_config *dsc_cfg)
 {
        int i;
 
        /* Protect against someone accidently changing struct size */
-       BUILD_BUG_ON(sizeof(pps_sdp->pps_payload) !=
+       BUILD_BUG_ON(sizeof(*pps_payload) !=
                     DP_SDP_PPS_HEADER_PAYLOAD_BYTES_MINUS_1 + 1);
 
-       memset(&pps_sdp->pps_payload, 0, sizeof(pps_sdp->pps_payload));
+       memset(pps_payload, 0, sizeof(*pps_payload));
 
        /* PPS 0 */
-       pps_sdp->pps_payload.dsc_version =
+       pps_payload->dsc_version =
                dsc_cfg->dsc_version_minor |
                dsc_cfg->dsc_version_major << DSC_PPS_VERSION_MAJOR_SHIFT;
 
        /* PPS 1, 2 is 0 */
 
        /* PPS 3 */
-       pps_sdp->pps_payload.pps_3 =
+       pps_payload->pps_3 =
                dsc_cfg->line_buf_depth |
                dsc_cfg->bits_per_component << DSC_PPS_BPC_SHIFT;
 
        /* PPS 4 */
-       pps_sdp->pps_payload.pps_4 =
+       pps_payload->pps_4 =
                ((dsc_cfg->bits_per_pixel & DSC_PPS_BPP_HIGH_MASK) >>
                 DSC_PPS_MSB_SHIFT) |
                dsc_cfg->vbr_enable << DSC_PPS_VBR_EN_SHIFT |
-               dsc_cfg->enable422 << DSC_PPS_SIMPLE422_SHIFT |
+               dsc_cfg->simple_422 << DSC_PPS_SIMPLE422_SHIFT |
                dsc_cfg->convert_rgb << DSC_PPS_CONVERT_RGB_SHIFT |
                dsc_cfg->block_pred_enable << DSC_PPS_BLOCK_PRED_EN_SHIFT;
 
        /* PPS 5 */
-       pps_sdp->pps_payload.bits_per_pixel_low =
+       pps_payload->bits_per_pixel_low =
                (dsc_cfg->bits_per_pixel & DSC_PPS_LSB_MASK);
 
        /*
@@ -110,103 +110,103 @@ void drm_dsc_pps_infoframe_pack(struct drm_dsc_pps_infoframe *pps_sdp,
         */
 
        /* PPS 6, 7 */
-       pps_sdp->pps_payload.pic_height = cpu_to_be16(dsc_cfg->pic_height);
+       pps_payload->pic_height = cpu_to_be16(dsc_cfg->pic_height);
 
        /* PPS 8, 9 */
-       pps_sdp->pps_payload.pic_width = cpu_to_be16(dsc_cfg->pic_width);
+       pps_payload->pic_width = cpu_to_be16(dsc_cfg->pic_width);
 
        /* PPS 10, 11 */
-       pps_sdp->pps_payload.slice_height = cpu_to_be16(dsc_cfg->slice_height);
+       pps_payload->slice_height = cpu_to_be16(dsc_cfg->slice_height);
 
        /* PPS 12, 13 */
-       pps_sdp->pps_payload.slice_width = cpu_to_be16(dsc_cfg->slice_width);
+       pps_payload->slice_width = cpu_to_be16(dsc_cfg->slice_width);
 
        /* PPS 14, 15 */
-       pps_sdp->pps_payload.chunk_size = cpu_to_be16(dsc_cfg->slice_chunk_size);
+       pps_payload->chunk_size = cpu_to_be16(dsc_cfg->slice_chunk_size);
 
        /* PPS 16 */
-       pps_sdp->pps_payload.initial_xmit_delay_high =
+       pps_payload->initial_xmit_delay_high =
                ((dsc_cfg->initial_xmit_delay &
                  DSC_PPS_INIT_XMIT_DELAY_HIGH_MASK) >>
                 DSC_PPS_MSB_SHIFT);
 
        /* PPS 17 */
-       pps_sdp->pps_payload.initial_xmit_delay_low =
+       pps_payload->initial_xmit_delay_low =
                (dsc_cfg->initial_xmit_delay & DSC_PPS_LSB_MASK);
 
        /* PPS 18, 19 */
-       pps_sdp->pps_payload.initial_dec_delay =
+       pps_payload->initial_dec_delay =
                cpu_to_be16(dsc_cfg->initial_dec_delay);
 
        /* PPS 20 is 0 */
 
        /* PPS 21 */
-       pps_sdp->pps_payload.initial_scale_value =
+       pps_payload->initial_scale_value =
                dsc_cfg->initial_scale_value;
 
        /* PPS 22, 23 */
-       pps_sdp->pps_payload.scale_increment_interval =
+       pps_payload->scale_increment_interval =
                cpu_to_be16(dsc_cfg->scale_increment_interval);
 
        /* PPS 24 */
-       pps_sdp->pps_payload.scale_decrement_interval_high =
+       pps_payload->scale_decrement_interval_high =
                ((dsc_cfg->scale_decrement_interval &
                  DSC_PPS_SCALE_DEC_INT_HIGH_MASK) >>
                 DSC_PPS_MSB_SHIFT);
 
        /* PPS 25 */
-       pps_sdp->pps_payload.scale_decrement_interval_low =
+       pps_payload->scale_decrement_interval_low =
                (dsc_cfg->scale_decrement_interval & DSC_PPS_LSB_MASK);
 
        /* PPS 26[7:0], PPS 27[7:5] RESERVED */
 
        /* PPS 27 */
-       pps_sdp->pps_payload.first_line_bpg_offset =
+       pps_payload->first_line_bpg_offset =
                dsc_cfg->first_line_bpg_offset;
 
        /* PPS 28, 29 */
-       pps_sdp->pps_payload.nfl_bpg_offset =
+       pps_payload->nfl_bpg_offset =
                cpu_to_be16(dsc_cfg->nfl_bpg_offset);
 
        /* PPS 30, 31 */
-       pps_sdp->pps_payload.slice_bpg_offset =
+       pps_payload->slice_bpg_offset =
                cpu_to_be16(dsc_cfg->slice_bpg_offset);
 
        /* PPS 32, 33 */
-       pps_sdp->pps_payload.initial_offset =
+       pps_payload->initial_offset =
                cpu_to_be16(dsc_cfg->initial_offset);
 
        /* PPS 34, 35 */
-       pps_sdp->pps_payload.final_offset = cpu_to_be16(dsc_cfg->final_offset);
+       pps_payload->final_offset = cpu_to_be16(dsc_cfg->final_offset);
 
        /* PPS 36 */
-       pps_sdp->pps_payload.flatness_min_qp = dsc_cfg->flatness_min_qp;
+       pps_payload->flatness_min_qp = dsc_cfg->flatness_min_qp;
 
        /* PPS 37 */
-       pps_sdp->pps_payload.flatness_max_qp = dsc_cfg->flatness_max_qp;
+       pps_payload->flatness_max_qp = dsc_cfg->flatness_max_qp;
 
        /* PPS 38, 39 */
-       pps_sdp->pps_payload.rc_model_size =
+       pps_payload->rc_model_size =
                cpu_to_be16(DSC_RC_MODEL_SIZE_CONST);
 
        /* PPS 40 */
-       pps_sdp->pps_payload.rc_edge_factor = DSC_RC_EDGE_FACTOR_CONST;
+       pps_payload->rc_edge_factor = DSC_RC_EDGE_FACTOR_CONST;
 
        /* PPS 41 */
-       pps_sdp->pps_payload.rc_quant_incr_limit0 =
+       pps_payload->rc_quant_incr_limit0 =
                dsc_cfg->rc_quant_incr_limit0;
 
        /* PPS 42 */
-       pps_sdp->pps_payload.rc_quant_incr_limit1 =
+       pps_payload->rc_quant_incr_limit1 =
                dsc_cfg->rc_quant_incr_limit1;
 
        /* PPS 43 */
-       pps_sdp->pps_payload.rc_tgt_offset = DSC_RC_TGT_OFFSET_LO_CONST |
+       pps_payload->rc_tgt_offset = DSC_RC_TGT_OFFSET_LO_CONST |
                DSC_RC_TGT_OFFSET_HI_CONST << DSC_PPS_RC_TGT_OFFSET_HI_SHIFT;
 
        /* PPS 44 - 57 */
        for (i = 0; i < DSC_NUM_BUF_RANGES - 1; i++)
-               pps_sdp->pps_payload.rc_buf_thresh[i] =
+               pps_payload->rc_buf_thresh[i] =
                        dsc_cfg->rc_buf_thresh[i];
 
        /* PPS 58 - 87 */
@@ -215,32 +215,181 @@ void drm_dsc_pps_infoframe_pack(struct drm_dsc_pps_infoframe *pps_sdp,
         * are as follows: Min_qp[15:11], max_qp[10:6], offset[5:0]
         */
        for (i = 0; i < DSC_NUM_BUF_RANGES; i++) {
-               pps_sdp->pps_payload.rc_range_parameters[i] =
+               pps_payload->rc_range_parameters[i] =
                        ((dsc_cfg->rc_range_params[i].range_min_qp <<
                          DSC_PPS_RC_RANGE_MINQP_SHIFT) |
                         (dsc_cfg->rc_range_params[i].range_max_qp <<
                          DSC_PPS_RC_RANGE_MAXQP_SHIFT) |
                         (dsc_cfg->rc_range_params[i].range_bpg_offset));
-               pps_sdp->pps_payload.rc_range_parameters[i] =
-                       cpu_to_be16(pps_sdp->pps_payload.rc_range_parameters[i]);
+               pps_payload->rc_range_parameters[i] =
+                       cpu_to_be16(pps_payload->rc_range_parameters[i]);
        }
 
        /* PPS 88 */
-       pps_sdp->pps_payload.native_422_420 = dsc_cfg->native_422 |
+       pps_payload->native_422_420 = dsc_cfg->native_422 |
                dsc_cfg->native_420 << DSC_PPS_NATIVE_420_SHIFT;
 
        /* PPS 89 */
-       pps_sdp->pps_payload.second_line_bpg_offset =
+       pps_payload->second_line_bpg_offset =
                dsc_cfg->second_line_bpg_offset;
 
        /* PPS 90, 91 */
-       pps_sdp->pps_payload.nsl_bpg_offset =
+       pps_payload->nsl_bpg_offset =
                cpu_to_be16(dsc_cfg->nsl_bpg_offset);
 
        /* PPS 92, 93 */
-       pps_sdp->pps_payload.second_line_offset_adj =
+       pps_payload->second_line_offset_adj =
                cpu_to_be16(dsc_cfg->second_line_offset_adj);
 
        /* PPS 94 - 127 are O */
 }
-EXPORT_SYMBOL(drm_dsc_pps_infoframe_pack);
+EXPORT_SYMBOL(drm_dsc_pps_payload_pack);
+
+/**
+ * drm_dsc_compute_rc_parameters() - Write rate control
+ * parameters to the dsc configuration defined in
+ * &struct drm_dsc_config in accordance with the DSC 1.2
+ * specification. Some configuration fields must be present
+ * beforehand.
+ *
+ * @vdsc_cfg:
+ * DSC Configuration data partially filled by driver
+ */
+int drm_dsc_compute_rc_parameters(struct drm_dsc_config *vdsc_cfg)
+{
+       unsigned long groups_per_line = 0;
+       unsigned long groups_total = 0;
+       unsigned long num_extra_mux_bits = 0;
+       unsigned long slice_bits = 0;
+       unsigned long hrd_delay = 0;
+       unsigned long final_scale = 0;
+       unsigned long rbs_min = 0;
+
+       if (vdsc_cfg->native_420 || vdsc_cfg->native_422) {
+               /* Number of groups used to code each line of a slice */
+               groups_per_line = DIV_ROUND_UP(vdsc_cfg->slice_width / 2,
+                                              DSC_RC_PIXELS_PER_GROUP);
+
+               /* chunksize in Bytes */
+               vdsc_cfg->slice_chunk_size = DIV_ROUND_UP(vdsc_cfg->slice_width / 2 *
+                                                         vdsc_cfg->bits_per_pixel,
+                                                         (8 * 16));
+       } else {
+               /* Number of groups used to code each line of a slice */
+               groups_per_line = DIV_ROUND_UP(vdsc_cfg->slice_width,
+                                              DSC_RC_PIXELS_PER_GROUP);
+
+               /* chunksize in Bytes */
+               vdsc_cfg->slice_chunk_size = DIV_ROUND_UP(vdsc_cfg->slice_width *
+                                                         vdsc_cfg->bits_per_pixel,
+                                                         (8 * 16));
+       }
+
+       if (vdsc_cfg->convert_rgb)
+               num_extra_mux_bits = 3 * (vdsc_cfg->mux_word_size +
+                                         (4 * vdsc_cfg->bits_per_component + 4)
+                                         - 2);
+       else if (vdsc_cfg->native_422)
+               num_extra_mux_bits = 4 * vdsc_cfg->mux_word_size +
+                       (4 * vdsc_cfg->bits_per_component + 4) +
+                       3 * (4 * vdsc_cfg->bits_per_component) - 2;
+       else
+               num_extra_mux_bits = 3 * vdsc_cfg->mux_word_size +
+                       (4 * vdsc_cfg->bits_per_component + 4) +
+                       2 * (4 * vdsc_cfg->bits_per_component) - 2;
+       /* Number of bits in one Slice */
+       slice_bits = 8 * vdsc_cfg->slice_chunk_size * vdsc_cfg->slice_height;
+
+       while ((num_extra_mux_bits > 0) &&
+              ((slice_bits - num_extra_mux_bits) % vdsc_cfg->mux_word_size))
+               num_extra_mux_bits--;
+
+       if (groups_per_line < vdsc_cfg->initial_scale_value - 8)
+               vdsc_cfg->initial_scale_value = groups_per_line + 8;
+
+       /* scale_decrement_interval calculation according to DSC spec 1.11 */
+       if (vdsc_cfg->initial_scale_value > 8)
+               vdsc_cfg->scale_decrement_interval = groups_per_line /
+                       (vdsc_cfg->initial_scale_value - 8);
+       else
+               vdsc_cfg->scale_decrement_interval = DSC_SCALE_DECREMENT_INTERVAL_MAX;
+
+       vdsc_cfg->final_offset = vdsc_cfg->rc_model_size -
+               (vdsc_cfg->initial_xmit_delay *
+                vdsc_cfg->bits_per_pixel + 8) / 16 + num_extra_mux_bits;
+
+       if (vdsc_cfg->final_offset >= vdsc_cfg->rc_model_size) {
+               DRM_DEBUG_KMS("FinalOfs < RcModelSze for this InitialXmitDelay\n");
+               return -ERANGE;
+       }
+
+       final_scale = (vdsc_cfg->rc_model_size * 8) /
+               (vdsc_cfg->rc_model_size - vdsc_cfg->final_offset);
+       if (vdsc_cfg->slice_height > 1)
+               /*
+                * NflBpgOffset is 16 bit value with 11 fractional bits
+                * hence we multiply by 2^11 for preserving the
+                * fractional part
+                */
+               vdsc_cfg->nfl_bpg_offset = DIV_ROUND_UP((vdsc_cfg->first_line_bpg_offset << 11),
+                                                       (vdsc_cfg->slice_height - 1));
+       else
+               vdsc_cfg->nfl_bpg_offset = 0;
+
+       /* 2^16 - 1 */
+       if (vdsc_cfg->nfl_bpg_offset > 65535) {
+               DRM_DEBUG_KMS("NflBpgOffset is too large for this slice height\n");
+               return -ERANGE;
+       }
+
+       /* Number of groups used to code the entire slice */
+       groups_total = groups_per_line * vdsc_cfg->slice_height;
+
+       /* slice_bpg_offset is 16 bit value with 11 fractional bits */
+       vdsc_cfg->slice_bpg_offset = DIV_ROUND_UP(((vdsc_cfg->rc_model_size -
+                                                   vdsc_cfg->initial_offset +
+                                                   num_extra_mux_bits) << 11),
+                                                 groups_total);
+
+       if (final_scale > 9) {
+               /*
+                * ScaleIncrementInterval =
+                * finaloffset/((NflBpgOffset + SliceBpgOffset)*8(finalscale - 1.125))
+                * as (NflBpgOffset + SliceBpgOffset) has 11 bit fractional value,
+                * we need divide by 2^11 from pstDscCfg values
+                */
+               vdsc_cfg->scale_increment_interval =
+                               (vdsc_cfg->final_offset * (1 << 11)) /
+                               ((vdsc_cfg->nfl_bpg_offset +
+                               vdsc_cfg->slice_bpg_offset) *
+                               (final_scale - 9));
+       } else {
+               /*
+                * If finalScaleValue is less than or equal to 9, a value of 0 should
+                * be used to disable the scale increment at the end of the slice
+                */
+               vdsc_cfg->scale_increment_interval = 0;
+       }
+
+       if (vdsc_cfg->scale_increment_interval > 65535) {
+               DRM_DEBUG_KMS("ScaleIncrementInterval is large for slice height\n");
+               return -ERANGE;
+       }
+
+       /*
+        * DSC spec mentions that bits_per_pixel specifies the target
+        * bits/pixel (bpp) rate that is used by the encoder,
+        * in steps of 1/16 of a bit per pixel
+        */
+       rbs_min = vdsc_cfg->rc_model_size - vdsc_cfg->initial_offset +
+               DIV_ROUND_UP(vdsc_cfg->initial_xmit_delay *
+                            vdsc_cfg->bits_per_pixel, 16) +
+               groups_per_line * vdsc_cfg->first_line_bpg_offset;
+
+       hrd_delay = DIV_ROUND_UP((rbs_min * 16), vdsc_cfg->bits_per_pixel);
+       vdsc_cfg->rc_bits = (hrd_delay * vdsc_cfg->bits_per_pixel) / 16;
+       vdsc_cfg->initial_dec_delay = hrd_delay - vdsc_cfg->initial_xmit_delay;
+
+       return 0;
+}
+EXPORT_SYMBOL(drm_dsc_compute_rc_parameters);
index 990b190..fa39592 100644 (file)
@@ -193,6 +193,12 @@ static const struct edid_quirk {
 
        /* Sony PlayStation VR Headset */
        { "SNY", 0x0704, EDID_QUIRK_NON_DESKTOP },
+
+       /* Sensics VR Headsets */
+       { "SEN", 0x1019, EDID_QUIRK_NON_DESKTOP },
+
+       /* OSVR HDK and HDK2 VR Headsets */
+       { "SVR", 0x1019, EDID_QUIRK_NON_DESKTOP },
 };
 
 /*
@@ -4924,6 +4930,76 @@ drm_hdmi_avi_infoframe_from_display_mode(struct hdmi_avi_infoframe *frame,
 }
 EXPORT_SYMBOL(drm_hdmi_avi_infoframe_from_display_mode);
 
+/* HDMI Colorspace Spec Definitions */
+#define FULL_COLORIMETRY_MASK          0x1FF
+#define NORMAL_COLORIMETRY_MASK                0x3
+#define EXTENDED_COLORIMETRY_MASK      0x7
+#define EXTENDED_ACE_COLORIMETRY_MASK  0xF
+
+#define C(x) ((x) << 0)
+#define EC(x) ((x) << 2)
+#define ACE(x) ((x) << 5)
+
+#define HDMI_COLORIMETRY_NO_DATA               0x0
+#define HDMI_COLORIMETRY_SMPTE_170M_YCC                (C(1) | EC(0) | ACE(0))
+#define HDMI_COLORIMETRY_BT709_YCC             (C(2) | EC(0) | ACE(0))
+#define HDMI_COLORIMETRY_XVYCC_601             (C(3) | EC(0) | ACE(0))
+#define HDMI_COLORIMETRY_XVYCC_709             (C(3) | EC(1) | ACE(0))
+#define HDMI_COLORIMETRY_SYCC_601              (C(3) | EC(2) | ACE(0))
+#define HDMI_COLORIMETRY_OPYCC_601             (C(3) | EC(3) | ACE(0))
+#define HDMI_COLORIMETRY_OPRGB                 (C(3) | EC(4) | ACE(0))
+#define HDMI_COLORIMETRY_BT2020_CYCC           (C(3) | EC(5) | ACE(0))
+#define HDMI_COLORIMETRY_BT2020_RGB            (C(3) | EC(6) | ACE(0))
+#define HDMI_COLORIMETRY_BT2020_YCC            (C(3) | EC(6) | ACE(0))
+#define HDMI_COLORIMETRY_DCI_P3_RGB_D65                (C(3) | EC(7) | ACE(0))
+#define HDMI_COLORIMETRY_DCI_P3_RGB_THEATER    (C(3) | EC(7) | ACE(1))
+
+static const u32 hdmi_colorimetry_val[] = {
+       [DRM_MODE_COLORIMETRY_NO_DATA] = HDMI_COLORIMETRY_NO_DATA,
+       [DRM_MODE_COLORIMETRY_SMPTE_170M_YCC] = HDMI_COLORIMETRY_SMPTE_170M_YCC,
+       [DRM_MODE_COLORIMETRY_BT709_YCC] = HDMI_COLORIMETRY_BT709_YCC,
+       [DRM_MODE_COLORIMETRY_XVYCC_601] = HDMI_COLORIMETRY_XVYCC_601,
+       [DRM_MODE_COLORIMETRY_XVYCC_709] = HDMI_COLORIMETRY_XVYCC_709,
+       [DRM_MODE_COLORIMETRY_SYCC_601] = HDMI_COLORIMETRY_SYCC_601,
+       [DRM_MODE_COLORIMETRY_OPYCC_601] = HDMI_COLORIMETRY_OPYCC_601,
+       [DRM_MODE_COLORIMETRY_OPRGB] = HDMI_COLORIMETRY_OPRGB,
+       [DRM_MODE_COLORIMETRY_BT2020_CYCC] = HDMI_COLORIMETRY_BT2020_CYCC,
+       [DRM_MODE_COLORIMETRY_BT2020_RGB] = HDMI_COLORIMETRY_BT2020_RGB,
+       [DRM_MODE_COLORIMETRY_BT2020_YCC] = HDMI_COLORIMETRY_BT2020_YCC,
+};
+
+#undef C
+#undef EC
+#undef ACE
+
+/**
+ * drm_hdmi_avi_infoframe_colorspace() - fill the HDMI AVI infoframe
+ *                                       colorspace information
+ * @frame: HDMI AVI infoframe
+ * @conn_state: connector state
+ */
+void
+drm_hdmi_avi_infoframe_colorspace(struct hdmi_avi_infoframe *frame,
+                                 const struct drm_connector_state *conn_state)
+{
+       u32 colorimetry_val;
+       u32 colorimetry_index = conn_state->colorspace & FULL_COLORIMETRY_MASK;
+
+       if (colorimetry_index >= ARRAY_SIZE(hdmi_colorimetry_val))
+               colorimetry_val = HDMI_COLORIMETRY_NO_DATA;
+       else
+               colorimetry_val = hdmi_colorimetry_val[colorimetry_index];
+
+       frame->colorimetry = colorimetry_val & NORMAL_COLORIMETRY_MASK;
+       /*
+        * ToDo: Extend it for ACE formats as well. Modify the infoframe
+        * structure and extend it in drivers/video/hdmi
+        */
+       frame->extended_colorimetry = (colorimetry_val >> 2) &
+                                       EXTENDED_COLORIMETRY_MASK;
+}
+EXPORT_SYMBOL(drm_hdmi_avi_infoframe_colorspace);
+
 /**
  * drm_hdmi_avi_infoframe_quant_range() - fill the HDMI AVI infoframe
  *                                        quantization range information
index 0e9349f..04d23cb 100644 (file)
@@ -3024,7 +3024,8 @@ static int drm_fbdev_fb_open(struct fb_info *info, int user)
 {
        struct drm_fb_helper *fb_helper = info->par;
 
-       if (!try_module_get(fb_helper->dev->driver->fops->owner))
+       /* No need to take a ref for fbcon because it unbinds on unregister */
+       if (user && !try_module_get(fb_helper->dev->driver->fops->owner))
                return -ENODEV;
 
        return 0;
@@ -3034,7 +3035,8 @@ static int drm_fbdev_fb_release(struct fb_info *info, int user)
 {
        struct drm_fb_helper *fb_helper = info->par;
 
-       module_put(fb_helper->dev->driver->fops->owner);
+       if (user)
+               module_put(fb_helper->dev->driver->fops->owner);
 
        return 0;
 }
index 83a5bbc..9701469 100644 (file)
@@ -489,11 +489,9 @@ int drm_release(struct inode *inode, struct file *filp)
 
        drm_close_helper(filp);
 
-       if (!--dev->open_count) {
+       if (!--dev->open_count)
                drm_lastclose(dev);
-               if (drm_dev_is_unplugged(dev))
-                       drm_put_dev(dev);
-       }
+
        mutex_unlock(&drm_global_mutex);
 
        drm_minor_release(minor);
@@ -579,6 +577,7 @@ put_back_event:
                                file_priv->event_space -= length;
                                list_add(&e->link, &file_priv->event_list);
                                spin_unlock_irq(&dev->event_lock);
+                               wake_up_interruptible(&file_priv->event_wait);
                                break;
                        }
 
index ba7e19d..6ea55fb 100644 (file)
@@ -198,6 +198,10 @@ const struct drm_format_info *__drm_format_info(u32 format)
                { .format = DRM_FORMAT_ABGR8888,        .depth = 32, .num_planes = 1, .cpp = { 4, 0, 0 }, .hsub = 1, .vsub = 1, .has_alpha = true },
                { .format = DRM_FORMAT_RGBA8888,        .depth = 32, .num_planes = 1, .cpp = { 4, 0, 0 }, .hsub = 1, .vsub = 1, .has_alpha = true },
                { .format = DRM_FORMAT_BGRA8888,        .depth = 32, .num_planes = 1, .cpp = { 4, 0, 0 }, .hsub = 1, .vsub = 1, .has_alpha = true },
+               { .format = DRM_FORMAT_XRGB16161616F,   .depth = 0,  .num_planes = 1, .cpp = { 8, 0, 0 }, .hsub = 1, .vsub = 1 },
+               { .format = DRM_FORMAT_XBGR16161616F,   .depth = 0,  .num_planes = 1, .cpp = { 8, 0, 0 }, .hsub = 1, .vsub = 1 },
+               { .format = DRM_FORMAT_ARGB16161616F,   .depth = 0,  .num_planes = 1, .cpp = { 8, 0, 0 }, .hsub = 1, .vsub = 1, .has_alpha = true },
+               { .format = DRM_FORMAT_ABGR16161616F,   .depth = 0,  .num_planes = 1, .cpp = { 8, 0, 0 }, .hsub = 1, .vsub = 1, .has_alpha = true },
                { .format = DRM_FORMAT_RGB888_A8,       .depth = 32, .num_planes = 2, .cpp = { 3, 1, 0 }, .hsub = 1, .vsub = 1, .has_alpha = true },
                { .format = DRM_FORMAT_BGR888_A8,       .depth = 32, .num_planes = 2, .cpp = { 3, 1, 0 }, .hsub = 1, .vsub = 1, .has_alpha = true },
                { .format = DRM_FORMAT_XRGB8888_A8,     .depth = 32, .num_planes = 2, .cpp = { 4, 1, 0 }, .hsub = 1, .vsub = 1, .has_alpha = true },
@@ -225,7 +229,17 @@ const struct drm_format_info *__drm_format_info(u32 format)
                { .format = DRM_FORMAT_UYVY,            .depth = 0,  .num_planes = 1, .cpp = { 2, 0, 0 }, .hsub = 2, .vsub = 1, .is_yuv = true },
                { .format = DRM_FORMAT_VYUY,            .depth = 0,  .num_planes = 1, .cpp = { 2, 0, 0 }, .hsub = 2, .vsub = 1, .is_yuv = true },
                { .format = DRM_FORMAT_XYUV8888,        .depth = 0,  .num_planes = 1, .cpp = { 4, 0, 0 }, .hsub = 1, .vsub = 1, .is_yuv = true },
+               { .format = DRM_FORMAT_VUY888,          .depth = 0,  .num_planes = 1, .cpp = { 3, 0, 0 }, .hsub = 1, .vsub = 1, .is_yuv = true },
                { .format = DRM_FORMAT_AYUV,            .depth = 0,  .num_planes = 1, .cpp = { 4, 0, 0 }, .hsub = 1, .vsub = 1, .has_alpha = true, .is_yuv = true },
+               { .format = DRM_FORMAT_Y210,            .depth = 0,  .num_planes = 1, .cpp = { 4, 0, 0 }, .hsub = 2, .vsub = 1, .is_yuv = true },
+               { .format = DRM_FORMAT_Y212,            .depth = 0,  .num_planes = 1, .cpp = { 4, 0, 0 }, .hsub = 2, .vsub = 1, .is_yuv = true },
+               { .format = DRM_FORMAT_Y216,            .depth = 0,  .num_planes = 1, .cpp = { 4, 0, 0 }, .hsub = 2, .vsub = 1, .is_yuv = true },
+               { .format = DRM_FORMAT_Y410,            .depth = 0,  .num_planes = 1, .cpp = { 4, 0, 0 }, .hsub = 1, .vsub = 1, .has_alpha = true, .is_yuv = true },
+               { .format = DRM_FORMAT_Y412,            .depth = 0,  .num_planes = 1, .cpp = { 8, 0, 0 }, .hsub = 1, .vsub = 1, .has_alpha = true, .is_yuv = true },
+               { .format = DRM_FORMAT_Y416,            .depth = 0,  .num_planes = 1, .cpp = { 8, 0, 0 }, .hsub = 1, .vsub = 1, .has_alpha = true, .is_yuv = true },
+               { .format = DRM_FORMAT_XVYU2101010,     .depth = 0,  .num_planes = 1, .cpp = { 4, 0, 0 }, .hsub = 1, .vsub = 1, .is_yuv = true },
+               { .format = DRM_FORMAT_XVYU12_16161616, .depth = 0,  .num_planes = 1, .cpp = { 8, 0, 0 }, .hsub = 1, .vsub = 1, .is_yuv = true },
+               { .format = DRM_FORMAT_XVYU16161616,    .depth = 0,  .num_planes = 1, .cpp = { 8, 0, 0 }, .hsub = 1, .vsub = 1, .is_yuv = true },
                { .format = DRM_FORMAT_Y0L0,            .depth = 0,  .num_planes = 1,
                  .char_per_block = { 8, 0, 0 }, .block_w = { 2, 0, 0 }, .block_h = { 2, 0, 0 },
                  .hsub = 2, .vsub = 2, .has_alpha = true, .is_yuv = true },
@@ -247,6 +261,19 @@ const struct drm_format_info *__drm_format_info(u32 format)
                { .format = DRM_FORMAT_P016,            .depth = 0,  .num_planes = 2,
                  .char_per_block = { 2, 4, 0 }, .block_w = { 1, 0, 0 }, .block_h = { 1, 0, 0 },
                  .hsub = 2, .vsub = 2, .is_yuv = true},
+               { .format = DRM_FORMAT_P210,            .depth = 0,
+                 .num_planes = 2, .char_per_block = { 2, 4, 0 },
+                 .block_w = { 1, 0, 0 }, .block_h = { 1, 0, 0 }, .hsub = 2,
+                 .vsub = 1, .is_yuv = true },
+               { .format = DRM_FORMAT_VUY101010,       .depth = 0,
+                 .num_planes = 1, .cpp = { 0, 0, 0 }, .hsub = 1, .vsub = 1,
+                 .is_yuv = true },
+               { .format = DRM_FORMAT_YUV420_8BIT,     .depth = 0,
+                 .num_planes = 1, .cpp = { 0, 0, 0 }, .hsub = 2, .vsub = 2,
+                 .is_yuv = true },
+               { .format = DRM_FORMAT_YUV420_10BIT,    .depth = 0,
+                 .num_planes = 1, .cpp = { 0, 0, 0 }, .hsub = 2, .vsub = 2,
+                 .is_yuv = true },
        };
 
        unsigned int i;
index d0b9f6a..388b374 100644 (file)
@@ -171,6 +171,10 @@ void drm_gem_private_object_init(struct drm_device *dev,
        kref_init(&obj->refcount);
        obj->handle_count = 0;
        obj->size = size;
+       reservation_object_init(&obj->_resv);
+       if (!obj->resv)
+               obj->resv = &obj->_resv;
+
        drm_vma_node_reset(&obj->vma_node);
 }
 EXPORT_SYMBOL(drm_gem_private_object_init);
@@ -688,6 +692,44 @@ drm_gem_object_lookup(struct drm_file *filp, u32 handle)
 EXPORT_SYMBOL(drm_gem_object_lookup);
 
 /**
+ * drm_gem_reservation_object_wait - Wait on GEM object's reservation's objects
+ * shared and/or exclusive fences.
+ * @filep: DRM file private date
+ * @handle: userspace handle
+ * @wait_all: if true, wait on all fences, else wait on just exclusive fence
+ * @timeout: timeout value in jiffies or zero to return immediately
+ *
+ * Returns:
+ *
+ * Returns -ERESTARTSYS if interrupted, 0 if the wait timed out, or
+ * greater than 0 on success.
+ */
+long drm_gem_reservation_object_wait(struct drm_file *filep, u32 handle,
+                                   bool wait_all, unsigned long timeout)
+{
+       long ret;
+       struct drm_gem_object *obj;
+
+       obj = drm_gem_object_lookup(filep, handle);
+       if (!obj) {
+               DRM_DEBUG("Failed to look up GEM BO %d\n", handle);
+               return -EINVAL;
+       }
+
+       ret = reservation_object_wait_timeout_rcu(obj->resv, wait_all,
+                                                 true, timeout);
+       if (ret == 0)
+               ret = -ETIME;
+       else if (ret > 0)
+               ret = 0;
+
+       drm_gem_object_put_unlocked(obj);
+
+       return ret;
+}
+EXPORT_SYMBOL(drm_gem_reservation_object_wait);
+
+/**
  * drm_gem_close_ioctl - implementation of the GEM_CLOSE ioctl
  * @dev: drm_device
  * @data: ioctl data
@@ -851,6 +893,7 @@ drm_gem_object_release(struct drm_gem_object *obj)
        if (obj->filp)
                fput(obj->filp);
 
+       reservation_object_fini(&obj->_resv);
        drm_gem_free_mmap_offset(obj);
 }
 EXPORT_SYMBOL(drm_gem_object_release);
@@ -1190,3 +1233,81 @@ void drm_gem_vunmap(struct drm_gem_object *obj, void *vaddr)
                obj->dev->driver->gem_prime_vunmap(obj, vaddr);
 }
 EXPORT_SYMBOL(drm_gem_vunmap);
+
+/**
+ * drm_gem_lock_reservations - Sets up the ww context and acquires
+ * the lock on an array of GEM objects.
+ *
+ * Once you've locked your reservations, you'll want to set up space
+ * for your shared fences (if applicable), submit your job, then
+ * drm_gem_unlock_reservations().
+ *
+ * @objs: drm_gem_objects to lock
+ * @count: Number of objects in @objs
+ * @acquire_ctx: struct ww_acquire_ctx that will be initialized as
+ * part of tracking this set of locked reservations.
+ */
+int
+drm_gem_lock_reservations(struct drm_gem_object **objs, int count,
+                         struct ww_acquire_ctx *acquire_ctx)
+{
+       int contended = -1;
+       int i, ret;
+
+       ww_acquire_init(acquire_ctx, &reservation_ww_class);
+
+retry:
+       if (contended != -1) {
+               struct drm_gem_object *obj = objs[contended];
+
+               ret = ww_mutex_lock_slow_interruptible(&obj->resv->lock,
+                                                      acquire_ctx);
+               if (ret) {
+                       ww_acquire_done(acquire_ctx);
+                       return ret;
+               }
+       }
+
+       for (i = 0; i < count; i++) {
+               if (i == contended)
+                       continue;
+
+               ret = ww_mutex_lock_interruptible(&objs[i]->resv->lock,
+                                                 acquire_ctx);
+               if (ret) {
+                       int j;
+
+                       for (j = 0; j < i; j++)
+                               ww_mutex_unlock(&objs[j]->resv->lock);
+
+                       if (contended != -1 && contended >= i)
+                               ww_mutex_unlock(&objs[contended]->resv->lock);
+
+                       if (ret == -EDEADLK) {
+                               contended = i;
+                               goto retry;
+                       }
+
+                       ww_acquire_done(acquire_ctx);
+                       return ret;
+               }
+       }
+
+       ww_acquire_done(acquire_ctx);
+
+       return 0;
+}
+EXPORT_SYMBOL(drm_gem_lock_reservations);
+
+void
+drm_gem_unlock_reservations(struct drm_gem_object **objs, int count,
+                           struct ww_acquire_ctx *acquire_ctx)
+{
+       int i;
+
+       for (i = 0; i < count; i++)
+               ww_mutex_unlock(&objs[i]->resv->lock);
+
+       ww_acquire_fini(acquire_ctx);
+}
+EXPORT_SYMBOL(drm_gem_unlock_reservations);
diff --git a/drivers/gpu/drm/drm_gem_shmem_helper.c b/drivers/gpu/drm/drm_gem_shmem_helper.c
new file mode 100644 (file)
index 0000000..3750a98
--- /dev/null
@@ -0,0 +1,625 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright 2018 Noralf Trønnes
+ */
+
+#include <linux/dma-buf.h>
+#include <linux/export.h>
+#include <linux/mutex.h>
+#include <linux/shmem_fs.h>
+#include <linux/slab.h>
+#include <linux/vmalloc.h>
+
+#include <drm/drm_device.h>
+#include <drm/drm_drv.h>
+#include <drm/drm_gem_shmem_helper.h>
+#include <drm/drm_prime.h>
+#include <drm/drm_print.h>
+
+/**
+ * DOC: overview
+ *
+ * This library provides helpers for GEM objects backed by shmem buffers
+ * allocated using anonymous pageable memory.
+ */
+
+static const struct drm_gem_object_funcs drm_gem_shmem_funcs = {
+       .free = drm_gem_shmem_free_object,
+       .print_info = drm_gem_shmem_print_info,
+       .pin = drm_gem_shmem_pin,
+       .unpin = drm_gem_shmem_unpin,
+       .get_sg_table = drm_gem_shmem_get_sg_table,
+       .vmap = drm_gem_shmem_vmap,
+       .vunmap = drm_gem_shmem_vunmap,
+       .vm_ops = &drm_gem_shmem_vm_ops,
+};
+
+/**
+ * drm_gem_shmem_create - Allocate an object with the given size
+ * @dev: DRM device
+ * @size: Size of the object to allocate
+ *
+ * This function creates a shmem GEM object.
+ *
+ * Returns:
+ * A struct drm_gem_shmem_object * on success or an ERR_PTR()-encoded negative
+ * error code on failure.
+ */
+struct drm_gem_shmem_object *drm_gem_shmem_create(struct drm_device *dev, size_t size)
+{
+       struct drm_gem_shmem_object *shmem;
+       struct drm_gem_object *obj;
+       int ret;
+
+       size = PAGE_ALIGN(size);
+
+       if (dev->driver->gem_create_object)
+               obj = dev->driver->gem_create_object(dev, size);
+       else
+               obj = kzalloc(sizeof(*shmem), GFP_KERNEL);
+       if (!obj)
+               return ERR_PTR(-ENOMEM);
+
+       if (!obj->funcs)
+               obj->funcs = &drm_gem_shmem_funcs;
+
+       ret = drm_gem_object_init(dev, obj, size);
+       if (ret)
+               goto err_free;
+
+       ret = drm_gem_create_mmap_offset(obj);
+       if (ret)
+               goto err_release;
+
+       shmem = to_drm_gem_shmem_obj(obj);
+       mutex_init(&shmem->pages_lock);
+       mutex_init(&shmem->vmap_lock);
+
+       /*
+        * Our buffers are kept pinned, so allocating them
+        * from the MOVABLE zone is a really bad idea, and
+        * conflicts with CMA. See comments above new_inode()
+        * why this is required _and_ expected if you're
+        * going to pin these pages.
+        */
+       mapping_set_gfp_mask(obj->filp->f_mapping, GFP_HIGHUSER |
+                            __GFP_RETRY_MAYFAIL | __GFP_NOWARN);
+
+       return shmem;
+
+err_release:
+       drm_gem_object_release(obj);
+err_free:
+       kfree(obj);
+
+       return ERR_PTR(ret);
+}
+EXPORT_SYMBOL_GPL(drm_gem_shmem_create);
+
+/**
+ * drm_gem_shmem_free_object - Free resources associated with a shmem GEM object
+ * @obj: GEM object to free
+ *
+ * This function cleans up the GEM object state and frees the memory used to
+ * store the object itself.
+ */
+void drm_gem_shmem_free_object(struct drm_gem_object *obj)
+{
+       struct drm_gem_shmem_object *shmem = to_drm_gem_shmem_obj(obj);
+
+       WARN_ON(shmem->vmap_use_count);
+
+       if (obj->import_attach) {
+               shmem->pages_use_count--;
+               drm_prime_gem_destroy(obj, shmem->sgt);
+               kvfree(shmem->pages);
+       } else {
+               if (shmem->sgt) {
+                       dma_unmap_sg(obj->dev->dev, shmem->sgt->sgl,
+                                    shmem->sgt->nents, DMA_BIDIRECTIONAL);
+
+                       drm_gem_shmem_put_pages(shmem);
+                       sg_free_table(shmem->sgt);
+                       kfree(shmem->sgt);
+               }
+       }
+
+       WARN_ON(shmem->pages_use_count);
+
+       drm_gem_object_release(obj);
+       mutex_destroy(&shmem->pages_lock);
+       mutex_destroy(&shmem->vmap_lock);
+       kfree(shmem);
+}
+EXPORT_SYMBOL_GPL(drm_gem_shmem_free_object);
+
+static int drm_gem_shmem_get_pages_locked(struct drm_gem_shmem_object *shmem)
+{
+       struct drm_gem_object *obj = &shmem->base;
+       struct page **pages;
+
+       if (shmem->pages_use_count++ > 0)
+               return 0;
+
+       pages = drm_gem_get_pages(obj);
+       if (IS_ERR(pages)) {
+               DRM_DEBUG_KMS("Failed to get pages (%ld)\n", PTR_ERR(pages));
+               shmem->pages_use_count = 0;
+               return PTR_ERR(pages);
+       }
+
+       shmem->pages = pages;
+
+       return 0;
+}
+
+/*
+ * drm_gem_shmem_get_pages - Allocate backing pages for a shmem GEM object
+ * @shmem: shmem GEM object
+ *
+ * This function makes sure that backing pages exists for the shmem GEM object
+ * and increases the use count.
+ *
+ * Returns:
+ * 0 on success or a negative error code on failure.
+ */
+int drm_gem_shmem_get_pages(struct drm_gem_shmem_object *shmem)
+{
+       int ret;
+
+       ret = mutex_lock_interruptible(&shmem->pages_lock);
+       if (ret)
+               return ret;
+       ret = drm_gem_shmem_get_pages_locked(shmem);
+       mutex_unlock(&shmem->pages_lock);
+
+       return ret;
+}
+EXPORT_SYMBOL(drm_gem_shmem_get_pages);
+
+static void drm_gem_shmem_put_pages_locked(struct drm_gem_shmem_object *shmem)
+{
+       struct drm_gem_object *obj = &shmem->base;
+
+       if (WARN_ON_ONCE(!shmem->pages_use_count))
+               return;
+
+       if (--shmem->pages_use_count > 0)
+               return;
+
+       drm_gem_put_pages(obj, shmem->pages,
+                         shmem->pages_mark_dirty_on_put,
+                         shmem->pages_mark_accessed_on_put);
+       shmem->pages = NULL;
+}
+
+/*
+ * drm_gem_shmem_put_pages - Decrease use count on the backing pages for a shmem GEM object
+ * @shmem: shmem GEM object
+ *
+ * This function decreases the use count and puts the backing pages when use drops to zero.
+ */
+void drm_gem_shmem_put_pages(struct drm_gem_shmem_object *shmem)
+{
+       mutex_lock(&shmem->pages_lock);
+       drm_gem_shmem_put_pages_locked(shmem);
+       mutex_unlock(&shmem->pages_lock);
+}
+EXPORT_SYMBOL(drm_gem_shmem_put_pages);
+
+/**
+ * drm_gem_shmem_pin - Pin backing pages for a shmem GEM object
+ * @obj: GEM object
+ *
+ * This function makes sure the backing pages are pinned in memory while the
+ * buffer is exported.
+ *
+ * Returns:
+ * 0 on success or a negative error code on failure.
+ */
+int drm_gem_shmem_pin(struct drm_gem_object *obj)
+{
+       struct drm_gem_shmem_object *shmem = to_drm_gem_shmem_obj(obj);
+
+       return drm_gem_shmem_get_pages(shmem);
+}
+EXPORT_SYMBOL(drm_gem_shmem_pin);
+
+/**
+ * drm_gem_shmem_unpin - Unpin backing pages for a shmem GEM object
+ * @obj: GEM object
+ *
+ * This function removes the requirement that the backing pages are pinned in
+ * memory.
+ */
+void drm_gem_shmem_unpin(struct drm_gem_object *obj)
+{
+       struct drm_gem_shmem_object *shmem = to_drm_gem_shmem_obj(obj);
+
+       drm_gem_shmem_put_pages(shmem);
+}
+EXPORT_SYMBOL(drm_gem_shmem_unpin);
+
+static void *drm_gem_shmem_vmap_locked(struct drm_gem_shmem_object *shmem)
+{
+       struct drm_gem_object *obj = &shmem->base;
+       int ret;
+
+       if (shmem->vmap_use_count++ > 0)
+               return shmem->vaddr;
+
+       ret = drm_gem_shmem_get_pages(shmem);
+       if (ret)
+               goto err_zero_use;
+
+       if (obj->import_attach)
+               shmem->vaddr = dma_buf_vmap(obj->import_attach->dmabuf);
+       else
+               shmem->vaddr = vmap(shmem->pages, obj->size >> PAGE_SHIFT, VM_MAP, PAGE_KERNEL);
+
+       if (!shmem->vaddr) {
+               DRM_DEBUG_KMS("Failed to vmap pages\n");
+               ret = -ENOMEM;
+               goto err_put_pages;
+       }
+
+       return shmem->vaddr;
+
+err_put_pages:
+       drm_gem_shmem_put_pages(shmem);
+err_zero_use:
+       shmem->vmap_use_count = 0;
+
+       return ERR_PTR(ret);
+}
+
+/*
+ * drm_gem_shmem_vmap - Create a virtual mapping for a shmem GEM object
+ * @shmem: shmem GEM object
+ *
+ * This function makes sure that a virtual address exists for the buffer backing
+ * the shmem GEM object.
+ *
+ * Returns:
+ * 0 on success or a negative error code on failure.
+ */
+void *drm_gem_shmem_vmap(struct drm_gem_object *obj)
+{
+       struct drm_gem_shmem_object *shmem = to_drm_gem_shmem_obj(obj);
+       void *vaddr;
+       int ret;
+
+       ret = mutex_lock_interruptible(&shmem->vmap_lock);
+       if (ret)
+               return ERR_PTR(ret);
+       vaddr = drm_gem_shmem_vmap_locked(shmem);
+       mutex_unlock(&shmem->vmap_lock);
+
+       return vaddr;
+}
+EXPORT_SYMBOL(drm_gem_shmem_vmap);
+
+static void drm_gem_shmem_vunmap_locked(struct drm_gem_shmem_object *shmem)
+{
+       struct drm_gem_object *obj = &shmem->base;
+
+       if (WARN_ON_ONCE(!shmem->vmap_use_count))
+               return;
+
+       if (--shmem->vmap_use_count > 0)
+               return;
+
+       if (obj->import_attach)
+               dma_buf_vunmap(obj->import_attach->dmabuf, shmem->vaddr);
+       else
+               vunmap(shmem->vaddr);
+
+       shmem->vaddr = NULL;
+       drm_gem_shmem_put_pages(shmem);
+}
+
+/*
+ * drm_gem_shmem_vunmap - Unmap a virtual mapping fo a shmem GEM object
+ * @shmem: shmem GEM object
+ *
+ * This function removes the virtual address when use count drops to zero.
+ */
+void drm_gem_shmem_vunmap(struct drm_gem_object *obj, void *vaddr)
+{
+       struct drm_gem_shmem_object *shmem = to_drm_gem_shmem_obj(obj);
+
+       mutex_lock(&shmem->vmap_lock);
+       drm_gem_shmem_vunmap_locked(shmem);
+       mutex_unlock(&shmem->vmap_lock);
+}
+EXPORT_SYMBOL(drm_gem_shmem_vunmap);
+
+struct drm_gem_shmem_object *
+drm_gem_shmem_create_with_handle(struct drm_file *file_priv,
+                                struct drm_device *dev, size_t size,
+                                uint32_t *handle)
+{
+       struct drm_gem_shmem_object *shmem;
+       int ret;
+
+       shmem = drm_gem_shmem_create(dev, size);
+       if (IS_ERR(shmem))
+               return shmem;
+
+       /*
+        * Allocate an id of idr table where the obj is registered
+        * and handle has the id what user can see.
+        */
+       ret = drm_gem_handle_create(file_priv, &shmem->base, handle);
+       /* drop reference from allocate - handle holds it now. */
+       drm_gem_object_put_unlocked(&shmem->base);
+       if (ret)
+               return ERR_PTR(ret);
+
+       return shmem;
+}
+EXPORT_SYMBOL(drm_gem_shmem_create_with_handle);
+
+/**
+ * drm_gem_shmem_dumb_create - Create a dumb shmem buffer object
+ * @file: DRM file structure to create the dumb buffer for
+ * @dev: DRM device
+ * @args: IOCTL data
+ *
+ * This function computes the pitch of the dumb buffer and rounds it up to an
+ * integer number of bytes per pixel. Drivers for hardware that doesn't have
+ * any additional restrictions on the pitch can directly use this function as
+ * their &drm_driver.dumb_create callback.
+ *
+ * For hardware with additional restrictions, drivers can adjust the fields
+ * set up by userspace before calling into this function.
+ *
+ * Returns:
+ * 0 on success or a negative error code on failure.
+ */
+int drm_gem_shmem_dumb_create(struct drm_file *file, struct drm_device *dev,
+                             struct drm_mode_create_dumb *args)
+{
+       u32 min_pitch = DIV_ROUND_UP(args->width * args->bpp, 8);
+       struct drm_gem_shmem_object *shmem;
+
+       if (!args->pitch || !args->size) {
+               args->pitch = min_pitch;
+               args->size = args->pitch * args->height;
+       } else {
+               /* ensure sane minimum values */
+               if (args->pitch < min_pitch)
+                       args->pitch = min_pitch;
+               if (args->size < args->pitch * args->height)
+                       args->size = args->pitch * args->height;
+       }
+
+       shmem = drm_gem_shmem_create_with_handle(file, dev, args->size, &args->handle);
+
+       return PTR_ERR_OR_ZERO(shmem);
+}
+EXPORT_SYMBOL_GPL(drm_gem_shmem_dumb_create);
+
+static vm_fault_t drm_gem_shmem_fault(struct vm_fault *vmf)
+{
+       struct vm_area_struct *vma = vmf->vma;
+       struct drm_gem_object *obj = vma->vm_private_data;
+       struct drm_gem_shmem_object *shmem = to_drm_gem_shmem_obj(obj);
+       loff_t num_pages = obj->size >> PAGE_SHIFT;
+       struct page *page;
+
+       if (vmf->pgoff > num_pages || WARN_ON_ONCE(!shmem->pages))
+               return VM_FAULT_SIGBUS;
+
+       page = shmem->pages[vmf->pgoff];
+
+       return vmf_insert_page(vma, vmf->address, page);
+}
+
+static void drm_gem_shmem_vm_open(struct vm_area_struct *vma)
+{
+       struct drm_gem_object *obj = vma->vm_private_data;
+       struct drm_gem_shmem_object *shmem = to_drm_gem_shmem_obj(obj);
+       int ret;
+
+       ret = drm_gem_shmem_get_pages(shmem);
+       WARN_ON_ONCE(ret != 0);
+
+       drm_gem_vm_open(vma);
+}
+
+static void drm_gem_shmem_vm_close(struct vm_area_struct *vma)
+{
+       struct drm_gem_object *obj = vma->vm_private_data;
+       struct drm_gem_shmem_object *shmem = to_drm_gem_shmem_obj(obj);
+
+       drm_gem_shmem_put_pages(shmem);
+       drm_gem_vm_close(vma);
+}
+
+const struct vm_operations_struct drm_gem_shmem_vm_ops = {
+       .fault = drm_gem_shmem_fault,
+       .open = drm_gem_shmem_vm_open,
+       .close = drm_gem_shmem_vm_close,
+};
+EXPORT_SYMBOL_GPL(drm_gem_shmem_vm_ops);
+
+/**
+ * drm_gem_shmem_mmap - Memory-map a shmem GEM object
+ * @filp: File object
+ * @vma: VMA for the area to be mapped
+ *
+ * This function implements an augmented version of the GEM DRM file mmap
+ * operation for shmem objects. Drivers which employ the shmem helpers should
+ * use this function as their &file_operations.mmap handler in the DRM device file's
+ * file_operations structure.
+ *
+ * Instead of directly referencing this function, drivers should use the
+ * DEFINE_DRM_GEM_SHMEM_FOPS() macro.
+ *
+ * Returns:
+ * 0 on success or a negative error code on failure.
+ */
+int drm_gem_shmem_mmap(struct file *filp, struct vm_area_struct *vma)
+{
+       struct drm_gem_shmem_object *shmem;
+       int ret;
+
+       ret = drm_gem_mmap(filp, vma);
+       if (ret)
+               return ret;
+
+       shmem = to_drm_gem_shmem_obj(vma->vm_private_data);
+
+       ret = drm_gem_shmem_get_pages(shmem);
+       if (ret) {
+               drm_gem_vm_close(vma);
+               return ret;
+       }
+
+       /* VM_PFNMAP was set by drm_gem_mmap() */
+       vma->vm_flags &= ~VM_PFNMAP;
+       vma->vm_flags |= VM_MIXEDMAP;
+
+       /* Remove the fake offset */
+       vma->vm_pgoff -= drm_vma_node_start(&shmem->base.vma_node);
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(drm_gem_shmem_mmap);
+
+/**
+ * drm_gem_shmem_print_info() - Print &drm_gem_shmem_object info for debugfs
+ * @p: DRM printer
+ * @indent: Tab indentation level
+ * @obj: GEM object
+ */
+void drm_gem_shmem_print_info(struct drm_printer *p, unsigned int indent,
+                             const struct drm_gem_object *obj)
+{
+       const struct drm_gem_shmem_object *shmem = to_drm_gem_shmem_obj(obj);
+
+       drm_printf_indent(p, indent, "pages_use_count=%u\n", shmem->pages_use_count);
+       drm_printf_indent(p, indent, "vmap_use_count=%u\n", shmem->vmap_use_count);
+       drm_printf_indent(p, indent, "vaddr=%p\n", shmem->vaddr);
+}
+EXPORT_SYMBOL(drm_gem_shmem_print_info);
+
+/**
+ * drm_gem_shmem_get_sg_table - Provide a scatter/gather table of pinned
+ *                              pages for a shmem GEM object
+ * @obj: GEM object
+ *
+ * This function exports a scatter/gather table suitable for PRIME usage by
+ * calling the standard DMA mapping API.
+ *
+ * Returns:
+ * A pointer to the scatter/gather table of pinned pages or NULL on failure.
+ */
+struct sg_table *drm_gem_shmem_get_sg_table(struct drm_gem_object *obj)
+{
+       struct drm_gem_shmem_object *shmem = to_drm_gem_shmem_obj(obj);
+
+       return drm_prime_pages_to_sg(shmem->pages, obj->size >> PAGE_SHIFT);
+}
+EXPORT_SYMBOL_GPL(drm_gem_shmem_get_sg_table);
+
+/**
+ * drm_gem_shmem_get_pages_sgt - Pin pages, dma map them, and return a
+ *                              scatter/gather table for a shmem GEM object.
+ * @obj: GEM object
+ *
+ * This function returns a scatter/gather table suitable for driver usage. If
+ * the sg table doesn't exist, the pages are pinned, dma-mapped, and a sg
+ * table created.
+ *
+ * Returns:
+ * A pointer to the scatter/gather table of pinned pages or errno on failure.
+ */
+struct sg_table *drm_gem_shmem_get_pages_sgt(struct drm_gem_object *obj)
+{
+       int ret;
+       struct drm_gem_shmem_object *shmem = to_drm_gem_shmem_obj(obj);
+       struct sg_table *sgt;
+
+       if (shmem->sgt)
+               return shmem->sgt;
+
+       WARN_ON(obj->import_attach);
+
+       ret = drm_gem_shmem_get_pages(shmem);
+       if (ret)
+               return ERR_PTR(ret);
+
+       sgt = drm_gem_shmem_get_sg_table(&shmem->base);
+       if (IS_ERR(sgt)) {
+               ret = PTR_ERR(sgt);
+               goto err_put_pages;
+       }
+       /* Map the pages for use by the h/w. */
+       dma_map_sg(obj->dev->dev, sgt->sgl, sgt->nents, DMA_BIDIRECTIONAL);
+
+       shmem->sgt = sgt;
+
+       return sgt;
+
+err_put_pages:
+       drm_gem_shmem_put_pages(shmem);
+       return ERR_PTR(ret);
+}
+EXPORT_SYMBOL_GPL(drm_gem_shmem_get_pages_sgt);
+
+/**
+ * drm_gem_shmem_prime_import_sg_table - Produce a shmem GEM object from
+ *                 another driver's scatter/gather table of pinned pages
+ * @dev: Device to import into
+ * @attach: DMA-BUF attachment
+ * @sgt: Scatter/gather table of pinned pages
+ *
+ * This function imports a scatter/gather table exported via DMA-BUF by
+ * another driver. Drivers that use the shmem helpers should set this as their
+ * &drm_driver.gem_prime_import_sg_table callback.
+ *
+ * Returns:
+ * A pointer to a newly created GEM object or an ERR_PTR-encoded negative
+ * error code on failure.
+ */
+struct drm_gem_object *
+drm_gem_shmem_prime_import_sg_table(struct drm_device *dev,
+                                   struct dma_buf_attachment *attach,
+                                   struct sg_table *sgt)
+{
+       size_t size = PAGE_ALIGN(attach->dmabuf->size);
+       size_t npages = size >> PAGE_SHIFT;
+       struct drm_gem_shmem_object *shmem;
+       int ret;
+
+       shmem = drm_gem_shmem_create(dev, size);
+       if (IS_ERR(shmem))
+               return ERR_CAST(shmem);
+
+       shmem->pages = kvmalloc_array(npages, sizeof(struct page *), GFP_KERNEL);
+       if (!shmem->pages) {
+               ret = -ENOMEM;
+               goto err_free_gem;
+       }
+
+       ret = drm_prime_sg_to_page_addr_arrays(sgt, shmem->pages, NULL, npages);
+       if (ret < 0)
+               goto err_free_array;
+
+       shmem->sgt = sgt;
+       shmem->pages_use_count = 1; /* Permanently pinned from our point of view */
+
+       DRM_DEBUG_PRIME("size = %zu\n", size);
+
+       return &shmem->base;
+
+err_free_array:
+       kvfree(shmem->pages);
+err_free_gem:
+       drm_gem_object_put_unlocked(&shmem->base);
+
+       return ERR_PTR(ret);
+}
+EXPORT_SYMBOL_GPL(drm_gem_shmem_prime_import_sg_table);
index 93e2b30..9c5ae82 100644 (file)
@@ -39,7 +39,7 @@ MODULE_LICENSE("GPL and additional rights");
 /* Backward compatibility for drm_kms_helper.edid_firmware */
 static int edid_firmware_set(const char *val, const struct kernel_param *kp)
 {
-       DRM_NOTE("drm_kms_firmware.edid_firmware is deprecated, please use drm.edid_firmware intead.\n");
+       DRM_NOTE("drm_kms_firmware.edid_firmware is deprecated, please use drm.edid_firmware instead.\n");
 
        return __drm_set_edid_firmware_path(val);
 }
index 40c4349..8dbcdc7 100644 (file)
@@ -35,6 +35,7 @@
 
 #include <linux/highmem.h>
 #include <linux/export.h>
+#include <xen/xen.h>
 #include <drm/drmP.h>
 #include "drm_legacy.h"
 
@@ -150,15 +151,27 @@ void drm_legacy_ioremapfree(struct drm_local_map *map, struct drm_device *dev)
 }
 EXPORT_SYMBOL(drm_legacy_ioremapfree);
 
-u64 drm_get_max_iomem(void)
+bool drm_need_swiotlb(int dma_bits)
 {
        struct resource *tmp;
        resource_size_t max_iomem = 0;
 
+       /*
+        * Xen paravirtual hosts require swiotlb regardless of requested dma
+        * transfer size.
+        *
+        * NOTE: Really, what it requires is use of the dma_alloc_coherent
+        *       allocator used in ttm_dma_populate() instead of
+        *       ttm_populate_and_map_pages(), which bounce buffers so much in
+        *       Xen it leads to swiotlb buffer exhaustion.
+        */
+       if (xen_pv_domain())
+               return true;
+
        for (tmp = iomem_resource.child; tmp; tmp = tmp->sibling) {
                max_iomem = max(max_iomem,  tmp->end);
        }
 
-       return max_iomem;
+       return max_iomem > ((u64)1 << dma_bits);
 }
-EXPORT_SYMBOL(drm_get_max_iomem);
+EXPORT_SYMBOL(drm_need_swiotlb);
index 52e445b..521aff9 100644 (file)
@@ -80,6 +80,12 @@ static const struct drm_dmi_panel_orientation_data lcd800x1280_rightside_up = {
        .orientation = DRM_MODE_PANEL_ORIENTATION_RIGHT_UP,
 };
 
+static const struct drm_dmi_panel_orientation_data lcd1200x1920_rightside_up = {
+       .width = 1200,
+       .height = 1920,
+       .orientation = DRM_MODE_PANEL_ORIENTATION_RIGHT_UP,
+};
+
 static const struct dmi_system_id orientation_data[] = {
        {       /* Acer One 10 (S1003) */
                .matches = {
@@ -148,6 +154,13 @@ static const struct dmi_system_id orientation_data[] = {
                  DMI_EXACT_MATCH(DMI_PRODUCT_VERSION, "Lenovo MIIX 320-10ICR"),
                },
                .driver_data = (void *)&lcd800x1280_rightside_up,
+       }, {    /* Lenovo Ideapad D330 */
+               .matches = {
+                 DMI_EXACT_MATCH(DMI_SYS_VENDOR, "LENOVO"),
+                 DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "81H3"),
+                 DMI_EXACT_MATCH(DMI_PRODUCT_VERSION, "Lenovo ideapad D330-10IGM"),
+               },
+               .driver_data = (void *)&lcd1200x1920_rightside_up,
        }, {    /* VIOS LTH17 */
                .matches = {
                  DMI_EXACT_MATCH(DMI_SYS_VENDOR, "VIOS"),
index 231e3f6..dc079ef 100644 (file)
@@ -504,6 +504,7 @@ struct dma_buf *drm_gem_prime_export(struct drm_device *dev,
                .size = obj->size,
                .flags = flags,
                .priv = obj,
+               .resv = obj->resv,
        };
 
        if (dev->driver->gem_prime_res_obj)
index e19525a..5329e66 100644 (file)
@@ -731,7 +731,7 @@ cleanup_entries:
  *
  * Calculate the timeout in jiffies from an absolute time in sec/nsec.
  */
-static signed long drm_timeout_abs_to_jiffies(int64_t timeout_nsec)
+signed long drm_timeout_abs_to_jiffies(int64_t timeout_nsec)
 {
        ktime_t abs_timeout, now;
        u64 timeout_ns, timeout_jiffies64;
@@ -755,6 +755,7 @@ static signed long drm_timeout_abs_to_jiffies(int64_t timeout_nsec)
 
        return timeout_jiffies64 + 1;
 }
+EXPORT_SYMBOL(drm_timeout_abs_to_jiffies);
 
 static int drm_syncobj_array_wait(struct drm_device *dev,
                                  struct drm_file *file_private,
index c330104..8987501 100644 (file)
@@ -584,8 +584,8 @@ static int drm_mmap_locked(struct file *filp, struct vm_area_struct *vma)
                        vma->vm_ops = &drm_vm_ops;
                        break;
                }
-               /* fall through to _DRM_FRAME_BUFFER... */
 #endif
+               /* fall through - to _DRM_FRAME_BUFFER... */
        case _DRM_FRAME_BUFFER:
        case _DRM_REGISTERS:
                offset = drm_core_get_reg_ofs(dev);
@@ -610,7 +610,7 @@ static int drm_mmap_locked(struct file *filp, struct vm_area_struct *vma)
                    vma->vm_end - vma->vm_start, vma->vm_page_prot))
                        return -EAGAIN;
                vma->vm_page_prot = drm_dma_prot(map->type, vma);
-       /* fall through to _DRM_SHM */
+               /* fall through - to _DRM_SHM */
        case _DRM_SHM:
                vma->vm_ops = &drm_vm_shm_ops;
                vma->vm_private_data = (void *)map;
index 18c27f7..9f42f75 100644 (file)
@@ -473,7 +473,6 @@ static struct drm_driver etnaviv_drm_driver = {
        .prime_fd_to_handle = drm_gem_prime_fd_to_handle,
        .gem_prime_export   = drm_gem_prime_export,
        .gem_prime_import   = drm_gem_prime_import,
-       .gem_prime_res_obj  = etnaviv_gem_prime_res_obj,
        .gem_prime_pin      = etnaviv_gem_prime_pin,
        .gem_prime_unpin    = etnaviv_gem_prime_unpin,
        .gem_prime_get_sg_table = etnaviv_gem_prime_get_sg_table,
index a6a7ded..6044ace 100644 (file)
@@ -60,7 +60,6 @@ void *etnaviv_gem_prime_vmap(struct drm_gem_object *obj);
 void etnaviv_gem_prime_vunmap(struct drm_gem_object *obj, void *vaddr);
 int etnaviv_gem_prime_mmap(struct drm_gem_object *obj,
                           struct vm_area_struct *vma);
-struct reservation_object *etnaviv_gem_prime_res_obj(struct drm_gem_object *obj);
 struct drm_gem_object *etnaviv_gem_prime_import_sg_table(struct drm_device *dev,
        struct dma_buf_attachment *attach, struct sg_table *sg);
 int etnaviv_gem_prime_pin(struct drm_gem_object *obj);
index 5c48915..c60752e 100644 (file)
@@ -397,13 +397,13 @@ int etnaviv_gem_cpu_prep(struct drm_gem_object *obj, u32 op,
        }
 
        if (op & ETNA_PREP_NOSYNC) {
-               if (!reservation_object_test_signaled_rcu(etnaviv_obj->resv,
+               if (!reservation_object_test_signaled_rcu(obj->resv,
                                                          write))
                        return -EBUSY;
        } else {
                unsigned long remain = etnaviv_timeout_to_jiffies(timeout);
 
-               ret = reservation_object_wait_timeout_rcu(etnaviv_obj->resv,
+               ret = reservation_object_wait_timeout_rcu(obj->resv,
                                                          write, true, remain);
                if (ret <= 0)
                        return ret == 0 ? -ETIMEDOUT : ret;
@@ -459,7 +459,7 @@ static void etnaviv_gem_describe_fence(struct dma_fence *fence,
 static void etnaviv_gem_describe(struct drm_gem_object *obj, struct seq_file *m)
 {
        struct etnaviv_gem_object *etnaviv_obj = to_etnaviv_bo(obj);
-       struct reservation_object *robj = etnaviv_obj->resv;
+       struct reservation_object *robj = obj->resv;
        struct reservation_object_list *fobj;
        struct dma_fence *fence;
        unsigned long off = drm_vma_node_start(&obj->vma_node);
@@ -549,8 +549,6 @@ void etnaviv_gem_free_object(struct drm_gem_object *obj)
 
        drm_gem_free_mmap_offset(obj);
        etnaviv_obj->ops->release(etnaviv_obj);
-       if (etnaviv_obj->resv == &etnaviv_obj->_resv)
-               reservation_object_fini(&etnaviv_obj->_resv);
        drm_gem_object_release(obj);
 
        kfree(etnaviv_obj);
@@ -596,12 +594,8 @@ static int etnaviv_gem_new_impl(struct drm_device *dev, u32 size, u32 flags,
 
        etnaviv_obj->flags = flags;
        etnaviv_obj->ops = ops;
-       if (robj) {
-               etnaviv_obj->resv = robj;
-       } else {
-               etnaviv_obj->resv = &etnaviv_obj->_resv;
-               reservation_object_init(&etnaviv_obj->_resv);
-       }
+       if (robj)
+               etnaviv_obj->base.resv = robj;
 
        mutex_init(&etnaviv_obj->lock);
        INIT_LIST_HEAD(&etnaviv_obj->vram_list);
index f0abb74..753c458 100644 (file)
@@ -47,10 +47,6 @@ struct etnaviv_gem_object {
        struct sg_table *sgt;
        void *vaddr;
 
-       /* normally (resv == &_resv) except for imported bo's */
-       struct reservation_object *resv;
-       struct reservation_object _resv;
-
        struct list_head vram_list;
 
        /* cache maintenance */
index f21529e..00e8b6a 100644 (file)
@@ -139,10 +139,3 @@ fail:
 
        return ERR_PTR(ret);
 }
-
-struct reservation_object *etnaviv_gem_prime_res_obj(struct drm_gem_object *obj)
-{
-       struct etnaviv_gem_object *etnaviv_obj = to_etnaviv_bo(obj);
-
-       return etnaviv_obj->resv;
-}
index b2fe344..e054f09 100644 (file)
@@ -108,9 +108,9 @@ out_unlock:
 static void submit_unlock_object(struct etnaviv_gem_submit *submit, int i)
 {
        if (submit->bos[i].flags & BO_LOCKED) {
-               struct etnaviv_gem_object *etnaviv_obj = submit->bos[i].obj;
+               struct drm_gem_object *obj = &submit->bos[i].obj->base;
 
-               ww_mutex_unlock(&etnaviv_obj->resv->lock);
+               ww_mutex_unlock(&obj->resv->lock);
                submit->bos[i].flags &= ~BO_LOCKED;
        }
 }
@@ -122,7 +122,7 @@ static int submit_lock_objects(struct etnaviv_gem_submit *submit,
 
 retry:
        for (i = 0; i < submit->nr_bos; i++) {
-               struct etnaviv_gem_object *etnaviv_obj = submit->bos[i].obj;
+               struct drm_gem_object *obj = &submit->bos[i].obj->base;
 
                if (slow_locked == i)
                        slow_locked = -1;
@@ -130,7 +130,7 @@ retry:
                contended = i;
 
                if (!(submit->bos[i].flags & BO_LOCKED)) {
-                       ret = ww_mutex_lock_interruptible(&etnaviv_obj->resv->lock,
+                       ret = ww_mutex_lock_interruptible(&obj->resv->lock,
                                                          ticket);
                        if (ret == -EALREADY)
                                DRM_ERROR("BO at index %u already on submit list\n",
@@ -153,12 +153,12 @@ fail:
                submit_unlock_object(submit, slow_locked);
 
        if (ret == -EDEADLK) {
-               struct etnaviv_gem_object *etnaviv_obj;
+               struct drm_gem_object *obj;
 
-               etnaviv_obj = submit->bos[contended].obj;
+               obj = &submit->bos[contended].obj->base;
 
                /* we lost out in a seqno race, lock and retry.. */
-               ret = ww_mutex_lock_slow_interruptible(&etnaviv_obj->resv->lock,
+               ret = ww_mutex_lock_slow_interruptible(&obj->resv->lock,
                                                       ticket);
                if (!ret) {
                        submit->bos[contended].flags |= BO_LOCKED;
@@ -176,7 +176,7 @@ static int submit_fence_sync(struct etnaviv_gem_submit *submit)
 
        for (i = 0; i < submit->nr_bos; i++) {
                struct etnaviv_gem_submit_bo *bo = &submit->bos[i];
-               struct reservation_object *robj = bo->obj->resv;
+               struct reservation_object *robj = bo->obj->base.resv;
 
                if (!(bo->flags & ETNA_SUBMIT_BO_WRITE)) {
                        ret = reservation_object_reserve_shared(robj, 1);
@@ -207,13 +207,13 @@ static void submit_attach_object_fences(struct etnaviv_gem_submit *submit)
        int i;
 
        for (i = 0; i < submit->nr_bos; i++) {
-               struct etnaviv_gem_object *etnaviv_obj = submit->bos[i].obj;
+               struct drm_gem_object *obj = &submit->bos[i].obj->base;
 
                if (submit->bos[i].flags & ETNA_SUBMIT_BO_WRITE)
-                       reservation_object_add_excl_fence(etnaviv_obj->resv,
+                       reservation_object_add_excl_fence(obj->resv,
                                                          submit->out_fence);
                else
-                       reservation_object_add_shared_fence(etnaviv_obj->resv,
+                       reservation_object_add_shared_fence(obj->resv,
                                                            submit->out_fence);
 
                submit_unlock_object(submit, i);
index 638a586..cf80c5d 100644 (file)
@@ -6557,13 +6557,22 @@ enum {
 #define   PLANE_CTL_FORMAT_YUV422              (0 << 24)
 #define   PLANE_CTL_FORMAT_NV12                        (1 << 24)
 #define   PLANE_CTL_FORMAT_XRGB_2101010                (2 << 24)
+#define   PLANE_CTL_FORMAT_P010                        (3 << 24)
 #define   PLANE_CTL_FORMAT_XRGB_8888           (4 << 24)
+#define   PLANE_CTL_FORMAT_P012                        (5 << 24)
 #define   PLANE_CTL_FORMAT_XRGB_16161616F      (6 << 24)
+#define   PLANE_CTL_FORMAT_P016                        (7 << 24)
 #define   PLANE_CTL_FORMAT_AYUV                        (8 << 24)
 #define   PLANE_CTL_FORMAT_INDEXED             (12 << 24)
 #define   PLANE_CTL_FORMAT_RGB_565             (14 << 24)
 #define   ICL_PLANE_CTL_FORMAT_MASK            (0x1f << 23)
 #define   PLANE_CTL_PIPE_CSC_ENABLE            (1 << 23) /* Pre-GLK */
+#define   PLANE_CTL_FORMAT_Y210                 (1 << 23)
+#define   PLANE_CTL_FORMAT_Y212                 (3 << 23)
+#define   PLANE_CTL_FORMAT_Y216                 (5 << 23)
+#define   PLANE_CTL_FORMAT_Y410                 (7 << 23)
+#define   PLANE_CTL_FORMAT_Y412                 (9 << 23)
+#define   PLANE_CTL_FORMAT_Y416                 (0xb << 23)
 #define   PLANE_CTL_KEY_ENABLE_MASK            (0x3 << 21)
 #define   PLANE_CTL_KEY_ENABLE_SOURCE          (1 << 21)
 #define   PLANE_CTL_KEY_ENABLE_DESTINATION     (2 << 21)
index 7cf9290..b844e88 100644 (file)
@@ -126,6 +126,7 @@ int intel_digital_connector_atomic_check(struct drm_connector *conn,
         */
        if (new_conn_state->force_audio != old_conn_state->force_audio ||
            new_conn_state->broadcast_rgb != old_conn_state->broadcast_rgb ||
+           new_conn_state->base.colorspace != old_conn_state->base.colorspace ||
            new_conn_state->base.picture_aspect_ratio != old_conn_state->base.picture_aspect_ratio ||
            new_conn_state->base.content_type != old_conn_state->base.content_type ||
            new_conn_state->base.scaling_mode != old_conn_state->base.scaling_mode)
@@ -234,10 +235,11 @@ static void intel_atomic_setup_scaler(struct intel_crtc_scaler_state *scaler_sta
        if (plane_state && plane_state->base.fb &&
            plane_state->base.fb->format->is_yuv &&
            plane_state->base.fb->format->num_planes > 1) {
+               struct intel_plane *plane = to_intel_plane(plane_state->base.plane);
                if (IS_GEN(dev_priv, 9) &&
                    !IS_GEMINILAKE(dev_priv)) {
                        mode = SKL_PS_SCALER_MODE_NV12;
-               } else if (icl_is_hdr_plane(to_intel_plane(plane_state->base.plane))) {
+               } else if (icl_is_hdr_plane(dev_priv, plane->id)) {
                        /*
                         * On gen11+'s HDR planes we only use the scaler for
                         * scaling. They have a dedicated chroma upsampler, so
index db09659..dd6c096 100644 (file)
@@ -135,7 +135,7 @@ int intel_plane_atomic_check_with_state(const struct intel_crtc_state *old_crtc_
                new_crtc_state->active_planes |= BIT(plane->id);
 
        if (new_plane_state->base.visible &&
-           new_plane_state->base.fb->format->format == DRM_FORMAT_NV12)
+           is_planar_yuv_format(new_plane_state->base.fb->format->format))
                new_crtc_state->nv12_planes |= BIT(plane->id);
 
        if (new_plane_state->base.visible || old_plane_state->base.visible)
index ee16758..8352d0b 100644 (file)
@@ -265,3 +265,11 @@ intel_attach_aspect_ratio_property(struct drm_connector *connector)
                        connector->dev->mode_config.aspect_ratio_property,
                        DRM_MODE_PICTURE_ASPECT_NONE);
 }
+
+void
+intel_attach_colorspace_property(struct drm_connector *connector)
+{
+       if (!drm_mode_create_colorspace_property(connector))
+               drm_object_attach_property(&connector->base,
+                                          connector->colorspace_property, 0);
+}
index ccb6163..9449648 100644 (file)
@@ -2677,6 +2677,24 @@ int skl_format_to_fourcc(int format, bool rgb_order, bool alpha)
                return DRM_FORMAT_RGB565;
        case PLANE_CTL_FORMAT_NV12:
                return DRM_FORMAT_NV12;
+       case PLANE_CTL_FORMAT_P010:
+               return DRM_FORMAT_P010;
+       case PLANE_CTL_FORMAT_P012:
+               return DRM_FORMAT_P012;
+       case PLANE_CTL_FORMAT_P016:
+               return DRM_FORMAT_P016;
+       case PLANE_CTL_FORMAT_Y210:
+               return DRM_FORMAT_Y210;
+       case PLANE_CTL_FORMAT_Y212:
+               return DRM_FORMAT_Y212;
+       case PLANE_CTL_FORMAT_Y216:
+               return DRM_FORMAT_Y216;
+       case PLANE_CTL_FORMAT_Y410:
+               return DRM_FORMAT_XVYU2101010;
+       case PLANE_CTL_FORMAT_Y412:
+               return DRM_FORMAT_XVYU12_16161616;
+       case PLANE_CTL_FORMAT_Y416:
+               return DRM_FORMAT_XVYU16161616;
        default:
        case PLANE_CTL_FORMAT_XRGB_8888:
                if (rgb_order) {
@@ -2695,6 +2713,18 @@ int skl_format_to_fourcc(int format, bool rgb_order, bool alpha)
                        return DRM_FORMAT_XBGR2101010;
                else
                        return DRM_FORMAT_XRGB2101010;
+       case PLANE_CTL_FORMAT_XRGB_16161616F:
+               if (rgb_order) {
+                       if (alpha)
+                               return DRM_FORMAT_ABGR16161616F;
+                       else
+                               return DRM_FORMAT_XBGR16161616F;
+               } else {
+                       if (alpha)
+                               return DRM_FORMAT_ARGB16161616F;
+                       else
+                               return DRM_FORMAT_XRGB16161616F;
+               }
        }
 }
 
@@ -3176,7 +3206,7 @@ int skl_check_plane_surface(struct intel_plane_state *plane_state)
         * Handle the AUX surface first since
         * the main surface setup depends on it.
         */
-       if (fb->format->format == DRM_FORMAT_NV12) {
+       if (is_planar_yuv_format(fb->format->format)) {
                ret = skl_check_nv12_aux_surface(plane_state);
                if (ret)
                        return ret;
@@ -3590,6 +3620,12 @@ static u32 skl_plane_ctl_format(u32 pixel_format)
                return PLANE_CTL_FORMAT_XRGB_2101010;
        case DRM_FORMAT_XBGR2101010:
                return PLANE_CTL_ORDER_RGBX | PLANE_CTL_FORMAT_XRGB_2101010;
+       case DRM_FORMAT_XBGR16161616F:
+       case DRM_FORMAT_ABGR16161616F:
+               return PLANE_CTL_FORMAT_XRGB_16161616F | PLANE_CTL_ORDER_RGBX;
+       case DRM_FORMAT_XRGB16161616F:
+       case DRM_FORMAT_ARGB16161616F:
+               return PLANE_CTL_FORMAT_XRGB_16161616F;
        case DRM_FORMAT_YUYV:
                return PLANE_CTL_FORMAT_YUV422 | PLANE_CTL_YUV422_YUYV;
        case DRM_FORMAT_YVYU:
@@ -3600,6 +3636,24 @@ static u32 skl_plane_ctl_format(u32 pixel_format)
                return PLANE_CTL_FORMAT_YUV422 | PLANE_CTL_YUV422_VYUY;
        case DRM_FORMAT_NV12:
                return PLANE_CTL_FORMAT_NV12;
+       case DRM_FORMAT_P010:
+               return PLANE_CTL_FORMAT_P010;
+       case DRM_FORMAT_P012:
+               return PLANE_CTL_FORMAT_P012;
+       case DRM_FORMAT_P016:
+               return PLANE_CTL_FORMAT_P016;
+       case DRM_FORMAT_Y210:
+               return PLANE_CTL_FORMAT_Y210;
+       case DRM_FORMAT_Y212:
+               return PLANE_CTL_FORMAT_Y212;
+       case DRM_FORMAT_Y216:
+               return PLANE_CTL_FORMAT_Y216;
+       case DRM_FORMAT_XVYU2101010:
+               return PLANE_CTL_FORMAT_Y410;
+       case DRM_FORMAT_XVYU12_16161616:
+               return PLANE_CTL_FORMAT_Y412;
+       case DRM_FORMAT_XVYU16161616:
+               return PLANE_CTL_FORMAT_Y416;
        default:
                MISSING_CASE(pixel_format);
        }
@@ -3772,6 +3826,8 @@ u32 glk_plane_color_ctl_crtc(const struct intel_crtc_state *crtc_state)
 u32 glk_plane_color_ctl(const struct intel_crtc_state *crtc_state,
                        const struct intel_plane_state *plane_state)
 {
+       struct drm_i915_private *dev_priv =
+               to_i915(plane_state->base.plane->dev);
        const struct drm_framebuffer *fb = plane_state->base.fb;
        struct intel_plane *plane = to_intel_plane(plane_state->base.plane);
        u32 plane_color_ctl = 0;
@@ -3779,7 +3835,7 @@ u32 glk_plane_color_ctl(const struct intel_crtc_state *crtc_state,
        plane_color_ctl |= PLANE_COLOR_PLANE_GAMMA_DISABLE;
        plane_color_ctl |= glk_plane_color_ctl_alpha(plane_state);
 
-       if (fb->format->is_yuv && !icl_is_hdr_plane(plane)) {
+       if (fb->format->is_yuv && !icl_is_hdr_plane(dev_priv, plane->id)) {
                if (plane_state->base.color_encoding == DRM_COLOR_YCBCR_BT709)
                        plane_color_ctl |= PLANE_COLOR_CSC_MODE_YUV709_TO_RGB709;
                else
@@ -5036,9 +5092,9 @@ skl_update_scaler(struct intel_crtc_state *crtc_state, bool force_detach,
                return 0;
        }
 
-       if (format && format->format == DRM_FORMAT_NV12 &&
+       if (format && is_planar_yuv_format(format->format) &&
            (src_h < SKL_MIN_YUV_420_SRC_H || src_w < SKL_MIN_YUV_420_SRC_W)) {
-               DRM_DEBUG_KMS("NV12: src dimensions not met\n");
+               DRM_DEBUG_KMS("Planar YUV: src dimensions not met\n");
                return -EINVAL;
        }
 
@@ -5105,14 +5161,15 @@ static int skl_update_scaler_plane(struct intel_crtc_state *crtc_state,
 {
        struct intel_plane *intel_plane =
                to_intel_plane(plane_state->base.plane);
+       struct drm_i915_private *dev_priv = to_i915(intel_plane->base.dev);
        struct drm_framebuffer *fb = plane_state->base.fb;
        int ret;
        bool force_detach = !fb || !plane_state->base.visible;
        bool need_scaler = false;
 
        /* Pre-gen11 and SDR planes always need a scaler for planar formats. */
-       if (!icl_is_hdr_plane(intel_plane) &&
-           fb && fb->format->format == DRM_FORMAT_NV12)
+       if (!icl_is_hdr_plane(dev_priv, intel_plane->id) &&
+           fb && is_planar_yuv_format(fb->format->format))
                need_scaler = true;
 
        ret = skl_update_scaler(crtc_state, force_detach,
@@ -5144,11 +5201,24 @@ static int skl_update_scaler_plane(struct intel_crtc_state *crtc_state,
        case DRM_FORMAT_ARGB8888:
        case DRM_FORMAT_XRGB2101010:
        case DRM_FORMAT_XBGR2101010:
+       case DRM_FORMAT_XBGR16161616F:
+       case DRM_FORMAT_ABGR16161616F:
+       case DRM_FORMAT_XRGB16161616F:
+       case DRM_FORMAT_ARGB16161616F:
        case DRM_FORMAT_YUYV:
        case DRM_FORMAT_YVYU:
        case DRM_FORMAT_UYVY:
        case DRM_FORMAT_VYUY:
        case DRM_FORMAT_NV12:
+       case DRM_FORMAT_P010:
+       case DRM_FORMAT_P012:
+       case DRM_FORMAT_P016:
+       case DRM_FORMAT_Y210:
+       case DRM_FORMAT_Y212:
+       case DRM_FORMAT_Y216:
+       case DRM_FORMAT_XVYU2101010:
+       case DRM_FORMAT_XVYU12_16161616:
+       case DRM_FORMAT_XVYU16161616:
                break;
        default:
                DRM_DEBUG_KMS("[PLANE:%d:%s] FB:%d unsupported scaling format 0x%x\n",
@@ -11134,7 +11204,7 @@ static int icl_check_nv12_planes(struct intel_crtc_state *crtc_state)
                }
 
                if (!linked_state) {
-                       DRM_DEBUG_KMS("Need %d free Y planes for NV12\n",
+                       DRM_DEBUG_KMS("Need %d free Y planes for planar YUV\n",
                                      hweight8(crtc_state->nv12_planes));
 
                        return -EINVAL;
@@ -13767,7 +13837,7 @@ skl_max_scale(const struct intel_crtc_state *crtc_state,
         *            or
         *    cdclk/crtc_clock
         */
-       mult = pixel_format == DRM_FORMAT_NV12 ? 2 : 3;
+       mult = is_planar_yuv_format(pixel_format) ? 2 : 3;
        tmpclk1 = (1 << 16) * mult - 1;
        tmpclk2 = (1 << 8) * ((max_dotclk << 8) / crtc_clock);
        max_scale = min(tmpclk1, tmpclk2);
index 15db413..375f51d 100644 (file)
@@ -1796,6 +1796,7 @@ int intel_ddc_get_modes(struct drm_connector *c, struct i2c_adapter *adapter);
 void intel_attach_force_audio_property(struct drm_connector *connector);
 void intel_attach_broadcast_rgb_property(struct drm_connector *connector);
 void intel_attach_aspect_ratio_property(struct drm_connector *connector);
+void intel_attach_colorspace_property(struct drm_connector *connector);
 
 /* intel_csr.c */
 void intel_csr_ucode_init(struct drm_i915_private *);
@@ -2300,6 +2301,7 @@ bool intel_sdvo_init(struct drm_i915_private *dev_priv,
 
 
 /* intel_sprite.c */
+bool is_planar_yuv_format(u32 pixelformat);
 int intel_usecs_to_scanlines(const struct drm_display_mode *adjusted_mode,
                             int usecs);
 struct intel_plane *intel_sprite_plane_create(struct drm_i915_private *dev_priv,
@@ -2324,12 +2326,13 @@ static inline bool icl_is_nv12_y_plane(enum plane_id id)
        return false;
 }
 
-static inline bool icl_is_hdr_plane(struct intel_plane *plane)
+static inline bool icl_is_hdr_plane(struct drm_i915_private *dev_priv,
+                                   enum plane_id plane_id)
 {
-       if (INTEL_GEN(to_i915(plane->base.dev)) < 11)
+       if (INTEL_GEN(dev_priv) < 11)
                return false;
 
-       return plane->id < PLANE_SPRITE2;
+       return plane_id < PLANE_SPRITE2;
 }
 
 /* intel_tv.c */
index f125a62..765718b 100644 (file)
@@ -498,6 +498,8 @@ static void intel_hdmi_set_avi_infoframe(struct intel_encoder *encoder,
        else
                frame.avi.colorspace = HDMI_COLORSPACE_RGB;
 
+       drm_hdmi_avi_infoframe_colorspace(&frame.avi, conn_state);
+
        drm_hdmi_avi_infoframe_quant_range(&frame.avi,
                                           conn_state->connector,
                                           adjusted_mode,
@@ -2143,10 +2145,21 @@ static void
 intel_hdmi_add_properties(struct intel_hdmi *intel_hdmi, struct drm_connector *connector)
 {
        struct drm_i915_private *dev_priv = to_i915(connector->dev);
+       struct intel_digital_port *intel_dig_port =
+                               hdmi_to_dig_port(intel_hdmi);
 
        intel_attach_force_audio_property(connector);
        intel_attach_broadcast_rgb_property(connector);
        intel_attach_aspect_ratio_property(connector);
+
+       /*
+        * Attach Colorspace property for Non LSPCON based device
+        * ToDo: This needs to be extended for LSPCON implementation
+        * as well. Will be implemented separately.
+        */
+       if (!intel_dig_port->lspcon.active)
+               intel_attach_colorspace_property(connector);
+
        drm_connector_attach_content_type_property(connector);
        connector->state->picture_aspect_ratio = HDMI_PICTURE_ASPECT_NONE;
 
index 54307f1..14ac318 100644 (file)
@@ -3970,7 +3970,7 @@ skl_ddb_get_hw_plane_state(struct drm_i915_private *dev_priv,
                val = I915_READ(PLANE_BUF_CFG(pipe, plane_id));
                val2 = I915_READ(PLANE_NV12_BUF_CFG(pipe, plane_id));
 
-               if (fourcc == DRM_FORMAT_NV12)
+               if (is_planar_yuv_format(fourcc))
                        swap(val, val2);
 
                skl_ddb_entry_init_from_hw(dev_priv, ddb_y, val);
@@ -4180,7 +4180,7 @@ skl_plane_relative_data_rate(const struct intel_crtc_state *cstate,
 
        if (intel_plane->id == PLANE_CURSOR)
                return 0;
-       if (plane == 1 && format != DRM_FORMAT_NV12)
+       if (plane == 1 && !is_planar_yuv_format(format))
                return 0;
 
        /*
@@ -4192,7 +4192,7 @@ skl_plane_relative_data_rate(const struct intel_crtc_state *cstate,
        height = drm_rect_height(&intel_pstate->base.src) >> 16;
 
        /* UV plane does 1/2 pixel sub-sampling */
-       if (plane == 1 && format == DRM_FORMAT_NV12) {
+       if (plane == 1 && is_planar_yuv_format(format)) {
                width /= 2;
                height /= 2;
        }
@@ -4578,9 +4578,9 @@ skl_compute_plane_wm_params(const struct intel_crtc_state *cstate,
        const struct drm_framebuffer *fb = pstate->fb;
        u32 interm_pbpl;
 
-       /* only NV12 format has two planes */
-       if (color_plane == 1 && fb->format->format != DRM_FORMAT_NV12) {
-               DRM_DEBUG_KMS("Non NV12 format have single plane\n");
+       /* only planar format has two planes */
+       if (color_plane == 1 && !is_planar_yuv_format(fb->format->format)) {
+               DRM_DEBUG_KMS("Non planar format have single plane\n");
                return -EINVAL;
        }
 
@@ -4591,7 +4591,7 @@ skl_compute_plane_wm_params(const struct intel_crtc_state *cstate,
        wp->x_tiled = fb->modifier == I915_FORMAT_MOD_X_TILED;
        wp->rc_surface = fb->modifier == I915_FORMAT_MOD_Y_TILED_CCS ||
                         fb->modifier == I915_FORMAT_MOD_Yf_TILED_CCS;
-       wp->is_planar = fb->format->format == DRM_FORMAT_NV12;
+       wp->is_planar = is_planar_yuv_format(fb->format->format);
 
        if (plane->id == PLANE_CURSOR) {
                wp->width = intel_pstate->base.crtc_w;
index b56a1a9..53174d5 100644 (file)
 #include "i915_drv.h"
 #include <drm/drm_color_mgmt.h>
 
+bool is_planar_yuv_format(u32 pixelformat)
+{
+       switch (pixelformat) {
+       case DRM_FORMAT_NV12:
+       case DRM_FORMAT_P010:
+       case DRM_FORMAT_P012:
+       case DRM_FORMAT_P016:
+               return true;
+       default:
+               return false;
+       }
+}
+
 int intel_usecs_to_scanlines(const struct drm_display_mode *adjusted_mode,
                             int usecs)
 {
@@ -335,8 +348,8 @@ skl_program_scaler(struct intel_plane *plane,
                                      0, INT_MAX);
 
        /* TODO: handle sub-pixel coordinates */
-       if (plane_state->base.fb->format->format == DRM_FORMAT_NV12 &&
-           !icl_is_hdr_plane(plane)) {
+       if (is_planar_yuv_format(plane_state->base.fb->format->format) &&
+           !icl_is_hdr_plane(dev_priv, plane->id)) {
                y_hphase = skl_scaler_calc_phase(1, hscale, false);
                y_vphase = skl_scaler_calc_phase(1, vscale, false);
 
@@ -518,7 +531,7 @@ skl_program_plane(struct intel_plane *plane,
        I915_WRITE_FW(PLANE_AUX_DIST(pipe, plane_id),
                      (plane_state->color_plane[1].offset - surf_addr) | aux_stride);
 
-       if (icl_is_hdr_plane(plane)) {
+       if (icl_is_hdr_plane(dev_priv, plane_id)) {
                u32 cus_ctl = 0;
 
                if (linked) {
@@ -542,7 +555,7 @@ skl_program_plane(struct intel_plane *plane,
        if (INTEL_GEN(dev_priv) >= 10 || IS_GEMINILAKE(dev_priv))
                I915_WRITE_FW(PLANE_COLOR_CTL(pipe, plane_id), plane_color_ctl);
 
-       if (fb->format->is_yuv && icl_is_hdr_plane(plane))
+       if (fb->format->is_yuv && icl_is_hdr_plane(dev_priv, plane_id))
                icl_program_input_csc(plane, crtc_state, plane_state);
 
        skl_write_plane_wm(plane, crtc_state);
@@ -1482,8 +1495,6 @@ static int skl_plane_check_fb(const struct intel_crtc_state *crtc_state,
                /*
                 * 90/270 is not allowed with RGB64 16:16:16:16 and
                 * Indexed 8-bit. RGB 16-bit 5:6:5 is allowed gen11 onwards.
-                * TBD: Add RGB64 case once its added in supported format
-                * list.
                 */
                switch (fb->format->format) {
                case DRM_FORMAT_RGB565:
@@ -1491,6 +1502,10 @@ static int skl_plane_check_fb(const struct intel_crtc_state *crtc_state,
                                break;
                        /* fall through */
                case DRM_FORMAT_C8:
+               case DRM_FORMAT_XRGB16161616F:
+               case DRM_FORMAT_XBGR16161616F:
+               case DRM_FORMAT_ARGB16161616F:
+               case DRM_FORMAT_ABGR16161616F:
                        DRM_DEBUG_KMS("Unsupported pixel format %s for 90/270!\n",
                                      drm_get_format_name(fb->format->format,
                                                          &format_name));
@@ -1551,10 +1566,10 @@ static int skl_plane_check_nv12_rotation(const struct intel_plane_state *plane_s
        int src_w = drm_rect_width(&plane_state->base.src) >> 16;
 
        /* Display WA #1106 */
-       if (fb->format->format == DRM_FORMAT_NV12 && src_w & 3 &&
+       if (is_planar_yuv_format(fb->format->format) && src_w & 3 &&
            (rotation == DRM_MODE_ROTATE_270 ||
             rotation == (DRM_MODE_REFLECT_X | DRM_MODE_ROTATE_90))) {
-               DRM_DEBUG_KMS("src width must be multiple of 4 for rotated NV12\n");
+               DRM_DEBUG_KMS("src width must be multiple of 4 for rotated planar YUV\n");
                return -EINVAL;
        }
 
@@ -1790,6 +1805,52 @@ static const u32 skl_plane_formats[] = {
        DRM_FORMAT_VYUY,
 };
 
+static const uint32_t icl_plane_formats[] = {
+       DRM_FORMAT_C8,
+       DRM_FORMAT_RGB565,
+       DRM_FORMAT_XRGB8888,
+       DRM_FORMAT_XBGR8888,
+       DRM_FORMAT_ARGB8888,
+       DRM_FORMAT_ABGR8888,
+       DRM_FORMAT_XRGB2101010,
+       DRM_FORMAT_XBGR2101010,
+       DRM_FORMAT_YUYV,
+       DRM_FORMAT_YVYU,
+       DRM_FORMAT_UYVY,
+       DRM_FORMAT_VYUY,
+       DRM_FORMAT_Y210,
+       DRM_FORMAT_Y212,
+       DRM_FORMAT_Y216,
+       DRM_FORMAT_XVYU2101010,
+       DRM_FORMAT_XVYU12_16161616,
+       DRM_FORMAT_XVYU16161616,
+};
+
+static const uint32_t icl_hdr_plane_formats[] = {
+       DRM_FORMAT_C8,
+       DRM_FORMAT_RGB565,
+       DRM_FORMAT_XRGB8888,
+       DRM_FORMAT_XBGR8888,
+       DRM_FORMAT_ARGB8888,
+       DRM_FORMAT_ABGR8888,
+       DRM_FORMAT_XRGB2101010,
+       DRM_FORMAT_XBGR2101010,
+       DRM_FORMAT_XRGB16161616F,
+       DRM_FORMAT_XBGR16161616F,
+       DRM_FORMAT_ARGB16161616F,
+       DRM_FORMAT_ABGR16161616F,
+       DRM_FORMAT_YUYV,
+       DRM_FORMAT_YVYU,
+       DRM_FORMAT_UYVY,
+       DRM_FORMAT_VYUY,
+       DRM_FORMAT_Y210,
+       DRM_FORMAT_Y212,
+       DRM_FORMAT_Y216,
+       DRM_FORMAT_XVYU2101010,
+       DRM_FORMAT_XVYU12_16161616,
+       DRM_FORMAT_XVYU16161616,
+};
+
 static const u32 skl_planar_formats[] = {
        DRM_FORMAT_C8,
        DRM_FORMAT_RGB565,
@@ -1806,6 +1867,79 @@ static const u32 skl_planar_formats[] = {
        DRM_FORMAT_NV12,
 };
 
+static const uint32_t glk_planar_formats[] = {
+       DRM_FORMAT_C8,
+       DRM_FORMAT_RGB565,
+       DRM_FORMAT_XRGB8888,
+       DRM_FORMAT_XBGR8888,
+       DRM_FORMAT_ARGB8888,
+       DRM_FORMAT_ABGR8888,
+       DRM_FORMAT_XRGB2101010,
+       DRM_FORMAT_XBGR2101010,
+       DRM_FORMAT_YUYV,
+       DRM_FORMAT_YVYU,
+       DRM_FORMAT_UYVY,
+       DRM_FORMAT_VYUY,
+       DRM_FORMAT_NV12,
+       DRM_FORMAT_P010,
+       DRM_FORMAT_P012,
+       DRM_FORMAT_P016,
+};
+
+static const uint32_t icl_planar_formats[] = {
+       DRM_FORMAT_C8,
+       DRM_FORMAT_RGB565,
+       DRM_FORMAT_XRGB8888,
+       DRM_FORMAT_XBGR8888,
+       DRM_FORMAT_ARGB8888,
+       DRM_FORMAT_ABGR8888,
+       DRM_FORMAT_XRGB2101010,
+       DRM_FORMAT_XBGR2101010,
+       DRM_FORMAT_YUYV,
+       DRM_FORMAT_YVYU,
+       DRM_FORMAT_UYVY,
+       DRM_FORMAT_VYUY,
+       DRM_FORMAT_NV12,
+       DRM_FORMAT_P010,
+       DRM_FORMAT_P012,
+       DRM_FORMAT_P016,
+       DRM_FORMAT_Y210,
+       DRM_FORMAT_Y212,
+       DRM_FORMAT_Y216,
+       DRM_FORMAT_XVYU2101010,
+       DRM_FORMAT_XVYU12_16161616,
+       DRM_FORMAT_XVYU16161616,
+};
+
+static const uint32_t icl_hdr_planar_formats[] = {
+       DRM_FORMAT_C8,
+       DRM_FORMAT_RGB565,
+       DRM_FORMAT_XRGB8888,
+       DRM_FORMAT_XBGR8888,
+       DRM_FORMAT_ARGB8888,
+       DRM_FORMAT_ABGR8888,
+       DRM_FORMAT_XRGB2101010,
+       DRM_FORMAT_XBGR2101010,
+       DRM_FORMAT_XRGB16161616F,
+       DRM_FORMAT_XBGR16161616F,
+       DRM_FORMAT_ARGB16161616F,
+       DRM_FORMAT_ABGR16161616F,
+       DRM_FORMAT_YUYV,
+       DRM_FORMAT_YVYU,
+       DRM_FORMAT_UYVY,
+       DRM_FORMAT_VYUY,
+       DRM_FORMAT_NV12,
+       DRM_FORMAT_P010,
+       DRM_FORMAT_P012,
+       DRM_FORMAT_P016,
+       DRM_FORMAT_Y210,
+       DRM_FORMAT_Y212,
+       DRM_FORMAT_Y216,
+       DRM_FORMAT_XVYU2101010,
+       DRM_FORMAT_XVYU12_16161616,
+       DRM_FORMAT_XVYU16161616,
+};
+
 static const u64 skl_plane_format_modifiers_noccs[] = {
        I915_FORMAT_MOD_Yf_TILED,
        I915_FORMAT_MOD_Y_TILED,
@@ -1945,10 +2079,23 @@ static bool skl_plane_format_mod_supported(struct drm_plane *_plane,
        case DRM_FORMAT_UYVY:
        case DRM_FORMAT_VYUY:
        case DRM_FORMAT_NV12:
+       case DRM_FORMAT_P010:
+       case DRM_FORMAT_P012:
+       case DRM_FORMAT_P016:
+       case DRM_FORMAT_Y210:
+       case DRM_FORMAT_Y212:
+       case DRM_FORMAT_Y216:
+       case DRM_FORMAT_XVYU2101010:
+       case DRM_FORMAT_XVYU12_16161616:
+       case DRM_FORMAT_XVYU16161616:
                if (modifier == I915_FORMAT_MOD_Yf_TILED)
                        return true;
                /* fall through */
        case DRM_FORMAT_C8:
+       case DRM_FORMAT_XBGR16161616F:
+       case DRM_FORMAT_ABGR16161616F:
+       case DRM_FORMAT_XRGB16161616F:
+       case DRM_FORMAT_ARGB16161616F:
                if (modifier == DRM_FORMAT_MOD_LINEAR ||
                    modifier == I915_FORMAT_MOD_X_TILED ||
                    modifier == I915_FORMAT_MOD_Y_TILED)
@@ -2085,8 +2232,25 @@ skl_universal_plane_create(struct drm_i915_private *dev_priv,
                plane->update_slave = icl_update_slave;
 
        if (skl_plane_has_planar(dev_priv, pipe, plane_id)) {
-               formats = skl_planar_formats;
-               num_formats = ARRAY_SIZE(skl_planar_formats);
+               if (icl_is_hdr_plane(dev_priv, plane_id)) {
+                       formats = icl_hdr_planar_formats;
+                       num_formats = ARRAY_SIZE(icl_hdr_planar_formats);
+               } else if (INTEL_GEN(dev_priv) >= 11) {
+                       formats = icl_planar_formats;
+                       num_formats = ARRAY_SIZE(icl_planar_formats);
+               } else if (INTEL_GEN(dev_priv) == 10 || IS_GEMINILAKE(dev_priv)) {
+                       formats = glk_planar_formats;
+                       num_formats = ARRAY_SIZE(glk_planar_formats);
+               } else {
+                       formats = skl_planar_formats;
+                       num_formats = ARRAY_SIZE(skl_planar_formats);
+               }
+       } else if (icl_is_hdr_plane(dev_priv, plane_id)) {
+               formats = icl_hdr_plane_formats;
+               num_formats = ARRAY_SIZE(icl_hdr_plane_formats);
+       } else if (INTEL_GEN(dev_priv) >= 11) {
+               formats = icl_plane_formats;
+               num_formats = ARRAY_SIZE(icl_plane_formats);
        } else {
                formats = skl_plane_formats;
                num_formats = ARRAY_SIZE(skl_plane_formats);
index 23abf03..3f9921b 100644 (file)
@@ -317,129 +317,6 @@ static int get_column_index_for_rc_params(u8 bits_per_component)
        }
 }
 
-static int intel_compute_rc_parameters(struct drm_dsc_config *vdsc_cfg)
-{
-       unsigned long groups_per_line = 0;
-       unsigned long groups_total = 0;
-       unsigned long num_extra_mux_bits = 0;
-       unsigned long slice_bits = 0;
-       unsigned long hrd_delay = 0;
-       unsigned long final_scale = 0;
-       unsigned long rbs_min = 0;
-
-       /* Number of groups used to code each line of a slice */
-       groups_per_line = DIV_ROUND_UP(vdsc_cfg->slice_width,
-                                      DSC_RC_PIXELS_PER_GROUP);
-
-       /* chunksize in Bytes */
-       vdsc_cfg->slice_chunk_size = DIV_ROUND_UP(vdsc_cfg->slice_width *
-                                                 vdsc_cfg->bits_per_pixel,
-                                                 (8 * 16));
-
-       if (vdsc_cfg->convert_rgb)
-               num_extra_mux_bits = 3 * (vdsc_cfg->mux_word_size +
-                                         (4 * vdsc_cfg->bits_per_component + 4)
-                                         - 2);
-       else
-               num_extra_mux_bits = 3 * vdsc_cfg->mux_word_size +
-                       (4 * vdsc_cfg->bits_per_component + 4) +
-                       2 * (4 * vdsc_cfg->bits_per_component) - 2;
-       /* Number of bits in one Slice */
-       slice_bits = 8 * vdsc_cfg->slice_chunk_size * vdsc_cfg->slice_height;
-
-       while ((num_extra_mux_bits > 0) &&
-              ((slice_bits - num_extra_mux_bits) % vdsc_cfg->mux_word_size))
-               num_extra_mux_bits--;
-
-       if (groups_per_line < vdsc_cfg->initial_scale_value - 8)
-               vdsc_cfg->initial_scale_value = groups_per_line + 8;
-
-       /* scale_decrement_interval calculation according to DSC spec 1.11 */
-       if (vdsc_cfg->initial_scale_value > 8)
-               vdsc_cfg->scale_decrement_interval = groups_per_line /
-                       (vdsc_cfg->initial_scale_value - 8);
-       else
-               vdsc_cfg->scale_decrement_interval = DSC_SCALE_DECREMENT_INTERVAL_MAX;
-
-       vdsc_cfg->final_offset = vdsc_cfg->rc_model_size -
-               (vdsc_cfg->initial_xmit_delay *
-                vdsc_cfg->bits_per_pixel + 8) / 16 + num_extra_mux_bits;
-
-       if (vdsc_cfg->final_offset >= vdsc_cfg->rc_model_size) {
-               DRM_DEBUG_KMS("FinalOfs < RcModelSze for this InitialXmitDelay\n");
-               return -ERANGE;
-       }
-
-       final_scale = (vdsc_cfg->rc_model_size * 8) /
-               (vdsc_cfg->rc_model_size - vdsc_cfg->final_offset);
-       if (vdsc_cfg->slice_height > 1)
-               /*
-                * NflBpgOffset is 16 bit value with 11 fractional bits
-                * hence we multiply by 2^11 for preserving the
-                * fractional part
-                */
-               vdsc_cfg->nfl_bpg_offset = DIV_ROUND_UP((vdsc_cfg->first_line_bpg_offset << 11),
-                                                       (vdsc_cfg->slice_height - 1));
-       else
-               vdsc_cfg->nfl_bpg_offset = 0;
-
-       /* 2^16 - 1 */
-       if (vdsc_cfg->nfl_bpg_offset > 65535) {
-               DRM_DEBUG_KMS("NflBpgOffset is too large for this slice height\n");
-               return -ERANGE;
-       }
-
-       /* Number of groups used to code the entire slice */
-       groups_total = groups_per_line * vdsc_cfg->slice_height;
-
-       /* slice_bpg_offset is 16 bit value with 11 fractional bits */
-       vdsc_cfg->slice_bpg_offset = DIV_ROUND_UP(((vdsc_cfg->rc_model_size -
-                                                   vdsc_cfg->initial_offset +
-                                                   num_extra_mux_bits) << 11),
-                                                 groups_total);
-
-       if (final_scale > 9) {
-               /*
-                * ScaleIncrementInterval =
-                * finaloffset/((NflBpgOffset + SliceBpgOffset)*8(finalscale - 1.125))
-                * as (NflBpgOffset + SliceBpgOffset) has 11 bit fractional value,
-                * we need divide by 2^11 from pstDscCfg values
-                */
-               vdsc_cfg->scale_increment_interval =
-                               (vdsc_cfg->final_offset * (1 << 11)) /
-                               ((vdsc_cfg->nfl_bpg_offset +
-                               vdsc_cfg->slice_bpg_offset) *
-                               (final_scale - 9));
-       } else {
-               /*
-                * If finalScaleValue is less than or equal to 9, a value of 0 should
-                * be used to disable the scale increment at the end of the slice
-                */
-               vdsc_cfg->scale_increment_interval = 0;
-       }
-
-       if (vdsc_cfg->scale_increment_interval > 65535) {
-               DRM_DEBUG_KMS("ScaleIncrementInterval is large for slice height\n");
-               return -ERANGE;
-       }
-
-       /*
-        * DSC spec mentions that bits_per_pixel specifies the target
-        * bits/pixel (bpp) rate that is used by the encoder,
-        * in steps of 1/16 of a bit per pixel
-        */
-       rbs_min = vdsc_cfg->rc_model_size - vdsc_cfg->initial_offset +
-               DIV_ROUND_UP(vdsc_cfg->initial_xmit_delay *
-                            vdsc_cfg->bits_per_pixel, 16) +
-               groups_per_line * vdsc_cfg->first_line_bpg_offset;
-
-       hrd_delay = DIV_ROUND_UP((rbs_min * 16), vdsc_cfg->bits_per_pixel);
-       vdsc_cfg->rc_bits = (hrd_delay * vdsc_cfg->bits_per_pixel) / 16;
-       vdsc_cfg->initial_dec_delay = hrd_delay - vdsc_cfg->initial_xmit_delay;
-
-       return 0;
-}
-
 int intel_dp_compute_dsc_params(struct intel_dp *intel_dp,
                                struct intel_crtc_state *pipe_config)
 {
@@ -491,7 +368,7 @@ int intel_dp_compute_dsc_params(struct intel_dp *intel_dp,
                        DSC_1_1_MAX_LINEBUF_DEPTH_BITS : line_buf_depth;
 
        /* Gen 11 does not support YCbCr */
-       vdsc_cfg->enable422 = false;
+       vdsc_cfg->simple_422 = false;
        /* Gen 11 does not support VBR */
        vdsc_cfg->vbr_enable = false;
        vdsc_cfg->block_pred_enable =
@@ -574,7 +451,7 @@ int intel_dp_compute_dsc_params(struct intel_dp *intel_dp,
        vdsc_cfg->initial_scale_value = (vdsc_cfg->rc_model_size << 3) /
                (vdsc_cfg->rc_model_size - vdsc_cfg->initial_offset);
 
-       return intel_compute_rc_parameters(vdsc_cfg);
+       return drm_dsc_compute_rc_parameters(vdsc_cfg);
 }
 
 enum intel_display_power_domain
@@ -618,7 +495,7 @@ static void intel_configure_pps_for_dsc_encoder(struct intel_encoder *encoder,
                pps_val |= DSC_BLOCK_PREDICTION;
        if (vdsc_cfg->convert_rgb)
                pps_val |= DSC_COLOR_SPACE_CONVERSION;
-       if (vdsc_cfg->enable422)
+       if (vdsc_cfg->simple_422)
                pps_val |= DSC_422_ENABLE;
        if (vdsc_cfg->vbr_enable)
                pps_val |= DSC_VBR_ENABLE;
@@ -1004,10 +881,10 @@ static void intel_dp_write_dsc_pps_sdp(struct intel_encoder *encoder,
        struct drm_dsc_pps_infoframe dp_dsc_pps_sdp;
 
        /* Prepare DP SDP PPS header as per DP 1.4 spec, Table 2-123 */
-       drm_dsc_dp_pps_header_init(&dp_dsc_pps_sdp);
+       drm_dsc_dp_pps_header_init(&dp_dsc_pps_sdp.pps_header);
 
        /* Fill the PPS payload bytes as per DSC spec 1.2 Table 4-1 */
-       drm_dsc_pps_infoframe_pack(&dp_dsc_pps_sdp, vdsc_cfg);
+       drm_dsc_pps_payload_pack(&dp_dsc_pps_sdp.pps_payload, vdsc_cfg);
 
        intel_dig_port->write_infoframe(encoder, crtc_state,
                                        DP_SDP_PPS, &dp_dsc_pps_sdp,
index c935cbe..3e8bece 100644 (file)
@@ -185,7 +185,7 @@ static int compare_of(struct device *dev, void *data)
        }
 
        /* Special case for LDB, one device for two channels */
-       if (of_node_cmp(np->name, "lvds-channel") == 0) {
+       if (of_node_name_eq(np, "lvds-channel")) {
                np = of_get_parent(np);
                of_node_put(np);
        }
index 7709f2f..d4ea82f 100644 (file)
@@ -1,5 +1,5 @@
 meson-drm-y := meson_drv.o meson_plane.o meson_crtc.o meson_venc_cvbs.o
-meson-drm-y += meson_viu.o meson_vpp.o meson_venc.o meson_vclk.o meson_canvas.o meson_overlay.o
+meson-drm-y += meson_viu.o meson_vpp.o meson_venc.o meson_vclk.o meson_overlay.o
 
 obj-$(CONFIG_DRM_MESON) += meson-drm.o
 obj-$(CONFIG_DRM_MESON_DW_HDMI) += meson_dw_hdmi.o
diff --git a/drivers/gpu/drm/meson/meson_canvas.c b/drivers/gpu/drm/meson/meson_canvas.c
deleted file mode 100644 (file)
index 5de11aa..0000000
+++ /dev/null
@@ -1,73 +0,0 @@
-/*
- * Copyright (C) 2016 BayLibre, SAS
- * Author: Neil Armstrong <narmstrong@baylibre.com>
- * Copyright (C) 2015 Amlogic, Inc. All rights reserved.
- * Copyright (C) 2014 Endless Mobile
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License as
- * published by the Free Software Foundation; either version 2 of the
- * License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, see <http://www.gnu.org/licenses/>.
- */
-
-#include <linux/kernel.h>
-#include <linux/module.h>
-#include "meson_drv.h"
-#include "meson_canvas.h"
-#include "meson_registers.h"
-
-/**
- * DOC: Canvas
- *
- * CANVAS is a memory zone where physical memory frames information
- * are stored for the VIU to scanout.
- */
-
-/* DMC Registers */
-#define DMC_CAV_LUT_DATAL      0x48 /* 0x12 offset in data sheet */
-#define CANVAS_WIDTH_LBIT      29
-#define CANVAS_WIDTH_LWID       3
-#define DMC_CAV_LUT_DATAH      0x4c /* 0x13 offset in data sheet */
-#define CANVAS_WIDTH_HBIT       0
-#define CANVAS_HEIGHT_BIT       9
-#define CANVAS_BLKMODE_BIT      24
-#define CANVAS_ENDIAN_BIT      26
-#define DMC_CAV_LUT_ADDR       0x50 /* 0x14 offset in data sheet */
-#define CANVAS_LUT_WR_EN        (0x2 << 8)
-#define CANVAS_LUT_RD_EN        (0x1 << 8)
-
-void meson_canvas_setup(struct meson_drm *priv,
-                       uint32_t canvas_index, uint32_t addr,
-                       uint32_t stride, uint32_t height,
-                       unsigned int wrap,
-                       unsigned int blkmode,
-                       unsigned int endian)
-{
-       unsigned int val;
-
-       regmap_write(priv->dmc, DMC_CAV_LUT_DATAL,
-               (((addr + 7) >> 3)) |
-               (((stride + 7) >> 3) << CANVAS_WIDTH_LBIT));
-
-       regmap_write(priv->dmc, DMC_CAV_LUT_DATAH,
-               ((((stride + 7) >> 3) >> CANVAS_WIDTH_LWID) <<
-                                               CANVAS_WIDTH_HBIT) |
-               (height << CANVAS_HEIGHT_BIT) |
-               (wrap << 22) |
-               (blkmode << CANVAS_BLKMODE_BIT) |
-               (endian << CANVAS_ENDIAN_BIT));
-
-       regmap_write(priv->dmc, DMC_CAV_LUT_ADDR,
-                       CANVAS_LUT_WR_EN | canvas_index);
-
-       /* Force a read-back to make sure everything is flushed. */
-       regmap_read(priv->dmc, DMC_CAV_LUT_DATAH, &val);
-}
diff --git a/drivers/gpu/drm/meson/meson_canvas.h b/drivers/gpu/drm/meson/meson_canvas.h
deleted file mode 100644 (file)
index 85dbf26..0000000
+++ /dev/null
@@ -1,51 +0,0 @@
-/*
- * Copyright (C) 2016 BayLibre, SAS
- * Author: Neil Armstrong <narmstrong@baylibre.com>
- * Copyright (C) 2014 Endless Mobile
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License as
- * published by the Free Software Foundation; either version 2 of the
- * License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, see <http://www.gnu.org/licenses/>.
- */
-
-/* Canvas LUT Memory */
-
-#ifndef __MESON_CANVAS_H
-#define __MESON_CANVAS_H
-
-#define MESON_CANVAS_ID_OSD1   0x4e
-#define MESON_CANVAS_ID_VD1_0  0x60
-#define MESON_CANVAS_ID_VD1_1  0x61
-#define MESON_CANVAS_ID_VD1_2  0x62
-
-/* Canvas configuration. */
-#define MESON_CANVAS_WRAP_NONE 0x00
-#define        MESON_CANVAS_WRAP_X     0x01
-#define        MESON_CANVAS_WRAP_Y     0x02
-
-#define        MESON_CANVAS_BLKMODE_LINEAR     0x00
-#define        MESON_CANVAS_BLKMODE_32x32      0x01
-#define        MESON_CANVAS_BLKMODE_64x64      0x02
-
-#define MESON_CANVAS_ENDIAN_SWAP16     0x1
-#define MESON_CANVAS_ENDIAN_SWAP32     0x3
-#define MESON_CANVAS_ENDIAN_SWAP64     0x7
-#define MESON_CANVAS_ENDIAN_SWAP128    0xf
-
-void meson_canvas_setup(struct meson_drm *priv,
-                       uint32_t canvas_index, uint32_t addr,
-                       uint32_t stride, uint32_t height,
-                       unsigned int wrap,
-                       unsigned int blkmode,
-                       unsigned int endian);
-
-#endif /* __MESON_CANVAS_H */
index 43e2998..6d9311e 100644 (file)
@@ -37,7 +37,6 @@
 #include "meson_venc.h"
 #include "meson_vpp.h"
 #include "meson_viu.h"
-#include "meson_canvas.h"
 #include "meson_registers.h"
 
 /* CRTC definition */
@@ -214,13 +213,7 @@ void meson_crtc_irq(struct meson_drm *priv)
                writel_relaxed(priv->viu.osd_sc_v_ctrl0,
                                priv->io_base + _REG(VPP_OSD_VSC_CTRL0));
 
-               if (priv->canvas)
-                       meson_canvas_config(priv->canvas, priv->canvas_id_osd1,
-                               priv->viu.osd1_addr, priv->viu.osd1_stride,
-                               priv->viu.osd1_height, MESON_CANVAS_WRAP_NONE,
-                               MESON_CANVAS_BLKMODE_LINEAR, 0);
-               else
-                       meson_canvas_setup(priv, MESON_CANVAS_ID_OSD1,
+               meson_canvas_config(priv->canvas, priv->canvas_id_osd1,
                                priv->viu.osd1_addr, priv->viu.osd1_stride,
                                priv->viu.osd1_height, MESON_CANVAS_WRAP_NONE,
                                MESON_CANVAS_BLKMODE_LINEAR, 0);
@@ -237,61 +230,34 @@ void meson_crtc_irq(struct meson_drm *priv)
 
                switch (priv->viu.vd1_planes) {
                case 3:
-                       if (priv->canvas)
-                               meson_canvas_config(priv->canvas,
-                                                   priv->canvas_id_vd1_2,
-                                                   priv->viu.vd1_addr2,
-                                                   priv->viu.vd1_stride2,
-                                                   priv->viu.vd1_height2,
-                                                   MESON_CANVAS_WRAP_NONE,
-                                                   MESON_CANVAS_BLKMODE_LINEAR,
-                                                   MESON_CANVAS_ENDIAN_SWAP64);
-                       else
-                               meson_canvas_setup(priv, MESON_CANVAS_ID_VD1_2,
-                                                  priv->viu.vd1_addr2,
-                                                  priv->viu.vd1_stride2,
-                                                  priv->viu.vd1_height2,
-                                                  MESON_CANVAS_WRAP_NONE,
-                                                  MESON_CANVAS_BLKMODE_LINEAR,
-                                                  MESON_CANVAS_ENDIAN_SWAP64);
+                       meson_canvas_config(priv->canvas,
+                                           priv->canvas_id_vd1_2,
+                                           priv->viu.vd1_addr2,
+                                           priv->viu.vd1_stride2,
+                                           priv->viu.vd1_height2,
+                                           MESON_CANVAS_WRAP_NONE,
+                                           MESON_CANVAS_BLKMODE_LINEAR,
+                                           MESON_CANVAS_ENDIAN_SWAP64);
                /* fallthrough */
                case 2:
-                       if (priv->canvas)
-                               meson_canvas_config(priv->canvas,
-                                                   priv->canvas_id_vd1_1,
-                                                   priv->viu.vd1_addr1,
-                                                   priv->viu.vd1_stride1,
-                                                   priv->viu.vd1_height1,
-                                                   MESON_CANVAS_WRAP_NONE,
-                                                   MESON_CANVAS_BLKMODE_LINEAR,
-                                                   MESON_CANVAS_ENDIAN_SWAP64);
-                       else
-                               meson_canvas_setup(priv, MESON_CANVAS_ID_VD1_1,
-                                                  priv->viu.vd1_addr2,
-                                                  priv->viu.vd1_stride2,
-                                                  priv->viu.vd1_height2,
-                                                  MESON_CANVAS_WRAP_NONE,
-                                                  MESON_CANVAS_BLKMODE_LINEAR,
-                                                  MESON_CANVAS_ENDIAN_SWAP64);
+                       meson_canvas_config(priv->canvas,
+                                           priv->canvas_id_vd1_1,
+                                           priv->viu.vd1_addr1,
+                                           priv->viu.vd1_stride1,
+                                           priv->viu.vd1_height1,
+                                           MESON_CANVAS_WRAP_NONE,
+                                           MESON_CANVAS_BLKMODE_LINEAR,
+                                           MESON_CANVAS_ENDIAN_SWAP64);
                /* fallthrough */
                case 1:
-                       if (priv->canvas)
-                               meson_canvas_config(priv->canvas,
-                                                   priv->canvas_id_vd1_0,
-                                                   priv->viu.vd1_addr0,
-                                                   priv->viu.vd1_stride0,
-                                                   priv->viu.vd1_height0,
-                                                   MESON_CANVAS_WRAP_NONE,
-                                                   MESON_CANVAS_BLKMODE_LINEAR,
-                                                   MESON_CANVAS_ENDIAN_SWAP64);
-                       else
-                               meson_canvas_setup(priv, MESON_CANVAS_ID_VD1_0,
-                                                  priv->viu.vd1_addr2,
-                                                  priv->viu.vd1_stride2,
-                                                  priv->viu.vd1_height2,
-                                                  MESON_CANVAS_WRAP_NONE,
-                                                  MESON_CANVAS_BLKMODE_LINEAR,
-                                                  MESON_CANVAS_ENDIAN_SWAP64);
+                       meson_canvas_config(priv->canvas,
+                                           priv->canvas_id_vd1_0,
+                                           priv->viu.vd1_addr0,
+                                           priv->viu.vd1_stride0,
+                                           priv->viu.vd1_height0,
+                                           MESON_CANVAS_WRAP_NONE,
+                                           MESON_CANVAS_BLKMODE_LINEAR,
+                                           MESON_CANVAS_ENDIAN_SWAP64);
                };
 
                writel_relaxed(priv->viu.vd1_if0_gen_reg,
index 2281ed3..70f9d7b 100644 (file)
@@ -48,7 +48,6 @@
 #include "meson_vpp.h"
 #include "meson_viu.h"
 #include "meson_venc.h"
-#include "meson_canvas.h"
 #include "meson_registers.h"
 
 #define DRIVER_NAME "meson"
@@ -231,50 +230,31 @@ static int meson_drv_bind_master(struct device *dev, bool has_components)
        }
 
        priv->canvas = meson_canvas_get(dev);
-       if (!IS_ERR(priv->canvas)) {
-               ret = meson_canvas_alloc(priv->canvas, &priv->canvas_id_osd1);
-               if (ret)
-                       goto free_drm;
-               ret = meson_canvas_alloc(priv->canvas, &priv->canvas_id_vd1_0);
-               if (ret) {
-                       meson_canvas_free(priv->canvas, priv->canvas_id_osd1);
-                       goto free_drm;
-               }
-               ret = meson_canvas_alloc(priv->canvas, &priv->canvas_id_vd1_1);
-               if (ret) {
-                       meson_canvas_free(priv->canvas, priv->canvas_id_osd1);
-                       meson_canvas_free(priv->canvas, priv->canvas_id_vd1_0);
-                       goto free_drm;
-               }
-               ret = meson_canvas_alloc(priv->canvas, &priv->canvas_id_vd1_2);
-               if (ret) {
-                       meson_canvas_free(priv->canvas, priv->canvas_id_osd1);
-                       meson_canvas_free(priv->canvas, priv->canvas_id_vd1_0);
-                       meson_canvas_free(priv->canvas, priv->canvas_id_vd1_1);
-                       goto free_drm;
-               }
-       } else {
-               priv->canvas = NULL;
-
-               res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "dmc");
-               if (!res) {
-                       ret = -EINVAL;
-                       goto free_drm;
-               }
-               /* Simply ioremap since it may be a shared register zone */
-               regs = devm_ioremap(dev, res->start, resource_size(res));
-               if (!regs) {
-                       ret = -EADDRNOTAVAIL;
-                       goto free_drm;
-               }
+       if (IS_ERR(priv->canvas)) {
+               ret = PTR_ERR(priv->canvas);
+               goto free_drm;
+       }
 
-               priv->dmc = devm_regmap_init_mmio(dev, regs,
-                                                 &meson_regmap_config);
-               if (IS_ERR(priv->dmc)) {
-                       dev_err(&pdev->dev, "Couldn't create the DMC regmap\n");
-                       ret = PTR_ERR(priv->dmc);
-                       goto free_drm;
-               }
+       ret = meson_canvas_alloc(priv->canvas, &priv->canvas_id_osd1);
+       if (ret)
+               goto free_drm;
+       ret = meson_canvas_alloc(priv->canvas, &priv->canvas_id_vd1_0);
+       if (ret) {
+               meson_canvas_free(priv->canvas, priv->canvas_id_osd1);
+               goto free_drm;
+       }
+       ret = meson_canvas_alloc(priv->canvas, &priv->canvas_id_vd1_1);
+       if (ret) {
+               meson_canvas_free(priv->canvas, priv->canvas_id_osd1);
+               meson_canvas_free(priv->canvas, priv->canvas_id_vd1_0);
+               goto free_drm;
+       }
+       ret = meson_canvas_alloc(priv->canvas, &priv->canvas_id_vd1_2);
+       if (ret) {
+               meson_canvas_free(priv->canvas, priv->canvas_id_osd1);
+               meson_canvas_free(priv->canvas, priv->canvas_id_vd1_0);
+               meson_canvas_free(priv->canvas, priv->canvas_id_vd1_1);
+               goto free_drm;
        }
 
        priv->vsync_irq = platform_get_irq(pdev, 0);
index 4dccf4c..214a7cb 100644 (file)
@@ -29,7 +29,6 @@ struct meson_drm {
        struct device *dev;
        void __iomem *io_base;
        struct regmap *hhi;
-       struct regmap *dmc;
        int vsync_irq;
 
        struct meson_canvas *canvas;
index 691a9fd..b54a22e 100644 (file)
@@ -22,7 +22,6 @@
 #include "meson_overlay.h"
 #include "meson_vpp.h"
 #include "meson_viu.h"
-#include "meson_canvas.h"
 #include "meson_registers.h"
 
 /* VD1_IF0_GEN_REG */
@@ -350,13 +349,6 @@ static void meson_overlay_atomic_update(struct drm_plane *plane,
 
        DRM_DEBUG_DRIVER("\n");
 
-       /* Fallback is canvas provider is not available */
-       if (!priv->canvas) {
-               priv->canvas_id_vd1_0 = MESON_CANVAS_ID_VD1_0;
-               priv->canvas_id_vd1_1 = MESON_CANVAS_ID_VD1_1;
-               priv->canvas_id_vd1_2 = MESON_CANVAS_ID_VD1_2;
-       }
-
        interlace_mode = state->crtc->mode.flags & DRM_MODE_FLAG_INTERLACE;
 
        spin_lock_irqsave(&priv->drm->event_lock, flags);
index 6119a02..b778621 100644 (file)
@@ -38,7 +38,6 @@
 #include "meson_plane.h"
 #include "meson_vpp.h"
 #include "meson_viu.h"
-#include "meson_canvas.h"
 #include "meson_registers.h"
 
 /* OSD_SCI_WH_M1 */
@@ -148,10 +147,7 @@ static void meson_plane_atomic_update(struct drm_plane *plane,
                                   (0xFF << OSD_GLOBAL_ALPHA_SHIFT) |
                                   OSD_BLK0_ENABLE;
 
-       if (priv->canvas)
-               canvas_id_osd1 = priv->canvas_id_osd1;
-       else
-               canvas_id_osd1 = MESON_CANVAS_ID_OSD1;
+       canvas_id_osd1 = priv->canvas_id_osd1;
 
        /* Set up BLK0 to point to the right canvas */
        priv->viu.osd1_blk0_cfg[0] = ((canvas_id_osd1 << OSD_CANVAS_SEL) |
index e46e05f..ac0f368 100644 (file)
@@ -25,7 +25,6 @@
 #include "meson_viu.h"
 #include "meson_vpp.h"
 #include "meson_venc.h"
-#include "meson_canvas.h"
 #include "meson_registers.h"
 
 /**
index 0bdd936..4697d85 100644 (file)
@@ -1027,7 +1027,6 @@ static struct drm_driver msm_driver = {
        .prime_fd_to_handle = drm_gem_prime_fd_to_handle,
        .gem_prime_export   = drm_gem_prime_export,
        .gem_prime_import   = drm_gem_prime_import,
-       .gem_prime_res_obj  = msm_gem_prime_res_obj,
        .gem_prime_pin      = msm_gem_prime_pin,
        .gem_prime_unpin    = msm_gem_prime_unpin,
        .gem_prime_get_sg_table = msm_gem_prime_get_sg_table,
index c56dade..163e24d 100644 (file)
@@ -292,7 +292,6 @@ struct sg_table *msm_gem_prime_get_sg_table(struct drm_gem_object *obj);
 void *msm_gem_prime_vmap(struct drm_gem_object *obj);
 void msm_gem_prime_vunmap(struct drm_gem_object *obj, void *vaddr);
 int msm_gem_prime_mmap(struct drm_gem_object *obj, struct vm_area_struct *vma);
-struct reservation_object *msm_gem_prime_res_obj(struct drm_gem_object *obj);
 struct drm_gem_object *msm_gem_prime_import_sg_table(struct drm_device *dev,
                struct dma_buf_attachment *attach, struct sg_table *sg);
 int msm_gem_prime_pin(struct drm_gem_object *obj);
index 18ca651..a72c648 100644 (file)
@@ -672,14 +672,13 @@ void msm_gem_vunmap(struct drm_gem_object *obj, enum msm_gem_lock subclass)
 int msm_gem_sync_object(struct drm_gem_object *obj,
                struct msm_fence_context *fctx, bool exclusive)
 {
-       struct msm_gem_object *msm_obj = to_msm_bo(obj);
        struct reservation_object_list *fobj;
        struct dma_fence *fence;
        int i, ret;
 
-       fobj = reservation_object_get_list(msm_obj->resv);
+       fobj = reservation_object_get_list(obj->resv);
        if (!fobj || (fobj->shared_count == 0)) {
-               fence = reservation_object_get_excl(msm_obj->resv);
+               fence = reservation_object_get_excl(obj->resv);
                /* don't need to wait on our own fences, since ring is fifo */
                if (fence && (fence->context != fctx->context)) {
                        ret = dma_fence_wait(fence, true);
@@ -693,7 +692,7 @@ int msm_gem_sync_object(struct drm_gem_object *obj,
 
        for (i = 0; i < fobj->shared_count; i++) {
                fence = rcu_dereference_protected(fobj->shared[i],
-                                               reservation_object_held(msm_obj->resv));
+                                               reservation_object_held(obj->resv));
                if (fence->context != fctx->context) {
                        ret = dma_fence_wait(fence, true);
                        if (ret)
@@ -711,9 +710,9 @@ void msm_gem_move_to_active(struct drm_gem_object *obj,
        WARN_ON(msm_obj->madv != MSM_MADV_WILLNEED);
        msm_obj->gpu = gpu;
        if (exclusive)
-               reservation_object_add_excl_fence(msm_obj->resv, fence);
+               reservation_object_add_excl_fence(obj->resv, fence);
        else
-               reservation_object_add_shared_fence(msm_obj->resv, fence);
+               reservation_object_add_shared_fence(obj->resv, fence);
        list_del_init(&msm_obj->mm_list);
        list_add_tail(&msm_obj->mm_list, &gpu->active_list);
 }
@@ -733,13 +732,12 @@ void msm_gem_move_to_inactive(struct drm_gem_object *obj)
 
 int msm_gem_cpu_prep(struct drm_gem_object *obj, uint32_t op, ktime_t *timeout)
 {
-       struct msm_gem_object *msm_obj = to_msm_bo(obj);
        bool write = !!(op & MSM_PREP_WRITE);
        unsigned long remain =
                op & MSM_PREP_NOSYNC ? 0 : timeout_to_jiffies(timeout);
        long ret;
 
-       ret = reservation_object_wait_timeout_rcu(msm_obj->resv, write,
+       ret = reservation_object_wait_timeout_rcu(obj->resv, write,
                                                  true,  remain);
        if (ret == 0)
                return remain == 0 ? -EBUSY : -ETIMEDOUT;
@@ -771,7 +769,7 @@ static void describe_fence(struct dma_fence *fence, const char *type,
 void msm_gem_describe(struct drm_gem_object *obj, struct seq_file *m)
 {
        struct msm_gem_object *msm_obj = to_msm_bo(obj);
-       struct reservation_object *robj = msm_obj->resv;
+       struct reservation_object *robj = obj->resv;
        struct reservation_object_list *fobj;
        struct dma_fence *fence;
        struct msm_gem_vma *vma;
@@ -883,9 +881,6 @@ void msm_gem_free_object(struct drm_gem_object *obj)
                put_pages(obj);
        }
 
-       if (msm_obj->resv == &msm_obj->_resv)
-               reservation_object_fini(msm_obj->resv);
-
        drm_gem_object_release(obj);
 
        mutex_unlock(&msm_obj->lock);
@@ -945,12 +940,8 @@ static int msm_gem_new_impl(struct drm_device *dev,
        msm_obj->flags = flags;
        msm_obj->madv = MSM_MADV_WILLNEED;
 
-       if (resv) {
-               msm_obj->resv = resv;
-       } else {
-               msm_obj->resv = &msm_obj->_resv;
-               reservation_object_init(msm_obj->resv);
-       }
+       if (resv)
+               msm_obj->base.resv = resv;
 
        INIT_LIST_HEAD(&msm_obj->submit_entry);
        INIT_LIST_HEAD(&msm_obj->vmas);
index 13403c6..60bb290 100644 (file)
@@ -70,10 +70,3 @@ void msm_gem_prime_unpin(struct drm_gem_object *obj)
        if (!obj->import_attach)
                msm_gem_put_pages(obj);
 }
-
-struct reservation_object *msm_gem_prime_res_obj(struct drm_gem_object *obj)
-{
-       struct msm_gem_object *msm_obj = to_msm_bo(obj);
-
-       return msm_obj->resv;
-}
index 12b983f..df30252 100644 (file)
@@ -173,7 +173,7 @@ static void submit_unlock_unpin_bo(struct msm_gem_submit *submit,
                msm_gem_unpin_iova(&msm_obj->base, submit->gpu->aspace);
 
        if (submit->bos[i].flags & BO_LOCKED)
-               ww_mutex_unlock(&msm_obj->resv->lock);
+               ww_mutex_unlock(&msm_obj->base.resv->lock);
 
        if (backoff && !(submit->bos[i].flags & BO_VALID))
                submit->bos[i].iova = 0;
@@ -196,7 +196,7 @@ retry:
                contended = i;
 
                if (!(submit->bos[i].flags & BO_LOCKED)) {
-                       ret = ww_mutex_lock_interruptible(&msm_obj->resv->lock,
+                       ret = ww_mutex_lock_interruptible(&msm_obj->base.resv->lock,
                                        &submit->ticket);
                        if (ret)
                                goto fail;
@@ -218,7 +218,7 @@ fail:
        if (ret == -EDEADLK) {
                struct msm_gem_object *msm_obj = submit->bos[contended].obj;
                /* we lost out in a seqno race, lock and retry.. */
-               ret = ww_mutex_lock_slow_interruptible(&msm_obj->resv->lock,
+               ret = ww_mutex_lock_slow_interruptible(&msm_obj->base.resv->lock,
                                &submit->ticket);
                if (!ret) {
                        submit->bos[contended].flags |= BO_LOCKED;
@@ -244,7 +244,7 @@ static int submit_fence_sync(struct msm_gem_submit *submit, bool no_implicit)
                         * strange place to call it.  OTOH this is a
                         * convenient can-fail point to hook it in.
                         */
-                       ret = reservation_object_reserve_shared(msm_obj->resv,
+                       ret = reservation_object_reserve_shared(msm_obj->base.resv,
                                                                1);
                        if (ret)
                                return ret;
index 3e07015..f53f817 100644 (file)
@@ -149,6 +149,15 @@ config DRM_PANEL_RAYDIUM_RM68200
          Say Y here if you want to enable support for Raydium RM68200
          720x1280 DSI video mode panel.
 
+config DRM_PANEL_RONBO_RB070D30
+       tristate "Ronbo Electronics RB070D30 panel"
+       depends on OF
+       depends on DRM_MIPI_DSI
+       depends on BACKLIGHT_CLASS_DEVICE
+       help
+         Say Y here if you want to enable support for Ronbo Electronics
+         RB070D30 1024x600 DSI panel.
+
 config DRM_PANEL_SAMSUNG_S6D16D0
        tristate "Samsung S6D16D0 DSI video mode panel"
        depends on OF
index e7ab719..7834947 100644 (file)
@@ -13,6 +13,7 @@ obj-$(CONFIG_DRM_PANEL_ORISETECH_OTM8009A) += panel-orisetech-otm8009a.o
 obj-$(CONFIG_DRM_PANEL_PANASONIC_VVX10F034N00) += panel-panasonic-vvx10f034n00.o
 obj-$(CONFIG_DRM_PANEL_RASPBERRYPI_TOUCHSCREEN) += panel-raspberrypi-touchscreen.o
 obj-$(CONFIG_DRM_PANEL_RAYDIUM_RM68200) += panel-raydium-rm68200.o
+obj-$(CONFIG_DRM_PANEL_RONBO_RB070D30) += panel-ronbo-rb070d30.o
 obj-$(CONFIG_DRM_PANEL_SAMSUNG_LD9040) += panel-samsung-ld9040.o
 obj-$(CONFIG_DRM_PANEL_SAMSUNG_S6D16D0) += panel-samsung-s6d16d0.o
 obj-$(CONFIG_DRM_PANEL_SAMSUNG_S6E3HA2) += panel-samsung-s6e3ha2.o
diff --git a/drivers/gpu/drm/panel/panel-ronbo-rb070d30.c b/drivers/gpu/drm/panel/panel-ronbo-rb070d30.c
new file mode 100644 (file)
index 0000000..3c15764
--- /dev/null
@@ -0,0 +1,258 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2018-2019, Bridge Systems BV
+ * Copyright (C) 2018-2019, Bootlin
+ * Copyright (C) 2017, Free Electrons
+ *
+ * This file based on panel-ilitek-ili9881c.c
+ */
+
+#include <linux/backlight.h>
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/err.h>
+#include <linux/errno.h>
+#include <linux/fb.h>
+#include <linux/kernel.h>
+#include <linux/media-bus-format.h>
+#include <linux/module.h>
+
+#include <linux/gpio/consumer.h>
+#include <linux/regulator/consumer.h>
+
+#include <drm/drm_connector.h>
+#include <drm/drm_mipi_dsi.h>
+#include <drm/drm_modes.h>
+#include <drm/drm_panel.h>
+#include <drm/drm_print.h>
+
+struct rb070d30_panel {
+       struct drm_panel panel;
+       struct mipi_dsi_device *dsi;
+       struct backlight_device *backlight;
+       struct regulator *supply;
+
+       struct {
+               struct gpio_desc *power;
+               struct gpio_desc *reset;
+               struct gpio_desc *updn;
+               struct gpio_desc *shlr;
+       } gpios;
+};
+
+static inline struct rb070d30_panel *panel_to_rb070d30_panel(struct drm_panel *panel)
+{
+       return container_of(panel, struct rb070d30_panel, panel);
+}
+
+static int rb070d30_panel_prepare(struct drm_panel *panel)
+{
+       struct rb070d30_panel *ctx = panel_to_rb070d30_panel(panel);
+       int ret;
+
+       ret = regulator_enable(ctx->supply);
+       if (ret < 0) {
+               DRM_DEV_ERROR(&ctx->dsi->dev, "Failed to enable supply: %d\n", ret);
+               return ret;
+       }
+
+       msleep(20);
+       gpiod_set_value(ctx->gpios.power, 1);
+       msleep(20);
+       gpiod_set_value(ctx->gpios.reset, 1);
+       msleep(20);
+       return 0;
+}
+
+static int rb070d30_panel_unprepare(struct drm_panel *panel)
+{
+       struct rb070d30_panel *ctx = panel_to_rb070d30_panel(panel);
+
+       gpiod_set_value(ctx->gpios.reset, 0);
+       gpiod_set_value(ctx->gpios.power, 0);
+       regulator_disable(ctx->supply);
+
+       return 0;
+}
+
+static int rb070d30_panel_enable(struct drm_panel *panel)
+{
+       struct rb070d30_panel *ctx = panel_to_rb070d30_panel(panel);
+       int ret;
+
+       ret = mipi_dsi_dcs_exit_sleep_mode(ctx->dsi);
+       if (ret)
+               return ret;
+
+       ret = backlight_enable(ctx->backlight);
+       if (ret)
+               goto out;
+
+       return 0;
+
+out:
+       mipi_dsi_dcs_enter_sleep_mode(ctx->dsi);
+       return ret;
+}
+
+static int rb070d30_panel_disable(struct drm_panel *panel)
+{
+       struct rb070d30_panel *ctx = panel_to_rb070d30_panel(panel);
+
+       backlight_disable(ctx->backlight);
+       return mipi_dsi_dcs_enter_sleep_mode(ctx->dsi);
+}
+
+/* Default timings */
+static const struct drm_display_mode default_mode = {
+       .clock          = 51206,
+       .hdisplay       = 1024,
+       .hsync_start    = 1024 + 160,
+       .hsync_end      = 1024 + 160 + 80,
+       .htotal         = 1024 + 160 + 80 + 80,
+       .vdisplay       = 600,
+       .vsync_start    = 600 + 12,
+       .vsync_end      = 600 + 12 + 10,
+       .vtotal         = 600 + 12 + 10 + 13,
+       .vrefresh       = 60,
+
+       .width_mm       = 154,
+       .height_mm      = 85,
+};
+
+static int rb070d30_panel_get_modes(struct drm_panel *panel)
+{
+       struct drm_connector *connector = panel->connector;
+       struct rb070d30_panel *ctx = panel_to_rb070d30_panel(panel);
+       struct drm_display_mode *mode;
+       static const u32 bus_format = MEDIA_BUS_FMT_RGB888_1X24;
+
+       mode = drm_mode_duplicate(panel->drm, &default_mode);
+       if (!mode) {
+               DRM_DEV_ERROR(&ctx->dsi->dev,
+                             "Failed to add mode " DRM_MODE_FMT "\n",
+                             DRM_MODE_ARG(&default_mode));
+               return -EINVAL;
+       }
+
+       drm_mode_set_name(mode);
+
+       mode->type = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED;
+       drm_mode_probed_add(connector, mode);
+
+       panel->connector->display_info.bpc = 8;
+       panel->connector->display_info.width_mm = mode->width_mm;
+       panel->connector->display_info.height_mm = mode->height_mm;
+       drm_display_info_set_bus_formats(&connector->display_info,
+                                        &bus_format, 1);
+
+       return 1;
+}
+
+static const struct drm_panel_funcs rb070d30_panel_funcs = {
+       .get_modes      = rb070d30_panel_get_modes,
+       .prepare        = rb070d30_panel_prepare,
+       .enable         = rb070d30_panel_enable,
+       .disable        = rb070d30_panel_disable,
+       .unprepare      = rb070d30_panel_unprepare,
+};
+
+static int rb070d30_panel_dsi_probe(struct mipi_dsi_device *dsi)
+{
+       struct rb070d30_panel *ctx;
+       int ret;
+
+       ctx = devm_kzalloc(&dsi->dev, sizeof(*ctx), GFP_KERNEL);
+       if (!ctx)
+               return -ENOMEM;
+
+       ctx->supply = devm_regulator_get(&dsi->dev, "vcc-lcd");
+       if (IS_ERR(ctx->supply))
+               return PTR_ERR(ctx->supply);
+
+       mipi_dsi_set_drvdata(dsi, ctx);
+       ctx->dsi = dsi;
+
+       drm_panel_init(&ctx->panel);
+       ctx->panel.dev = &dsi->dev;
+       ctx->panel.funcs = &rb070d30_panel_funcs;
+
+       ctx->gpios.reset = devm_gpiod_get(&dsi->dev, "reset", GPIOD_OUT_LOW);
+       if (IS_ERR(ctx->gpios.reset)) {
+               DRM_DEV_ERROR(&dsi->dev, "Couldn't get our reset GPIO\n");
+               return PTR_ERR(ctx->gpios.reset);
+       }
+
+       ctx->gpios.power = devm_gpiod_get(&dsi->dev, "power", GPIOD_OUT_LOW);
+       if (IS_ERR(ctx->gpios.power)) {
+               DRM_DEV_ERROR(&dsi->dev, "Couldn't get our power GPIO\n");
+               return PTR_ERR(ctx->gpios.power);
+       }
+
+       /*
+        * We don't change the state of that GPIO later on but we need
+        * to force it into a low state.
+        */
+       ctx->gpios.updn = devm_gpiod_get(&dsi->dev, "updn", GPIOD_OUT_LOW);
+       if (IS_ERR(ctx->gpios.updn)) {
+               DRM_DEV_ERROR(&dsi->dev, "Couldn't get our updn GPIO\n");
+               return PTR_ERR(ctx->gpios.updn);
+       }
+
+       /*
+        * We don't change the state of that GPIO later on but we need
+        * to force it into a low state.
+        */
+       ctx->gpios.shlr = devm_gpiod_get(&dsi->dev, "shlr", GPIOD_OUT_LOW);
+       if (IS_ERR(ctx->gpios.shlr)) {
+               DRM_DEV_ERROR(&dsi->dev, "Couldn't get our shlr GPIO\n");
+               return PTR_ERR(ctx->gpios.shlr);
+       }
+
+       ctx->backlight = devm_of_find_backlight(&dsi->dev);
+       if (IS_ERR(ctx->backlight)) {
+               DRM_DEV_ERROR(&dsi->dev, "Couldn't get our backlight\n");
+               return PTR_ERR(ctx->backlight);
+       }
+
+       ret = drm_panel_add(&ctx->panel);
+       if (ret < 0)
+               return ret;
+
+       dsi->mode_flags = MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_VIDEO_BURST | MIPI_DSI_MODE_LPM;
+       dsi->format = MIPI_DSI_FMT_RGB888;
+       dsi->lanes = 4;
+
+       return mipi_dsi_attach(dsi);
+}
+
+static int rb070d30_panel_dsi_remove(struct mipi_dsi_device *dsi)
+{
+       struct rb070d30_panel *ctx = mipi_dsi_get_drvdata(dsi);
+
+       mipi_dsi_detach(dsi);
+       drm_panel_remove(&ctx->panel);
+
+       return 0;
+}
+
+static const struct of_device_id rb070d30_panel_of_match[] = {
+       { .compatible = "ronbo,rb070d30" },
+       { /* sentinel */ },
+};
+MODULE_DEVICE_TABLE(of, rb070d30_panel_of_match);
+
+static struct mipi_dsi_driver rb070d30_panel_driver = {
+       .probe = rb070d30_panel_dsi_probe,
+       .remove = rb070d30_panel_dsi_remove,
+       .driver = {
+               .name = "panel-ronbo-rb070d30",
+               .of_match_table = rb070d30_panel_of_match,
+       },
+};
+module_mipi_dsi_driver(rb070d30_panel_driver);
+
+MODULE_AUTHOR("Boris Brezillon <boris.brezillon@bootlin.com>");
+MODULE_AUTHOR("Konstantin Sudakov <k.sudakov@integrasources.com>");
+MODULE_DESCRIPTION("Ronbo RB070D30 Panel Driver");
+MODULE_LICENSE("GPL");
index 08c7255..8b319eb 100644 (file)
@@ -535,7 +535,7 @@ static void qxl_primary_atomic_update(struct drm_plane *plane,
 {
        struct qxl_device *qdev = plane->dev->dev_private;
        struct qxl_bo *bo = gem_to_qxl_bo(plane->state->fb->obj[0]);
-       struct qxl_bo *bo_old, *primary;
+       struct qxl_bo *primary;
        struct drm_clip_rect norect = {
            .x1 = 0,
            .y1 = 0,
@@ -544,12 +544,6 @@ static void qxl_primary_atomic_update(struct drm_plane *plane,
        };
        uint32_t dumb_shadow_offset = 0;
 
-       if (old_state->fb) {
-               bo_old = gem_to_qxl_bo(old_state->fb->obj[0]);
-       } else {
-               bo_old = NULL;
-       }
-
        primary = bo->shadow ? bo->shadow : bo;
 
        if (!primary->is_primary) {
index 53f29a1..0a9312e 100644 (file)
@@ -1388,7 +1388,7 @@ int radeon_device_init(struct radeon_device *rdev,
                pci_set_consistent_dma_mask(rdev->pdev, DMA_BIT_MASK(32));
                pr_warn("radeon: No coherent DMA available\n");
        }
-       rdev->need_swiotlb = drm_get_max_iomem() > ((u64)1 << dma_bits);
+       rdev->need_swiotlb = drm_need_swiotlb(dma_bits);
 
        /* Registers mapping */
        /* TODO: block userspace mapping of io register */
index fbed2c9..286a0ee 100644 (file)
@@ -1615,7 +1615,7 @@ static int igt_topdown(void *ignored)
        DRM_RND_STATE(prng, random_seed);
        const unsigned int count = 8192;
        unsigned int size;
-       unsigned long *bitmap = NULL;
+       unsigned long *bitmap;
        struct drm_mm mm;
        struct drm_mm_node *nodes, *node, *next;
        unsigned int *order, n, m, o = 0;
@@ -1631,8 +1631,7 @@ static int igt_topdown(void *ignored)
        if (!nodes)
                goto err;
 
-       bitmap = kcalloc(count / BITS_PER_LONG, sizeof(unsigned long),
-                        GFP_KERNEL);
+       bitmap = bitmap_zalloc(count, GFP_KERNEL);
        if (!bitmap)
                goto err_nodes;
 
@@ -1717,7 +1716,7 @@ out:
        drm_mm_takedown(&mm);
        kfree(order);
 err_bitmap:
-       kfree(bitmap);
+       bitmap_free(bitmap);
 err_nodes:
        vfree(nodes);
 err:
@@ -1745,8 +1744,7 @@ static int igt_bottomup(void *ignored)
        if (!nodes)
                goto err;
 
-       bitmap = kcalloc(count / BITS_PER_LONG, sizeof(unsigned long),
-                        GFP_KERNEL);
+       bitmap = bitmap_zalloc(count, GFP_KERNEL);
        if (!bitmap)
                goto err_nodes;
 
@@ -1818,7 +1816,7 @@ out:
        drm_mm_takedown(&mm);
        kfree(order);
 err_bitmap:
-       kfree(bitmap);
+       bitmap_free(bitmap);
 err_nodes:
        vfree(nodes);
 err:
index 35367ad..d15b10d 100644 (file)
@@ -6,7 +6,7 @@ config DRM_STM
        select DRM_KMS_CMA_HELPER
        select DRM_PANEL_BRIDGE
        select VIDEOMODE_HELPERS
-       select FB_PROVIDE_GET_FB_UNMAPPED_AREA
+       select FB_PROVIDE_GET_FB_UNMAPPED_AREA if FB
 
        help
          Enable support for the on-chip display controller on
index 4c0d51f..ee59da4 100644 (file)
@@ -720,33 +720,22 @@ static int sun4i_backend_free_sat(struct device *dev) {
  */
 static int sun4i_backend_of_get_id(struct device_node *node)
 {
-       struct device_node *port, *ep;
-       int ret = -EINVAL;
+       struct device_node *ep, *remote;
+       struct of_endpoint of_ep;
 
-       /* input is port 0 */
-       port = of_graph_get_port_by_id(node, 0);
-       if (!port)
+       /* Input port is 0, and we want the first endpoint. */
+       ep = of_graph_get_endpoint_by_regs(node, 0, -1);
+       if (!ep)
                return -EINVAL;
 
-       /* try finding an upstream endpoint */
-       for_each_available_child_of_node(port, ep) {
-               struct device_node *remote;
-               u32 reg;
-
-               remote = of_graph_get_remote_endpoint(ep);
-               if (!remote)
-                       continue;
-
-               ret = of_property_read_u32(remote, "reg", &reg);
-               if (ret)
-                       continue;
-
-               ret = reg;
-       }
-
-       of_node_put(port);
+       remote = of_graph_get_remote_endpoint(ep);
+       of_node_put(ep);
+       if (!remote)
+               return -EINVAL;
 
-       return ret;
+       of_graph_parse_endpoint(remote, &of_ep);
+       of_node_put(remote);
+       return of_ep.id;
 }
 
 /* TODO: This needs to take multiple pipelines into account */
index 147b97e..3a3ba99 100644 (file)
@@ -20,7 +20,7 @@ struct sun4i_lvds {
        struct drm_connector    connector;
        struct drm_encoder      encoder;
 
-       struct sun4i_tcon       *tcon;
+       struct drm_panel        *panel;
 };
 
 static inline struct sun4i_lvds *
@@ -41,9 +41,8 @@ static int sun4i_lvds_get_modes(struct drm_connector *connector)
 {
        struct sun4i_lvds *lvds =
                drm_connector_to_sun4i_lvds(connector);
-       struct sun4i_tcon *tcon = lvds->tcon;
 
-       return drm_panel_get_modes(tcon->panel);
+       return drm_panel_get_modes(lvds->panel);
 }
 
 static struct drm_connector_helper_funcs sun4i_lvds_con_helper_funcs = {
@@ -54,9 +53,8 @@ static void
 sun4i_lvds_connector_destroy(struct drm_connector *connector)
 {
        struct sun4i_lvds *lvds = drm_connector_to_sun4i_lvds(connector);
-       struct sun4i_tcon *tcon = lvds->tcon;
 
-       drm_panel_detach(tcon->panel);
+       drm_panel_detach(lvds->panel);
        drm_connector_cleanup(connector);
 }
 
@@ -71,26 +69,24 @@ static const struct drm_connector_funcs sun4i_lvds_con_funcs = {
 static void sun4i_lvds_encoder_enable(struct drm_encoder *encoder)
 {
        struct sun4i_lvds *lvds = drm_encoder_to_sun4i_lvds(encoder);
-       struct sun4i_tcon *tcon = lvds->tcon;
 
        DRM_DEBUG_DRIVER("Enabling LVDS output\n");
 
-       if (tcon->panel) {
-               drm_panel_prepare(tcon->panel);
-               drm_panel_enable(tcon->panel);
+       if (lvds->panel) {
+               drm_panel_prepare(lvds->panel);
+               drm_panel_enable(lvds->panel);
        }
 }
 
 static void sun4i_lvds_encoder_disable(struct drm_encoder *encoder)
 {
        struct sun4i_lvds *lvds = drm_encoder_to_sun4i_lvds(encoder);
-       struct sun4i_tcon *tcon = lvds->tcon;
 
        DRM_DEBUG_DRIVER("Disabling LVDS output\n");
 
-       if (tcon->panel) {
-               drm_panel_disable(tcon->panel);
-               drm_panel_unprepare(tcon->panel);
+       if (lvds->panel) {
+               drm_panel_disable(lvds->panel);
+               drm_panel_unprepare(lvds->panel);
        }
 }
 
@@ -113,11 +109,10 @@ int sun4i_lvds_init(struct drm_device *drm, struct sun4i_tcon *tcon)
        lvds = devm_kzalloc(drm->dev, sizeof(*lvds), GFP_KERNEL);
        if (!lvds)
                return -ENOMEM;
-       lvds->tcon = tcon;
        encoder = &lvds->encoder;
 
        ret = drm_of_find_panel_or_bridge(tcon->dev->of_node, 1, 0,
-                                         &tcon->panel, &bridge);
+                                         &lvds->panel, &bridge);
        if (ret) {
                dev_info(drm->dev, "No panel or bridge found... LVDS output disabled\n");
                return 0;
@@ -138,7 +133,7 @@ int sun4i_lvds_init(struct drm_device *drm, struct sun4i_tcon *tcon)
        /* The LVDS encoder can only work with the TCON channel 0 */
        lvds->encoder.possible_crtcs = drm_crtc_mask(&tcon->crtc->crtc);
 
-       if (tcon->panel) {
+       if (lvds->panel) {
                drm_connector_helper_add(&lvds->connector,
                                         &sun4i_lvds_con_helper_funcs);
                ret = drm_connector_init(drm, &lvds->connector,
@@ -152,7 +147,7 @@ int sun4i_lvds_init(struct drm_device *drm, struct sun4i_tcon *tcon)
                drm_connector_attach_encoder(&lvds->connector,
                                                  &lvds->encoder);
 
-               ret = drm_panel_attach(tcon->panel, &lvds->connector);
+               ret = drm_panel_attach(lvds->panel, &lvds->connector);
                if (ret) {
                        dev_err(drm->dev, "Couldn't attach our panel\n");
                        goto err_cleanup_connector;
index cae19e7..d9e2502 100644 (file)
@@ -27,6 +27,8 @@ struct sun4i_rgb {
        struct drm_encoder      encoder;
 
        struct sun4i_tcon       *tcon;
+       struct drm_panel        *panel;
+       struct drm_bridge       *bridge;
 };
 
 static inline struct sun4i_rgb *
@@ -47,11 +49,18 @@ static int sun4i_rgb_get_modes(struct drm_connector *connector)
 {
        struct sun4i_rgb *rgb =
                drm_connector_to_sun4i_rgb(connector);
-       struct sun4i_tcon *tcon = rgb->tcon;
 
-       return drm_panel_get_modes(tcon->panel);
+       return drm_panel_get_modes(rgb->panel);
 }
 
+/*
+ * VESA DMT defines a tolerance of 0.5% on the pixel clock, while the
+ * CVT spec reuses that tolerance in its examples, so it looks to be a
+ * good default tolerance for the EDID-based modes. Define it to 5 per
+ * mille to avoid floating point operations.
+ */
+#define SUN4I_RGB_DOTCLOCK_TOLERANCE_PER_MILLE 5
+
 static enum drm_mode_status sun4i_rgb_mode_valid(struct drm_encoder *crtc,
                                                 const struct drm_display_mode *mode)
 {
@@ -59,8 +68,9 @@ static enum drm_mode_status sun4i_rgb_mode_valid(struct drm_encoder *crtc,
        struct sun4i_tcon *tcon = rgb->tcon;
        u32 hsync = mode->hsync_end - mode->hsync_start;
        u32 vsync = mode->vsync_end - mode->vsync_start;
-       unsigned long rate = mode->clock * 1000;
-       long rounded_rate;
+       unsigned long long rate = mode->clock * 1000;
+       unsigned long long lowest, highest;
+       unsigned long long rounded_rate;
 
        DRM_DEBUG_DRIVER("Validating modes...\n");
 
@@ -92,15 +102,39 @@ static enum drm_mode_status sun4i_rgb_mode_valid(struct drm_encoder *crtc,
 
        DRM_DEBUG_DRIVER("Vertical parameters OK\n");
 
+       /*
+        * TODO: We should use the struct display_timing if available
+        * and / or trying to stretch the timings within that
+        * tolerancy to take care of panels that we wouldn't be able
+        * to have a exact match for.
+        */
+       if (rgb->panel) {
+               DRM_DEBUG_DRIVER("RGB panel used, skipping clock rate checks");
+               goto out;
+       }
+
+       /*
+        * That shouldn't ever happen unless something is really wrong, but it
+        * doesn't harm to check.
+        */
+       if (!rgb->bridge)
+               goto out;
+
        tcon->dclk_min_div = 6;
        tcon->dclk_max_div = 127;
        rounded_rate = clk_round_rate(tcon->dclk, rate);
-       if (rounded_rate < rate)
+
+       lowest = rate * (1000 - SUN4I_RGB_DOTCLOCK_TOLERANCE_PER_MILLE);
+       do_div(lowest, 1000);
+       if (rounded_rate < lowest)
                return MODE_CLOCK_LOW;
 
-       if (rounded_rate > rate)
+       highest = rate * (1000 + SUN4I_RGB_DOTCLOCK_TOLERANCE_PER_MILLE);
+       do_div(highest, 1000);
+       if (rounded_rate > highest)
                return MODE_CLOCK_HIGH;
 
+out:
        DRM_DEBUG_DRIVER("Clock rate OK\n");
 
        return MODE_OK;
@@ -114,9 +148,8 @@ static void
 sun4i_rgb_connector_destroy(struct drm_connector *connector)
 {
        struct sun4i_rgb *rgb = drm_connector_to_sun4i_rgb(connector);
-       struct sun4i_tcon *tcon = rgb->tcon;
 
-       drm_panel_detach(tcon->panel);
+       drm_panel_detach(rgb->panel);
        drm_connector_cleanup(connector);
 }
 
@@ -131,26 +164,24 @@ static const struct drm_connector_funcs sun4i_rgb_con_funcs = {
 static void sun4i_rgb_encoder_enable(struct drm_encoder *encoder)
 {
        struct sun4i_rgb *rgb = drm_encoder_to_sun4i_rgb(encoder);
-       struct sun4i_tcon *tcon = rgb->tcon;
 
        DRM_DEBUG_DRIVER("Enabling RGB output\n");
 
-       if (tcon->panel) {
-               drm_panel_prepare(tcon->panel);
-               drm_panel_enable(tcon->panel);
+       if (rgb->panel) {
+               drm_panel_prepare(rgb->panel);
+               drm_panel_enable(rgb->panel);
        }
 }
 
 static void sun4i_rgb_encoder_disable(struct drm_encoder *encoder)
 {
        struct sun4i_rgb *rgb = drm_encoder_to_sun4i_rgb(encoder);
-       struct sun4i_tcon *tcon = rgb->tcon;
 
        DRM_DEBUG_DRIVER("Disabling RGB output\n");
 
-       if (tcon->panel) {
-               drm_panel_disable(tcon->panel);
-               drm_panel_unprepare(tcon->panel);
+       if (rgb->panel) {
+               drm_panel_disable(rgb->panel);
+               drm_panel_unprepare(rgb->panel);
        }
 }
 
@@ -172,7 +203,6 @@ static struct drm_encoder_funcs sun4i_rgb_enc_funcs = {
 int sun4i_rgb_init(struct drm_device *drm, struct sun4i_tcon *tcon)
 {
        struct drm_encoder *encoder;
-       struct drm_bridge *bridge;
        struct sun4i_rgb *rgb;
        int ret;
 
@@ -183,7 +213,7 @@ int sun4i_rgb_init(struct drm_device *drm, struct sun4i_tcon *tcon)
        encoder = &rgb->encoder;
 
        ret = drm_of_find_panel_or_bridge(tcon->dev->of_node, 1, 0,
-                                         &tcon->panel, &bridge);
+                                         &rgb->panel, &rgb->bridge);
        if (ret) {
                dev_info(drm->dev, "No panel or bridge found... RGB output disabled\n");
                return 0;
@@ -204,7 +234,7 @@ int sun4i_rgb_init(struct drm_device *drm, struct sun4i_tcon *tcon)
        /* The RGB encoder can only work with the TCON channel 0 */
        rgb->encoder.possible_crtcs = drm_crtc_mask(&tcon->crtc->crtc);
 
-       if (tcon->panel) {
+       if (rgb->panel) {
                drm_connector_helper_add(&rgb->connector,
                                         &sun4i_rgb_con_helper_funcs);
                ret = drm_connector_init(drm, &rgb->connector,
@@ -218,15 +248,15 @@ int sun4i_rgb_init(struct drm_device *drm, struct sun4i_tcon *tcon)
                drm_connector_attach_encoder(&rgb->connector,
                                                  &rgb->encoder);
 
-               ret = drm_panel_attach(tcon->panel, &rgb->connector);
+               ret = drm_panel_attach(rgb->panel, &rgb->connector);
                if (ret) {
                        dev_err(drm->dev, "Couldn't attach our panel\n");
                        goto err_cleanup_connector;
                }
        }
 
-       if (bridge) {
-               ret = drm_bridge_attach(encoder, bridge, NULL);
+       if (rgb->bridge) {
+               ret = drm_bridge_attach(encoder, rgb->bridge, NULL);
                if (ret) {
                        dev_err(drm->dev, "Couldn't attach our bridge\n");
                        goto err_cleanup_connector;
index ca713d2..fa92e99 100644 (file)
@@ -341,8 +341,8 @@ static void sun4i_tcon0_mode_set_cpu(struct sun4i_tcon *tcon,
        u32 block_space, start_delay;
        u32 tcon_div;
 
-       tcon->dclk_min_div = 4;
-       tcon->dclk_max_div = 127;
+       tcon->dclk_min_div = SUN6I_DSI_TCON_DIV;
+       tcon->dclk_max_div = SUN6I_DSI_TCON_DIV;
 
        sun4i_tcon0_mode_set_common(tcon, mode);
 
index b5214d7..84cfb19 100644 (file)
@@ -257,8 +257,6 @@ struct sun4i_tcon {
        struct reset_control            *lcd_rst;
        struct reset_control            *lvds_rst;
 
-       struct drm_panel                *panel;
-
        /* Platform adjustments */
        const struct sun4i_tcon_quirks  *quirks;
 
index 318994c..6ff5850 100644 (file)
@@ -24,7 +24,9 @@
 #include <drm/drm_panel.h>
 #include <drm/drm_probe_helper.h>
 
+#include "sun4i_crtc.h"
 #include "sun4i_drv.h"
+#include "sun4i_tcon.h"
 #include "sun6i_mipi_dsi.h"
 
 #include <video/mipi_display.h>
@@ -33,6 +35,8 @@
 #define SUN6I_DSI_CTL_EN                       BIT(0)
 
 #define SUN6I_DSI_BASIC_CTL_REG                0x00c
+#define SUN6I_DSI_BASIC_CTL_TRAIL_INV(n)               (((n) & 0xf) << 4)
+#define SUN6I_DSI_BASIC_CTL_TRAIL_FILL         BIT(3)
 #define SUN6I_DSI_BASIC_CTL_HBP_DIS            BIT(2)
 #define SUN6I_DSI_BASIC_CTL_HSA_HSE_DIS                BIT(1)
 #define SUN6I_DSI_BASIC_CTL_VIDEO_BURST                BIT(0)
 
 #define SUN6I_DSI_CMD_TX_REG(n)                (0x300 + (n) * 0x04)
 
+#define SUN6I_DSI_SYNC_POINT           40
+
 enum sun6i_dsi_start_inst {
        DSI_START_LPRX,
        DSI_START_LPTX,
@@ -358,7 +364,54 @@ static void sun6i_dsi_inst_init(struct sun6i_dsi *dsi,
 static u16 sun6i_dsi_get_video_start_delay(struct sun6i_dsi *dsi,
                                           struct drm_display_mode *mode)
 {
-       return mode->vtotal - (mode->vsync_end - mode->vdisplay) + 1;
+       u16 start = clamp(mode->vtotal - mode->vdisplay - 10, 8, 100);
+       u16 delay = mode->vtotal - (mode->vsync_end - mode->vdisplay) + start;
+
+       if (delay > mode->vtotal)
+               delay = delay % mode->vtotal;
+
+       return max_t(u16, delay, 1);
+}
+
+static u16 sun6i_dsi_get_line_num(struct sun6i_dsi *dsi,
+                                 struct drm_display_mode *mode)
+{
+       struct mipi_dsi_device *device = dsi->device;
+       unsigned int Bpp = mipi_dsi_pixel_format_to_bpp(device->format) / 8;
+
+       return mode->htotal * Bpp / device->lanes;
+}
+
+static u16 sun6i_dsi_get_drq_edge0(struct sun6i_dsi *dsi,
+                                  struct drm_display_mode *mode,
+                                  u16 line_num, u16 edge1)
+{
+       u16 edge0 = edge1;
+
+       edge0 += (mode->hdisplay + 40) * SUN6I_DSI_TCON_DIV / 8;
+
+       if (edge0 > line_num)
+               return edge0 - line_num;
+
+       return 1;
+}
+
+static u16 sun6i_dsi_get_drq_edge1(struct sun6i_dsi *dsi,
+                                  struct drm_display_mode *mode,
+                                  u16 line_num)
+{
+       struct mipi_dsi_device *device = dsi->device;
+       unsigned int Bpp = mipi_dsi_pixel_format_to_bpp(device->format) / 8;
+       unsigned int hbp = mode->htotal - mode->hsync_end;
+       u16 edge1;
+
+       edge1 = SUN6I_DSI_SYNC_POINT;
+       edge1 += (mode->hdisplay + hbp + 20) * Bpp / device->lanes;
+
+       if (edge1 > line_num)
+               return line_num;
+
+       return edge1;
 }
 
 static void sun6i_dsi_setup_burst(struct sun6i_dsi *dsi,
@@ -367,7 +420,23 @@ static void sun6i_dsi_setup_burst(struct sun6i_dsi *dsi,
        struct mipi_dsi_device *device = dsi->device;
        u32 val = 0;
 
-       if ((mode->hsync_end - mode->hdisplay) > 20) {
+       if (device->mode_flags & MIPI_DSI_MODE_VIDEO_BURST) {
+               u16 line_num = sun6i_dsi_get_line_num(dsi, mode);
+               u16 edge0, edge1;
+
+               edge1 = sun6i_dsi_get_drq_edge1(dsi, mode, line_num);
+               edge0 = sun6i_dsi_get_drq_edge0(dsi, mode, line_num, edge1);
+
+               regmap_write(dsi->regs, SUN6I_DSI_BURST_DRQ_REG,
+                            SUN6I_DSI_BURST_DRQ_EDGE0(edge0) |
+                            SUN6I_DSI_BURST_DRQ_EDGE1(edge1));
+
+               regmap_write(dsi->regs, SUN6I_DSI_BURST_LINE_REG,
+                            SUN6I_DSI_BURST_LINE_NUM(line_num) |
+                            SUN6I_DSI_BURST_LINE_SYNC_POINT(SUN6I_DSI_SYNC_POINT));
+
+               val = SUN6I_DSI_TCON_DRQ_ENABLE_MODE;
+       } else if ((mode->hsync_end - mode->hdisplay) > 20) {
                /* Maaaaaagic */
                u16 drq = (mode->hsync_end - mode->hdisplay) - 20;
 
@@ -384,8 +453,19 @@ static void sun6i_dsi_setup_burst(struct sun6i_dsi *dsi,
 static void sun6i_dsi_setup_inst_loop(struct sun6i_dsi *dsi,
                                      struct drm_display_mode *mode)
 {
+       struct mipi_dsi_device *device = dsi->device;
        u16 delay = 50 - 1;
 
+       if (device->mode_flags & MIPI_DSI_MODE_VIDEO_BURST) {
+               delay = (mode->htotal - mode->hdisplay) * 150;
+               delay /= (mode->clock / 1000) * 8;
+               delay -= 50;
+       }
+
+       regmap_write(dsi->regs, SUN6I_DSI_INST_LOOP_SEL_REG,
+                    2 << (4 * DSI_INST_ID_LP11) |
+                    3 << (4 * DSI_INST_ID_DLY));
+
        regmap_write(dsi->regs, SUN6I_DSI_INST_LOOP_NUM_REG(0),
                     SUN6I_DSI_INST_LOOP_NUM_N0(50 - 1) |
                     SUN6I_DSI_INST_LOOP_NUM_N1(delay));
@@ -451,49 +531,68 @@ static void sun6i_dsi_setup_timings(struct sun6i_dsi *dsi,
 {
        struct mipi_dsi_device *device = dsi->device;
        unsigned int Bpp = mipi_dsi_pixel_format_to_bpp(device->format) / 8;
-       u16 hbp, hfp, hsa, hblk, vblk;
+       u16 hbp = 0, hfp = 0, hsa = 0, hblk = 0, vblk = 0;
+       u32 basic_ctl = 0;
        size_t bytes;
        u8 *buffer;
 
        /* Do all timing calculations up front to allocate buffer space */
 
-       /*
-        * A sync period is composed of a blanking packet (4 bytes +
-        * payload + 2 bytes) and a sync event packet (4 bytes). Its
-        * minimal size is therefore 10 bytes
-        */
-#define HSA_PACKET_OVERHEAD    10
-       hsa = max((unsigned int)HSA_PACKET_OVERHEAD,
-                 (mode->hsync_end - mode->hsync_start) * Bpp - HSA_PACKET_OVERHEAD);
+       if (device->mode_flags & MIPI_DSI_MODE_VIDEO_BURST) {
+               hblk = mode->hdisplay * Bpp;
+               basic_ctl = SUN6I_DSI_BASIC_CTL_VIDEO_BURST |
+                           SUN6I_DSI_BASIC_CTL_HSA_HSE_DIS |
+                           SUN6I_DSI_BASIC_CTL_HBP_DIS;
 
-       /*
-        * The backporch is set using a blanking packet (4 bytes +
-        * payload + 2 bytes). Its minimal size is therefore 6 bytes
-        */
+               if (device->lanes == 4)
+                       basic_ctl |= SUN6I_DSI_BASIC_CTL_TRAIL_FILL |
+                                    SUN6I_DSI_BASIC_CTL_TRAIL_INV(0xc);
+       } else {
+               /*
+                * A sync period is composed of a blanking packet (4
+                * bytes + payload + 2 bytes) and a sync event packet
+                * (4 bytes). Its minimal size is therefore 10 bytes
+                */
+#define HSA_PACKET_OVERHEAD    10
+               hsa = max((unsigned int)HSA_PACKET_OVERHEAD,
+                         (mode->hsync_end - mode->hsync_start) * Bpp - HSA_PACKET_OVERHEAD);
+
+               /*
+                * The backporch is set using a blanking packet (4
+                * bytes + payload + 2 bytes). Its minimal size is
+                * therefore 6 bytes
+                */
 #define HBP_PACKET_OVERHEAD    6
-       hbp = max((unsigned int)HBP_PACKET_OVERHEAD,
-                 (mode->hsync_start - mode->hdisplay) * Bpp - HBP_PACKET_OVERHEAD);
-
-       /*
-        * The frontporch is set using a blanking packet (4 bytes +
-        * payload + 2 bytes). Its minimal size is therefore 6 bytes
-        */
+               hbp = max((unsigned int)HBP_PACKET_OVERHEAD,
+                         (mode->htotal - mode->hsync_end) * Bpp - HBP_PACKET_OVERHEAD);
+
+               /*
+                * The frontporch is set using a blanking packet (4
+                * bytes + payload + 2 bytes). Its minimal size is
+                * therefore 6 bytes
+                */
 #define HFP_PACKET_OVERHEAD    6
-       hfp = max((unsigned int)HFP_PACKET_OVERHEAD,
-                 (mode->htotal - mode->hsync_end) * Bpp - HFP_PACKET_OVERHEAD);
-
-       /*
-        * hblk seems to be the line + porches length.
-        */
-       hblk = mode->htotal * Bpp - hsa;
-
-       /*
-        * And I'm not entirely sure what vblk is about. The driver in
-        * Allwinner BSP is using a rather convoluted calculation
-        * there only for 4 lanes. However, using 0 (the !4 lanes
-        * case) even with a 4 lanes screen seems to work...
-        */
-       vblk = 0;
+               hfp = max((unsigned int)HFP_PACKET_OVERHEAD,
+                         (mode->hsync_start - mode->hdisplay) * Bpp - HFP_PACKET_OVERHEAD);
+
+               /*
+                * The blanking is set using a sync event (4 bytes)
+                * and a blanking packet (4 bytes + payload + 2
+                * bytes). Its minimal size is therefore 10 bytes.
+                */
+#define HBLK_PACKET_OVERHEAD   10
+               hblk = max((unsigned int)HBLK_PACKET_OVERHEAD,
+                          (mode->htotal - (mode->hsync_end - mode->hsync_start)) * Bpp -
+                          HBLK_PACKET_OVERHEAD);
+
+               /*
+                * And I'm not entirely sure what vblk is about. The driver in
+                * Allwinner BSP is using a rather convoluted calculation
+                * there only for 4 lanes. However, using 0 (the !4 lanes
+                * case) even with a 4 lanes screen seems to work...
+                */
+               vblk = 0;
+       }
 
        /* How many bytes do we need to send all payloads? */
        bytes = max_t(size_t, max(max(hfp, hblk), max(hsa, hbp)), vblk);
@@ -501,7 +600,7 @@ static void sun6i_dsi_setup_timings(struct sun6i_dsi *dsi,
        if (WARN_ON(!buffer))
                return;
 
-       regmap_write(dsi->regs, SUN6I_DSI_BASIC_CTL_REG, 0);
+       regmap_write(dsi->regs, SUN6I_DSI_BASIC_CTL_REG, basic_ctl);
 
        regmap_write(dsi->regs, SUN6I_DSI_SYNC_HSS_REG,
                     sun6i_dsi_build_sync_pkt(MIPI_DSI_H_SYNC_START,
@@ -526,8 +625,8 @@ static void sun6i_dsi_setup_timings(struct sun6i_dsi *dsi,
        regmap_write(dsi->regs, SUN6I_DSI_BASIC_SIZE0_REG,
                     SUN6I_DSI_BASIC_SIZE0_VSA(mode->vsync_end -
                                               mode->vsync_start) |
-                    SUN6I_DSI_BASIC_SIZE0_VBP(mode->vsync_start -
-                                              mode->vdisplay));
+                    SUN6I_DSI_BASIC_SIZE0_VBP(mode->vtotal -
+                                              mode->vsync_end));
 
        regmap_write(dsi->regs, SUN6I_DSI_BASIC_SIZE1_REG,
                     SUN6I_DSI_BASIC_SIZE1_VACT(mode->vdisplay) |
index a070905..5c3ad5b 100644 (file)
@@ -13,6 +13,8 @@
 #include <drm/drm_encoder.h>
 #include <drm/drm_mipi_dsi.h>
 
+#define SUN6I_DSI_TCON_DIV     4
+
 struct sun6i_dsi {
        struct drm_connector    connector;
        struct drm_encoder      encoder;
index 30a2eff..fd20a92 100644 (file)
@@ -325,38 +325,22 @@ static struct regmap_config sun8i_mixer_regmap_config = {
 
 static int sun8i_mixer_of_get_id(struct device_node *node)
 {
-       struct device_node *port, *ep;
-       int ret = -EINVAL;
+       struct device_node *ep, *remote;
+       struct of_endpoint of_ep;
 
-       /* output is port 1 */
-       port = of_graph_get_port_by_id(node, 1);
-       if (!port)
+       /* Output port is 1, and we want the first endpoint. */
+       ep = of_graph_get_endpoint_by_regs(node, 1, -1);
+       if (!ep)
                return -EINVAL;
 
-       /* try to find downstream endpoint */
-       for_each_available_child_of_node(port, ep) {
-               struct device_node *remote;
-               u32 reg;
-
-               remote = of_graph_get_remote_endpoint(ep);
-               if (!remote)
-                       continue;
-
-               ret = of_property_read_u32(remote, "reg", &reg);
-               if (!ret) {
-                       of_node_put(remote);
-                       of_node_put(ep);
-                       of_node_put(port);
-
-                       return reg;
-               }
-
-               of_node_put(remote);
-       }
-
-       of_node_put(port);
+       remote = of_graph_get_remote_endpoint(ep);
+       of_node_put(ep);
+       if (!remote)
+               return -EINVAL;
 
-       return ret;
+       of_graph_parse_endpoint(remote, &of_ep);
+       of_node_put(remote);
+       return of_ep.id;
 }
 
 static int sun8i_mixer_bind(struct device *dev, struct device *master,
@@ -554,6 +538,7 @@ static int sun8i_mixer_remove(struct platform_device *pdev)
 static const struct sun8i_mixer_cfg sun8i_a83t_mixer0_cfg = {
        .ccsc           = 0,
        .scaler_mask    = 0xf,
+       .scanline_yuv   = 2048,
        .ui_num         = 3,
        .vi_num         = 1,
 };
@@ -561,6 +546,7 @@ static const struct sun8i_mixer_cfg sun8i_a83t_mixer0_cfg = {
 static const struct sun8i_mixer_cfg sun8i_a83t_mixer1_cfg = {
        .ccsc           = 1,
        .scaler_mask    = 0x3,
+       .scanline_yuv   = 2048,
        .ui_num         = 1,
        .vi_num         = 1,
 };
@@ -569,6 +555,7 @@ static const struct sun8i_mixer_cfg sun8i_h3_mixer0_cfg = {
        .ccsc           = 0,
        .mod_rate       = 432000000,
        .scaler_mask    = 0xf,
+       .scanline_yuv   = 2048,
        .ui_num         = 3,
        .vi_num         = 1,
 };
@@ -577,6 +564,7 @@ static const struct sun8i_mixer_cfg sun8i_r40_mixer0_cfg = {
        .ccsc           = 0,
        .mod_rate       = 297000000,
        .scaler_mask    = 0xf,
+       .scanline_yuv   = 2048,
        .ui_num         = 3,
        .vi_num         = 1,
 };
@@ -585,6 +573,7 @@ static const struct sun8i_mixer_cfg sun8i_r40_mixer1_cfg = {
        .ccsc           = 1,
        .mod_rate       = 297000000,
        .scaler_mask    = 0x3,
+       .scanline_yuv   = 2048,
        .ui_num         = 1,
        .vi_num         = 1,
 };
@@ -593,6 +582,7 @@ static const struct sun8i_mixer_cfg sun8i_v3s_mixer_cfg = {
        .vi_num = 2,
        .ui_num = 1,
        .scaler_mask = 0x3,
+       .scanline_yuv = 2048,
        .ccsc = 0,
        .mod_rate = 150000000,
 };
@@ -601,6 +591,7 @@ static const struct sun8i_mixer_cfg sun50i_a64_mixer0_cfg = {
        .ccsc           = 0,
        .mod_rate       = 297000000,
        .scaler_mask    = 0xf,
+       .scanline_yuv   = 4096,
        .ui_num         = 3,
        .vi_num         = 1,
 };
@@ -609,6 +600,7 @@ static const struct sun8i_mixer_cfg sun50i_a64_mixer1_cfg = {
        .ccsc           = 1,
        .mod_rate       = 297000000,
        .scaler_mask    = 0x3,
+       .scanline_yuv   = 2048,
        .ui_num         = 1,
        .vi_num         = 1,
 };
@@ -618,6 +610,7 @@ static const struct sun8i_mixer_cfg sun50i_h6_mixer0_cfg = {
        .is_de3         = true,
        .mod_rate       = 600000000,
        .scaler_mask    = 0xf,
+       .scanline_yuv   = 4096,
        .ui_num         = 3,
        .vi_num         = 1,
 };
index 913d14c..80e084c 100644 (file)
@@ -159,6 +159,7 @@ struct de2_fmt_info {
  * @mod_rate: module clock rate that needs to be set in order to have
  *     a functional block.
  * @is_de3: true, if this is next gen display engine 3.0, false otherwise.
+ * @scaline_yuv: size of a scanline for VI scaler for YUV formats.
  */
 struct sun8i_mixer_cfg {
        int             vi_num;
@@ -167,6 +168,7 @@ struct sun8i_mixer_cfg {
        int             ccsc;
        unsigned long   mod_rate;
        unsigned int    is_de3 : 1;
+       unsigned int    scanline_yuv;
 };
 
 struct sun8i_mixer {
index 8a06162..bb8e026 100644 (file)
@@ -80,6 +80,8 @@ static int sun8i_vi_layer_update_coord(struct sun8i_mixer *mixer, int channel,
        u32 bld_base, ch_base;
        u32 outsize, insize;
        u32 hphase, vphase;
+       u32 hn = 0, hm = 0;
+       u32 vn = 0, vm = 0;
        bool subsampled;
 
        DRM_DEBUG_DRIVER("Updating VI channel %d overlay %d\n",
@@ -137,12 +139,41 @@ static int sun8i_vi_layer_update_coord(struct sun8i_mixer *mixer, int channel,
        subsampled = format->hsub > 1 || format->vsub > 1;
 
        if (insize != outsize || subsampled || hphase || vphase) {
-               u32 hscale, vscale;
+               unsigned int scanline, required;
+               struct drm_display_mode *mode;
+               u32 hscale, vscale, fps;
+               u64 ability;
 
                DRM_DEBUG_DRIVER("HW scaling is enabled\n");
 
-               hscale = state->src_w / state->crtc_w;
-               vscale = state->src_h / state->crtc_h;
+               mode = &plane->state->crtc->state->mode;
+               fps = (mode->clock * 1000) / (mode->vtotal * mode->htotal);
+               ability = clk_get_rate(mixer->mod_clk);
+               /* BSP algorithm assumes 80% efficiency of VI scaler unit */
+               ability *= 80;
+               do_div(ability, mode->vdisplay * fps * max(src_w, dst_w));
+
+               required = src_h * 100 / dst_h;
+
+               if (ability < required) {
+                       DRM_DEBUG_DRIVER("Using vertical coarse scaling\n");
+                       vm = src_h;
+                       vn = (u32)ability * dst_h / 100;
+                       src_h = vn;
+               }
+
+               /* it seems that every RGB scaler has buffer for 2048 pixels */
+               scanline = subsampled ? mixer->cfg->scanline_yuv : 2048;
+
+               if (src_w > scanline) {
+                       DRM_DEBUG_DRIVER("Using horizontal coarse scaling\n");
+                       hm = src_w;
+                       hn = scanline;
+                       src_w = hn;
+               }
+
+               hscale = (src_w << 16) / dst_w;
+               vscale = (src_h << 16) / dst_h;
 
                sun8i_vi_scaler_setup(mixer, channel, src_w, src_h, dst_w,
                                      dst_h, hscale, vscale, hphase, vphase,
@@ -153,6 +184,23 @@ static int sun8i_vi_layer_update_coord(struct sun8i_mixer *mixer, int channel,
                sun8i_vi_scaler_enable(mixer, channel, false);
        }
 
+       regmap_write(mixer->engine.regs,
+                    SUN8I_MIXER_CHAN_VI_HDS_Y(ch_base),
+                    SUN8I_MIXER_CHAN_VI_DS_N(hn) |
+                    SUN8I_MIXER_CHAN_VI_DS_M(hm));
+       regmap_write(mixer->engine.regs,
+                    SUN8I_MIXER_CHAN_VI_HDS_UV(ch_base),
+                    SUN8I_MIXER_CHAN_VI_DS_N(hn) |
+                    SUN8I_MIXER_CHAN_VI_DS_M(hm));
+       regmap_write(mixer->engine.regs,
+                    SUN8I_MIXER_CHAN_VI_VDS_Y(ch_base),
+                    SUN8I_MIXER_CHAN_VI_DS_N(vn) |
+                    SUN8I_MIXER_CHAN_VI_DS_M(vm));
+       regmap_write(mixer->engine.regs,
+                    SUN8I_MIXER_CHAN_VI_VDS_UV(ch_base),
+                    SUN8I_MIXER_CHAN_VI_DS_N(vn) |
+                    SUN8I_MIXER_CHAN_VI_DS_M(vm));
+
        /* Set base coordinates */
        DRM_DEBUG_DRIVER("Layer destination coordinates X: %d Y: %d\n",
                         state->dst.x1, state->dst.y1);
index 8a5e6d0..a223a48 100644 (file)
                ((base) + 0x30 * (layer) + 0x18 + 4 * (plane))
 #define SUN8I_MIXER_CHAN_VI_OVL_SIZE(base) \
                ((base) + 0xe8)
+#define SUN8I_MIXER_CHAN_VI_HDS_Y(base) \
+               ((base) + 0xf0)
+#define SUN8I_MIXER_CHAN_VI_HDS_UV(base) \
+               ((base) + 0xf4)
+#define SUN8I_MIXER_CHAN_VI_VDS_Y(base) \
+               ((base) + 0xf8)
+#define SUN8I_MIXER_CHAN_VI_VDS_UV(base) \
+               ((base) + 0xfc)
 
 #define SUN8I_MIXER_CHAN_VI_LAYER_ATTR_EN              BIT(0)
 /* RGB mode should be set for RGB formats and cleared for YCbCr */
@@ -33,6 +41,9 @@
 #define SUN50I_MIXER_CHAN_VI_LAYER_ATTR_ALPHA_MASK     GENMASK(31, 24)
 #define SUN50I_MIXER_CHAN_VI_LAYER_ATTR_ALPHA(x)       ((x) << 24)
 
+#define SUN8I_MIXER_CHAN_VI_DS_N(x)                    ((x) << 16)
+#define SUN8I_MIXER_CHAN_VI_DS_M(x)                    ((x) << 0)
+
 struct sun8i_mixer;
 
 struct sun8i_vi_layer {
index fb221e6..6f8f764 100644 (file)
@@ -1,3 +1,3 @@
-tinydrm-y := tinydrm-core.o tinydrm-pipe.o tinydrm-helpers.o
+tinydrm-y := tinydrm-pipe.o tinydrm-helpers.o
 
 obj-$(CONFIG_DRM_TINYDRM) += tinydrm.o
diff --git a/drivers/gpu/drm/tinydrm/core/tinydrm-core.c b/drivers/gpu/drm/tinydrm/core/tinydrm-core.c
deleted file mode 100644 (file)
index 554abd5..0000000
+++ /dev/null
@@ -1,183 +0,0 @@
-/*
- * Copyright (C) 2016 Noralf Trønnes
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- */
-
-#include <drm/drm_atomic.h>
-#include <drm/drm_atomic_helper.h>
-#include <drm/drm_drv.h>
-#include <drm/drm_fb_helper.h>
-#include <drm/drm_gem_framebuffer_helper.h>
-#include <drm/drm_probe_helper.h>
-#include <drm/drm_print.h>
-#include <drm/tinydrm/tinydrm.h>
-#include <linux/device.h>
-#include <linux/dma-buf.h>
-#include <linux/module.h>
-
-/**
- * DOC: overview
- *
- * This library provides driver helpers for very simple display hardware.
- *
- * It is based on &drm_simple_display_pipe coupled with a &drm_connector which
- * has only one fixed &drm_display_mode. The framebuffers are backed by the
- * cma helper and have support for framebuffer flushing (dirty).
- * fbdev support is also included.
- *
- */
-
-/**
- * DOC: core
- *
- * The driver allocates &tinydrm_device, initializes it using
- * devm_tinydrm_init(), sets up the pipeline using tinydrm_display_pipe_init()
- * and registers the DRM device using devm_tinydrm_register().
- */
-
-static const struct drm_mode_config_funcs tinydrm_mode_config_funcs = {
-       .fb_create = drm_gem_fb_create_with_dirty,
-       .atomic_check = drm_atomic_helper_check,
-       .atomic_commit = drm_atomic_helper_commit,
-};
-
-static int tinydrm_init(struct device *parent, struct tinydrm_device *tdev,
-                       struct drm_driver *driver)
-{
-       struct drm_device *drm;
-
-       /*
-        * We don't embed drm_device, because that prevent us from using
-        * devm_kzalloc() to allocate tinydrm_device in the driver since
-        * drm_dev_put() frees the structure. The devm_ functions provide
-        * for easy error handling.
-        */
-       drm = drm_dev_alloc(driver, parent);
-       if (IS_ERR(drm))
-               return PTR_ERR(drm);
-
-       tdev->drm = drm;
-       drm->dev_private = tdev;
-       drm_mode_config_init(drm);
-       drm->mode_config.funcs = &tinydrm_mode_config_funcs;
-       drm->mode_config.allow_fb_modifiers = true;
-
-       return 0;
-}
-
-static void tinydrm_fini(struct tinydrm_device *tdev)
-{
-       drm_mode_config_cleanup(tdev->drm);
-       tdev->drm->dev_private = NULL;
-       drm_dev_put(tdev->drm);
-}
-
-static void devm_tinydrm_release(void *data)
-{
-       tinydrm_fini(data);
-}
-
-/**
- * devm_tinydrm_init - Initialize tinydrm device
- * @parent: Parent device object
- * @tdev: tinydrm device
- * @driver: DRM driver
- *
- * This function initializes @tdev, the underlying DRM device and it's
- * mode_config. Resources will be automatically freed on driver detach (devres)
- * using drm_mode_config_cleanup() and drm_dev_put().
- *
- * Returns:
- * Zero on success, negative error code on failure.
- */
-int devm_tinydrm_init(struct device *parent, struct tinydrm_device *tdev,
-                     struct drm_driver *driver)
-{
-       int ret;
-
-       ret = tinydrm_init(parent, tdev, driver);
-       if (ret)
-               return ret;
-
-       ret = devm_add_action(parent, devm_tinydrm_release, tdev);
-       if (ret)
-               tinydrm_fini(tdev);
-
-       return ret;
-}
-EXPORT_SYMBOL(devm_tinydrm_init);
-
-static int tinydrm_register(struct tinydrm_device *tdev)
-{
-       struct drm_device *drm = tdev->drm;
-       int ret;
-
-       ret = drm_dev_register(tdev->drm, 0);
-       if (ret)
-               return ret;
-
-       ret = drm_fbdev_generic_setup(drm, 0);
-       if (ret)
-               DRM_ERROR("Failed to initialize fbdev: %d\n", ret);
-
-       return 0;
-}
-
-static void tinydrm_unregister(struct tinydrm_device *tdev)
-{
-       drm_atomic_helper_shutdown(tdev->drm);
-       drm_dev_unregister(tdev->drm);
-}
-
-static void devm_tinydrm_register_release(void *data)
-{
-       tinydrm_unregister(data);
-}
-
-/**
- * devm_tinydrm_register - Register tinydrm device
- * @tdev: tinydrm device
- *
- * This function registers the underlying DRM device and fbdev.
- * These resources will be automatically unregistered on driver detach (devres)
- * and the display pipeline will be disabled.
- *
- * Returns:
- * Zero on success, negative error code on failure.
- */
-int devm_tinydrm_register(struct tinydrm_device *tdev)
-{
-       struct device *dev = tdev->drm->dev;
-       int ret;
-
-       ret = tinydrm_register(tdev);
-       if (ret)
-               return ret;
-
-       ret = devm_add_action(dev, devm_tinydrm_register_release, tdev);
-       if (ret)
-               tinydrm_unregister(tdev);
-
-       return ret;
-}
-EXPORT_SYMBOL(devm_tinydrm_register);
-
-/**
- * tinydrm_shutdown - Shutdown tinydrm
- * @tdev: tinydrm device
- *
- * This function makes sure that the display pipeline is disabled.
- * Used by drivers in their shutdown callback to turn off the display
- * on machine shutdown and reboot.
- */
-void tinydrm_shutdown(struct tinydrm_device *tdev)
-{
-       drm_atomic_helper_shutdown(tdev->drm);
-}
-EXPORT_SYMBOL(tinydrm_shutdown);
-
-MODULE_LICENSE("GPL");
index 2737b6f..d7b38df 100644 (file)
@@ -365,3 +365,5 @@ int tinydrm_spi_transfer(struct spi_device *spi, u32 speed_hz,
 EXPORT_SYMBOL(tinydrm_spi_transfer);
 
 #endif /* CONFIG_SPI */
+
+MODULE_LICENSE("GPL");
index bb5b1c1..bb8a7ed 100644 (file)
@@ -13,7 +13,7 @@
 #include <drm/drm_modes.h>
 #include <drm/drm_probe_helper.h>
 #include <drm/drm_print.h>
-#include <drm/tinydrm/tinydrm.h>
+#include <drm/drm_simple_kms_helper.h>
 
 struct tinydrm_connector {
        struct drm_connector base;
@@ -129,7 +129,8 @@ static int tinydrm_rotate_mode(struct drm_display_mode *mode,
 
 /**
  * tinydrm_display_pipe_init - Initialize display pipe
- * @tdev: tinydrm device
+ * @drm: DRM device
+ * @pipe: Display pipe
  * @funcs: Display pipe functions
  * @connector_type: Connector type
  * @formats: Array of supported formats (DRM_FORMAT\_\*)
@@ -143,16 +144,15 @@ static int tinydrm_rotate_mode(struct drm_display_mode *mode,
  * Returns:
  * Zero on success, negative error code on failure.
  */
-int
-tinydrm_display_pipe_init(struct tinydrm_device *tdev,
-                         const struct drm_simple_display_pipe_funcs *funcs,
-                         int connector_type,
-                         const uint32_t *formats,
-                         unsigned int format_count,
-                         const struct drm_display_mode *mode,
-                         unsigned int rotation)
+int tinydrm_display_pipe_init(struct drm_device *drm,
+                             struct drm_simple_display_pipe *pipe,
+                             const struct drm_simple_display_pipe_funcs *funcs,
+                             int connector_type,
+                             const uint32_t *formats,
+                             unsigned int format_count,
+                             const struct drm_display_mode *mode,
+                             unsigned int rotation)
 {
-       struct drm_device *drm = tdev->drm;
        struct drm_display_mode mode_copy;
        struct drm_connector *connector;
        int ret;
@@ -177,7 +177,7 @@ tinydrm_display_pipe_init(struct tinydrm_device *tdev,
        if (IS_ERR(connector))
                return PTR_ERR(connector);
 
-       return drm_simple_display_pipe_init(drm, &tdev->pipe, funcs, formats,
+       return drm_simple_display_pipe_init(drm, pipe, funcs, formats,
                                            format_count, modifiers, connector);
 }
 EXPORT_SYMBOL(tinydrm_display_pipe_init);
index 8bbd0be..fab961d 100644 (file)
@@ -16,7 +16,9 @@
 #include <linux/property.h>
 #include <linux/spi/spi.h>
 
+#include <drm/drm_atomic_helper.h>
 #include <drm/drm_drv.h>
+#include <drm/drm_fb_helper.h>
 #include <drm/drm_gem_cma_helper.h>
 #include <drm/drm_gem_framebuffer_helper.h>
 #include <drm/drm_modeset_helper.h>
@@ -46,16 +48,18 @@ static void yx240qv29_enable(struct drm_simple_display_pipe *pipe,
                             struct drm_crtc_state *crtc_state,
                             struct drm_plane_state *plane_state)
 {
-       struct tinydrm_device *tdev = pipe_to_tinydrm(pipe);
-       struct mipi_dbi *mipi = mipi_dbi_from_tinydrm(tdev);
+       struct mipi_dbi *mipi = drm_to_mipi_dbi(pipe->crtc.dev);
        u8 addr_mode;
-       int ret;
+       int ret, idx;
+
+       if (!drm_dev_enter(pipe->crtc.dev, &idx))
+               return;
 
        DRM_DEBUG_KMS("\n");
 
        ret = mipi_dbi_poweron_conditional_reset(mipi);
        if (ret < 0)
-               return;
+               goto out_exit;
        if (ret == 1)
                goto out_enable;
 
@@ -171,6 +175,8 @@ out_enable:
        }
        mipi_dbi_command(mipi, MIPI_DCS_SET_ADDRESS_MODE, addr_mode);
        mipi_dbi_enable_flush(mipi, crtc_state, plane_state);
+out_exit:
+       drm_dev_exit(idx);
 }
 
 static const struct drm_simple_display_pipe_funcs hx8357d_pipe_funcs = {
@@ -181,7 +187,7 @@ static const struct drm_simple_display_pipe_funcs hx8357d_pipe_funcs = {
 };
 
 static const struct drm_display_mode yx350hv15_mode = {
-       TINYDRM_MODE(320, 480, 60, 75),
+       DRM_SIMPLE_MODE(320, 480, 60, 75),
 };
 
 DEFINE_DRM_GEM_CMA_FOPS(hx8357d_fops);
@@ -189,6 +195,7 @@ DEFINE_DRM_GEM_CMA_FOPS(hx8357d_fops);
 static struct drm_driver hx8357d_driver = {
        .driver_features        = DRIVER_GEM | DRIVER_MODESET | DRIVER_PRIME | DRIVER_ATOMIC,
        .fops                   = &hx8357d_fops,
+       .release                = mipi_dbi_release,
        DRM_GEM_CMA_VMAP_DRIVER_OPS,
        .debugfs_init           = mipi_dbi_debugfs_init,
        .name                   = "hx8357d",
@@ -213,15 +220,25 @@ MODULE_DEVICE_TABLE(spi, hx8357d_id);
 static int hx8357d_probe(struct spi_device *spi)
 {
        struct device *dev = &spi->dev;
+       struct drm_device *drm;
        struct mipi_dbi *mipi;
        struct gpio_desc *dc;
        u32 rotation = 0;
        int ret;
 
-       mipi = devm_kzalloc(dev, sizeof(*mipi), GFP_KERNEL);
+       mipi = kzalloc(sizeof(*mipi), GFP_KERNEL);
        if (!mipi)
                return -ENOMEM;
 
+       drm = &mipi->drm;
+       ret = devm_drm_dev_init(dev, drm, &hx8357d_driver);
+       if (ret) {
+               kfree(mipi);
+               return ret;
+       }
+
+       drm_mode_config_init(drm);
+
        dc = devm_gpiod_get(dev, "dc", GPIOD_OUT_LOW);
        if (IS_ERR(dc)) {
                DRM_DEV_ERROR(dev, "Failed to get gpio 'dc'\n");
@@ -238,21 +255,36 @@ static int hx8357d_probe(struct spi_device *spi)
        if (ret)
                return ret;
 
-       ret = mipi_dbi_init(&spi->dev, mipi, &hx8357d_pipe_funcs,
-                           &hx8357d_driver, &yx350hv15_mode, rotation);
+       ret = mipi_dbi_init(mipi, &hx8357d_pipe_funcs, &yx350hv15_mode, rotation);
        if (ret)
                return ret;
 
-       spi_set_drvdata(spi, mipi);
+       drm_mode_config_reset(drm);
 
-       return devm_tinydrm_register(&mipi->tinydrm);
+       ret = drm_dev_register(drm, 0);
+       if (ret)
+               return ret;
+
+       spi_set_drvdata(spi, drm);
+
+       drm_fbdev_generic_setup(drm, 32);
+
+       return 0;
 }
 
-static void hx8357d_shutdown(struct spi_device *spi)
+static int hx8357d_remove(struct spi_device *spi)
 {
-       struct mipi_dbi *mipi = spi_get_drvdata(spi);
+       struct drm_device *drm = spi_get_drvdata(spi);
+
+       drm_dev_unplug(drm);
+       drm_atomic_helper_shutdown(drm);
 
-       tinydrm_shutdown(&mipi->tinydrm);
+       return 0;
+}
+
+static void hx8357d_shutdown(struct spi_device *spi)
+{
+       drm_atomic_helper_shutdown(spi_get_drvdata(spi));
 }
 
 static struct spi_driver hx8357d_spi_driver = {
@@ -262,6 +294,7 @@ static struct spi_driver hx8357d_spi_driver = {
        },
        .id_table = hx8357d_id,
        .probe = hx8357d_probe,
+       .remove = hx8357d_remove,
        .shutdown = hx8357d_shutdown,
 };
 module_spi_driver(hx8357d_spi_driver);
index 43a3b68..e9116ef 100644 (file)
 #include <linux/spi/spi.h>
 #include <video/mipi_display.h>
 
+#include <drm/drm_atomic_helper.h>
 #include <drm/drm_damage_helper.h>
 #include <drm/drm_drv.h>
 #include <drm/drm_fb_cma_helper.h>
+#include <drm/drm_fb_helper.h>
 #include <drm/drm_fourcc.h>
 #include <drm/drm_gem_cma_helper.h>
 #include <drm/drm_gem_framebuffer_helper.h>
@@ -81,20 +83,22 @@ static inline int ili9225_command(struct mipi_dbi *mipi, u8 cmd, u16 data)
 static void ili9225_fb_dirty(struct drm_framebuffer *fb, struct drm_rect *rect)
 {
        struct drm_gem_cma_object *cma_obj = drm_fb_cma_get_gem_obj(fb, 0);
-       struct tinydrm_device *tdev = fb->dev->dev_private;
-       struct mipi_dbi *mipi = mipi_dbi_from_tinydrm(tdev);
+       struct mipi_dbi *mipi = drm_to_mipi_dbi(fb->dev);
        unsigned int height = rect->y2 - rect->y1;
        unsigned int width = rect->x2 - rect->x1;
        bool swap = mipi->swap_bytes;
        u16 x_start, y_start;
        u16 x1, x2, y1, y2;
-       int ret = 0;
+       int idx, ret = 0;
        bool full;
        void *tr;
 
        if (!mipi->enabled)
                return;
 
+       if (!drm_dev_enter(fb->dev, &idx))
+               return;
+
        full = width == fb->width && height == fb->height;
 
        DRM_DEBUG_KMS("Flushing [FB:%d] " DRM_RECT_FMT "\n", fb->base.id, DRM_RECT_ARG(rect));
@@ -157,6 +161,8 @@ static void ili9225_fb_dirty(struct drm_framebuffer *fb, struct drm_rect *rect)
 err_msg:
        if (ret)
                dev_err_once(fb->dev->dev, "Failed to update display %d\n", ret);
+
+       drm_dev_exit(idx);
 }
 
 static void ili9225_pipe_update(struct drm_simple_display_pipe *pipe,
@@ -181,19 +187,21 @@ static void ili9225_pipe_enable(struct drm_simple_display_pipe *pipe,
                                struct drm_crtc_state *crtc_state,
                                struct drm_plane_state *plane_state)
 {
-       struct tinydrm_device *tdev = pipe_to_tinydrm(pipe);
-       struct mipi_dbi *mipi = mipi_dbi_from_tinydrm(tdev);
+       struct mipi_dbi *mipi = drm_to_mipi_dbi(pipe->crtc.dev);
        struct drm_framebuffer *fb = plane_state->fb;
-       struct device *dev = tdev->drm->dev;
+       struct device *dev = pipe->crtc.dev->dev;
        struct drm_rect rect = {
                .x1 = 0,
                .x2 = fb->width,
                .y1 = 0,
                .y2 = fb->height,
        };
-       int ret;
+       int ret, idx;
        u8 am_id;
 
+       if (!drm_dev_enter(pipe->crtc.dev, &idx))
+               return;
+
        DRM_DEBUG_KMS("\n");
 
        mipi_dbi_hw_reset(mipi);
@@ -207,7 +215,7 @@ static void ili9225_pipe_enable(struct drm_simple_display_pipe *pipe,
        ret = ili9225_command(mipi, ILI9225_POWER_CONTROL_1, 0x0000);
        if (ret) {
                DRM_DEV_ERROR(dev, "Error sending command %d\n", ret);
-               return;
+               goto out_exit;
        }
        ili9225_command(mipi, ILI9225_POWER_CONTROL_2, 0x0000);
        ili9225_command(mipi, ILI9225_POWER_CONTROL_3, 0x0000);
@@ -280,15 +288,23 @@ static void ili9225_pipe_enable(struct drm_simple_display_pipe *pipe,
 
        mipi->enabled = true;
        ili9225_fb_dirty(fb, &rect);
+out_exit:
+       drm_dev_exit(idx);
 }
 
 static void ili9225_pipe_disable(struct drm_simple_display_pipe *pipe)
 {
-       struct tinydrm_device *tdev = pipe_to_tinydrm(pipe);
-       struct mipi_dbi *mipi = mipi_dbi_from_tinydrm(tdev);
+       struct mipi_dbi *mipi = drm_to_mipi_dbi(pipe->crtc.dev);
 
        DRM_DEBUG_KMS("\n");
 
+       /*
+        * This callback is not protected by drm_dev_enter/exit since we want to
+        * turn off the display on regular driver unload. It's highly unlikely
+        * that the underlying SPI controller is gone should this be called after
+        * unplug.
+        */
+
        if (!mipi->enabled)
                return;
 
@@ -301,7 +317,7 @@ static void ili9225_pipe_disable(struct drm_simple_display_pipe *pipe)
        mipi->enabled = false;
 }
 
-static int ili9225_dbi_command(struct mipi_dbi *mipi, u8 cmd, u8 *par,
+static int ili9225_dbi_command(struct mipi_dbi *mipi, u8 *cmd, u8 *par,
                               size_t num)
 {
        struct spi_device *spi = mipi->spi;
@@ -311,11 +327,11 @@ static int ili9225_dbi_command(struct mipi_dbi *mipi, u8 cmd, u8 *par,
 
        gpiod_set_value_cansleep(mipi->dc, 0);
        speed_hz = mipi_dbi_spi_cmd_max_speed(spi, 1);
-       ret = tinydrm_spi_transfer(spi, speed_hz, NULL, 8, &cmd, 1);
+       ret = tinydrm_spi_transfer(spi, speed_hz, NULL, 8, cmd, 1);
        if (ret || !num)
                return ret;
 
-       if (cmd == ILI9225_WRITE_DATA_TO_GRAM && !mipi->swap_bytes)
+       if (*cmd == ILI9225_WRITE_DATA_TO_GRAM && !mipi->swap_bytes)
                bpw = 16;
 
        gpiod_set_value_cansleep(mipi->dc, 1);
@@ -332,7 +348,7 @@ static const struct drm_simple_display_pipe_funcs ili9225_pipe_funcs = {
 };
 
 static const struct drm_display_mode ili9225_mode = {
-       TINYDRM_MODE(176, 220, 35, 44),
+       DRM_SIMPLE_MODE(176, 220, 35, 44),
 };
 
 DEFINE_DRM_GEM_CMA_FOPS(ili9225_fops);
@@ -341,6 +357,7 @@ static struct drm_driver ili9225_driver = {
        .driver_features        = DRIVER_GEM | DRIVER_MODESET | DRIVER_PRIME |
                                  DRIVER_ATOMIC,
        .fops                   = &ili9225_fops,
+       .release                = mipi_dbi_release,
        DRM_GEM_CMA_VMAP_DRIVER_OPS,
        .name                   = "ili9225",
        .desc                   = "Ilitek ILI9225",
@@ -364,15 +381,25 @@ MODULE_DEVICE_TABLE(spi, ili9225_id);
 static int ili9225_probe(struct spi_device *spi)
 {
        struct device *dev = &spi->dev;
+       struct drm_device *drm;
        struct mipi_dbi *mipi;
        struct gpio_desc *rs;
        u32 rotation = 0;
        int ret;
 
-       mipi = devm_kzalloc(dev, sizeof(*mipi), GFP_KERNEL);
+       mipi = kzalloc(sizeof(*mipi), GFP_KERNEL);
        if (!mipi)
                return -ENOMEM;
 
+       drm = &mipi->drm;
+       ret = devm_drm_dev_init(dev, drm, &ili9225_driver);
+       if (ret) {
+               kfree(mipi);
+               return ret;
+       }
+
+       drm_mode_config_init(drm);
+
        mipi->reset = devm_gpiod_get(dev, "reset", GPIOD_OUT_HIGH);
        if (IS_ERR(mipi->reset)) {
                DRM_DEV_ERROR(dev, "Failed to get gpio 'reset'\n");
@@ -394,21 +421,36 @@ static int ili9225_probe(struct spi_device *spi)
        /* override the command function set in  mipi_dbi_spi_init() */
        mipi->command = ili9225_dbi_command;
 
-       ret = mipi_dbi_init(&spi->dev, mipi, &ili9225_pipe_funcs,
-                           &ili9225_driver, &ili9225_mode, rotation);
+       ret = mipi_dbi_init(mipi, &ili9225_pipe_funcs, &ili9225_mode, rotation);
        if (ret)
                return ret;
 
-       spi_set_drvdata(spi, mipi);
+       drm_mode_config_reset(drm);
+
+       ret = drm_dev_register(drm, 0);
+       if (ret)
+               return ret;
+
+       spi_set_drvdata(spi, drm);
+
+       drm_fbdev_generic_setup(drm, 32);
 
-       return devm_tinydrm_register(&mipi->tinydrm);
+       return 0;
 }
 
-static void ili9225_shutdown(struct spi_device *spi)
+static int ili9225_remove(struct spi_device *spi)
 {
-       struct mipi_dbi *mipi = spi_get_drvdata(spi);
+       struct drm_device *drm = spi_get_drvdata(spi);
+
+       drm_dev_unplug(drm);
+       drm_atomic_helper_shutdown(drm);
+
+       return 0;
+}
 
-       tinydrm_shutdown(&mipi->tinydrm);
+static void ili9225_shutdown(struct spi_device *spi)
+{
+       drm_atomic_helper_shutdown(spi_get_drvdata(spi));
 }
 
 static struct spi_driver ili9225_spi_driver = {
@@ -419,6 +461,7 @@ static struct spi_driver ili9225_spi_driver = {
        },
        .id_table = ili9225_id,
        .probe = ili9225_probe,
+       .remove = ili9225_remove,
        .shutdown = ili9225_shutdown,
 };
 module_spi_driver(ili9225_spi_driver);
index 713bb2d..d15f85e 100644 (file)
@@ -15,7 +15,9 @@
 #include <linux/property.h>
 #include <linux/spi/spi.h>
 
+#include <drm/drm_atomic_helper.h>
 #include <drm/drm_drv.h>
+#include <drm/drm_fb_helper.h>
 #include <drm/drm_gem_cma_helper.h>
 #include <drm/drm_gem_framebuffer_helper.h>
 #include <drm/drm_modeset_helper.h>
@@ -52,16 +54,18 @@ static void yx240qv29_enable(struct drm_simple_display_pipe *pipe,
                             struct drm_crtc_state *crtc_state,
                             struct drm_plane_state *plane_state)
 {
-       struct tinydrm_device *tdev = pipe_to_tinydrm(pipe);
-       struct mipi_dbi *mipi = mipi_dbi_from_tinydrm(tdev);
+       struct mipi_dbi *mipi = drm_to_mipi_dbi(pipe->crtc.dev);
        u8 addr_mode;
-       int ret;
+       int ret, idx;
+
+       if (!drm_dev_enter(pipe->crtc.dev, &idx))
+               return;
 
        DRM_DEBUG_KMS("\n");
 
        ret = mipi_dbi_poweron_conditional_reset(mipi);
        if (ret < 0)
-               return;
+               goto out_exit;
        if (ret == 1)
                goto out_enable;
 
@@ -127,6 +131,8 @@ out_enable:
        addr_mode |= ILI9341_MADCTL_BGR;
        mipi_dbi_command(mipi, MIPI_DCS_SET_ADDRESS_MODE, addr_mode);
        mipi_dbi_enable_flush(mipi, crtc_state, plane_state);
+out_exit:
+       drm_dev_exit(idx);
 }
 
 static const struct drm_simple_display_pipe_funcs ili9341_pipe_funcs = {
@@ -137,7 +143,7 @@ static const struct drm_simple_display_pipe_funcs ili9341_pipe_funcs = {
 };
 
 static const struct drm_display_mode yx240qv29_mode = {
-       TINYDRM_MODE(240, 320, 37, 49),
+       DRM_SIMPLE_MODE(240, 320, 37, 49),
 };
 
 DEFINE_DRM_GEM_CMA_FOPS(ili9341_fops);
@@ -145,6 +151,7 @@ DEFINE_DRM_GEM_CMA_FOPS(ili9341_fops);
 static struct drm_driver ili9341_driver = {
        .driver_features        = DRIVER_GEM | DRIVER_MODESET | DRIVER_PRIME | DRIVER_ATOMIC,
        .fops                   = &ili9341_fops,
+       .release                = mipi_dbi_release,
        DRM_GEM_CMA_VMAP_DRIVER_OPS,
        .debugfs_init           = mipi_dbi_debugfs_init,
        .name                   = "ili9341",
@@ -169,15 +176,25 @@ MODULE_DEVICE_TABLE(spi, ili9341_id);
 static int ili9341_probe(struct spi_device *spi)
 {
        struct device *dev = &spi->dev;
+       struct drm_device *drm;
        struct mipi_dbi *mipi;
        struct gpio_desc *dc;
        u32 rotation = 0;
        int ret;
 
-       mipi = devm_kzalloc(dev, sizeof(*mipi), GFP_KERNEL);
+       mipi = kzalloc(sizeof(*mipi), GFP_KERNEL);
        if (!mipi)
                return -ENOMEM;
 
+       drm = &mipi->drm;
+       ret = devm_drm_dev_init(dev, drm, &ili9341_driver);
+       if (ret) {
+               kfree(mipi);
+               return ret;
+       }
+
+       drm_mode_config_init(drm);
+
        mipi->reset = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_HIGH);
        if (IS_ERR(mipi->reset)) {
                DRM_DEV_ERROR(dev, "Failed to get gpio 'reset'\n");
@@ -200,21 +217,36 @@ static int ili9341_probe(struct spi_device *spi)
        if (ret)
                return ret;
 
-       ret = mipi_dbi_init(&spi->dev, mipi, &ili9341_pipe_funcs,
-                           &ili9341_driver, &yx240qv29_mode, rotation);
+       ret = mipi_dbi_init(mipi, &ili9341_pipe_funcs, &yx240qv29_mode, rotation);
        if (ret)
                return ret;
 
-       spi_set_drvdata(spi, mipi);
+       drm_mode_config_reset(drm);
 
-       return devm_tinydrm_register(&mipi->tinydrm);
+       ret = drm_dev_register(drm, 0);
+       if (ret)
+               return ret;
+
+       spi_set_drvdata(spi, drm);
+
+       drm_fbdev_generic_setup(drm, 32);
+
+       return 0;
 }
 
-static void ili9341_shutdown(struct spi_device *spi)
+static int ili9341_remove(struct spi_device *spi)
 {
-       struct mipi_dbi *mipi = spi_get_drvdata(spi);
+       struct drm_device *drm = spi_get_drvdata(spi);
+
+       drm_dev_unplug(drm);
+       drm_atomic_helper_shutdown(drm);
 
-       tinydrm_shutdown(&mipi->tinydrm);
+       return 0;
+}
+
+static void ili9341_shutdown(struct spi_device *spi)
+{
+       drm_atomic_helper_shutdown(spi_get_drvdata(spi));
 }
 
 static struct spi_driver ili9341_spi_driver = {
@@ -224,6 +256,7 @@ static struct spi_driver ili9341_spi_driver = {
        },
        .id_table = ili9341_id,
        .probe = ili9341_probe,
+       .remove = ili9341_remove,
        .shutdown = ili9341_shutdown,
 };
 module_spi_driver(ili9341_spi_driver);
index 82a92ec..c6dc310 100644 (file)
@@ -17,7 +17,9 @@
 #include <linux/regulator/consumer.h>
 #include <linux/spi/spi.h>
 
+#include <drm/drm_atomic_helper.h>
 #include <drm/drm_drv.h>
+#include <drm/drm_fb_helper.h>
 #include <drm/drm_gem_cma_helper.h>
 #include <drm/drm_gem_framebuffer_helper.h>
 #include <drm/drm_modeset_helper.h>
@@ -54,16 +56,18 @@ static void mi0283qt_enable(struct drm_simple_display_pipe *pipe,
                            struct drm_crtc_state *crtc_state,
                            struct drm_plane_state *plane_state)
 {
-       struct tinydrm_device *tdev = pipe_to_tinydrm(pipe);
-       struct mipi_dbi *mipi = mipi_dbi_from_tinydrm(tdev);
+       struct mipi_dbi *mipi = drm_to_mipi_dbi(pipe->crtc.dev);
        u8 addr_mode;
-       int ret;
+       int ret, idx;
+
+       if (!drm_dev_enter(pipe->crtc.dev, &idx))
+               return;
 
        DRM_DEBUG_KMS("\n");
 
        ret = mipi_dbi_poweron_conditional_reset(mipi);
        if (ret < 0)
-               return;
+               goto out_exit;
        if (ret == 1)
                goto out_enable;
 
@@ -135,6 +139,8 @@ out_enable:
        addr_mode |= ILI9341_MADCTL_BGR;
        mipi_dbi_command(mipi, MIPI_DCS_SET_ADDRESS_MODE, addr_mode);
        mipi_dbi_enable_flush(mipi, crtc_state, plane_state);
+out_exit:
+       drm_dev_exit(idx);
 }
 
 static const struct drm_simple_display_pipe_funcs mi0283qt_pipe_funcs = {
@@ -145,7 +151,7 @@ static const struct drm_simple_display_pipe_funcs mi0283qt_pipe_funcs = {
 };
 
 static const struct drm_display_mode mi0283qt_mode = {
-       TINYDRM_MODE(320, 240, 58, 43),
+       DRM_SIMPLE_MODE(320, 240, 58, 43),
 };
 
 DEFINE_DRM_GEM_CMA_FOPS(mi0283qt_fops);
@@ -154,6 +160,7 @@ static struct drm_driver mi0283qt_driver = {
        .driver_features        = DRIVER_GEM | DRIVER_MODESET | DRIVER_PRIME |
                                  DRIVER_ATOMIC,
        .fops                   = &mi0283qt_fops,
+       .release                = mipi_dbi_release,
        DRM_GEM_CMA_VMAP_DRIVER_OPS,
        .debugfs_init           = mipi_dbi_debugfs_init,
        .name                   = "mi0283qt",
@@ -178,15 +185,25 @@ MODULE_DEVICE_TABLE(spi, mi0283qt_id);
 static int mi0283qt_probe(struct spi_device *spi)
 {
        struct device *dev = &spi->dev;
+       struct drm_device *drm;
        struct mipi_dbi *mipi;
        struct gpio_desc *dc;
        u32 rotation = 0;
        int ret;
 
-       mipi = devm_kzalloc(dev, sizeof(*mipi), GFP_KERNEL);
+       mipi = kzalloc(sizeof(*mipi), GFP_KERNEL);
        if (!mipi)
                return -ENOMEM;
 
+       drm = &mipi->drm;
+       ret = devm_drm_dev_init(dev, drm, &mi0283qt_driver);
+       if (ret) {
+               kfree(mipi);
+               return ret;
+       }
+
+       drm_mode_config_init(drm);
+
        mipi->reset = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_HIGH);
        if (IS_ERR(mipi->reset)) {
                DRM_DEV_ERROR(dev, "Failed to get gpio 'reset'\n");
@@ -213,35 +230,46 @@ static int mi0283qt_probe(struct spi_device *spi)
        if (ret)
                return ret;
 
-       ret = mipi_dbi_init(&spi->dev, mipi, &mi0283qt_pipe_funcs,
-                           &mi0283qt_driver, &mi0283qt_mode, rotation);
+       ret = mipi_dbi_init(mipi, &mi0283qt_pipe_funcs, &mi0283qt_mode, rotation);
+       if (ret)
+               return ret;
+
+       drm_mode_config_reset(drm);
+
+       ret = drm_dev_register(drm, 0);
        if (ret)
                return ret;
 
-       spi_set_drvdata(spi, mipi);
+       spi_set_drvdata(spi, drm);
 
-       return devm_tinydrm_register(&mipi->tinydrm);
+       drm_fbdev_generic_setup(drm, 32);
+
+       return 0;
 }
 
-static void mi0283qt_shutdown(struct spi_device *spi)
+static int mi0283qt_remove(struct spi_device *spi)
 {
-       struct mipi_dbi *mipi = spi_get_drvdata(spi);
+       struct drm_device *drm = spi_get_drvdata(spi);
 
-       tinydrm_shutdown(&mipi->tinydrm);
+       drm_dev_unplug(drm);
+       drm_atomic_helper_shutdown(drm);
+
+       return 0;
 }
 
-static int __maybe_unused mi0283qt_pm_suspend(struct device *dev)
+static void mi0283qt_shutdown(struct spi_device *spi)
 {
-       struct mipi_dbi *mipi = dev_get_drvdata(dev);
+       drm_atomic_helper_shutdown(spi_get_drvdata(spi));
+}
 
-       return drm_mode_config_helper_suspend(mipi->tinydrm.drm);
+static int __maybe_unused mi0283qt_pm_suspend(struct device *dev)
+{
+       return drm_mode_config_helper_suspend(dev_get_drvdata(dev));
 }
 
 static int __maybe_unused mi0283qt_pm_resume(struct device *dev)
 {
-       struct mipi_dbi *mipi = dev_get_drvdata(dev);
-
-       drm_mode_config_helper_resume(mipi->tinydrm.drm);
+       drm_mode_config_helper_resume(dev_get_drvdata(dev));
 
        return 0;
 }
@@ -259,6 +287,7 @@ static struct spi_driver mi0283qt_spi_driver = {
        },
        .id_table = mi0283qt_id,
        .probe = mi0283qt_probe,
+       .remove = mi0283qt_remove,
        .shutdown = mi0283qt_shutdown,
 };
 module_spi_driver(mi0283qt_spi_driver);
index 918f77c..869c8f5 100644 (file)
@@ -153,16 +153,42 @@ EXPORT_SYMBOL(mipi_dbi_command_read);
  */
 int mipi_dbi_command_buf(struct mipi_dbi *mipi, u8 cmd, u8 *data, size_t len)
 {
+       u8 *cmdbuf;
        int ret;
 
+       /* SPI requires dma-safe buffers */
+       cmdbuf = kmemdup(&cmd, 1, GFP_KERNEL);
+       if (!cmdbuf)
+               return -ENOMEM;
+
        mutex_lock(&mipi->cmdlock);
-       ret = mipi->command(mipi, cmd, data, len);
+       ret = mipi->command(mipi, cmdbuf, data, len);
        mutex_unlock(&mipi->cmdlock);
 
+       kfree(cmdbuf);
+
        return ret;
 }
 EXPORT_SYMBOL(mipi_dbi_command_buf);
 
+/* This should only be used by mipi_dbi_command() */
+int mipi_dbi_command_stackbuf(struct mipi_dbi *mipi, u8 cmd, u8 *data, size_t len)
+{
+       u8 *buf;
+       int ret;
+
+       buf = kmemdup(data, len, GFP_KERNEL);
+       if (!buf)
+               return -ENOMEM;
+
+       ret = mipi_dbi_command_buf(mipi, cmd, buf, len);
+
+       kfree(buf);
+
+       return ret;
+}
+EXPORT_SYMBOL(mipi_dbi_command_stackbuf);
+
 /**
  * mipi_dbi_buf_copy - Copy a framebuffer, transforming it if necessary
  * @dst: The destination buffer
@@ -216,18 +242,20 @@ EXPORT_SYMBOL(mipi_dbi_buf_copy);
 static void mipi_dbi_fb_dirty(struct drm_framebuffer *fb, struct drm_rect *rect)
 {
        struct drm_gem_cma_object *cma_obj = drm_fb_cma_get_gem_obj(fb, 0);
-       struct tinydrm_device *tdev = fb->dev->dev_private;
-       struct mipi_dbi *mipi = mipi_dbi_from_tinydrm(tdev);
+       struct mipi_dbi *mipi = drm_to_mipi_dbi(fb->dev);
        unsigned int height = rect->y2 - rect->y1;
        unsigned int width = rect->x2 - rect->x1;
        bool swap = mipi->swap_bytes;
-       int ret = 0;
+       int idx, ret = 0;
        bool full;
        void *tr;
 
        if (!mipi->enabled)
                return;
 
+       if (!drm_dev_enter(fb->dev, &idx))
+               return;
+
        full = width == fb->width && height == fb->height;
 
        DRM_DEBUG_KMS("Flushing [FB:%d] " DRM_RECT_FMT "\n", fb->base.id, DRM_RECT_ARG(rect));
@@ -254,6 +282,8 @@ static void mipi_dbi_fb_dirty(struct drm_framebuffer *fb, struct drm_rect *rect)
 err_msg:
        if (ret)
                dev_err_once(fb->dev->dev, "Failed to update display %d\n", ret);
+
+       drm_dev_exit(idx);
 }
 
 /**
@@ -308,19 +338,29 @@ void mipi_dbi_enable_flush(struct mipi_dbi *mipi,
                .y1 = 0,
                .y2 = fb->height,
        };
+       int idx;
+
+       if (!drm_dev_enter(&mipi->drm, &idx))
+               return;
 
        mipi->enabled = true;
        mipi_dbi_fb_dirty(fb, &rect);
        backlight_enable(mipi->backlight);
+
+       drm_dev_exit(idx);
 }
 EXPORT_SYMBOL(mipi_dbi_enable_flush);
 
 static void mipi_dbi_blank(struct mipi_dbi *mipi)
 {
-       struct drm_device *drm = mipi->tinydrm.drm;
+       struct drm_device *drm = &mipi->drm;
        u16 height = drm->mode_config.min_height;
        u16 width = drm->mode_config.min_width;
        size_t len = width * height * 2;
+       int idx;
+
+       if (!drm_dev_enter(drm, &idx))
+               return;
 
        memset(mipi->tx_buf, 0, len);
 
@@ -330,6 +370,8 @@ static void mipi_dbi_blank(struct mipi_dbi *mipi)
                         (height >> 8) & 0xFF, (height - 1) & 0xFF);
        mipi_dbi_command_buf(mipi, MIPI_DCS_WRITE_MEMORY_START,
                             (u8 *)mipi->tx_buf, len);
+
+       drm_dev_exit(idx);
 }
 
 /**
@@ -342,8 +384,10 @@ static void mipi_dbi_blank(struct mipi_dbi *mipi)
  */
 void mipi_dbi_pipe_disable(struct drm_simple_display_pipe *pipe)
 {
-       struct tinydrm_device *tdev = pipe_to_tinydrm(pipe);
-       struct mipi_dbi *mipi = mipi_dbi_from_tinydrm(tdev);
+       struct mipi_dbi *mipi = drm_to_mipi_dbi(pipe->crtc.dev);
+
+       if (!mipi->enabled)
+               return;
 
        DRM_DEBUG_KMS("\n");
 
@@ -359,6 +403,12 @@ void mipi_dbi_pipe_disable(struct drm_simple_display_pipe *pipe)
 }
 EXPORT_SYMBOL(mipi_dbi_pipe_disable);
 
+static const struct drm_mode_config_funcs mipi_dbi_mode_config_funcs = {
+       .fb_create = drm_gem_fb_create_with_dirty,
+       .atomic_check = drm_atomic_helper_check,
+       .atomic_commit = drm_atomic_helper_commit,
+};
+
 static const uint32_t mipi_dbi_formats[] = {
        DRM_FORMAT_RGB565,
        DRM_FORMAT_XRGB8888,
@@ -366,31 +416,27 @@ static const uint32_t mipi_dbi_formats[] = {
 
 /**
  * mipi_dbi_init - MIPI DBI initialization
- * @dev: Parent device
  * @mipi: &mipi_dbi structure to initialize
- * @pipe_funcs: Display pipe functions
- * @driver: DRM driver
+ * @funcs: Display pipe functions
  * @mode: Display mode
  * @rotation: Initial rotation in degrees Counter Clock Wise
  *
- * This function initializes a &mipi_dbi structure and it's underlying
- * @tinydrm_device. It also sets up the display pipeline.
+ * This function sets up a &drm_simple_display_pipe with a &drm_connector that
+ * has one fixed &drm_display_mode which is rotated according to @rotation.
+ * This mode is used to set the mode config min/max width/height properties.
+ * Additionally &mipi_dbi.tx_buf is allocated.
  *
  * Supported formats: Native RGB565 and emulated XRGB8888.
  *
- * Objects created by this function will be automatically freed on driver
- * detach (devres).
- *
  * Returns:
  * Zero on success, negative error code on failure.
  */
-int mipi_dbi_init(struct device *dev, struct mipi_dbi *mipi,
-                 const struct drm_simple_display_pipe_funcs *pipe_funcs,
-                 struct drm_driver *driver,
+int mipi_dbi_init(struct mipi_dbi *mipi,
+                 const struct drm_simple_display_pipe_funcs *funcs,
                  const struct drm_display_mode *mode, unsigned int rotation)
 {
        size_t bufsize = mode->vdisplay * mode->hdisplay * sizeof(u16);
-       struct tinydrm_device *tdev = &mipi->tinydrm;
+       struct drm_device *drm = &mipi->drm;
        int ret;
 
        if (!mipi->command)
@@ -398,16 +444,12 @@ int mipi_dbi_init(struct device *dev, struct mipi_dbi *mipi,
 
        mutex_init(&mipi->cmdlock);
 
-       mipi->tx_buf = devm_kmalloc(dev, bufsize, GFP_KERNEL);
+       mipi->tx_buf = devm_kmalloc(drm->dev, bufsize, GFP_KERNEL);
        if (!mipi->tx_buf)
                return -ENOMEM;
 
-       ret = devm_tinydrm_init(dev, tdev, driver);
-       if (ret)
-               return ret;
-
        /* TODO: Maybe add DRM_MODE_CONNECTOR_SPI */
-       ret = tinydrm_display_pipe_init(tdev, pipe_funcs,
+       ret = tinydrm_display_pipe_init(drm, &mipi->pipe, funcs,
                                        DRM_MODE_CONNECTOR_VIRTUAL,
                                        mipi_dbi_formats,
                                        ARRAY_SIZE(mipi_dbi_formats), mode,
@@ -415,21 +457,40 @@ int mipi_dbi_init(struct device *dev, struct mipi_dbi *mipi,
        if (ret)
                return ret;
 
-       drm_plane_enable_fb_damage_clips(&tdev->pipe.plane);
+       drm_plane_enable_fb_damage_clips(&mipi->pipe.plane);
 
-       tdev->drm->mode_config.preferred_depth = 16;
+       drm->mode_config.funcs = &mipi_dbi_mode_config_funcs;
+       drm->mode_config.preferred_depth = 16;
        mipi->rotation = rotation;
 
-       drm_mode_config_reset(tdev->drm);
-
        DRM_DEBUG_KMS("preferred_depth=%u, rotation = %u\n",
-                     tdev->drm->mode_config.preferred_depth, rotation);
+                     drm->mode_config.preferred_depth, rotation);
 
        return 0;
 }
 EXPORT_SYMBOL(mipi_dbi_init);
 
 /**
+ * mipi_dbi_release - DRM driver release helper
+ * @drm: DRM device
+ *
+ * This function finalizes and frees &mipi_dbi.
+ *
+ * Drivers can use this as their &drm_driver->release callback.
+ */
+void mipi_dbi_release(struct drm_device *drm)
+{
+       struct mipi_dbi *dbi = drm_to_mipi_dbi(drm);
+
+       DRM_DEBUG_DRIVER("\n");
+
+       drm_mode_config_cleanup(drm);
+       drm_dev_fini(drm);
+       kfree(dbi);
+}
+EXPORT_SYMBOL(mipi_dbi_release);
+
+/**
  * mipi_dbi_hw_reset - Hardware reset of controller
  * @mipi: MIPI DBI structure
  *
@@ -481,7 +542,7 @@ EXPORT_SYMBOL(mipi_dbi_display_is_on);
 
 static int mipi_dbi_poweron_reset_conditional(struct mipi_dbi *mipi, bool cond)
 {
-       struct device *dev = mipi->tinydrm.drm->dev;
+       struct device *dev = mipi->drm.dev;
        int ret;
 
        if (mipi->regulator) {
@@ -774,18 +835,18 @@ static int mipi_dbi_spi1_transfer(struct mipi_dbi *mipi, int dc,
        return 0;
 }
 
-static int mipi_dbi_typec1_command(struct mipi_dbi *mipi, u8 cmd,
+static int mipi_dbi_typec1_command(struct mipi_dbi *mipi, u8 *cmd,
                                   u8 *parameters, size_t num)
 {
-       unsigned int bpw = (cmd == MIPI_DCS_WRITE_MEMORY_START) ? 16 : 8;
+       unsigned int bpw = (*cmd == MIPI_DCS_WRITE_MEMORY_START) ? 16 : 8;
        int ret;
 
-       if (mipi_dbi_command_is_read(mipi, cmd))
+       if (mipi_dbi_command_is_read(mipi, *cmd))
                return -ENOTSUPP;
 
-       MIPI_DBI_DEBUG_COMMAND(cmd, parameters, num);
+       MIPI_DBI_DEBUG_COMMAND(*cmd, parameters, num);
 
-       ret = mipi_dbi_spi1_transfer(mipi, 0, &cmd, 1, 8);
+       ret = mipi_dbi_spi1_transfer(mipi, 0, cmd, 1, 8);
        if (ret || !num)
                return ret;
 
@@ -794,7 +855,7 @@ static int mipi_dbi_typec1_command(struct mipi_dbi *mipi, u8 cmd,
 
 /* MIPI DBI Type C Option 3 */
 
-static int mipi_dbi_typec3_command_read(struct mipi_dbi *mipi, u8 cmd,
+static int mipi_dbi_typec3_command_read(struct mipi_dbi *mipi, u8 *cmd,
                                        u8 *data, size_t len)
 {
        struct spi_device *spi = mipi->spi;
@@ -803,7 +864,7 @@ static int mipi_dbi_typec3_command_read(struct mipi_dbi *mipi, u8 cmd,
        struct spi_transfer tr[2] = {
                {
                        .speed_hz = speed_hz,
-                       .tx_buf = &cmd,
+                       .tx_buf = cmd,
                        .len = 1,
                }, {
                        .speed_hz = speed_hz,
@@ -821,8 +882,8 @@ static int mipi_dbi_typec3_command_read(struct mipi_dbi *mipi, u8 cmd,
         * Support non-standard 24-bit and 32-bit Nokia read commands which
         * start with a dummy clock, so we need to read an extra byte.
         */
-       if (cmd == MIPI_DCS_GET_DISPLAY_ID ||
-           cmd == MIPI_DCS_GET_DISPLAY_STATUS) {
+       if (*cmd == MIPI_DCS_GET_DISPLAY_ID ||
+           *cmd == MIPI_DCS_GET_DISPLAY_STATUS) {
                if (!(len == 3 || len == 4))
                        return -EINVAL;
 
@@ -852,7 +913,7 @@ static int mipi_dbi_typec3_command_read(struct mipi_dbi *mipi, u8 cmd,
                        data[i] = (buf[i] << 1) | !!(buf[i + 1] & BIT(7));
        }
 
-       MIPI_DBI_DEBUG_COMMAND(cmd, data, len);
+       MIPI_DBI_DEBUG_COMMAND(*cmd, data, len);
 
 err_free:
        kfree(buf);
@@ -860,7 +921,7 @@ err_free:
        return ret;
 }
 
-static int mipi_dbi_typec3_command(struct mipi_dbi *mipi, u8 cmd,
+static int mipi_dbi_typec3_command(struct mipi_dbi *mipi, u8 *cmd,
                                   u8 *par, size_t num)
 {
        struct spi_device *spi = mipi->spi;
@@ -868,18 +929,18 @@ static int mipi_dbi_typec3_command(struct mipi_dbi *mipi, u8 cmd,
        u32 speed_hz;
        int ret;
 
-       if (mipi_dbi_command_is_read(mipi, cmd))
+       if (mipi_dbi_command_is_read(mipi, *cmd))
                return mipi_dbi_typec3_command_read(mipi, cmd, par, num);
 
-       MIPI_DBI_DEBUG_COMMAND(cmd, par, num);
+       MIPI_DBI_DEBUG_COMMAND(*cmd, par, num);
 
        gpiod_set_value_cansleep(mipi->dc, 0);
        speed_hz = mipi_dbi_spi_cmd_max_speed(spi, 1);
-       ret = tinydrm_spi_transfer(spi, speed_hz, NULL, 8, &cmd, 1);
+       ret = tinydrm_spi_transfer(spi, speed_hz, NULL, 8, cmd, 1);
        if (ret || !num)
                return ret;
 
-       if (cmd == MIPI_DCS_WRITE_MEMORY_START && !mipi->swap_bytes)
+       if (*cmd == MIPI_DCS_WRITE_MEMORY_START && !mipi->swap_bytes)
                bpw = 16;
 
        gpiod_set_value_cansleep(mipi->dc, 1);
@@ -926,7 +987,7 @@ int mipi_dbi_spi_init(struct spi_device *spi, struct mipi_dbi *mipi,
         * Even though it's not the SPI device that does DMA (the master does),
         * the dma mask is necessary for the dma_alloc_wc() in
         * drm_gem_cma_create(). The dma_addr returned will be a physical
-        * adddress which might be different from the bus address, but this is
+        * address which might be different from the bus address, but this is
         * not a problem since the address will not be used.
         * The virtual address is used in the transfer and the SPI core
         * re-maps it on the SPI master device using the DMA streaming API
@@ -976,11 +1037,16 @@ static ssize_t mipi_dbi_debugfs_command_write(struct file *file,
        u8 val, cmd = 0, parameters[64];
        char *buf, *pos, *token;
        unsigned int i;
-       int ret;
+       int ret, idx;
+
+       if (!drm_dev_enter(&mipi->drm, &idx))
+               return -ENODEV;
 
        buf = memdup_user_nul(ubuf, count);
-       if (IS_ERR(buf))
-               return PTR_ERR(buf);
+       if (IS_ERR(buf)) {
+               ret = PTR_ERR(buf);
+               goto err_exit;
+       }
 
        /* strip trailing whitespace */
        for (i = count - 1; i > 0; i--)
@@ -1016,6 +1082,8 @@ static ssize_t mipi_dbi_debugfs_command_write(struct file *file,
 
 err_free:
        kfree(buf);
+err_exit:
+       drm_dev_exit(idx);
 
        return ret < 0 ? ret : count;
 }
@@ -1024,8 +1092,11 @@ static int mipi_dbi_debugfs_command_show(struct seq_file *m, void *unused)
 {
        struct mipi_dbi *mipi = m->private;
        u8 cmd, val[4];
+       int ret, idx;
        size_t len;
-       int ret;
+
+       if (!drm_dev_enter(&mipi->drm, &idx))
+               return -ENODEV;
 
        for (cmd = 0; cmd < 255; cmd++) {
                if (!mipi_dbi_command_is_read(mipi, cmd))
@@ -1056,6 +1127,8 @@ static int mipi_dbi_debugfs_command_show(struct seq_file *m, void *unused)
                seq_printf(m, "%*phN\n", (int)len, val);
        }
 
+       drm_dev_exit(idx);
+
        return 0;
 }
 
@@ -1088,8 +1161,7 @@ static const struct file_operations mipi_dbi_debugfs_command_fops = {
  */
 int mipi_dbi_debugfs_init(struct drm_minor *minor)
 {
-       struct tinydrm_device *tdev = minor->dev->dev_private;
-       struct mipi_dbi *mipi = mipi_dbi_from_tinydrm(tdev);
+       struct mipi_dbi *mipi = drm_to_mipi_dbi(minor->dev);
        umode_t mode = S_IFREG | S_IWUSR;
 
        if (mipi->read_commands)
index b037c65..3f36324 100644 (file)
 #include <linux/spi/spi.h>
 #include <linux/thermal.h>
 
+#include <drm/drm_atomic_helper.h>
 #include <drm/drm_damage_helper.h>
 #include <drm/drm_drv.h>
 #include <drm/drm_fb_cma_helper.h>
+#include <drm/drm_fb_helper.h>
 #include <drm/drm_gem_cma_helper.h>
 #include <drm/drm_gem_framebuffer_helper.h>
 #include <drm/drm_rect.h>
 #include <drm/drm_vblank.h>
-#include <drm/tinydrm/tinydrm.h>
+#include <drm/drm_simple_kms_helper.h>
 #include <drm/tinydrm/tinydrm-helpers.h>
 
 #define REPAPER_RID_G2_COG_ID  0x12
@@ -59,7 +61,8 @@ enum repaper_epd_border_byte {
 };
 
 struct repaper_epd {
-       struct tinydrm_device tinydrm;
+       struct drm_device drm;
+       struct drm_simple_display_pipe pipe;
        struct spi_device *spi;
 
        struct gpio_desc *panel_on;
@@ -88,10 +91,9 @@ struct repaper_epd {
        bool partial;
 };
 
-static inline struct repaper_epd *
-epd_from_tinydrm(struct tinydrm_device *tdev)
+static inline struct repaper_epd *drm_to_epd(struct drm_device *drm)
 {
-       return container_of(tdev, struct repaper_epd, tinydrm);
+       return container_of(drm, struct repaper_epd, drm);
 }
 
 static int repaper_spi_transfer(struct spi_device *spi, u8 header,
@@ -529,11 +531,16 @@ static int repaper_fb_dirty(struct drm_framebuffer *fb)
 {
        struct drm_gem_cma_object *cma_obj = drm_fb_cma_get_gem_obj(fb, 0);
        struct dma_buf_attachment *import_attach = cma_obj->base.import_attach;
-       struct tinydrm_device *tdev = fb->dev->dev_private;
-       struct repaper_epd *epd = epd_from_tinydrm(tdev);
+       struct repaper_epd *epd = drm_to_epd(fb->dev);
        struct drm_rect clip;
+       int idx, ret = 0;
        u8 *buf = NULL;
-       int ret = 0;
+
+       if (!epd->enabled)
+               return 0;
+
+       if (!drm_dev_enter(fb->dev, &idx))
+               return -ENODEV;
 
        /* repaper can't do partial updates */
        clip.x1 = 0;
@@ -541,17 +548,16 @@ static int repaper_fb_dirty(struct drm_framebuffer *fb)
        clip.y1 = 0;
        clip.y2 = fb->height;
 
-       if (!epd->enabled)
-               return 0;
-
        repaper_get_temperature(epd);
 
        DRM_DEBUG("Flushing [FB:%d] st=%ums\n", fb->base.id,
                  epd->factored_stage_time);
 
        buf = kmalloc_array(fb->width, fb->height, GFP_KERNEL);
-       if (!buf)
-               return -ENOMEM;
+       if (!buf) {
+               ret = -ENOMEM;
+               goto out_exit;
+       }
 
        if (import_attach) {
                ret = dma_buf_begin_cpu_access(import_attach->dmabuf,
@@ -620,6 +626,8 @@ static int repaper_fb_dirty(struct drm_framebuffer *fb)
 
 out_free:
        kfree(buf);
+out_exit:
+       drm_dev_exit(idx);
 
        return ret;
 }
@@ -645,12 +653,14 @@ static void repaper_pipe_enable(struct drm_simple_display_pipe *pipe,
                                struct drm_crtc_state *crtc_state,
                                struct drm_plane_state *plane_state)
 {
-       struct tinydrm_device *tdev = pipe_to_tinydrm(pipe);
-       struct repaper_epd *epd = epd_from_tinydrm(tdev);
+       struct repaper_epd *epd = drm_to_epd(pipe->crtc.dev);
        struct spi_device *spi = epd->spi;
        struct device *dev = &spi->dev;
        bool dc_ok = false;
-       int i, ret;
+       int i, ret, idx;
+
+       if (!drm_dev_enter(pipe->crtc.dev, &idx))
+               return;
 
        DRM_DEBUG_DRIVER("\n");
 
@@ -689,7 +699,7 @@ static void repaper_pipe_enable(struct drm_simple_display_pipe *pipe,
        if (!i) {
                DRM_DEV_ERROR(dev, "timeout waiting for panel to become ready.\n");
                power_off(epd);
-               return;
+               goto out_exit;
        }
 
        repaper_read_id(spi);
@@ -700,7 +710,7 @@ static void repaper_pipe_enable(struct drm_simple_display_pipe *pipe,
                else
                        dev_err(dev, "wrong COG ID 0x%02x\n", ret);
                power_off(epd);
-               return;
+               goto out_exit;
        }
 
        /* Disable OE */
@@ -713,7 +723,7 @@ static void repaper_pipe_enable(struct drm_simple_display_pipe *pipe,
                else
                        DRM_DEV_ERROR(dev, "panel is reported broken\n");
                power_off(epd);
-               return;
+               goto out_exit;
        }
 
        /* Power saving mode */
@@ -753,7 +763,7 @@ static void repaper_pipe_enable(struct drm_simple_display_pipe *pipe,
                if (ret < 0) {
                        DRM_DEV_ERROR(dev, "failed to read chip (%d)\n", ret);
                        power_off(epd);
-                       return;
+                       goto out_exit;
                }
 
                if (ret & 0x40) {
@@ -765,7 +775,7 @@ static void repaper_pipe_enable(struct drm_simple_display_pipe *pipe,
        if (!dc_ok) {
                DRM_DEV_ERROR(dev, "dc/dc failed\n");
                power_off(epd);
-               return;
+               goto out_exit;
        }
 
        /*
@@ -776,15 +786,26 @@ static void repaper_pipe_enable(struct drm_simple_display_pipe *pipe,
 
        epd->enabled = true;
        epd->partial = false;
+out_exit:
+       drm_dev_exit(idx);
 }
 
 static void repaper_pipe_disable(struct drm_simple_display_pipe *pipe)
 {
-       struct tinydrm_device *tdev = pipe_to_tinydrm(pipe);
-       struct repaper_epd *epd = epd_from_tinydrm(tdev);
+       struct repaper_epd *epd = drm_to_epd(pipe->crtc.dev);
        struct spi_device *spi = epd->spi;
        unsigned int line;
 
+       /*
+        * This callback is not protected by drm_dev_enter/exit since we want to
+        * turn off the display on regular driver unload. It's highly unlikely
+        * that the underlying SPI controller is gone should this be called after
+        * unplug.
+        */
+
+       if (!epd->enabled)
+               return;
+
        DRM_DEBUG_DRIVER("\n");
 
        epd->enabled = false;
@@ -855,33 +876,50 @@ static const struct drm_simple_display_pipe_funcs repaper_pipe_funcs = {
        .prepare_fb = drm_gem_fb_simple_display_pipe_prepare_fb,
 };
 
+static const struct drm_mode_config_funcs repaper_mode_config_funcs = {
+       .fb_create = drm_gem_fb_create_with_dirty,
+       .atomic_check = drm_atomic_helper_check,
+       .atomic_commit = drm_atomic_helper_commit,
+};
+
+static void repaper_release(struct drm_device *drm)
+{
+       struct repaper_epd *epd = drm_to_epd(drm);
+
+       DRM_DEBUG_DRIVER("\n");
+
+       drm_mode_config_cleanup(drm);
+       drm_dev_fini(drm);
+       kfree(epd);
+}
+
 static const uint32_t repaper_formats[] = {
        DRM_FORMAT_XRGB8888,
 };
 
 static const struct drm_display_mode repaper_e1144cs021_mode = {
-       TINYDRM_MODE(128, 96, 29, 22),
+       DRM_SIMPLE_MODE(128, 96, 29, 22),
 };
 
 static const u8 repaper_e1144cs021_cs[] = { 0x00, 0x00, 0x00, 0x00,
                                            0x00, 0x0f, 0xff, 0x00 };
 
 static const struct drm_display_mode repaper_e1190cs021_mode = {
-       TINYDRM_MODE(144, 128, 36, 32),
+       DRM_SIMPLE_MODE(144, 128, 36, 32),
 };
 
 static const u8 repaper_e1190cs021_cs[] = { 0x00, 0x00, 0x00, 0x03,
                                            0xfc, 0x00, 0x00, 0xff };
 
 static const struct drm_display_mode repaper_e2200cs021_mode = {
-       TINYDRM_MODE(200, 96, 46, 22),
+       DRM_SIMPLE_MODE(200, 96, 46, 22),
 };
 
 static const u8 repaper_e2200cs021_cs[] = { 0x00, 0x00, 0x00, 0x00,
                                            0x01, 0xff, 0xe0, 0x00 };
 
 static const struct drm_display_mode repaper_e2271cs021_mode = {
-       TINYDRM_MODE(264, 176, 57, 38),
+       DRM_SIMPLE_MODE(264, 176, 57, 38),
 };
 
 static const u8 repaper_e2271cs021_cs[] = { 0x00, 0x00, 0x00, 0x7f,
@@ -893,6 +931,7 @@ static struct drm_driver repaper_driver = {
        .driver_features        = DRIVER_GEM | DRIVER_MODESET | DRIVER_PRIME |
                                  DRIVER_ATOMIC,
        .fops                   = &repaper_fops,
+       .release                = repaper_release,
        DRM_GEM_CMA_VMAP_DRIVER_OPS,
        .name                   = "repaper",
        .desc                   = "Pervasive Displays RePaper e-ink panels",
@@ -925,11 +964,11 @@ static int repaper_probe(struct spi_device *spi)
        const struct spi_device_id *spi_id;
        const struct of_device_id *match;
        struct device *dev = &spi->dev;
-       struct tinydrm_device *tdev;
        enum repaper_model model;
        const char *thermal_zone;
        struct repaper_epd *epd;
        size_t line_buffer_size;
+       struct drm_device *drm;
        int ret;
 
        match = of_match_device(repaper_of_match, dev);
@@ -949,10 +988,21 @@ static int repaper_probe(struct spi_device *spi)
                }
        }
 
-       epd = devm_kzalloc(dev, sizeof(*epd), GFP_KERNEL);
+       epd = kzalloc(sizeof(*epd), GFP_KERNEL);
        if (!epd)
                return -ENOMEM;
 
+       drm = &epd->drm;
+
+       ret = devm_drm_dev_init(dev, drm, &repaper_driver);
+       if (ret) {
+               kfree(epd);
+               return ret;
+       }
+
+       drm_mode_config_init(drm);
+       drm->mode_config.funcs = &repaper_mode_config_funcs;
+
        epd->spi = spi;
 
        epd->panel_on = devm_gpiod_get(dev, "panel-on", GPIOD_OUT_LOW);
@@ -1063,32 +1113,41 @@ static int repaper_probe(struct spi_device *spi)
        if (!epd->current_frame)
                return -ENOMEM;
 
-       tdev = &epd->tinydrm;
-
-       ret = devm_tinydrm_init(dev, tdev, &repaper_driver);
-       if (ret)
-               return ret;
-
-       ret = tinydrm_display_pipe_init(tdev, &repaper_pipe_funcs,
+       ret = tinydrm_display_pipe_init(drm, &epd->pipe, &repaper_pipe_funcs,
                                        DRM_MODE_CONNECTOR_VIRTUAL,
                                        repaper_formats,
                                        ARRAY_SIZE(repaper_formats), mode, 0);
        if (ret)
                return ret;
 
-       drm_mode_config_reset(tdev->drm);
-       spi_set_drvdata(spi, tdev);
+       drm_mode_config_reset(drm);
+
+       ret = drm_dev_register(drm, 0);
+       if (ret)
+               return ret;
+
+       spi_set_drvdata(spi, drm);
 
        DRM_DEBUG_DRIVER("SPI speed: %uMHz\n", spi->max_speed_hz / 1000000);
 
-       return devm_tinydrm_register(tdev);
+       drm_fbdev_generic_setup(drm, 32);
+
+       return 0;
 }
 
-static void repaper_shutdown(struct spi_device *spi)
+static int repaper_remove(struct spi_device *spi)
 {
-       struct tinydrm_device *tdev = spi_get_drvdata(spi);
+       struct drm_device *drm = spi_get_drvdata(spi);
+
+       drm_dev_unplug(drm);
+       drm_atomic_helper_shutdown(drm);
+
+       return 0;
+}
 
-       tinydrm_shutdown(tdev);
+static void repaper_shutdown(struct spi_device *spi)
+{
+       drm_atomic_helper_shutdown(spi_get_drvdata(spi));
 }
 
 static struct spi_driver repaper_spi_driver = {
@@ -1099,6 +1158,7 @@ static struct spi_driver repaper_spi_driver = {
        },
        .id_table = repaper_id,
        .probe = repaper_probe,
+       .remove = repaper_remove,
        .shutdown = repaper_shutdown,
 };
 module_spi_driver(repaper_spi_driver);
index 01a8077..d99957b 100644 (file)
 #include <linux/spi/spi.h>
 #include <video/mipi_display.h>
 
+#include <drm/drm_atomic_helper.h>
 #include <drm/drm_damage_helper.h>
 #include <drm/drm_drv.h>
 #include <drm/drm_fb_cma_helper.h>
+#include <drm/drm_fb_helper.h>
 #include <drm/drm_gem_cma_helper.h>
 #include <drm/drm_gem_framebuffer_helper.h>
 #include <drm/drm_rect.h>
@@ -116,14 +118,15 @@ static int st7586_buf_copy(void *dst, struct drm_framebuffer *fb,
 
 static void st7586_fb_dirty(struct drm_framebuffer *fb, struct drm_rect *rect)
 {
-       struct tinydrm_device *tdev = fb->dev->dev_private;
-       struct mipi_dbi *mipi = mipi_dbi_from_tinydrm(tdev);
-       int start, end;
-       int ret = 0;
+       struct mipi_dbi *mipi = drm_to_mipi_dbi(fb->dev);
+       int start, end, idx, ret = 0;
 
        if (!mipi->enabled)
                return;
 
+       if (!drm_dev_enter(fb->dev, &idx))
+               return;
+
        /* 3 pixels per byte, so grow clip to nearest multiple of 3 */
        rect->x1 = rounddown(rect->x1, 3);
        rect->x2 = roundup(rect->x2, 3);
@@ -151,6 +154,8 @@ static void st7586_fb_dirty(struct drm_framebuffer *fb, struct drm_rect *rect)
 err_msg:
        if (ret)
                dev_err_once(fb->dev->dev, "Failed to update display %d\n", ret);
+
+       drm_dev_exit(idx);
 }
 
 static void st7586_pipe_update(struct drm_simple_display_pipe *pipe,
@@ -175,8 +180,7 @@ static void st7586_pipe_enable(struct drm_simple_display_pipe *pipe,
                               struct drm_crtc_state *crtc_state,
                               struct drm_plane_state *plane_state)
 {
-       struct tinydrm_device *tdev = pipe_to_tinydrm(pipe);
-       struct mipi_dbi *mipi = mipi_dbi_from_tinydrm(tdev);
+       struct mipi_dbi *mipi = drm_to_mipi_dbi(pipe->crtc.dev);
        struct drm_framebuffer *fb = plane_state->fb;
        struct drm_rect rect = {
                .x1 = 0,
@@ -184,14 +188,17 @@ static void st7586_pipe_enable(struct drm_simple_display_pipe *pipe,
                .y1 = 0,
                .y2 = fb->height,
        };
-       int ret;
+       int idx, ret;
        u8 addr_mode;
 
+       if (!drm_dev_enter(pipe->crtc.dev, &idx))
+               return;
+
        DRM_DEBUG_KMS("\n");
 
        ret = mipi_dbi_poweron_reset(mipi);
        if (ret)
-               return;
+               goto out_exit;
 
        mipi_dbi_command(mipi, ST7586_AUTO_READ_CTRL, 0x9f);
        mipi_dbi_command(mipi, ST7586_OTP_RW_CTRL, 0x00);
@@ -244,12 +251,20 @@ static void st7586_pipe_enable(struct drm_simple_display_pipe *pipe,
        st7586_fb_dirty(fb, &rect);
 
        mipi_dbi_command(mipi, MIPI_DCS_SET_DISPLAY_ON);
+out_exit:
+       drm_dev_exit(idx);
 }
 
 static void st7586_pipe_disable(struct drm_simple_display_pipe *pipe)
 {
-       struct tinydrm_device *tdev = pipe_to_tinydrm(pipe);
-       struct mipi_dbi *mipi = mipi_dbi_from_tinydrm(tdev);
+       struct mipi_dbi *mipi = drm_to_mipi_dbi(pipe->crtc.dev);
+
+       /*
+        * This callback is not protected by drm_dev_enter/exit since we want to
+        * turn off the display on regular driver unload. It's highly unlikely
+        * that the underlying SPI controller is gone should this be called after
+        * unplug.
+        */
 
        DRM_DEBUG_KMS("\n");
 
@@ -264,46 +279,6 @@ static const u32 st7586_formats[] = {
        DRM_FORMAT_XRGB8888,
 };
 
-static int st7586_init(struct device *dev, struct mipi_dbi *mipi,
-               const struct drm_simple_display_pipe_funcs *pipe_funcs,
-               struct drm_driver *driver, const struct drm_display_mode *mode,
-               unsigned int rotation)
-{
-       size_t bufsize = (mode->vdisplay + 2) / 3 * mode->hdisplay;
-       struct tinydrm_device *tdev = &mipi->tinydrm;
-       int ret;
-
-       mutex_init(&mipi->cmdlock);
-
-       mipi->tx_buf = devm_kmalloc(dev, bufsize, GFP_KERNEL);
-       if (!mipi->tx_buf)
-               return -ENOMEM;
-
-       ret = devm_tinydrm_init(dev, tdev, driver);
-       if (ret)
-               return ret;
-
-       ret = tinydrm_display_pipe_init(tdev, pipe_funcs,
-                                       DRM_MODE_CONNECTOR_VIRTUAL,
-                                       st7586_formats,
-                                       ARRAY_SIZE(st7586_formats),
-                                       mode, rotation);
-       if (ret)
-               return ret;
-
-       drm_plane_enable_fb_damage_clips(&tdev->pipe.plane);
-
-       tdev->drm->mode_config.preferred_depth = 32;
-       mipi->rotation = rotation;
-
-       drm_mode_config_reset(tdev->drm);
-
-       DRM_DEBUG_KMS("preferred_depth=%u, rotation = %u\n",
-                     tdev->drm->mode_config.preferred_depth, rotation);
-
-       return 0;
-}
-
 static const struct drm_simple_display_pipe_funcs st7586_pipe_funcs = {
        .enable         = st7586_pipe_enable,
        .disable        = st7586_pipe_disable,
@@ -311,8 +286,14 @@ static const struct drm_simple_display_pipe_funcs st7586_pipe_funcs = {
        .prepare_fb     = drm_gem_fb_simple_display_pipe_prepare_fb,
 };
 
+static const struct drm_mode_config_funcs st7586_mode_config_funcs = {
+       .fb_create = drm_gem_fb_create_with_dirty,
+       .atomic_check = drm_atomic_helper_check,
+       .atomic_commit = drm_atomic_helper_commit,
+};
+
 static const struct drm_display_mode st7586_mode = {
-       TINYDRM_MODE(178, 128, 37, 27),
+       DRM_SIMPLE_MODE(178, 128, 37, 27),
 };
 
 DEFINE_DRM_GEM_CMA_FOPS(st7586_fops);
@@ -321,6 +302,7 @@ static struct drm_driver st7586_driver = {
        .driver_features        = DRIVER_GEM | DRIVER_MODESET | DRIVER_PRIME |
                                  DRIVER_ATOMIC,
        .fops                   = &st7586_fops,
+       .release                = mipi_dbi_release,
        DRM_GEM_CMA_VMAP_DRIVER_OPS,
        .debugfs_init           = mipi_dbi_debugfs_init,
        .name                   = "st7586",
@@ -345,15 +327,35 @@ MODULE_DEVICE_TABLE(spi, st7586_id);
 static int st7586_probe(struct spi_device *spi)
 {
        struct device *dev = &spi->dev;
+       struct drm_device *drm;
        struct mipi_dbi *mipi;
        struct gpio_desc *a0;
        u32 rotation = 0;
+       size_t bufsize;
        int ret;
 
-       mipi = devm_kzalloc(dev, sizeof(*mipi), GFP_KERNEL);
+       mipi = kzalloc(sizeof(*mipi), GFP_KERNEL);
        if (!mipi)
                return -ENOMEM;
 
+       drm = &mipi->drm;
+       ret = devm_drm_dev_init(dev, drm, &st7586_driver);
+       if (ret) {
+               kfree(mipi);
+               return ret;
+       }
+
+       drm_mode_config_init(drm);
+       drm->mode_config.preferred_depth = 32;
+       drm->mode_config.funcs = &st7586_mode_config_funcs;
+
+       mutex_init(&mipi->cmdlock);
+
+       bufsize = (st7586_mode.vdisplay + 2) / 3 * st7586_mode.hdisplay;
+       mipi->tx_buf = devm_kmalloc(dev, bufsize, GFP_KERNEL);
+       if (!mipi->tx_buf)
+               return -ENOMEM;
+
        mipi->reset = devm_gpiod_get(dev, "reset", GPIOD_OUT_HIGH);
        if (IS_ERR(mipi->reset)) {
                DRM_DEV_ERROR(dev, "Failed to get gpio 'reset'\n");
@@ -367,6 +369,7 @@ static int st7586_probe(struct spi_device *spi)
        }
 
        device_property_read_u32(dev, "rotation", &rotation);
+       mipi->rotation = rotation;
 
        ret = mipi_dbi_spi_init(spi, mipi, a0);
        if (ret)
@@ -384,21 +387,44 @@ static int st7586_probe(struct spi_device *spi)
         */
        mipi->swap_bytes = true;
 
-       ret = st7586_init(&spi->dev, mipi, &st7586_pipe_funcs, &st7586_driver,
-                         &st7586_mode, rotation);
+       ret = tinydrm_display_pipe_init(drm, &mipi->pipe, &st7586_pipe_funcs,
+                                       DRM_MODE_CONNECTOR_VIRTUAL,
+                                       st7586_formats, ARRAY_SIZE(st7586_formats),
+                                       &st7586_mode, rotation);
        if (ret)
                return ret;
 
-       spi_set_drvdata(spi, mipi);
+       drm_plane_enable_fb_damage_clips(&mipi->pipe.plane);
 
-       return devm_tinydrm_register(&mipi->tinydrm);
+       drm_mode_config_reset(drm);
+
+       ret = drm_dev_register(drm, 0);
+       if (ret)
+               return ret;
+
+       spi_set_drvdata(spi, drm);
+
+       DRM_DEBUG_KMS("preferred_depth=%u, rotation = %u\n",
+                     drm->mode_config.preferred_depth, rotation);
+
+       drm_fbdev_generic_setup(drm, 32);
+
+       return 0;
 }
 
-static void st7586_shutdown(struct spi_device *spi)
+static int st7586_remove(struct spi_device *spi)
 {
-       struct mipi_dbi *mipi = spi_get_drvdata(spi);
+       struct drm_device *drm = spi_get_drvdata(spi);
+
+       drm_dev_unplug(drm);
+       drm_atomic_helper_shutdown(drm);
+
+       return 0;
+}
 
-       tinydrm_shutdown(&mipi->tinydrm);
+static void st7586_shutdown(struct spi_device *spi)
+{
+       drm_atomic_helper_shutdown(spi_get_drvdata(spi));
 }
 
 static struct spi_driver st7586_spi_driver = {
@@ -409,6 +435,7 @@ static struct spi_driver st7586_spi_driver = {
        },
        .id_table = st7586_id,
        .probe = st7586_probe,
+       .remove = st7586_remove,
        .shutdown = st7586_shutdown,
 };
 module_spi_driver(st7586_spi_driver);
index 3bab9a9..022e984 100644 (file)
@@ -14,7 +14,9 @@
 #include <linux/spi/spi.h>
 #include <video/mipi_display.h>
 
+#include <drm/drm_atomic_helper.h>
 #include <drm/drm_drv.h>
+#include <drm/drm_fb_helper.h>
 #include <drm/drm_gem_cma_helper.h>
 #include <drm/drm_gem_framebuffer_helper.h>
 #include <drm/tinydrm/mipi-dbi.h>
@@ -41,16 +43,18 @@ static void jd_t18003_t01_pipe_enable(struct drm_simple_display_pipe *pipe,
                                      struct drm_crtc_state *crtc_state,
                                      struct drm_plane_state *plane_state)
 {
-       struct tinydrm_device *tdev = pipe_to_tinydrm(pipe);
-       struct mipi_dbi *mipi = mipi_dbi_from_tinydrm(tdev);
-       int ret;
+       struct mipi_dbi *mipi = drm_to_mipi_dbi(pipe->crtc.dev);
+       int ret, idx;
        u8 addr_mode;
 
+       if (!drm_dev_enter(pipe->crtc.dev, &idx))
+               return;
+
        DRM_DEBUG_KMS("\n");
 
        ret = mipi_dbi_poweron_reset(mipi);
        if (ret)
-               return;
+               goto out_exit;
 
        msleep(150);
 
@@ -101,6 +105,8 @@ static void jd_t18003_t01_pipe_enable(struct drm_simple_display_pipe *pipe,
        msleep(20);
 
        mipi_dbi_enable_flush(mipi, crtc_state, plane_state);
+out_exit:
+       drm_dev_exit(idx);
 }
 
 static const struct drm_simple_display_pipe_funcs jd_t18003_t01_pipe_funcs = {
@@ -111,7 +117,7 @@ static const struct drm_simple_display_pipe_funcs jd_t18003_t01_pipe_funcs = {
 };
 
 static const struct drm_display_mode jd_t18003_t01_mode = {
-       TINYDRM_MODE(128, 160, 28, 35),
+       DRM_SIMPLE_MODE(128, 160, 28, 35),
 };
 
 DEFINE_DRM_GEM_CMA_FOPS(st7735r_fops);
@@ -120,6 +126,7 @@ static struct drm_driver st7735r_driver = {
        .driver_features        = DRIVER_GEM | DRIVER_MODESET | DRIVER_PRIME |
                                  DRIVER_ATOMIC,
        .fops                   = &st7735r_fops,
+       .release                = mipi_dbi_release,
        DRM_GEM_CMA_VMAP_DRIVER_OPS,
        .debugfs_init           = mipi_dbi_debugfs_init,
        .name                   = "st7735r",
@@ -144,15 +151,25 @@ MODULE_DEVICE_TABLE(spi, st7735r_id);
 static int st7735r_probe(struct spi_device *spi)
 {
        struct device *dev = &spi->dev;
+       struct drm_device *drm;
        struct mipi_dbi *mipi;
        struct gpio_desc *dc;
        u32 rotation = 0;
        int ret;
 
-       mipi = devm_kzalloc(dev, sizeof(*mipi), GFP_KERNEL);
+       mipi = kzalloc(sizeof(*mipi), GFP_KERNEL);
        if (!mipi)
                return -ENOMEM;
 
+       drm = &mipi->drm;
+       ret = devm_drm_dev_init(dev, drm, &st7735r_driver);
+       if (ret) {
+               kfree(mipi);
+               return ret;
+       }
+
+       drm_mode_config_init(drm);
+
        mipi->reset = devm_gpiod_get(dev, "reset", GPIOD_OUT_HIGH);
        if (IS_ERR(mipi->reset)) {
                DRM_DEV_ERROR(dev, "Failed to get gpio 'reset'\n");
@@ -178,21 +195,36 @@ static int st7735r_probe(struct spi_device *spi)
        /* Cannot read from Adafruit 1.8" display via SPI */
        mipi->read_commands = NULL;
 
-       ret = mipi_dbi_init(&spi->dev, mipi, &jd_t18003_t01_pipe_funcs,
-                           &st7735r_driver, &jd_t18003_t01_mode, rotation);
+       ret = mipi_dbi_init(mipi, &jd_t18003_t01_pipe_funcs, &jd_t18003_t01_mode, rotation);
        if (ret)
                return ret;
 
-       spi_set_drvdata(spi, mipi);
+       drm_mode_config_reset(drm);
 
-       return devm_tinydrm_register(&mipi->tinydrm);
+       ret = drm_dev_register(drm, 0);
+       if (ret)
+               return ret;
+
+       spi_set_drvdata(spi, drm);
+
+       drm_fbdev_generic_setup(drm, 32);
+
+       return 0;
 }
 
-static void st7735r_shutdown(struct spi_device *spi)
+static int st7735r_remove(struct spi_device *spi)
 {
-       struct mipi_dbi *mipi = spi_get_drvdata(spi);
+       struct drm_device *drm = spi_get_drvdata(spi);
+
+       drm_dev_unplug(drm);
+       drm_atomic_helper_shutdown(drm);
 
-       tinydrm_shutdown(&mipi->tinydrm);
+       return 0;
+}
+
+static void st7735r_shutdown(struct spi_device *spi)
+{
+       drm_atomic_helper_shutdown(spi_get_drvdata(spi));
 }
 
 static struct spi_driver st7735r_spi_driver = {
@@ -203,6 +235,7 @@ static struct spi_driver st7735r_spi_driver = {
        },
        .id_table = st7735r_id,
        .probe = st7735r_probe,
+       .remove = st7735r_remove,
        .shutdown = st7735r_shutdown,
 };
 module_spi_driver(st7735r_spi_driver);
index 22cd2d1..53b7b8c 100644 (file)
@@ -107,6 +107,7 @@ static void udl_usb_disconnect(struct usb_interface *interface)
        udl_fbdev_unplug(dev);
        udl_drop_usb(dev);
        drm_dev_unplug(dev);
+       drm_dev_put(dev);
 }
 
 /*
index 1552bf5..75a74c4 100644 (file)
@@ -5,6 +5,7 @@ config DRM_V3D
        depends on COMMON_CLK
        depends on MMU
        select DRM_SCHED
+       select DRM_GEM_SHMEM_HELPER
        help
          Choose this option if you have a system that has a Broadcom
          V3D 3.x or newer GPU, such as BCM7268.
index a08766d..c0219eb 100644 (file)
 #include "v3d_drv.h"
 #include "uapi/drm/v3d_drm.h"
 
-/* Pins the shmem pages, fills in the .pages and .sgt fields of the BO, and maps
- * it for DMA.
- */
-static int
-v3d_bo_get_pages(struct v3d_bo *bo)
-{
-       struct drm_gem_object *obj = &bo->base;
-       struct drm_device *dev = obj->dev;
-       int npages = obj->size >> PAGE_SHIFT;
-       int ret = 0;
-
-       mutex_lock(&bo->lock);
-       if (bo->pages_refcount++ != 0)
-               goto unlock;
-
-       if (!obj->import_attach) {
-               bo->pages = drm_gem_get_pages(obj);
-               if (IS_ERR(bo->pages)) {
-                       ret = PTR_ERR(bo->pages);
-                       goto unlock;
-               }
-
-               bo->sgt = drm_prime_pages_to_sg(bo->pages, npages);
-               if (IS_ERR(bo->sgt)) {
-                       ret = PTR_ERR(bo->sgt);
-                       goto put_pages;
-               }
-
-               /* Map the pages for use by the GPU. */
-               dma_map_sg(dev->dev, bo->sgt->sgl,
-                          bo->sgt->nents, DMA_BIDIRECTIONAL);
-       } else {
-               bo->pages = kcalloc(npages, sizeof(*bo->pages), GFP_KERNEL);
-               if (!bo->pages)
-                       goto put_pages;
-
-               drm_prime_sg_to_page_addr_arrays(bo->sgt, bo->pages,
-                                                NULL, npages);
-
-               /* Note that dma-bufs come in mapped. */
-       }
-
-       mutex_unlock(&bo->lock);
-
-       return 0;
-
-put_pages:
-       drm_gem_put_pages(obj, bo->pages, true, true);
-       bo->pages = NULL;
-unlock:
-       bo->pages_refcount--;
-       mutex_unlock(&bo->lock);
-       return ret;
-}
-
-static void
-v3d_bo_put_pages(struct v3d_bo *bo)
-{
-       struct drm_gem_object *obj = &bo->base;
-
-       mutex_lock(&bo->lock);
-       if (--bo->pages_refcount == 0) {
-               if (!obj->import_attach) {
-                       dma_unmap_sg(obj->dev->dev, bo->sgt->sgl,
-                                    bo->sgt->nents, DMA_BIDIRECTIONAL);
-                       sg_free_table(bo->sgt);
-                       kfree(bo->sgt);
-                       drm_gem_put_pages(obj, bo->pages, true, true);
-               } else {
-                       kfree(bo->pages);
-               }
-       }
-       mutex_unlock(&bo->lock);
-}
-
-static struct v3d_bo *v3d_bo_create_struct(struct drm_device *dev,
-                                          size_t unaligned_size)
-{
-       struct v3d_dev *v3d = to_v3d_dev(dev);
-       struct drm_gem_object *obj;
-       struct v3d_bo *bo;
-       size_t size = roundup(unaligned_size, PAGE_SIZE);
-       int ret;
-
-       if (size == 0)
-               return ERR_PTR(-EINVAL);
-
-       bo = kzalloc(sizeof(*bo), GFP_KERNEL);
-       if (!bo)
-               return ERR_PTR(-ENOMEM);
-       obj = &bo->base;
-
-       INIT_LIST_HEAD(&bo->vmas);
-       INIT_LIST_HEAD(&bo->unref_head);
-       mutex_init(&bo->lock);
-
-       ret = drm_gem_object_init(dev, obj, size);
-       if (ret)
-               goto free_bo;
-
-       spin_lock(&v3d->mm_lock);
-       ret = drm_mm_insert_node_generic(&v3d->mm, &bo->node,
-                                        obj->size >> PAGE_SHIFT,
-                                        GMP_GRANULARITY >> PAGE_SHIFT, 0, 0);
-       spin_unlock(&v3d->mm_lock);
-       if (ret)
-               goto free_obj;
-
-       return bo;
-
-free_obj:
-       drm_gem_object_release(obj);
-free_bo:
-       kfree(bo);
-       return ERR_PTR(ret);
-}
-
-struct v3d_bo *v3d_bo_create(struct drm_device *dev, struct drm_file *file_priv,
-                            size_t unaligned_size)
-{
-       struct v3d_dev *v3d = to_v3d_dev(dev);
-       struct drm_gem_object *obj;
-       struct v3d_bo *bo;
-       int ret;
-
-       bo = v3d_bo_create_struct(dev, unaligned_size);
-       if (IS_ERR(bo))
-               return bo;
-       obj = &bo->base;
-
-       bo->resv = &bo->_resv;
-       reservation_object_init(bo->resv);
-
-       ret = v3d_bo_get_pages(bo);
-       if (ret)
-               goto free_mm;
-
-       v3d_mmu_insert_ptes(bo);
-
-       mutex_lock(&v3d->bo_lock);
-       v3d->bo_stats.num_allocated++;
-       v3d->bo_stats.pages_allocated += obj->size >> PAGE_SHIFT;
-       mutex_unlock(&v3d->bo_lock);
-
-       return bo;
-
-free_mm:
-       spin_lock(&v3d->mm_lock);
-       drm_mm_remove_node(&bo->node);
-       spin_unlock(&v3d->mm_lock);
-
-       drm_gem_object_release(obj);
-       kfree(bo);
-       return ERR_PTR(ret);
-}
-
 /* Called DRM core on the last userspace/kernel unreference of the
  * BO.
  */
@@ -189,92 +33,116 @@ void v3d_free_object(struct drm_gem_object *obj)
        struct v3d_dev *v3d = to_v3d_dev(obj->dev);
        struct v3d_bo *bo = to_v3d_bo(obj);
 
+       v3d_mmu_remove_ptes(bo);
+
        mutex_lock(&v3d->bo_lock);
        v3d->bo_stats.num_allocated--;
        v3d->bo_stats.pages_allocated -= obj->size >> PAGE_SHIFT;
        mutex_unlock(&v3d->bo_lock);
 
-       reservation_object_fini(&bo->_resv);
-
-       v3d_bo_put_pages(bo);
-
-       if (obj->import_attach)
-               drm_prime_gem_destroy(obj, bo->sgt);
-
-       v3d_mmu_remove_ptes(bo);
        spin_lock(&v3d->mm_lock);
        drm_mm_remove_node(&bo->node);
        spin_unlock(&v3d->mm_lock);
 
-       mutex_destroy(&bo->lock);
+       /* GPU execution may have dirtied any pages in the BO. */
+       bo->base.pages_mark_dirty_on_put = true;
 
-       drm_gem_object_release(obj);
-       kfree(bo);
+       drm_gem_shmem_free_object(obj);
 }
 
-struct reservation_object *v3d_prime_res_obj(struct drm_gem_object *obj)
+static const struct drm_gem_object_funcs v3d_gem_funcs = {
+       .free = v3d_free_object,
+       .print_info = drm_gem_shmem_print_info,
+       .pin = drm_gem_shmem_pin,
+       .unpin = drm_gem_shmem_unpin,
+       .get_sg_table = drm_gem_shmem_get_sg_table,
+       .vmap = drm_gem_shmem_vmap,
+       .vunmap = drm_gem_shmem_vunmap,
+       .vm_ops = &drm_gem_shmem_vm_ops,
+};
+
+/* gem_create_object function for allocating a BO struct and doing
+ * early setup.
+ */
+struct drm_gem_object *v3d_create_object(struct drm_device *dev, size_t size)
 {
-       struct v3d_bo *bo = to_v3d_bo(obj);
+       struct v3d_bo *bo;
+       struct drm_gem_object *obj;
 
-       return bo->resv;
-}
+       if (size == 0)
+               return NULL;
 
-static void
-v3d_set_mmap_vma_flags(struct vm_area_struct *vma)
-{
-       vma->vm_flags &= ~VM_PFNMAP;
-       vma->vm_flags |= VM_MIXEDMAP;
-       vma->vm_page_prot = pgprot_writecombine(vm_get_page_prot(vma->vm_flags));
-}
+       bo = kzalloc(sizeof(*bo), GFP_KERNEL);
+       if (!bo)
+               return NULL;
+       obj = &bo->base.base;
 
-vm_fault_t v3d_gem_fault(struct vm_fault *vmf)
-{
-       struct vm_area_struct *vma = vmf->vma;
-       struct drm_gem_object *obj = vma->vm_private_data;
-       struct v3d_bo *bo = to_v3d_bo(obj);
-       pfn_t pfn;
-       pgoff_t pgoff;
+       obj->funcs = &v3d_gem_funcs;
 
-       /* We don't use vmf->pgoff since that has the fake offset: */
-       pgoff = (vmf->address - vma->vm_start) >> PAGE_SHIFT;
-       pfn = __pfn_to_pfn_t(page_to_pfn(bo->pages[pgoff]), PFN_DEV);
+       INIT_LIST_HEAD(&bo->unref_head);
 
-       return vmf_insert_mixed(vma, vmf->address, pfn);
+       return &bo->base.base;
 }
 
-int v3d_mmap(struct file *filp, struct vm_area_struct *vma)
+static int
+v3d_bo_create_finish(struct drm_gem_object *obj)
 {
+       struct v3d_dev *v3d = to_v3d_dev(obj->dev);
+       struct v3d_bo *bo = to_v3d_bo(obj);
+       struct sg_table *sgt;
        int ret;
 
-       ret = drm_gem_mmap(filp, vma);
+       /* So far we pin the BO in the MMU for its lifetime, so use
+        * shmem's helper for getting a lifetime sgt.
+        */
+       sgt = drm_gem_shmem_get_pages_sgt(&bo->base.base);
+       if (IS_ERR(sgt))
+               return PTR_ERR(sgt);
+
+       spin_lock(&v3d->mm_lock);
+       /* Allocate the object's space in the GPU's page tables.
+        * Inserting PTEs will happen later, but the offset is for the
+        * lifetime of the BO.
+        */
+       ret = drm_mm_insert_node_generic(&v3d->mm, &bo->node,
+                                        obj->size >> PAGE_SHIFT,
+                                        GMP_GRANULARITY >> PAGE_SHIFT, 0, 0);
+       spin_unlock(&v3d->mm_lock);
        if (ret)
                return ret;
 
-       v3d_set_mmap_vma_flags(vma);
+       /* Track stats for /debug/dri/n/bo_stats. */
+       mutex_lock(&v3d->bo_lock);
+       v3d->bo_stats.num_allocated++;
+       v3d->bo_stats.pages_allocated += obj->size >> PAGE_SHIFT;
+       mutex_unlock(&v3d->bo_lock);
 
-       return ret;
+       v3d_mmu_insert_ptes(bo);
+
+       return 0;
 }
 
-int v3d_prime_mmap(struct drm_gem_object *obj, struct vm_area_struct *vma)
+struct v3d_bo *v3d_bo_create(struct drm_device *dev, struct drm_file *file_priv,
+                            size_t unaligned_size)
 {
+       struct drm_gem_shmem_object *shmem_obj;
+       struct v3d_bo *bo;
        int ret;
 
-       ret = drm_gem_mmap_obj(obj, obj->size, vma);
-       if (ret < 0)
-               return ret;
-
-       v3d_set_mmap_vma_flags(vma);
+       shmem_obj = drm_gem_shmem_create(dev, unaligned_size);
+       if (!shmem_obj)
+               return NULL;
+       bo = to_v3d_bo(&shmem_obj->base);
 
-       return 0;
-}
+       ret = v3d_bo_create_finish(&shmem_obj->base);
+       if (ret)
+               goto free_obj;
 
-struct sg_table *
-v3d_prime_get_sg_table(struct drm_gem_object *obj)
-{
-       struct v3d_bo *bo = to_v3d_bo(obj);
-       int npages = obj->size >> PAGE_SHIFT;
+       return bo;
 
-       return drm_prime_pages_to_sg(bo->pages, npages);
+free_obj:
+       drm_gem_shmem_free_object(&shmem_obj->base);
+       return ERR_PTR(ret);
 }
 
 struct drm_gem_object *
@@ -283,20 +151,17 @@ v3d_prime_import_sg_table(struct drm_device *dev,
                          struct sg_table *sgt)
 {
        struct drm_gem_object *obj;
-       struct v3d_bo *bo;
-
-       bo = v3d_bo_create_struct(dev, attach->dmabuf->size);
-       if (IS_ERR(bo))
-               return ERR_CAST(bo);
-       obj = &bo->base;
-
-       bo->resv = attach->dmabuf->resv;
+       int ret;
 
-       bo->sgt = sgt;
-       obj->import_attach = attach;
-       v3d_bo_get_pages(bo);
+       obj = drm_gem_shmem_prime_import_sg_table(dev, attach, sgt);
+       if (IS_ERR(obj))
+               return obj;
 
-       v3d_mmu_insert_ptes(bo);
+       ret = v3d_bo_create_finish(obj);
+       if (ret) {
+               drm_gem_shmem_free_object(obj);
+               return ERR_PTR(ret);
+       }
 
        return obj;
 }
@@ -319,8 +184,8 @@ int v3d_create_bo_ioctl(struct drm_device *dev, void *data,
 
        args->offset = bo->node.start << PAGE_SHIFT;
 
-       ret = drm_gem_handle_create(file_priv, &bo->base, &args->handle);
-       drm_gem_object_put_unlocked(&bo->base);
+       ret = drm_gem_handle_create(file_priv, &bo->base.base, &args->handle);
+       drm_gem_object_put_unlocked(&bo->base.base);
 
        return ret;
 }
@@ -330,7 +195,6 @@ int v3d_mmap_bo_ioctl(struct drm_device *dev, void *data,
 {
        struct drm_v3d_mmap_bo *args = data;
        struct drm_gem_object *gem_obj;
-       int ret;
 
        if (args->flags != 0) {
                DRM_INFO("unknown mmap_bo flags: %d\n", args->flags);
@@ -343,12 +207,10 @@ int v3d_mmap_bo_ioctl(struct drm_device *dev, void *data,
                return -ENOENT;
        }
 
-       ret = drm_gem_create_mmap_offset(gem_obj);
-       if (ret == 0)
-               args->offset = drm_vma_node_offset_addr(&gem_obj->vma_node);
+       args->offset = drm_vma_node_offset_addr(&gem_obj->vma_node);
        drm_gem_object_put_unlocked(gem_obj);
 
-       return ret;
+       return 0;
 }
 
 int v3d_get_bo_offset_ioctl(struct drm_device *dev, void *data,
index eb2b2d2..a24af2d 100644 (file)
@@ -187,6 +187,11 @@ static int v3d_measure_clock(struct seq_file *m, void *unused)
        uint32_t cycles;
        int core = 0;
        int measure_ms = 1000;
+       int ret;
+
+       ret = pm_runtime_get_sync(v3d->dev);
+       if (ret < 0)
+               return ret;
 
        if (v3d->ver >= 40) {
                V3D_CORE_WRITE(core, V3D_V4_PCTR_0_SRC_0_3,
@@ -210,6 +215,9 @@ static int v3d_measure_clock(struct seq_file *m, void *unused)
                   cycles / (measure_ms * 1000),
                   (cycles / (measure_ms * 100)) % 10);
 
+       pm_runtime_mark_last_busy(v3d->dev);
+       pm_runtime_put_autosuspend(v3d->dev);
+
        return 0;
 }
 
index f0afcec..d600628 100644 (file)
@@ -7,9 +7,9 @@
  * This driver supports the Broadcom V3D 3.3 and 4.1 OpenGL ES GPUs.
  * For V3D 2.x support, see the VC4 driver.
  *
- * Currently only single-core rendering using the binner and renderer
- * is supported.  The TFU (texture formatting unit) and V3D 4.x's CSD
- * (compute shader dispatch) are not yet supported.
+ * Currently only single-core rendering using the binner and renderer,
+ * along with TFU (texture formatting unit) rendering is supported.
+ * V3D 4.x's CSD (compute shader dispatch) is not yet supported.
  */
 
 #include <linux/clk.h>
@@ -19,6 +19,7 @@
 #include <linux/of_platform.h>
 #include <linux/platform_device.h>
 #include <linux/pm_runtime.h>
+#include <linux/reset.h>
 #include <drm/drm_fb_cma_helper.h>
 #include <drm/drm_fb_helper.h>
 
@@ -160,17 +161,7 @@ v3d_postclose(struct drm_device *dev, struct drm_file *file)
        kfree(v3d_priv);
 }
 
-static const struct file_operations v3d_drm_fops = {
-       .owner = THIS_MODULE,
-       .open = drm_open,
-       .release = drm_release,
-       .unlocked_ioctl = drm_ioctl,
-       .mmap = v3d_mmap,
-       .poll = drm_poll,
-       .read = drm_read,
-       .compat_ioctl = drm_compat_ioctl,
-       .llseek = noop_llseek,
-};
+DEFINE_DRM_GEM_SHMEM_FOPS(v3d_drm_fops);
 
 /* DRM_AUTH is required on SUBMIT_CL for now, while we don't have GMP
  * protection between clients.  Note that render nodes would be be
@@ -188,12 +179,6 @@ static const struct drm_ioctl_desc v3d_drm_ioctls[] = {
        DRM_IOCTL_DEF_DRV(V3D_SUBMIT_TFU, v3d_submit_tfu_ioctl, DRM_RENDER_ALLOW | DRM_AUTH),
 };
 
-static const struct vm_operations_struct v3d_vm_ops = {
-       .fault = v3d_gem_fault,
-       .open = drm_gem_vm_open,
-       .close = drm_gem_vm_close,
-};
-
 static struct drm_driver v3d_drm_driver = {
        .driver_features = (DRIVER_GEM |
                            DRIVER_RENDER |
@@ -207,17 +192,11 @@ static struct drm_driver v3d_drm_driver = {
        .debugfs_init = v3d_debugfs_init,
 #endif
 
-       .gem_free_object_unlocked = v3d_free_object,
-       .gem_vm_ops = &v3d_vm_ops,
-
+       .gem_create_object = v3d_create_object,
        .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_export = drm_gem_prime_export,
-       .gem_prime_res_obj = v3d_prime_res_obj,
-       .gem_prime_get_sg_table = v3d_prime_get_sg_table,
        .gem_prime_import_sg_table = v3d_prime_import_sg_table,
-       .gem_prime_mmap = v3d_prime_mmap,
+       .gem_prime_mmap = drm_gem_prime_mmap,
 
        .ioctls = v3d_drm_ioctls,
        .num_ioctls = ARRAY_SIZE(v3d_drm_ioctls),
@@ -265,10 +244,6 @@ static int v3d_platform_drm_probe(struct platform_device *pdev)
        v3d->pdev = pdev;
        drm = &v3d->drm;
 
-       ret = map_regs(v3d, &v3d->bridge_regs, "bridge");
-       if (ret)
-               goto dev_free;
-
        ret = map_regs(v3d, &v3d->hub_regs, "hub");
        if (ret)
                goto dev_free;
@@ -283,6 +258,22 @@ static int v3d_platform_drm_probe(struct platform_device *pdev)
        v3d->cores = V3D_GET_FIELD(ident1, V3D_HUB_IDENT1_NCORES);
        WARN_ON(v3d->cores > 1); /* multicore not yet implemented */
 
+       v3d->reset = devm_reset_control_get_exclusive(dev, NULL);
+       if (IS_ERR(v3d->reset)) {
+               ret = PTR_ERR(v3d->reset);
+
+               if (ret == -EPROBE_DEFER)
+                       goto dev_free;
+
+               v3d->reset = NULL;
+               ret = map_regs(v3d, &v3d->bridge_regs, "bridge");
+               if (ret) {
+                       dev_err(dev,
+                               "Failed to get reset control or bridge regs\n");
+                       goto dev_free;
+               }
+       }
+
        if (v3d->ver < 41) {
                ret = map_regs(v3d, &v3d->gca_regs, "gca");
                if (ret)
@@ -312,14 +303,18 @@ static int v3d_platform_drm_probe(struct platform_device *pdev)
        if (ret)
                goto dev_destroy;
 
-       v3d_irq_init(v3d);
+       ret = v3d_irq_init(v3d);
+       if (ret)
+               goto gem_destroy;
 
        ret = drm_dev_register(drm, 0);
        if (ret)
-               goto gem_destroy;
+               goto irq_disable;
 
        return 0;
 
+irq_disable:
+       v3d_irq_disable(v3d);
 gem_destroy:
        v3d_gem_destroy(drm);
 dev_destroy:
index fdda303..7b0fe62 100644 (file)
@@ -1,11 +1,11 @@
 // SPDX-License-Identifier: GPL-2.0+
 /* Copyright (C) 2015-2018 Broadcom */
 
-#include <linux/reservation.h>
 #include <linux/mm_types.h>
 #include <drm/drmP.h>
 #include <drm/drm_encoder.h>
 #include <drm/drm_gem.h>
+#include <drm/drm_gem_shmem_helper.h>
 #include <drm/gpu_scheduler.h>
 #include "uapi/drm/v3d_drm.h"
 
@@ -34,6 +34,7 @@ struct v3d_dev {
         * and revision.
         */
        int ver;
+       bool single_irq_line;
 
        struct device *dev;
        struct platform_device *pdev;
@@ -42,6 +43,7 @@ struct v3d_dev {
        void __iomem *bridge_regs;
        void __iomem *gca_regs;
        struct clk *clk;
+       struct reset_control *reset;
 
        /* Virtual and DMA addresses of the single shared page table. */
        volatile u32 *pt;
@@ -109,34 +111,15 @@ struct v3d_file_priv {
        struct drm_sched_entity sched_entity[V3D_MAX_QUEUES];
 };
 
-/* Tracks a mapping of a BO into a per-fd address space */
-struct v3d_vma {
-       struct v3d_page_table *pt;
-       struct list_head list; /* entry in v3d_bo.vmas */
-};
-
 struct v3d_bo {
-       struct drm_gem_object base;
-
-       struct mutex lock;
+       struct drm_gem_shmem_object base;
 
        struct drm_mm_node node;
 
-       u32 pages_refcount;
-       struct page **pages;
-       struct sg_table *sgt;
-       void *vaddr;
-
-       struct list_head vmas;    /* list of v3d_vma */
-
        /* List entry for the BO's position in
         * v3d_exec_info->unref_list
         */
        struct list_head unref_head;
-
-       /* normally (resv == &_resv) except for imported bo's */
-       struct reservation_object *resv;
-       struct reservation_object _resv;
 };
 
 static inline struct v3d_bo *
@@ -270,6 +253,7 @@ static inline unsigned long nsecs_to_jiffies_timeout(const u64 n)
 }
 
 /* v3d_bo.c */
+struct drm_gem_object *v3d_create_object(struct drm_device *dev, size_t size);
 void v3d_free_object(struct drm_gem_object *gem_obj);
 struct v3d_bo *v3d_bo_create(struct drm_device *dev, struct drm_file *file_priv,
                             size_t size);
@@ -279,11 +263,6 @@ int v3d_mmap_bo_ioctl(struct drm_device *dev, void *data,
                      struct drm_file *file_priv);
 int v3d_get_bo_offset_ioctl(struct drm_device *dev, void *data,
                            struct drm_file *file_priv);
-vm_fault_t v3d_gem_fault(struct vm_fault *vmf);
-int v3d_mmap(struct file *filp, struct vm_area_struct *vma);
-struct reservation_object *v3d_prime_res_obj(struct drm_gem_object *obj);
-int v3d_prime_mmap(struct drm_gem_object *obj, struct vm_area_struct *vma);
-struct sg_table *v3d_prime_get_sg_table(struct drm_gem_object *obj);
 struct drm_gem_object *v3d_prime_import_sg_table(struct drm_device *dev,
                                                 struct dma_buf_attachment *attach,
                                                 struct sg_table *sgt);
@@ -310,7 +289,7 @@ void v3d_reset(struct v3d_dev *v3d);
 void v3d_invalidate_caches(struct v3d_dev *v3d);
 
 /* v3d_irq.c */
-void v3d_irq_init(struct v3d_dev *v3d);
+int v3d_irq_init(struct v3d_dev *v3d);
 void v3d_irq_enable(struct v3d_dev *v3d);
 void v3d_irq_disable(struct v3d_dev *v3d);
 void v3d_irq_reset(struct v3d_dev *v3d);
index 803f314..b84d89c 100644 (file)
@@ -6,6 +6,7 @@
 #include <linux/module.h>
 #include <linux/platform_device.h>
 #include <linux/pm_runtime.h>
+#include <linux/reset.h>
 #include <linux/device.h>
 #include <linux/io.h>
 #include <linux/sched/signal.h>
@@ -24,7 +25,8 @@ v3d_init_core(struct v3d_dev *v3d, int core)
         * type.  If you want the default behavior, you can still put
         * "2" in the indirect texture state's output_type field.
         */
-       V3D_CORE_WRITE(core, V3D_CTL_MISCCFG, V3D_MISCCFG_OVRTMUOUT);
+       if (v3d->ver < 40)
+               V3D_CORE_WRITE(core, V3D_CTL_MISCCFG, V3D_MISCCFG_OVRTMUOUT);
 
        /* Whenever we flush the L2T cache, we always want to flush
         * the whole thing.
@@ -69,7 +71,7 @@ v3d_idle_gca(struct v3d_dev *v3d)
 }
 
 static void
-v3d_reset_v3d(struct v3d_dev *v3d)
+v3d_reset_by_bridge(struct v3d_dev *v3d)
 {
        int version = V3D_BRIDGE_READ(V3D_TOP_GR_BRIDGE_REVISION);
 
@@ -89,6 +91,15 @@ v3d_reset_v3d(struct v3d_dev *v3d)
                                 V3D_TOP_GR_BRIDGE_SW_INIT_1_V3D_CLK_108_SW_INIT);
                V3D_BRIDGE_WRITE(V3D_TOP_GR_BRIDGE_SW_INIT_1, 0);
        }
+}
+
+static void
+v3d_reset_v3d(struct v3d_dev *v3d)
+{
+       if (v3d->reset)
+               reset_control_reset(v3d->reset);
+       else
+               v3d_reset_by_bridge(v3d);
 
        v3d_init_hw_state(v3d);
 }
@@ -190,7 +201,8 @@ v3d_attach_object_fences(struct v3d_bo **bos, int bo_count,
 
        for (i = 0; i < bo_count; i++) {
                /* XXX: Use shared fences for read-only objects. */
-               reservation_object_add_excl_fence(bos[i]->resv, fence);
+               reservation_object_add_excl_fence(bos[i]->base.base.resv,
+                                                 fence);
        }
 }
 
@@ -199,12 +211,8 @@ v3d_unlock_bo_reservations(struct v3d_bo **bos,
                           int bo_count,
                           struct ww_acquire_ctx *acquire_ctx)
 {
-       int i;
-
-       for (i = 0; i < bo_count; i++)
-               ww_mutex_unlock(&bos[i]->resv->lock);
-
-       ww_acquire_fini(acquire_ctx);
+       drm_gem_unlock_reservations((struct drm_gem_object **)bos, bo_count,
+                                   acquire_ctx);
 }
 
 /* Takes the reservation lock on all the BOs being referenced, so that
@@ -219,58 +227,19 @@ v3d_lock_bo_reservations(struct v3d_bo **bos,
                         int bo_count,
                         struct ww_acquire_ctx *acquire_ctx)
 {
-       int contended_lock = -1;
        int i, ret;
 
-       ww_acquire_init(acquire_ctx, &reservation_ww_class);
-
-retry:
-       if (contended_lock != -1) {
-               struct v3d_bo *bo = bos[contended_lock];
-
-               ret = ww_mutex_lock_slow_interruptible(&bo->resv->lock,
-                                                      acquire_ctx);
-               if (ret) {
-                       ww_acquire_done(acquire_ctx);
-                       return ret;
-               }
-       }
-
-       for (i = 0; i < bo_count; i++) {
-               if (i == contended_lock)
-                       continue;
-
-               ret = ww_mutex_lock_interruptible(&bos[i]->resv->lock,
-                                                 acquire_ctx);
-               if (ret) {
-                       int j;
-
-                       for (j = 0; j < i; j++)
-                               ww_mutex_unlock(&bos[j]->resv->lock);
-
-                       if (contended_lock != -1 && contended_lock >= i) {
-                               struct v3d_bo *bo = bos[contended_lock];
-
-                               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);
+       ret = drm_gem_lock_reservations((struct drm_gem_object **)bos,
+                                       bo_count, acquire_ctx);
+       if (ret)
+               return ret;
 
        /* Reserve space for our shared (read-only) fence references,
         * before we commit the CL to the hardware.
         */
        for (i = 0; i < bo_count; i++) {
-               ret = reservation_object_reserve_shared(bos[i]->resv, 1);
+               ret = reservation_object_reserve_shared(bos[i]->base.base.resv,
+                                                       1);
                if (ret) {
                        v3d_unlock_bo_reservations(bos, bo_count,
                                                   acquire_ctx);
@@ -378,11 +347,11 @@ v3d_exec_cleanup(struct kref *ref)
        dma_fence_put(exec->render_done_fence);
 
        for (i = 0; i < exec->bo_count; i++)
-               drm_gem_object_put_unlocked(&exec->bo[i]->base);
+               drm_gem_object_put_unlocked(&exec->bo[i]->base.base);
        kvfree(exec->bo);
 
        list_for_each_entry_safe(bo, save, &exec->unref_list, unref_head) {
-               drm_gem_object_put_unlocked(&bo->base);
+               drm_gem_object_put_unlocked(&bo->base.base);
        }
 
        pm_runtime_mark_last_busy(v3d->dev);
@@ -409,7 +378,7 @@ v3d_tfu_job_cleanup(struct kref *ref)
 
        for (i = 0; i < ARRAY_SIZE(job->bo); i++) {
                if (job->bo[i])
-                       drm_gem_object_put_unlocked(&job->bo[i]->base);
+                       drm_gem_object_put_unlocked(&job->bo[i]->base.base);
        }
 
        pm_runtime_mark_last_busy(v3d->dev);
@@ -429,8 +398,6 @@ v3d_wait_bo_ioctl(struct drm_device *dev, void *data,
 {
        int ret;
        struct drm_v3d_wait_bo *args = data;
-       struct drm_gem_object *gem_obj;
-       struct v3d_bo *bo;
        ktime_t start = ktime_get();
        u64 delta_ns;
        unsigned long timeout_jiffies =
@@ -439,21 +406,8 @@ v3d_wait_bo_ioctl(struct drm_device *dev, void *data,
        if (args->pad != 0)
                return -EINVAL;
 
-       gem_obj = drm_gem_object_lookup(file_priv, args->handle);
-       if (!gem_obj) {
-               DRM_DEBUG("Failed to look up GEM BO %d\n", args->handle);
-               return -EINVAL;
-       }
-       bo = to_v3d_bo(gem_obj);
-
-       ret = reservation_object_wait_timeout_rcu(bo->resv,
-                                                 true, true,
-                                                 timeout_jiffies);
-
-       if (ret == 0)
-               ret = -ETIME;
-       else if (ret > 0)
-               ret = 0;
+       ret = drm_gem_reservation_object_wait(file_priv, args->handle,
+                                             true, timeout_jiffies);
 
        /* Decrement the user's timeout, in case we got interrupted
         * such that the ioctl will be restarted.
@@ -468,8 +422,6 @@ v3d_wait_bo_ioctl(struct drm_device *dev, void *data,
        if (ret == -ETIME && args->timeout_ns)
                ret = -EAGAIN;
 
-       drm_gem_object_put_unlocked(gem_obj);
-
        return ret;
 }
 
index 69338da..b4d6ae8 100644 (file)
@@ -27,6 +27,9 @@
                            V3D_HUB_INT_MMU_CAP |       \
                            V3D_HUB_INT_TFUC))
 
+static irqreturn_t
+v3d_hub_irq(int irq, void *arg);
+
 static void
 v3d_overflow_mem_work(struct work_struct *work)
 {
@@ -34,12 +37,14 @@ v3d_overflow_mem_work(struct work_struct *work)
                container_of(work, struct v3d_dev, overflow_mem_work);
        struct drm_device *dev = &v3d->drm;
        struct v3d_bo *bo = v3d_bo_create(dev, NULL /* XXX: GMP */, 256 * 1024);
+       struct drm_gem_object *obj;
        unsigned long irqflags;
 
        if (IS_ERR(bo)) {
                DRM_ERROR("Couldn't allocate binner overflow mem\n");
                return;
        }
+       obj = &bo->base.base;
 
        /* We lost a race, and our work task came in after the bin job
         * completed and exited.  This can happen because the HW
@@ -56,15 +61,15 @@ v3d_overflow_mem_work(struct work_struct *work)
                goto out;
        }
 
-       drm_gem_object_get(&bo->base);
+       drm_gem_object_get(obj);
        list_add_tail(&bo->unref_head, &v3d->bin_job->unref_list);
        spin_unlock_irqrestore(&v3d->job_lock, irqflags);
 
        V3D_CORE_WRITE(0, V3D_PTB_BPOA, bo->node.start << PAGE_SHIFT);
-       V3D_CORE_WRITE(0, V3D_PTB_BPOS, bo->base.size);
+       V3D_CORE_WRITE(0, V3D_PTB_BPOS, obj->size);
 
 out:
-       drm_gem_object_put_unlocked(&bo->base);
+       drm_gem_object_put_unlocked(obj);
 }
 
 static irqreturn_t
@@ -112,6 +117,12 @@ v3d_irq(int irq, void *arg)
        if (intsts & V3D_INT_GMPV)
                dev_err(v3d->dev, "GMP violation\n");
 
+       /* V3D 4.2 wires the hub and core IRQs together, so if we &
+        * didn't see the common one then check hub for MMU IRQs.
+        */
+       if (v3d->single_irq_line && status == IRQ_NONE)
+               return v3d_hub_irq(irq, arg);
+
        return status;
 }
 
@@ -156,10 +167,10 @@ v3d_hub_irq(int irq, void *arg)
        return status;
 }
 
-void
+int
 v3d_irq_init(struct v3d_dev *v3d)
 {
-       int ret, core;
+       int irq1, ret, core;
 
        INIT_WORK(&v3d->overflow_mem_work, v3d_overflow_mem_work);
 
@@ -170,16 +181,37 @@ v3d_irq_init(struct v3d_dev *v3d)
                V3D_CORE_WRITE(core, V3D_CTL_INT_CLR, V3D_CORE_IRQS);
        V3D_WRITE(V3D_HUB_INT_CLR, V3D_HUB_IRQS);
 
-       ret = devm_request_irq(v3d->dev, platform_get_irq(v3d->pdev, 0),
-                              v3d_hub_irq, IRQF_SHARED,
-                              "v3d_hub", v3d);
-       ret = devm_request_irq(v3d->dev, platform_get_irq(v3d->pdev, 1),
-                              v3d_irq, IRQF_SHARED,
-                              "v3d_core0", v3d);
-       if (ret)
-               dev_err(v3d->dev, "IRQ setup failed: %d\n", ret);
+       irq1 = platform_get_irq(v3d->pdev, 1);
+       if (irq1 == -EPROBE_DEFER)
+               return irq1;
+       if (irq1 > 0) {
+               ret = devm_request_irq(v3d->dev, irq1,
+                                      v3d_irq, IRQF_SHARED,
+                                      "v3d_core0", v3d);
+               if (ret)
+                       goto fail;
+               ret = devm_request_irq(v3d->dev, platform_get_irq(v3d->pdev, 0),
+                                      v3d_hub_irq, IRQF_SHARED,
+                                      "v3d_hub", v3d);
+               if (ret)
+                       goto fail;
+       } else {
+               v3d->single_irq_line = true;
+
+               ret = devm_request_irq(v3d->dev, platform_get_irq(v3d->pdev, 0),
+                                      v3d_irq, IRQF_SHARED,
+                                      "v3d", v3d);
+               if (ret)
+                       goto fail;
+       }
 
        v3d_irq_enable(v3d);
+       return 0;
+
+fail:
+       if (ret != -EPROBE_DEFER)
+               dev_err(v3d->dev, "IRQ setup failed: %d\n", ret);
+       return ret;
 }
 
 void
index b00f97c..7a21f17 100644 (file)
@@ -83,13 +83,14 @@ int v3d_mmu_set_page_table(struct v3d_dev *v3d)
 
 void v3d_mmu_insert_ptes(struct v3d_bo *bo)
 {
-       struct v3d_dev *v3d = to_v3d_dev(bo->base.dev);
+       struct drm_gem_shmem_object *shmem_obj = &bo->base;
+       struct v3d_dev *v3d = to_v3d_dev(shmem_obj->base.dev);
        u32 page = bo->node.start;
        u32 page_prot = V3D_PTE_WRITEABLE | V3D_PTE_VALID;
        unsigned int count;
        struct scatterlist *sgl;
 
-       for_each_sg(bo->sgt->sgl, sgl, bo->sgt->nents, count) {
+       for_each_sg(shmem_obj->sgt->sgl, sgl, shmem_obj->sgt->nents, count) {
                u32 page_address = sg_dma_address(sgl) >> V3D_MMU_PAGE_SHIFT;
                u32 pte = page_prot | page_address;
                u32 i;
@@ -102,7 +103,7 @@ void v3d_mmu_insert_ptes(struct v3d_bo *bo)
        }
 
        WARN_ON_ONCE(page - bo->node.start !=
-                    bo->base.size >> V3D_MMU_PAGE_SHIFT);
+                    shmem_obj->base.size >> V3D_MMU_PAGE_SHIFT);
 
        if (v3d_mmu_flush_all(v3d))
                dev_err(v3d->dev, "MMU flush timeout\n");
@@ -110,8 +111,8 @@ void v3d_mmu_insert_ptes(struct v3d_bo *bo)
 
 void v3d_mmu_remove_ptes(struct v3d_bo *bo)
 {
-       struct v3d_dev *v3d = to_v3d_dev(bo->base.dev);
-       u32 npages = bo->base.size >> V3D_MMU_PAGE_SHIFT;
+       struct v3d_dev *v3d = to_v3d_dev(bo->base.base.dev);
+       u32 npages = bo->base.base.size >> V3D_MMU_PAGE_SHIFT;
        u32 page;
 
        for (page = bo->node.start; page < bo->node.start + npages; page++)
index 6ccdee9..8e88af2 100644 (file)
 # define V3D_IDENT2_BCG_INT                            BIT(28)
 
 #define V3D_CTL_MISCCFG                                0x00018
+# define V3D_CTL_MISCCFG_QRMAXCNT_MASK                 V3D_MASK(3, 1)
+# define V3D_CTL_MISCCFG_QRMAXCNT_SHIFT                1
 # define V3D_MISCCFG_OVRTMUOUT                         BIT(0)
 
 #define V3D_CTL_L2CACTL                                0x00020
index 4704b2d..d0c68b7 100644 (file)
@@ -231,20 +231,17 @@ v3d_gpu_reset_for_timeout(struct v3d_dev *v3d, struct drm_sched_job *sched_job)
        mutex_lock(&v3d->reset_lock);
 
        /* block scheduler */
-       for (q = 0; q < V3D_MAX_QUEUES; q++) {
-               struct drm_gpu_scheduler *sched = &v3d->queue[q].sched;
-
-               drm_sched_stop(sched);
+       for (q = 0; q < V3D_MAX_QUEUES; q++)
+               drm_sched_stop(&v3d->queue[q].sched);
 
-               if(sched_job)
-                       drm_sched_increase_karma(sched_job);
-       }
+       if (sched_job)
+               drm_sched_increase_karma(sched_job);
 
        /* get the GPU back into the init state */
        v3d_reset(v3d);
 
        for (q = 0; q < V3D_MAX_QUEUES; q++)
-               drm_sched_resubmit_jobs(sched_job->sched);
+               drm_sched_resubmit_jobs(&v3d->queue[q].sched);
 
        /* Unblock schedulers and restart their jobs. */
        for (q = 0; q < V3D_MAX_QUEUES; q++) {
similarity index 89%
rename from drivers/staging/vboxvideo/vbox_drv.c
rename to drivers/gpu/drm/vboxvideo/vbox_drv.c
index e7755a1..fb6a0f0 100644 (file)
@@ -200,36 +200,11 @@ static const struct file_operations vbox_fops = {
        .read = drm_read,
 };
 
-static int vbox_master_set(struct drm_device *dev,
-                          struct drm_file *file_priv, bool from_open)
-{
-       struct vbox_private *vbox = dev->dev_private;
-
-       /*
-        * We do not yet know whether the new owner can handle hotplug, so we
-        * do not advertise dynamic modes on the first query and send a
-        * tentative hotplug notification after that to see if they query again.
-        */
-       vbox->initial_mode_queried = false;
-
-       return 0;
-}
-
-static void vbox_master_drop(struct drm_device *dev, struct drm_file *file_priv)
-{
-       struct vbox_private *vbox = dev->dev_private;
-
-       /* See vbox_master_set() */
-       vbox->initial_mode_queried = false;
-}
-
 static struct drm_driver driver = {
        .driver_features =
            DRIVER_MODESET | DRIVER_GEM | DRIVER_PRIME | DRIVER_ATOMIC,
 
        .lastclose = drm_fb_helper_lastclose,
-       .master_set = vbox_master_set,
-       .master_drop = vbox_master_drop,
 
        .fops = &vbox_fops,
        .irq_handler = vbox_irq_handler,
similarity index 97%
rename from drivers/staging/vboxvideo/vbox_drv.h
rename to drivers/gpu/drm/vboxvideo/vbox_drv.h
index aa40e5c..0ecd0a4 100644 (file)
@@ -14,7 +14,6 @@
 #include <linux/io.h>
 #include <linux/irqreturn.h>
 #include <linux/string.h>
-#include <linux/version.h>
 
 #include <drm/drm_encoder.h>
 #include <drm/drm_fb_helper.h>
@@ -83,12 +82,6 @@ struct vbox_private {
        } ttm;
 
        struct mutex hw_mutex; /* protects modeset and accel/vbva accesses */
-       /*
-        * We decide whether or not user-space supports display hot-plug
-        * depending on whether they react to a hot-plug event after the initial
-        * mode query.
-        */
-       bool initial_mode_queried;
        struct work_struct hotplug_work;
        u32 input_mapping_width;
        u32 input_mapping_height;
similarity index 93%
rename from drivers/staging/vboxvideo/vbox_irq.c
rename to drivers/gpu/drm/vboxvideo/vbox_irq.c
index 1954847..16a1e29 100644 (file)
@@ -105,6 +105,7 @@ static void validate_or_set_position_hints(struct vbox_private *vbox)
 /* Query the host for the most recent video mode hints. */
 static void vbox_update_mode_hints(struct vbox_private *vbox)
 {
+       struct drm_connector_list_iter conn_iter;
        struct drm_device *dev = &vbox->ddev;
        struct drm_connector *connector;
        struct vbox_connector *vbox_conn;
@@ -122,8 +123,10 @@ static void vbox_update_mode_hints(struct vbox_private *vbox)
        }
 
        validate_or_set_position_hints(vbox);
-       drm_modeset_lock_all(dev);
-       list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
+
+       drm_modeset_lock(&dev->mode_config.connection_mutex, NULL);
+       drm_connector_list_iter_begin(dev, &conn_iter);
+       drm_for_each_connector_iter(connector, &conn_iter) {
                vbox_conn = to_vbox_connector(connector);
 
                hints = &vbox->last_mode_hints[vbox_conn->vbox_crtc->crtc_id];
@@ -152,7 +155,8 @@ static void vbox_update_mode_hints(struct vbox_private *vbox)
 
                vbox_conn->vbox_crtc->disconnected = disconnected;
        }
-       drm_modeset_unlock_all(dev);
+       drm_connector_list_iter_end(&conn_iter);
+       drm_modeset_unlock(&dev->mode_config.connection_mutex);
 }
 
 static void vbox_hotplug_worker(struct work_struct *work)
similarity index 98%
rename from drivers/staging/vboxvideo/vbox_main.c
rename to drivers/gpu/drm/vboxvideo/vbox_main.c
index e1fb70a..f4d02de 100644 (file)
@@ -32,9 +32,9 @@ void vbox_report_caps(struct vbox_private *vbox)
        u32 caps = VBVACAPS_DISABLE_CURSOR_INTEGRATION |
                   VBVACAPS_IRQ | VBVACAPS_USE_VBVA_ONLY;
 
-       if (vbox->initial_mode_queried)
-               caps |= VBVACAPS_VIDEO_MODE_HINTS;
-
+       /* The host only accepts VIDEO_MODE_HINTS if it is send separately. */
+       hgsmi_send_caps_info(vbox->guest_pool, caps);
+       caps |= VBVACAPS_VIDEO_MODE_HINTS;
        hgsmi_send_caps_info(vbox->guest_pool, caps);
 }
 
similarity index 97%
rename from drivers/staging/vboxvideo/vbox_mode.c
rename to drivers/gpu/drm/vboxvideo/vbox_mode.c
index 2135513..620a6e3 100644 (file)
@@ -736,29 +736,12 @@ static int vbox_get_modes(struct drm_connector *connector)
 
        vbox_connector = to_vbox_connector(connector);
        vbox = connector->dev->dev_private;
-       /*
-        * Heuristic: we do not want to tell the host that we support dynamic
-        * resizing unless we feel confident that the user space client using
-        * the video driver can handle hot-plug events.  So the first time modes
-        * are queried after a "master" switch we tell the host that we do not,
-        * and immediately after we send the client a hot-plug notification as
-        * a test to see if they will respond and query again.
-        * That is also the reason why capabilities are reported to the host at
-        * this place in the code rather than elsewhere.
-        * We need to report the flags location before reporting the IRQ
-        * capability.
-        */
+
        hgsmi_report_flags_location(vbox->guest_pool, GUEST_HEAP_OFFSET(vbox) +
                                    HOST_FLAGS_OFFSET);
        if (vbox_connector->vbox_crtc->crtc_id == 0)
                vbox_report_caps(vbox);
-       if (!vbox->initial_mode_queried) {
-               if (vbox_connector->vbox_crtc->crtc_id == 0) {
-                       vbox->initial_mode_queried = true;
-                       vbox_report_hotplug(vbox);
-               }
-               return drm_add_modes_noedid(connector, 800, 600);
-       }
+
        num_modes = drm_add_modes_noedid(connector, 2560, 1600);
        preferred_width = vbox_connector->mode_hint.width ?
                          vbox_connector->mode_hint.width : 1024;
index 8dcce71..92e3f98 100644 (file)
@@ -201,8 +201,6 @@ static void vc4_bo_destroy(struct vc4_bo *bo)
                bo->validated_shader = NULL;
        }
 
-       reservation_object_fini(&bo->_resv);
-
        drm_gem_cma_free_object(obj);
 }
 
@@ -427,8 +425,6 @@ struct drm_gem_object *vc4_create_object(struct drm_device *dev, size_t size)
        vc4->bo_labels[VC4_BO_TYPE_KERNEL].num_allocated++;
        vc4->bo_labels[VC4_BO_TYPE_KERNEL].size_allocated += size;
        mutex_unlock(&vc4->bo_lock);
-       bo->resv = &bo->_resv;
-       reservation_object_init(bo->resv);
 
        return &bo->base.base;
 }
@@ -684,13 +680,6 @@ static void vc4_bo_cache_time_timer(struct timer_list *t)
        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)
 {
@@ -822,14 +811,12 @@ vc4_prime_import_sg_table(struct drm_device *dev,
                          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;
+       obj->resv = attach->dmabuf->resv;
 
        return obj;
 }
index 730008d..64c964b 100644 (file)
@@ -834,6 +834,14 @@ static void vc4_crtc_handle_page_flip(struct vc4_crtc *vc4_crtc)
                drm_crtc_send_vblank_event(crtc, vc4_crtc->event);
                vc4_crtc->event = NULL;
                drm_crtc_vblank_put(crtc);
+
+               /* Wait for the page flip to unmask the underrun to ensure that
+                * the display list was updated by the hardware. Before that
+                * happens, the HVS will be using the previous display list with
+                * the CRTC and encoder already reconfigured, leading to
+                * underruns. This can be seen when reconfiguring the CRTC.
+                */
+               vc4_hvs_unmask_underrun(dev, vc4_crtc->channel);
        }
        spin_unlock_irqrestore(&dev->event_lock, flags);
 }
index 7a0003d..59cdad8 100644 (file)
@@ -23,6 +23,7 @@ static const struct drm_info_list vc4_debugfs_list[] = {
        {"vec_regs", vc4_vec_debugfs_regs, 0},
        {"txp_regs", vc4_txp_debugfs_regs, 0},
        {"hvs_regs", vc4_hvs_debugfs_regs, 0},
+       {"hvs_underrun",  vc4_hvs_debugfs_underrun, 0},
        {"crtc0_regs", vc4_crtc_debugfs_regs, 0, (void *)(uintptr_t)0},
        {"crtc1_regs", vc4_crtc_debugfs_regs, 0, (void *)(uintptr_t)1},
        {"crtc2_regs", vc4_crtc_debugfs_regs, 0, (void *)(uintptr_t)2},
@@ -35,6 +36,15 @@ static const struct drm_info_list vc4_debugfs_list[] = {
 int
 vc4_debugfs_init(struct drm_minor *minor)
 {
+       struct vc4_dev *vc4 = to_vc4_dev(minor->dev);
+       struct dentry *dentry;
+
+       dentry = debugfs_create_bool("hvs_load_tracker", S_IRUGO | S_IWUSR,
+                                    minor->debugfs_root,
+                                    &vc4->load_tracker_enabled);
+       if (!dentry)
+               return -ENOMEM;
+
        return drm_debugfs_create_files(vc4_debugfs_list, VC4_DEBUGFS_ENTRIES,
                                        minor->debugfs_root, minor);
 }
index 5fcd2f0..4daf44f 100644 (file)
@@ -200,7 +200,6 @@ 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 = vc4_prime_import_sg_table,
        .gem_prime_vmap = vc4_prime_vmap,
@@ -287,7 +286,7 @@ static int vc4_drm_bind(struct device *dev)
 
        vc4_kms_load(drm);
 
-       drm_fbdev_generic_setup(drm, 32);
+       drm_fbdev_generic_setup(drm, 16);
 
        return 0;
 
@@ -312,6 +311,7 @@ static void vc4_drm_unbind(struct device *dev)
 
        drm_mode_config_cleanup(drm);
 
+       drm_atomic_private_obj_fini(&vc4->load_tracker);
        drm_atomic_private_obj_fini(&vc4->ctm_manager);
 
        drm_dev_put(drm);
index 2c635f0..7a3c093 100644 (file)
@@ -7,7 +7,6 @@
  */
 
 #include <linux/mm_types.h>
-#include <linux/reservation.h>
 #include <drm/drmP.h>
 #include <drm/drm_util.h>
 #include <drm/drm_encoder.h>
@@ -185,10 +184,20 @@ struct vc4_dev {
        /* Bitmask of the current bin_alloc used for overflow memory. */
        uint32_t bin_alloc_overflow;
 
+       /* Incremented when an underrun error happened after an atomic commit.
+        * This is particularly useful to detect when a specific modeset is too
+        * demanding in term of memory or HVS bandwidth which is hard to guess
+        * at atomic check time.
+        */
+       atomic_t underrun;
+
        struct work_struct overflow_mem_work;
 
        int power_refcount;
 
+       /* Set to true when the load tracker is active. */
+       bool load_tracker_enabled;
+
        /* Mutex controlling the power refcount. */
        struct mutex power_lock;
 
@@ -201,6 +210,7 @@ struct vc4_dev {
 
        struct drm_modeset_lock ctm_state_lock;
        struct drm_private_obj ctm_manager;
+       struct drm_private_obj load_tracker;
 };
 
 static inline struct vc4_dev *
@@ -240,10 +250,6 @@ struct vc4_bo {
         */
        struct vc4_validated_shader_info *validated_shader;
 
-       /* normally (resv == &_resv) except for imported bo's */
-       struct reservation_object *resv;
-       struct reservation_object _resv;
-
        /* One of enum vc4_kernel_bo_type, or VC4_BO_TYPE_COUNT + i
         * for user-allocated labels.
         */
@@ -376,6 +382,16 @@ struct vc4_plane_state {
         * when async update is not possible.
         */
        bool dlist_initialized;
+
+       /* Load of this plane on the HVS block. The load is expressed in HVS
+        * cycles/sec.
+        */
+       u64 hvs_load;
+
+       /* Memory bandwidth needed for this plane. This is expressed in
+        * bytes/sec.
+        */
+       u64 membus_load;
 };
 
 static inline struct vc4_plane_state *
@@ -685,7 +701,6 @@ int vc4_label_bo_ioctl(struct drm_device *dev, void *data,
                       struct drm_file *file_priv);
 vm_fault_t vc4_fault(struct vm_fault *vmf);
 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,
@@ -773,6 +788,9 @@ void vc4_irq_reset(struct drm_device *dev);
 extern struct platform_driver vc4_hvs_driver;
 void vc4_hvs_dump_state(struct drm_device *dev);
 int vc4_hvs_debugfs_regs(struct seq_file *m, void *unused);
+int vc4_hvs_debugfs_underrun(struct seq_file *m, void *unused);
+void vc4_hvs_unmask_underrun(struct drm_device *dev, int channel);
+void vc4_hvs_mask_underrun(struct drm_device *dev, int channel);
 
 /* vc4_kms.c */
 int vc4_kms_load(struct drm_device *dev);
index aea2b8d..5ee5bf7 100644 (file)
@@ -536,7 +536,7 @@ vc4_update_bo_seqnos(struct vc4_exec_info *exec, uint64_t seqno)
                bo = to_vc4_bo(&exec->bo[i]->base);
                bo->seqno = seqno;
 
-               reservation_object_add_shared_fence(bo->resv, exec->fence);
+               reservation_object_add_shared_fence(bo->base.base.resv, exec->fence);
        }
 
        list_for_each_entry(bo, &exec->unref_list, unref_head) {
@@ -547,7 +547,7 @@ vc4_update_bo_seqnos(struct vc4_exec_info *exec, uint64_t seqno)
                bo = to_vc4_bo(&exec->rcl_write_bo[i]->base);
                bo->write_seqno = seqno;
 
-               reservation_object_add_excl_fence(bo->resv, exec->fence);
+               reservation_object_add_excl_fence(bo->base.base.resv, exec->fence);
        }
 }
 
@@ -559,7 +559,7 @@ vc4_unlock_bo_reservations(struct drm_device *dev,
        int i;
 
        for (i = 0; i < exec->bo_count; i++) {
-               struct vc4_bo *bo = to_vc4_bo(&exec->bo[i]->base);
+               struct drm_gem_object *bo = &exec->bo[i]->base;
 
                ww_mutex_unlock(&bo->resv->lock);
        }
@@ -581,13 +581,13 @@ vc4_lock_bo_reservations(struct drm_device *dev,
 {
        int contended_lock = -1;
        int i, ret;
-       struct vc4_bo *bo;
+       struct drm_gem_object *bo;
 
        ww_acquire_init(acquire_ctx, &reservation_ww_class);
 
 retry:
        if (contended_lock != -1) {
-               bo = to_vc4_bo(&exec->bo[contended_lock]->base);
+               bo = &exec->bo[contended_lock]->base;
                ret = ww_mutex_lock_slow_interruptible(&bo->resv->lock,
                                                       acquire_ctx);
                if (ret) {
@@ -600,19 +600,19 @@ retry:
                if (i == contended_lock)
                        continue;
 
-               bo = to_vc4_bo(&exec->bo[i]->base);
+               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);
+                               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);
+                               bo = &exec->bo[contended_lock]->base;
 
                                ww_mutex_unlock(&bo->resv->lock);
                        }
@@ -633,7 +633,7 @@ retry:
         * before we commit the CL to the hardware.
         */
        for (i = 0; i < exec->bo_count; i++) {
-               bo = to_vc4_bo(&exec->bo[i]->base);
+               bo = &exec->bo[i]->base;
 
                ret = reservation_object_reserve_shared(bo->resv, 1);
                if (ret) {
index 5d8c749..918e712 100644 (file)
@@ -22,6 +22,7 @@
  * each CRTC.
  */
 
+#include <drm/drm_atomic_helper.h>
 #include <linux/component.h>
 #include "vc4_drv.h"
 #include "vc4_regs.h"
@@ -102,6 +103,18 @@ int vc4_hvs_debugfs_regs(struct seq_file *m, void *unused)
 
        return 0;
 }
+
+int vc4_hvs_debugfs_underrun(struct seq_file *m, void *data)
+{
+       struct drm_info_node *node = m->private;
+       struct drm_device *dev = node->minor->dev;
+       struct vc4_dev *vc4 = to_vc4_dev(dev);
+       struct drm_printer p = drm_seq_file_printer(m);
+
+       drm_printf(&p, "%d\n", atomic_read(&vc4->underrun));
+
+       return 0;
+}
 #endif
 
 /* The filter kernel is composed of dwords each containing 3 9-bit
@@ -166,6 +179,67 @@ static int vc4_hvs_upload_linear_kernel(struct vc4_hvs *hvs,
        return 0;
 }
 
+void vc4_hvs_mask_underrun(struct drm_device *dev, int channel)
+{
+       struct vc4_dev *vc4 = to_vc4_dev(dev);
+       u32 dispctrl = HVS_READ(SCALER_DISPCTRL);
+
+       dispctrl &= ~SCALER_DISPCTRL_DSPEISLUR(channel);
+
+       HVS_WRITE(SCALER_DISPCTRL, dispctrl);
+}
+
+void vc4_hvs_unmask_underrun(struct drm_device *dev, int channel)
+{
+       struct vc4_dev *vc4 = to_vc4_dev(dev);
+       u32 dispctrl = HVS_READ(SCALER_DISPCTRL);
+
+       dispctrl |= SCALER_DISPCTRL_DSPEISLUR(channel);
+
+       HVS_WRITE(SCALER_DISPSTAT,
+                 SCALER_DISPSTAT_EUFLOW(channel));
+       HVS_WRITE(SCALER_DISPCTRL, dispctrl);
+}
+
+static void vc4_hvs_report_underrun(struct drm_device *dev)
+{
+       struct vc4_dev *vc4 = to_vc4_dev(dev);
+
+       atomic_inc(&vc4->underrun);
+       DRM_DEV_ERROR(dev->dev, "HVS underrun\n");
+}
+
+static irqreturn_t vc4_hvs_irq_handler(int irq, void *data)
+{
+       struct drm_device *dev = data;
+       struct vc4_dev *vc4 = to_vc4_dev(dev);
+       irqreturn_t irqret = IRQ_NONE;
+       int channel;
+       u32 control;
+       u32 status;
+
+       status = HVS_READ(SCALER_DISPSTAT);
+       control = HVS_READ(SCALER_DISPCTRL);
+
+       for (channel = 0; channel < SCALER_CHANNELS_COUNT; channel++) {
+               /* Interrupt masking is not always honored, so check it here. */
+               if (status & SCALER_DISPSTAT_EUFLOW(channel) &&
+                   control & SCALER_DISPCTRL_DSPEISLUR(channel)) {
+                       vc4_hvs_mask_underrun(dev, channel);
+                       vc4_hvs_report_underrun(dev);
+
+                       irqret = IRQ_HANDLED;
+               }
+       }
+
+       /* Clear every per-channel interrupt flag. */
+       HVS_WRITE(SCALER_DISPSTAT, SCALER_DISPSTAT_IRQMASK(0) |
+                                  SCALER_DISPSTAT_IRQMASK(1) |
+                                  SCALER_DISPSTAT_IRQMASK(2));
+
+       return irqret;
+}
+
 static int vc4_hvs_bind(struct device *dev, struct device *master, void *data)
 {
        struct platform_device *pdev = to_platform_device(dev);
@@ -219,15 +293,36 @@ static int vc4_hvs_bind(struct device *dev, struct device *master, void *data)
        dispctrl = HVS_READ(SCALER_DISPCTRL);
 
        dispctrl |= SCALER_DISPCTRL_ENABLE;
+       dispctrl |= SCALER_DISPCTRL_DISPEIRQ(0) |
+                   SCALER_DISPCTRL_DISPEIRQ(1) |
+                   SCALER_DISPCTRL_DISPEIRQ(2);
 
        /* Set DSP3 (PV1) to use HVS channel 2, which would otherwise
         * be unused.
         */
        dispctrl &= ~SCALER_DISPCTRL_DSP3_MUX_MASK;
+       dispctrl &= ~(SCALER_DISPCTRL_DMAEIRQ |
+                     SCALER_DISPCTRL_SLVWREIRQ |
+                     SCALER_DISPCTRL_SLVRDEIRQ |
+                     SCALER_DISPCTRL_DSPEIEOF(0) |
+                     SCALER_DISPCTRL_DSPEIEOF(1) |
+                     SCALER_DISPCTRL_DSPEIEOF(2) |
+                     SCALER_DISPCTRL_DSPEIEOLN(0) |
+                     SCALER_DISPCTRL_DSPEIEOLN(1) |
+                     SCALER_DISPCTRL_DSPEIEOLN(2) |
+                     SCALER_DISPCTRL_DSPEISLUR(0) |
+                     SCALER_DISPCTRL_DSPEISLUR(1) |
+                     SCALER_DISPCTRL_DSPEISLUR(2) |
+                     SCALER_DISPCTRL_SCLEIRQ);
        dispctrl |= VC4_SET_FIELD(2, SCALER_DISPCTRL_DSP3_MUX);
 
        HVS_WRITE(SCALER_DISPCTRL, dispctrl);
 
+       ret = devm_request_irq(dev, platform_get_irq(pdev, 0),
+                              vc4_hvs_irq_handler, 0, "vc4 hvs", drm);
+       if (ret)
+               return ret;
+
        return 0;
 }
 
index 91b8c72..5160cad 100644 (file)
@@ -34,6 +34,18 @@ static struct vc4_ctm_state *to_vc4_ctm_state(struct drm_private_state *priv)
        return container_of(priv, struct vc4_ctm_state, base);
 }
 
+struct vc4_load_tracker_state {
+       struct drm_private_state base;
+       u64 hvs_load;
+       u64 membus_load;
+};
+
+static struct vc4_load_tracker_state *
+to_vc4_load_tracker_state(struct drm_private_state *priv)
+{
+       return container_of(priv, struct vc4_load_tracker_state, base);
+}
+
 static struct vc4_ctm_state *vc4_get_ctm_state(struct drm_atomic_state *state,
                                               struct drm_private_obj *manager)
 {
@@ -138,6 +150,16 @@ vc4_atomic_complete_commit(struct drm_atomic_state *state)
 {
        struct drm_device *dev = state->dev;
        struct vc4_dev *vc4 = to_vc4_dev(dev);
+       struct vc4_crtc *vc4_crtc;
+       int i;
+
+       for (i = 0; i < dev->mode_config.num_crtc; i++) {
+               if (!state->crtcs[i].ptr || !state->crtcs[i].commit)
+                       continue;
+
+               vc4_crtc = to_vc4_crtc(state->crtcs[i].ptr);
+               vc4_hvs_mask_underrun(dev, vc4_crtc->channel);
+       }
 
        drm_atomic_helper_wait_for_fences(dev, state, false);
 
@@ -385,6 +407,85 @@ vc4_ctm_atomic_check(struct drm_device *dev, struct drm_atomic_state *state)
        return 0;
 }
 
+static int vc4_load_tracker_atomic_check(struct drm_atomic_state *state)
+{
+       struct drm_plane_state *old_plane_state, *new_plane_state;
+       struct vc4_dev *vc4 = to_vc4_dev(state->dev);
+       struct vc4_load_tracker_state *load_state;
+       struct drm_private_state *priv_state;
+       struct drm_plane *plane;
+       int i;
+
+       priv_state = drm_atomic_get_private_obj_state(state,
+                                                     &vc4->load_tracker);
+       if (IS_ERR(priv_state))
+               return PTR_ERR(priv_state);
+
+       load_state = to_vc4_load_tracker_state(priv_state);
+       for_each_oldnew_plane_in_state(state, plane, old_plane_state,
+                                      new_plane_state, i) {
+               struct vc4_plane_state *vc4_plane_state;
+
+               if (old_plane_state->fb && old_plane_state->crtc) {
+                       vc4_plane_state = to_vc4_plane_state(old_plane_state);
+                       load_state->membus_load -= vc4_plane_state->membus_load;
+                       load_state->hvs_load -= vc4_plane_state->hvs_load;
+               }
+
+               if (new_plane_state->fb && new_plane_state->crtc) {
+                       vc4_plane_state = to_vc4_plane_state(new_plane_state);
+                       load_state->membus_load += vc4_plane_state->membus_load;
+                       load_state->hvs_load += vc4_plane_state->hvs_load;
+               }
+       }
+
+       /* Don't check the load when the tracker is disabled. */
+       if (!vc4->load_tracker_enabled)
+               return 0;
+
+       /* The absolute limit is 2Gbyte/sec, but let's take a margin to let
+        * the system work when other blocks are accessing the memory.
+        */
+       if (load_state->membus_load > SZ_1G + SZ_512M)
+               return -ENOSPC;
+
+       /* HVS clock is supposed to run @ 250Mhz, let's take a margin and
+        * consider the maximum number of cycles is 240M.
+        */
+       if (load_state->hvs_load > 240000000ULL)
+               return -ENOSPC;
+
+       return 0;
+}
+
+static struct drm_private_state *
+vc4_load_tracker_duplicate_state(struct drm_private_obj *obj)
+{
+       struct vc4_load_tracker_state *state;
+
+       state = kmemdup(obj->state, sizeof(*state), GFP_KERNEL);
+       if (!state)
+               return NULL;
+
+       __drm_atomic_helper_private_obj_duplicate_state(obj, &state->base);
+
+       return &state->base;
+}
+
+static void vc4_load_tracker_destroy_state(struct drm_private_obj *obj,
+                                          struct drm_private_state *state)
+{
+       struct vc4_load_tracker_state *load_state;
+
+       load_state = to_vc4_load_tracker_state(state);
+       kfree(load_state);
+}
+
+static const struct drm_private_state_funcs vc4_load_tracker_state_funcs = {
+       .atomic_duplicate_state = vc4_load_tracker_duplicate_state,
+       .atomic_destroy_state = vc4_load_tracker_destroy_state,
+};
+
 static int
 vc4_atomic_check(struct drm_device *dev, struct drm_atomic_state *state)
 {
@@ -394,7 +495,11 @@ vc4_atomic_check(struct drm_device *dev, struct drm_atomic_state *state)
        if (ret < 0)
                return ret;
 
-       return drm_atomic_helper_check(dev, state);
+       ret = drm_atomic_helper_check(dev, state);
+       if (ret)
+               return ret;
+
+       return vc4_load_tracker_atomic_check(state);
 }
 
 static const struct drm_mode_config_funcs vc4_mode_funcs = {
@@ -407,8 +512,14 @@ int vc4_kms_load(struct drm_device *dev)
 {
        struct vc4_dev *vc4 = to_vc4_dev(dev);
        struct vc4_ctm_state *ctm_state;
+       struct vc4_load_tracker_state *load_state;
        int ret;
 
+       /* Start with the load tracker enabled. Can be disabled through the
+        * debugfs load_tracker file.
+        */
+       vc4->load_tracker_enabled = true;
+
        sema_init(&vc4->async_modeset, 1);
 
        /* Set support for vblank irq fast disable, before drm_vblank_init() */
@@ -436,6 +547,15 @@ int vc4_kms_load(struct drm_device *dev)
        drm_atomic_private_obj_init(dev, &vc4->ctm_manager, &ctm_state->base,
                                    &vc4_ctm_state_funcs);
 
+       load_state = kzalloc(sizeof(*load_state), GFP_KERNEL);
+       if (!load_state) {
+               drm_atomic_private_obj_fini(&vc4->ctm_manager);
+               return -ENOMEM;
+       }
+
+       drm_atomic_private_obj_init(dev, &vc4->load_tracker, &load_state->base,
+                                   &vc4_load_tracker_state_funcs);
+
        drm_mode_config_reset(dev);
 
        drm_kms_helper_poll_init(dev);
index d098337..4d918d3 100644 (file)
@@ -488,6 +488,61 @@ static void vc4_write_scaling_parameters(struct drm_plane_state *state,
        }
 }
 
+static void vc4_plane_calc_load(struct drm_plane_state *state)
+{
+       unsigned int hvs_load_shift, vrefresh, i;
+       struct drm_framebuffer *fb = state->fb;
+       struct vc4_plane_state *vc4_state;
+       struct drm_crtc_state *crtc_state;
+       unsigned int vscale_factor;
+
+       vc4_state = to_vc4_plane_state(state);
+       crtc_state = drm_atomic_get_existing_crtc_state(state->state,
+                                                       state->crtc);
+       vrefresh = drm_mode_vrefresh(&crtc_state->adjusted_mode);
+
+       /* The HVS is able to process 2 pixels/cycle when scaling the source,
+        * 4 pixels/cycle otherwise.
+        * Alpha blending step seems to be pipelined and it's always operating
+        * at 4 pixels/cycle, so the limiting aspect here seems to be the
+        * scaler block.
+        * HVS load is expressed in clk-cycles/sec (AKA Hz).
+        */
+       if (vc4_state->x_scaling[0] != VC4_SCALING_NONE ||
+           vc4_state->x_scaling[1] != VC4_SCALING_NONE ||
+           vc4_state->y_scaling[0] != VC4_SCALING_NONE ||
+           vc4_state->y_scaling[1] != VC4_SCALING_NONE)
+               hvs_load_shift = 1;
+       else
+               hvs_load_shift = 2;
+
+       vc4_state->membus_load = 0;
+       vc4_state->hvs_load = 0;
+       for (i = 0; i < fb->format->num_planes; i++) {
+               /* Even if the bandwidth/plane required for a single frame is
+                *
+                * vc4_state->src_w[i] * vc4_state->src_h[i] * cpp * vrefresh
+                *
+                * when downscaling, we have to read more pixels per line in
+                * the time frame reserved for a single line, so the bandwidth
+                * demand can be punctually higher. To account for that, we
+                * calculate the down-scaling factor and multiply the plane
+                * load by this number. We're likely over-estimating the read
+                * demand, but that's better than under-estimating it.
+                */
+               vscale_factor = DIV_ROUND_UP(vc4_state->src_h[i],
+                                            vc4_state->crtc_h);
+               vc4_state->membus_load += vc4_state->src_w[i] *
+                                         vc4_state->src_h[i] * vscale_factor *
+                                         fb->format->cpp[i];
+               vc4_state->hvs_load += vc4_state->crtc_h * vc4_state->crtc_w;
+       }
+
+       vc4_state->hvs_load *= vrefresh;
+       vc4_state->hvs_load >>= hvs_load_shift;
+       vc4_state->membus_load *= vrefresh;
+}
+
 static int vc4_plane_allocate_lbm(struct drm_plane_state *state)
 {
        struct vc4_dev *vc4 = to_vc4_dev(state->plane->dev);
@@ -875,6 +930,8 @@ static int vc4_plane_mode_set(struct drm_plane *plane,
         */
        vc4_state->dlist_initialized = 1;
 
+       vc4_plane_calc_load(state);
+
        return 0;
 }
 
@@ -1082,7 +1139,7 @@ static int vc4_prepare_fb(struct drm_plane *plane,
 
        bo = to_vc4_bo(&drm_fb_cma_get_gem_obj(state->fb, 0)->base);
 
-       fence = reservation_object_get_excl_rcu(bo->resv);
+       fence = reservation_object_get_excl_rcu(bo->base.base.resv);
        drm_atomic_set_fence_for_plane(state, fence);
 
        if (plane->state->fb == state->fb)
index 9310880..c0c5fad 100644 (file)
 
 #define PV_HACT_ACT                            0x30
 
+#define SCALER_CHANNELS_COUNT                  3
+
 #define SCALER_DISPCTRL                         0x00000000
 /* Global register for clock gating the HVS */
 # define SCALER_DISPCTRL_ENABLE                        BIT(31)
-# define SCALER_DISPCTRL_DSP2EISLUR            BIT(15)
-# define SCALER_DISPCTRL_DSP1EISLUR            BIT(14)
 # define SCALER_DISPCTRL_DSP3_MUX_MASK         VC4_MASK(19, 18)
 # define SCALER_DISPCTRL_DSP3_MUX_SHIFT                18
 
  * SCALER_DISPSTAT_IRQDISP0.  Note that short frame contributions are
  * always enabled.
  */
-# define SCALER_DISPCTRL_DSP0EISLUR            BIT(13)
-# define SCALER_DISPCTRL_DSP2EIEOLN            BIT(12)
-# define SCALER_DISPCTRL_DSP2EIEOF             BIT(11)
-# define SCALER_DISPCTRL_DSP1EIEOLN            BIT(10)
-# define SCALER_DISPCTRL_DSP1EIEOF             BIT(9)
+# define SCALER_DISPCTRL_DSPEISLUR(x)          BIT(13 + (x))
 /* Enables Display 0 end-of-line-N contribution to
  * SCALER_DISPSTAT_IRQDISP0
  */
-# define SCALER_DISPCTRL_DSP0EIEOLN            BIT(8)
+# define SCALER_DISPCTRL_DSPEIEOLN(x)          BIT(8 + ((x) * 2))
 /* Enables Display 0 EOF contribution to SCALER_DISPSTAT_IRQDISP0 */
-# define SCALER_DISPCTRL_DSP0EIEOF             BIT(7)
+# define SCALER_DISPCTRL_DSPEIEOF(x)           BIT(7 + ((x) * 2))
 
 # define SCALER_DISPCTRL_SLVRDEIRQ             BIT(6)
 # define SCALER_DISPCTRL_SLVWREIRQ             BIT(5)
 # define SCALER_DISPCTRL_DMAEIRQ               BIT(4)
-# define SCALER_DISPCTRL_DISP2EIRQ             BIT(3)
-# define SCALER_DISPCTRL_DISP1EIRQ             BIT(2)
 /* Enables interrupt generation on the enabled EOF/EOLN/EISLUR
  * bits and short frames..
  */
-# define SCALER_DISPCTRL_DISP0EIRQ             BIT(1)
+# define SCALER_DISPCTRL_DISPEIRQ(x)           BIT(1 + (x))
 /* Enables interrupt generation on scaler profiler interrupt. */
 # define SCALER_DISPCTRL_SCLEIRQ               BIT(0)
 
 #define SCALER_DISPSTAT                         0x00000004
-# define SCALER_DISPSTAT_COBLOW2               BIT(29)
-# define SCALER_DISPSTAT_EOLN2                 BIT(28)
-# define SCALER_DISPSTAT_ESFRAME2              BIT(27)
-# define SCALER_DISPSTAT_ESLINE2               BIT(26)
-# define SCALER_DISPSTAT_EUFLOW2               BIT(25)
-# define SCALER_DISPSTAT_EOF2                  BIT(24)
-
-# define SCALER_DISPSTAT_COBLOW1               BIT(21)
-# define SCALER_DISPSTAT_EOLN1                 BIT(20)
-# define SCALER_DISPSTAT_ESFRAME1              BIT(19)
-# define SCALER_DISPSTAT_ESLINE1               BIT(18)
-# define SCALER_DISPSTAT_EUFLOW1               BIT(17)
-# define SCALER_DISPSTAT_EOF1                  BIT(16)
-
 # define SCALER_DISPSTAT_RESP_MASK             VC4_MASK(15, 14)
 # define SCALER_DISPSTAT_RESP_SHIFT            14
 # define SCALER_DISPSTAT_RESP_OKAY             0
 # define SCALER_DISPSTAT_RESP_SLVERR           2
 # define SCALER_DISPSTAT_RESP_DECERR           3
 
-# define SCALER_DISPSTAT_COBLOW0               BIT(13)
+# define SCALER_DISPSTAT_COBLOW(x)             BIT(13 + ((x) * 8))
 /* Set when the DISPEOLN line is done compositing. */
-# define SCALER_DISPSTAT_EOLN0                 BIT(12)
+# define SCALER_DISPSTAT_EOLN(x)               BIT(12 + ((x) * 8))
 /* Set when VSTART is seen but there are still pixels in the current
  * output line.
  */
-# define SCALER_DISPSTAT_ESFRAME0              BIT(11)
+# define SCALER_DISPSTAT_ESFRAME(x)            BIT(11 + ((x) * 8))
 /* Set when HSTART is seen but there are still pixels in the current
  * output line.
  */
-# define SCALER_DISPSTAT_ESLINE0               BIT(10)
+# define SCALER_DISPSTAT_ESLINE(x)             BIT(10 + ((x) * 8))
 /* Set when the the downstream tries to read from the display FIFO
  * while it's empty.
  */
-# define SCALER_DISPSTAT_EUFLOW0               BIT(9)
+# define SCALER_DISPSTAT_EUFLOW(x)             BIT(9 + ((x) * 8))
 /* Set when the display mode changes from RUN to EOF */
-# define SCALER_DISPSTAT_EOF0                  BIT(8)
+# define SCALER_DISPSTAT_EOF(x)                        BIT(8 + ((x) * 8))
+
+# define SCALER_DISPSTAT_IRQMASK(x)            VC4_MASK(13 + ((x) * 8), \
+                                                        8 + ((x) * 8))
 
 /* Set on AXI invalid DMA ID error. */
 # define SCALER_DISPSTAT_DMA_ERROR             BIT(7)
  * SCALER_DISPSTAT_RESP_ERROR is not SCALER_DISPSTAT_RESP_OKAY.
  */
 # define SCALER_DISPSTAT_IRQDMA                        BIT(4)
-# define SCALER_DISPSTAT_IRQDISP2              BIT(3)
-# define SCALER_DISPSTAT_IRQDISP1              BIT(2)
 /* Set when any of the EOF/EOLN/ESFRAME/ESLINE bits are set and their
  * corresponding interrupt bit is enabled in DISPCTRL.
  */
-# define SCALER_DISPSTAT_IRQDISP0              BIT(1)
+# define SCALER_DISPSTAT_IRQDISP(x)            BIT(1 + (x))
 /* On read, the profiler interrupt.  On write, clear *all* interrupt bits. */
 # define SCALER_DISPSTAT_IRQSCL                        BIT(0)
 
index 5dabd91..cc2888d 100644 (file)
@@ -249,7 +249,6 @@ static int vc4_txp_connector_atomic_check(struct drm_connector *conn,
                                        struct drm_connector_state *conn_state)
 {
        struct drm_crtc_state *crtc_state;
-       struct drm_gem_cma_object *gem;
        struct drm_framebuffer *fb;
        int i;
 
@@ -275,8 +274,6 @@ static int vc4_txp_connector_atomic_check(struct drm_connector *conn,
        if (i == ARRAY_SIZE(drm_fmts))
                return -EINVAL;
 
-       gem = drm_fb_cma_get_gem_obj(fb, 0);
-
        /* Pitch must be aligned on 16 bytes. */
        if (fb->pitches[0] & GENMASK(3, 0))
                return -EINVAL;
index b996ac1..7c28931 100644 (file)
@@ -205,10 +205,10 @@ static struct drm_driver driver = {
 #if defined(CONFIG_DEBUG_FS)
        .debugfs_init = virtio_gpu_debugfs_init,
 #endif
+       .prime_handle_to_fd = drm_gem_prime_handle_to_fd,
        .gem_prime_export = drm_gem_prime_export,
        .gem_prime_import = drm_gem_prime_import,
-       .gem_prime_pin = virtgpu_gem_prime_pin,
-       .gem_prime_unpin = virtgpu_gem_prime_unpin,
+       .gem_prime_get_sg_table = virtgpu_gem_prime_get_sg_table,
        .gem_prime_vmap = virtgpu_gem_prime_vmap,
        .gem_prime_vunmap = virtgpu_gem_prime_vunmap,
        .gem_prime_mmap = virtgpu_gem_prime_mmap,
index 3238fdf..86a264c 100644 (file)
@@ -352,8 +352,7 @@ void virtio_gpu_object_free_sg_table(struct virtio_gpu_object *bo);
 int virtio_gpu_object_wait(struct virtio_gpu_object *bo, bool no_wait);
 
 /* virtgpu_prime.c */
-int virtgpu_gem_prime_pin(struct drm_gem_object *obj);
-void virtgpu_gem_prime_unpin(struct drm_gem_object *obj);
+struct sg_table *virtgpu_gem_prime_get_sg_table(struct drm_gem_object *obj);
 void *virtgpu_gem_prime_vmap(struct drm_gem_object *obj);
 void virtgpu_gem_prime_vunmap(struct drm_gem_object *obj, void *vaddr);
 int virtgpu_gem_prime_mmap(struct drm_gem_object *obj,
index c59ec34..22ef151 100644 (file)
  * device that might share buffers with virtgpu
  */
 
-int virtgpu_gem_prime_pin(struct drm_gem_object *obj)
+struct sg_table *virtgpu_gem_prime_get_sg_table(struct drm_gem_object *obj)
 {
-       WARN_ONCE(1, "not implemented");
-       return -ENODEV;
-}
+       struct virtio_gpu_object *bo = gem_to_virtio_gpu_obj(obj);
 
-void virtgpu_gem_prime_unpin(struct drm_gem_object *obj)
-{
-       WARN_ONCE(1, "not implemented");
+       if (!bo->tbo.ttm->pages || !bo->tbo.ttm->num_pages)
+               /* should not happen */
+               return ERR_PTR(-EINVAL);
+
+       return drm_prime_pages_to_sg(bo->tbo.ttm->pages,
+                                    bo->tbo.ttm->num_pages);
 }
 
 void *virtgpu_gem_prime_vmap(struct drm_gem_object *obj)
@@ -56,7 +57,10 @@ void virtgpu_gem_prime_vunmap(struct drm_gem_object *obj, void *vaddr)
 }
 
 int virtgpu_gem_prime_mmap(struct drm_gem_object *obj,
-                      struct vm_area_struct *area)
+                          struct vm_area_struct *vma)
 {
-       return -ENODEV;
+       struct virtio_gpu_object *bo = gem_to_virtio_gpu_obj(obj);
+
+       bo->gem_base.vma_node.vm_node.start = bo->tbo.vma_node.vm_node.start;
+       return drm_gem_prime_mmap(obj, vma);
 }
index 3e78a83..84aa4d6 100644 (file)
@@ -582,6 +582,7 @@ static void xen_drm_drv_fini(struct xen_drm_front_info *front_info)
 
        drm_kms_helper_poll_fini(dev);
        drm_dev_unplug(dev);
+       drm_dev_put(dev);
 
        front_info->drm_info = NULL;
 
index c0901b9..dee9e92 100644 (file)
@@ -96,8 +96,6 @@ source "drivers/staging/greybus/Kconfig"
 
 source "drivers/staging/vc04_services/Kconfig"
 
-source "drivers/staging/vboxvideo/Kconfig"
-
 source "drivers/staging/pi433/Kconfig"
 
 source "drivers/staging/mt7621-pci/Kconfig"
index 57c6bce..d344078 100644 (file)
@@ -38,7 +38,6 @@ obj-$(CONFIG_MOST)            += most/
 obj-$(CONFIG_KS7010)           += ks7010/
 obj-$(CONFIG_GREYBUS)          += greybus/
 obj-$(CONFIG_BCM2835_VCHIQ)    += vc04_services/
-obj-$(CONFIG_DRM_VBOXVIDEO)    += vboxvideo/
 obj-$(CONFIG_PI433)            += pi433/
 obj-$(CONFIG_PCI_MT7621)       += mt7621-pci/
 obj-$(CONFIG_PCI_MT7621_PHY)   += mt7621-pci-phy/
diff --git a/drivers/staging/vboxvideo/TODO b/drivers/staging/vboxvideo/TODO
deleted file mode 100644 (file)
index 7f97c47..0000000
+++ /dev/null
@@ -1,10 +0,0 @@
-TODO:
--Get a full review from the drm-maintainers on dri-devel done on this driver
--Drop all the logic around initial_mode_queried, the master_set and
- master_drop callbacks and everything related to this. kms clients can handle
- hotplugs.
--Extend this TODO with the results of that review
-
-Please send any patches to Greg Kroah-Hartman <gregkh@linuxfoundation.org>,
-Hans de Goede <hdegoede@redhat.com>, Michael Thayer <michael.thayer@oracle.com>
-and dri-devel@lists.freedesktop.org .
index 97fc498..987ff16 100644 (file)
@@ -38,7 +38,7 @@
 void drm_clflush_pages(struct page *pages[], unsigned long num_pages);
 void drm_clflush_sg(struct sg_table *st);
 void drm_clflush_virt_range(void *addr, unsigned long length);
-u64 drm_get_max_iomem(void);
+bool drm_need_swiotlb(int dma_bits);
 
 
 static inline bool drm_arch_can_wc_memory(void)
index c5fbc12..bb3bd8e 100644 (file)
@@ -253,6 +253,34 @@ enum drm_panel_orientation {
        DRM_MODE_PANEL_ORIENTATION_RIGHT_UP,
 };
 
+/*
+ * This is a consolidated colorimetry list supported by HDMI and
+ * DP protocol standard. The respective connectors will register
+ * a property with the subset of this list (supported by that
+ * respective protocol). Userspace will set the colorspace through
+ * a colorspace property which will be created and exposed to
+ * userspace.
+ */
+
+/* For Default case, driver will set the colorspace */
+#define DRM_MODE_COLORIMETRY_DEFAULT                   0
+/* CEA 861 Normal Colorimetry options */
+#define DRM_MODE_COLORIMETRY_NO_DATA                   0
+#define DRM_MODE_COLORIMETRY_SMPTE_170M_YCC            1
+#define DRM_MODE_COLORIMETRY_BT709_YCC                 2
+/* CEA 861 Extended Colorimetry Options */
+#define DRM_MODE_COLORIMETRY_XVYCC_601                 3
+#define DRM_MODE_COLORIMETRY_XVYCC_709                 4
+#define DRM_MODE_COLORIMETRY_SYCC_601                  5
+#define DRM_MODE_COLORIMETRY_OPYCC_601                 6
+#define DRM_MODE_COLORIMETRY_OPRGB                     7
+#define DRM_MODE_COLORIMETRY_BT2020_CYCC               8
+#define DRM_MODE_COLORIMETRY_BT2020_RGB                        9
+#define DRM_MODE_COLORIMETRY_BT2020_YCC                        10
+/* Additional Colorimetry extension added as part of CTA 861.G */
+#define DRM_MODE_COLORIMETRY_DCI_P3_RGB_D65            11
+#define DRM_MODE_COLORIMETRY_DCI_P3_RGB_THEATER                12
+
 /**
  * enum drm_bus_flags - bus_flags info for &drm_display_info
  *
@@ -551,6 +579,13 @@ struct drm_connector_state {
        unsigned int content_protection;
 
        /**
+        * @colorspace: State variable for Connector property to request
+        * colorspace change on Sink. This is most commonly used to switch
+        * to wider color gamuts like BT2020.
+        */
+       u32 colorspace;
+
+       /**
         * @writeback_job: Writeback job for writeback connectors
         *
         * Holds the framebuffer and out-fence for a writeback connector. As
@@ -1043,6 +1078,12 @@ struct drm_connector {
        struct drm_property *content_protection_property;
 
        /**
+        * @colorspace_property: Connector property to set the suitable
+        * colorspace supported by the sink.
+        */
+       struct drm_property *colorspace_property;
+
+       /**
         * @path_blob_ptr:
         *
         * DRM blob property data for the DP MST path property. This should only
@@ -1317,6 +1358,7 @@ int drm_connector_attach_vrr_capable_property(
 int drm_connector_attach_content_protection_property(
                struct drm_connector *connector);
 int drm_mode_create_aspect_ratio_property(struct drm_device *dev);
+int drm_mode_create_colorspace_property(struct drm_connector *connector);
 int drm_mode_create_content_type_property(struct drm_device *dev);
 void drm_hdmi_avi_infoframe_content_type(struct hdmi_avi_infoframe *frame,
                                         const struct drm_connector_state *conn_state);
index 85abd3f..f7c3022 100644 (file)
@@ -472,7 +472,7 @@ struct drm_crtc_funcs {
        /**
         * @destroy:
         *
-        * Clean up plane resources. This is only called at driver unload time
+        * Clean up CRTC resources. This is only called at driver unload time
         * through drm_mode_config_cleanup() since a CRTC cannot be hotplugged
         * in DRM.
         */
index 570f9d0..3224abb 100644 (file)
@@ -718,6 +718,9 @@ extern unsigned int drm_debug;
 int drm_dev_init(struct drm_device *dev,
                 struct drm_driver *driver,
                 struct device *parent);
+int devm_drm_dev_init(struct device *parent,
+                     struct drm_device *dev,
+                     struct drm_driver *driver);
 void drm_dev_fini(struct drm_device *dev);
 
 struct drm_device *drm_dev_alloc(struct drm_driver *driver,
index 9c26f08..887954c 100644 (file)
@@ -101,9 +101,9 @@ struct drm_dsc_config {
         */
        u16 slice_height;
        /**
-        * @enable422: True for 4_2_2 sampling, false for 4_4_4 sampling
+        * @simple_422: True if simple 4_2_2 mode is enabled else False
         */
-       bool enable422;
+       bool simple_422;
        /**
         * @pic_width: Width of the input display frame in pixels
         */
@@ -601,8 +601,9 @@ struct drm_dsc_pps_infoframe {
        struct drm_dsc_picture_parameter_set pps_payload;
 } __packed;
 
-void drm_dsc_dp_pps_header_init(struct drm_dsc_pps_infoframe *pps_sdp);
-void drm_dsc_pps_infoframe_pack(struct drm_dsc_pps_infoframe *pps_sdp,
+void drm_dsc_dp_pps_header_init(struct dp_sdp_header *pps_header);
+void drm_dsc_pps_payload_pack(struct drm_dsc_picture_parameter_set *pps_sdp,
                                const struct drm_dsc_config *dsc_cfg);
+int drm_dsc_compute_rc_parameters(struct drm_dsc_config *vdsc_cfg);
 
 #endif /* _DRM_DSC_H_ */
index 8dc1a08..9d3b5b9 100644 (file)
@@ -331,6 +331,7 @@ struct cea_sad {
 
 struct drm_encoder;
 struct drm_connector;
+struct drm_connector_state;
 struct drm_display_mode;
 
 int drm_edid_to_sad(struct edid *edid, struct cea_sad **sads);
@@ -358,6 +359,11 @@ int
 drm_hdmi_vendor_infoframe_from_display_mode(struct hdmi_vendor_infoframe *frame,
                                            struct drm_connector *connector,
                                            const struct drm_display_mode *mode);
+
+void
+drm_hdmi_avi_infoframe_colorspace(struct hdmi_avi_infoframe *frame,
+                                 const struct drm_connector_state *conn_state);
+
 void
 drm_hdmi_avi_infoframe_quant_range(struct hdmi_avi_infoframe *frame,
                                   struct drm_connector *connector,
index c957274..2955aaa 100644 (file)
@@ -35,6 +35,7 @@
  */
 
 #include <linux/kref.h>
+#include <linux/reservation.h>
 
 #include <drm/drm_vma_manager.h>
 
@@ -263,6 +264,24 @@ struct drm_gem_object {
        struct dma_buf_attachment *import_attach;
 
        /**
+        * @resv:
+        *
+        * Pointer to reservation object associated with the this GEM object.
+        *
+        * Normally (@resv == &@_resv) except for imported GEM objects.
+        */
+       struct reservation_object *resv;
+
+       /**
+        * @_resv:
+        *
+        * A reservation object for this GEM object.
+        *
+        * This is unused for imported GEM objects.
+        */
+       struct reservation_object _resv;
+
+       /**
         * @funcs:
         *
         * Optional GEM object functions. If this is set, it will be used instead of the
@@ -363,6 +382,12 @@ void drm_gem_put_pages(struct drm_gem_object *obj, struct page **pages,
                bool dirty, bool accessed);
 
 struct drm_gem_object *drm_gem_object_lookup(struct drm_file *filp, u32 handle);
+long drm_gem_reservation_object_wait(struct drm_file *filep, u32 handle,
+                                   bool wait_all, unsigned long timeout);
+int drm_gem_lock_reservations(struct drm_gem_object **objs, int count,
+                             struct ww_acquire_ctx *acquire_ctx);
+void drm_gem_unlock_reservations(struct drm_gem_object **objs, int count,
+                                struct ww_acquire_ctx *acquire_ctx);
 int drm_gem_dumb_map_offset(struct drm_file *file, struct drm_device *dev,
                            u32 handle, u64 *offset);
 int drm_gem_dumb_destroy(struct drm_file *file,
diff --git a/include/drm/drm_gem_shmem_helper.h b/include/drm/drm_gem_shmem_helper.h
new file mode 100644 (file)
index 0000000..038b6d3
--- /dev/null
@@ -0,0 +1,159 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+
+#ifndef __DRM_GEM_SHMEM_HELPER_H__
+#define __DRM_GEM_SHMEM_HELPER_H__
+
+#include <linux/fs.h>
+#include <linux/mm.h>
+#include <linux/mutex.h>
+
+#include <drm/drm_file.h>
+#include <drm/drm_gem.h>
+#include <drm/drm_ioctl.h>
+#include <drm/drm_prime.h>
+
+struct dma_buf_attachment;
+struct drm_mode_create_dumb;
+struct drm_printer;
+struct sg_table;
+
+/**
+ * struct drm_gem_shmem_object - GEM object backed by shmem
+ */
+struct drm_gem_shmem_object {
+       /**
+        * @base: Base GEM object
+        */
+       struct drm_gem_object base;
+
+       /**
+        * @pages_lock: Protects the page table and use count
+        */
+       struct mutex pages_lock;
+
+       /**
+        * @pages: Page table
+        */
+       struct page **pages;
+
+       /**
+        * @pages_use_count:
+        *
+        * Reference count on the pages table.
+        * The pages are put when the count reaches zero.
+        */
+       unsigned int pages_use_count;
+
+       /**
+        * @pages_mark_dirty_on_put:
+        *
+        * Mark pages as dirty when they are put.
+        */
+       unsigned int pages_mark_dirty_on_put    : 1;
+
+       /**
+        * @pages_mark_accessed_on_put:
+        *
+        * Mark pages as accessed when they are put.
+        */
+       unsigned int pages_mark_accessed_on_put : 1;
+
+       /**
+        * @sgt: Scatter/gather table for imported PRIME buffers
+        */
+       struct sg_table *sgt;
+
+       /**
+        * @vmap_lock: Protects the vmap address and use count
+        */
+       struct mutex vmap_lock;
+
+       /**
+        * @vaddr: Kernel virtual address of the backing memory
+        */
+       void *vaddr;
+
+       /**
+        * @vmap_use_count:
+        *
+        * Reference count on the virtual address.
+        * The address are un-mapped when the count reaches zero.
+        */
+       unsigned int vmap_use_count;
+};
+
+#define to_drm_gem_shmem_obj(obj) \
+       container_of(obj, struct drm_gem_shmem_object, base)
+
+/**
+ * DEFINE_DRM_GEM_SHMEM_FOPS() - Macro to generate file operations for shmem drivers
+ * @name: name for the generated structure
+ *
+ * This macro autogenerates a suitable &struct file_operations for shmem based
+ * drivers, which can be assigned to &drm_driver.fops. Note that this structure
+ * cannot be shared between drivers, because it contains a reference to the
+ * current module using THIS_MODULE.
+ *
+ * Note that the declaration is already marked as static - if you need a
+ * non-static version of this you're probably doing it wrong and will break the
+ * THIS_MODULE reference by accident.
+ */
+#define DEFINE_DRM_GEM_SHMEM_FOPS(name) \
+       static const struct file_operations name = {\
+               .owner          = THIS_MODULE,\
+               .open           = drm_open,\
+               .release        = drm_release,\
+               .unlocked_ioctl = drm_ioctl,\
+               .compat_ioctl   = drm_compat_ioctl,\
+               .poll           = drm_poll,\
+               .read           = drm_read,\
+               .llseek         = noop_llseek,\
+               .mmap           = drm_gem_shmem_mmap, \
+       }
+
+struct drm_gem_shmem_object *drm_gem_shmem_create(struct drm_device *dev, size_t size);
+void drm_gem_shmem_free_object(struct drm_gem_object *obj);
+
+int drm_gem_shmem_get_pages(struct drm_gem_shmem_object *shmem);
+void drm_gem_shmem_put_pages(struct drm_gem_shmem_object *shmem);
+int drm_gem_shmem_pin(struct drm_gem_object *obj);
+void drm_gem_shmem_unpin(struct drm_gem_object *obj);
+void *drm_gem_shmem_vmap(struct drm_gem_object *obj);
+void drm_gem_shmem_vunmap(struct drm_gem_object *obj, void *vaddr);
+
+struct drm_gem_shmem_object *
+drm_gem_shmem_create_with_handle(struct drm_file *file_priv,
+                                struct drm_device *dev, size_t size,
+                                uint32_t *handle);
+int drm_gem_shmem_dumb_create(struct drm_file *file, struct drm_device *dev,
+                             struct drm_mode_create_dumb *args);
+
+int drm_gem_shmem_mmap(struct file *filp, struct vm_area_struct *vma);
+
+extern const struct vm_operations_struct drm_gem_shmem_vm_ops;
+
+void drm_gem_shmem_print_info(struct drm_printer *p, unsigned int indent,
+                             const struct drm_gem_object *obj);
+
+struct sg_table *drm_gem_shmem_get_sg_table(struct drm_gem_object *obj);
+struct drm_gem_object *
+drm_gem_shmem_prime_import_sg_table(struct drm_device *dev,
+                                   struct dma_buf_attachment *attach,
+                                   struct sg_table *sgt);
+
+struct sg_table *drm_gem_shmem_get_pages_sgt(struct drm_gem_object *obj);
+
+/**
+ * DRM_GEM_SHMEM_DRIVER_OPS - Default shmem GEM operations
+ *
+ * This macro provides a shortcut for setting the shmem GEM operations in
+ * the &drm_driver structure.
+ */
+#define DRM_GEM_SHMEM_DRIVER_OPS \
+       .prime_handle_to_fd     = drm_gem_prime_handle_to_fd, \
+       .prime_fd_to_handle     = drm_gem_prime_fd_to_handle, \
+       .gem_prime_import_sg_table = drm_gem_shmem_prime_import_sg_table, \
+       .gem_prime_mmap         = drm_gem_prime_mmap, \
+       .dumb_create            = drm_gem_shmem_dumb_create
+
+#endif /* __DRM_GEM_SHMEM_HELPER_H__ */
index be4fed9..083f167 100644 (file)
@@ -138,6 +138,23 @@ enum drm_mode_status {
        .vsync_start = (vss), .vsync_end = (vse), .vtotal = (vt), \
        .vscan = (vs), .flags = (f)
 
+/**
+ * DRM_SIMPLE_MODE - Simple display mode
+ * @hd: Horizontal resolution, width
+ * @vd: Vertical resolution, height
+ * @hd_mm: Display width in millimeters
+ * @vd_mm: Display height in millimeters
+ *
+ * This macro initializes a &drm_display_mode that only contains info about
+ * resolution and physical size.
+ */
+#define DRM_SIMPLE_MODE(hd, vd, hd_mm, vd_mm) \
+       .type = DRM_MODE_TYPE_DRIVER, .clock = 1 /* pass validation */, \
+       .hdisplay = (hd), .hsync_start = (hd), .hsync_end = (hd), \
+       .htotal = (hd), .vdisplay = (vd), .vsync_start = (vd), \
+       .vsync_end = (vd), .vtotal = (vd), .width_mm = (hd_mm), \
+       .height_mm = (vd_mm)
+
 #define CRTC_INTERLACE_HALVE_V (1 << 0) /* halve V values for interlacing */
 #define CRTC_STEREO_DOUBLE     (1 << 1) /* adjust timings for stereo modes */
 #define CRTC_NO_DBLSCAN                (1 << 2) /* don't adjust doublescan */
index a803988..7077574 100644 (file)
 #ifndef __DRM_UTILS_H__
 #define __DRM_UTILS_H__
 
+#include <linux/types.h>
+
 int drm_get_panel_orientation_quirk(int width, int height);
 
+signed long drm_timeout_abs_to_jiffies(int64_t timeout_nsec);
+
 #endif
index f4ec283..af203b3 100644 (file)
@@ -12,7 +12,9 @@
 #ifndef __LINUX_MIPI_DBI_H
 #define __LINUX_MIPI_DBI_H
 
-#include <drm/tinydrm/tinydrm.h>
+#include <linux/mutex.h>
+#include <drm/drm_device.h>
+#include <drm/drm_simple_kms_helper.h>
 
 struct drm_rect;
 struct spi_device;
@@ -21,7 +23,6 @@ struct regulator;
 
 /**
  * struct mipi_dbi - MIPI DBI controller
- * @tinydrm: tinydrm base
  * @spi: SPI device
  * @enabled: Pipeline is enabled
  * @cmdlock: Command lock
@@ -39,11 +40,20 @@ struct regulator;
  * @regulator: power regulator (optional)
  */
 struct mipi_dbi {
-       struct tinydrm_device tinydrm;
+       /**
+        * @drm: DRM device
+        */
+       struct drm_device drm;
+
+       /**
+        * @pipe: Display pipe structure
+        */
+       struct drm_simple_display_pipe pipe;
+
        struct spi_device *spi;
        bool enabled;
        struct mutex cmdlock;
-       int (*command)(struct mipi_dbi *mipi, u8 cmd, u8 *param, size_t num);
+       int (*command)(struct mipi_dbi *mipi, u8 *cmd, u8 *param, size_t num);
        const u8 *read_commands;
        struct gpio_desc *dc;
        u16 *tx_buf;
@@ -56,18 +66,17 @@ struct mipi_dbi {
        struct regulator *regulator;
 };
 
-static inline struct mipi_dbi *
-mipi_dbi_from_tinydrm(struct tinydrm_device *tdev)
+static inline struct mipi_dbi *drm_to_mipi_dbi(struct drm_device *drm)
 {
-       return container_of(tdev, struct mipi_dbi, tinydrm);
+       return container_of(drm, struct mipi_dbi, drm);
 }
 
 int mipi_dbi_spi_init(struct spi_device *spi, struct mipi_dbi *mipi,
                      struct gpio_desc *dc);
-int mipi_dbi_init(struct device *dev, struct mipi_dbi *mipi,
-                 const struct drm_simple_display_pipe_funcs *pipe_funcs,
-                 struct drm_driver *driver,
+int mipi_dbi_init(struct mipi_dbi *mipi,
+                 const struct drm_simple_display_pipe_funcs *funcs,
                  const struct drm_display_mode *mode, unsigned int rotation);
+void mipi_dbi_release(struct drm_device *drm);
 void mipi_dbi_pipe_update(struct drm_simple_display_pipe *pipe,
                          struct drm_plane_state *old_state);
 void mipi_dbi_enable_flush(struct mipi_dbi *mipi,
@@ -82,6 +91,7 @@ u32 mipi_dbi_spi_cmd_max_speed(struct spi_device *spi, size_t len);
 
 int mipi_dbi_command_read(struct mipi_dbi *mipi, u8 cmd, u8 *val);
 int mipi_dbi_command_buf(struct mipi_dbi *mipi, u8 cmd, u8 *data, size_t len);
+int mipi_dbi_command_stackbuf(struct mipi_dbi *mipi, u8 cmd, u8 *data, size_t len);
 int mipi_dbi_buf_copy(void *dst, struct drm_framebuffer *fb,
                      struct drm_rect *clip, bool swap);
 /**
@@ -99,7 +109,7 @@ int mipi_dbi_buf_copy(void *dst, struct drm_framebuffer *fb,
 #define mipi_dbi_command(mipi, cmd, seq...) \
 ({ \
        u8 d[] = { seq }; \
-       mipi_dbi_command_buf(mipi, cmd, d, ARRAY_SIZE(d)); \
+       mipi_dbi_command_stackbuf(mipi, cmd, d, ARRAY_SIZE(d)); \
 })
 
 #ifdef CONFIG_DEBUG_FS
index f0d5987..ae4a6ab 100644 (file)
 #define __LINUX_TINYDRM_HELPERS_H
 
 struct backlight_device;
+struct drm_device;
+struct drm_display_mode;
 struct drm_framebuffer;
 struct drm_rect;
+struct drm_simple_display_pipe;
+struct drm_simple_display_pipe_funcs;
 struct spi_transfer;
 struct spi_message;
 struct spi_device;
@@ -33,6 +37,15 @@ static inline bool tinydrm_machine_little_endian(void)
 #endif
 }
 
+int tinydrm_display_pipe_init(struct drm_device *drm,
+                             struct drm_simple_display_pipe *pipe,
+                             const struct drm_simple_display_pipe_funcs *funcs,
+                             int connector_type,
+                             const uint32_t *formats,
+                             unsigned int format_count,
+                             const struct drm_display_mode *mode,
+                             unsigned int rotation);
+
 void tinydrm_memcpy(void *dst, void *vaddr, struct drm_framebuffer *fb,
                    struct drm_rect *clip);
 void tinydrm_swab16(u16 *dst, void *vaddr, struct drm_framebuffer *fb,
diff --git a/include/drm/tinydrm/tinydrm.h b/include/drm/tinydrm/tinydrm.h
deleted file mode 100644 (file)
index 5621688..0000000
+++ /dev/null
@@ -1,75 +0,0 @@
-/*
- * Copyright (C) 2016 Noralf Trønnes
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- */
-
-#ifndef __LINUX_TINYDRM_H
-#define __LINUX_TINYDRM_H
-
-#include <drm/drm_simple_kms_helper.h>
-
-struct drm_driver;
-
-/**
- * struct tinydrm_device - tinydrm device
- */
-struct tinydrm_device {
-       /**
-        * @drm: DRM device
-        */
-       struct drm_device *drm;
-
-       /**
-        * @pipe: Display pipe structure
-        */
-       struct drm_simple_display_pipe pipe;
-};
-
-static inline struct tinydrm_device *
-pipe_to_tinydrm(struct drm_simple_display_pipe *pipe)
-{
-       return container_of(pipe, struct tinydrm_device, pipe);
-}
-
-/**
- * TINYDRM_MODE - tinydrm display mode
- * @hd: Horizontal resolution, width
- * @vd: Vertical resolution, height
- * @hd_mm: Display width in millimeters
- * @vd_mm: Display height in millimeters
- *
- * This macro creates a &drm_display_mode for use with tinydrm.
- */
-#define TINYDRM_MODE(hd, vd, hd_mm, vd_mm) \
-       .hdisplay = (hd), \
-       .hsync_start = (hd), \
-       .hsync_end = (hd), \
-       .htotal = (hd), \
-       .vdisplay = (vd), \
-       .vsync_start = (vd), \
-       .vsync_end = (vd), \
-       .vtotal = (vd), \
-       .width_mm = (hd_mm), \
-       .height_mm = (vd_mm), \
-       .type = DRM_MODE_TYPE_DRIVER, \
-       .clock = 1 /* pass validation */
-
-int devm_tinydrm_init(struct device *parent, struct tinydrm_device *tdev,
-                     struct drm_driver *driver);
-int devm_tinydrm_register(struct tinydrm_device *tdev);
-void tinydrm_shutdown(struct tinydrm_device *tdev);
-
-int
-tinydrm_display_pipe_init(struct tinydrm_device *tdev,
-                         const struct drm_simple_display_pipe_funcs *funcs,
-                         int connector_type,
-                         const uint32_t *formats,
-                         unsigned int format_count,
-                         const struct drm_display_mode *mode,
-                         unsigned int rotation);
-
-#endif /* __LINUX_TINYDRM_H */
index 2f0ffca..ee75076 100644 (file)
@@ -228,7 +228,8 @@ reservation_object_unlock(struct reservation_object *obj)
  * @obj: the reservation object
  *
  * Returns the exclusive fence (if any).  Does NOT take a
- * reference.  The obj->lock must be held.
+ * reference. Writers must hold obj->lock, readers may only
+ * hold a RCU read side lock.
  *
  * RETURNS
  * The exclusive fence or NULL
index bab2029..3feeaa3 100644 (file)
@@ -144,6 +144,17 @@ extern "C" {
 #define DRM_FORMAT_RGBA1010102 fourcc_code('R', 'A', '3', '0') /* [31:0] R:G:B:A 10:10:10:2 little endian */
 #define DRM_FORMAT_BGRA1010102 fourcc_code('B', 'A', '3', '0') /* [31:0] B:G:R:A 10:10:10:2 little endian */
 
+/*
+ * Floating point 64bpp RGB
+ * IEEE 754-2008 binary16 half-precision float
+ * [15:0] sign:exponent:mantissa 1:5:10
+ */
+#define DRM_FORMAT_XRGB16161616F fourcc_code('X', 'R', '4', 'H') /* [63:0] x:R:G:B 16:16:16:16 little endian */
+#define DRM_FORMAT_XBGR16161616F fourcc_code('X', 'B', '4', 'H') /* [63:0] x:B:G:R 16:16:16:16 little endian */
+
+#define DRM_FORMAT_ARGB16161616F fourcc_code('A', 'R', '4', 'H') /* [63:0] A:R:G:B 16:16:16:16 little endian */
+#define DRM_FORMAT_ABGR16161616F fourcc_code('A', 'B', '4', 'H') /* [63:0] A:B:G:R 16:16:16:16 little endian */
+
 /* packed YCbCr */
 #define DRM_FORMAT_YUYV                fourcc_code('Y', 'U', 'Y', 'V') /* [31:0] Cr0:Y1:Cb0:Y0 8:8:8:8 little endian */
 #define DRM_FORMAT_YVYU                fourcc_code('Y', 'V', 'Y', 'U') /* [31:0] Cb0:Y1:Cr0:Y0 8:8:8:8 little endian */
@@ -151,7 +162,29 @@ extern "C" {
 #define DRM_FORMAT_VYUY                fourcc_code('V', 'Y', 'U', 'Y') /* [31:0] Y1:Cb0:Y0:Cr0 8:8:8:8 little endian */
 
 #define DRM_FORMAT_AYUV                fourcc_code('A', 'Y', 'U', 'V') /* [31:0] A:Y:Cb:Cr 8:8:8:8 little endian */
-#define DRM_FORMAT_XYUV8888            fourcc_code('X', 'Y', 'U', 'V') /* [31:0] X:Y:Cb:Cr 8:8:8:8 little endian */
+#define DRM_FORMAT_XYUV8888    fourcc_code('X', 'Y', 'U', 'V') /* [31:0] X:Y:Cb:Cr 8:8:8:8 little endian */
+#define DRM_FORMAT_VUY888      fourcc_code('V', 'U', '2', '4') /* [23:0] Cr:Cb:Y 8:8:8 little endian */
+#define DRM_FORMAT_VUY101010   fourcc_code('V', 'U', '3', '0') /* Y followed by U then V, 10:10:10. Non-linear modifier only */
+
+/*
+ * packed Y2xx indicate for each component, xx valid data occupy msb
+ * 16-xx padding occupy lsb
+ */
+#define DRM_FORMAT_Y210         fourcc_code('Y', '2', '1', '0') /* [63:0] Cr0:0:Y1:0:Cb0:0:Y0:0 10:6:10:6:10:6:10:6 little endian per 2 Y pixels */
+#define DRM_FORMAT_Y212         fourcc_code('Y', '2', '1', '2') /* [63:0] Cr0:0:Y1:0:Cb0:0:Y0:0 12:4:12:4:12:4:12:4 little endian per 2 Y pixels */
+#define DRM_FORMAT_Y216         fourcc_code('Y', '2', '1', '6') /* [63:0] Cr0:Y1:Cb0:Y0 16:16:16:16 little endian per 2 Y pixels */
+
+/*
+ * packed Y4xx indicate for each component, xx valid data occupy msb
+ * 16-xx padding occupy lsb except Y410
+ */
+#define DRM_FORMAT_Y410         fourcc_code('Y', '4', '1', '0') /* [31:0] A:Cr:Y:Cb 2:10:10:10 little endian */
+#define DRM_FORMAT_Y412         fourcc_code('Y', '4', '1', '2') /* [63:0] A:0:Cr:0:Y:0:Cb:0 12:4:12:4:12:4:12:4 little endian */
+#define DRM_FORMAT_Y416         fourcc_code('Y', '4', '1', '6') /* [63:0] A:Cr:Y:Cb 16:16:16:16 little endian */
+
+#define DRM_FORMAT_XVYU2101010 fourcc_code('X', 'V', '3', '0') /* [31:0] X:Cr:Y:Cb 2:10:10:10 little endian */
+#define DRM_FORMAT_XVYU12_16161616     fourcc_code('X', 'V', '3', '6') /* [63:0] X:0:Cr:0:Y:0:Cb:0 12:4:12:4:12:4:12:4 little endian */
+#define DRM_FORMAT_XVYU16161616        fourcc_code('X', 'V', '4', '8') /* [63:0] X:Cr:Y:Cb 16:16:16:16 little endian */
 
 /*
  * packed YCbCr420 2x2 tiled formats
@@ -168,6 +201,15 @@ extern "C" {
 #define DRM_FORMAT_X0L2                fourcc_code('X', '0', 'L', '2')
 
 /*
+ * 1-plane YUV 4:2:0
+ * In these formats, the component ordering is specified (Y, followed by U
+ * then V), but the exact Linear layout is undefined.
+ * These formats can only be used with a non-Linear modifier.
+ */
+#define DRM_FORMAT_YUV420_8BIT fourcc_code('Y', 'U', '0', '8')
+#define DRM_FORMAT_YUV420_10BIT        fourcc_code('Y', 'U', '1', '0')
+
+/*
  * 2 plane RGB + A
  * index 0 = RGB plane, same format as the corresponding non _A8 format has
  * index 1 = A plane, [7:0] A
@@ -200,6 +242,13 @@ extern "C" {
  * index 0 = Y plane, [15:0] Y:x [10:6] little endian
  * index 1 = Cr:Cb plane, [31:0] Cr:x:Cb:x [10:6:10:6] little endian
  */
+#define DRM_FORMAT_P210                fourcc_code('P', '2', '1', '0') /* 2x1 subsampled Cr:Cb plane, 10 bit per channel */
+
+/*
+ * 2 plane YCbCr MSB aligned
+ * index 0 = Y plane, [15:0] Y:x [10:6] little endian
+ * index 1 = Cr:Cb plane, [31:0] Cr:x:Cb:x [10:6:10:6] little endian
+ */
 #define DRM_FORMAT_P010                fourcc_code('P', '0', '1', '0') /* 2x2 subsampled Cr:Cb plane 10 bits per channel */
 
 /*