diff options
30 files changed, 2998 insertions, 8 deletions
diff --git a/Documentation/devicetree/bindings/display/arm,komeda.txt b/Documentation/devicetree/bindings/display/arm,komeda.txt new file mode 100644 index 000000000000..02b226532ebd --- /dev/null +++ b/Documentation/devicetree/bindings/display/arm,komeda.txt | |||
| @@ -0,0 +1,73 @@ | |||
| 1 | Device Tree bindings for Arm Komeda display driver | ||
| 2 | |||
| 3 | Required properties: | ||
| 4 | - compatible: Should be "arm,mali-d71" | ||
| 5 | - reg: Physical base address and length of the registers in the system | ||
| 6 | - interrupts: the interrupt line number of the device in the system | ||
| 7 | - clocks: A list of phandle + clock-specifier pairs, one for each entry | ||
| 8 | in 'clock-names' | ||
| 9 | - clock-names: A list of clock names. It should contain: | ||
| 10 | - "mclk": for the main processor clock | ||
| 11 | - "pclk": for the APB interface clock | ||
| 12 | - #address-cells: Must be 1 | ||
| 13 | - #size-cells: Must be 0 | ||
| 14 | |||
| 15 | Required properties for sub-node: pipeline@nq | ||
| 16 | Each device contains one or two pipeline sub-nodes (at least one), each | ||
| 17 | pipeline node should provide properties: | ||
| 18 | - reg: Zero-indexed identifier for the pipeline | ||
| 19 | - clocks: A list of phandle + clock-specifier pairs, one for each entry | ||
| 20 | in 'clock-names' | ||
| 21 | - clock-names: should contain: | ||
| 22 | - "pxclk": pixel clock | ||
| 23 | - "aclk": AXI interface clock | ||
| 24 | |||
| 25 | - port: each pipeline connect to an encoder input port. The connection is | ||
| 26 | modeled using the OF graph bindings specified in | ||
| 27 | Documentation/devicetree/bindings/graph.txt | ||
| 28 | |||
| 29 | Optional properties: | ||
| 30 | - memory-region: phandle to a node describing memory (see | ||
| 31 | Documentation/devicetree/bindings/reserved-memory/reserved-memory.txt) | ||
| 32 | to be used for the framebuffer; if not present, the framebuffer may | ||
| 33 | be located anywhere in memory. | ||
| 34 | |||
| 35 | Example: | ||
| 36 | / { | ||
| 37 | ... | ||
| 38 | |||
| 39 | dp0: display@c00000 { | ||
| 40 | #address-cells = <1>; | ||
| 41 | #size-cells = <0>; | ||
| 42 | compatible = "arm,mali-d71"; | ||
| 43 | reg = <0xc00000 0x20000>; | ||
| 44 | interrupts = <0 168 4>; | ||
| 45 | clocks = <&dpu_mclk>, <&dpu_aclk>; | ||
| 46 | clock-names = "mclk", "pclk"; | ||
| 47 | |||
| 48 | dp0_pipe0: pipeline@0 { | ||
| 49 | clocks = <&fpgaosc2>, <&dpu_aclk>; | ||
| 50 | clock-names = "pxclk", "aclk"; | ||
| 51 | reg = <0>; | ||
| 52 | |||
| 53 | port { | ||
| 54 | dp0_pipe0_out: endpoint { | ||
| 55 | remote-endpoint = <&db_dvi0_in>; | ||
| 56 | }; | ||
| 57 | }; | ||
| 58 | }; | ||
| 59 | |||
| 60 | dp0_pipe1: pipeline@1 { | ||
| 61 | clocks = <&fpgaosc2>, <&dpu_aclk>; | ||
| 62 | clock-names = "pxclk", "aclk"; | ||
| 63 | reg = <1>; | ||
| 64 | |||
| 65 | port { | ||
| 66 | dp0_pipe1_out: endpoint { | ||
| 67 | remote-endpoint = <&db_dvi1_in>; | ||
| 68 | }; | ||
| 69 | }; | ||
| 70 | }; | ||
| 71 | }; | ||
| 72 | ... | ||
| 73 | }; | ||
diff --git a/Documentation/gpu/afbc.rst b/Documentation/gpu/afbc.rst new file mode 100644 index 000000000000..4d38dc49d105 --- /dev/null +++ b/Documentation/gpu/afbc.rst | |||
| @@ -0,0 +1,235 @@ | |||
| 1 | .. SPDX-License-Identifier: GPL-2.0+ | ||
| 2 | |||
| 3 | =================================== | ||
| 4 | Arm Framebuffer Compression (AFBC) | ||
| 5 | =================================== | ||
| 6 | |||
| 7 | AFBC is a proprietary lossless image compression protocol and format. | ||
| 8 | It provides fine-grained random access and minimizes the amount of | ||
| 9 | data transferred between IP blocks. | ||
| 10 | |||
| 11 | AFBC can be enabled on drivers which support it via use of the AFBC | ||
| 12 | format modifiers defined in drm_fourcc.h. See DRM_FORMAT_MOD_ARM_AFBC(*). | ||
| 13 | |||
| 14 | All users of the AFBC modifiers must follow the usage guidelines laid | ||
| 15 | out in this document, to ensure compatibility across different AFBC | ||
| 16 | producers and consumers. | ||
| 17 | |||
| 18 | Components and Ordering | ||
| 19 | ======================= | ||
| 20 | |||
| 21 | AFBC streams can contain several components - where a component | ||
| 22 | corresponds to a color channel (i.e. R, G, B, X, A, Y, Cb, Cr). | ||
| 23 | The assignment of input/output color channels must be consistent | ||
| 24 | between the encoder and the decoder for correct operation, otherwise | ||
| 25 | the consumer will interpret the decoded data incorrectly. | ||
| 26 | |||
| 27 | Furthermore, when the lossless colorspace transform is used | ||
| 28 | (AFBC_FORMAT_MOD_YTR, which should be enabled for RGB buffers for | ||
| 29 | maximum compression efficiency), the component order must be: | ||
| 30 | |||
| 31 | * Component 0: R | ||
| 32 | * Component 1: G | ||
| 33 | * Component 2: B | ||
| 34 | |||
| 35 | The component ordering is communicated via the fourcc code in the | ||
| 36 | fourcc:modifier pair. In general, component '0' is considered to | ||
| 37 | reside in the least-significant bits of the corresponding linear | ||
| 38 | format. For example, COMP(bits): | ||
| 39 | |||
| 40 | * DRM_FORMAT_ABGR8888 | ||
| 41 | |||
| 42 | * Component 0: R(8) | ||
| 43 | * Component 1: G(8) | ||
| 44 | * Component 2: B(8) | ||
| 45 | * Component 3: A(8) | ||
| 46 | |||
| 47 | * DRM_FORMAT_BGR888 | ||
| 48 | |||
| 49 | * Component 0: R(8) | ||
| 50 | * Component 1: G(8) | ||
| 51 | * Component 2: B(8) | ||
| 52 | |||
| 53 | * DRM_FORMAT_YUYV | ||
| 54 | |||
| 55 | * Component 0: Y(8) | ||
| 56 | * Component 1: Cb(8, 2x1 subsampled) | ||
| 57 | * Component 2: Cr(8, 2x1 subsampled) | ||
| 58 | |||
| 59 | In AFBC, 'X' components are not treated any differently from any other | ||
| 60 | component. Therefore, an AFBC buffer with fourcc DRM_FORMAT_XBGR8888 | ||
| 61 | encodes with 4 components, like so: | ||
| 62 | |||
| 63 | * DRM_FORMAT_XBGR8888 | ||
| 64 | |||
| 65 | * Component 0: R(8) | ||
| 66 | * Component 1: G(8) | ||
| 67 | * Component 2: B(8) | ||
| 68 | * Component 3: X(8) | ||
| 69 | |||
| 70 | Please note, however, that the inclusion of a "wasted" 'X' channel is | ||
| 71 | bad for compression efficiency, and so it's recommended to avoid | ||
| 72 | formats containing 'X' bits. If a fourth component is | ||
| 73 | required/expected by the encoder/decoder, then it is recommended to | ||
| 74 | instead use an equivalent format with alpha, setting all alpha bits to | ||
| 75 | '1'. If there is no requirement for a fourth component, then a format | ||
| 76 | which doesn't include alpha can be used, e.g. DRM_FORMAT_BGR888. | ||
| 77 | |||
| 78 | Number of Planes | ||
| 79 | ================ | ||
| 80 | |||
| 81 | Formats which are typically multi-planar in linear layouts (e.g. YUV | ||
| 82 | 420), can be encoded into one, or multiple, AFBC planes. As with | ||
| 83 | component order, the encoder and decoder must agree about the number | ||
| 84 | of planes in order to correctly decode the buffer. The fourcc code is | ||
| 85 | used to determine the number of encoded planes in an AFBC buffer, | ||
| 86 | matching the number of planes for the linear (unmodified) format. | ||
| 87 | Within each plane, the component ordering also follows the fourcc | ||
| 88 | code: | ||
| 89 | |||
| 90 | For example: | ||
| 91 | |||
| 92 | * DRM_FORMAT_YUYV: nplanes = 1 | ||
| 93 | |||
| 94 | * Plane 0: | ||
| 95 | |||
| 96 | * Component 0: Y(8) | ||
| 97 | * Component 1: Cb(8, 2x1 subsampled) | ||
| 98 | * Component 2: Cr(8, 2x1 subsampled) | ||
| 99 | |||
| 100 | * DRM_FORMAT_NV12: nplanes = 2 | ||
| 101 | |||
| 102 | * Plane 0: | ||
| 103 | |||
| 104 | * Component 0: Y(8) | ||
| 105 | |||
| 106 | * Plane 1: | ||
| 107 | |||
| 108 | * Component 0: Cb(8, 2x1 subsampled) | ||
| 109 | * Component 1: Cr(8, 2x1 subsampled) | ||
| 110 | |||
| 111 | Cross-device interoperability | ||
| 112 | ============================= | ||
| 113 | |||
| 114 | For maximum compatibility across devices, the table below defines | ||
| 115 | canonical formats for use between AFBC-enabled devices. Formats which | ||
| 116 | are listed here must be used exactly as specified when using the AFBC | ||
| 117 | modifiers. Formats which are not listed should be avoided. | ||
| 118 | |||
| 119 | .. flat-table:: AFBC formats | ||
| 120 | |||
| 121 | * - Fourcc code | ||
| 122 | - Description | ||
| 123 | - Planes/Components | ||
| 124 | |||
| 125 | * - DRM_FORMAT_ABGR2101010 | ||
| 126 | - 10-bit per component RGB, with 2-bit alpha | ||
| 127 | - Plane 0: 4 components | ||
| 128 | * Component 0: R(10) | ||
| 129 | * Component 1: G(10) | ||
| 130 | * Component 2: B(10) | ||
| 131 | * Component 3: A(2) | ||
| 132 | |||
| 133 | * - DRM_FORMAT_ABGR8888 | ||
| 134 | - 8-bit per component RGB, with 8-bit alpha | ||
| 135 | - Plane 0: 4 components | ||
| 136 | * Component 0: R(8) | ||
| 137 | * Component 1: G(8) | ||
| 138 | * Component 2: B(8) | ||
| 139 | * Component 3: A(8) | ||
| 140 | |||
| 141 | * - DRM_FORMAT_BGR888 | ||
| 142 | - 8-bit per component RGB | ||
| 143 | - Plane 0: 3 components | ||
| 144 | * Component 0: R(8) | ||
| 145 | * Component 1: G(8) | ||
| 146 | * Component 2: B(8) | ||
| 147 | |||
| 148 | * - DRM_FORMAT_BGR565 | ||
| 149 | - 5/6-bit per component RGB | ||
| 150 | - Plane 0: 3 components | ||
| 151 | * Component 0: R(5) | ||
| 152 | * Component 1: G(6) | ||
| 153 | * Component 2: B(5) | ||
| 154 | |||
| 155 | * - DRM_FORMAT_ABGR1555 | ||
| 156 | - 5-bit per component RGB, with 1-bit alpha | ||
| 157 | - Plane 0: 4 components | ||
| 158 | * Component 0: R(5) | ||
| 159 | * Component 1: G(5) | ||
| 160 | * Component 2: B(5) | ||
| 161 | * Component 3: A(1) | ||
| 162 | |||
| 163 | * - DRM_FORMAT_VUY888 | ||
| 164 | - 8-bit per component YCbCr 444, single plane | ||
| 165 | - Plane 0: 3 components | ||
| 166 | * Component 0: Y(8) | ||
| 167 | * Component 1: Cb(8) | ||
| 168 | * Component 2: Cr(8) | ||
| 169 | |||
| 170 | * - DRM_FORMAT_VUY101010 | ||
| 171 | - 10-bit per component YCbCr 444, single plane | ||
| 172 | - Plane 0: 3 components | ||
| 173 | * Component 0: Y(10) | ||
| 174 | * Component 1: Cb(10) | ||
| 175 | * Component 2: Cr(10) | ||
| 176 | |||
| 177 | * - DRM_FORMAT_YUYV | ||
| 178 | - 8-bit per component YCbCr 422, single plane | ||
| 179 | - Plane 0: 3 components | ||
| 180 | * Component 0: Y(8) | ||
| 181 | * Component 1: Cb(8, 2x1 subsampled) | ||
| 182 | * Component 2: Cr(8, 2x1 subsampled) | ||
| 183 | |||
| 184 | * - DRM_FORMAT_NV16 | ||
| 185 | - 8-bit per component YCbCr 422, two plane | ||
| 186 | - Plane 0: 1 component | ||
| 187 | * Component 0: Y(8) | ||
| 188 | Plane 1: 2 components | ||
| 189 | * Component 0: Cb(8, 2x1 subsampled) | ||
| 190 | * Component 1: Cr(8, 2x1 subsampled) | ||
| 191 | |||
| 192 | * - DRM_FORMAT_Y210 | ||
| 193 | - 10-bit per component YCbCr 422, single plane | ||
| 194 | - Plane 0: 3 components | ||
| 195 | * Component 0: Y(10) | ||
| 196 | * Component 1: Cb(10, 2x1 subsampled) | ||
| 197 | * Component 2: Cr(10, 2x1 subsampled) | ||
| 198 | |||
| 199 | * - DRM_FORMAT_P210 | ||
| 200 | - 10-bit per component YCbCr 422, two plane | ||
| 201 | - Plane 0: 1 component | ||
| 202 | * Component 0: Y(10) | ||
| 203 | Plane 1: 2 components | ||
| 204 | * Component 0: Cb(10, 2x1 subsampled) | ||
| 205 | * Component 1: Cr(10, 2x1 subsampled) | ||
| 206 | |||
| 207 | * - DRM_FORMAT_YUV420_8BIT | ||
| 208 | - 8-bit per component YCbCr 420, single plane | ||
| 209 | - Plane 0: 3 components | ||
| 210 | * Component 0: Y(8) | ||
| 211 | * Component 1: Cb(8, 2x2 subsampled) | ||
| 212 | * Component 2: Cr(8, 2x2 subsampled) | ||
| 213 | |||
| 214 | * - DRM_FORMAT_YUV420_10BIT | ||
| 215 | - 10-bit per component YCbCr 420, single plane | ||
| 216 | - Plane 0: 3 components | ||
| 217 | * Component 0: Y(10) | ||
| 218 | * Component 1: Cb(10, 2x2 subsampled) | ||
| 219 | * Component 2: Cr(10, 2x2 subsampled) | ||
| 220 | |||
| 221 | * - DRM_FORMAT_NV12 | ||
| 222 | - 8-bit per component YCbCr 420, two plane | ||
| 223 | - Plane 0: 1 component | ||
| 224 | * Component 0: Y(8) | ||
| 225 | Plane 1: 2 components | ||
| 226 | * Component 0: Cb(8, 2x2 subsampled) | ||
| 227 | * Component 1: Cr(8, 2x2 subsampled) | ||
| 228 | |||
| 229 | * - DRM_FORMAT_P010 | ||
| 230 | - 10-bit per component YCbCr 420, two plane | ||
| 231 | - Plane 0: 1 component | ||
| 232 | * Component 0: Y(10) | ||
| 233 | Plane 1: 2 components | ||
| 234 | * Component 0: Cb(10, 2x2 subsampled) | ||
| 235 | * Component 1: Cr(10, 2x2 subsampled) | ||
diff --git a/Documentation/gpu/drivers.rst b/Documentation/gpu/drivers.rst index 7c1672118a73..044a7025477c 100644 --- a/Documentation/gpu/drivers.rst +++ b/Documentation/gpu/drivers.rst | |||
| @@ -17,6 +17,8 @@ GPU Driver Documentation | |||
| 17 | vkms | 17 | vkms |
| 18 | bridge/dw-hdmi | 18 | bridge/dw-hdmi |
| 19 | xen-front | 19 | xen-front |
| 20 | afbc | ||
| 21 | komeda-kms | ||
| 20 | 22 | ||
| 21 | .. only:: subproject and html | 23 | .. only:: subproject and html |
| 22 | 24 | ||
diff --git a/Documentation/gpu/komeda-kms.rst b/Documentation/gpu/komeda-kms.rst new file mode 100644 index 000000000000..b08da1cffecc --- /dev/null +++ b/Documentation/gpu/komeda-kms.rst | |||
| @@ -0,0 +1,488 @@ | |||
| 1 | .. SPDX-License-Identifier: GPL-2.0 | ||
| 2 | |||
| 3 | ============================== | ||
| 4 | drm/komeda Arm display driver | ||
| 5 | ============================== | ||
| 6 | |||
| 7 | The drm/komeda driver supports the Arm display processor D71 and later products, | ||
| 8 | this document gives a brief overview of driver design: how it works and why | ||
| 9 | design it like that. | ||
| 10 | |||
| 11 | Overview of D71 like display IPs | ||
| 12 | ================================ | ||
| 13 | |||
| 14 | From D71, Arm display IP begins to adopt a flexible and modularized | ||
| 15 | architecture. A display pipeline is made up of multiple individual and | ||
| 16 | functional pipeline stages called components, and every component has some | ||
| 17 | specific capabilities that can give the flowed pipeline pixel data a | ||
| 18 | particular processing. | ||
| 19 | |||
| 20 | Typical D71 components: | ||
| 21 | |||
| 22 | Layer | ||
| 23 | ----- | ||
| 24 | Layer is the first pipeline stage, which prepares the pixel data for the next | ||
| 25 | stage. It fetches the pixel from memory, decodes it if it's AFBC, rotates the | ||
| 26 | source image, unpacks or converts YUV pixels to the device internal RGB pixels, | ||
| 27 | then adjusts the color_space of pixels if needed. | ||
| 28 | |||
| 29 | Scaler | ||
| 30 | ------ | ||
| 31 | As its name suggests, scaler takes responsibility for scaling, and D71 also | ||
| 32 | supports image enhancements by scaler. | ||
| 33 | The usage of scaler is very flexible and can be connected to layer output | ||
| 34 | for layer scaling, or connected to compositor and scale the whole display | ||
| 35 | frame and then feed the output data into wb_layer which will then write it | ||
| 36 | into memory. | ||
| 37 | |||
| 38 | Compositor (compiz) | ||
| 39 | ------------------- | ||
| 40 | Compositor blends multiple layers or pixel data flows into one single display | ||
| 41 | frame. its output frame can be fed into post image processor for showing it on | ||
| 42 | the monitor or fed into wb_layer and written to memory at the same time. | ||
| 43 | user can also insert a scaler between compositor and wb_layer to down scale | ||
| 44 | the display frame first and and then write to memory. | ||
| 45 | |||
| 46 | Writeback Layer (wb_layer) | ||
| 47 | -------------------------- | ||
| 48 | Writeback layer does the opposite things of Layer, which connects to compiz | ||
| 49 | and writes the composition result to memory. | ||
| 50 | |||
| 51 | Post image processor (improc) | ||
| 52 | ----------------------------- | ||
| 53 | Post image processor adjusts frame data like gamma and color space to fit the | ||
| 54 | requirements of the monitor. | ||
| 55 | |||
| 56 | Timing controller (timing_ctrlr) | ||
| 57 | -------------------------------- | ||
| 58 | Final stage of display pipeline, Timing controller is not for the pixel | ||
| 59 | handling, but only for controlling the display timing. | ||
| 60 | |||
| 61 | Merger | ||
| 62 | ------ | ||
| 63 | D71 scaler mostly only has the half horizontal input/output capabilities | ||
| 64 | compared with Layer, like if Layer supports 4K input size, the scaler only can | ||
| 65 | support 2K input/output in the same time. To achieve the ful frame scaling, D71 | ||
| 66 | introduces Layer Split, which splits the whole image to two half parts and feeds | ||
| 67 | them to two Layers A and B, and does the scaling independently. After scaling | ||
| 68 | the result need to be fed to merger to merge two part images together, and then | ||
| 69 | output merged result to compiz. | ||
| 70 | |||
| 71 | Splitter | ||
| 72 | -------- | ||
| 73 | Similar to Layer Split, but Splitter is used for writeback, which splits the | ||
| 74 | compiz result to two parts and then feed them to two scalers. | ||
| 75 | |||
| 76 | Possible D71 Pipeline usage | ||
| 77 | =========================== | ||
| 78 | |||
| 79 | Benefitting from the modularized architecture, D71 pipelines can be easily | ||
| 80 | adjusted to fit different usages. And D71 has two pipelines, which support two | ||
| 81 | types of working mode: | ||
| 82 | |||
| 83 | - Dual display mode | ||
| 84 | Two pipelines work independently and separately to drive two display outputs. | ||
| 85 | |||
| 86 | - Single display mode | ||
| 87 | Two pipelines work together to drive only one display output. | ||
| 88 | |||
| 89 | On this mode, pipeline_B doesn't work indenpendently, but outputs its | ||
| 90 | composition result into pipeline_A, and its pixel timing also derived from | ||
| 91 | pipeline_A.timing_ctrlr. The pipeline_B works just like a "slave" of | ||
| 92 | pipeline_A(master) | ||
| 93 | |||
| 94 | Single pipeline data flow | ||
| 95 | ------------------------- | ||
| 96 | |||
| 97 | .. kernel-render:: DOT | ||
| 98 | :alt: Single pipeline digraph | ||
| 99 | :caption: Single pipeline data flow | ||
| 100 | |||
| 101 | digraph single_ppl { | ||
| 102 | rankdir=LR; | ||
| 103 | |||
| 104 | subgraph { | ||
| 105 | "Memory"; | ||
| 106 | "Monitor"; | ||
| 107 | } | ||
| 108 | |||
| 109 | subgraph cluster_pipeline { | ||
| 110 | style=dashed | ||
| 111 | node [shape=box] | ||
| 112 | { | ||
| 113 | node [bgcolor=grey style=dashed] | ||
| 114 | "Scaler-0"; | ||
| 115 | "Scaler-1"; | ||
| 116 | "Scaler-0/1" | ||
| 117 | } | ||
| 118 | |||
| 119 | node [bgcolor=grey style=filled] | ||
| 120 | "Layer-0" -> "Scaler-0" | ||
| 121 | "Layer-1" -> "Scaler-0" | ||
| 122 | "Layer-2" -> "Scaler-1" | ||
| 123 | "Layer-3" -> "Scaler-1" | ||
| 124 | |||
| 125 | "Layer-0" -> "Compiz" | ||
| 126 | "Layer-1" -> "Compiz" | ||
| 127 | "Layer-2" -> "Compiz" | ||
| 128 | "Layer-3" -> "Compiz" | ||
| 129 | "Scaler-0" -> "Compiz" | ||
| 130 | "Scaler-1" -> "Compiz" | ||
| 131 | |||
| 132 | "Compiz" -> "Scaler-0/1" -> "Wb_layer" | ||
| 133 | "Compiz" -> "Improc" -> "Timing Controller" | ||
| 134 | } | ||
| 135 | |||
| 136 | "Wb_layer" -> "Memory" | ||
| 137 | "Timing Controller" -> "Monitor" | ||
| 138 | } | ||
| 139 | |||
| 140 | Dual pipeline with Slave enabled | ||
| 141 | -------------------------------- | ||
| 142 | |||
| 143 | .. kernel-render:: DOT | ||
| 144 | :alt: Slave pipeline digraph | ||
| 145 | :caption: Slave pipeline enabled data flow | ||
| 146 | |||
| 147 | digraph slave_ppl { | ||
| 148 | rankdir=LR; | ||
| 149 | |||
| 150 | subgraph { | ||
| 151 | "Memory"; | ||
| 152 | "Monitor"; | ||
| 153 | } | ||
| 154 | node [shape=box] | ||
| 155 | subgraph cluster_pipeline_slave { | ||
| 156 | style=dashed | ||
| 157 | label="Slave Pipeline_B" | ||
| 158 | node [shape=box] | ||
| 159 | { | ||
| 160 | node [bgcolor=grey style=dashed] | ||
| 161 | "Slave.Scaler-0"; | ||
| 162 | "Slave.Scaler-1"; | ||
| 163 | } | ||
| 164 | |||
| 165 | node [bgcolor=grey style=filled] | ||
| 166 | "Slave.Layer-0" -> "Slave.Scaler-0" | ||
| 167 | "Slave.Layer-1" -> "Slave.Scaler-0" | ||
| 168 | "Slave.Layer-2" -> "Slave.Scaler-1" | ||
| 169 | "Slave.Layer-3" -> "Slave.Scaler-1" | ||
| 170 | |||
| 171 | "Slave.Layer-0" -> "Slave.Compiz" | ||
| 172 | "Slave.Layer-1" -> "Slave.Compiz" | ||
| 173 | "Slave.Layer-2" -> "Slave.Compiz" | ||
| 174 | "Slave.Layer-3" -> "Slave.Compiz" | ||
| 175 | "Slave.Scaler-0" -> "Slave.Compiz" | ||
| 176 | "Slave.Scaler-1" -> "Slave.Compiz" | ||
| 177 | } | ||
| 178 | |||
| 179 | subgraph cluster_pipeline_master { | ||
| 180 | style=dashed | ||
| 181 | label="Master Pipeline_A" | ||
| 182 | node [shape=box] | ||
| 183 | { | ||
| 184 | node [bgcolor=grey style=dashed] | ||
| 185 | "Scaler-0"; | ||
| 186 | "Scaler-1"; | ||
| 187 | "Scaler-0/1" | ||
| 188 | } | ||
| 189 | |||
| 190 | node [bgcolor=grey style=filled] | ||
| 191 | "Layer-0" -> "Scaler-0" | ||
| 192 | "Layer-1" -> "Scaler-0" | ||
| 193 | "Layer-2" -> "Scaler-1" | ||
| 194 | "Layer-3" -> "Scaler-1" | ||
| 195 | |||
| 196 | "Slave.Compiz" -> "Compiz" | ||
| 197 | "Layer-0" -> "Compiz" | ||
| 198 | "Layer-1" -> "Compiz" | ||
| 199 | "Layer-2" -> "Compiz" | ||
| 200 | "Layer-3" -> "Compiz" | ||
| 201 | "Scaler-0" -> "Compiz" | ||
| 202 | "Scaler-1" -> "Compiz" | ||
| 203 | |||
| 204 | "Compiz" -> "Scaler-0/1" -> "Wb_layer" | ||
| 205 | "Compiz" -> "Improc" -> "Timing Controller" | ||
| 206 | } | ||
| 207 | |||
| 208 | "Wb_layer" -> "Memory" | ||
| 209 | "Timing Controller" -> "Monitor" | ||
| 210 | } | ||
| 211 | |||
| 212 | Sub-pipelines for input and output | ||
| 213 | ---------------------------------- | ||
| 214 | |||
| 215 | A complete display pipeline can be easily divided into three sub-pipelines | ||
| 216 | according to the in/out usage. | ||
| 217 | |||
| 218 | Layer(input) pipeline | ||
| 219 | ~~~~~~~~~~~~~~~~~~~~~ | ||
| 220 | |||
| 221 | .. kernel-render:: DOT | ||
| 222 | :alt: Layer data digraph | ||
| 223 | :caption: Layer (input) data flow | ||
| 224 | |||
| 225 | digraph layer_data_flow { | ||
| 226 | rankdir=LR; | ||
| 227 | node [shape=box] | ||
| 228 | |||
| 229 | { | ||
| 230 | node [bgcolor=grey style=dashed] | ||
| 231 | "Scaler-n"; | ||
| 232 | } | ||
| 233 | |||
| 234 | "Layer-n" -> "Scaler-n" -> "Compiz" | ||
| 235 | } | ||
| 236 | |||
| 237 | .. kernel-render:: DOT | ||
| 238 | :alt: Layer Split digraph | ||
| 239 | :caption: Layer Split pipeline | ||
| 240 | |||
| 241 | digraph layer_data_flow { | ||
| 242 | rankdir=LR; | ||
| 243 | node [shape=box] | ||
| 244 | |||
| 245 | "Layer-0/1" -> "Scaler-0" -> "Merger" | ||
| 246 | "Layer-2/3" -> "Scaler-1" -> "Merger" | ||
| 247 | "Merger" -> "Compiz" | ||
| 248 | } | ||
| 249 | |||
| 250 | Writeback(output) pipeline | ||
| 251 | ~~~~~~~~~~~~~~~~~~~~~~~~~~ | ||
| 252 | .. kernel-render:: DOT | ||
| 253 | :alt: writeback digraph | ||
| 254 | :caption: Writeback(output) data flow | ||
| 255 | |||
| 256 | digraph writeback_data_flow { | ||
| 257 | rankdir=LR; | ||
| 258 | node [shape=box] | ||
| 259 | |||
| 260 | { | ||
| 261 | node [bgcolor=grey style=dashed] | ||
| 262 | "Scaler-n"; | ||
| 263 | } | ||
| 264 | |||
| 265 | "Compiz" -> "Scaler-n" -> "Wb_layer" | ||
| 266 | } | ||
| 267 | |||
| 268 | .. kernel-render:: DOT | ||
| 269 | :alt: split writeback digraph | ||
| 270 | :caption: Writeback(output) Split data flow | ||
| 271 | |||
| 272 | digraph writeback_data_flow { | ||
| 273 | rankdir=LR; | ||
| 274 | node [shape=box] | ||
| 275 | |||
| 276 | "Compiz" -> "Splitter" | ||
| 277 | "Splitter" -> "Scaler-0" -> "Merger" | ||
| 278 | "Splitter" -> "Scaler-1" -> "Merger" | ||
| 279 | "Merger" -> "Wb_layer" | ||
| 280 | } | ||
| 281 | |||
| 282 | Display output pipeline | ||
| 283 | ~~~~~~~~~~~~~~~~~~~~~~~ | ||
| 284 | .. kernel-render:: DOT | ||
| 285 | :alt: display digraph | ||
| 286 | :caption: display output data flow | ||
| 287 | |||
| 288 | digraph single_ppl { | ||
| 289 | rankdir=LR; | ||
| 290 | node [shape=box] | ||
| 291 | |||
| 292 | "Compiz" -> "Improc" -> "Timing Controller" | ||
| 293 | } | ||
| 294 | |||
| 295 | In the following section we'll see these three sub-pipelines will be handled | ||
| 296 | by KMS-plane/wb_conn/crtc respectively. | ||
| 297 | |||
| 298 | Komeda Resource abstraction | ||
| 299 | =========================== | ||
| 300 | |||
| 301 | struct komeda_pipeline/component | ||
| 302 | -------------------------------- | ||
| 303 | |||
| 304 | To fully utilize and easily access/configure the HW, the driver side also uses | ||
| 305 | a similar architecture: Pipeline/Component to describe the HW features and | ||
| 306 | capabilities, and a specific component includes two parts: | ||
| 307 | |||
| 308 | - Data flow controlling. | ||
| 309 | - Specific component capabilities and features. | ||
| 310 | |||
| 311 | So the driver defines a common header struct komeda_component to describe the | ||
| 312 | data flow control and all specific components are a subclass of this base | ||
| 313 | structure. | ||
| 314 | |||
| 315 | .. kernel-doc:: drivers/gpu/drm/arm/display/komeda/komeda_pipeline.h | ||
| 316 | :internal: | ||
| 317 | |||
| 318 | Resource discovery and initialization | ||
| 319 | ===================================== | ||
| 320 | |||
| 321 | Pipeline and component are used to describe how to handle the pixel data. We | ||
| 322 | still need a @struct komeda_dev to describe the whole view of the device, and | ||
| 323 | the control-abilites of device. | ||
| 324 | |||
| 325 | We have &komeda_dev, &komeda_pipeline, &komeda_component. Now fill devices with | ||
| 326 | pipelines. Since komeda is not for D71 only but also intended for later products, | ||
| 327 | of course we’d better share as much as possible between different products. To | ||
| 328 | achieve this, split the komeda device into two layers: CORE and CHIP. | ||
| 329 | |||
| 330 | - CORE: for common features and capabilities handling. | ||
| 331 | - CHIP: for register programing and HW specific feature (limitation) handling. | ||
| 332 | |||
| 333 | CORE can access CHIP by three chip function structures: | ||
| 334 | |||
| 335 | - struct komeda_dev_funcs | ||
| 336 | - struct komeda_pipeline_funcs | ||
| 337 | - struct komeda_component_funcs | ||
| 338 | |||
| 339 | .. kernel-doc:: drivers/gpu/drm/arm/display/komeda/komeda_dev.h | ||
| 340 | :internal: | ||
| 341 | |||
| 342 | Format handling | ||
| 343 | =============== | ||
| 344 | |||
| 345 | .. kernel-doc:: drivers/gpu/drm/arm/display/komeda/komeda_format_caps.h | ||
| 346 | :internal: | ||
| 347 | .. kernel-doc:: drivers/gpu/drm/arm/display/komeda/komeda_framebuffer.h | ||
| 348 | :internal: | ||
| 349 | |||
| 350 | Attach komeda_dev to DRM-KMS | ||
| 351 | ============================ | ||
| 352 | |||
| 353 | Komeda abstracts resources by pipeline/component, but DRM-KMS uses | ||
| 354 | crtc/plane/connector. One KMS-obj cannot represent only one single component, | ||
| 355 | since the requirements of a single KMS object cannot simply be achieved by a | ||
| 356 | single component, usually that needs multiple components to fit the requirement. | ||
| 357 | Like set mode, gamma, ctm for KMS all target on CRTC-obj, but komeda needs | ||
| 358 | compiz, improc and timing_ctrlr to work together to fit these requirements. | ||
| 359 | And a KMS-Plane may require multiple komeda resources: layer/scaler/compiz. | ||
| 360 | |||
| 361 | So, one KMS-Obj represents a sub-pipeline of komeda resources. | ||
| 362 | |||
| 363 | - Plane: `Layer(input) pipeline`_ | ||
| 364 | - Wb_connector: `Writeback(output) pipeline`_ | ||
| 365 | - Crtc: `Display output pipeline`_ | ||
| 366 | |||
| 367 | So, for komeda, we treat KMS crtc/plane/connector as users of pipeline and | ||
| 368 | component, and at any one time a pipeline/component only can be used by one | ||
| 369 | user. And pipeline/component will be treated as private object of DRM-KMS; the | ||
| 370 | state will be managed by drm_atomic_state as well. | ||
| 371 | |||
| 372 | How to map plane to Layer(input) pipeline | ||
| 373 | ----------------------------------------- | ||
| 374 | |||
| 375 | Komeda has multiple Layer input pipelines, see: | ||
| 376 | - `Single pipeline data flow`_ | ||
| 377 | - `Dual pipeline with Slave enabled`_ | ||
| 378 | |||
| 379 | The easiest way is binding a plane to a fixed Layer pipeline, but consider the | ||
| 380 | komeda capabilities: | ||
| 381 | |||
| 382 | - Layer Split, See `Layer(input) pipeline`_ | ||
| 383 | |||
| 384 | Layer_Split is quite complicated feature, which splits a big image into two | ||
| 385 | parts and handles it by two layers and two scalers individually. But it | ||
| 386 | imports an edge problem or effect in the middle of the image after the split. | ||
| 387 | To avoid such a problem, it needs a complicated Split calculation and some | ||
| 388 | special configurations to the layer and scaler. We'd better hide such HW | ||
| 389 | related complexity to user mode. | ||
| 390 | |||
| 391 | - Slave pipeline, See `Dual pipeline with Slave enabled`_ | ||
| 392 | |||
| 393 | Since the compiz component doesn't output alpha value, the slave pipeline | ||
| 394 | only can be used for bottom layers composition. The komeda driver wants to | ||
| 395 | hide this limitation to the user. The way to do this is to pick a suitable | ||
| 396 | Layer according to plane_state->zpos. | ||
| 397 | |||
| 398 | So for komeda, the KMS-plane doesn't represent a fixed komeda layer pipeline, | ||
| 399 | but multiple Layers with same capabilities. Komeda will select one or more | ||
| 400 | Layers to fit the requirement of one KMS-plane. | ||
| 401 | |||
| 402 | Make component/pipeline to be drm_private_obj | ||
| 403 | --------------------------------------------- | ||
| 404 | |||
| 405 | Add :c:type:`drm_private_obj` to :c:type:`komeda_component`, :c:type:`komeda_pipeline` | ||
| 406 | |||
| 407 | .. code-block:: c | ||
| 408 | |||
| 409 | struct komeda_component { | ||
| 410 | struct drm_private_obj obj; | ||
| 411 | ... | ||
| 412 | } | ||
| 413 | |||
| 414 | struct komeda_pipeline { | ||
| 415 | struct drm_private_obj obj; | ||
| 416 | ... | ||
| 417 | } | ||
| 418 | |||
| 419 | Tracking component_state/pipeline_state by drm_atomic_state | ||
| 420 | ----------------------------------------------------------- | ||
| 421 | |||
| 422 | Add :c:type:`drm_private_state` and user to :c:type:`komeda_component_state`, | ||
| 423 | :c:type:`komeda_pipeline_state` | ||
| 424 | |||
| 425 | .. code-block:: c | ||
| 426 | |||
| 427 | struct komeda_component_state { | ||
| 428 | struct drm_private_state obj; | ||
| 429 | void *binding_user; | ||
| 430 | ... | ||
| 431 | } | ||
| 432 | |||
| 433 | struct komeda_pipeline_state { | ||
| 434 | struct drm_private_state obj; | ||
| 435 | struct drm_crtc *crtc; | ||
| 436 | ... | ||
| 437 | } | ||
| 438 | |||
| 439 | komeda component validation | ||
| 440 | --------------------------- | ||
| 441 | |||
| 442 | Komeda has multiple types of components, but the process of validation are | ||
| 443 | similar, usually including the following steps: | ||
| 444 | |||
| 445 | .. code-block:: c | ||
| 446 | |||
| 447 | int komeda_xxxx_validate(struct komeda_component_xxx xxx_comp, | ||
| 448 | struct komeda_component_output *input_dflow, | ||
| 449 | struct drm_plane/crtc/connector *user, | ||
| 450 | struct drm_plane/crtc/connector_state, *user_state) | ||
| 451 | { | ||
| 452 | setup 1: check if component is needed, like the scaler is optional depending | ||
| 453 | on the user_state; if unneeded, just return, and the caller will | ||
| 454 | put the data flow into next stage. | ||
| 455 | Setup 2: check user_state with component features and capabilities to see | ||
| 456 | if requirements can be met; if not, return fail. | ||
| 457 | Setup 3: get component_state from drm_atomic_state, and try set to set | ||
| 458 | user to component; fail if component has been assigned to another | ||
| 459 | user already. | ||
| 460 | Setup 3: configure the component_state, like set its input component, | ||
| 461 | convert user_state to component specific state. | ||
| 462 | Setup 4: adjust the input_dflow and prepare it for the next stage. | ||
| 463 | } | ||
| 464 | |||
| 465 | komeda_kms Abstraction | ||
| 466 | ---------------------- | ||
| 467 | |||
| 468 | .. kernel-doc:: drivers/gpu/drm/arm/display/komeda/komeda_kms.h | ||
| 469 | :internal: | ||
| 470 | |||
| 471 | komde_kms Functions | ||
| 472 | ------------------- | ||
| 473 | .. kernel-doc:: drivers/gpu/drm/arm/display/komeda/komeda_crtc.c | ||
| 474 | :internal: | ||
| 475 | .. kernel-doc:: drivers/gpu/drm/arm/display/komeda/komeda_plane.c | ||
| 476 | :internal: | ||
| 477 | |||
| 478 | Build komeda to be a Linux module driver | ||
| 479 | ======================================== | ||
| 480 | |||
| 481 | Now we have two level devices: | ||
| 482 | |||
| 483 | - komeda_dev: describes the real display hardware. | ||
| 484 | - komeda_kms_dev: attachs or connects komeda_dev to DRM-KMS. | ||
| 485 | |||
| 486 | All komeda operations are supplied or operated by komeda_dev or komeda_kms_dev, | ||
| 487 | the module driver is only a simple wrapper to pass the Linux command | ||
| 488 | (probe/remove/pm) into komeda_dev or komeda_kms_dev. | ||
diff --git a/MAINTAINERS b/MAINTAINERS index 6e1cef2f21d9..4b752571fe03 100644 --- a/MAINTAINERS +++ b/MAINTAINERS | |||
| @@ -1133,13 +1133,26 @@ S: Supported | |||
| 1133 | F: drivers/gpu/drm/arm/hdlcd_* | 1133 | F: drivers/gpu/drm/arm/hdlcd_* |
| 1134 | F: Documentation/devicetree/bindings/display/arm,hdlcd.txt | 1134 | F: Documentation/devicetree/bindings/display/arm,hdlcd.txt |
| 1135 | 1135 | ||
| 1136 | ARM KOMEDA DRM-KMS DRIVER | ||
| 1137 | M: James (Qian) Wang <james.qian.wang@arm.com> | ||
| 1138 | M: Liviu Dudau <liviu.dudau@arm.com> | ||
| 1139 | L: Mali DP Maintainers <malidp@foss.arm.com> | ||
| 1140 | S: Supported | ||
| 1141 | T: git git://linux-arm.org/linux-ld.git for-upstream/mali-dp | ||
| 1142 | F: drivers/gpu/drm/arm/display/include/ | ||
| 1143 | F: drivers/gpu/drm/arm/display/komeda/ | ||
| 1144 | F: Documentation/devicetree/bindings/display/arm/arm,komeda.txt | ||
| 1145 | F: Documentation/gpu/komeda-kms.rst | ||
| 1146 | |||
| 1136 | ARM MALI-DP DRM DRIVER | 1147 | ARM MALI-DP DRM DRIVER |
| 1137 | M: Liviu Dudau <liviu.dudau@arm.com> | 1148 | M: Liviu Dudau <liviu.dudau@arm.com> |
| 1138 | M: Brian Starkey <brian.starkey@arm.com> | 1149 | M: Brian Starkey <brian.starkey@arm.com> |
| 1139 | M: Mali DP Maintainers <malidp@foss.arm.com> | 1150 | L: Mali DP Maintainers <malidp@foss.arm.com> |
| 1140 | S: Supported | 1151 | S: Supported |
| 1152 | T: git git://linux-arm.org/linux-ld.git for-upstream/mali-dp | ||
| 1141 | F: drivers/gpu/drm/arm/ | 1153 | F: drivers/gpu/drm/arm/ |
| 1142 | F: Documentation/devicetree/bindings/display/arm,malidp.txt | 1154 | F: Documentation/devicetree/bindings/display/arm,malidp.txt |
| 1155 | F: Documentation/gpu/afbc.rst | ||
| 1143 | 1156 | ||
| 1144 | ARM MFM AND FLOPPY DRIVERS | 1157 | ARM MFM AND FLOPPY DRIVERS |
| 1145 | M: Ian Molton <spyro@f2s.com> | 1158 | M: Ian Molton <spyro@f2s.com> |
diff --git a/drivers/gpu/drm/Makefile b/drivers/gpu/drm/Makefile index ce8d1d384319..f0c1f8731a27 100644 --- a/drivers/gpu/drm/Makefile +++ b/drivers/gpu/drm/Makefile | |||
| @@ -51,7 +51,7 @@ obj-$(CONFIG_DRM_DEBUG_SELFTEST) += selftests/ | |||
| 51 | obj-$(CONFIG_DRM) += drm.o | 51 | obj-$(CONFIG_DRM) += drm.o |
| 52 | obj-$(CONFIG_DRM_MIPI_DSI) += drm_mipi_dsi.o | 52 | obj-$(CONFIG_DRM_MIPI_DSI) += drm_mipi_dsi.o |
| 53 | obj-$(CONFIG_DRM_PANEL_ORIENTATION_QUIRKS) += drm_panel_orientation_quirks.o | 53 | obj-$(CONFIG_DRM_PANEL_ORIENTATION_QUIRKS) += drm_panel_orientation_quirks.o |
| 54 | obj-$(CONFIG_DRM_ARM) += arm/ | 54 | obj-y += arm/ |
| 55 | obj-$(CONFIG_DRM_TTM) += ttm/ | 55 | obj-$(CONFIG_DRM_TTM) += ttm/ |
| 56 | obj-$(CONFIG_DRM_SCHED) += scheduler/ | 56 | obj-$(CONFIG_DRM_SCHED) += scheduler/ |
| 57 | obj-$(CONFIG_DRM_TDFX) += tdfx/ | 57 | obj-$(CONFIG_DRM_TDFX) += tdfx/ |
diff --git a/drivers/gpu/drm/arm/Kconfig b/drivers/gpu/drm/arm/Kconfig index 9a18e1bd57b4..a204103b3efb 100644 --- a/drivers/gpu/drm/arm/Kconfig +++ b/drivers/gpu/drm/arm/Kconfig | |||
| @@ -1,13 +1,10 @@ | |||
| 1 | config DRM_ARM | 1 | # SPDX-License-Identifier: GPL-2.0 |
| 2 | bool | 2 | menu "ARM devices" |
| 3 | help | ||
| 4 | Choose this option to select drivers for ARM's devices | ||
| 5 | 3 | ||
| 6 | config DRM_HDLCD | 4 | config DRM_HDLCD |
| 7 | tristate "ARM HDLCD" | 5 | tristate "ARM HDLCD" |
| 8 | depends on DRM && OF && (ARM || ARM64) | 6 | depends on DRM && OF && (ARM || ARM64) |
| 9 | depends on COMMON_CLK | 7 | depends on COMMON_CLK |
| 10 | select DRM_ARM | ||
| 11 | select DRM_KMS_HELPER | 8 | select DRM_KMS_HELPER |
| 12 | select DRM_KMS_CMA_HELPER | 9 | select DRM_KMS_CMA_HELPER |
| 13 | help | 10 | help |
| @@ -29,7 +26,6 @@ config DRM_MALI_DISPLAY | |||
| 29 | tristate "ARM Mali Display Processor" | 26 | tristate "ARM Mali Display Processor" |
| 30 | depends on DRM && OF && (ARM || ARM64) | 27 | depends on DRM && OF && (ARM || ARM64) |
| 31 | depends on COMMON_CLK | 28 | depends on COMMON_CLK |
| 32 | select DRM_ARM | ||
| 33 | select DRM_KMS_HELPER | 29 | select DRM_KMS_HELPER |
| 34 | select DRM_KMS_CMA_HELPER | 30 | select DRM_KMS_CMA_HELPER |
| 35 | select DRM_GEM_CMA_HELPER | 31 | select DRM_GEM_CMA_HELPER |
| @@ -40,3 +36,7 @@ config DRM_MALI_DISPLAY | |||
| 40 | of the hardware. | 36 | of the hardware. |
| 41 | 37 | ||
| 42 | If compiled as a module it will be called mali-dp. | 38 | If compiled as a module it will be called mali-dp. |
| 39 | |||
| 40 | source "drivers/gpu/drm/arm/display/Kconfig" | ||
| 41 | |||
| 42 | endmenu | ||
diff --git a/drivers/gpu/drm/arm/Makefile b/drivers/gpu/drm/arm/Makefile index 3bf31d1a4722..120bef801fcf 100644 --- a/drivers/gpu/drm/arm/Makefile +++ b/drivers/gpu/drm/arm/Makefile | |||
| @@ -3,3 +3,4 @@ obj-$(CONFIG_DRM_HDLCD) += hdlcd.o | |||
| 3 | mali-dp-y := malidp_drv.o malidp_hw.o malidp_planes.o malidp_crtc.o | 3 | mali-dp-y := malidp_drv.o malidp_hw.o malidp_planes.o malidp_crtc.o |
| 4 | mali-dp-y += malidp_mw.o | 4 | mali-dp-y += malidp_mw.o |
| 5 | obj-$(CONFIG_DRM_MALI_DISPLAY) += mali-dp.o | 5 | obj-$(CONFIG_DRM_MALI_DISPLAY) += mali-dp.o |
| 6 | 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 index 000000000000..382f1ca831e4 --- /dev/null +++ b/drivers/gpu/drm/arm/display/Kbuild | |||
| @@ -0,0 +1,3 @@ | |||
| 1 | # SPDX-License-Identifier: GPL-2.0 | ||
| 2 | |||
| 3 | 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 index 000000000000..cec0639e3aa1 --- /dev/null +++ b/drivers/gpu/drm/arm/display/Kconfig | |||
| @@ -0,0 +1,14 @@ | |||
| 1 | # SPDX-License-Identifier: GPL-2.0 | ||
| 2 | config DRM_KOMEDA | ||
| 3 | tristate "ARM Komeda display driver" | ||
| 4 | depends on DRM && OF | ||
| 5 | depends on COMMON_CLK | ||
| 6 | select DRM_KMS_HELPER | ||
| 7 | select DRM_KMS_CMA_HELPER | ||
| 8 | select DRM_GEM_CMA_HELPER | ||
| 9 | select VIDEOMODE_HELPERS | ||
| 10 | help | ||
| 11 | Choose this option if you want to compile the ARM Komeda display | ||
| 12 | Processor driver. It supports the D71 variants of the hardware. | ||
| 13 | |||
| 14 | 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 index 000000000000..4fb3caf864ce --- /dev/null +++ b/drivers/gpu/drm/arm/display/include/malidp_io.h | |||
| @@ -0,0 +1,42 @@ | |||
| 1 | /* SPDX-License-Identifier: GPL-2.0 */ | ||
| 2 | /* | ||
| 3 | * (C) COPYRIGHT 2018 ARM Limited. All rights reserved. | ||
| 4 | * Author: James.Qian.Wang <james.qian.wang@arm.com> | ||
| 5 | * | ||
| 6 | */ | ||
| 7 | #ifndef _MALIDP_IO_H_ | ||
| 8 | #define _MALIDP_IO_H_ | ||
| 9 | |||
| 10 | #include <linux/io.h> | ||
| 11 | |||
| 12 | static inline u32 | ||
| 13 | malidp_read32(u32 __iomem *base, u32 offset) | ||
| 14 | { | ||
| 15 | return readl((base + (offset >> 2))); | ||
| 16 | } | ||
| 17 | |||
| 18 | static inline void | ||
| 19 | malidp_write32(u32 __iomem *base, u32 offset, u32 v) | ||
| 20 | { | ||
| 21 | writel(v, (base + (offset >> 2))); | ||
| 22 | } | ||
| 23 | |||
| 24 | static inline void | ||
| 25 | malidp_write32_mask(u32 __iomem *base, u32 offset, u32 m, u32 v) | ||
| 26 | { | ||
| 27 | u32 tmp = malidp_read32(base, offset); | ||
| 28 | |||
| 29 | tmp &= (~m); | ||
| 30 | malidp_write32(base, offset, v | tmp); | ||
| 31 | } | ||
| 32 | |||
| 33 | static inline void | ||
| 34 | malidp_write_group(u32 __iomem *base, u32 offset, int num, const u32 *values) | ||
| 35 | { | ||
| 36 | int i; | ||
| 37 | |||
| 38 | for (i = 0; i < num; i++) | ||
| 39 | malidp_write32(base, offset + i * 4, values[i]); | ||
| 40 | } | ||
| 41 | |||
| 42 | #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 index 000000000000..b35fc5db866b --- /dev/null +++ b/drivers/gpu/drm/arm/display/include/malidp_product.h | |||
| @@ -0,0 +1,23 @@ | |||
| 1 | /* SPDX-License-Identifier: GPL-2.0 */ | ||
| 2 | /* | ||
| 3 | * (C) COPYRIGHT 2018 ARM Limited. All rights reserved. | ||
| 4 | * Author: James.Qian.Wang <james.qian.wang@arm.com> | ||
| 5 | * | ||
| 6 | */ | ||
| 7 | #ifndef _MALIDP_PRODUCT_H_ | ||
| 8 | #define _MALIDP_PRODUCT_H_ | ||
| 9 | |||
| 10 | /* Product identification */ | ||
| 11 | #define MALIDP_CORE_ID(__product, __major, __minor, __status) \ | ||
| 12 | ((((__product) & 0xFFFF) << 16) | (((__major) & 0xF) << 12) | \ | ||
| 13 | (((__minor) & 0xF) << 8) | ((__status) & 0xFF)) | ||
| 14 | |||
| 15 | #define MALIDP_CORE_ID_PRODUCT_ID(__core_id) ((__u32)(__core_id) >> 16) | ||
| 16 | #define MALIDP_CORE_ID_MAJOR(__core_id) (((__u32)(__core_id) >> 12) & 0xF) | ||
| 17 | #define MALIDP_CORE_ID_MINOR(__core_id) (((__u32)(__core_id) >> 8) & 0xF) | ||
| 18 | #define MALIDP_CORE_ID_STATUS(__core_id) (((__u32)(__core_id)) & 0xFF) | ||
| 19 | |||
| 20 | /* Mali-display product IDs */ | ||
| 21 | #define MALIDP_D71_PRODUCT_ID 0x0071 | ||
| 22 | |||
| 23 | #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 index 000000000000..63cc47cefcf8 --- /dev/null +++ b/drivers/gpu/drm/arm/display/include/malidp_utils.h | |||
| @@ -0,0 +1,16 @@ | |||
| 1 | /* SPDX-License-Identifier: GPL-2.0 */ | ||
| 2 | /* | ||
| 3 | * (C) COPYRIGHT 2018 ARM Limited. All rights reserved. | ||
| 4 | * Author: James.Qian.Wang <james.qian.wang@arm.com> | ||
| 5 | * | ||
| 6 | */ | ||
| 7 | #ifndef _MALIDP_UTILS_ | ||
| 8 | #define _MALIDP_UTILS_ | ||
| 9 | |||
| 10 | #define has_bit(nr, mask) (BIT(nr) & (mask)) | ||
| 11 | #define has_bits(bits, mask) (((bits) & (mask)) == (bits)) | ||
| 12 | |||
| 13 | #define dp_for_each_set_bit(bit, mask) \ | ||
| 14 | for_each_set_bit((bit), ((unsigned long *)&(mask)), sizeof(mask) * 8) | ||
| 15 | |||
| 16 | #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 index 000000000000..1b875e5dc0f6 --- /dev/null +++ b/drivers/gpu/drm/arm/display/komeda/Makefile | |||
| @@ -0,0 +1,21 @@ | |||
| 1 | # SPDX-License-Identifier: GPL-2.0 | ||
| 2 | |||
| 3 | ccflags-y := \ | ||
| 4 | -I$(src)/../include \ | ||
| 5 | -I$(src) | ||
| 6 | |||
| 7 | komeda-y := \ | ||
| 8 | komeda_drv.o \ | ||
| 9 | komeda_dev.o \ | ||
| 10 | komeda_format_caps.o \ | ||
| 11 | komeda_pipeline.o \ | ||
| 12 | komeda_framebuffer.o \ | ||
| 13 | komeda_kms.o \ | ||
| 14 | komeda_crtc.o \ | ||
| 15 | komeda_plane.o \ | ||
| 16 | komeda_private_obj.o | ||
| 17 | |||
| 18 | komeda-y += \ | ||
| 19 | d71/d71_dev.o | ||
| 20 | |||
| 21 | 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 index 000000000000..edbf9daa1545 --- /dev/null +++ b/drivers/gpu/drm/arm/display/komeda/d71/d71_dev.c | |||
| @@ -0,0 +1,111 @@ | |||
| 1 | // SPDX-License-Identifier: GPL-2.0 | ||
| 2 | /* | ||
| 3 | * (C) COPYRIGHT 2018 ARM Limited. All rights reserved. | ||
| 4 | * Author: James.Qian.Wang <james.qian.wang@arm.com> | ||
| 5 | * | ||
| 6 | */ | ||
| 7 | #include "malidp_io.h" | ||
| 8 | #include "komeda_dev.h" | ||
| 9 | |||
| 10 | static int d71_enum_resources(struct komeda_dev *mdev) | ||
| 11 | { | ||
| 12 | /* TODO add enum resources */ | ||
| 13 | return -1; | ||
| 14 | } | ||
| 15 | |||
| 16 | #define __HW_ID(__group, __format) \ | ||
| 17 | ((((__group) & 0x7) << 3) | ((__format) & 0x7)) | ||
| 18 | |||
| 19 | #define RICH KOMEDA_FMT_RICH_LAYER | ||
| 20 | #define SIMPLE KOMEDA_FMT_SIMPLE_LAYER | ||
| 21 | #define RICH_SIMPLE (KOMEDA_FMT_RICH_LAYER | KOMEDA_FMT_SIMPLE_LAYER) | ||
| 22 | #define RICH_WB (KOMEDA_FMT_RICH_LAYER | KOMEDA_FMT_WB_LAYER) | ||
| 23 | #define RICH_SIMPLE_WB (RICH_SIMPLE | KOMEDA_FMT_WB_LAYER) | ||
| 24 | |||
| 25 | #define Rot_0 DRM_MODE_ROTATE_0 | ||
| 26 | #define Flip_H_V (DRM_MODE_REFLECT_X | DRM_MODE_REFLECT_Y | Rot_0) | ||
| 27 | #define Rot_ALL_H_V (DRM_MODE_ROTATE_MASK | Flip_H_V) | ||
| 28 | |||
| 29 | #define LYT_NM BIT(AFBC_FORMAT_MOD_BLOCK_SIZE_16x16) | ||
| 30 | #define LYT_WB BIT(AFBC_FORMAT_MOD_BLOCK_SIZE_32x8) | ||
| 31 | #define LYT_NM_WB (LYT_NM | LYT_WB) | ||
| 32 | |||
| 33 | #define AFB_TH AFBC(_TILED | _SPARSE) | ||
| 34 | #define AFB_TH_SC_YTR AFBC(_TILED | _SC | _SPARSE | _YTR) | ||
| 35 | #define AFB_TH_SC_YTR_BS AFBC(_TILED | _SC | _SPARSE | _YTR | _SPLIT) | ||
| 36 | |||
| 37 | static struct komeda_format_caps d71_format_caps_table[] = { | ||
| 38 | /* HW_ID | fourcc | tile_sz | layer_types | rots | afbc_layouts | afbc_features */ | ||
| 39 | /* ABGR_2101010*/ | ||
| 40 | {__HW_ID(0, 0), DRM_FORMAT_ARGB2101010, 1, RICH_SIMPLE_WB, Flip_H_V, 0, 0}, | ||
| 41 | {__HW_ID(0, 1), DRM_FORMAT_ABGR2101010, 1, RICH_SIMPLE_WB, Flip_H_V, 0, 0}, | ||
| 42 | {__HW_ID(0, 1), DRM_FORMAT_ABGR2101010, 1, RICH_SIMPLE, Rot_ALL_H_V, LYT_NM_WB, AFB_TH_SC_YTR_BS}, /* afbc */ | ||
| 43 | {__HW_ID(0, 2), DRM_FORMAT_RGBA1010102, 1, RICH_SIMPLE_WB, Flip_H_V, 0, 0}, | ||
| 44 | {__HW_ID(0, 3), DRM_FORMAT_BGRA1010102, 1, RICH_SIMPLE_WB, Flip_H_V, 0, 0}, | ||
| 45 | /* ABGR_8888*/ | ||
| 46 | {__HW_ID(1, 0), DRM_FORMAT_ARGB8888, 1, RICH_SIMPLE_WB, Flip_H_V, 0, 0}, | ||
| 47 | {__HW_ID(1, 1), DRM_FORMAT_ABGR8888, 1, RICH_SIMPLE_WB, Flip_H_V, 0, 0}, | ||
| 48 | {__HW_ID(1, 1), DRM_FORMAT_ABGR8888, 1, RICH_SIMPLE, Rot_ALL_H_V, LYT_NM_WB, AFB_TH_SC_YTR_BS}, /* afbc */ | ||
| 49 | {__HW_ID(1, 2), DRM_FORMAT_RGBA8888, 1, RICH_SIMPLE_WB, Flip_H_V, 0, 0}, | ||
| 50 | {__HW_ID(1, 3), DRM_FORMAT_BGRA8888, 1, RICH_SIMPLE_WB, Flip_H_V, 0, 0}, | ||
| 51 | /* XBGB_8888 */ | ||
| 52 | {__HW_ID(2, 0), DRM_FORMAT_XRGB8888, 1, RICH_SIMPLE_WB, Flip_H_V, 0, 0}, | ||
| 53 | {__HW_ID(2, 1), DRM_FORMAT_XBGR8888, 1, RICH_SIMPLE_WB, Flip_H_V, 0, 0}, | ||
| 54 | {__HW_ID(2, 2), DRM_FORMAT_RGBX8888, 1, RICH_SIMPLE_WB, Flip_H_V, 0, 0}, | ||
| 55 | {__HW_ID(2, 3), DRM_FORMAT_BGRX8888, 1, RICH_SIMPLE_WB, Flip_H_V, 0, 0}, | ||
| 56 | /* BGR_888 */ /* none-afbc RGB888 doesn't support rotation and flip */ | ||
| 57 | {__HW_ID(3, 0), DRM_FORMAT_RGB888, 1, RICH_SIMPLE_WB, Rot_0, 0, 0}, | ||
| 58 | {__HW_ID(3, 1), DRM_FORMAT_BGR888, 1, RICH_SIMPLE_WB, Rot_0, 0, 0}, | ||
| 59 | {__HW_ID(3, 1), DRM_FORMAT_BGR888, 1, RICH_SIMPLE, Rot_ALL_H_V, LYT_NM_WB, AFB_TH_SC_YTR_BS}, /* afbc */ | ||
| 60 | /* BGR 16bpp */ | ||
| 61 | {__HW_ID(4, 0), DRM_FORMAT_RGBA5551, 1, RICH_SIMPLE, Flip_H_V, 0, 0}, | ||
| 62 | {__HW_ID(4, 1), DRM_FORMAT_ABGR1555, 1, RICH_SIMPLE, Flip_H_V, 0, 0}, | ||
| 63 | {__HW_ID(4, 1), DRM_FORMAT_ABGR1555, 1, RICH_SIMPLE, Rot_ALL_H_V, LYT_NM_WB, AFB_TH_SC_YTR}, /* afbc */ | ||
| 64 | {__HW_ID(4, 2), DRM_FORMAT_RGB565, 1, RICH_SIMPLE, Flip_H_V, 0, 0}, | ||
| 65 | {__HW_ID(4, 3), DRM_FORMAT_BGR565, 1, RICH_SIMPLE, Flip_H_V, 0, 0}, | ||
| 66 | {__HW_ID(4, 3), DRM_FORMAT_BGR565, 1, RICH_SIMPLE, Rot_ALL_H_V, LYT_NM_WB, AFB_TH_SC_YTR}, /* afbc */ | ||
| 67 | {__HW_ID(4, 4), DRM_FORMAT_R8, 1, SIMPLE, Rot_0, 0, 0}, | ||
| 68 | /* YUV 444/422/420 8bit */ | ||
| 69 | {__HW_ID(5, 0), 0 /*XYUV8888*/, 1, 0, 0, 0, 0}, | ||
| 70 | /* XYUV unsupported*/ | ||
| 71 | {__HW_ID(5, 1), DRM_FORMAT_YUYV, 1, RICH, Rot_ALL_H_V, LYT_NM, AFB_TH}, /* afbc */ | ||
| 72 | {__HW_ID(5, 2), DRM_FORMAT_YUYV, 1, RICH, Flip_H_V, 0, 0}, | ||
| 73 | {__HW_ID(5, 3), DRM_FORMAT_UYVY, 1, RICH, Flip_H_V, 0, 0}, | ||
| 74 | {__HW_ID(5, 4), 0, /*X0L0 */ 2, 0, 0, 0}, /* Y0L0 unsupported */ | ||
| 75 | {__HW_ID(5, 6), DRM_FORMAT_NV12, 1, RICH, Flip_H_V, 0, 0}, | ||
| 76 | {__HW_ID(5, 6), 0/*DRM_FORMAT_YUV420_8BIT*/, 1, RICH, Rot_ALL_H_V, LYT_NM, AFB_TH}, /* afbc */ | ||
| 77 | {__HW_ID(5, 7), DRM_FORMAT_YUV420, 1, RICH, Flip_H_V, 0, 0}, | ||
| 78 | /* YUV 10bit*/ | ||
| 79 | {__HW_ID(6, 0), 0,/*XVYU2101010*/ 1, 0, 0, 0, 0},/* VYV30 unsupported */ | ||
| 80 | {__HW_ID(6, 6), 0/*DRM_FORMAT_X0L2*/, 2, RICH, Flip_H_V, 0, 0}, | ||
| 81 | {__HW_ID(6, 7), 0/*DRM_FORMAT_P010*/, 1, RICH, Flip_H_V, 0, 0}, | ||
| 82 | {__HW_ID(6, 7), 0/*DRM_FORMAT_YUV420_10BIT*/, 1, RICH, Rot_ALL_H_V, LYT_NM, AFB_TH}, | ||
| 83 | }; | ||
| 84 | |||
| 85 | static void d71_init_fmt_tbl(struct komeda_dev *mdev) | ||
| 86 | { | ||
| 87 | struct komeda_format_caps_table *table = &mdev->fmt_tbl; | ||
| 88 | |||
| 89 | table->format_caps = d71_format_caps_table; | ||
| 90 | table->n_formats = ARRAY_SIZE(d71_format_caps_table); | ||
| 91 | } | ||
| 92 | |||
| 93 | static struct komeda_dev_funcs d71_chip_funcs = { | ||
| 94 | .init_format_table = d71_init_fmt_tbl, | ||
| 95 | .enum_resources = d71_enum_resources, | ||
| 96 | .cleanup = NULL, | ||
| 97 | }; | ||
| 98 | |||
| 99 | #define GLB_ARCH_ID 0x000 | ||
| 100 | #define GLB_CORE_ID 0x004 | ||
| 101 | #define GLB_CORE_INFO 0x008 | ||
| 102 | |||
| 103 | struct komeda_dev_funcs * | ||
| 104 | d71_identify(u32 __iomem *reg_base, struct komeda_chip_info *chip) | ||
| 105 | { | ||
| 106 | chip->arch_id = malidp_read32(reg_base, GLB_ARCH_ID); | ||
| 107 | chip->core_id = malidp_read32(reg_base, GLB_CORE_ID); | ||
| 108 | chip->core_info = malidp_read32(reg_base, GLB_CORE_INFO); | ||
| 109 | |||
| 110 | return &d71_chip_funcs; | ||
| 111 | } | ||
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 index 000000000000..5bb5a55f6b31 --- /dev/null +++ b/drivers/gpu/drm/arm/display/komeda/komeda_crtc.c | |||
| @@ -0,0 +1,106 @@ | |||
| 1 | // SPDX-License-Identifier: GPL-2.0 | ||
| 2 | /* | ||
| 3 | * (C) COPYRIGHT 2018 ARM Limited. All rights reserved. | ||
| 4 | * Author: James.Qian.Wang <james.qian.wang@arm.com> | ||
| 5 | * | ||
| 6 | */ | ||
| 7 | #include <linux/clk.h> | ||
| 8 | #include <linux/spinlock.h> | ||
| 9 | #include <drm/drm_atomic.h> | ||
| 10 | #include <drm/drm_atomic_helper.h> | ||
| 11 | #include <drm/drm_plane_helper.h> | ||
| 12 | #include <drm/drm_crtc_helper.h> | ||
| 13 | #include <linux/pm_runtime.h> | ||
| 14 | #include "komeda_dev.h" | ||
| 15 | #include "komeda_kms.h" | ||
| 16 | |||
| 17 | struct drm_crtc_helper_funcs komeda_crtc_helper_funcs = { | ||
| 18 | }; | ||
| 19 | |||
| 20 | static const struct drm_crtc_funcs komeda_crtc_funcs = { | ||
| 21 | }; | ||
| 22 | |||
| 23 | int komeda_kms_setup_crtcs(struct komeda_kms_dev *kms, | ||
| 24 | struct komeda_dev *mdev) | ||
| 25 | { | ||
| 26 | struct komeda_crtc *crtc; | ||
| 27 | struct komeda_pipeline *master; | ||
| 28 | char str[16]; | ||
| 29 | int i; | ||
| 30 | |||
| 31 | kms->n_crtcs = 0; | ||
| 32 | |||
| 33 | for (i = 0; i < mdev->n_pipelines; i++) { | ||
| 34 | crtc = &kms->crtcs[kms->n_crtcs]; | ||
| 35 | master = mdev->pipelines[i]; | ||
| 36 | |||
| 37 | crtc->master = master; | ||
| 38 | crtc->slave = NULL; | ||
| 39 | |||
| 40 | if (crtc->slave) | ||
| 41 | sprintf(str, "pipe-%d", crtc->slave->id); | ||
| 42 | else | ||
| 43 | sprintf(str, "None"); | ||
| 44 | |||
| 45 | DRM_INFO("crtc%d: master(pipe-%d) slave(%s) output: %s.\n", | ||
| 46 | kms->n_crtcs, master->id, str, | ||
| 47 | master->of_output_dev ? | ||
| 48 | master->of_output_dev->full_name : "None"); | ||
| 49 | |||
| 50 | kms->n_crtcs++; | ||
| 51 | } | ||
| 52 | |||
| 53 | return 0; | ||
| 54 | } | ||
| 55 | |||
| 56 | static struct drm_plane * | ||
| 57 | get_crtc_primary(struct komeda_kms_dev *kms, struct komeda_crtc *crtc) | ||
| 58 | { | ||
| 59 | struct komeda_plane *kplane; | ||
| 60 | struct drm_plane *plane; | ||
| 61 | |||
| 62 | drm_for_each_plane(plane, &kms->base) { | ||
| 63 | if (plane->type != DRM_PLANE_TYPE_PRIMARY) | ||
| 64 | continue; | ||
| 65 | |||
| 66 | kplane = to_kplane(plane); | ||
| 67 | /* only master can be primary */ | ||
| 68 | if (kplane->layer->base.pipeline == crtc->master) | ||
| 69 | return plane; | ||
| 70 | } | ||
| 71 | |||
| 72 | return NULL; | ||
| 73 | } | ||
| 74 | |||
| 75 | static int komeda_crtc_add(struct komeda_kms_dev *kms, | ||
| 76 | struct komeda_crtc *kcrtc) | ||
| 77 | { | ||
| 78 | struct drm_crtc *crtc = &kcrtc->base; | ||
| 79 | int err; | ||
| 80 | |||
| 81 | err = drm_crtc_init_with_planes(&kms->base, crtc, | ||
| 82 | get_crtc_primary(kms, kcrtc), NULL, | ||
| 83 | &komeda_crtc_funcs, NULL); | ||
| 84 | if (err) | ||
| 85 | return err; | ||
| 86 | |||
| 87 | drm_crtc_helper_add(crtc, &komeda_crtc_helper_funcs); | ||
| 88 | drm_crtc_vblank_reset(crtc); | ||
| 89 | |||
| 90 | crtc->port = kcrtc->master->of_output_port; | ||
| 91 | |||
| 92 | return 0; | ||
| 93 | } | ||
| 94 | |||
| 95 | int komeda_kms_add_crtcs(struct komeda_kms_dev *kms, struct komeda_dev *mdev) | ||
| 96 | { | ||
| 97 | int i, err; | ||
| 98 | |||
| 99 | for (i = 0; i < kms->n_crtcs; i++) { | ||
| 100 | err = komeda_crtc_add(kms, &kms->crtcs[i]); | ||
| 101 | if (err) | ||
| 102 | return err; | ||
| 103 | } | ||
| 104 | |||
| 105 | return 0; | ||
| 106 | } | ||
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 index 000000000000..0fe6954fbbf4 --- /dev/null +++ b/drivers/gpu/drm/arm/display/komeda/komeda_dev.c | |||
| @@ -0,0 +1,186 @@ | |||
| 1 | // SPDX-License-Identifier: GPL-2.0 | ||
| 2 | /* | ||
| 3 | * (C) COPYRIGHT 2018 ARM Limited. All rights reserved. | ||
| 4 | * Author: James.Qian.Wang <james.qian.wang@arm.com> | ||
| 5 | * | ||
| 6 | */ | ||
| 7 | #include <linux/platform_device.h> | ||
| 8 | #include <linux/of_device.h> | ||
| 9 | #include <linux/of_graph.h> | ||
| 10 | #include "komeda_dev.h" | ||
| 11 | |||
| 12 | static int komeda_parse_pipe_dt(struct komeda_dev *mdev, struct device_node *np) | ||
| 13 | { | ||
| 14 | struct komeda_pipeline *pipe; | ||
| 15 | struct clk *clk; | ||
| 16 | u32 pipe_id; | ||
| 17 | int ret = 0; | ||
| 18 | |||
| 19 | ret = of_property_read_u32(np, "reg", &pipe_id); | ||
| 20 | if (ret != 0 || pipe_id >= mdev->n_pipelines) | ||
| 21 | return -EINVAL; | ||
| 22 | |||
| 23 | pipe = mdev->pipelines[pipe_id]; | ||
| 24 | |||
| 25 | clk = of_clk_get_by_name(np, "aclk"); | ||
| 26 | if (IS_ERR(clk)) { | ||
| 27 | DRM_ERROR("get aclk for pipeline %d failed!\n", pipe_id); | ||
| 28 | return PTR_ERR(clk); | ||
| 29 | } | ||
| 30 | pipe->aclk = clk; | ||
| 31 | |||
| 32 | clk = of_clk_get_by_name(np, "pxclk"); | ||
| 33 | if (IS_ERR(clk)) { | ||
| 34 | DRM_ERROR("get pxclk for pipeline %d failed!\n", pipe_id); | ||
| 35 | return PTR_ERR(clk); | ||
| 36 | } | ||
| 37 | pipe->pxlclk = clk; | ||
| 38 | |||
| 39 | /* enum ports */ | ||
| 40 | pipe->of_output_dev = | ||
| 41 | of_graph_get_remote_node(np, KOMEDA_OF_PORT_OUTPUT, 0); | ||
| 42 | pipe->of_output_port = | ||
| 43 | of_graph_get_port_by_id(np, KOMEDA_OF_PORT_OUTPUT); | ||
| 44 | |||
| 45 | pipe->of_node = np; | ||
| 46 | |||
| 47 | return 0; | ||
| 48 | } | ||
| 49 | |||
| 50 | static int komeda_parse_dt(struct device *dev, struct komeda_dev *mdev) | ||
| 51 | { | ||
| 52 | struct device_node *child, *np = dev->of_node; | ||
| 53 | struct clk *clk; | ||
| 54 | int ret; | ||
| 55 | |||
| 56 | clk = devm_clk_get(dev, "mclk"); | ||
| 57 | if (IS_ERR(clk)) | ||
| 58 | return PTR_ERR(clk); | ||
| 59 | |||
| 60 | mdev->mclk = clk; | ||
| 61 | |||
| 62 | for_each_available_child_of_node(np, child) { | ||
| 63 | if (of_node_cmp(child->name, "pipeline") == 0) { | ||
| 64 | ret = komeda_parse_pipe_dt(mdev, child); | ||
| 65 | if (ret) { | ||
| 66 | DRM_ERROR("parse pipeline dt error!\n"); | ||
| 67 | of_node_put(child); | ||
| 68 | break; | ||
| 69 | } | ||
| 70 | } | ||
| 71 | } | ||
| 72 | |||
| 73 | return ret; | ||
| 74 | } | ||
| 75 | |||
| 76 | struct komeda_dev *komeda_dev_create(struct device *dev) | ||
| 77 | { | ||
| 78 | struct platform_device *pdev = to_platform_device(dev); | ||
| 79 | const struct komeda_product_data *product; | ||
| 80 | struct komeda_dev *mdev; | ||
| 81 | struct resource *io_res; | ||
| 82 | int err = 0; | ||
| 83 | |||
| 84 | product = of_device_get_match_data(dev); | ||
| 85 | if (!product) | ||
| 86 | return ERR_PTR(-ENODEV); | ||
| 87 | |||
| 88 | io_res = platform_get_resource(pdev, IORESOURCE_MEM, 0); | ||
| 89 | if (!io_res) { | ||
| 90 | DRM_ERROR("No registers defined.\n"); | ||
| 91 | return ERR_PTR(-ENODEV); | ||
| 92 | } | ||
| 93 | |||
| 94 | mdev = devm_kzalloc(dev, sizeof(*mdev), GFP_KERNEL); | ||
| 95 | if (!mdev) | ||
| 96 | return ERR_PTR(-ENOMEM); | ||
| 97 | |||
| 98 | mdev->dev = dev; | ||
| 99 | mdev->reg_base = devm_ioremap_resource(dev, io_res); | ||
| 100 | if (IS_ERR(mdev->reg_base)) { | ||
| 101 | DRM_ERROR("Map register space failed.\n"); | ||
| 102 | err = PTR_ERR(mdev->reg_base); | ||
| 103 | mdev->reg_base = NULL; | ||
| 104 | goto err_cleanup; | ||
| 105 | } | ||
| 106 | |||
| 107 | mdev->pclk = devm_clk_get(dev, "pclk"); | ||
| 108 | if (IS_ERR(mdev->pclk)) { | ||
| 109 | DRM_ERROR("Get APB clk failed.\n"); | ||
| 110 | err = PTR_ERR(mdev->pclk); | ||
| 111 | mdev->pclk = NULL; | ||
| 112 | goto err_cleanup; | ||
| 113 | } | ||
| 114 | |||
| 115 | /* Enable APB clock to access the registers */ | ||
| 116 | clk_prepare_enable(mdev->pclk); | ||
| 117 | |||
| 118 | mdev->funcs = product->identify(mdev->reg_base, &mdev->chip); | ||
| 119 | if (!komeda_product_match(mdev, product->product_id)) { | ||
| 120 | DRM_ERROR("DT configured %x mismatch with real HW %x.\n", | ||
| 121 | product->product_id, | ||
| 122 | MALIDP_CORE_ID_PRODUCT_ID(mdev->chip.core_id)); | ||
| 123 | err = -ENODEV; | ||
| 124 | goto err_cleanup; | ||
| 125 | } | ||
| 126 | |||
| 127 | DRM_INFO("Found ARM Mali-D%x version r%dp%d\n", | ||
| 128 | MALIDP_CORE_ID_PRODUCT_ID(mdev->chip.core_id), | ||
| 129 | MALIDP_CORE_ID_MAJOR(mdev->chip.core_id), | ||
| 130 | MALIDP_CORE_ID_MINOR(mdev->chip.core_id)); | ||
| 131 | |||
| 132 | mdev->funcs->init_format_table(mdev); | ||
| 133 | |||
| 134 | err = mdev->funcs->enum_resources(mdev); | ||
| 135 | if (err) { | ||
| 136 | DRM_ERROR("enumerate display resource failed.\n"); | ||
| 137 | goto err_cleanup; | ||
| 138 | } | ||
| 139 | |||
| 140 | err = komeda_parse_dt(dev, mdev); | ||
| 141 | if (err) { | ||
| 142 | DRM_ERROR("parse device tree failed.\n"); | ||
| 143 | goto err_cleanup; | ||
| 144 | } | ||
| 145 | |||
| 146 | return mdev; | ||
| 147 | |||
| 148 | err_cleanup: | ||
| 149 | komeda_dev_destroy(mdev); | ||
| 150 | return ERR_PTR(err); | ||
| 151 | } | ||
| 152 | |||
| 153 | void komeda_dev_destroy(struct komeda_dev *mdev) | ||
| 154 | { | ||
| 155 | struct device *dev = mdev->dev; | ||
| 156 | struct komeda_dev_funcs *funcs = mdev->funcs; | ||
| 157 | int i; | ||
| 158 | |||
| 159 | for (i = 0; i < mdev->n_pipelines; i++) { | ||
| 160 | komeda_pipeline_destroy(mdev, mdev->pipelines[i]); | ||
| 161 | mdev->pipelines[i] = NULL; | ||
| 162 | } | ||
| 163 | |||
| 164 | mdev->n_pipelines = 0; | ||
| 165 | |||
| 166 | if (funcs && funcs->cleanup) | ||
| 167 | funcs->cleanup(mdev); | ||
| 168 | |||
| 169 | if (mdev->reg_base) { | ||
| 170 | devm_iounmap(dev, mdev->reg_base); | ||
| 171 | mdev->reg_base = NULL; | ||
| 172 | } | ||
| 173 | |||
| 174 | if (mdev->mclk) { | ||
| 175 | devm_clk_put(dev, mdev->mclk); | ||
| 176 | mdev->mclk = NULL; | ||
| 177 | } | ||
| 178 | |||
| 179 | if (mdev->pclk) { | ||
| 180 | clk_disable_unprepare(mdev->pclk); | ||
| 181 | devm_clk_put(dev, mdev->pclk); | ||
| 182 | mdev->pclk = NULL; | ||
| 183 | } | ||
| 184 | |||
| 185 | devm_kfree(dev, mdev); | ||
| 186 | } | ||
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 index 000000000000..0f77dead6a23 --- /dev/null +++ b/drivers/gpu/drm/arm/display/komeda/komeda_dev.h | |||
| @@ -0,0 +1,110 @@ | |||
| 1 | /* SPDX-License-Identifier: GPL-2.0 */ | ||
| 2 | /* | ||
| 3 | * (C) COPYRIGHT 2018 ARM Limited. All rights reserved. | ||
| 4 | * Author: James.Qian.Wang <james.qian.wang@arm.com> | ||
| 5 | * | ||
| 6 | */ | ||
| 7 | #ifndef _KOMEDA_DEV_H_ | ||
| 8 | #define _KOMEDA_DEV_H_ | ||
| 9 | |||
| 10 | #include <linux/device.h> | ||
| 11 | #include <linux/clk.h> | ||
| 12 | #include "komeda_pipeline.h" | ||
| 13 | #include "malidp_product.h" | ||
| 14 | #include "komeda_format_caps.h" | ||
| 15 | |||
| 16 | /* malidp device id */ | ||
| 17 | enum { | ||
| 18 | MALI_D71 = 0, | ||
| 19 | }; | ||
| 20 | |||
| 21 | /* pipeline DT ports */ | ||
| 22 | enum { | ||
| 23 | KOMEDA_OF_PORT_OUTPUT = 0, | ||
| 24 | KOMEDA_OF_PORT_COPROC = 1, | ||
| 25 | }; | ||
| 26 | |||
| 27 | struct komeda_chip_info { | ||
| 28 | u32 arch_id; | ||
| 29 | u32 core_id; | ||
| 30 | u32 core_info; | ||
| 31 | u32 bus_width; | ||
| 32 | }; | ||
| 33 | |||
| 34 | struct komeda_product_data { | ||
| 35 | u32 product_id; | ||
| 36 | struct komeda_dev_funcs *(*identify)(u32 __iomem *reg, | ||
| 37 | struct komeda_chip_info *info); | ||
| 38 | }; | ||
| 39 | |||
| 40 | struct komeda_dev; | ||
| 41 | |||
| 42 | /** | ||
| 43 | * struct komeda_dev_funcs | ||
| 44 | * | ||
| 45 | * Supplied by chip level and returned by the chip entry function xxx_identify, | ||
| 46 | */ | ||
| 47 | struct komeda_dev_funcs { | ||
| 48 | /** | ||
| 49 | * @init_format_table: | ||
| 50 | * | ||
| 51 | * initialize &komeda_dev->format_table, this function should be called | ||
| 52 | * before the &enum_resource | ||
| 53 | */ | ||
| 54 | void (*init_format_table)(struct komeda_dev *mdev); | ||
| 55 | /** | ||
| 56 | * @enum_resources: | ||
| 57 | * | ||
| 58 | * for CHIP to report or add pipeline and component resources to CORE | ||
| 59 | */ | ||
| 60 | int (*enum_resources)(struct komeda_dev *mdev); | ||
| 61 | /** @cleanup: call to chip to cleanup komeda_dev->chip data */ | ||
| 62 | void (*cleanup)(struct komeda_dev *mdev); | ||
| 63 | }; | ||
| 64 | |||
| 65 | /** | ||
| 66 | * struct komeda_dev | ||
| 67 | * | ||
| 68 | * Pipeline and component are used to describe how to handle the pixel data. | ||
| 69 | * komeda_device is for describing the whole view of the device, and the | ||
| 70 | * control-abilites of device. | ||
| 71 | */ | ||
| 72 | struct komeda_dev { | ||
| 73 | struct device *dev; | ||
| 74 | u32 __iomem *reg_base; | ||
| 75 | |||
| 76 | struct komeda_chip_info chip; | ||
| 77 | /** @fmt_tbl: initialized by &komeda_dev_funcs->init_format_table */ | ||
| 78 | struct komeda_format_caps_table fmt_tbl; | ||
| 79 | /** @pclk: APB clock for register access */ | ||
| 80 | struct clk *pclk; | ||
| 81 | /** @mck: HW main engine clk */ | ||
| 82 | struct clk *mclk; | ||
| 83 | |||
| 84 | int n_pipelines; | ||
| 85 | struct komeda_pipeline *pipelines[KOMEDA_MAX_PIPELINES]; | ||
| 86 | |||
| 87 | /** @funcs: chip funcs to access to HW */ | ||
| 88 | struct komeda_dev_funcs *funcs; | ||
| 89 | /** | ||
| 90 | * @chip_data: | ||
| 91 | * | ||
| 92 | * chip data will be added by &komeda_dev_funcs.enum_resources() and | ||
| 93 | * destroyed by &komeda_dev_funcs.cleanup() | ||
| 94 | */ | ||
| 95 | void *chip_data; | ||
| 96 | }; | ||
| 97 | |||
| 98 | static inline bool | ||
| 99 | komeda_product_match(struct komeda_dev *mdev, u32 target) | ||
| 100 | { | ||
| 101 | return MALIDP_CORE_ID_PRODUCT_ID(mdev->chip.core_id) == target; | ||
| 102 | } | ||
| 103 | |||
| 104 | struct komeda_dev_funcs * | ||
| 105 | d71_identify(u32 __iomem *reg, struct komeda_chip_info *chip); | ||
| 106 | |||
| 107 | struct komeda_dev *komeda_dev_create(struct device *dev); | ||
| 108 | void komeda_dev_destroy(struct komeda_dev *mdev); | ||
| 109 | |||
| 110 | #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 index 000000000000..2bdd189b041d --- /dev/null +++ b/drivers/gpu/drm/arm/display/komeda/komeda_drv.c | |||
| @@ -0,0 +1,144 @@ | |||
| 1 | // SPDX-License-Identifier: GPL-2.0 | ||
| 2 | /* | ||
| 3 | * (C) COPYRIGHT 2018 ARM Limited. All rights reserved. | ||
| 4 | * Author: James.Qian.Wang <james.qian.wang@arm.com> | ||
| 5 | * | ||
| 6 | */ | ||
| 7 | #include <linux/module.h> | ||
| 8 | #include <linux/kernel.h> | ||
| 9 | #include <linux/platform_device.h> | ||
| 10 | #include <linux/component.h> | ||
| 11 | #include <drm/drm_of.h> | ||
| 12 | #include "komeda_dev.h" | ||
| 13 | #include "komeda_kms.h" | ||
| 14 | |||
| 15 | struct komeda_drv { | ||
| 16 | struct komeda_dev *mdev; | ||
| 17 | struct komeda_kms_dev *kms; | ||
| 18 | }; | ||
| 19 | |||
| 20 | static void komeda_unbind(struct device *dev) | ||
| 21 | { | ||
| 22 | struct komeda_drv *mdrv = dev_get_drvdata(dev); | ||
| 23 | |||
| 24 | if (!mdrv) | ||
| 25 | return; | ||
| 26 | |||
| 27 | komeda_kms_detach(mdrv->kms); | ||
| 28 | komeda_dev_destroy(mdrv->mdev); | ||
| 29 | |||
| 30 | dev_set_drvdata(dev, NULL); | ||
| 31 | devm_kfree(dev, mdrv); | ||
| 32 | } | ||
| 33 | |||
| 34 | static int komeda_bind(struct device *dev) | ||
| 35 | { | ||
| 36 | struct komeda_drv *mdrv; | ||
| 37 | int err; | ||
| 38 | |||
| 39 | mdrv = devm_kzalloc(dev, sizeof(*mdrv), GFP_KERNEL); | ||
| 40 | if (!mdrv) | ||
| 41 | return -ENOMEM; | ||
| 42 | |||
| 43 | mdrv->mdev = komeda_dev_create(dev); | ||
| 44 | if (IS_ERR(mdrv->mdev)) { | ||
| 45 | err = PTR_ERR(mdrv->mdev); | ||
| 46 | goto free_mdrv; | ||
| 47 | } | ||
| 48 | |||
| 49 | mdrv->kms = komeda_kms_attach(mdrv->mdev); | ||
| 50 | if (IS_ERR(mdrv->kms)) { | ||
| 51 | err = PTR_ERR(mdrv->kms); | ||
| 52 | goto destroy_mdev; | ||
| 53 | } | ||
| 54 | |||
| 55 | dev_set_drvdata(dev, mdrv); | ||
| 56 | |||
| 57 | return 0; | ||
| 58 | |||
| 59 | destroy_mdev: | ||
| 60 | komeda_dev_destroy(mdrv->mdev); | ||
| 61 | |||
| 62 | free_mdrv: | ||
| 63 | devm_kfree(dev, mdrv); | ||
| 64 | return err; | ||
| 65 | } | ||
| 66 | |||
| 67 | static const struct component_master_ops komeda_master_ops = { | ||
| 68 | .bind = komeda_bind, | ||
| 69 | .unbind = komeda_unbind, | ||
| 70 | }; | ||
| 71 | |||
| 72 | static int compare_of(struct device *dev, void *data) | ||
| 73 | { | ||
| 74 | return dev->of_node == data; | ||
| 75 | } | ||
| 76 | |||
| 77 | static void komeda_add_slave(struct device *master, | ||
| 78 | struct component_match **match, | ||
| 79 | struct device_node *np, int port) | ||
| 80 | { | ||
| 81 | struct device_node *remote; | ||
| 82 | |||
| 83 | remote = of_graph_get_remote_node(np, port, 0); | ||
| 84 | if (remote) { | ||
| 85 | drm_of_component_match_add(master, match, compare_of, remote); | ||
| 86 | of_node_put(remote); | ||
| 87 | } | ||
| 88 | } | ||
| 89 | |||
| 90 | static int komeda_platform_probe(struct platform_device *pdev) | ||
| 91 | { | ||
| 92 | struct device *dev = &pdev->dev; | ||
| 93 | struct component_match *match = NULL; | ||
| 94 | struct device_node *child; | ||
| 95 | |||
| 96 | if (!dev->of_node) | ||
| 97 | return -ENODEV; | ||
| 98 | |||
| 99 | for_each_available_child_of_node(dev->of_node, child) { | ||
| 100 | if (of_node_cmp(child->name, "pipeline") != 0) | ||
| 101 | continue; | ||
| 102 | |||
| 103 | /* add connector */ | ||
| 104 | komeda_add_slave(dev, &match, child, KOMEDA_OF_PORT_OUTPUT); | ||
| 105 | } | ||
| 106 | |||
| 107 | return component_master_add_with_match(dev, &komeda_master_ops, match); | ||
| 108 | } | ||
| 109 | |||
| 110 | static int komeda_platform_remove(struct platform_device *pdev) | ||
| 111 | { | ||
| 112 | component_master_del(&pdev->dev, &komeda_master_ops); | ||
| 113 | return 0; | ||
| 114 | } | ||
| 115 | |||
| 116 | static const struct komeda_product_data komeda_products[] = { | ||
| 117 | [MALI_D71] = { | ||
| 118 | .product_id = MALIDP_D71_PRODUCT_ID, | ||
| 119 | .identify = d71_identify, | ||
| 120 | }, | ||
| 121 | }; | ||
| 122 | |||
| 123 | const struct of_device_id komeda_of_match[] = { | ||
| 124 | { .compatible = "arm,mali-d71", .data = &komeda_products[MALI_D71], }, | ||
| 125 | {}, | ||
| 126 | }; | ||
| 127 | |||
| 128 | MODULE_DEVICE_TABLE(of, komeda_of_match); | ||
| 129 | |||
| 130 | static struct platform_driver komeda_platform_driver = { | ||
| 131 | .probe = komeda_platform_probe, | ||
| 132 | .remove = komeda_platform_remove, | ||
| 133 | .driver = { | ||
| 134 | .name = "komeda", | ||
| 135 | .of_match_table = komeda_of_match, | ||
| 136 | .pm = NULL, | ||
| 137 | }, | ||
| 138 | }; | ||
| 139 | |||
| 140 | module_platform_driver(komeda_platform_driver); | ||
| 141 | |||
| 142 | MODULE_AUTHOR("James.Qian.Wang <james.qian.wang@arm.com>"); | ||
| 143 | MODULE_DESCRIPTION("Komeda KMS driver"); | ||
| 144 | 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 index 000000000000..1e17bd6107a4 --- /dev/null +++ b/drivers/gpu/drm/arm/display/komeda/komeda_format_caps.c | |||
| @@ -0,0 +1,75 @@ | |||
| 1 | // SPDX-License-Identifier: GPL-2.0 | ||
| 2 | /* | ||
| 3 | * (C) COPYRIGHT 2018 ARM Limited. All rights reserved. | ||
| 4 | * Author: James.Qian.Wang <james.qian.wang@arm.com> | ||
| 5 | * | ||
| 6 | */ | ||
| 7 | |||
| 8 | #include <linux/slab.h> | ||
| 9 | #include "komeda_format_caps.h" | ||
| 10 | #include "malidp_utils.h" | ||
| 11 | |||
| 12 | const struct komeda_format_caps * | ||
| 13 | komeda_get_format_caps(struct komeda_format_caps_table *table, | ||
| 14 | u32 fourcc, u64 modifier) | ||
| 15 | { | ||
| 16 | const struct komeda_format_caps *caps; | ||
| 17 | u64 afbc_features = modifier & ~(AFBC_FORMAT_MOD_BLOCK_SIZE_MASK); | ||
| 18 | u32 afbc_layout = modifier & AFBC_FORMAT_MOD_BLOCK_SIZE_MASK; | ||
| 19 | int id; | ||
| 20 | |||
| 21 | for (id = 0; id < table->n_formats; id++) { | ||
| 22 | caps = &table->format_caps[id]; | ||
| 23 | |||
| 24 | if (fourcc != caps->fourcc) | ||
| 25 | continue; | ||
| 26 | |||
| 27 | if ((modifier == 0ULL) && (caps->supported_afbc_layouts == 0)) | ||
| 28 | return caps; | ||
| 29 | |||
| 30 | if (has_bits(afbc_features, caps->supported_afbc_features) && | ||
| 31 | has_bit(afbc_layout, caps->supported_afbc_layouts)) | ||
| 32 | return caps; | ||
| 33 | } | ||
| 34 | |||
| 35 | return NULL; | ||
| 36 | } | ||
| 37 | |||
| 38 | u32 *komeda_get_layer_fourcc_list(struct komeda_format_caps_table *table, | ||
| 39 | u32 layer_type, u32 *n_fmts) | ||
| 40 | { | ||
| 41 | const struct komeda_format_caps *cap; | ||
| 42 | u32 *fmts; | ||
| 43 | int i, j, n = 0; | ||
| 44 | |||
| 45 | fmts = kcalloc(table->n_formats, sizeof(u32), GFP_KERNEL); | ||
| 46 | if (!fmts) | ||
| 47 | return NULL; | ||
| 48 | |||
| 49 | for (i = 0; i < table->n_formats; i++) { | ||
| 50 | cap = &table->format_caps[i]; | ||
| 51 | if (!(layer_type & cap->supported_layer_types) || | ||
| 52 | (cap->fourcc == 0)) | ||
| 53 | continue; | ||
| 54 | |||
| 55 | /* one fourcc may has two caps items in table (afbc/none-afbc), | ||
| 56 | * so check the existing list to avoid adding a duplicated one. | ||
| 57 | */ | ||
| 58 | for (j = n - 1; j >= 0; j--) | ||
| 59 | if (fmts[j] == cap->fourcc) | ||
| 60 | break; | ||
| 61 | |||
| 62 | if (j < 0) | ||
| 63 | fmts[n++] = cap->fourcc; | ||
| 64 | } | ||
| 65 | |||
| 66 | if (n_fmts) | ||
| 67 | *n_fmts = n; | ||
| 68 | |||
| 69 | return fmts; | ||
| 70 | } | ||
| 71 | |||
| 72 | void komeda_put_fourcc_list(u32 *fourcc_list) | ||
| 73 | { | ||
| 74 | kfree(fourcc_list); | ||
| 75 | } | ||
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 index 000000000000..60f39e77b098 --- /dev/null +++ b/drivers/gpu/drm/arm/display/komeda/komeda_format_caps.h | |||
| @@ -0,0 +1,89 @@ | |||
| 1 | /* SPDX-License-Identifier: GPL-2.0 */ | ||
| 2 | /* | ||
| 3 | * (C) COPYRIGHT 2018 ARM Limited. All rights reserved. | ||
| 4 | * Author: James.Qian.Wang <james.qian.wang@arm.com> | ||
| 5 | * | ||
| 6 | */ | ||
| 7 | |||
| 8 | #ifndef _KOMEDA_FORMAT_CAPS_H_ | ||
| 9 | #define _KOMEDA_FORMAT_CAPS_H_ | ||
| 10 | |||
| 11 | #include <linux/types.h> | ||
| 12 | #include <uapi/drm/drm_fourcc.h> | ||
| 13 | #include <drm/drm_fourcc.h> | ||
| 14 | |||
| 15 | #define AFBC(x) DRM_FORMAT_MOD_ARM_AFBC(x) | ||
| 16 | |||
| 17 | /* afbc layerout */ | ||
| 18 | #define AFBC_16x16(x) AFBC(AFBC_FORMAT_MOD_BLOCK_SIZE_16x16 | (x)) | ||
| 19 | #define AFBC_32x8(x) AFBC(AFBC_FORMAT_MOD_BLOCK_SIZE_32x8 | (x)) | ||
| 20 | |||
| 21 | /* afbc features */ | ||
| 22 | #define _YTR AFBC_FORMAT_MOD_YTR | ||
| 23 | #define _SPLIT AFBC_FORMAT_MOD_SPLIT | ||
| 24 | #define _SPARSE AFBC_FORMAT_MOD_SPARSE | ||
| 25 | #define _CBR AFBC_FORMAT_MOD_CBR | ||
| 26 | #define _TILED AFBC_FORMAT_MOD_TILED | ||
| 27 | #define _SC AFBC_FORMAT_MOD_SC | ||
| 28 | |||
| 29 | /* layer_type */ | ||
| 30 | #define KOMEDA_FMT_RICH_LAYER BIT(0) | ||
| 31 | #define KOMEDA_FMT_SIMPLE_LAYER BIT(1) | ||
| 32 | #define KOMEDA_FMT_WB_LAYER BIT(2) | ||
| 33 | |||
| 34 | #define AFBC_TH_LAYOUT_ALIGNMENT 8 | ||
| 35 | #define AFBC_HEADER_SIZE 16 | ||
| 36 | #define AFBC_SUPERBLK_ALIGNMENT 128 | ||
| 37 | #define AFBC_SUPERBLK_PIXELS 256 | ||
| 38 | #define AFBC_BODY_START_ALIGNMENT 1024 | ||
| 39 | #define AFBC_TH_BODY_START_ALIGNMENT 4096 | ||
| 40 | |||
| 41 | /** | ||
| 42 | * struct komeda_format_caps | ||
| 43 | * | ||
| 44 | * komeda_format_caps is for describing ARM display specific features and | ||
| 45 | * limitations for a specific format, and format_caps will be linked into | ||
| 46 | * &komeda_framebuffer like a extension of &drm_format_info. | ||
| 47 | * | ||
| 48 | * NOTE: one fourcc may has two different format_caps items for fourcc and | ||
| 49 | * fourcc+modifier | ||
| 50 | * | ||
| 51 | * @hw_id: hw format id, hw specific value. | ||
| 52 | * @fourcc: drm fourcc format. | ||
| 53 | * @tile_size: format tiled size, used by ARM format X0L0/X0L2 | ||
| 54 | * @supported_layer_types: indicate which layer supports this format | ||
| 55 | * @supported_rots: allowed rotations for this format | ||
| 56 | * @supported_afbc_layouts: supported afbc layerout | ||
| 57 | * @supported_afbc_features: supported afbc features | ||
| 58 | */ | ||
| 59 | struct komeda_format_caps { | ||
| 60 | u32 hw_id; | ||
| 61 | u32 fourcc; | ||
| 62 | u32 tile_size; | ||
| 63 | u32 supported_layer_types; | ||
| 64 | u32 supported_rots; | ||
| 65 | u32 supported_afbc_layouts; | ||
| 66 | u64 supported_afbc_features; | ||
| 67 | }; | ||
| 68 | |||
| 69 | /** | ||
| 70 | * struct komeda_format_caps_table - format_caps mananger | ||
| 71 | * | ||
| 72 | * @n_formats: the size of format_caps list. | ||
| 73 | * @format_caps: format_caps list. | ||
| 74 | */ | ||
| 75 | struct komeda_format_caps_table { | ||
| 76 | u32 n_formats; | ||
| 77 | const struct komeda_format_caps *format_caps; | ||
| 78 | }; | ||
| 79 | |||
| 80 | const struct komeda_format_caps * | ||
| 81 | komeda_get_format_caps(struct komeda_format_caps_table *table, | ||
| 82 | u32 fourcc, u64 modifier); | ||
| 83 | |||
| 84 | u32 *komeda_get_layer_fourcc_list(struct komeda_format_caps_table *table, | ||
| 85 | u32 layer_type, u32 *n_fmts); | ||
| 86 | |||
| 87 | void komeda_put_fourcc_list(u32 *fourcc_list); | ||
| 88 | |||
| 89 | #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 index 000000000000..23ee74d42239 --- /dev/null +++ b/drivers/gpu/drm/arm/display/komeda/komeda_framebuffer.c | |||
| @@ -0,0 +1,165 @@ | |||
| 1 | // SPDX-License-Identifier: GPL-2.0 | ||
| 2 | /* | ||
| 3 | * (C) COPYRIGHT 2018 ARM Limited. All rights reserved. | ||
| 4 | * Author: James.Qian.Wang <james.qian.wang@arm.com> | ||
| 5 | * | ||
| 6 | */ | ||
| 7 | #include <drm/drm_gem.h> | ||
| 8 | #include <drm/drm_gem_framebuffer_helper.h> | ||
| 9 | #include <drm/drm_fb_cma_helper.h> | ||
| 10 | #include <drm/drm_gem_cma_helper.h> | ||
| 11 | #include "komeda_framebuffer.h" | ||
| 12 | #include "komeda_dev.h" | ||
| 13 | |||
| 14 | static void komeda_fb_destroy(struct drm_framebuffer *fb) | ||
| 15 | { | ||
| 16 | struct komeda_fb *kfb = to_kfb(fb); | ||
| 17 | u32 i; | ||
| 18 | |||
| 19 | for (i = 0; i < fb->format->num_planes; i++) | ||
| 20 | drm_gem_object_put_unlocked(fb->obj[i]); | ||
| 21 | |||
| 22 | drm_framebuffer_cleanup(fb); | ||
| 23 | kfree(kfb); | ||
| 24 | } | ||
| 25 | |||
| 26 | static int komeda_fb_create_handle(struct drm_framebuffer *fb, | ||
| 27 | struct drm_file *file, u32 *handle) | ||
| 28 | { | ||
| 29 | return drm_gem_handle_create(file, fb->obj[0], handle); | ||
| 30 | } | ||
| 31 | |||
| 32 | static const struct drm_framebuffer_funcs komeda_fb_funcs = { | ||
| 33 | .destroy = komeda_fb_destroy, | ||
| 34 | .create_handle = komeda_fb_create_handle, | ||
| 35 | }; | ||
| 36 | |||
| 37 | static int | ||
| 38 | komeda_fb_none_afbc_size_check(struct komeda_dev *mdev, struct komeda_fb *kfb, | ||
| 39 | struct drm_file *file, | ||
| 40 | const struct drm_mode_fb_cmd2 *mode_cmd) | ||
| 41 | { | ||
| 42 | struct drm_framebuffer *fb = &kfb->base; | ||
| 43 | struct drm_gem_object *obj; | ||
| 44 | u32 min_size = 0; | ||
| 45 | u32 i; | ||
| 46 | |||
| 47 | for (i = 0; i < fb->format->num_planes; i++) { | ||
| 48 | obj = drm_gem_object_lookup(file, mode_cmd->handles[i]); | ||
| 49 | if (!obj) { | ||
| 50 | DRM_DEBUG_KMS("Failed to lookup GEM object\n"); | ||
| 51 | fb->obj[i] = NULL; | ||
| 52 | |||
| 53 | return -ENOENT; | ||
| 54 | } | ||
| 55 | |||
| 56 | kfb->aligned_w = fb->width / (i ? fb->format->hsub : 1); | ||
| 57 | kfb->aligned_h = fb->height / (i ? fb->format->vsub : 1); | ||
| 58 | |||
| 59 | if (fb->pitches[i] % mdev->chip.bus_width) { | ||
| 60 | DRM_DEBUG_KMS("Pitch[%d]: 0x%x doesn't align to 0x%x\n", | ||
| 61 | i, fb->pitches[i], mdev->chip.bus_width); | ||
| 62 | drm_gem_object_put_unlocked(obj); | ||
| 63 | fb->obj[i] = NULL; | ||
| 64 | |||
| 65 | return -EINVAL; | ||
| 66 | } | ||
| 67 | |||
| 68 | min_size = ((kfb->aligned_h / kfb->format_caps->tile_size - 1) | ||
| 69 | * fb->pitches[i]) | ||
| 70 | + (kfb->aligned_w * fb->format->cpp[i] | ||
| 71 | * kfb->format_caps->tile_size) | ||
| 72 | + fb->offsets[i]; | ||
| 73 | |||
| 74 | if (obj->size < min_size) { | ||
| 75 | DRM_DEBUG_KMS("Fail to check none afbc fb size.\n"); | ||
| 76 | drm_gem_object_put_unlocked(obj); | ||
| 77 | fb->obj[i] = NULL; | ||
| 78 | |||
| 79 | return -EINVAL; | ||
| 80 | } | ||
| 81 | |||
| 82 | fb->obj[i] = obj; | ||
| 83 | } | ||
| 84 | |||
| 85 | if (fb->format->num_planes == 3) { | ||
| 86 | if (fb->pitches[1] != fb->pitches[2]) { | ||
| 87 | DRM_DEBUG_KMS("The pitch[1] and [2] are not same\n"); | ||
| 88 | return -EINVAL; | ||
| 89 | } | ||
| 90 | } | ||
| 91 | |||
| 92 | return 0; | ||
| 93 | } | ||
| 94 | |||
| 95 | struct drm_framebuffer * | ||
| 96 | komeda_fb_create(struct drm_device *dev, struct drm_file *file, | ||
| 97 | const struct drm_mode_fb_cmd2 *mode_cmd) | ||
| 98 | { | ||
| 99 | struct komeda_dev *mdev = dev->dev_private; | ||
| 100 | struct komeda_fb *kfb; | ||
| 101 | int ret = 0, i; | ||
| 102 | |||
| 103 | kfb = kzalloc(sizeof(*kfb), GFP_KERNEL); | ||
| 104 | if (!kfb) | ||
| 105 | return ERR_PTR(-ENOMEM); | ||
| 106 | |||
| 107 | kfb->format_caps = komeda_get_format_caps(&mdev->fmt_tbl, | ||
| 108 | mode_cmd->pixel_format, | ||
| 109 | mode_cmd->modifier[0]); | ||
| 110 | if (!kfb->format_caps) { | ||
| 111 | DRM_DEBUG_KMS("FMT %x is not supported.\n", | ||
| 112 | mode_cmd->pixel_format); | ||
| 113 | kfree(kfb); | ||
| 114 | return ERR_PTR(-EINVAL); | ||
| 115 | } | ||
| 116 | |||
| 117 | drm_helper_mode_fill_fb_struct(dev, &kfb->base, mode_cmd); | ||
| 118 | |||
| 119 | ret = komeda_fb_none_afbc_size_check(mdev, kfb, file, mode_cmd); | ||
| 120 | if (ret < 0) | ||
| 121 | goto err_cleanup; | ||
| 122 | |||
| 123 | ret = drm_framebuffer_init(dev, &kfb->base, &komeda_fb_funcs); | ||
| 124 | if (ret < 0) { | ||
| 125 | DRM_DEBUG_KMS("failed to initialize fb\n"); | ||
| 126 | |||
| 127 | goto err_cleanup; | ||
| 128 | } | ||
| 129 | |||
| 130 | return &kfb->base; | ||
| 131 | |||
| 132 | err_cleanup: | ||
| 133 | for (i = 0; i < kfb->base.format->num_planes; i++) | ||
| 134 | drm_gem_object_put_unlocked(kfb->base.obj[i]); | ||
| 135 | |||
| 136 | kfree(kfb); | ||
| 137 | return ERR_PTR(ret); | ||
| 138 | } | ||
| 139 | |||
| 140 | dma_addr_t | ||
| 141 | komeda_fb_get_pixel_addr(struct komeda_fb *kfb, int x, int y, int plane) | ||
| 142 | { | ||
| 143 | struct drm_framebuffer *fb = &kfb->base; | ||
| 144 | const struct drm_gem_cma_object *obj; | ||
| 145 | u32 plane_x, plane_y, cpp, pitch, offset; | ||
| 146 | |||
| 147 | if (plane >= fb->format->num_planes) { | ||
| 148 | DRM_DEBUG_KMS("Out of max plane num.\n"); | ||
| 149 | return -EINVAL; | ||
| 150 | } | ||
| 151 | |||
| 152 | obj = drm_fb_cma_get_gem_obj(fb, plane); | ||
| 153 | |||
| 154 | offset = fb->offsets[plane]; | ||
| 155 | if (!fb->modifier) { | ||
| 156 | plane_x = x / (plane ? fb->format->hsub : 1); | ||
| 157 | plane_y = y / (plane ? fb->format->vsub : 1); | ||
| 158 | cpp = fb->format->cpp[plane]; | ||
| 159 | pitch = fb->pitches[plane]; | ||
| 160 | offset += plane_x * cpp * kfb->format_caps->tile_size + | ||
| 161 | (plane_y * pitch) / kfb->format_caps->tile_size; | ||
| 162 | } | ||
| 163 | |||
| 164 | return obj->paddr + offset; | ||
| 165 | } | ||
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 index 000000000000..0de2e4a2afd2 --- /dev/null +++ b/drivers/gpu/drm/arm/display/komeda/komeda_framebuffer.h | |||
| @@ -0,0 +1,34 @@ | |||
| 1 | /* SPDX-License-Identifier: GPL-2.0 */ | ||
| 2 | /* | ||
| 3 | * (C) COPYRIGHT 2018 ARM Limited. All rights reserved. | ||
| 4 | * Author: James.Qian.Wang <james.qian.wang@arm.com> | ||
| 5 | * | ||
| 6 | */ | ||
| 7 | #ifndef _KOMEDA_FRAMEBUFFER_H_ | ||
| 8 | #define _KOMEDA_FRAMEBUFFER_H_ | ||
| 9 | |||
| 10 | #include <drm/drm_framebuffer.h> | ||
| 11 | #include "komeda_format_caps.h" | ||
| 12 | |||
| 13 | /** struct komeda_fb - entend drm_framebuffer with komeda attribute */ | ||
| 14 | struct komeda_fb { | ||
| 15 | /** @base: &drm_framebuffer */ | ||
| 16 | struct drm_framebuffer base; | ||
| 17 | /* @format_caps: &komeda_format_caps */ | ||
| 18 | const struct komeda_format_caps *format_caps; | ||
| 19 | /** @aligned_w: aligned frame buffer width */ | ||
| 20 | u32 aligned_w; | ||
| 21 | /** @aligned_h: aligned frame buffer height */ | ||
| 22 | u32 aligned_h; | ||
| 23 | }; | ||
| 24 | |||
| 25 | #define to_kfb(dfb) container_of(dfb, struct komeda_fb, base) | ||
| 26 | |||
| 27 | struct drm_framebuffer * | ||
| 28 | komeda_fb_create(struct drm_device *dev, struct drm_file *file, | ||
| 29 | const struct drm_mode_fb_cmd2 *mode_cmd); | ||
| 30 | dma_addr_t | ||
| 31 | komeda_fb_get_pixel_addr(struct komeda_fb *kfb, int x, int y, int plane); | ||
| 32 | bool komeda_fb_is_layer_supported(struct komeda_fb *kfb, u32 layer_type); | ||
| 33 | |||
| 34 | #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 index 000000000000..3fc096d3883e --- /dev/null +++ b/drivers/gpu/drm/arm/display/komeda/komeda_kms.c | |||
| @@ -0,0 +1,167 @@ | |||
| 1 | // SPDX-License-Identifier: GPL-2.0 | ||
| 2 | /* | ||
| 3 | * (C) COPYRIGHT 2018 ARM Limited. All rights reserved. | ||
| 4 | * Author: James.Qian.Wang <james.qian.wang@arm.com> | ||
| 5 | * | ||
| 6 | */ | ||
| 7 | #include <linux/component.h> | ||
| 8 | #include <drm/drm_atomic.h> | ||
| 9 | #include <drm/drm_atomic_helper.h> | ||
| 10 | #include <drm/drm_gem_framebuffer_helper.h> | ||
| 11 | #include <drm/drm_gem_cma_helper.h> | ||
| 12 | #include <drm/drm_fb_helper.h> | ||
| 13 | #include <linux/interrupt.h> | ||
| 14 | #include "komeda_dev.h" | ||
| 15 | #include "komeda_kms.h" | ||
| 16 | #include "komeda_framebuffer.h" | ||
| 17 | |||
| 18 | DEFINE_DRM_GEM_CMA_FOPS(komeda_cma_fops); | ||
| 19 | |||
| 20 | static int komeda_gem_cma_dumb_create(struct drm_file *file, | ||
| 21 | struct drm_device *dev, | ||
| 22 | struct drm_mode_create_dumb *args) | ||
| 23 | { | ||
| 24 | u32 alignment = 16; /* TODO get alignment from dev */ | ||
| 25 | |||
| 26 | args->pitch = ALIGN(DIV_ROUND_UP(args->width * args->bpp, 8), | ||
| 27 | alignment); | ||
| 28 | |||
| 29 | return drm_gem_cma_dumb_create_internal(file, dev, args); | ||
| 30 | } | ||
| 31 | |||
| 32 | static struct drm_driver komeda_kms_driver = { | ||
| 33 | .driver_features = DRIVER_GEM | DRIVER_MODESET | DRIVER_ATOMIC | | ||
| 34 | DRIVER_PRIME, | ||
| 35 | .lastclose = drm_fb_helper_lastclose, | ||
| 36 | .gem_free_object_unlocked = drm_gem_cma_free_object, | ||
| 37 | .gem_vm_ops = &drm_gem_cma_vm_ops, | ||
| 38 | .dumb_create = komeda_gem_cma_dumb_create, | ||
| 39 | .prime_handle_to_fd = drm_gem_prime_handle_to_fd, | ||
| 40 | .prime_fd_to_handle = drm_gem_prime_fd_to_handle, | ||
| 41 | .gem_prime_export = drm_gem_prime_export, | ||
| 42 | .gem_prime_import = drm_gem_prime_import, | ||
| 43 | .gem_prime_get_sg_table = drm_gem_cma_prime_get_sg_table, | ||
| 44 | .gem_prime_import_sg_table = drm_gem_cma_prime_import_sg_table, | ||
| 45 | .gem_prime_vmap = drm_gem_cma_prime_vmap, | ||
| 46 | .gem_prime_vunmap = drm_gem_cma_prime_vunmap, | ||
| 47 | .gem_prime_mmap = drm_gem_cma_prime_mmap, | ||
| 48 | .fops = &komeda_cma_fops, | ||
| 49 | .name = "komeda", | ||
| 50 | .desc = "Arm Komeda Display Processor driver", | ||
| 51 | .date = "20181101", | ||
| 52 | .major = 0, | ||
| 53 | .minor = 1, | ||
| 54 | }; | ||
| 55 | |||
| 56 | static void komeda_kms_commit_tail(struct drm_atomic_state *old_state) | ||
| 57 | { | ||
| 58 | struct drm_device *dev = old_state->dev; | ||
| 59 | |||
| 60 | drm_atomic_helper_commit_modeset_disables(dev, old_state); | ||
| 61 | |||
| 62 | drm_atomic_helper_commit_planes(dev, old_state, 0); | ||
| 63 | |||
| 64 | drm_atomic_helper_commit_modeset_enables(dev, old_state); | ||
| 65 | |||
| 66 | drm_atomic_helper_wait_for_flip_done(dev, old_state); | ||
| 67 | |||
| 68 | drm_atomic_helper_commit_hw_done(old_state); | ||
| 69 | |||
| 70 | drm_atomic_helper_cleanup_planes(dev, old_state); | ||
| 71 | } | ||
| 72 | |||
| 73 | static const struct drm_mode_config_helper_funcs komeda_mode_config_helpers = { | ||
| 74 | .atomic_commit_tail = komeda_kms_commit_tail, | ||
| 75 | }; | ||
| 76 | |||
| 77 | static const struct drm_mode_config_funcs komeda_mode_config_funcs = { | ||
| 78 | .fb_create = komeda_fb_create, | ||
| 79 | .atomic_check = drm_atomic_helper_check, | ||
| 80 | .atomic_commit = drm_atomic_helper_commit, | ||
| 81 | }; | ||
| 82 | |||
| 83 | static void komeda_kms_mode_config_init(struct komeda_kms_dev *kms, | ||
| 84 | struct komeda_dev *mdev) | ||
| 85 | { | ||
| 86 | struct drm_mode_config *config = &kms->base.mode_config; | ||
| 87 | |||
| 88 | drm_mode_config_init(&kms->base); | ||
| 89 | |||
| 90 | komeda_kms_setup_crtcs(kms, mdev); | ||
| 91 | |||
| 92 | /* Get value from dev */ | ||
| 93 | config->min_width = 0; | ||
| 94 | config->min_height = 0; | ||
| 95 | config->max_width = 4096; | ||
| 96 | config->max_height = 4096; | ||
| 97 | config->allow_fb_modifiers = false; | ||
| 98 | |||
| 99 | config->funcs = &komeda_mode_config_funcs; | ||
| 100 | config->helper_private = &komeda_mode_config_helpers; | ||
| 101 | } | ||
| 102 | |||
| 103 | struct komeda_kms_dev *komeda_kms_attach(struct komeda_dev *mdev) | ||
| 104 | { | ||
| 105 | struct komeda_kms_dev *kms = kzalloc(sizeof(*kms), GFP_KERNEL); | ||
| 106 | struct drm_device *drm; | ||
| 107 | int err; | ||
| 108 | |||
| 109 | if (!kms) | ||
| 110 | return ERR_PTR(-ENOMEM); | ||
| 111 | |||
| 112 | drm = &kms->base; | ||
| 113 | err = drm_dev_init(drm, &komeda_kms_driver, mdev->dev); | ||
| 114 | if (err) | ||
| 115 | goto free_kms; | ||
| 116 | |||
| 117 | drm->dev_private = mdev; | ||
| 118 | |||
| 119 | komeda_kms_mode_config_init(kms, mdev); | ||
| 120 | |||
| 121 | err = komeda_kms_add_private_objs(kms, mdev); | ||
| 122 | if (err) | ||
| 123 | goto cleanup_mode_config; | ||
| 124 | |||
| 125 | err = komeda_kms_add_planes(kms, mdev); | ||
| 126 | if (err) | ||
| 127 | goto cleanup_mode_config; | ||
| 128 | |||
| 129 | err = drm_vblank_init(drm, kms->n_crtcs); | ||
| 130 | if (err) | ||
| 131 | goto cleanup_mode_config; | ||
| 132 | |||
| 133 | err = komeda_kms_add_crtcs(kms, mdev); | ||
| 134 | if (err) | ||
| 135 | goto cleanup_mode_config; | ||
| 136 | |||
| 137 | err = component_bind_all(mdev->dev, kms); | ||
| 138 | if (err) | ||
| 139 | goto cleanup_mode_config; | ||
| 140 | |||
| 141 | drm_mode_config_reset(drm); | ||
| 142 | |||
| 143 | err = drm_dev_register(drm, 0); | ||
| 144 | if (err) | ||
| 145 | goto cleanup_mode_config; | ||
| 146 | |||
| 147 | return kms; | ||
| 148 | |||
| 149 | cleanup_mode_config: | ||
| 150 | drm_mode_config_cleanup(drm); | ||
| 151 | free_kms: | ||
| 152 | kfree(kms); | ||
| 153 | return ERR_PTR(err); | ||
| 154 | } | ||
| 155 | |||
| 156 | void komeda_kms_detach(struct komeda_kms_dev *kms) | ||
| 157 | { | ||
| 158 | struct drm_device *drm = &kms->base; | ||
| 159 | struct komeda_dev *mdev = drm->dev_private; | ||
| 160 | |||
| 161 | drm_dev_unregister(drm); | ||
| 162 | component_unbind_all(mdev->dev, drm); | ||
| 163 | komeda_kms_cleanup_private_objs(mdev); | ||
| 164 | drm_mode_config_cleanup(drm); | ||
| 165 | drm->dev_private = NULL; | ||
| 166 | drm_dev_put(drm); | ||
| 167 | } | ||
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 index 000000000000..f13666004a42 --- /dev/null +++ b/drivers/gpu/drm/arm/display/komeda/komeda_kms.h | |||
| @@ -0,0 +1,113 @@ | |||
| 1 | /* SPDX-License-Identifier: GPL-2.0 */ | ||
| 2 | /* | ||
| 3 | * (C) COPYRIGHT 2018 ARM Limited. All rights reserved. | ||
| 4 | * Author: James.Qian.Wang <james.qian.wang@arm.com> | ||
| 5 | * | ||
| 6 | */ | ||
| 7 | #ifndef _KOMEDA_KMS_H_ | ||
| 8 | #define _KOMEDA_KMS_H_ | ||
| 9 | |||
| 10 | #include <drm/drm_atomic.h> | ||
| 11 | #include <drm/drm_atomic_helper.h> | ||
| 12 | #include <drm/drm_crtc_helper.h> | ||
| 13 | #include <drm/drm_writeback.h> | ||
| 14 | |||
| 15 | /** struct komeda_plane - komeda instance of drm_plane */ | ||
| 16 | struct komeda_plane { | ||
| 17 | /** @base: &drm_plane */ | ||
| 18 | struct drm_plane base; | ||
| 19 | /** | ||
| 20 | * @layer: | ||
| 21 | * | ||
| 22 | * represents available layer input pipelines for this plane. | ||
| 23 | * | ||
| 24 | * NOTE: | ||
| 25 | * the layer is not for a specific Layer, but indicate a group of | ||
| 26 | * Layers with same capabilities. | ||
| 27 | */ | ||
| 28 | struct komeda_layer *layer; | ||
| 29 | }; | ||
| 30 | |||
| 31 | /** | ||
| 32 | * struct komeda_plane_state | ||
| 33 | * | ||
| 34 | * The plane_state can be split into two data flow (left/right) and handled | ||
| 35 | * by two layers &komeda_plane.layer and &komeda_plane.layer.right | ||
| 36 | */ | ||
| 37 | struct komeda_plane_state { | ||
| 38 | /** @base: &drm_plane_state */ | ||
| 39 | struct drm_plane_state base; | ||
| 40 | |||
| 41 | /* private properties */ | ||
| 42 | }; | ||
| 43 | |||
| 44 | /** | ||
| 45 | * struct komeda_wb_connector | ||
| 46 | */ | ||
| 47 | struct komeda_wb_connector { | ||
| 48 | /** @base: &drm_writeback_connector */ | ||
| 49 | struct drm_writeback_connector base; | ||
| 50 | |||
| 51 | /** @wb_layer: represents associated writeback pipeline of komeda */ | ||
| 52 | struct komeda_layer *wb_layer; | ||
| 53 | }; | ||
| 54 | |||
| 55 | /** | ||
| 56 | * struct komeda_crtc | ||
| 57 | */ | ||
| 58 | struct komeda_crtc { | ||
| 59 | /** @base: &drm_crtc */ | ||
| 60 | struct drm_crtc base; | ||
| 61 | /** @master: only master has display output */ | ||
| 62 | struct komeda_pipeline *master; | ||
| 63 | /** | ||
| 64 | * @slave: optional | ||
| 65 | * | ||
| 66 | * Doesn't have its own display output, the handled data flow will | ||
| 67 | * merge into the master. | ||
| 68 | */ | ||
| 69 | struct komeda_pipeline *slave; | ||
| 70 | }; | ||
| 71 | |||
| 72 | /** struct komeda_crtc_state */ | ||
| 73 | struct komeda_crtc_state { | ||
| 74 | /** @base: &drm_crtc_state */ | ||
| 75 | struct drm_crtc_state base; | ||
| 76 | |||
| 77 | /* private properties */ | ||
| 78 | |||
| 79 | /* computed state which are used by validate/check */ | ||
| 80 | u32 affected_pipes; | ||
| 81 | u32 active_pipes; | ||
| 82 | }; | ||
| 83 | |||
| 84 | /** struct komeda_kms_dev - for gather KMS related things */ | ||
| 85 | struct komeda_kms_dev { | ||
| 86 | /** @base: &drm_device */ | ||
| 87 | struct drm_device base; | ||
| 88 | |||
| 89 | /** @n_crtcs: valid numbers of crtcs in &komeda_kms_dev.crtcs */ | ||
| 90 | int n_crtcs; | ||
| 91 | /** @crtcs: crtcs list */ | ||
| 92 | struct komeda_crtc crtcs[KOMEDA_MAX_PIPELINES]; | ||
| 93 | }; | ||
| 94 | |||
| 95 | #define to_kplane(p) container_of(p, struct komeda_plane, base) | ||
| 96 | #define to_kplane_st(p) container_of(p, struct komeda_plane_state, base) | ||
| 97 | #define to_kconn(p) container_of(p, struct komeda_wb_connector, base) | ||
| 98 | #define to_kcrtc(p) container_of(p, struct komeda_crtc, base) | ||
| 99 | #define to_kcrtc_st(p) container_of(p, struct komeda_crtc_state, base) | ||
| 100 | #define to_kdev(p) container_of(p, struct komeda_kms_dev, base) | ||
| 101 | |||
| 102 | int komeda_kms_setup_crtcs(struct komeda_kms_dev *kms, struct komeda_dev *mdev); | ||
| 103 | |||
| 104 | int komeda_kms_add_crtcs(struct komeda_kms_dev *kms, struct komeda_dev *mdev); | ||
| 105 | int komeda_kms_add_planes(struct komeda_kms_dev *kms, struct komeda_dev *mdev); | ||
| 106 | int komeda_kms_add_private_objs(struct komeda_kms_dev *kms, | ||
| 107 | struct komeda_dev *mdev); | ||
| 108 | void komeda_kms_cleanup_private_objs(struct komeda_dev *mdev); | ||
| 109 | |||
| 110 | struct komeda_kms_dev *komeda_kms_attach(struct komeda_dev *mdev); | ||
| 111 | void komeda_kms_detach(struct komeda_kms_dev *kms); | ||
| 112 | |||
| 113 | #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 index 000000000000..edb1cd7795f9 --- /dev/null +++ b/drivers/gpu/drm/arm/display/komeda/komeda_pipeline.c | |||
| @@ -0,0 +1,200 @@ | |||
| 1 | // SPDX-License-Identifier: GPL-2.0 | ||
| 2 | /* | ||
| 3 | * (C) COPYRIGHT 2018 ARM Limited. All rights reserved. | ||
| 4 | * Author: James.Qian.Wang <james.qian.wang@arm.com> | ||
| 5 | * | ||
| 6 | */ | ||
| 7 | #include "komeda_dev.h" | ||
| 8 | #include "komeda_pipeline.h" | ||
| 9 | |||
| 10 | /** komeda_pipeline_add - Add a pipeline to &komeda_dev */ | ||
| 11 | struct komeda_pipeline * | ||
| 12 | komeda_pipeline_add(struct komeda_dev *mdev, size_t size, | ||
| 13 | struct komeda_pipeline_funcs *funcs) | ||
| 14 | { | ||
| 15 | struct komeda_pipeline *pipe; | ||
| 16 | |||
| 17 | if (mdev->n_pipelines + 1 > KOMEDA_MAX_PIPELINES) { | ||
| 18 | DRM_ERROR("Exceed max support %d pipelines.\n", | ||
| 19 | KOMEDA_MAX_PIPELINES); | ||
| 20 | return NULL; | ||
| 21 | } | ||
| 22 | |||
| 23 | if (size < sizeof(*pipe)) { | ||
| 24 | DRM_ERROR("Request pipeline size too small.\n"); | ||
| 25 | return NULL; | ||
| 26 | } | ||
| 27 | |||
| 28 | pipe = devm_kzalloc(mdev->dev, size, GFP_KERNEL); | ||
| 29 | if (!pipe) | ||
| 30 | return NULL; | ||
| 31 | |||
| 32 | pipe->mdev = mdev; | ||
| 33 | pipe->id = mdev->n_pipelines; | ||
| 34 | pipe->funcs = funcs; | ||
| 35 | |||
| 36 | mdev->pipelines[mdev->n_pipelines] = pipe; | ||
| 37 | mdev->n_pipelines++; | ||
| 38 | |||
| 39 | return pipe; | ||
| 40 | } | ||
| 41 | |||
| 42 | void komeda_pipeline_destroy(struct komeda_dev *mdev, | ||
| 43 | struct komeda_pipeline *pipe) | ||
| 44 | { | ||
| 45 | struct komeda_component *c; | ||
| 46 | int i; | ||
| 47 | |||
| 48 | dp_for_each_set_bit(i, pipe->avail_comps) { | ||
| 49 | c = komeda_pipeline_get_component(pipe, i); | ||
| 50 | komeda_component_destroy(mdev, c); | ||
| 51 | } | ||
| 52 | |||
| 53 | clk_put(pipe->pxlclk); | ||
| 54 | clk_put(pipe->aclk); | ||
| 55 | |||
| 56 | of_node_put(pipe->of_output_dev); | ||
| 57 | of_node_put(pipe->of_output_port); | ||
| 58 | of_node_put(pipe->of_node); | ||
| 59 | |||
| 60 | devm_kfree(mdev->dev, pipe); | ||
| 61 | } | ||
| 62 | |||
| 63 | struct komeda_component ** | ||
| 64 | komeda_pipeline_get_component_pos(struct komeda_pipeline *pipe, int id) | ||
| 65 | { | ||
| 66 | struct komeda_dev *mdev = pipe->mdev; | ||
| 67 | struct komeda_pipeline *temp = NULL; | ||
| 68 | struct komeda_component **pos = NULL; | ||
| 69 | |||
| 70 | switch (id) { | ||
| 71 | case KOMEDA_COMPONENT_LAYER0: | ||
| 72 | case KOMEDA_COMPONENT_LAYER1: | ||
| 73 | case KOMEDA_COMPONENT_LAYER2: | ||
| 74 | case KOMEDA_COMPONENT_LAYER3: | ||
| 75 | pos = to_cpos(pipe->layers[id - KOMEDA_COMPONENT_LAYER0]); | ||
| 76 | break; | ||
| 77 | case KOMEDA_COMPONENT_WB_LAYER: | ||
| 78 | pos = to_cpos(pipe->wb_layer); | ||
| 79 | break; | ||
| 80 | case KOMEDA_COMPONENT_COMPIZ0: | ||
| 81 | case KOMEDA_COMPONENT_COMPIZ1: | ||
| 82 | temp = mdev->pipelines[id - KOMEDA_COMPONENT_COMPIZ0]; | ||
| 83 | if (!temp) { | ||
| 84 | DRM_ERROR("compiz-%d doesn't exist.\n", id); | ||
| 85 | return NULL; | ||
| 86 | } | ||
| 87 | pos = to_cpos(temp->compiz); | ||
| 88 | break; | ||
| 89 | case KOMEDA_COMPONENT_SCALER0: | ||
| 90 | case KOMEDA_COMPONENT_SCALER1: | ||
| 91 | pos = to_cpos(pipe->scalers[id - KOMEDA_COMPONENT_SCALER0]); | ||
| 92 | break; | ||
| 93 | case KOMEDA_COMPONENT_IPS0: | ||
| 94 | case KOMEDA_COMPONENT_IPS1: | ||
| 95 | temp = mdev->pipelines[id - KOMEDA_COMPONENT_IPS0]; | ||
| 96 | if (!temp) { | ||
| 97 | DRM_ERROR("ips-%d doesn't exist.\n", id); | ||
| 98 | return NULL; | ||
| 99 | } | ||
| 100 | pos = to_cpos(temp->improc); | ||
| 101 | break; | ||
| 102 | case KOMEDA_COMPONENT_TIMING_CTRLR: | ||
| 103 | pos = to_cpos(pipe->ctrlr); | ||
| 104 | break; | ||
| 105 | default: | ||
| 106 | pos = NULL; | ||
| 107 | DRM_ERROR("Unknown pipeline resource ID: %d.\n", id); | ||
| 108 | break; | ||
| 109 | } | ||
| 110 | |||
| 111 | return pos; | ||
| 112 | } | ||
| 113 | |||
| 114 | struct komeda_component * | ||
| 115 | komeda_pipeline_get_component(struct komeda_pipeline *pipe, int id) | ||
| 116 | { | ||
| 117 | struct komeda_component **pos = NULL; | ||
| 118 | struct komeda_component *c = NULL; | ||
| 119 | |||
| 120 | pos = komeda_pipeline_get_component_pos(pipe, id); | ||
| 121 | if (pos) | ||
| 122 | c = *pos; | ||
| 123 | |||
| 124 | return c; | ||
| 125 | } | ||
| 126 | |||
| 127 | /** komeda_component_add - Add a component to &komeda_pipeline */ | ||
| 128 | struct komeda_component * | ||
| 129 | komeda_component_add(struct komeda_pipeline *pipe, | ||
| 130 | size_t comp_sz, u32 id, u32 hw_id, | ||
| 131 | struct komeda_component_funcs *funcs, | ||
| 132 | u8 max_active_inputs, u32 supported_inputs, | ||
| 133 | u8 max_active_outputs, u32 __iomem *reg, | ||
| 134 | const char *name_fmt, ...) | ||
| 135 | { | ||
| 136 | struct komeda_component **pos; | ||
| 137 | struct komeda_component *c; | ||
| 138 | int idx, *num = NULL; | ||
| 139 | |||
| 140 | if (max_active_inputs > KOMEDA_COMPONENT_N_INPUTS) { | ||
| 141 | WARN(1, "please large KOMEDA_COMPONENT_N_INPUTS to %d.\n", | ||
| 142 | max_active_inputs); | ||
| 143 | return NULL; | ||
| 144 | } | ||
| 145 | |||
| 146 | pos = komeda_pipeline_get_component_pos(pipe, id); | ||
| 147 | if (!pos || (*pos)) | ||
| 148 | return NULL; | ||
| 149 | |||
| 150 | if (has_bit(id, KOMEDA_PIPELINE_LAYERS)) { | ||
| 151 | idx = id - KOMEDA_COMPONENT_LAYER0; | ||
| 152 | num = &pipe->n_layers; | ||
| 153 | if (idx != pipe->n_layers) { | ||
| 154 | DRM_ERROR("please add Layer by id sequence.\n"); | ||
| 155 | return NULL; | ||
| 156 | } | ||
| 157 | } else if (has_bit(id, KOMEDA_PIPELINE_SCALERS)) { | ||
| 158 | idx = id - KOMEDA_COMPONENT_SCALER0; | ||
| 159 | num = &pipe->n_scalers; | ||
| 160 | if (idx != pipe->n_scalers) { | ||
| 161 | DRM_ERROR("please add Scaler by id sequence.\n"); | ||
| 162 | return NULL; | ||
| 163 | } | ||
| 164 | } | ||
| 165 | |||
| 166 | c = devm_kzalloc(pipe->mdev->dev, comp_sz, GFP_KERNEL); | ||
| 167 | if (!c) | ||
| 168 | return NULL; | ||
| 169 | |||
| 170 | c->id = id; | ||
| 171 | c->hw_id = hw_id; | ||
| 172 | c->reg = reg; | ||
| 173 | c->pipeline = pipe; | ||
| 174 | c->max_active_inputs = max_active_inputs; | ||
| 175 | c->max_active_outputs = max_active_outputs; | ||
| 176 | c->supported_inputs = supported_inputs; | ||
| 177 | c->funcs = funcs; | ||
| 178 | |||
| 179 | if (name_fmt) { | ||
| 180 | va_list args; | ||
| 181 | |||
| 182 | va_start(args, name_fmt); | ||
| 183 | vsnprintf(c->name, sizeof(c->name), name_fmt, args); | ||
| 184 | va_end(args); | ||
| 185 | } | ||
| 186 | |||
| 187 | if (num) | ||
| 188 | *num = *num + 1; | ||
| 189 | |||
| 190 | pipe->avail_comps |= BIT(c->id); | ||
| 191 | *pos = c; | ||
| 192 | |||
| 193 | return c; | ||
| 194 | } | ||
| 195 | |||
| 196 | void komeda_component_destroy(struct komeda_dev *mdev, | ||
| 197 | struct komeda_component *c) | ||
| 198 | { | ||
| 199 | devm_kfree(mdev->dev, c); | ||
| 200 | } | ||
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 index 000000000000..8c950bc8ae96 --- /dev/null +++ b/drivers/gpu/drm/arm/display/komeda/komeda_pipeline.h | |||
| @@ -0,0 +1,359 @@ | |||
| 1 | /* SPDX-License-Identifier: GPL-2.0 */ | ||
| 2 | /* | ||
| 3 | * (C) COPYRIGHT 2018 ARM Limited. All rights reserved. | ||
| 4 | * Author: James.Qian.Wang <james.qian.wang@arm.com> | ||
| 5 | * | ||
| 6 | */ | ||
| 7 | #ifndef _KOMEDA_PIPELINE_H_ | ||
| 8 | #define _KOMEDA_PIPELINE_H_ | ||
| 9 | |||
| 10 | #include <linux/types.h> | ||
| 11 | #include <drm/drm_atomic.h> | ||
| 12 | #include <drm/drm_atomic_helper.h> | ||
| 13 | #include "malidp_utils.h" | ||
| 14 | |||
| 15 | #define KOMEDA_MAX_PIPELINES 2 | ||
| 16 | #define KOMEDA_PIPELINE_MAX_LAYERS 4 | ||
| 17 | #define KOMEDA_PIPELINE_MAX_SCALERS 2 | ||
| 18 | #define KOMEDA_COMPONENT_N_INPUTS 5 | ||
| 19 | |||
| 20 | /* pipeline component IDs */ | ||
| 21 | enum { | ||
| 22 | KOMEDA_COMPONENT_LAYER0 = 0, | ||
| 23 | KOMEDA_COMPONENT_LAYER1 = 1, | ||
| 24 | KOMEDA_COMPONENT_LAYER2 = 2, | ||
| 25 | KOMEDA_COMPONENT_LAYER3 = 3, | ||
| 26 | KOMEDA_COMPONENT_WB_LAYER = 7, /* write back layer */ | ||
| 27 | KOMEDA_COMPONENT_SCALER0 = 8, | ||
| 28 | KOMEDA_COMPONENT_SCALER1 = 9, | ||
| 29 | KOMEDA_COMPONENT_SPLITTER = 12, | ||
| 30 | KOMEDA_COMPONENT_MERGER = 14, | ||
| 31 | KOMEDA_COMPONENT_COMPIZ0 = 16, /* compositor */ | ||
| 32 | KOMEDA_COMPONENT_COMPIZ1 = 17, | ||
| 33 | KOMEDA_COMPONENT_IPS0 = 20, /* post image processor */ | ||
| 34 | KOMEDA_COMPONENT_IPS1 = 21, | ||
| 35 | KOMEDA_COMPONENT_TIMING_CTRLR = 22, /* timing controller */ | ||
| 36 | }; | ||
| 37 | |||
| 38 | #define KOMEDA_PIPELINE_LAYERS (BIT(KOMEDA_COMPONENT_LAYER0) |\ | ||
| 39 | BIT(KOMEDA_COMPONENT_LAYER1) |\ | ||
| 40 | BIT(KOMEDA_COMPONENT_LAYER2) |\ | ||
| 41 | BIT(KOMEDA_COMPONENT_LAYER3)) | ||
| 42 | |||
| 43 | #define KOMEDA_PIPELINE_SCALERS (BIT(KOMEDA_COMPONENT_SCALER0) |\ | ||
| 44 | BIT(KOMEDA_COMPONENT_SCALER1)) | ||
| 45 | |||
| 46 | #define KOMEDA_PIPELINE_COMPIZS (BIT(KOMEDA_COMPONENT_COMPIZ0) |\ | ||
| 47 | BIT(KOMEDA_COMPONENT_COMPIZ1)) | ||
| 48 | |||
| 49 | #define KOMEDA_PIPELINE_IMPROCS (BIT(KOMEDA_COMPONENT_IPS0) |\ | ||
| 50 | BIT(KOMEDA_COMPONENT_IPS1)) | ||
| 51 | struct komeda_component; | ||
| 52 | struct komeda_component_state; | ||
| 53 | |||
| 54 | /** komeda_component_funcs - component control functions */ | ||
| 55 | struct komeda_component_funcs { | ||
| 56 | /** @validate: optional, | ||
| 57 | * component may has special requirements or limitations, this function | ||
| 58 | * supply HW the ability to do the further HW specific check. | ||
| 59 | */ | ||
| 60 | int (*validate)(struct komeda_component *c, | ||
| 61 | struct komeda_component_state *state); | ||
| 62 | /** @update: update is a active update */ | ||
| 63 | void (*update)(struct komeda_component *c, | ||
| 64 | struct komeda_component_state *state); | ||
| 65 | /** @disable: disable component */ | ||
| 66 | void (*disable)(struct komeda_component *c); | ||
| 67 | /** @dump_register: Optional, dump registers to seq_file */ | ||
| 68 | void (*dump_register)(struct komeda_component *c, struct seq_file *seq); | ||
| 69 | }; | ||
| 70 | |||
| 71 | /** | ||
| 72 | * struct komeda_component | ||
| 73 | * | ||
| 74 | * struct komeda_component describe the data flow capabilities for how to link a | ||
| 75 | * component into the display pipeline. | ||
| 76 | * all specified components are subclass of this structure. | ||
| 77 | */ | ||
| 78 | struct komeda_component { | ||
| 79 | /** @obj: treat component as private obj */ | ||
| 80 | struct drm_private_obj obj; | ||
| 81 | /** @pipeline: the komeda pipeline this component belongs to */ | ||
| 82 | struct komeda_pipeline *pipeline; | ||
| 83 | /** @name: component name */ | ||
| 84 | char name[32]; | ||
| 85 | /** | ||
| 86 | * @reg: | ||
| 87 | * component register base, | ||
| 88 | * which is initialized by chip and used by chip only | ||
| 89 | */ | ||
| 90 | u32 __iomem *reg; | ||
| 91 | /** @id: component id */ | ||
| 92 | u32 id; | ||
| 93 | /** @hw_ic: component hw id, | ||
| 94 | * which is initialized by chip and used by chip only | ||
| 95 | */ | ||
| 96 | u32 hw_id; | ||
| 97 | |||
| 98 | /** | ||
| 99 | * @max_active_inputs: | ||
| 100 | * @max_active_outpus: | ||
| 101 | * | ||
| 102 | * maximum number of inputs/outputs that can be active in the same time | ||
| 103 | * Note: | ||
| 104 | * the number isn't the bit number of @supported_inputs or | ||
| 105 | * @supported_outputs, but may be less than it, since component may not | ||
| 106 | * support enabling all @supported_inputs/outputs at the same time. | ||
| 107 | */ | ||
| 108 | u8 max_active_inputs; | ||
| 109 | u8 max_active_outputs; | ||
| 110 | /** | ||
| 111 | * @supported_inputs: | ||
| 112 | * @supported_outputs: | ||
| 113 | * | ||
| 114 | * bitmask of BIT(component->id) for the supported inputs/outputs | ||
| 115 | * describes the possibilities of how a component is linked into a | ||
| 116 | * pipeline. | ||
| 117 | */ | ||
| 118 | u32 supported_inputs; | ||
| 119 | u32 supported_outputs; | ||
| 120 | |||
| 121 | /** | ||
| 122 | * @funcs: chip functions to access HW | ||
| 123 | */ | ||
| 124 | struct komeda_component_funcs *funcs; | ||
| 125 | }; | ||
| 126 | |||
| 127 | /** | ||
| 128 | * struct komeda_component_output | ||
| 129 | * | ||
| 130 | * a component has multiple outputs, if want to know where the data | ||
| 131 | * comes from, only know the component is not enough, we still need to know | ||
| 132 | * its output port | ||
| 133 | */ | ||
| 134 | struct komeda_component_output { | ||
| 135 | /** @component: indicate which component the data comes from */ | ||
| 136 | struct komeda_component *component; | ||
| 137 | /** @output_port: | ||
| 138 | * the output port of the &komeda_component_output.component | ||
| 139 | */ | ||
| 140 | u8 output_port; | ||
| 141 | }; | ||
| 142 | |||
| 143 | /** | ||
| 144 | * struct komeda_component_state | ||
| 145 | * | ||
| 146 | * component_state is the data flow configuration of the component, and it's | ||
| 147 | * the superclass of all specific component_state like @komeda_layer_state, | ||
| 148 | * @komeda_scaler_state | ||
| 149 | */ | ||
| 150 | struct komeda_component_state { | ||
| 151 | /** @obj: tracking component_state by drm_atomic_state */ | ||
| 152 | struct drm_private_state obj; | ||
| 153 | struct komeda_component *component; | ||
| 154 | /** | ||
| 155 | * @binding_user: | ||
| 156 | * currently bound user, the user can be crtc/plane/wb_conn, which is | ||
| 157 | * valid decided by @component and @inputs | ||
| 158 | * | ||
| 159 | * - Layer: its user always is plane. | ||
| 160 | * - compiz/improc/timing_ctrlr: the user is crtc. | ||
| 161 | * - wb_layer: wb_conn; | ||
| 162 | * - scaler: plane when input is layer, wb_conn if input is compiz. | ||
| 163 | */ | ||
| 164 | union { | ||
| 165 | struct drm_crtc *crtc; | ||
| 166 | struct drm_plane *plane; | ||
| 167 | struct drm_connector *wb_conn; | ||
| 168 | void *binding_user; | ||
| 169 | }; | ||
| 170 | /** | ||
| 171 | * @active_inputs: | ||
| 172 | * | ||
| 173 | * active_inputs is bitmask of @inputs index | ||
| 174 | * | ||
| 175 | * - active_inputs = changed_active_inputs + unchanged_active_inputs | ||
| 176 | * - affected_inputs = old->active_inputs + new->active_inputs; | ||
| 177 | * - disabling_inputs = affected_inputs ^ active_inputs; | ||
| 178 | * - changed_inputs = disabling_inputs + changed_active_inputs; | ||
| 179 | * | ||
| 180 | * NOTE: | ||
| 181 | * changed_inputs doesn't include all active_input but only | ||
| 182 | * @changed_active_inputs, and this bitmask can be used in chip | ||
| 183 | * level for dirty update. | ||
| 184 | */ | ||
| 185 | u16 active_inputs; | ||
| 186 | u16 changed_active_inputs; | ||
| 187 | u16 affected_inputs; | ||
| 188 | /** | ||
| 189 | * @inputs: | ||
| 190 | * | ||
| 191 | * the specific inputs[i] only valid on BIT(i) has been set in | ||
| 192 | * @active_inputs, if not the inputs[i] is undefined. | ||
| 193 | */ | ||
| 194 | struct komeda_component_output inputs[KOMEDA_COMPONENT_N_INPUTS]; | ||
| 195 | }; | ||
| 196 | |||
| 197 | static inline u16 component_disabling_inputs(struct komeda_component_state *st) | ||
| 198 | { | ||
| 199 | return st->affected_inputs ^ st->active_inputs; | ||
| 200 | } | ||
| 201 | |||
| 202 | static inline u16 component_changed_inputs(struct komeda_component_state *st) | ||
| 203 | { | ||
| 204 | return component_disabling_inputs(st) | st->changed_active_inputs; | ||
| 205 | } | ||
| 206 | |||
| 207 | #define to_comp(__c) (((__c) == NULL) ? NULL : &((__c)->base)) | ||
| 208 | #define to_cpos(__c) ((struct komeda_component **)&(__c)) | ||
| 209 | |||
| 210 | /* these structures are going to be filled in in uture patches */ | ||
| 211 | struct komeda_layer { | ||
| 212 | struct komeda_component base; | ||
| 213 | /* layer specific features and caps */ | ||
| 214 | int layer_type; /* RICH, SIMPLE or WB */ | ||
| 215 | }; | ||
| 216 | |||
| 217 | struct komeda_layer_state { | ||
| 218 | struct komeda_component_state base; | ||
| 219 | /* layer specific configuration state */ | ||
| 220 | }; | ||
| 221 | |||
| 222 | struct komeda_compiz { | ||
| 223 | struct komeda_component base; | ||
| 224 | /* compiz specific features and caps */ | ||
| 225 | }; | ||
| 226 | |||
| 227 | struct komeda_compiz_state { | ||
| 228 | struct komeda_component_state base; | ||
| 229 | /* compiz specific configuration state */ | ||
| 230 | }; | ||
| 231 | |||
| 232 | struct komeda_scaler { | ||
| 233 | struct komeda_component base; | ||
| 234 | /* scaler features and caps */ | ||
| 235 | }; | ||
| 236 | |||
| 237 | struct komeda_scaler_state { | ||
| 238 | struct komeda_component_state base; | ||
| 239 | }; | ||
| 240 | |||
| 241 | struct komeda_improc { | ||
| 242 | struct komeda_component base; | ||
| 243 | }; | ||
| 244 | |||
| 245 | struct komeda_improc_state { | ||
| 246 | struct komeda_component_state base; | ||
| 247 | }; | ||
| 248 | |||
| 249 | /* display timing controller */ | ||
| 250 | struct komeda_timing_ctrlr { | ||
| 251 | struct komeda_component base; | ||
| 252 | }; | ||
| 253 | |||
| 254 | struct komeda_timing_ctrlr_state { | ||
| 255 | struct komeda_component_state base; | ||
| 256 | }; | ||
| 257 | |||
| 258 | /** struct komeda_pipeline_funcs */ | ||
| 259 | struct komeda_pipeline_funcs { | ||
| 260 | /* dump_register: Optional, dump registers to seq_file */ | ||
| 261 | void (*dump_register)(struct komeda_pipeline *pipe, | ||
| 262 | struct seq_file *sf); | ||
| 263 | }; | ||
| 264 | |||
| 265 | /** | ||
| 266 | * struct komeda_pipeline | ||
| 267 | * | ||
| 268 | * Represent a complete display pipeline and hold all functional components. | ||
| 269 | */ | ||
| 270 | struct komeda_pipeline { | ||
| 271 | /** @obj: link pipeline as private obj of drm_atomic_state */ | ||
| 272 | struct drm_private_obj obj; | ||
| 273 | /** @mdev: the parent komeda_dev */ | ||
| 274 | struct komeda_dev *mdev; | ||
| 275 | /** @pxlclk: pixel clock */ | ||
| 276 | struct clk *pxlclk; | ||
| 277 | /** @aclk: AXI clock */ | ||
| 278 | struct clk *aclk; | ||
| 279 | /** @id: pipeline id */ | ||
| 280 | int id; | ||
| 281 | /** @avail_comps: available components mask of pipeline */ | ||
| 282 | u32 avail_comps; | ||
| 283 | int n_layers; | ||
| 284 | struct komeda_layer *layers[KOMEDA_PIPELINE_MAX_LAYERS]; | ||
| 285 | int n_scalers; | ||
| 286 | struct komeda_scaler *scalers[KOMEDA_PIPELINE_MAX_SCALERS]; | ||
| 287 | struct komeda_compiz *compiz; | ||
| 288 | struct komeda_layer *wb_layer; | ||
| 289 | struct komeda_improc *improc; | ||
| 290 | struct komeda_timing_ctrlr *ctrlr; | ||
| 291 | struct komeda_pipeline_funcs *funcs; /* private pipeline functions */ | ||
| 292 | |||
| 293 | /** @of_node: pipeline dt node */ | ||
| 294 | struct device_node *of_node; | ||
| 295 | /** @of_output_port: pipeline output port */ | ||
| 296 | struct device_node *of_output_port; | ||
| 297 | /** @of_output_dev: output connector device node */ | ||
| 298 | struct device_node *of_output_dev; | ||
| 299 | }; | ||
| 300 | |||
| 301 | /** | ||
| 302 | * struct komeda_pipeline_state | ||
| 303 | * | ||
| 304 | * NOTE: | ||
| 305 | * Unlike the pipeline, pipeline_state doesn’t gather any component_state | ||
| 306 | * into it. It because all component will be managed by drm_atomic_state. | ||
| 307 | */ | ||
| 308 | struct komeda_pipeline_state { | ||
| 309 | /** @obj: tracking pipeline_state by drm_atomic_state */ | ||
| 310 | struct drm_private_state obj; | ||
| 311 | struct komeda_pipeline *pipe; | ||
| 312 | /** @crtc: currently bound crtc */ | ||
| 313 | struct drm_crtc *crtc; | ||
| 314 | /** | ||
| 315 | * @active_comps: | ||
| 316 | * | ||
| 317 | * bitmask - BIT(component->id) of active components | ||
| 318 | */ | ||
| 319 | u32 active_comps; | ||
| 320 | }; | ||
| 321 | |||
| 322 | #define to_layer(c) container_of(c, struct komeda_layer, base) | ||
| 323 | #define to_compiz(c) container_of(c, struct komeda_compiz, base) | ||
| 324 | #define to_scaler(c) container_of(c, struct komeda_scaler, base) | ||
| 325 | #define to_improc(c) container_of(c, struct komeda_improc, base) | ||
| 326 | #define to_ctrlr(c) container_of(c, struct komeda_timing_ctrlr, base) | ||
| 327 | |||
| 328 | #define to_layer_st(c) container_of(c, struct komeda_layer_state, base) | ||
| 329 | #define to_compiz_st(c) container_of(c, struct komeda_compiz_state, base) | ||
| 330 | #define to_scaler_st(c) container_of(c, struct komeda_scaler_state, base) | ||
| 331 | #define to_improc_st(c) container_of(c, struct komeda_improc_state, base) | ||
| 332 | #define to_ctrlr_st(c) container_of(c, struct komeda_timing_ctrlr_state, base) | ||
| 333 | |||
| 334 | #define priv_to_comp_st(o) container_of(o, struct komeda_component_state, obj) | ||
| 335 | #define priv_to_pipe_st(o) container_of(o, struct komeda_pipeline_state, obj) | ||
| 336 | |||
| 337 | /* pipeline APIs */ | ||
| 338 | struct komeda_pipeline * | ||
| 339 | komeda_pipeline_add(struct komeda_dev *mdev, size_t size, | ||
| 340 | struct komeda_pipeline_funcs *funcs); | ||
| 341 | void komeda_pipeline_destroy(struct komeda_dev *mdev, | ||
| 342 | struct komeda_pipeline *pipe); | ||
| 343 | |||
| 344 | struct komeda_component * | ||
| 345 | komeda_pipeline_get_component(struct komeda_pipeline *pipe, int id); | ||
| 346 | |||
| 347 | /* component APIs */ | ||
| 348 | struct komeda_component * | ||
| 349 | komeda_component_add(struct komeda_pipeline *pipe, | ||
| 350 | size_t comp_sz, u32 id, u32 hw_id, | ||
| 351 | struct komeda_component_funcs *funcs, | ||
| 352 | u8 max_active_inputs, u32 supported_inputs, | ||
| 353 | u8 max_active_outputs, u32 __iomem *reg, | ||
| 354 | const char *name_fmt, ...); | ||
| 355 | |||
| 356 | void komeda_component_destroy(struct komeda_dev *mdev, | ||
| 357 | struct komeda_component *c); | ||
| 358 | |||
| 359 | #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 index 000000000000..0a4953a9a909 --- /dev/null +++ b/drivers/gpu/drm/arm/display/komeda/komeda_plane.c | |||
| @@ -0,0 +1,109 @@ | |||
| 1 | // SPDX-License-Identifier: GPL-2.0 | ||
| 2 | /* | ||
| 3 | * (C) COPYRIGHT 2018 ARM Limited. All rights reserved. | ||
| 4 | * Author: James.Qian.Wang <james.qian.wang@arm.com> | ||
| 5 | * | ||
| 6 | */ | ||
| 7 | #include <drm/drm_atomic.h> | ||
| 8 | #include <drm/drm_atomic_helper.h> | ||
| 9 | #include <drm/drm_plane_helper.h> | ||
| 10 | #include "komeda_dev.h" | ||
| 11 | #include "komeda_kms.h" | ||
| 12 | |||
| 13 | static const struct drm_plane_helper_funcs komeda_plane_helper_funcs = { | ||
| 14 | }; | ||
| 15 | |||
| 16 | static void komeda_plane_destroy(struct drm_plane *plane) | ||
| 17 | { | ||
| 18 | drm_plane_cleanup(plane); | ||
| 19 | |||
| 20 | kfree(to_kplane(plane)); | ||
| 21 | } | ||
| 22 | |||
| 23 | static const struct drm_plane_funcs komeda_plane_funcs = { | ||
| 24 | }; | ||
| 25 | |||
| 26 | /* for komeda, which is pipeline can be share between crtcs */ | ||
| 27 | static u32 get_possible_crtcs(struct komeda_kms_dev *kms, | ||
| 28 | struct komeda_pipeline *pipe) | ||
| 29 | { | ||
| 30 | struct komeda_crtc *crtc; | ||
| 31 | u32 possible_crtcs = 0; | ||
| 32 | int i; | ||
| 33 | |||
| 34 | for (i = 0; i < kms->n_crtcs; i++) { | ||
| 35 | crtc = &kms->crtcs[i]; | ||
| 36 | |||
| 37 | if ((pipe == crtc->master) || (pipe == crtc->slave)) | ||
| 38 | possible_crtcs |= BIT(i); | ||
| 39 | } | ||
| 40 | |||
| 41 | return possible_crtcs; | ||
| 42 | } | ||
| 43 | |||
| 44 | /* use Layer0 as primary */ | ||
| 45 | static u32 get_plane_type(struct komeda_kms_dev *kms, | ||
| 46 | struct komeda_component *c) | ||
| 47 | { | ||
| 48 | bool is_primary = (c->id == KOMEDA_COMPONENT_LAYER0); | ||
| 49 | |||
| 50 | return is_primary ? DRM_PLANE_TYPE_PRIMARY : DRM_PLANE_TYPE_OVERLAY; | ||
| 51 | } | ||
| 52 | |||
| 53 | static int komeda_plane_add(struct komeda_kms_dev *kms, | ||
| 54 | struct komeda_layer *layer) | ||
| 55 | { | ||
| 56 | struct komeda_dev *mdev = kms->base.dev_private; | ||
| 57 | struct komeda_component *c = &layer->base; | ||
| 58 | struct komeda_plane *kplane; | ||
| 59 | struct drm_plane *plane; | ||
| 60 | u32 *formats, n_formats = 0; | ||
| 61 | int err; | ||
| 62 | |||
| 63 | kplane = kzalloc(sizeof(*kplane), GFP_KERNEL); | ||
| 64 | if (!kplane) | ||
| 65 | return -ENOMEM; | ||
| 66 | |||
| 67 | plane = &kplane->base; | ||
| 68 | kplane->layer = layer; | ||
| 69 | |||
| 70 | formats = komeda_get_layer_fourcc_list(&mdev->fmt_tbl, | ||
| 71 | layer->layer_type, &n_formats); | ||
| 72 | |||
| 73 | err = drm_universal_plane_init(&kms->base, plane, | ||
| 74 | get_possible_crtcs(kms, c->pipeline), | ||
| 75 | &komeda_plane_funcs, | ||
| 76 | formats, n_formats, NULL, | ||
| 77 | get_plane_type(kms, c), | ||
| 78 | "%s", c->name); | ||
| 79 | |||
| 80 | komeda_put_fourcc_list(formats); | ||
| 81 | |||
| 82 | if (err) | ||
| 83 | goto cleanup; | ||
| 84 | |||
| 85 | drm_plane_helper_add(plane, &komeda_plane_helper_funcs); | ||
| 86 | |||
| 87 | return 0; | ||
| 88 | cleanup: | ||
| 89 | komeda_plane_destroy(plane); | ||
| 90 | return err; | ||
| 91 | } | ||
| 92 | |||
| 93 | int komeda_kms_add_planes(struct komeda_kms_dev *kms, struct komeda_dev *mdev) | ||
| 94 | { | ||
| 95 | struct komeda_pipeline *pipe; | ||
| 96 | int i, j, err; | ||
| 97 | |||
| 98 | for (i = 0; i < mdev->n_pipelines; i++) { | ||
| 99 | pipe = mdev->pipelines[i]; | ||
| 100 | |||
| 101 | for (j = 0; j < pipe->n_layers; j++) { | ||
| 102 | err = komeda_plane_add(kms, pipe->layers[j]); | ||
| 103 | if (err) | ||
| 104 | return err; | ||
| 105 | } | ||
| 106 | } | ||
| 107 | |||
| 108 | return 0; | ||
| 109 | } | ||
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 index 000000000000..f1c9e3fefa86 --- /dev/null +++ b/drivers/gpu/drm/arm/display/komeda/komeda_private_obj.c | |||
| @@ -0,0 +1,88 @@ | |||
| 1 | // SPDX-License-Identifier: GPL-2.0 | ||
| 2 | /* | ||
| 3 | * (C) COPYRIGHT 2018 ARM Limited. All rights reserved. | ||
| 4 | * Author: James.Qian.Wang <james.qian.wang@arm.com> | ||
| 5 | * | ||
| 6 | */ | ||
| 7 | #include "komeda_dev.h" | ||
| 8 | #include "komeda_kms.h" | ||
| 9 | |||
| 10 | static struct drm_private_state * | ||
| 11 | komeda_pipeline_atomic_duplicate_state(struct drm_private_obj *obj) | ||
| 12 | { | ||
| 13 | struct komeda_pipeline_state *st; | ||
| 14 | |||
| 15 | st = kmemdup(obj->state, sizeof(*st), GFP_KERNEL); | ||
| 16 | if (!st) | ||
| 17 | return NULL; | ||
| 18 | |||
| 19 | st->active_comps = 0; | ||
| 20 | |||
| 21 | __drm_atomic_helper_private_obj_duplicate_state(obj, &st->obj); | ||
| 22 | |||
| 23 | return &st->obj; | ||
| 24 | } | ||
| 25 | |||
| 26 | static void | ||
| 27 | komeda_pipeline_atomic_destroy_state(struct drm_private_obj *obj, | ||
| 28 | struct drm_private_state *state) | ||
| 29 | { | ||
| 30 | kfree(priv_to_pipe_st(state)); | ||
| 31 | } | ||
| 32 | |||
| 33 | static const struct drm_private_state_funcs komeda_pipeline_obj_funcs = { | ||
| 34 | .atomic_duplicate_state = komeda_pipeline_atomic_duplicate_state, | ||
| 35 | .atomic_destroy_state = komeda_pipeline_atomic_destroy_state, | ||
| 36 | }; | ||
| 37 | |||
| 38 | static int komeda_pipeline_obj_add(struct komeda_kms_dev *kms, | ||
| 39 | struct komeda_pipeline *pipe) | ||
| 40 | { | ||
| 41 | struct komeda_pipeline_state *st; | ||
| 42 | |||
| 43 | st = kzalloc(sizeof(*st), GFP_KERNEL); | ||
| 44 | if (!st) | ||
| 45 | return -ENOMEM; | ||
| 46 | |||
| 47 | st->pipe = pipe; | ||
| 48 | drm_atomic_private_obj_init(&kms->base, &pipe->obj, &st->obj, | ||
| 49 | &komeda_pipeline_obj_funcs); | ||
| 50 | |||
| 51 | return 0; | ||
| 52 | } | ||
| 53 | |||
| 54 | int komeda_kms_add_private_objs(struct komeda_kms_dev *kms, | ||
| 55 | struct komeda_dev *mdev) | ||
| 56 | { | ||
| 57 | struct komeda_pipeline *pipe; | ||
| 58 | int i, err; | ||
| 59 | |||
| 60 | for (i = 0; i < mdev->n_pipelines; i++) { | ||
| 61 | pipe = mdev->pipelines[i]; | ||
| 62 | |||
| 63 | err = komeda_pipeline_obj_add(kms, pipe); | ||
| 64 | if (err) | ||
| 65 | return err; | ||
| 66 | |||
| 67 | /* Add component */ | ||
| 68 | } | ||
| 69 | |||
| 70 | return 0; | ||
| 71 | } | ||
| 72 | |||
| 73 | void komeda_kms_cleanup_private_objs(struct komeda_dev *mdev) | ||
| 74 | { | ||
| 75 | struct komeda_pipeline *pipe; | ||
| 76 | struct komeda_component *c; | ||
| 77 | int i, id; | ||
| 78 | |||
| 79 | for (i = 0; i < mdev->n_pipelines; i++) { | ||
| 80 | pipe = mdev->pipelines[i]; | ||
| 81 | dp_for_each_set_bit(id, pipe->avail_comps) { | ||
| 82 | c = komeda_pipeline_get_component(pipe, id); | ||
| 83 | |||
| 84 | drm_atomic_private_obj_fini(&c->obj); | ||
| 85 | } | ||
| 86 | drm_atomic_private_obj_fini(&pipe->obj); | ||
| 87 | } | ||
| 88 | } | ||
diff --git a/include/uapi/drm/drm_fourcc.h b/include/uapi/drm/drm_fourcc.h index 91d08a23f125..93a341d278a6 100644 --- a/include/uapi/drm/drm_fourcc.h +++ b/include/uapi/drm/drm_fourcc.h | |||
| @@ -574,6 +574,9 @@ extern "C" { | |||
| 574 | * AFBC has several features which may be supported and/or used, which are | 574 | * AFBC has several features which may be supported and/or used, which are |
| 575 | * represented using bits in the modifier. Not all combinations are valid, | 575 | * represented using bits in the modifier. Not all combinations are valid, |
| 576 | * and different devices or use-cases may support different combinations. | 576 | * and different devices or use-cases may support different combinations. |
| 577 | * | ||
| 578 | * Further information on the use of AFBC modifiers can be found in | ||
| 579 | * Documentation/gpu/afbc.rst | ||
| 577 | */ | 580 | */ |
| 578 | #define DRM_FORMAT_MOD_ARM_AFBC(__afbc_mode) fourcc_mod_code(ARM, __afbc_mode) | 581 | #define DRM_FORMAT_MOD_ARM_AFBC(__afbc_mode) fourcc_mod_code(ARM, __afbc_mode) |
| 579 | 582 | ||
