Merge branch 'for-upstream/mali-dp' of git://linux-arm.org/linux-ld into drm-next
authorDave Airlie <airlied@redhat.com>
Thu, 31 Jan 2019 23:51:23 +0000 (09:51 +1000)
committerDave Airlie <airlied@redhat.com>
Fri, 1 Feb 2019 00:01:50 +0000 (10:01 +1000)
This pull includes the new Arm "komeda" DRM driver. It is currently hosted
in the same repo as the other "mali-dp" driver because it is the next
iteration of the IP.

Signed-off-by: Dave Airlie <airlied@redhat.com>
From: Liviu Dudau <Liviu.Dudau@arm.com>
Link: https://patchwork.freedesktop.org/patch/msgid/20190131173600.GN25147@e110455-lin.cambridge.arm.com
30 files changed:
Documentation/devicetree/bindings/display/arm,komeda.txt [new file with mode: 0644]
Documentation/gpu/afbc.rst [new file with mode: 0644]
Documentation/gpu/drivers.rst
Documentation/gpu/komeda-kms.rst [new file with mode: 0644]
MAINTAINERS
drivers/gpu/drm/Makefile
drivers/gpu/drm/arm/Kconfig
drivers/gpu/drm/arm/Makefile
drivers/gpu/drm/arm/display/Kbuild [new file with mode: 0644]
drivers/gpu/drm/arm/display/Kconfig [new file with mode: 0644]
drivers/gpu/drm/arm/display/include/malidp_io.h [new file with mode: 0644]
drivers/gpu/drm/arm/display/include/malidp_product.h [new file with mode: 0644]
drivers/gpu/drm/arm/display/include/malidp_utils.h [new file with mode: 0644]
drivers/gpu/drm/arm/display/komeda/Makefile [new file with mode: 0644]
drivers/gpu/drm/arm/display/komeda/d71/d71_dev.c [new file with mode: 0644]
drivers/gpu/drm/arm/display/komeda/komeda_crtc.c [new file with mode: 0644]
drivers/gpu/drm/arm/display/komeda/komeda_dev.c [new file with mode: 0644]
drivers/gpu/drm/arm/display/komeda/komeda_dev.h [new file with mode: 0644]
drivers/gpu/drm/arm/display/komeda/komeda_drv.c [new file with mode: 0644]
drivers/gpu/drm/arm/display/komeda/komeda_format_caps.c [new file with mode: 0644]
drivers/gpu/drm/arm/display/komeda/komeda_format_caps.h [new file with mode: 0644]
drivers/gpu/drm/arm/display/komeda/komeda_framebuffer.c [new file with mode: 0644]
drivers/gpu/drm/arm/display/komeda/komeda_framebuffer.h [new file with mode: 0644]
drivers/gpu/drm/arm/display/komeda/komeda_kms.c [new file with mode: 0644]
drivers/gpu/drm/arm/display/komeda/komeda_kms.h [new file with mode: 0644]
drivers/gpu/drm/arm/display/komeda/komeda_pipeline.c [new file with mode: 0644]
drivers/gpu/drm/arm/display/komeda/komeda_pipeline.h [new file with mode: 0644]
drivers/gpu/drm/arm/display/komeda/komeda_plane.c [new file with mode: 0644]
drivers/gpu/drm/arm/display/komeda/komeda_private_obj.c [new file with mode: 0644]
include/uapi/drm/drm_fourcc.h

diff --git a/Documentation/devicetree/bindings/display/arm,komeda.txt b/Documentation/devicetree/bindings/display/arm,komeda.txt
new file mode 100644 (file)
index 0000000..02b2265
--- /dev/null
@@ -0,0 +1,73 @@
+Device Tree bindings for Arm Komeda display driver
+
+Required properties:
+- compatible: Should be "arm,mali-d71"
+- reg: Physical base address and length of the registers in the system
+- interrupts: the interrupt line number of the device in the system
+- clocks: A list of phandle + clock-specifier pairs, one for each entry
+    in 'clock-names'
+- clock-names: A list of clock names. It should contain:
+      - "mclk": for the main processor clock
+      - "pclk": for the APB interface clock
+- #address-cells: Must be 1
+- #size-cells: Must be 0
+
+Required properties for sub-node: pipeline@nq
+Each device contains one or two pipeline sub-nodes (at least one), each
+pipeline node should provide properties:
+- reg: Zero-indexed identifier for the pipeline
+- clocks: A list of phandle + clock-specifier pairs, one for each entry
+    in 'clock-names'
+- clock-names: should contain:
+      - "pxclk": pixel clock
+      - "aclk": AXI interface clock
+
+- port: each pipeline connect to an encoder input port. The connection is
+    modeled using the OF graph bindings specified in
+    Documentation/devicetree/bindings/graph.txt
+
+Optional properties:
+  - memory-region: phandle to a node describing memory (see
+    Documentation/devicetree/bindings/reserved-memory/reserved-memory.txt)
+    to be used for the framebuffer; if not present, the framebuffer may
+    be located anywhere in memory.
+
+Example:
+/ {
+       ...
+
+       dp0: display@c00000 {
+               #address-cells = <1>;
+               #size-cells = <0>;
+               compatible = "arm,mali-d71";
+               reg = <0xc00000 0x20000>;
+               interrupts = <0 168 4>;
+               clocks = <&dpu_mclk>, <&dpu_aclk>;
+               clock-names = "mclk", "pclk";
+
+               dp0_pipe0: pipeline@0 {
+                       clocks = <&fpgaosc2>, <&dpu_aclk>;
+                       clock-names = "pxclk", "aclk";
+                       reg = <0>;
+
+                       port {
+                               dp0_pipe0_out: endpoint {
+                                       remote-endpoint = <&db_dvi0_in>;
+                               };
+                       };
+               };
+
+               dp0_pipe1: pipeline@1 {
+                       clocks = <&fpgaosc2>, <&dpu_aclk>;
+                       clock-names = "pxclk", "aclk";
+                       reg = <1>;
+
+                       port {
+                               dp0_pipe1_out: endpoint {
+                                       remote-endpoint = <&db_dvi1_in>;
+                               };
+                       };
+               };
+       };
+       ...
+};
diff --git a/Documentation/gpu/afbc.rst b/Documentation/gpu/afbc.rst
new file mode 100644 (file)
index 0000000..4d38dc4
--- /dev/null
@@ -0,0 +1,235 @@
+.. SPDX-License-Identifier: GPL-2.0+
+
+===================================
+ Arm Framebuffer Compression (AFBC)
+===================================
+
+AFBC is a proprietary lossless image compression protocol and format.
+It provides fine-grained random access and minimizes the amount of
+data transferred between IP blocks.
+
+AFBC can be enabled on drivers which support it via use of the AFBC
+format modifiers defined in drm_fourcc.h. See DRM_FORMAT_MOD_ARM_AFBC(*).
+
+All users of the AFBC modifiers must follow the usage guidelines laid
+out in this document, to ensure compatibility across different AFBC
+producers and consumers.
+
+Components and Ordering
+=======================
+
+AFBC streams can contain several components - where a component
+corresponds to a color channel (i.e. R, G, B, X, A, Y, Cb, Cr).
+The assignment of input/output color channels must be consistent
+between the encoder and the decoder for correct operation, otherwise
+the consumer will interpret the decoded data incorrectly.
+
+Furthermore, when the lossless colorspace transform is used
+(AFBC_FORMAT_MOD_YTR, which should be enabled for RGB buffers for
+maximum compression efficiency), the component order must be:
+
+ * Component 0: R
+ * Component 1: G
+ * Component 2: B
+
+The component ordering is communicated via the fourcc code in the
+fourcc:modifier pair. In general, component '0' is considered to
+reside in the least-significant bits of the corresponding linear
+format. For example, COMP(bits):
+
+ * DRM_FORMAT_ABGR8888
+
+   * Component 0: R(8)
+   * Component 1: G(8)
+   * Component 2: B(8)
+   * Component 3: A(8)
+
+ * DRM_FORMAT_BGR888
+
+   * Component 0: R(8)
+   * Component 1: G(8)
+   * Component 2: B(8)
+
+ * DRM_FORMAT_YUYV
+
+   * Component 0: Y(8)
+   * Component 1: Cb(8, 2x1 subsampled)
+   * Component 2: Cr(8, 2x1 subsampled)
+
+In AFBC, 'X' components are not treated any differently from any other
+component. Therefore, an AFBC buffer with fourcc DRM_FORMAT_XBGR8888
+encodes with 4 components, like so:
+
+ * DRM_FORMAT_XBGR8888
+
+   * Component 0: R(8)
+   * Component 1: G(8)
+   * Component 2: B(8)
+   * Component 3: X(8)
+
+Please note, however, that the inclusion of a "wasted" 'X' channel is
+bad for compression efficiency, and so it's recommended to avoid
+formats containing 'X' bits. If a fourth component is
+required/expected by the encoder/decoder, then it is recommended to
+instead use an equivalent format with alpha, setting all alpha bits to
+'1'. If there is no requirement for a fourth component, then a format
+which doesn't include alpha can be used, e.g. DRM_FORMAT_BGR888.
+
+Number of Planes
+================
+
+Formats which are typically multi-planar in linear layouts (e.g. YUV
+420), can be encoded into one, or multiple, AFBC planes. As with
+component order, the encoder and decoder must agree about the number
+of planes in order to correctly decode the buffer. The fourcc code is
+used to determine the number of encoded planes in an AFBC buffer,
+matching the number of planes for the linear (unmodified) format.
+Within each plane, the component ordering also follows the fourcc
+code:
+
+For example:
+
+ * DRM_FORMAT_YUYV: nplanes = 1
+
+   * Plane 0:
+
+     * Component 0: Y(8)
+     * Component 1: Cb(8, 2x1 subsampled)
+     * Component 2: Cr(8, 2x1 subsampled)
+
+ * DRM_FORMAT_NV12: nplanes = 2
+
+   * Plane 0:
+
+     * Component 0: Y(8)
+
+   * Plane 1:
+
+     * Component 0: Cb(8, 2x1 subsampled)
+     * Component 1: Cr(8, 2x1 subsampled)
+
+Cross-device interoperability
+=============================
+
+For maximum compatibility across devices, the table below defines
+canonical formats for use between AFBC-enabled devices. Formats which
+are listed here must be used exactly as specified when using the AFBC
+modifiers. Formats which are not listed should be avoided.
+
+.. flat-table:: AFBC formats
+
+   * - Fourcc code
+     - Description
+     - Planes/Components
+
+   * - DRM_FORMAT_ABGR2101010
+     - 10-bit per component RGB, with 2-bit alpha
+     - Plane 0: 4 components
+              * Component 0: R(10)
+              * Component 1: G(10)
+              * Component 2: B(10)
+              * Component 3: A(2)
+
+   * - DRM_FORMAT_ABGR8888
+     - 8-bit per component RGB, with 8-bit alpha
+     - Plane 0: 4 components
+              * Component 0: R(8)
+              * Component 1: G(8)
+              * Component 2: B(8)
+              * Component 3: A(8)
+
+   * - DRM_FORMAT_BGR888
+     - 8-bit per component RGB
+     - Plane 0: 3 components
+              * Component 0: R(8)
+              * Component 1: G(8)
+              * Component 2: B(8)
+
+   * - DRM_FORMAT_BGR565
+     - 5/6-bit per component RGB
+     - Plane 0: 3 components
+              * Component 0: R(5)
+              * Component 1: G(6)
+              * Component 2: B(5)
+
+   * - DRM_FORMAT_ABGR1555
+     - 5-bit per component RGB, with 1-bit alpha
+     - Plane 0: 4 components
+              * Component 0: R(5)
+              * Component 1: G(5)
+              * Component 2: B(5)
+              * Component 3: A(1)
+
+   * - DRM_FORMAT_VUY888
+     - 8-bit per component YCbCr 444, single plane
+     - Plane 0: 3 components
+              * Component 0: Y(8)
+              * Component 1: Cb(8)
+              * Component 2: Cr(8)
+
+   * - DRM_FORMAT_VUY101010
+     - 10-bit per component YCbCr 444, single plane
+     - Plane 0: 3 components
+              * Component 0: Y(10)
+              * Component 1: Cb(10)
+              * Component 2: Cr(10)
+
+   * - DRM_FORMAT_YUYV
+     - 8-bit per component YCbCr 422, single plane
+     - Plane 0: 3 components
+              * Component 0: Y(8)
+              * Component 1: Cb(8, 2x1 subsampled)
+              * Component 2: Cr(8, 2x1 subsampled)
+
+   * - DRM_FORMAT_NV16
+     - 8-bit per component YCbCr 422, two plane
+     - Plane 0: 1 component
+              * Component 0: Y(8)
+       Plane 1: 2 components
+              * Component 0: Cb(8, 2x1 subsampled)
+              * Component 1: Cr(8, 2x1 subsampled)
+
+   * - DRM_FORMAT_Y210
+     - 10-bit per component YCbCr 422, single plane
+     - Plane 0: 3 components
+              * Component 0: Y(10)
+              * Component 1: Cb(10, 2x1 subsampled)
+              * Component 2: Cr(10, 2x1 subsampled)
+
+   * - DRM_FORMAT_P210
+     - 10-bit per component YCbCr 422, two plane
+     - Plane 0: 1 component
+              * Component 0: Y(10)
+       Plane 1: 2 components
+              * Component 0: Cb(10, 2x1 subsampled)
+              * Component 1: Cr(10, 2x1 subsampled)
+
+   * - DRM_FORMAT_YUV420_8BIT
+     - 8-bit per component YCbCr 420, single plane
+     - Plane 0: 3 components
+              * Component 0: Y(8)
+              * Component 1: Cb(8, 2x2 subsampled)
+              * Component 2: Cr(8, 2x2 subsampled)
+
+   * - DRM_FORMAT_YUV420_10BIT
+     - 10-bit per component YCbCr 420, single plane
+     - Plane 0: 3 components
+              * Component 0: Y(10)
+              * Component 1: Cb(10, 2x2 subsampled)
+              * Component 2: Cr(10, 2x2 subsampled)
+
+   * - DRM_FORMAT_NV12
+     - 8-bit per component YCbCr 420, two plane
+     - Plane 0: 1 component
+              * Component 0: Y(8)
+       Plane 1: 2 components
+              * Component 0: Cb(8, 2x2 subsampled)
+              * Component 1: Cr(8, 2x2 subsampled)
+
+   * - DRM_FORMAT_P010
+     - 10-bit per component YCbCr 420, two plane
+     - Plane 0: 1 component
+              * Component 0: Y(10)
+       Plane 1: 2 components
+              * Component 0: Cb(10, 2x2 subsampled)
+              * Component 1: Cr(10, 2x2 subsampled)
index 7c1672118a73f4c59f066ef78fa95badb6a1ce43..044a7025477c167e278e95eb15692cafd45c9734 100644 (file)
@@ -17,6 +17,8 @@ GPU Driver Documentation
    vkms
    bridge/dw-hdmi
    xen-front
+   afbc
+   komeda-kms
 
 .. only::  subproject and html
 
diff --git a/Documentation/gpu/komeda-kms.rst b/Documentation/gpu/komeda-kms.rst
new file mode 100644 (file)
index 0000000..b08da1c
--- /dev/null
@@ -0,0 +1,488 @@
+.. SPDX-License-Identifier: GPL-2.0
+
+==============================
+ drm/komeda Arm display driver
+==============================
+
+The drm/komeda driver supports the Arm display processor D71 and later products,
+this document gives a brief overview of driver design: how it works and why
+design it like that.
+
+Overview of D71 like display IPs
+================================
+
+From D71, Arm display IP begins to adopt a flexible and modularized
+architecture. A display pipeline is made up of multiple individual and
+functional pipeline stages called components, and every component has some
+specific capabilities that can give the flowed pipeline pixel data a
+particular processing.
+
+Typical D71 components:
+
+Layer
+-----
+Layer is the first pipeline stage, which prepares the pixel data for the next
+stage. It fetches the pixel from memory, decodes it if it's AFBC, rotates the
+source image, unpacks or converts YUV pixels to the device internal RGB pixels,
+then adjusts the color_space of pixels if needed.
+
+Scaler
+------
+As its name suggests, scaler takes responsibility for scaling, and D71 also
+supports image enhancements by scaler.
+The usage of scaler is very flexible and can be connected to layer output
+for layer scaling, or connected to compositor and scale the whole display
+frame and then feed the output data into wb_layer which will then write it
+into memory.
+
+Compositor (compiz)
+-------------------
+Compositor blends multiple layers or pixel data flows into one single display
+frame. its output frame can be fed into post image processor for showing it on
+the monitor or fed into wb_layer and written to memory at the same time.
+user can also insert a scaler between compositor and wb_layer to down scale
+the display frame first and and then write to memory.
+
+Writeback Layer (wb_layer)
+--------------------------
+Writeback layer does the opposite things of Layer, which connects to compiz
+and writes the composition result to memory.
+
+Post image processor (improc)
+-----------------------------
+Post image processor adjusts frame data like gamma and color space to fit the
+requirements of the monitor.
+
+Timing controller (timing_ctrlr)
+--------------------------------
+Final stage of display pipeline, Timing controller is not for the pixel
+handling, but only for controlling the display timing.
+
+Merger
+------
+D71 scaler mostly only has the half horizontal input/output capabilities
+compared with Layer, like if Layer supports 4K input size, the scaler only can
+support 2K input/output in the same time. To achieve the ful frame scaling, D71
+introduces Layer Split, which splits the whole image to two half parts and feeds
+them to two Layers A and B, and does the scaling independently. After scaling
+the result need to be fed to merger to merge two part images together, and then
+output merged result to compiz.
+
+Splitter
+--------
+Similar to Layer Split, but Splitter is used for writeback, which splits the
+compiz result to two parts and then feed them to two scalers.
+
+Possible D71 Pipeline usage
+===========================
+
+Benefitting from the modularized architecture, D71 pipelines can be easily
+adjusted to fit different usages. And D71 has two pipelines, which support two
+types of working mode:
+
+-   Dual display mode
+    Two pipelines work independently and separately to drive two display outputs.
+
+-   Single display mode
+    Two pipelines work together to drive only one display output.
+
+    On this mode, pipeline_B doesn't work indenpendently, but outputs its
+    composition result into pipeline_A, and its pixel timing also derived from
+    pipeline_A.timing_ctrlr. The pipeline_B works just like a "slave" of
+    pipeline_A(master)
+
+Single pipeline data flow
+-------------------------
+
+.. kernel-render:: DOT
+   :alt: Single pipeline digraph
+   :caption: Single pipeline data flow
+
+   digraph single_ppl {
+      rankdir=LR;
+
+      subgraph {
+         "Memory";
+         "Monitor";
+      }
+
+      subgraph cluster_pipeline {
+          style=dashed
+          node [shape=box]
+          {
+              node [bgcolor=grey style=dashed]
+              "Scaler-0";
+              "Scaler-1";
+              "Scaler-0/1"
+          }
+
+         node [bgcolor=grey style=filled]
+         "Layer-0" -> "Scaler-0"
+         "Layer-1" -> "Scaler-0"
+         "Layer-2" -> "Scaler-1"
+         "Layer-3" -> "Scaler-1"
+
+         "Layer-0" -> "Compiz"
+         "Layer-1" -> "Compiz"
+         "Layer-2" -> "Compiz"
+         "Layer-3" -> "Compiz"
+         "Scaler-0" -> "Compiz"
+         "Scaler-1" -> "Compiz"
+
+         "Compiz" -> "Scaler-0/1" -> "Wb_layer"
+         "Compiz" -> "Improc" -> "Timing Controller"
+      }
+
+      "Wb_layer" -> "Memory"
+      "Timing Controller" -> "Monitor"
+   }
+
+Dual pipeline with Slave enabled
+--------------------------------
+
+.. kernel-render:: DOT
+   :alt: Slave pipeline digraph
+   :caption: Slave pipeline enabled data flow
+
+   digraph slave_ppl {
+      rankdir=LR;
+
+      subgraph {
+         "Memory";
+         "Monitor";
+      }
+      node [shape=box]
+      subgraph cluster_pipeline_slave {
+          style=dashed
+          label="Slave Pipeline_B"
+          node [shape=box]
+          {
+              node [bgcolor=grey style=dashed]
+              "Slave.Scaler-0";
+              "Slave.Scaler-1";
+          }
+
+         node [bgcolor=grey style=filled]
+         "Slave.Layer-0" -> "Slave.Scaler-0"
+         "Slave.Layer-1" -> "Slave.Scaler-0"
+         "Slave.Layer-2" -> "Slave.Scaler-1"
+         "Slave.Layer-3" -> "Slave.Scaler-1"
+
+         "Slave.Layer-0" -> "Slave.Compiz"
+         "Slave.Layer-1" -> "Slave.Compiz"
+         "Slave.Layer-2" -> "Slave.Compiz"
+         "Slave.Layer-3" -> "Slave.Compiz"
+         "Slave.Scaler-0" -> "Slave.Compiz"
+         "Slave.Scaler-1" -> "Slave.Compiz"
+      }
+
+      subgraph cluster_pipeline_master {
+          style=dashed
+          label="Master Pipeline_A"
+          node [shape=box]
+          {
+              node [bgcolor=grey style=dashed]
+              "Scaler-0";
+              "Scaler-1";
+              "Scaler-0/1"
+          }
+
+         node [bgcolor=grey style=filled]
+         "Layer-0" -> "Scaler-0"
+         "Layer-1" -> "Scaler-0"
+         "Layer-2" -> "Scaler-1"
+         "Layer-3" -> "Scaler-1"
+
+         "Slave.Compiz" -> "Compiz"
+         "Layer-0" -> "Compiz"
+         "Layer-1" -> "Compiz"
+         "Layer-2" -> "Compiz"
+         "Layer-3" -> "Compiz"
+         "Scaler-0" -> "Compiz"
+         "Scaler-1" -> "Compiz"
+
+         "Compiz" -> "Scaler-0/1" -> "Wb_layer"
+         "Compiz" -> "Improc" -> "Timing Controller"
+      }
+
+      "Wb_layer" -> "Memory"
+      "Timing Controller" -> "Monitor"
+   }
+
+Sub-pipelines for input and output
+----------------------------------
+
+A complete display pipeline can be easily divided into three sub-pipelines
+according to the in/out usage.
+
+Layer(input) pipeline
+~~~~~~~~~~~~~~~~~~~~~
+
+.. kernel-render:: DOT
+   :alt: Layer data digraph
+   :caption: Layer (input) data flow
+
+   digraph layer_data_flow {
+      rankdir=LR;
+      node [shape=box]
+
+      {
+         node [bgcolor=grey style=dashed]
+           "Scaler-n";
+      }
+
+      "Layer-n" -> "Scaler-n" -> "Compiz"
+   }
+
+.. kernel-render:: DOT
+   :alt: Layer Split digraph
+   :caption: Layer Split pipeline
+
+   digraph layer_data_flow {
+      rankdir=LR;
+      node [shape=box]
+
+      "Layer-0/1" -> "Scaler-0" -> "Merger"
+      "Layer-2/3" -> "Scaler-1" -> "Merger"
+      "Merger" -> "Compiz"
+   }
+
+Writeback(output) pipeline
+~~~~~~~~~~~~~~~~~~~~~~~~~~
+.. kernel-render:: DOT
+   :alt: writeback digraph
+   :caption: Writeback(output) data flow
+
+   digraph writeback_data_flow {
+      rankdir=LR;
+      node [shape=box]
+
+      {
+         node [bgcolor=grey style=dashed]
+           "Scaler-n";
+      }
+
+      "Compiz" -> "Scaler-n" -> "Wb_layer"
+   }
+
+.. kernel-render:: DOT
+   :alt: split writeback digraph
+   :caption: Writeback(output) Split data flow
+
+   digraph writeback_data_flow {
+      rankdir=LR;
+      node [shape=box]
+
+      "Compiz" -> "Splitter"
+      "Splitter" -> "Scaler-0" -> "Merger"
+      "Splitter" -> "Scaler-1" -> "Merger"
+      "Merger" -> "Wb_layer"
+   }
+
+Display output pipeline
+~~~~~~~~~~~~~~~~~~~~~~~
+.. kernel-render:: DOT
+   :alt: display digraph
+   :caption: display output data flow
+
+   digraph single_ppl {
+      rankdir=LR;
+      node [shape=box]
+
+      "Compiz" -> "Improc" -> "Timing Controller"
+   }
+
+In the following section we'll see these three sub-pipelines will be handled
+by KMS-plane/wb_conn/crtc respectively.
+
+Komeda Resource abstraction
+===========================
+
+struct komeda_pipeline/component
+--------------------------------
+
+To fully utilize and easily access/configure the HW, the driver side also uses
+a similar architecture: Pipeline/Component to describe the HW features and
+capabilities, and a specific component includes two parts:
+
+-  Data flow controlling.
+-  Specific component capabilities and features.
+
+So the driver defines a common header struct komeda_component to describe the
+data flow control and all specific components are a subclass of this base
+structure.
+
+.. kernel-doc:: drivers/gpu/drm/arm/display/komeda/komeda_pipeline.h
+   :internal:
+
+Resource discovery and initialization
+=====================================
+
+Pipeline and component are used to describe how to handle the pixel data. We
+still need a @struct komeda_dev to describe the whole view of the device, and
+the control-abilites of device.
+
+We have &komeda_dev, &komeda_pipeline, &komeda_component. Now fill devices with
+pipelines. Since komeda is not for D71 only but also intended for later products,
+of course we’d better share as much as possible between different products. To
+achieve this, split the komeda device into two layers: CORE and CHIP.
+
+-   CORE: for common features and capabilities handling.
+-   CHIP: for register programing and HW specific feature (limitation) handling.
+
+CORE can access CHIP by three chip function structures:
+
+-   struct komeda_dev_funcs
+-   struct komeda_pipeline_funcs
+-   struct komeda_component_funcs
+
+.. kernel-doc:: drivers/gpu/drm/arm/display/komeda/komeda_dev.h
+   :internal:
+
+Format handling
+===============
+
+.. kernel-doc:: drivers/gpu/drm/arm/display/komeda/komeda_format_caps.h
+   :internal:
+.. kernel-doc:: drivers/gpu/drm/arm/display/komeda/komeda_framebuffer.h
+   :internal:
+
+Attach komeda_dev to DRM-KMS
+============================
+
+Komeda abstracts resources by pipeline/component, but DRM-KMS uses
+crtc/plane/connector. One KMS-obj cannot represent only one single component,
+since the requirements of a single KMS object cannot simply be achieved by a
+single component, usually that needs multiple components to fit the requirement.
+Like set mode, gamma, ctm for KMS all target on CRTC-obj, but komeda needs
+compiz, improc and timing_ctrlr to work together to fit these requirements.
+And a KMS-Plane may require multiple komeda resources: layer/scaler/compiz.
+
+So, one KMS-Obj represents a sub-pipeline of komeda resources.
+
+-   Plane: `Layer(input) pipeline`_
+-   Wb_connector: `Writeback(output) pipeline`_
+-   Crtc: `Display output pipeline`_
+
+So, for komeda, we treat KMS crtc/plane/connector as users of pipeline and
+component, and at any one time a pipeline/component only can be used by one
+user. And pipeline/component will be treated as private object of DRM-KMS; the
+state will be managed by drm_atomic_state as well.
+
+How to map plane to Layer(input) pipeline
+-----------------------------------------
+
+Komeda has multiple Layer input pipelines, see:
+-   `Single pipeline data flow`_
+-   `Dual pipeline with Slave enabled`_
+
+The easiest way is binding a plane to a fixed Layer pipeline, but consider the
+komeda capabilities:
+
+-   Layer Split, See `Layer(input) pipeline`_
+
+    Layer_Split is quite complicated feature, which splits a big image into two
+    parts and handles it by two layers and two scalers individually. But it
+    imports an edge problem or effect in the middle of the image after the split.
+    To avoid such a problem, it needs a complicated Split calculation and some
+    special configurations to the layer and scaler. We'd better hide such HW
+    related complexity to user mode.
+
+-   Slave pipeline, See `Dual pipeline with Slave enabled`_
+
+    Since the compiz component doesn't output alpha value, the slave pipeline
+    only can be used for bottom layers composition. The komeda driver wants to
+    hide this limitation to the user. The way to do this is to pick a suitable
+    Layer according to plane_state->zpos.
+
+So for komeda, the KMS-plane doesn't represent a fixed komeda layer pipeline,
+but multiple Layers with same capabilities. Komeda will select one or more
+Layers to fit the requirement of one KMS-plane.
+
+Make component/pipeline to be drm_private_obj
+---------------------------------------------
+
+Add :c:type:`drm_private_obj` to :c:type:`komeda_component`, :c:type:`komeda_pipeline`
+
+.. code-block:: c
+
+    struct komeda_component {
+        struct drm_private_obj obj;
+        ...
+    }
+
+    struct komeda_pipeline {
+        struct drm_private_obj obj;
+        ...
+    }
+
+Tracking component_state/pipeline_state by drm_atomic_state
+-----------------------------------------------------------
+
+Add :c:type:`drm_private_state` and user to :c:type:`komeda_component_state`,
+:c:type:`komeda_pipeline_state`
+
+.. code-block:: c
+
+    struct komeda_component_state {
+        struct drm_private_state obj;
+        void *binding_user;
+        ...
+    }
+
+    struct komeda_pipeline_state {
+        struct drm_private_state obj;
+        struct drm_crtc *crtc;
+        ...
+    }
+
+komeda component validation
+---------------------------
+
+Komeda has multiple types of components, but the process of validation are
+similar, usually including the following steps:
+
+.. code-block:: c
+
+    int komeda_xxxx_validate(struct komeda_component_xxx xxx_comp,
+                struct komeda_component_output *input_dflow,
+                struct drm_plane/crtc/connector *user,
+                struct drm_plane/crtc/connector_state, *user_state)
+    {
+         setup 1: check if component is needed, like the scaler is optional depending
+                  on the user_state; if unneeded, just return, and the caller will
+                  put the data flow into next stage.
+         Setup 2: check user_state with component features and capabilities to see
+                  if requirements can be met; if not, return fail.
+         Setup 3: get component_state from drm_atomic_state, and try set to set
+                  user to component; fail if component has been assigned to another
+                  user already.
+         Setup 3: configure the component_state, like set its input component,
+                  convert user_state to component specific state.
+         Setup 4: adjust the input_dflow and prepare it for the next stage.
+    }
+
+komeda_kms Abstraction
+----------------------
+
+.. kernel-doc:: drivers/gpu/drm/arm/display/komeda/komeda_kms.h
+   :internal:
+
+komde_kms Functions
+-------------------
+.. kernel-doc:: drivers/gpu/drm/arm/display/komeda/komeda_crtc.c
+   :internal:
+.. kernel-doc:: drivers/gpu/drm/arm/display/komeda/komeda_plane.c
+   :internal:
+
+Build komeda to be a Linux module driver
+========================================
+
+Now we have two level devices:
+
+-   komeda_dev: describes the real display hardware.
+-   komeda_kms_dev: attachs or connects komeda_dev to DRM-KMS.
+
+All komeda operations are supplied or operated by komeda_dev or komeda_kms_dev,
+the module driver is only a simple wrapper to pass the Linux command
+(probe/remove/pm) into komeda_dev or komeda_kms_dev.
index 6e1cef2f21d9f8add3ae6238ca53a3eac348f2fd..4b752571fe03b7e7aa94bf19281f304cfd4316b7 100644 (file)
@@ -1133,13 +1133,26 @@ S:      Supported
 F:     drivers/gpu/drm/arm/hdlcd_*
 F:     Documentation/devicetree/bindings/display/arm,hdlcd.txt
 
+ARM KOMEDA DRM-KMS DRIVER
+M:     James (Qian) Wang <james.qian.wang@arm.com>
+M:     Liviu Dudau <liviu.dudau@arm.com>
+L:     Mali DP Maintainers <malidp@foss.arm.com>
+S:     Supported
+T:     git git://linux-arm.org/linux-ld.git for-upstream/mali-dp
+F:     drivers/gpu/drm/arm/display/include/
+F:     drivers/gpu/drm/arm/display/komeda/
+F:     Documentation/devicetree/bindings/display/arm/arm,komeda.txt
+F:     Documentation/gpu/komeda-kms.rst
+
 ARM MALI-DP DRM DRIVER
 M:     Liviu Dudau <liviu.dudau@arm.com>
 M:     Brian Starkey <brian.starkey@arm.com>
-M:     Mali DP Maintainers <malidp@foss.arm.com>
+L:     Mali DP Maintainers <malidp@foss.arm.com>
 S:     Supported
+T:     git git://linux-arm.org/linux-ld.git for-upstream/mali-dp
 F:     drivers/gpu/drm/arm/
 F:     Documentation/devicetree/bindings/display/arm,malidp.txt
+F:     Documentation/gpu/afbc.rst
 
 ARM MFM AND FLOPPY DRIVERS
 M:     Ian Molton <spyro@f2s.com>
index ce8d1d38431924f95fa5d2a695d8e71c5479f92e..f0c1f8731a2761ed4d1408ced0a8f05c1cc7993d 100644 (file)
@@ -51,7 +51,7 @@ obj-$(CONFIG_DRM_DEBUG_SELFTEST) += selftests/
 obj-$(CONFIG_DRM)      += drm.o
 obj-$(CONFIG_DRM_MIPI_DSI) += drm_mipi_dsi.o
 obj-$(CONFIG_DRM_PANEL_ORIENTATION_QUIRKS) += drm_panel_orientation_quirks.o
-obj-$(CONFIG_DRM_ARM)  += arm/
+obj-y                  += arm/
 obj-$(CONFIG_DRM_TTM)  += ttm/
 obj-$(CONFIG_DRM_SCHED)        += scheduler/
 obj-$(CONFIG_DRM_TDFX) += tdfx/
index 9a18e1bd57b427f7362366ffe9af64d02b21d63c..a204103b3efbf5a235b35aac98831aedab762267 100644 (file)
@@ -1,13 +1,10 @@
-config DRM_ARM
-       bool
-       help
-         Choose this option to select drivers for ARM's devices
+# SPDX-License-Identifier: GPL-2.0
+menu "ARM devices"
 
 config DRM_HDLCD
        tristate "ARM HDLCD"
        depends on DRM && OF && (ARM || ARM64)
        depends on COMMON_CLK
-       select DRM_ARM
        select DRM_KMS_HELPER
        select DRM_KMS_CMA_HELPER
        help
@@ -29,7 +26,6 @@ config DRM_MALI_DISPLAY
        tristate "ARM Mali Display Processor"
        depends on DRM && OF && (ARM || ARM64)
        depends on COMMON_CLK
-       select DRM_ARM
        select DRM_KMS_HELPER
        select DRM_KMS_CMA_HELPER
        select DRM_GEM_CMA_HELPER
@@ -40,3 +36,7 @@ config DRM_MALI_DISPLAY
          of the hardware.
 
          If compiled as a module it will be called mali-dp.
+
+source "drivers/gpu/drm/arm/display/Kconfig"
+
+endmenu
index 3bf31d1a4722cafa2bb2108e8bad2f616d7aa895..120bef801fcf1ce28bbc3e372a086900d92b74af 100644 (file)
@@ -3,3 +3,4 @@ obj-$(CONFIG_DRM_HDLCD) += hdlcd.o
 mali-dp-y := malidp_drv.o malidp_hw.o malidp_planes.o malidp_crtc.o
 mali-dp-y += malidp_mw.o
 obj-$(CONFIG_DRM_MALI_DISPLAY) += mali-dp.o
+obj-$(CONFIG_DRM_KOMEDA) += display/
diff --git a/drivers/gpu/drm/arm/display/Kbuild b/drivers/gpu/drm/arm/display/Kbuild
new file mode 100644 (file)
index 0000000..382f1ca
--- /dev/null
@@ -0,0 +1,3 @@
+# SPDX-License-Identifier: GPL-2.0
+
+obj-$(CONFIG_DRM_KOMEDA) += komeda/
diff --git a/drivers/gpu/drm/arm/display/Kconfig b/drivers/gpu/drm/arm/display/Kconfig
new file mode 100644 (file)
index 0000000..cec0639
--- /dev/null
@@ -0,0 +1,14 @@
+# SPDX-License-Identifier: GPL-2.0
+config DRM_KOMEDA
+       tristate "ARM Komeda display driver"
+       depends on DRM && OF
+       depends on COMMON_CLK
+       select DRM_KMS_HELPER
+       select DRM_KMS_CMA_HELPER
+       select DRM_GEM_CMA_HELPER
+       select VIDEOMODE_HELPERS
+       help
+         Choose this option if you want to compile the ARM Komeda display
+         Processor driver. It supports the D71 variants of the hardware.
+
+         If compiled as a module it will be called komeda.
diff --git a/drivers/gpu/drm/arm/display/include/malidp_io.h b/drivers/gpu/drm/arm/display/include/malidp_io.h
new file mode 100644 (file)
index 0000000..4fb3caf
--- /dev/null
@@ -0,0 +1,42 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * (C) COPYRIGHT 2018 ARM Limited. All rights reserved.
+ * Author: James.Qian.Wang <james.qian.wang@arm.com>
+ *
+ */
+#ifndef _MALIDP_IO_H_
+#define _MALIDP_IO_H_
+
+#include <linux/io.h>
+
+static inline u32
+malidp_read32(u32 __iomem *base, u32 offset)
+{
+       return readl((base + (offset >> 2)));
+}
+
+static inline void
+malidp_write32(u32 __iomem *base, u32 offset, u32 v)
+{
+       writel(v, (base + (offset >> 2)));
+}
+
+static inline void
+malidp_write32_mask(u32 __iomem *base, u32 offset, u32 m, u32 v)
+{
+       u32 tmp = malidp_read32(base, offset);
+
+       tmp &= (~m);
+       malidp_write32(base, offset, v | tmp);
+}
+
+static inline void
+malidp_write_group(u32 __iomem *base, u32 offset, int num, const u32 *values)
+{
+       int i;
+
+       for (i = 0; i < num; i++)
+               malidp_write32(base, offset + i * 4, values[i]);
+}
+
+#endif /*_MALIDP_IO_H_*/
diff --git a/drivers/gpu/drm/arm/display/include/malidp_product.h b/drivers/gpu/drm/arm/display/include/malidp_product.h
new file mode 100644 (file)
index 0000000..b35fc5d
--- /dev/null
@@ -0,0 +1,23 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * (C) COPYRIGHT 2018 ARM Limited. All rights reserved.
+ * Author: James.Qian.Wang <james.qian.wang@arm.com>
+ *
+ */
+#ifndef _MALIDP_PRODUCT_H_
+#define _MALIDP_PRODUCT_H_
+
+/* Product identification */
+#define MALIDP_CORE_ID(__product, __major, __minor, __status) \
+       ((((__product) & 0xFFFF) << 16) | (((__major) & 0xF) << 12) | \
+       (((__minor) & 0xF) << 8) | ((__status) & 0xFF))
+
+#define MALIDP_CORE_ID_PRODUCT_ID(__core_id) ((__u32)(__core_id) >> 16)
+#define MALIDP_CORE_ID_MAJOR(__core_id)      (((__u32)(__core_id) >> 12) & 0xF)
+#define MALIDP_CORE_ID_MINOR(__core_id)      (((__u32)(__core_id) >> 8) & 0xF)
+#define MALIDP_CORE_ID_STATUS(__core_id)     (((__u32)(__core_id)) & 0xFF)
+
+/* Mali-display product IDs */
+#define MALIDP_D71_PRODUCT_ID   0x0071
+
+#endif /* _MALIDP_PRODUCT_H_ */
diff --git a/drivers/gpu/drm/arm/display/include/malidp_utils.h b/drivers/gpu/drm/arm/display/include/malidp_utils.h
new file mode 100644 (file)
index 0000000..63cc47c
--- /dev/null
@@ -0,0 +1,16 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * (C) COPYRIGHT 2018 ARM Limited. All rights reserved.
+ * Author: James.Qian.Wang <james.qian.wang@arm.com>
+ *
+ */
+#ifndef _MALIDP_UTILS_
+#define _MALIDP_UTILS_
+
+#define has_bit(nr, mask)      (BIT(nr) & (mask))
+#define has_bits(bits, mask)   (((bits) & (mask)) == (bits))
+
+#define dp_for_each_set_bit(bit, mask) \
+       for_each_set_bit((bit), ((unsigned long *)&(mask)), sizeof(mask) * 8)
+
+#endif /* _MALIDP_UTILS_ */
diff --git a/drivers/gpu/drm/arm/display/komeda/Makefile b/drivers/gpu/drm/arm/display/komeda/Makefile
new file mode 100644 (file)
index 0000000..1b875e5
--- /dev/null
@@ -0,0 +1,21 @@
+# SPDX-License-Identifier: GPL-2.0
+
+ccflags-y := \
+       -I$(src)/../include \
+       -I$(src)
+
+komeda-y := \
+       komeda_drv.o \
+       komeda_dev.o \
+       komeda_format_caps.o \
+       komeda_pipeline.o \
+       komeda_framebuffer.o \
+       komeda_kms.o \
+       komeda_crtc.o \
+       komeda_plane.o \
+       komeda_private_obj.o
+
+komeda-y += \
+       d71/d71_dev.o
+
+obj-$(CONFIG_DRM_KOMEDA) += komeda.o
diff --git a/drivers/gpu/drm/arm/display/komeda/d71/d71_dev.c b/drivers/gpu/drm/arm/display/komeda/d71/d71_dev.c
new file mode 100644 (file)
index 0000000..edbf9da
--- /dev/null
@@ -0,0 +1,111 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * (C) COPYRIGHT 2018 ARM Limited. All rights reserved.
+ * Author: James.Qian.Wang <james.qian.wang@arm.com>
+ *
+ */
+#include "malidp_io.h"
+#include "komeda_dev.h"
+
+static int d71_enum_resources(struct komeda_dev *mdev)
+{
+       /* TODO add enum resources */
+       return -1;
+}
+
+#define __HW_ID(__group, __format) \
+       ((((__group) & 0x7) << 3) | ((__format) & 0x7))
+
+#define RICH           KOMEDA_FMT_RICH_LAYER
+#define SIMPLE         KOMEDA_FMT_SIMPLE_LAYER
+#define RICH_SIMPLE    (KOMEDA_FMT_RICH_LAYER | KOMEDA_FMT_SIMPLE_LAYER)
+#define RICH_WB                (KOMEDA_FMT_RICH_LAYER | KOMEDA_FMT_WB_LAYER)
+#define RICH_SIMPLE_WB (RICH_SIMPLE | KOMEDA_FMT_WB_LAYER)
+
+#define Rot_0          DRM_MODE_ROTATE_0
+#define Flip_H_V       (DRM_MODE_REFLECT_X | DRM_MODE_REFLECT_Y | Rot_0)
+#define Rot_ALL_H_V    (DRM_MODE_ROTATE_MASK | Flip_H_V)
+
+#define LYT_NM         BIT(AFBC_FORMAT_MOD_BLOCK_SIZE_16x16)
+#define LYT_WB         BIT(AFBC_FORMAT_MOD_BLOCK_SIZE_32x8)
+#define LYT_NM_WB      (LYT_NM | LYT_WB)
+
+#define AFB_TH         AFBC(_TILED | _SPARSE)
+#define AFB_TH_SC_YTR  AFBC(_TILED | _SC | _SPARSE | _YTR)
+#define AFB_TH_SC_YTR_BS AFBC(_TILED | _SC | _SPARSE | _YTR | _SPLIT)
+
+static struct komeda_format_caps d71_format_caps_table[] = {
+       /*   HW_ID    |        fourcc        | tile_sz |   layer_types |   rots    | afbc_layouts | afbc_features */
+       /* ABGR_2101010*/
+       {__HW_ID(0, 0), DRM_FORMAT_ARGB2101010, 1,      RICH_SIMPLE_WB, Flip_H_V,               0, 0},
+       {__HW_ID(0, 1), DRM_FORMAT_ABGR2101010, 1,      RICH_SIMPLE_WB, Flip_H_V,               0, 0},
+       {__HW_ID(0, 1), DRM_FORMAT_ABGR2101010, 1,      RICH_SIMPLE,    Rot_ALL_H_V,    LYT_NM_WB, AFB_TH_SC_YTR_BS}, /* afbc */
+       {__HW_ID(0, 2), DRM_FORMAT_RGBA1010102, 1,      RICH_SIMPLE_WB, Flip_H_V,               0, 0},
+       {__HW_ID(0, 3), DRM_FORMAT_BGRA1010102, 1,      RICH_SIMPLE_WB, Flip_H_V,               0, 0},
+       /* ABGR_8888*/
+       {__HW_ID(1, 0), DRM_FORMAT_ARGB8888,    1,      RICH_SIMPLE_WB, Flip_H_V,               0, 0},
+       {__HW_ID(1, 1), DRM_FORMAT_ABGR8888,    1,      RICH_SIMPLE_WB, Flip_H_V,               0, 0},
+       {__HW_ID(1, 1), DRM_FORMAT_ABGR8888,    1,      RICH_SIMPLE,    Rot_ALL_H_V,    LYT_NM_WB, AFB_TH_SC_YTR_BS}, /* afbc */
+       {__HW_ID(1, 2), DRM_FORMAT_RGBA8888,    1,      RICH_SIMPLE_WB, Flip_H_V,               0, 0},
+       {__HW_ID(1, 3), DRM_FORMAT_BGRA8888,    1,      RICH_SIMPLE_WB, Flip_H_V,               0, 0},
+       /* XBGB_8888 */
+       {__HW_ID(2, 0), DRM_FORMAT_XRGB8888,    1,      RICH_SIMPLE_WB, Flip_H_V,               0, 0},
+       {__HW_ID(2, 1), DRM_FORMAT_XBGR8888,    1,      RICH_SIMPLE_WB, Flip_H_V,               0, 0},
+       {__HW_ID(2, 2), DRM_FORMAT_RGBX8888,    1,      RICH_SIMPLE_WB, Flip_H_V,               0, 0},
+       {__HW_ID(2, 3), DRM_FORMAT_BGRX8888,    1,      RICH_SIMPLE_WB, Flip_H_V,               0, 0},
+       /* BGR_888 */ /* none-afbc RGB888 doesn't support rotation and flip */
+       {__HW_ID(3, 0), DRM_FORMAT_RGB888,      1,      RICH_SIMPLE_WB, Rot_0,                  0, 0},
+       {__HW_ID(3, 1), DRM_FORMAT_BGR888,      1,      RICH_SIMPLE_WB, Rot_0,                  0, 0},
+       {__HW_ID(3, 1), DRM_FORMAT_BGR888,      1,      RICH_SIMPLE,    Rot_ALL_H_V,    LYT_NM_WB, AFB_TH_SC_YTR_BS}, /* afbc */
+       /* BGR 16bpp */
+       {__HW_ID(4, 0), DRM_FORMAT_RGBA5551,    1,      RICH_SIMPLE,    Flip_H_V,               0, 0},
+       {__HW_ID(4, 1), DRM_FORMAT_ABGR1555,    1,      RICH_SIMPLE,    Flip_H_V,               0, 0},
+       {__HW_ID(4, 1), DRM_FORMAT_ABGR1555,    1,      RICH_SIMPLE,    Rot_ALL_H_V,    LYT_NM_WB, AFB_TH_SC_YTR}, /* afbc */
+       {__HW_ID(4, 2), DRM_FORMAT_RGB565,      1,      RICH_SIMPLE,    Flip_H_V,               0, 0},
+       {__HW_ID(4, 3), DRM_FORMAT_BGR565,      1,      RICH_SIMPLE,    Flip_H_V,               0, 0},
+       {__HW_ID(4, 3), DRM_FORMAT_BGR565,      1,      RICH_SIMPLE,    Rot_ALL_H_V,    LYT_NM_WB, AFB_TH_SC_YTR}, /* afbc */
+       {__HW_ID(4, 4), DRM_FORMAT_R8,          1,      SIMPLE,         Rot_0,                  0, 0},
+       /* YUV 444/422/420 8bit  */
+       {__HW_ID(5, 0), 0 /*XYUV8888*/,         1,      0,              0,                      0, 0},
+       /* XYUV unsupported*/
+       {__HW_ID(5, 1), DRM_FORMAT_YUYV,        1,      RICH,           Rot_ALL_H_V,    LYT_NM, AFB_TH}, /* afbc */
+       {__HW_ID(5, 2), DRM_FORMAT_YUYV,        1,      RICH,           Flip_H_V,               0, 0},
+       {__HW_ID(5, 3), DRM_FORMAT_UYVY,        1,      RICH,           Flip_H_V,               0, 0},
+       {__HW_ID(5, 4), 0, /*X0L0 */            2,              0,                      0, 0}, /* Y0L0 unsupported */
+       {__HW_ID(5, 6), DRM_FORMAT_NV12,        1,      RICH,           Flip_H_V,               0, 0},
+       {__HW_ID(5, 6), 0/*DRM_FORMAT_YUV420_8BIT*/,    1,      RICH,   Rot_ALL_H_V,    LYT_NM, AFB_TH}, /* afbc */
+       {__HW_ID(5, 7), DRM_FORMAT_YUV420,      1,      RICH,           Flip_H_V,               0, 0},
+       /* YUV 10bit*/
+       {__HW_ID(6, 0), 0,/*XVYU2101010*/       1,      0,              0,                      0, 0},/* VYV30 unsupported */
+       {__HW_ID(6, 6), 0/*DRM_FORMAT_X0L2*/,   2,      RICH,           Flip_H_V,               0, 0},
+       {__HW_ID(6, 7), 0/*DRM_FORMAT_P010*/,   1,      RICH,           Flip_H_V,               0, 0},
+       {__HW_ID(6, 7), 0/*DRM_FORMAT_YUV420_10BIT*/, 1,        RICH,   Rot_ALL_H_V,    LYT_NM, AFB_TH},
+};
+
+static void d71_init_fmt_tbl(struct komeda_dev *mdev)
+{
+       struct komeda_format_caps_table *table = &mdev->fmt_tbl;
+
+       table->format_caps = d71_format_caps_table;
+       table->n_formats = ARRAY_SIZE(d71_format_caps_table);
+}
+
+static struct komeda_dev_funcs d71_chip_funcs = {
+       .init_format_table = d71_init_fmt_tbl,
+       .enum_resources = d71_enum_resources,
+       .cleanup        = NULL,
+};
+
+#define GLB_ARCH_ID            0x000
+#define GLB_CORE_ID            0x004
+#define GLB_CORE_INFO          0x008
+
+struct komeda_dev_funcs *
+d71_identify(u32 __iomem *reg_base, struct komeda_chip_info *chip)
+{
+       chip->arch_id   = malidp_read32(reg_base, GLB_ARCH_ID);
+       chip->core_id   = malidp_read32(reg_base, GLB_CORE_ID);
+       chip->core_info = malidp_read32(reg_base, GLB_CORE_INFO);
+
+       return &d71_chip_funcs;
+}
diff --git a/drivers/gpu/drm/arm/display/komeda/komeda_crtc.c b/drivers/gpu/drm/arm/display/komeda/komeda_crtc.c
new file mode 100644 (file)
index 0000000..5bb5a55
--- /dev/null
@@ -0,0 +1,106 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * (C) COPYRIGHT 2018 ARM Limited. All rights reserved.
+ * Author: James.Qian.Wang <james.qian.wang@arm.com>
+ *
+ */
+#include <linux/clk.h>
+#include <linux/spinlock.h>
+#include <drm/drm_atomic.h>
+#include <drm/drm_atomic_helper.h>
+#include <drm/drm_plane_helper.h>
+#include <drm/drm_crtc_helper.h>
+#include <linux/pm_runtime.h>
+#include "komeda_dev.h"
+#include "komeda_kms.h"
+
+struct drm_crtc_helper_funcs komeda_crtc_helper_funcs = {
+};
+
+static const struct drm_crtc_funcs komeda_crtc_funcs = {
+};
+
+int komeda_kms_setup_crtcs(struct komeda_kms_dev *kms,
+                          struct komeda_dev *mdev)
+{
+       struct komeda_crtc *crtc;
+       struct komeda_pipeline *master;
+       char str[16];
+       int i;
+
+       kms->n_crtcs = 0;
+
+       for (i = 0; i < mdev->n_pipelines; i++) {
+               crtc = &kms->crtcs[kms->n_crtcs];
+               master = mdev->pipelines[i];
+
+               crtc->master = master;
+               crtc->slave  = NULL;
+
+               if (crtc->slave)
+                       sprintf(str, "pipe-%d", crtc->slave->id);
+               else
+                       sprintf(str, "None");
+
+               DRM_INFO("crtc%d: master(pipe-%d) slave(%s) output: %s.\n",
+                        kms->n_crtcs, master->id, str,
+                        master->of_output_dev ?
+                        master->of_output_dev->full_name : "None");
+
+               kms->n_crtcs++;
+       }
+
+       return 0;
+}
+
+static struct drm_plane *
+get_crtc_primary(struct komeda_kms_dev *kms, struct komeda_crtc *crtc)
+{
+       struct komeda_plane *kplane;
+       struct drm_plane *plane;
+
+       drm_for_each_plane(plane, &kms->base) {
+               if (plane->type != DRM_PLANE_TYPE_PRIMARY)
+                       continue;
+
+               kplane = to_kplane(plane);
+               /* only master can be primary */
+               if (kplane->layer->base.pipeline == crtc->master)
+                       return plane;
+       }
+
+       return NULL;
+}
+
+static int komeda_crtc_add(struct komeda_kms_dev *kms,
+                          struct komeda_crtc *kcrtc)
+{
+       struct drm_crtc *crtc = &kcrtc->base;
+       int err;
+
+       err = drm_crtc_init_with_planes(&kms->base, crtc,
+                                       get_crtc_primary(kms, kcrtc), NULL,
+                                       &komeda_crtc_funcs, NULL);
+       if (err)
+               return err;
+
+       drm_crtc_helper_add(crtc, &komeda_crtc_helper_funcs);
+       drm_crtc_vblank_reset(crtc);
+
+       crtc->port = kcrtc->master->of_output_port;
+
+       return 0;
+}
+
+int komeda_kms_add_crtcs(struct komeda_kms_dev *kms, struct komeda_dev *mdev)
+{
+       int i, err;
+
+       for (i = 0; i < kms->n_crtcs; i++) {
+               err = komeda_crtc_add(kms, &kms->crtcs[i]);
+               if (err)
+                       return err;
+       }
+
+       return 0;
+}
diff --git a/drivers/gpu/drm/arm/display/komeda/komeda_dev.c b/drivers/gpu/drm/arm/display/komeda/komeda_dev.c
new file mode 100644 (file)
index 0000000..0fe6954
--- /dev/null
@@ -0,0 +1,186 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * (C) COPYRIGHT 2018 ARM Limited. All rights reserved.
+ * Author: James.Qian.Wang <james.qian.wang@arm.com>
+ *
+ */
+#include <linux/platform_device.h>
+#include <linux/of_device.h>
+#include <linux/of_graph.h>
+#include "komeda_dev.h"
+
+static int komeda_parse_pipe_dt(struct komeda_dev *mdev, struct device_node *np)
+{
+       struct komeda_pipeline *pipe;
+       struct clk *clk;
+       u32 pipe_id;
+       int ret = 0;
+
+       ret = of_property_read_u32(np, "reg", &pipe_id);
+       if (ret != 0 || pipe_id >= mdev->n_pipelines)
+               return -EINVAL;
+
+       pipe = mdev->pipelines[pipe_id];
+
+       clk = of_clk_get_by_name(np, "aclk");
+       if (IS_ERR(clk)) {
+               DRM_ERROR("get aclk for pipeline %d failed!\n", pipe_id);
+               return PTR_ERR(clk);
+       }
+       pipe->aclk = clk;
+
+       clk = of_clk_get_by_name(np, "pxclk");
+       if (IS_ERR(clk)) {
+               DRM_ERROR("get pxclk for pipeline %d failed!\n", pipe_id);
+               return PTR_ERR(clk);
+       }
+       pipe->pxlclk = clk;
+
+       /* enum ports */
+       pipe->of_output_dev =
+               of_graph_get_remote_node(np, KOMEDA_OF_PORT_OUTPUT, 0);
+       pipe->of_output_port =
+               of_graph_get_port_by_id(np, KOMEDA_OF_PORT_OUTPUT);
+
+       pipe->of_node = np;
+
+       return 0;
+}
+
+static int komeda_parse_dt(struct device *dev, struct komeda_dev *mdev)
+{
+       struct device_node *child, *np = dev->of_node;
+       struct clk *clk;
+       int ret;
+
+       clk = devm_clk_get(dev, "mclk");
+       if (IS_ERR(clk))
+               return PTR_ERR(clk);
+
+       mdev->mclk = clk;
+
+       for_each_available_child_of_node(np, child) {
+               if (of_node_cmp(child->name, "pipeline") == 0) {
+                       ret = komeda_parse_pipe_dt(mdev, child);
+                       if (ret) {
+                               DRM_ERROR("parse pipeline dt error!\n");
+                               of_node_put(child);
+                               break;
+                       }
+               }
+       }
+
+       return ret;
+}
+
+struct komeda_dev *komeda_dev_create(struct device *dev)
+{
+       struct platform_device *pdev = to_platform_device(dev);
+       const struct komeda_product_data *product;
+       struct komeda_dev *mdev;
+       struct resource *io_res;
+       int err = 0;
+
+       product = of_device_get_match_data(dev);
+       if (!product)
+               return ERR_PTR(-ENODEV);
+
+       io_res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+       if (!io_res) {
+               DRM_ERROR("No registers defined.\n");
+               return ERR_PTR(-ENODEV);
+       }
+
+       mdev = devm_kzalloc(dev, sizeof(*mdev), GFP_KERNEL);
+       if (!mdev)
+               return ERR_PTR(-ENOMEM);
+
+       mdev->dev = dev;
+       mdev->reg_base = devm_ioremap_resource(dev, io_res);
+       if (IS_ERR(mdev->reg_base)) {
+               DRM_ERROR("Map register space failed.\n");
+               err = PTR_ERR(mdev->reg_base);
+               mdev->reg_base = NULL;
+               goto err_cleanup;
+       }
+
+       mdev->pclk = devm_clk_get(dev, "pclk");
+       if (IS_ERR(mdev->pclk)) {
+               DRM_ERROR("Get APB clk failed.\n");
+               err = PTR_ERR(mdev->pclk);
+               mdev->pclk = NULL;
+               goto err_cleanup;
+       }
+
+       /* Enable APB clock to access the registers */
+       clk_prepare_enable(mdev->pclk);
+
+       mdev->funcs = product->identify(mdev->reg_base, &mdev->chip);
+       if (!komeda_product_match(mdev, product->product_id)) {
+               DRM_ERROR("DT configured %x mismatch with real HW %x.\n",
+                         product->product_id,
+                         MALIDP_CORE_ID_PRODUCT_ID(mdev->chip.core_id));
+               err = -ENODEV;
+               goto err_cleanup;
+       }
+
+       DRM_INFO("Found ARM Mali-D%x version r%dp%d\n",
+                MALIDP_CORE_ID_PRODUCT_ID(mdev->chip.core_id),
+                MALIDP_CORE_ID_MAJOR(mdev->chip.core_id),
+                MALIDP_CORE_ID_MINOR(mdev->chip.core_id));
+
+       mdev->funcs->init_format_table(mdev);
+
+       err = mdev->funcs->enum_resources(mdev);
+       if (err) {
+               DRM_ERROR("enumerate display resource failed.\n");
+               goto err_cleanup;
+       }
+
+       err = komeda_parse_dt(dev, mdev);
+       if (err) {
+               DRM_ERROR("parse device tree failed.\n");
+               goto err_cleanup;
+       }
+
+       return mdev;
+
+err_cleanup:
+       komeda_dev_destroy(mdev);
+       return ERR_PTR(err);
+}
+
+void komeda_dev_destroy(struct komeda_dev *mdev)
+{
+       struct device *dev = mdev->dev;
+       struct komeda_dev_funcs *funcs = mdev->funcs;
+       int i;
+
+       for (i = 0; i < mdev->n_pipelines; i++) {
+               komeda_pipeline_destroy(mdev, mdev->pipelines[i]);
+               mdev->pipelines[i] = NULL;
+       }
+
+       mdev->n_pipelines = 0;
+
+       if (funcs && funcs->cleanup)
+               funcs->cleanup(mdev);
+
+       if (mdev->reg_base) {
+               devm_iounmap(dev, mdev->reg_base);
+               mdev->reg_base = NULL;
+       }
+
+       if (mdev->mclk) {
+               devm_clk_put(dev, mdev->mclk);
+               mdev->mclk = NULL;
+       }
+
+       if (mdev->pclk) {
+               clk_disable_unprepare(mdev->pclk);
+               devm_clk_put(dev, mdev->pclk);
+               mdev->pclk = NULL;
+       }
+
+       devm_kfree(dev, mdev);
+}
diff --git a/drivers/gpu/drm/arm/display/komeda/komeda_dev.h b/drivers/gpu/drm/arm/display/komeda/komeda_dev.h
new file mode 100644 (file)
index 0000000..0f77dea
--- /dev/null
@@ -0,0 +1,110 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * (C) COPYRIGHT 2018 ARM Limited. All rights reserved.
+ * Author: James.Qian.Wang <james.qian.wang@arm.com>
+ *
+ */
+#ifndef _KOMEDA_DEV_H_
+#define _KOMEDA_DEV_H_
+
+#include <linux/device.h>
+#include <linux/clk.h>
+#include "komeda_pipeline.h"
+#include "malidp_product.h"
+#include "komeda_format_caps.h"
+
+/* malidp device id */
+enum {
+       MALI_D71 = 0,
+};
+
+/* pipeline DT ports */
+enum {
+       KOMEDA_OF_PORT_OUTPUT           = 0,
+       KOMEDA_OF_PORT_COPROC           = 1,
+};
+
+struct komeda_chip_info {
+       u32 arch_id;
+       u32 core_id;
+       u32 core_info;
+       u32 bus_width;
+};
+
+struct komeda_product_data {
+       u32 product_id;
+       struct komeda_dev_funcs *(*identify)(u32 __iomem *reg,
+                                            struct komeda_chip_info *info);
+};
+
+struct komeda_dev;
+
+/**
+ * struct komeda_dev_funcs
+ *
+ * Supplied by chip level and returned by the chip entry function xxx_identify,
+ */
+struct komeda_dev_funcs {
+       /**
+        * @init_format_table:
+        *
+        * initialize &komeda_dev->format_table, this function should be called
+        * before the &enum_resource
+        */
+       void (*init_format_table)(struct komeda_dev *mdev);
+       /**
+        * @enum_resources:
+        *
+        * for CHIP to report or add pipeline and component resources to CORE
+        */
+       int (*enum_resources)(struct komeda_dev *mdev);
+       /** @cleanup: call to chip to cleanup komeda_dev->chip data */
+       void (*cleanup)(struct komeda_dev *mdev);
+};
+
+/**
+ * struct komeda_dev
+ *
+ * Pipeline and component are used to describe how to handle the pixel data.
+ * komeda_device is for describing the whole view of the device, and the
+ * control-abilites of device.
+ */
+struct komeda_dev {
+       struct device *dev;
+       u32 __iomem   *reg_base;
+
+       struct komeda_chip_info chip;
+       /** @fmt_tbl: initialized by &komeda_dev_funcs->init_format_table */
+       struct komeda_format_caps_table fmt_tbl;
+       /** @pclk: APB clock for register access */
+       struct clk *pclk;
+       /** @mck: HW main engine clk */
+       struct clk *mclk;
+
+       int n_pipelines;
+       struct komeda_pipeline *pipelines[KOMEDA_MAX_PIPELINES];
+
+       /** @funcs: chip funcs to access to HW */
+       struct komeda_dev_funcs *funcs;
+       /**
+        * @chip_data:
+        *
+        * chip data will be added by &komeda_dev_funcs.enum_resources() and
+        * destroyed by &komeda_dev_funcs.cleanup()
+        */
+       void *chip_data;
+};
+
+static inline bool
+komeda_product_match(struct komeda_dev *mdev, u32 target)
+{
+       return MALIDP_CORE_ID_PRODUCT_ID(mdev->chip.core_id) == target;
+}
+
+struct komeda_dev_funcs *
+d71_identify(u32 __iomem *reg, struct komeda_chip_info *chip);
+
+struct komeda_dev *komeda_dev_create(struct device *dev);
+void komeda_dev_destroy(struct komeda_dev *mdev);
+
+#endif /*_KOMEDA_DEV_H_*/
diff --git a/drivers/gpu/drm/arm/display/komeda/komeda_drv.c b/drivers/gpu/drm/arm/display/komeda/komeda_drv.c
new file mode 100644 (file)
index 0000000..2bdd189
--- /dev/null
@@ -0,0 +1,144 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * (C) COPYRIGHT 2018 ARM Limited. All rights reserved.
+ * Author: James.Qian.Wang <james.qian.wang@arm.com>
+ *
+ */
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/platform_device.h>
+#include <linux/component.h>
+#include <drm/drm_of.h>
+#include "komeda_dev.h"
+#include "komeda_kms.h"
+
+struct komeda_drv {
+       struct komeda_dev *mdev;
+       struct komeda_kms_dev *kms;
+};
+
+static void komeda_unbind(struct device *dev)
+{
+       struct komeda_drv *mdrv = dev_get_drvdata(dev);
+
+       if (!mdrv)
+               return;
+
+       komeda_kms_detach(mdrv->kms);
+       komeda_dev_destroy(mdrv->mdev);
+
+       dev_set_drvdata(dev, NULL);
+       devm_kfree(dev, mdrv);
+}
+
+static int komeda_bind(struct device *dev)
+{
+       struct komeda_drv *mdrv;
+       int err;
+
+       mdrv = devm_kzalloc(dev, sizeof(*mdrv), GFP_KERNEL);
+       if (!mdrv)
+               return -ENOMEM;
+
+       mdrv->mdev = komeda_dev_create(dev);
+       if (IS_ERR(mdrv->mdev)) {
+               err = PTR_ERR(mdrv->mdev);
+               goto free_mdrv;
+       }
+
+       mdrv->kms = komeda_kms_attach(mdrv->mdev);
+       if (IS_ERR(mdrv->kms)) {
+               err = PTR_ERR(mdrv->kms);
+               goto destroy_mdev;
+       }
+
+       dev_set_drvdata(dev, mdrv);
+
+       return 0;
+
+destroy_mdev:
+       komeda_dev_destroy(mdrv->mdev);
+
+free_mdrv:
+       devm_kfree(dev, mdrv);
+       return err;
+}
+
+static const struct component_master_ops komeda_master_ops = {
+       .bind   = komeda_bind,
+       .unbind = komeda_unbind,
+};
+
+static int compare_of(struct device *dev, void *data)
+{
+       return dev->of_node == data;
+}
+
+static void komeda_add_slave(struct device *master,
+                            struct component_match **match,
+                            struct device_node *np, int port)
+{
+       struct device_node *remote;
+
+       remote = of_graph_get_remote_node(np, port, 0);
+       if (remote) {
+               drm_of_component_match_add(master, match, compare_of, remote);
+               of_node_put(remote);
+       }
+}
+
+static int komeda_platform_probe(struct platform_device *pdev)
+{
+       struct device *dev = &pdev->dev;
+       struct component_match *match = NULL;
+       struct device_node *child;
+
+       if (!dev->of_node)
+               return -ENODEV;
+
+       for_each_available_child_of_node(dev->of_node, child) {
+               if (of_node_cmp(child->name, "pipeline") != 0)
+                       continue;
+
+               /* add connector */
+               komeda_add_slave(dev, &match, child, KOMEDA_OF_PORT_OUTPUT);
+       }
+
+       return component_master_add_with_match(dev, &komeda_master_ops, match);
+}
+
+static int komeda_platform_remove(struct platform_device *pdev)
+{
+       component_master_del(&pdev->dev, &komeda_master_ops);
+       return 0;
+}
+
+static const struct komeda_product_data komeda_products[] = {
+       [MALI_D71] = {
+               .product_id = MALIDP_D71_PRODUCT_ID,
+               .identify = d71_identify,
+       },
+};
+
+const struct of_device_id komeda_of_match[] = {
+       { .compatible = "arm,mali-d71", .data = &komeda_products[MALI_D71], },
+       {},
+};
+
+MODULE_DEVICE_TABLE(of, komeda_of_match);
+
+static struct platform_driver komeda_platform_driver = {
+       .probe  = komeda_platform_probe,
+       .remove = komeda_platform_remove,
+       .driver = {
+               .name = "komeda",
+               .of_match_table = komeda_of_match,
+               .pm = NULL,
+       },
+};
+
+module_platform_driver(komeda_platform_driver);
+
+MODULE_AUTHOR("James.Qian.Wang <james.qian.wang@arm.com>");
+MODULE_DESCRIPTION("Komeda KMS driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/gpu/drm/arm/display/komeda/komeda_format_caps.c b/drivers/gpu/drm/arm/display/komeda/komeda_format_caps.c
new file mode 100644 (file)
index 0000000..1e17bd6
--- /dev/null
@@ -0,0 +1,75 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * (C) COPYRIGHT 2018 ARM Limited. All rights reserved.
+ * Author: James.Qian.Wang <james.qian.wang@arm.com>
+ *
+ */
+
+#include <linux/slab.h>
+#include "komeda_format_caps.h"
+#include "malidp_utils.h"
+
+const struct komeda_format_caps *
+komeda_get_format_caps(struct komeda_format_caps_table *table,
+                      u32 fourcc, u64 modifier)
+{
+       const struct komeda_format_caps *caps;
+       u64 afbc_features = modifier & ~(AFBC_FORMAT_MOD_BLOCK_SIZE_MASK);
+       u32 afbc_layout = modifier & AFBC_FORMAT_MOD_BLOCK_SIZE_MASK;
+       int id;
+
+       for (id = 0; id < table->n_formats; id++) {
+               caps = &table->format_caps[id];
+
+               if (fourcc != caps->fourcc)
+                       continue;
+
+               if ((modifier == 0ULL) && (caps->supported_afbc_layouts == 0))
+                       return caps;
+
+               if (has_bits(afbc_features, caps->supported_afbc_features) &&
+                   has_bit(afbc_layout, caps->supported_afbc_layouts))
+                       return caps;
+       }
+
+       return NULL;
+}
+
+u32 *komeda_get_layer_fourcc_list(struct komeda_format_caps_table *table,
+                                 u32 layer_type, u32 *n_fmts)
+{
+       const struct komeda_format_caps *cap;
+       u32 *fmts;
+       int i, j, n = 0;
+
+       fmts = kcalloc(table->n_formats, sizeof(u32), GFP_KERNEL);
+       if (!fmts)
+               return NULL;
+
+       for (i = 0; i < table->n_formats; i++) {
+               cap = &table->format_caps[i];
+               if (!(layer_type & cap->supported_layer_types) ||
+                   (cap->fourcc == 0))
+                       continue;
+
+               /* one fourcc may has two caps items in table (afbc/none-afbc),
+                * so check the existing list to avoid adding a duplicated one.
+                */
+               for (j = n - 1; j >= 0; j--)
+                       if (fmts[j] == cap->fourcc)
+                               break;
+
+               if (j < 0)
+                       fmts[n++] = cap->fourcc;
+       }
+
+       if (n_fmts)
+               *n_fmts = n;
+
+       return fmts;
+}
+
+void komeda_put_fourcc_list(u32 *fourcc_list)
+{
+       kfree(fourcc_list);
+}
diff --git a/drivers/gpu/drm/arm/display/komeda/komeda_format_caps.h b/drivers/gpu/drm/arm/display/komeda/komeda_format_caps.h
new file mode 100644 (file)
index 0000000..60f39e7
--- /dev/null
@@ -0,0 +1,89 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * (C) COPYRIGHT 2018 ARM Limited. All rights reserved.
+ * Author: James.Qian.Wang <james.qian.wang@arm.com>
+ *
+ */
+
+#ifndef _KOMEDA_FORMAT_CAPS_H_
+#define _KOMEDA_FORMAT_CAPS_H_
+
+#include <linux/types.h>
+#include <uapi/drm/drm_fourcc.h>
+#include <drm/drm_fourcc.h>
+
+#define AFBC(x)                DRM_FORMAT_MOD_ARM_AFBC(x)
+
+/* afbc layerout */
+#define AFBC_16x16(x)  AFBC(AFBC_FORMAT_MOD_BLOCK_SIZE_16x16 | (x))
+#define AFBC_32x8(x)   AFBC(AFBC_FORMAT_MOD_BLOCK_SIZE_32x8 | (x))
+
+/* afbc features */
+#define _YTR           AFBC_FORMAT_MOD_YTR
+#define _SPLIT         AFBC_FORMAT_MOD_SPLIT
+#define _SPARSE                AFBC_FORMAT_MOD_SPARSE
+#define _CBR           AFBC_FORMAT_MOD_CBR
+#define _TILED         AFBC_FORMAT_MOD_TILED
+#define _SC            AFBC_FORMAT_MOD_SC
+
+/* layer_type */
+#define KOMEDA_FMT_RICH_LAYER          BIT(0)
+#define KOMEDA_FMT_SIMPLE_LAYER                BIT(1)
+#define KOMEDA_FMT_WB_LAYER            BIT(2)
+
+#define AFBC_TH_LAYOUT_ALIGNMENT       8
+#define AFBC_HEADER_SIZE               16
+#define AFBC_SUPERBLK_ALIGNMENT                128
+#define AFBC_SUPERBLK_PIXELS           256
+#define AFBC_BODY_START_ALIGNMENT      1024
+#define AFBC_TH_BODY_START_ALIGNMENT   4096
+
+/**
+ * struct komeda_format_caps
+ *
+ * komeda_format_caps is for describing ARM display specific features and
+ * limitations for a specific format, and format_caps will be linked into
+ * &komeda_framebuffer like a extension of &drm_format_info.
+ *
+ * NOTE: one fourcc may has two different format_caps items for fourcc and
+ * fourcc+modifier
+ *
+ * @hw_id: hw format id, hw specific value.
+ * @fourcc: drm fourcc format.
+ * @tile_size: format tiled size, used by ARM format X0L0/X0L2
+ * @supported_layer_types: indicate which layer supports this format
+ * @supported_rots: allowed rotations for this format
+ * @supported_afbc_layouts: supported afbc layerout
+ * @supported_afbc_features: supported afbc features
+ */
+struct komeda_format_caps {
+       u32 hw_id;
+       u32 fourcc;
+       u32 tile_size;
+       u32 supported_layer_types;
+       u32 supported_rots;
+       u32 supported_afbc_layouts;
+       u64 supported_afbc_features;
+};
+
+/**
+ * struct komeda_format_caps_table - format_caps mananger
+ *
+ * @n_formats: the size of format_caps list.
+ * @format_caps: format_caps list.
+ */
+struct komeda_format_caps_table {
+       u32 n_formats;
+       const struct komeda_format_caps *format_caps;
+};
+
+const struct komeda_format_caps *
+komeda_get_format_caps(struct komeda_format_caps_table *table,
+                      u32 fourcc, u64 modifier);
+
+u32 *komeda_get_layer_fourcc_list(struct komeda_format_caps_table *table,
+                                 u32 layer_type, u32 *n_fmts);
+
+void komeda_put_fourcc_list(u32 *fourcc_list);
+
+#endif
diff --git a/drivers/gpu/drm/arm/display/komeda/komeda_framebuffer.c b/drivers/gpu/drm/arm/display/komeda/komeda_framebuffer.c
new file mode 100644 (file)
index 0000000..23ee74d
--- /dev/null
@@ -0,0 +1,165 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * (C) COPYRIGHT 2018 ARM Limited. All rights reserved.
+ * Author: James.Qian.Wang <james.qian.wang@arm.com>
+ *
+ */
+#include <drm/drm_gem.h>
+#include <drm/drm_gem_framebuffer_helper.h>
+#include <drm/drm_fb_cma_helper.h>
+#include <drm/drm_gem_cma_helper.h>
+#include "komeda_framebuffer.h"
+#include "komeda_dev.h"
+
+static void komeda_fb_destroy(struct drm_framebuffer *fb)
+{
+       struct komeda_fb *kfb = to_kfb(fb);
+       u32 i;
+
+       for (i = 0; i < fb->format->num_planes; i++)
+               drm_gem_object_put_unlocked(fb->obj[i]);
+
+       drm_framebuffer_cleanup(fb);
+       kfree(kfb);
+}
+
+static int komeda_fb_create_handle(struct drm_framebuffer *fb,
+                                  struct drm_file *file, u32 *handle)
+{
+       return drm_gem_handle_create(file, fb->obj[0], handle);
+}
+
+static const struct drm_framebuffer_funcs komeda_fb_funcs = {
+       .destroy        = komeda_fb_destroy,
+       .create_handle  = komeda_fb_create_handle,
+};
+
+static int
+komeda_fb_none_afbc_size_check(struct komeda_dev *mdev, struct komeda_fb *kfb,
+                              struct drm_file *file,
+                              const struct drm_mode_fb_cmd2 *mode_cmd)
+{
+       struct drm_framebuffer *fb = &kfb->base;
+       struct drm_gem_object *obj;
+       u32 min_size = 0;
+       u32 i;
+
+       for (i = 0; i < fb->format->num_planes; i++) {
+               obj = drm_gem_object_lookup(file, mode_cmd->handles[i]);
+               if (!obj) {
+                       DRM_DEBUG_KMS("Failed to lookup GEM object\n");
+                       fb->obj[i] = NULL;
+
+                       return -ENOENT;
+               }
+
+               kfb->aligned_w = fb->width / (i ? fb->format->hsub : 1);
+               kfb->aligned_h = fb->height / (i ? fb->format->vsub : 1);
+
+               if (fb->pitches[i] % mdev->chip.bus_width) {
+                       DRM_DEBUG_KMS("Pitch[%d]: 0x%x doesn't align to 0x%x\n",
+                                     i, fb->pitches[i], mdev->chip.bus_width);
+                       drm_gem_object_put_unlocked(obj);
+                       fb->obj[i] = NULL;
+
+                       return -EINVAL;
+               }
+
+               min_size = ((kfb->aligned_h / kfb->format_caps->tile_size - 1)
+                           * fb->pitches[i])
+                           + (kfb->aligned_w * fb->format->cpp[i]
+                              * kfb->format_caps->tile_size)
+                           + fb->offsets[i];
+
+               if (obj->size < min_size) {
+                       DRM_DEBUG_KMS("Fail to check none afbc fb size.\n");
+                       drm_gem_object_put_unlocked(obj);
+                       fb->obj[i] = NULL;
+
+                       return -EINVAL;
+               }
+
+               fb->obj[i] = obj;
+       }
+
+       if (fb->format->num_planes == 3) {
+               if (fb->pitches[1] != fb->pitches[2]) {
+                       DRM_DEBUG_KMS("The pitch[1] and [2] are not same\n");
+                       return -EINVAL;
+               }
+       }
+
+       return 0;
+}
+
+struct drm_framebuffer *
+komeda_fb_create(struct drm_device *dev, struct drm_file *file,
+                const struct drm_mode_fb_cmd2 *mode_cmd)
+{
+       struct komeda_dev *mdev = dev->dev_private;
+       struct komeda_fb *kfb;
+       int ret = 0, i;
+
+       kfb = kzalloc(sizeof(*kfb), GFP_KERNEL);
+       if (!kfb)
+               return ERR_PTR(-ENOMEM);
+
+       kfb->format_caps = komeda_get_format_caps(&mdev->fmt_tbl,
+                                                 mode_cmd->pixel_format,
+                                                 mode_cmd->modifier[0]);
+       if (!kfb->format_caps) {
+               DRM_DEBUG_KMS("FMT %x is not supported.\n",
+                             mode_cmd->pixel_format);
+               kfree(kfb);
+               return ERR_PTR(-EINVAL);
+       }
+
+       drm_helper_mode_fill_fb_struct(dev, &kfb->base, mode_cmd);
+
+       ret = komeda_fb_none_afbc_size_check(mdev, kfb, file, mode_cmd);
+       if (ret < 0)
+               goto err_cleanup;
+
+       ret = drm_framebuffer_init(dev, &kfb->base, &komeda_fb_funcs);
+       if (ret < 0) {
+               DRM_DEBUG_KMS("failed to initialize fb\n");
+
+               goto err_cleanup;
+       }
+
+       return &kfb->base;
+
+err_cleanup:
+       for (i = 0; i < kfb->base.format->num_planes; i++)
+               drm_gem_object_put_unlocked(kfb->base.obj[i]);
+
+       kfree(kfb);
+       return ERR_PTR(ret);
+}
+
+dma_addr_t
+komeda_fb_get_pixel_addr(struct komeda_fb *kfb, int x, int y, int plane)
+{
+       struct drm_framebuffer *fb = &kfb->base;
+       const struct drm_gem_cma_object *obj;
+       u32 plane_x, plane_y, cpp, pitch, offset;
+
+       if (plane >= fb->format->num_planes) {
+               DRM_DEBUG_KMS("Out of max plane num.\n");
+               return -EINVAL;
+       }
+
+       obj = drm_fb_cma_get_gem_obj(fb, plane);
+
+       offset = fb->offsets[plane];
+       if (!fb->modifier) {
+               plane_x = x / (plane ? fb->format->hsub : 1);
+               plane_y = y / (plane ? fb->format->vsub : 1);
+               cpp = fb->format->cpp[plane];
+               pitch = fb->pitches[plane];
+               offset += plane_x * cpp *  kfb->format_caps->tile_size +
+                               (plane_y * pitch) / kfb->format_caps->tile_size;
+       }
+
+       return obj->paddr + offset;
+}
diff --git a/drivers/gpu/drm/arm/display/komeda/komeda_framebuffer.h b/drivers/gpu/drm/arm/display/komeda/komeda_framebuffer.h
new file mode 100644 (file)
index 0000000..0de2e4a
--- /dev/null
@@ -0,0 +1,34 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * (C) COPYRIGHT 2018 ARM Limited. All rights reserved.
+ * Author: James.Qian.Wang <james.qian.wang@arm.com>
+ *
+ */
+#ifndef _KOMEDA_FRAMEBUFFER_H_
+#define _KOMEDA_FRAMEBUFFER_H_
+
+#include <drm/drm_framebuffer.h>
+#include "komeda_format_caps.h"
+
+/** struct komeda_fb - entend drm_framebuffer with komeda attribute */
+struct komeda_fb {
+       /** @base: &drm_framebuffer */
+       struct drm_framebuffer base;
+       /* @format_caps: &komeda_format_caps */
+       const struct komeda_format_caps *format_caps;
+       /** @aligned_w: aligned frame buffer width */
+       u32 aligned_w;
+       /** @aligned_h: aligned frame buffer height */
+       u32 aligned_h;
+};
+
+#define to_kfb(dfb)    container_of(dfb, struct komeda_fb, base)
+
+struct drm_framebuffer *
+komeda_fb_create(struct drm_device *dev, struct drm_file *file,
+                const struct drm_mode_fb_cmd2 *mode_cmd);
+dma_addr_t
+komeda_fb_get_pixel_addr(struct komeda_fb *kfb, int x, int y, int plane);
+bool komeda_fb_is_layer_supported(struct komeda_fb *kfb, u32 layer_type);
+
+#endif
diff --git a/drivers/gpu/drm/arm/display/komeda/komeda_kms.c b/drivers/gpu/drm/arm/display/komeda/komeda_kms.c
new file mode 100644 (file)
index 0000000..3fc096d
--- /dev/null
@@ -0,0 +1,167 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * (C) COPYRIGHT 2018 ARM Limited. All rights reserved.
+ * Author: James.Qian.Wang <james.qian.wang@arm.com>
+ *
+ */
+#include <linux/component.h>
+#include <drm/drm_atomic.h>
+#include <drm/drm_atomic_helper.h>
+#include <drm/drm_gem_framebuffer_helper.h>
+#include <drm/drm_gem_cma_helper.h>
+#include <drm/drm_fb_helper.h>
+#include <linux/interrupt.h>
+#include "komeda_dev.h"
+#include "komeda_kms.h"
+#include "komeda_framebuffer.h"
+
+DEFINE_DRM_GEM_CMA_FOPS(komeda_cma_fops);
+
+static int komeda_gem_cma_dumb_create(struct drm_file *file,
+                                     struct drm_device *dev,
+                                     struct drm_mode_create_dumb *args)
+{
+       u32 alignment = 16; /* TODO get alignment from dev */
+
+       args->pitch = ALIGN(DIV_ROUND_UP(args->width * args->bpp, 8),
+                           alignment);
+
+       return drm_gem_cma_dumb_create_internal(file, dev, args);
+}
+
+static struct drm_driver komeda_kms_driver = {
+       .driver_features = DRIVER_GEM | DRIVER_MODESET | DRIVER_ATOMIC |
+                          DRIVER_PRIME,
+       .lastclose                      = drm_fb_helper_lastclose,
+       .gem_free_object_unlocked       = drm_gem_cma_free_object,
+       .gem_vm_ops                     = &drm_gem_cma_vm_ops,
+       .dumb_create                    = komeda_gem_cma_dumb_create,
+       .prime_handle_to_fd             = drm_gem_prime_handle_to_fd,
+       .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_get_sg_table         = drm_gem_cma_prime_get_sg_table,
+       .gem_prime_import_sg_table      = drm_gem_cma_prime_import_sg_table,
+       .gem_prime_vmap                 = drm_gem_cma_prime_vmap,
+       .gem_prime_vunmap               = drm_gem_cma_prime_vunmap,
+       .gem_prime_mmap                 = drm_gem_cma_prime_mmap,
+       .fops = &komeda_cma_fops,
+       .name = "komeda",
+       .desc = "Arm Komeda Display Processor driver",
+       .date = "20181101",
+       .major = 0,
+       .minor = 1,
+};
+
+static void komeda_kms_commit_tail(struct drm_atomic_state *old_state)
+{
+       struct drm_device *dev = old_state->dev;
+
+       drm_atomic_helper_commit_modeset_disables(dev, old_state);
+
+       drm_atomic_helper_commit_planes(dev, old_state, 0);
+
+       drm_atomic_helper_commit_modeset_enables(dev, old_state);
+
+       drm_atomic_helper_wait_for_flip_done(dev, old_state);
+
+       drm_atomic_helper_commit_hw_done(old_state);
+
+       drm_atomic_helper_cleanup_planes(dev, old_state);
+}
+
+static const struct drm_mode_config_helper_funcs komeda_mode_config_helpers = {
+       .atomic_commit_tail = komeda_kms_commit_tail,
+};
+
+static const struct drm_mode_config_funcs komeda_mode_config_funcs = {
+       .fb_create              = komeda_fb_create,
+       .atomic_check           = drm_atomic_helper_check,
+       .atomic_commit          = drm_atomic_helper_commit,
+};
+
+static void komeda_kms_mode_config_init(struct komeda_kms_dev *kms,
+                                       struct komeda_dev *mdev)
+{
+       struct drm_mode_config *config = &kms->base.mode_config;
+
+       drm_mode_config_init(&kms->base);
+
+       komeda_kms_setup_crtcs(kms, mdev);
+
+       /* Get value from dev */
+       config->min_width       = 0;
+       config->min_height      = 0;
+       config->max_width       = 4096;
+       config->max_height      = 4096;
+       config->allow_fb_modifiers = false;
+
+       config->funcs = &komeda_mode_config_funcs;
+       config->helper_private = &komeda_mode_config_helpers;
+}
+
+struct komeda_kms_dev *komeda_kms_attach(struct komeda_dev *mdev)
+{
+       struct komeda_kms_dev *kms = kzalloc(sizeof(*kms), GFP_KERNEL);
+       struct drm_device *drm;
+       int err;
+
+       if (!kms)
+               return ERR_PTR(-ENOMEM);
+
+       drm = &kms->base;
+       err = drm_dev_init(drm, &komeda_kms_driver, mdev->dev);
+       if (err)
+               goto free_kms;
+
+       drm->dev_private = mdev;
+
+       komeda_kms_mode_config_init(kms, mdev);
+
+       err = komeda_kms_add_private_objs(kms, mdev);
+       if (err)
+               goto cleanup_mode_config;
+
+       err = komeda_kms_add_planes(kms, mdev);
+       if (err)
+               goto cleanup_mode_config;
+
+       err = drm_vblank_init(drm, kms->n_crtcs);
+       if (err)
+               goto cleanup_mode_config;
+
+       err = komeda_kms_add_crtcs(kms, mdev);
+       if (err)
+               goto cleanup_mode_config;
+
+       err = component_bind_all(mdev->dev, kms);
+       if (err)
+               goto cleanup_mode_config;
+
+       drm_mode_config_reset(drm);
+
+       err = drm_dev_register(drm, 0);
+       if (err)
+               goto cleanup_mode_config;
+
+       return kms;
+
+cleanup_mode_config:
+       drm_mode_config_cleanup(drm);
+free_kms:
+       kfree(kms);
+       return ERR_PTR(err);
+}
+
+void komeda_kms_detach(struct komeda_kms_dev *kms)
+{
+       struct drm_device *drm = &kms->base;
+       struct komeda_dev *mdev = drm->dev_private;
+
+       drm_dev_unregister(drm);
+       component_unbind_all(mdev->dev, drm);
+       komeda_kms_cleanup_private_objs(mdev);
+       drm_mode_config_cleanup(drm);
+       drm->dev_private = NULL;
+       drm_dev_put(drm);
+}
diff --git a/drivers/gpu/drm/arm/display/komeda/komeda_kms.h b/drivers/gpu/drm/arm/display/komeda/komeda_kms.h
new file mode 100644 (file)
index 0000000..f136660
--- /dev/null
@@ -0,0 +1,113 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * (C) COPYRIGHT 2018 ARM Limited. All rights reserved.
+ * Author: James.Qian.Wang <james.qian.wang@arm.com>
+ *
+ */
+#ifndef _KOMEDA_KMS_H_
+#define _KOMEDA_KMS_H_
+
+#include <drm/drm_atomic.h>
+#include <drm/drm_atomic_helper.h>
+#include <drm/drm_crtc_helper.h>
+#include <drm/drm_writeback.h>
+
+/** struct komeda_plane - komeda instance of drm_plane */
+struct komeda_plane {
+       /** @base: &drm_plane */
+       struct drm_plane base;
+       /**
+        * @layer:
+        *
+        * represents available layer input pipelines for this plane.
+        *
+        * NOTE:
+        * the layer is not for a specific Layer, but indicate a group of
+        * Layers with same capabilities.
+        */
+       struct komeda_layer *layer;
+};
+
+/**
+ * struct komeda_plane_state
+ *
+ * The plane_state can be split into two data flow (left/right) and handled
+ * by two layers &komeda_plane.layer and &komeda_plane.layer.right
+ */
+struct komeda_plane_state {
+       /** @base: &drm_plane_state */
+       struct drm_plane_state base;
+
+       /* private properties */
+};
+
+/**
+ * struct komeda_wb_connector
+ */
+struct komeda_wb_connector {
+       /** @base: &drm_writeback_connector */
+       struct drm_writeback_connector base;
+
+       /** @wb_layer: represents associated writeback pipeline of komeda */
+       struct komeda_layer *wb_layer;
+};
+
+/**
+ * struct komeda_crtc
+ */
+struct komeda_crtc {
+       /** @base: &drm_crtc */
+       struct drm_crtc base;
+       /** @master: only master has display output */
+       struct komeda_pipeline *master;
+       /**
+        * @slave: optional
+        *
+        * Doesn't have its own display output, the handled data flow will
+        * merge into the master.
+        */
+       struct komeda_pipeline *slave;
+};
+
+/** struct komeda_crtc_state */
+struct komeda_crtc_state {
+       /** @base: &drm_crtc_state */
+       struct drm_crtc_state base;
+
+       /* private properties */
+
+       /* computed state which are used by validate/check */
+       u32 affected_pipes;
+       u32 active_pipes;
+};
+
+/** struct komeda_kms_dev - for gather KMS related things */
+struct komeda_kms_dev {
+       /** @base: &drm_device */
+       struct drm_device base;
+
+       /** @n_crtcs: valid numbers of crtcs in &komeda_kms_dev.crtcs */
+       int n_crtcs;
+       /** @crtcs: crtcs list */
+       struct komeda_crtc crtcs[KOMEDA_MAX_PIPELINES];
+};
+
+#define to_kplane(p)   container_of(p, struct komeda_plane, base)
+#define to_kplane_st(p)        container_of(p, struct komeda_plane_state, base)
+#define to_kconn(p)    container_of(p, struct komeda_wb_connector, base)
+#define to_kcrtc(p)    container_of(p, struct komeda_crtc, base)
+#define to_kcrtc_st(p) container_of(p, struct komeda_crtc_state, base)
+#define to_kdev(p)     container_of(p, struct komeda_kms_dev, base)
+
+int komeda_kms_setup_crtcs(struct komeda_kms_dev *kms, struct komeda_dev *mdev);
+
+int komeda_kms_add_crtcs(struct komeda_kms_dev *kms, struct komeda_dev *mdev);
+int komeda_kms_add_planes(struct komeda_kms_dev *kms, struct komeda_dev *mdev);
+int komeda_kms_add_private_objs(struct komeda_kms_dev *kms,
+                               struct komeda_dev *mdev);
+void komeda_kms_cleanup_private_objs(struct komeda_dev *mdev);
+
+struct komeda_kms_dev *komeda_kms_attach(struct komeda_dev *mdev);
+void komeda_kms_detach(struct komeda_kms_dev *kms);
+
+#endif /*_KOMEDA_KMS_H_*/
diff --git a/drivers/gpu/drm/arm/display/komeda/komeda_pipeline.c b/drivers/gpu/drm/arm/display/komeda/komeda_pipeline.c
new file mode 100644 (file)
index 0000000..edb1cd7
--- /dev/null
@@ -0,0 +1,200 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * (C) COPYRIGHT 2018 ARM Limited. All rights reserved.
+ * Author: James.Qian.Wang <james.qian.wang@arm.com>
+ *
+ */
+#include "komeda_dev.h"
+#include "komeda_pipeline.h"
+
+/** komeda_pipeline_add - Add a pipeline to &komeda_dev */
+struct komeda_pipeline *
+komeda_pipeline_add(struct komeda_dev *mdev, size_t size,
+                   struct komeda_pipeline_funcs *funcs)
+{
+       struct komeda_pipeline *pipe;
+
+       if (mdev->n_pipelines + 1 > KOMEDA_MAX_PIPELINES) {
+               DRM_ERROR("Exceed max support %d pipelines.\n",
+                         KOMEDA_MAX_PIPELINES);
+               return NULL;
+       }
+
+       if (size < sizeof(*pipe)) {
+               DRM_ERROR("Request pipeline size too small.\n");
+               return NULL;
+       }
+
+       pipe = devm_kzalloc(mdev->dev, size, GFP_KERNEL);
+       if (!pipe)
+               return NULL;
+
+       pipe->mdev = mdev;
+       pipe->id   = mdev->n_pipelines;
+       pipe->funcs = funcs;
+
+       mdev->pipelines[mdev->n_pipelines] = pipe;
+       mdev->n_pipelines++;
+
+       return pipe;
+}
+
+void komeda_pipeline_destroy(struct komeda_dev *mdev,
+                            struct komeda_pipeline *pipe)
+{
+       struct komeda_component *c;
+       int i;
+
+       dp_for_each_set_bit(i, pipe->avail_comps) {
+               c = komeda_pipeline_get_component(pipe, i);
+               komeda_component_destroy(mdev, c);
+       }
+
+       clk_put(pipe->pxlclk);
+       clk_put(pipe->aclk);
+
+       of_node_put(pipe->of_output_dev);
+       of_node_put(pipe->of_output_port);
+       of_node_put(pipe->of_node);
+
+       devm_kfree(mdev->dev, pipe);
+}
+
+struct komeda_component **
+komeda_pipeline_get_component_pos(struct komeda_pipeline *pipe, int id)
+{
+       struct komeda_dev *mdev = pipe->mdev;
+       struct komeda_pipeline *temp = NULL;
+       struct komeda_component **pos = NULL;
+
+       switch (id) {
+       case KOMEDA_COMPONENT_LAYER0:
+       case KOMEDA_COMPONENT_LAYER1:
+       case KOMEDA_COMPONENT_LAYER2:
+       case KOMEDA_COMPONENT_LAYER3:
+               pos = to_cpos(pipe->layers[id - KOMEDA_COMPONENT_LAYER0]);
+               break;
+       case KOMEDA_COMPONENT_WB_LAYER:
+               pos = to_cpos(pipe->wb_layer);
+               break;
+       case KOMEDA_COMPONENT_COMPIZ0:
+       case KOMEDA_COMPONENT_COMPIZ1:
+               temp = mdev->pipelines[id - KOMEDA_COMPONENT_COMPIZ0];
+               if (!temp) {
+                       DRM_ERROR("compiz-%d doesn't exist.\n", id);
+                       return NULL;
+               }
+               pos = to_cpos(temp->compiz);
+               break;
+       case KOMEDA_COMPONENT_SCALER0:
+       case KOMEDA_COMPONENT_SCALER1:
+               pos = to_cpos(pipe->scalers[id - KOMEDA_COMPONENT_SCALER0]);
+               break;
+       case KOMEDA_COMPONENT_IPS0:
+       case KOMEDA_COMPONENT_IPS1:
+               temp = mdev->pipelines[id - KOMEDA_COMPONENT_IPS0];
+               if (!temp) {
+                       DRM_ERROR("ips-%d doesn't exist.\n", id);
+                       return NULL;
+               }
+               pos = to_cpos(temp->improc);
+               break;
+       case KOMEDA_COMPONENT_TIMING_CTRLR:
+               pos = to_cpos(pipe->ctrlr);
+               break;
+       default:
+               pos = NULL;
+               DRM_ERROR("Unknown pipeline resource ID: %d.\n", id);
+               break;
+       }
+
+       return pos;
+}
+
+struct komeda_component *
+komeda_pipeline_get_component(struct komeda_pipeline *pipe, int id)
+{
+       struct komeda_component **pos = NULL;
+       struct komeda_component *c = NULL;
+
+       pos = komeda_pipeline_get_component_pos(pipe, id);
+       if (pos)
+               c = *pos;
+
+       return c;
+}
+
+/** komeda_component_add - Add a component to &komeda_pipeline */
+struct komeda_component *
+komeda_component_add(struct komeda_pipeline *pipe,
+                    size_t comp_sz, u32 id, u32 hw_id,
+                    struct komeda_component_funcs *funcs,
+                    u8 max_active_inputs, u32 supported_inputs,
+                    u8 max_active_outputs, u32 __iomem *reg,
+                    const char *name_fmt, ...)
+{
+       struct komeda_component **pos;
+       struct komeda_component *c;
+       int idx, *num = NULL;
+
+       if (max_active_inputs > KOMEDA_COMPONENT_N_INPUTS) {
+               WARN(1, "please large KOMEDA_COMPONENT_N_INPUTS to %d.\n",
+                    max_active_inputs);
+               return NULL;
+       }
+
+       pos = komeda_pipeline_get_component_pos(pipe, id);
+       if (!pos || (*pos))
+               return NULL;
+
+       if (has_bit(id, KOMEDA_PIPELINE_LAYERS)) {
+               idx = id - KOMEDA_COMPONENT_LAYER0;
+               num = &pipe->n_layers;
+               if (idx != pipe->n_layers) {
+                       DRM_ERROR("please add Layer by id sequence.\n");
+                       return NULL;
+               }
+       } else if (has_bit(id,  KOMEDA_PIPELINE_SCALERS)) {
+               idx = id - KOMEDA_COMPONENT_SCALER0;
+               num = &pipe->n_scalers;
+               if (idx != pipe->n_scalers) {
+                       DRM_ERROR("please add Scaler by id sequence.\n");
+                       return NULL;
+               }
+       }
+
+       c = devm_kzalloc(pipe->mdev->dev, comp_sz, GFP_KERNEL);
+       if (!c)
+               return NULL;
+
+       c->id = id;
+       c->hw_id = hw_id;
+       c->reg = reg;
+       c->pipeline = pipe;
+       c->max_active_inputs = max_active_inputs;
+       c->max_active_outputs = max_active_outputs;
+       c->supported_inputs = supported_inputs;
+       c->funcs = funcs;
+
+       if (name_fmt) {
+               va_list args;
+
+               va_start(args, name_fmt);
+               vsnprintf(c->name, sizeof(c->name), name_fmt, args);
+               va_end(args);
+       }
+
+       if (num)
+               *num = *num + 1;
+
+       pipe->avail_comps |= BIT(c->id);
+       *pos = c;
+
+       return c;
+}
+
+void komeda_component_destroy(struct komeda_dev *mdev,
+                             struct komeda_component *c)
+{
+       devm_kfree(mdev->dev, c);
+}
diff --git a/drivers/gpu/drm/arm/display/komeda/komeda_pipeline.h b/drivers/gpu/drm/arm/display/komeda/komeda_pipeline.h
new file mode 100644 (file)
index 0000000..8c950bc
--- /dev/null
@@ -0,0 +1,359 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * (C) COPYRIGHT 2018 ARM Limited. All rights reserved.
+ * Author: James.Qian.Wang <james.qian.wang@arm.com>
+ *
+ */
+#ifndef _KOMEDA_PIPELINE_H_
+#define _KOMEDA_PIPELINE_H_
+
+#include <linux/types.h>
+#include <drm/drm_atomic.h>
+#include <drm/drm_atomic_helper.h>
+#include "malidp_utils.h"
+
+#define KOMEDA_MAX_PIPELINES           2
+#define KOMEDA_PIPELINE_MAX_LAYERS     4
+#define KOMEDA_PIPELINE_MAX_SCALERS    2
+#define KOMEDA_COMPONENT_N_INPUTS      5
+
+/* pipeline component IDs */
+enum {
+       KOMEDA_COMPONENT_LAYER0         = 0,
+       KOMEDA_COMPONENT_LAYER1         = 1,
+       KOMEDA_COMPONENT_LAYER2         = 2,
+       KOMEDA_COMPONENT_LAYER3         = 3,
+       KOMEDA_COMPONENT_WB_LAYER       = 7, /* write back layer */
+       KOMEDA_COMPONENT_SCALER0        = 8,
+       KOMEDA_COMPONENT_SCALER1        = 9,
+       KOMEDA_COMPONENT_SPLITTER       = 12,
+       KOMEDA_COMPONENT_MERGER         = 14,
+       KOMEDA_COMPONENT_COMPIZ0        = 16, /* compositor */
+       KOMEDA_COMPONENT_COMPIZ1        = 17,
+       KOMEDA_COMPONENT_IPS0           = 20, /* post image processor */
+       KOMEDA_COMPONENT_IPS1           = 21,
+       KOMEDA_COMPONENT_TIMING_CTRLR   = 22, /* timing controller */
+};
+
+#define KOMEDA_PIPELINE_LAYERS         (BIT(KOMEDA_COMPONENT_LAYER0) |\
+                                        BIT(KOMEDA_COMPONENT_LAYER1) |\
+                                        BIT(KOMEDA_COMPONENT_LAYER2) |\
+                                        BIT(KOMEDA_COMPONENT_LAYER3))
+
+#define KOMEDA_PIPELINE_SCALERS                (BIT(KOMEDA_COMPONENT_SCALER0) |\
+                                        BIT(KOMEDA_COMPONENT_SCALER1))
+
+#define KOMEDA_PIPELINE_COMPIZS                (BIT(KOMEDA_COMPONENT_COMPIZ0) |\
+                                        BIT(KOMEDA_COMPONENT_COMPIZ1))
+
+#define KOMEDA_PIPELINE_IMPROCS                (BIT(KOMEDA_COMPONENT_IPS0) |\
+                                        BIT(KOMEDA_COMPONENT_IPS1))
+struct komeda_component;
+struct komeda_component_state;
+
+/** komeda_component_funcs - component control functions */
+struct komeda_component_funcs {
+       /** @validate: optional,
+        * component may has special requirements or limitations, this function
+        * supply HW the ability to do the further HW specific check.
+        */
+       int (*validate)(struct komeda_component *c,
+                       struct komeda_component_state *state);
+       /** @update: update is a active update */
+       void (*update)(struct komeda_component *c,
+                      struct komeda_component_state *state);
+       /** @disable: disable component */
+       void (*disable)(struct komeda_component *c);
+       /** @dump_register: Optional, dump registers to seq_file */
+       void (*dump_register)(struct komeda_component *c, struct seq_file *seq);
+};
+
+/**
+ * struct komeda_component
+ *
+ * struct komeda_component describe the data flow capabilities for how to link a
+ * component into the display pipeline.
+ * all specified components are subclass of this structure.
+ */
+struct komeda_component {
+       /** @obj: treat component as private obj */
+       struct drm_private_obj obj;
+       /** @pipeline: the komeda pipeline this component belongs to */
+       struct komeda_pipeline *pipeline;
+       /** @name: component name */
+       char name[32];
+       /**
+        * @reg:
+        * component register base,
+        * which is initialized by chip and used by chip only
+        */
+       u32 __iomem *reg;
+       /** @id: component id */
+       u32 id;
+       /** @hw_ic: component hw id,
+        *  which is initialized by chip and used by chip only
+        */
+       u32 hw_id;
+
+       /**
+        * @max_active_inputs:
+        * @max_active_outpus:
+        *
+        * maximum number of inputs/outputs that can be active in the same time
+        * Note:
+        * the number isn't the bit number of @supported_inputs or
+        * @supported_outputs, but may be less than it, since component may not
+        * support enabling all @supported_inputs/outputs at the same time.
+        */
+       u8 max_active_inputs;
+       u8 max_active_outputs;
+       /**
+        * @supported_inputs:
+        * @supported_outputs:
+        *
+        * bitmask of BIT(component->id) for the supported inputs/outputs
+        * describes the possibilities of how a component is linked into a
+        * pipeline.
+        */
+       u32 supported_inputs;
+       u32 supported_outputs;
+
+       /**
+        * @funcs: chip functions to access HW
+        */
+       struct komeda_component_funcs *funcs;
+};
+
+/**
+ * struct komeda_component_output
+ *
+ * a component has multiple outputs, if want to know where the data
+ * comes from, only know the component is not enough, we still need to know
+ * its output port
+ */
+struct komeda_component_output {
+       /** @component: indicate which component the data comes from */
+       struct komeda_component *component;
+       /** @output_port:
+        * the output port of the &komeda_component_output.component
+        */
+       u8 output_port;
+};
+
+/**
+ * struct komeda_component_state
+ *
+ * component_state is the data flow configuration of the component, and it's
+ * the superclass of all specific component_state like @komeda_layer_state,
+ * @komeda_scaler_state
+ */
+struct komeda_component_state {
+       /** @obj: tracking component_state by drm_atomic_state */
+       struct drm_private_state obj;
+       struct komeda_component *component;
+       /**
+        * @binding_user:
+        * currently bound user, the user can be crtc/plane/wb_conn, which is
+        * valid decided by @component and @inputs
+        *
+        * -  Layer: its user always is plane.
+        * -  compiz/improc/timing_ctrlr: the user is crtc.
+        * -  wb_layer: wb_conn;
+        * -  scaler: plane when input is layer, wb_conn if input is compiz.
+        */
+       union {
+               struct drm_crtc *crtc;
+               struct drm_plane *plane;
+               struct drm_connector *wb_conn;
+               void *binding_user;
+       };
+       /**
+        * @active_inputs:
+        *
+        * active_inputs is bitmask of @inputs index
+        *
+        * -  active_inputs = changed_active_inputs + unchanged_active_inputs
+        * -  affected_inputs = old->active_inputs + new->active_inputs;
+        * -  disabling_inputs = affected_inputs ^ active_inputs;
+        * -  changed_inputs = disabling_inputs + changed_active_inputs;
+        *
+        * NOTE:
+        * changed_inputs doesn't include all active_input but only
+        * @changed_active_inputs, and this bitmask can be used in chip
+        * level for dirty update.
+        */
+       u16 active_inputs;
+       u16 changed_active_inputs;
+       u16 affected_inputs;
+       /**
+        * @inputs:
+        *
+        * the specific inputs[i] only valid on BIT(i) has been set in
+        * @active_inputs, if not the inputs[i] is undefined.
+        */
+       struct komeda_component_output inputs[KOMEDA_COMPONENT_N_INPUTS];
+};
+
+static inline u16 component_disabling_inputs(struct komeda_component_state *st)
+{
+       return st->affected_inputs ^ st->active_inputs;
+}
+
+static inline u16 component_changed_inputs(struct komeda_component_state *st)
+{
+       return component_disabling_inputs(st) | st->changed_active_inputs;
+}
+
+#define to_comp(__c)   (((__c) == NULL) ? NULL : &((__c)->base))
+#define to_cpos(__c)   ((struct komeda_component **)&(__c))
+
+/* these structures are going to be filled in in uture patches */
+struct komeda_layer {
+       struct komeda_component base;
+       /* layer specific features and caps */
+       int layer_type; /* RICH, SIMPLE or WB */
+};
+
+struct komeda_layer_state {
+       struct komeda_component_state base;
+       /* layer specific configuration state */
+};
+
+struct komeda_compiz {
+       struct komeda_component base;
+       /* compiz specific features and caps */
+};
+
+struct komeda_compiz_state {
+       struct komeda_component_state base;
+       /* compiz specific configuration state */
+};
+
+struct komeda_scaler {
+       struct komeda_component base;
+       /* scaler features and caps */
+};
+
+struct komeda_scaler_state {
+       struct komeda_component_state base;
+};
+
+struct komeda_improc {
+       struct komeda_component base;
+};
+
+struct komeda_improc_state {
+       struct komeda_component_state base;
+};
+
+/* display timing controller */
+struct komeda_timing_ctrlr {
+       struct komeda_component base;
+};
+
+struct komeda_timing_ctrlr_state {
+       struct komeda_component_state base;
+};
+
+/** struct komeda_pipeline_funcs */
+struct komeda_pipeline_funcs {
+       /* dump_register: Optional, dump registers to seq_file */
+       void (*dump_register)(struct komeda_pipeline *pipe,
+                             struct seq_file *sf);
+};
+
+/**
+ * struct komeda_pipeline
+ *
+ * Represent a complete display pipeline and hold all functional components.
+ */
+struct komeda_pipeline {
+       /** @obj: link pipeline as private obj of drm_atomic_state */
+       struct drm_private_obj obj;
+       /** @mdev: the parent komeda_dev */
+       struct komeda_dev *mdev;
+       /** @pxlclk: pixel clock */
+       struct clk *pxlclk;
+       /** @aclk: AXI clock */
+       struct clk *aclk;
+       /** @id: pipeline id */
+       int id;
+       /** @avail_comps: available components mask of pipeline */
+       u32 avail_comps;
+       int n_layers;
+       struct komeda_layer *layers[KOMEDA_PIPELINE_MAX_LAYERS];
+       int n_scalers;
+       struct komeda_scaler *scalers[KOMEDA_PIPELINE_MAX_SCALERS];
+       struct komeda_compiz *compiz;
+       struct komeda_layer  *wb_layer;
+       struct komeda_improc *improc;
+       struct komeda_timing_ctrlr *ctrlr;
+       struct komeda_pipeline_funcs *funcs; /* private pipeline functions */
+
+       /** @of_node: pipeline dt node */
+       struct device_node *of_node;
+       /** @of_output_port: pipeline output port */
+       struct device_node *of_output_port;
+       /** @of_output_dev: output connector device node */
+       struct device_node *of_output_dev;
+};
+
+/**
+ * struct komeda_pipeline_state
+ *
+ * NOTE:
+ * Unlike the pipeline, pipeline_state doesn’t gather any component_state
+ * into it. It because all component will be managed by drm_atomic_state.
+ */
+struct komeda_pipeline_state {
+       /** @obj: tracking pipeline_state by drm_atomic_state */
+       struct drm_private_state obj;
+       struct komeda_pipeline *pipe;
+       /** @crtc: currently bound crtc */
+       struct drm_crtc *crtc;
+       /**
+        * @active_comps:
+        *
+        * bitmask - BIT(component->id) of active components
+        */
+       u32 active_comps;
+};
+
+#define to_layer(c)    container_of(c, struct komeda_layer, base)
+#define to_compiz(c)   container_of(c, struct komeda_compiz, base)
+#define to_scaler(c)   container_of(c, struct komeda_scaler, base)
+#define to_improc(c)   container_of(c, struct komeda_improc, base)
+#define to_ctrlr(c)    container_of(c, struct komeda_timing_ctrlr, base)
+
+#define to_layer_st(c) container_of(c, struct komeda_layer_state, base)
+#define to_compiz_st(c)        container_of(c, struct komeda_compiz_state, base)
+#define to_scaler_st(c) container_of(c, struct komeda_scaler_state, base)
+#define to_improc_st(c)        container_of(c, struct komeda_improc_state, base)
+#define to_ctrlr_st(c) container_of(c, struct komeda_timing_ctrlr_state, base)
+
+#define priv_to_comp_st(o) container_of(o, struct komeda_component_state, obj)
+#define priv_to_pipe_st(o)  container_of(o, struct komeda_pipeline_state, obj)
+
+/* pipeline APIs */
+struct komeda_pipeline *
+komeda_pipeline_add(struct komeda_dev *mdev, size_t size,
+                   struct komeda_pipeline_funcs *funcs);
+void komeda_pipeline_destroy(struct komeda_dev *mdev,
+                            struct komeda_pipeline *pipe);
+
+struct komeda_component *
+komeda_pipeline_get_component(struct komeda_pipeline *pipe, int id);
+
+/* component APIs */
+struct komeda_component *
+komeda_component_add(struct komeda_pipeline *pipe,
+                    size_t comp_sz, u32 id, u32 hw_id,
+                    struct komeda_component_funcs *funcs,
+                    u8 max_active_inputs, u32 supported_inputs,
+                    u8 max_active_outputs, u32 __iomem *reg,
+                    const char *name_fmt, ...);
+
+void komeda_component_destroy(struct komeda_dev *mdev,
+                             struct komeda_component *c);
+
+#endif /* _KOMEDA_PIPELINE_H_*/
diff --git a/drivers/gpu/drm/arm/display/komeda/komeda_plane.c b/drivers/gpu/drm/arm/display/komeda/komeda_plane.c
new file mode 100644 (file)
index 0000000..0a4953a
--- /dev/null
@@ -0,0 +1,109 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * (C) COPYRIGHT 2018 ARM Limited. All rights reserved.
+ * Author: James.Qian.Wang <james.qian.wang@arm.com>
+ *
+ */
+#include <drm/drm_atomic.h>
+#include <drm/drm_atomic_helper.h>
+#include <drm/drm_plane_helper.h>
+#include "komeda_dev.h"
+#include "komeda_kms.h"
+
+static const struct drm_plane_helper_funcs komeda_plane_helper_funcs = {
+};
+
+static void komeda_plane_destroy(struct drm_plane *plane)
+{
+       drm_plane_cleanup(plane);
+
+       kfree(to_kplane(plane));
+}
+
+static const struct drm_plane_funcs komeda_plane_funcs = {
+};
+
+/* for komeda, which is pipeline can be share between crtcs */
+static u32 get_possible_crtcs(struct komeda_kms_dev *kms,
+                             struct komeda_pipeline *pipe)
+{
+       struct komeda_crtc *crtc;
+       u32 possible_crtcs = 0;
+       int i;
+
+       for (i = 0; i < kms->n_crtcs; i++) {
+               crtc = &kms->crtcs[i];
+
+               if ((pipe == crtc->master) || (pipe == crtc->slave))
+                       possible_crtcs |= BIT(i);
+       }
+
+       return possible_crtcs;
+}
+
+/* use Layer0 as primary */
+static u32 get_plane_type(struct komeda_kms_dev *kms,
+                         struct komeda_component *c)
+{
+       bool is_primary = (c->id == KOMEDA_COMPONENT_LAYER0);
+
+       return is_primary ? DRM_PLANE_TYPE_PRIMARY : DRM_PLANE_TYPE_OVERLAY;
+}
+
+static int komeda_plane_add(struct komeda_kms_dev *kms,
+                           struct komeda_layer *layer)
+{
+       struct komeda_dev *mdev = kms->base.dev_private;
+       struct komeda_component *c = &layer->base;
+       struct komeda_plane *kplane;
+       struct drm_plane *plane;
+       u32 *formats, n_formats = 0;
+       int err;
+
+       kplane = kzalloc(sizeof(*kplane), GFP_KERNEL);
+       if (!kplane)
+               return -ENOMEM;
+
+       plane = &kplane->base;
+       kplane->layer = layer;
+
+       formats = komeda_get_layer_fourcc_list(&mdev->fmt_tbl,
+                                              layer->layer_type, &n_formats);
+
+       err = drm_universal_plane_init(&kms->base, plane,
+                       get_possible_crtcs(kms, c->pipeline),
+                       &komeda_plane_funcs,
+                       formats, n_formats, NULL,
+                       get_plane_type(kms, c),
+                       "%s", c->name);
+
+       komeda_put_fourcc_list(formats);
+
+       if (err)
+               goto cleanup;
+
+       drm_plane_helper_add(plane, &komeda_plane_helper_funcs);
+
+       return 0;
+cleanup:
+       komeda_plane_destroy(plane);
+       return err;
+}
+
+int komeda_kms_add_planes(struct komeda_kms_dev *kms, struct komeda_dev *mdev)
+{
+       struct komeda_pipeline *pipe;
+       int i, j, err;
+
+       for (i = 0; i < mdev->n_pipelines; i++) {
+               pipe = mdev->pipelines[i];
+
+               for (j = 0; j < pipe->n_layers; j++) {
+                       err = komeda_plane_add(kms, pipe->layers[j]);
+                       if (err)
+                               return err;
+               }
+       }
+
+       return 0;
+}
diff --git a/drivers/gpu/drm/arm/display/komeda/komeda_private_obj.c b/drivers/gpu/drm/arm/display/komeda/komeda_private_obj.c
new file mode 100644 (file)
index 0000000..f1c9e3f
--- /dev/null
@@ -0,0 +1,88 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * (C) COPYRIGHT 2018 ARM Limited. All rights reserved.
+ * Author: James.Qian.Wang <james.qian.wang@arm.com>
+ *
+ */
+#include "komeda_dev.h"
+#include "komeda_kms.h"
+
+static struct drm_private_state *
+komeda_pipeline_atomic_duplicate_state(struct drm_private_obj *obj)
+{
+       struct komeda_pipeline_state *st;
+
+       st = kmemdup(obj->state, sizeof(*st), GFP_KERNEL);
+       if (!st)
+               return NULL;
+
+       st->active_comps = 0;
+
+       __drm_atomic_helper_private_obj_duplicate_state(obj, &st->obj);
+
+       return &st->obj;
+}
+
+static void
+komeda_pipeline_atomic_destroy_state(struct drm_private_obj *obj,
+                                    struct drm_private_state *state)
+{
+       kfree(priv_to_pipe_st(state));
+}
+
+static const struct drm_private_state_funcs komeda_pipeline_obj_funcs = {
+       .atomic_duplicate_state = komeda_pipeline_atomic_duplicate_state,
+       .atomic_destroy_state   = komeda_pipeline_atomic_destroy_state,
+};
+
+static int komeda_pipeline_obj_add(struct komeda_kms_dev *kms,
+                                  struct komeda_pipeline *pipe)
+{
+       struct komeda_pipeline_state *st;
+
+       st = kzalloc(sizeof(*st), GFP_KERNEL);
+       if (!st)
+               return -ENOMEM;
+
+       st->pipe = pipe;
+       drm_atomic_private_obj_init(&kms->base, &pipe->obj, &st->obj,
+                                   &komeda_pipeline_obj_funcs);
+
+       return 0;
+}
+
+int komeda_kms_add_private_objs(struct komeda_kms_dev *kms,
+                               struct komeda_dev *mdev)
+{
+       struct komeda_pipeline *pipe;
+       int i, err;
+
+       for (i = 0; i < mdev->n_pipelines; i++) {
+               pipe = mdev->pipelines[i];
+
+               err = komeda_pipeline_obj_add(kms, pipe);
+               if (err)
+                       return err;
+
+               /* Add component */
+       }
+
+       return 0;
+}
+
+void komeda_kms_cleanup_private_objs(struct komeda_dev *mdev)
+{
+       struct komeda_pipeline *pipe;
+       struct komeda_component *c;
+       int i, id;
+
+       for (i = 0; i < mdev->n_pipelines; i++) {
+               pipe = mdev->pipelines[i];
+               dp_for_each_set_bit(id, pipe->avail_comps) {
+                       c = komeda_pipeline_get_component(pipe, id);
+
+                       drm_atomic_private_obj_fini(&c->obj);
+               }
+               drm_atomic_private_obj_fini(&pipe->obj);
+       }
+}
index 91d08a23f125b078dda6abf29a44c86cc80d2914..93a341d278a67406168864bf673c0cb7a5fdc5bf 100644 (file)
@@ -574,6 +574,9 @@ extern "C" {
  * AFBC has several features which may be supported and/or used, which are
  * represented using bits in the modifier. Not all combinations are valid,
  * and different devices or use-cases may support different combinations.
+ *
+ * Further information on the use of AFBC modifiers can be found in
+ * Documentation/gpu/afbc.rst
  */
 #define DRM_FORMAT_MOD_ARM_AFBC(__afbc_mode)   fourcc_mod_code(ARM, __afbc_mode)