diff options
| author | Dave Airlie <airlied@redhat.com> | 2025-06-18 08:09:27 +1000 |
|---|---|---|
| committer | Dave Airlie <airlied@redhat.com> | 2025-06-18 08:09:35 +1000 |
| commit | 45215c589e7f22641e2fc6f518bcbead71d90f9c (patch) | |
| tree | 1faca88e4993230babcf5b95d590d3e5e0b14f2b | |
| parent | e04c78d86a9699d136910cfc0bdcf01087e3267e (diff) | |
| parent | c5b4393c5492555e35c08677a326c9c53b275abd (diff) | |
Merge tag 'drm-misc-next-2025-06-12' of https://gitlab.freedesktop.org/drm/misc/kernel into drm-next
drm-misc-next for 6.17:
UAPI Changes:
Cross-subsystem Changes:
Core Changes:
- atomic-helpers: Tune the enable / disable sequence
- bridge: Add destroy hook
- color management: Add helpers for hardware gamma LUT handling
- HDMI: Add CEC handling, YUV420 output support
- sched: tracing improvements
Driver Changes:
- hyperv: Move out of simple-kms, drm_panic support
- i915: drm_panel_follower support
- imx: Add IMX8qxq Display Controller Support
- lima: Add Rockchip RK3528 GPU Support
- nouveau: fence handling cleanup
- panfrost: Add BO labeling, 64-bit registers access
- qaic: Add RAS Support
- rz-du: Add RZ/V2H(P) Support, MIPI-DSI DCS Support
- sun4i: Add H616 Support
- tidss: Add TI AM62L Support
- vkms: YUV and R* formats support
- bridges:
- Switched to reference counted drm_bridge allocations
- panels:
- Switched to reference counted drm_panel allocations
- Add support for fwnode-based panel lookup
- himax-hx8394: Support for Huiling hl055fhv028c
- ilitek-ili9881c: Support for 7" Raspberry Pi 720x1280
- panel-edp: Support for KDC KD116N3730A05, N160JCE-ELL CMN,
- panel-simple: Support for AUO P238HAN01
- st7701: Support for Winstar wf40eswaa6mnn0
- visionox-rm69299: Support for rm69299-shift
- New panels: Renesas R61307, Renesas R69328
Signed-off-by: Dave Airlie <airlied@redhat.com>
From: Maxime Ripard <mripard@redhat.com>
Link: https://lore.kernel.org/r/20250612-coucal-of-impossible-cleaning-a5eecf@houat
381 files changed, 15333 insertions, 2787 deletions
diff --git a/Documentation/ABI/testing/sysfs-driver-qaic b/Documentation/ABI/testing/sysfs-driver-qaic new file mode 100644 index 000000000000..f794fd734163 --- /dev/null +++ b/Documentation/ABI/testing/sysfs-driver-qaic @@ -0,0 +1,18 @@ +What: /sys/bus/pci/drivers/qaic/XXXX:XX:XX.X/ce_count +Date: May 2025 +KernelVersion: 6.17 +Contact: dri-devel@lists.freedesktop.org +Description: Number of correctable errors received from device since driver is loaded. + +What: /sys/bus/pci/drivers/qaic/XXXX:XX:XX.X/ue_count +Date: May 2025 +KernelVersion: 6.17 +Contact: dri-devel@lists.freedesktop.org +Description: Number of uncorrectable errors received from device since driver is loaded. + +What: /sys/bus/pci/drivers/qaic/XXXX:XX:XX.X/ue_nonfatal_count +Date: May 2025 +KernelVersion: 6.17 +Contact: dri-devel@lists.freedesktop.org +Description: Number of uncorrectable non-fatal errors received from device since driver + is loaded. diff --git a/Documentation/devicetree/bindings/display/allwinner,sun8i-a83t-de2-mixer.yaml b/Documentation/devicetree/bindings/display/allwinner,sun8i-a83t-de2-mixer.yaml index b75c1ec686ad..cbd18fd83e52 100644 --- a/Documentation/devicetree/bindings/display/allwinner,sun8i-a83t-de2-mixer.yaml +++ b/Documentation/devicetree/bindings/display/allwinner,sun8i-a83t-de2-mixer.yaml @@ -24,9 +24,11 @@ properties: - allwinner,sun50i-a64-de2-mixer-0 - allwinner,sun50i-a64-de2-mixer-1 - allwinner,sun50i-h6-de3-mixer-0 + - allwinner,sun50i-h616-de33-mixer-0 - reg: - maxItems: 1 + reg: true + + reg-names: true clocks: items: @@ -61,6 +63,34 @@ properties: required: - port@1 +allOf: + - if: + properties: + compatible: + contains: + enum: + - allwinner,sun50i-h616-de33-mixer-0 + then: + properties: + reg: + description: | + Registers for controlling individual layers of the display + engine (layers), global control (top), and display blending + control (display). Names are from Allwinner BSP kernel. + maxItems: 3 + reg-names: + items: + - const: layers + - const: top + - const: display + required: + - reg-names + + else: + properties: + reg: + maxItems: 1 + required: - compatible - reg diff --git a/Documentation/devicetree/bindings/display/imx/fsl,imx8qxp-dc-axi-performance-counter.yaml b/Documentation/devicetree/bindings/display/imx/fsl,imx8qxp-dc-axi-performance-counter.yaml new file mode 100644 index 000000000000..1d6501afc7f2 --- /dev/null +++ b/Documentation/devicetree/bindings/display/imx/fsl,imx8qxp-dc-axi-performance-counter.yaml @@ -0,0 +1,57 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/display/imx/fsl,imx8qxp-dc-axi-performance-counter.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Freescale i.MX8qxp Display Controller AXI Performance Counter + +description: | + Performance counters are provided to allow measurement of average bandwidth + and latency during operation. The following features are supported: + + * Manual and timer controlled measurement mode. + + * Measurement counters: + - GLOBAL_COUNTER for overall measurement time + - BUSY_COUNTER for number of data bus busy cycles + - DATA_COUNTER for number of data transfer cycles + - TRANSFER_COUNTER for number of transfers + - ADDRBUSY_COUNTER for number of address bus busy cycles + - LATENCY_COUNTER for average latency + + * Counter overflow detection. + + * Outstanding Transfer Counters (OTC) which are used for latency measurement + have to run immediately after reset, but can be disabled by software when + there is no need for latency measurement. + +maintainers: + - Liu Ying <victor.liu@nxp.com> + +properties: + compatible: + const: fsl,imx8qxp-dc-axi-performance-counter + + reg: + maxItems: 1 + + clocks: + maxItems: 1 + +required: + - compatible + - reg + - clocks + +additionalProperties: false + +examples: + - | + #include <dt-bindings/clock/imx8-lpcg.h> + + pmu@5618f000 { + compatible = "fsl,imx8qxp-dc-axi-performance-counter"; + reg = <0x5618f000 0x90>; + clocks = <&dc0_lpcg IMX_LPCG_CLK_5>; + }; diff --git a/Documentation/devicetree/bindings/display/imx/fsl,imx8qxp-dc-blit-engine.yaml b/Documentation/devicetree/bindings/display/imx/fsl,imx8qxp-dc-blit-engine.yaml new file mode 100644 index 000000000000..45db6da39e20 --- /dev/null +++ b/Documentation/devicetree/bindings/display/imx/fsl,imx8qxp-dc-blit-engine.yaml @@ -0,0 +1,204 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/display/imx/fsl,imx8qxp-dc-blit-engine.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Freescale i.MX8qxp Display Controller Blit Engine + +description: | + A blit operation (block based image transfer) reads up to 3 source images + from memory and computes one destination image from it, which is written + back to memory. The following basic operations are supported: + + * Buffer Fill + Fills a buffer with constant color + + * Buffer Copy + Copies one source to a destination buffer. + + * Image Blend + Combines two source images by a blending equation and writes result to + destination (which can be one of the sources). + + * Image Rop2/3 + Combines up to three source images by a logical equation (raster operation) + and writes result to destination (which can be one of the sources). + + * Image Flip + Mirrors the source image in horizontal and/or vertical direction. + + * Format Convert + Convert between the supported color and buffer formats. + + * Color Transform + Modify colors by linear or non-linear transformations. + + * Image Scale + Changes size of the source image. + + * Image Rotate + Rotates the source image by any angle. + + * Image Filter + Performs an FIR filter operation on the source image. + + * Image Warp + Performs a re-sampling of the source image with any pattern. The sample + point positions are read from a compressed coordinate buffer. + + * Buffer Pack + Writes an image with color components stored in up to three different + buffers (planar formats) into a single buffer (packed format). + + * Chroma Resample + Converts between different YUV formats that differ in chroma sampling rate + (4:4:4, 4:2:2, 4:2:0). + +maintainers: + - Liu Ying <victor.liu@nxp.com> + +properties: + compatible: + const: fsl,imx8qxp-dc-blit-engine + + reg: + maxItems: 2 + + reg-names: + items: + - const: pec + - const: cfg + + "#address-cells": + const: 1 + + "#size-cells": + const: 1 + + ranges: true + +patternProperties: + "^blitblend@[0-9a-f]+$": + type: object + additionalProperties: true + + properties: + compatible: + const: fsl,imx8qxp-dc-blitblend + + "^clut@[0-9a-f]+$": + type: object + additionalProperties: true + + properties: + compatible: + const: fsl,imx8qxp-dc-clut + + "^fetchdecode@[0-9a-f]+$": + type: object + additionalProperties: true + + properties: + compatible: + const: fsl,imx8qxp-dc-fetchdecode + + "^fetcheco@[0-9a-f]+$": + type: object + additionalProperties: true + + properties: + compatible: + const: fsl,imx8qxp-dc-fetcheco + + "^fetchwarp@[0-9a-f]+$": + type: object + additionalProperties: true + + properties: + compatible: + const: fsl,imx8qxp-dc-fetchwarp + + "^filter@[0-9a-f]+$": + type: object + additionalProperties: true + + properties: + compatible: + const: fsl,imx8qxp-dc-filter + + "^hscaler@[0-9a-f]+$": + type: object + additionalProperties: true + + properties: + compatible: + const: fsl,imx8qxp-dc-hscaler + + "^matrix@[0-9a-f]+$": + type: object + additionalProperties: true + + properties: + compatible: + const: fsl,imx8qxp-dc-matrix + + "^rop@[0-9a-f]+$": + type: object + additionalProperties: true + + properties: + compatible: + const: fsl,imx8qxp-dc-rop + + "^store@[0-9a-f]+$": + type: object + additionalProperties: true + + properties: + compatible: + const: fsl,imx8qxp-dc-store + + "^vscaler@[0-9a-f]+$": + type: object + additionalProperties: true + + properties: + compatible: + const: fsl,imx8qxp-dc-vscaler + +required: + - compatible + - reg + - reg-names + - "#address-cells" + - "#size-cells" + - ranges + +additionalProperties: false + +examples: + - | + blit-engine@56180820 { + compatible = "fsl,imx8qxp-dc-blit-engine"; + reg = <0x56180820 0x13c>, <0x56181000 0x3400>; + reg-names = "pec", "cfg"; + #address-cells = <1>; + #size-cells = <1>; + ranges; + + fetchdecode@56180820 { + compatible = "fsl,imx8qxp-dc-fetchdecode"; + reg = <0x56180820 0x10>, <0x56181000 0x404>; + reg-names = "pec", "cfg"; + }; + + store@56180940 { + compatible = "fsl,imx8qxp-dc-store"; + reg = <0x56180940 0x1c>, <0x56184000 0x5c>; + reg-names = "pec", "cfg"; + interrupt-parent = <&dc0_intc>; + interrupts = <0>, <1>, <2>; + interrupt-names = "shdload", "framecomplete", "seqcomplete"; + }; + }; diff --git a/Documentation/devicetree/bindings/display/imx/fsl,imx8qxp-dc-blitblend.yaml b/Documentation/devicetree/bindings/display/imx/fsl,imx8qxp-dc-blitblend.yaml new file mode 100644 index 000000000000..095e65939fba --- /dev/null +++ b/Documentation/devicetree/bindings/display/imx/fsl,imx8qxp-dc-blitblend.yaml @@ -0,0 +1,41 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/display/imx/fsl,imx8qxp-dc-blitblend.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Freescale i.MX8qxp Display Controller Blit Blend Unit + +description: + Combines two input frames to a single output frame, all frames having the + same dimension. + +maintainers: + - Liu Ying <victor.liu@nxp.com> + +properties: + compatible: + const: fsl,imx8qxp-dc-blitblend + + reg: + maxItems: 2 + + reg-names: + items: + - const: pec + - const: cfg + +required: + - compatible + - reg + - reg-names + +additionalProperties: false + +examples: + - | + blitblend@56180920 { + compatible = "fsl,imx8qxp-dc-blitblend"; + reg = <0x56180920 0x10>, <0x56183c00 0x3c>; + reg-names = "pec", "cfg"; + }; diff --git a/Documentation/devicetree/bindings/display/imx/fsl,imx8qxp-dc-clut.yaml b/Documentation/devicetree/bindings/display/imx/fsl,imx8qxp-dc-clut.yaml new file mode 100644 index 000000000000..21d42aa11b52 --- /dev/null +++ b/Documentation/devicetree/bindings/display/imx/fsl,imx8qxp-dc-clut.yaml @@ -0,0 +1,44 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/display/imx/fsl,imx8qxp-dc-clut.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Freescale i.MX8qxp Display Controller Color Lookup Table + +description: | + The unit implements 3 look-up tables with 256 x 10 bit entries each. These + can be used for different kinds of applications. From 10-bit input values + only upper 8 bits are used. + + The unit supports color lookup, index lookup, dithering and alpha masking. + +maintainers: + - Liu Ying <victor.liu@nxp.com> + +properties: + compatible: + const: fsl,imx8qxp-dc-clut + + reg: + maxItems: 2 + + reg-names: + items: + - const: pec + - const: cfg + +required: + - compatible + - reg + - reg-names + +additionalProperties: false + +examples: + - | + clut@56180880 { + compatible = "fsl,imx8qxp-dc-clut"; + reg = <0x56180880 0x10>, <0x56182400 0x404>; + reg-names = "pec", "cfg"; + }; diff --git a/Documentation/devicetree/bindings/display/imx/fsl,imx8qxp-dc-command-sequencer.yaml b/Documentation/devicetree/bindings/display/imx/fsl,imx8qxp-dc-command-sequencer.yaml new file mode 100644 index 000000000000..27118f4c0d28 --- /dev/null +++ b/Documentation/devicetree/bindings/display/imx/fsl,imx8qxp-dc-command-sequencer.yaml @@ -0,0 +1,67 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/display/imx/fsl,imx8qxp-dc-command-sequencer.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Freescale i.MX8qxp Display Controller Command Sequencer + +description: | + The Command Sequencer is designed to autonomously process command lists. + By that it can load setups into the DC configuration and synchronize to + hardware events. This releases a system's CPU from workload, because it + does not need to wait for certain events. Also it simplifies SW architecture, + because no interrupt handlers are required. Setups are read via AXI bus, + while write access to configuration registers occurs directly via an internal + bus. This saves bandwidth for the AXI interconnect and improves the system + architecture in terms of safety aspects. + +maintainers: + - Liu Ying <victor.liu@nxp.com> + +properties: + compatible: + const: fsl,imx8qxp-dc-command-sequencer + + reg: + maxItems: 1 + + clocks: + maxItems: 1 + + interrupts: + maxItems: 5 + + interrupt-names: + items: + - const: error + - const: sw0 + - const: sw1 + - const: sw2 + - const: sw3 + + sram: + $ref: /schemas/types.yaml#/definitions/phandle + description: phandle pointing to the mmio-sram device node + +required: + - compatible + - reg + - clocks + - interrupts + - interrupt-names + +additionalProperties: false + +examples: + - | + #include <dt-bindings/clock/imx8-lpcg.h> + + command-sequencer@56180400 { + compatible = "fsl,imx8qxp-dc-command-sequencer"; + reg = <0x56180400 0x1a4>; + clocks = <&dc0_lpcg IMX_LPCG_CLK_5>; + interrupt-parent = <&dc0_intc>; + interrupts = <36>, <37>, <38>, <39>, <40>; + interrupt-names = "error", "sw0", "sw1", "sw2", "sw3"; + }; diff --git a/Documentation/devicetree/bindings/display/imx/fsl,imx8qxp-dc-constframe.yaml b/Documentation/devicetree/bindings/display/imx/fsl,imx8qxp-dc-constframe.yaml new file mode 100644 index 000000000000..94f678563608 --- /dev/null +++ b/Documentation/devicetree/bindings/display/imx/fsl,imx8qxp-dc-constframe.yaml @@ -0,0 +1,44 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/display/imx/fsl,imx8qxp-dc-constframe.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Freescale i.MX8qxp Display Controller Constant Frame + +description: | + The Constant Frame unit is used instead of a Fetch unit where generation of + constant color frames only is sufficient. This is the case for the background + planes of content and safety streams in a Display Controller. + + The color can be setup to any RGBA value. + +maintainers: + - Liu Ying <victor.liu@nxp.com> + +properties: + compatible: + const: fsl,imx8qxp-dc-constframe + + reg: + maxItems: 2 + + reg-names: + items: + - const: pec + - const: cfg + +required: + - compatible + - reg + - reg-names + +additionalProperties: false + +examples: + - | + constframe@56180960 { + compatible = "fsl,imx8qxp-dc-constframe"; + reg = <0x56180960 0xc>, <0x56184400 0x20>; + reg-names = "pec", "cfg"; + }; diff --git a/Documentation/devicetree/bindings/display/imx/fsl,imx8qxp-dc-display-engine.yaml b/Documentation/devicetree/bindings/display/imx/fsl,imx8qxp-dc-display-engine.yaml new file mode 100644 index 000000000000..91f3bb77d8d0 --- /dev/null +++ b/Documentation/devicetree/bindings/display/imx/fsl,imx8qxp-dc-display-engine.yaml @@ -0,0 +1,152 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/display/imx/fsl,imx8qxp-dc-display-engine.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Freescale i.MX8qxp Display Controller Display Engine + +description: + All Processing Units that operate in a display clock domain. Pixel pipeline + is driven by a video timing and cannot be stalled. Implements all display + specific processing. + +maintainers: + - Liu Ying <victor.liu@nxp.com> + +properties: + compatible: + const: fsl,imx8qxp-dc-display-engine + + reg: + maxItems: 2 + + reg-names: + items: + - const: top + - const: cfg + + resets: + maxItems: 1 + + interrupts: + maxItems: 3 + + interrupt-names: + items: + - const: shdload + - const: framecomplete + - const: seqcomplete + + power-domains: + maxItems: 1 + + "#address-cells": + const: 1 + + "#size-cells": + const: 1 + + ranges: true + +patternProperties: + "^dither@[0-9a-f]+$": + type: object + additionalProperties: true + + properties: + compatible: + const: fsl,imx8qxp-dc-dither + + "^framegen@[0-9a-f]+$": + type: object + additionalProperties: true + + properties: + compatible: + const: fsl,imx8qxp-dc-framegen + + "^gammacor@[0-9a-f]+$": + type: object + additionalProperties: true + + properties: + compatible: + const: fsl,imx8qxp-dc-gammacor + + "^matrix@[0-9a-f]+$": + type: object + additionalProperties: true + + properties: + compatible: + const: fsl,imx8qxp-dc-matrix + + "^signature@[0-9a-f]+$": + type: object + additionalProperties: true + + properties: + compatible: + const: fsl,imx8qxp-dc-signature + + "^tcon@[0-9a-f]+$": + type: object + additionalProperties: true + + properties: + compatible: + const: fsl,imx8qxp-dc-tcon + +required: + - compatible + - reg + - reg-names + - interrupts + - interrupt-names + - power-domains + - "#address-cells" + - "#size-cells" + - ranges + +additionalProperties: false + +examples: + - | + #include <dt-bindings/clock/imx8-lpcg.h> + #include <dt-bindings/firmware/imx/rsrc.h> + + display-engine@5618b400 { + compatible = "fsl,imx8qxp-dc-display-engine"; + reg = <0x5618b400 0x14>, <0x5618b800 0x1c00>; + reg-names = "top", "cfg"; + interrupt-parent = <&dc0_intc>; + interrupts = <15>, <16>, <17>; + interrupt-names = "shdload", "framecomplete", "seqcomplete"; + power-domains = <&pd IMX_SC_R_DC_0_PLL_0>; + #address-cells = <1>; + #size-cells = <1>; + ranges; + + framegen@5618b800 { + compatible = "fsl,imx8qxp-dc-framegen"; + reg = <0x5618b800 0x98>; + clocks = <&dc0_disp_lpcg IMX_LPCG_CLK_0>; + interrupt-parent = <&dc0_intc>; + interrupts = <18>, <19>, <20>, <21>, <41>, <42>, <43>, <44>; + interrupt-names = "int0", "int1", "int2", "int3", + "primsync_on", "primsync_off", + "secsync_on", "secsync_off"; + }; + + tcon@5618c800 { + compatible = "fsl,imx8qxp-dc-tcon"; + reg = <0x5618c800 0x588>; + + port { + dc0_disp0_dc0_pixel_combiner_ch0: endpoint { + remote-endpoint = <&dc0_pixel_combiner_ch0_dc0_disp0>; + }; + }; + }; + }; diff --git a/Documentation/devicetree/bindings/display/imx/fsl,imx8qxp-dc-dither.yaml b/Documentation/devicetree/bindings/display/imx/fsl,imx8qxp-dc-dither.yaml new file mode 100644 index 000000000000..8e4468d91836 --- /dev/null +++ b/Documentation/devicetree/bindings/display/imx/fsl,imx8qxp-dc-dither.yaml @@ -0,0 +1,45 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/display/imx/fsl,imx8qxp-dc-dither.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Freescale i.MX8qxp Display Controller Dither Unit + +description: | + The unit can increase the physical color resolution of a display from 5, 6, 7 + or 8 bits per RGB channel to a virtual resolution of 10 bits. The physical + resolution can be set individually for each channel. + + The resolution is increased by mixing the two physical colors that are nearest + to the virtual color code in a variable ratio either by time (temporal + dithering) or by position (spatial dithering). + + An optimized algorithm for temporal dithering minimizes noise artifacts on the + output image. + + The dither operation can be individually enabled or disabled for each pixel + using the alpha input bit. + +maintainers: + - Liu Ying <victor.liu@nxp.com> + +properties: + compatible: + const: fsl,imx8qxp-dc-dither + + reg: + maxItems: 1 + +required: + - compatible + - reg + +additionalProperties: false + +examples: + - | + dither@5618c400 { + compatible = "fsl,imx8qxp-dc-dither"; + reg = <0x5618c400 0x14>; + }; diff --git a/Documentation/devicetree/bindings/display/imx/fsl,imx8qxp-dc-extdst.yaml b/Documentation/devicetree/bindings/display/imx/fsl,imx8qxp-dc-extdst.yaml new file mode 100644 index 000000000000..dfc2d4f94f8e --- /dev/null +++ b/Documentation/devicetree/bindings/display/imx/fsl,imx8qxp-dc-extdst.yaml @@ -0,0 +1,72 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/display/imx/fsl,imx8qxp-dc-extdst.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Freescale i.MX8qxp Display Controller External Destination Interface + +description: | + The External Destination unit is the interface between the internal pixel + processing pipeline of the Pixel Engine, which is 30-bit RGB plus 8-bit Alpha, + and a Display Engine. + + It comprises the following built-in Gamma apply function. + + +------X-----------------------+ + | | ExtDst Unit | + | V | + | +-------+ | + | | Gamma | | + | +-------+ | + | | | + | V + + +------X-----------------------+ + + The output format is 24-bit RGB plus 1-bit Alpha. Conversion from 10 to 8 + bits is done by LSBit truncation. Alpha output bit is 1 for input 255, 0 + otherwise. + +maintainers: + - Liu Ying <victor.liu@nxp.com> + +properties: + compatible: + const: fsl,imx8qxp-dc-extdst + + reg: + maxItems: 2 + + reg-names: + items: + - const: pec + - const: cfg + + interrupts: + maxItems: 3 + + interrupt-names: + items: + - const: shdload + - const: framecomplete + - const: seqcomplete + +required: + - compatible + - reg + - reg-names + - interrupts + - interrupt-names + +additionalProperties: false + +examples: + - | + extdst@56180980 { + compatible = "fsl,imx8qxp-dc-extdst"; + reg = <0x56180980 0x1c>, <0x56184800 0x28>; + reg-names = "pec", "cfg"; + interrupt-parent = <&dc0_intc>; + interrupts = <3>, <4>, <5>; + interrupt-names = "shdload", "framecomplete", "seqcomplete"; + }; diff --git a/Documentation/devicetree/bindings/display/imx/fsl,imx8qxp-dc-fetchunit.yaml b/Documentation/devicetree/bindings/display/imx/fsl,imx8qxp-dc-fetchunit.yaml new file mode 100644 index 000000000000..97fb6a4598d9 --- /dev/null +++ b/Documentation/devicetree/bindings/display/imx/fsl,imx8qxp-dc-fetchunit.yaml @@ -0,0 +1,141 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/display/imx/fsl,imx8qxp-dc-fetchunit.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Freescale i.MX8qxp Display Controller Fetch Unit + +description: | + The Fetch Unit is the interface between the AXI bus for source buffer access + and the internal pixel processing pipeline, which is 30-bit RGB plus 8-bit + Alpha. + + It is used to generate foreground planes in Display Controllers and source + planes in Blit Engines, and comprises the following built-in functions to + convert a wide range of frame buffer types. + + +---------X-----------------------------------------+ + | | Fetch Unit | + | V | + | +---------+ | + | | | | + | | Decode | Decompression [Decode] | + | | | | + | +---------+ | + | | | + | V | + | +---------+ | + | | Clip & | Clip Window [All] | + | | Overlay | Plane composition [Layer, Warp] | + | | | | + | +---------+ | + | | | + | V | + | +---------+ | + | | Re- | Flip/Rotate/Repl./Drop [All] | + X--> | sample | Perspective/Affine warping [Persp] | + | | | | Arbitrary warping [Warp, Persp] | + | | +---------+ | + | | | | + | | V | + | | +---------+ | + | | | | | + | | | Palette | Color Palette [Layer, Decode] | + | | | | | + | | +---------+ | + | | | | + | | V | + | | +---------+ | + | | | Extract | Raw to RGBA/YUV [All] | + | | | & | Bit width expansion [All] | + | | | Expand | | + | | +---------+ | + | | | | + | | V | + | | +---------+ | + | | | | Planar to packed | + | |->| Combine | [Decode, Warp, Persp] | + | | | | | + | | +---------+ | + | | | | + | | V | + | | +---------+ | + | | | | YUV422 to YUV444 | + | | | Chroma | [Decode, Persp] | + | | | | | + | | +---------+ | + | | | | + | | V | + | | +---------+ | + | | | | YUV to RGB | + | | | Color | [Warp, Persp, Decode, Layer] | + | | | | | + | | +---------+ | + | | | | + | | V | + | | +---------+ | + | | | | Gamma removal | + | | | Gamma | [Warp, Persp, Decode, Layer] | + | | | | | + | | +---------+ | + | | | | + | | V | + | | +---------+ | + | | | | Alpla multiply, RGB pre-multiply | + | ->| Multiply| [Warp, Persp, Decode, Layer] | + | | | | + | --------- | + | | | + | V | + | +---------+ | + | | | Bilinear filter | + | | Filter | [Warp, Persp] | + | | | | + | +---------+ | + | | | + | V | + +---------X-----------------------------------------+ + + Note that different derivatives of the Fetch Unit exist. Each implements a + specific subset only of the pipeline stages shown above. Restrictions for the + units are specified in [square brackets]. + +maintainers: + - Liu Ying <victor.liu@nxp.com> + +properties: + compatible: + enum: + - fsl,imx8qxp-dc-fetchdecode + - fsl,imx8qxp-dc-fetcheco + - fsl,imx8qxp-dc-fetchlayer + - fsl,imx8qxp-dc-fetchwarp + + reg: + maxItems: 2 + + reg-names: + items: + - const: pec + - const: cfg + + fsl,prg: + $ref: /schemas/types.yaml#/definitions/phandle + description: + Optional Prefetch Resolve Gasket associated with the Fetch Unit. + +required: + - compatible + - reg + - reg-names + +additionalProperties: false + +examples: + - | + fetchlayer@56180ac0 { + compatible = "fsl,imx8qxp-dc-fetchlayer"; + reg = <0x56180ac0 0xc>, <0x56188400 0x404>; + reg-names = "pec", "cfg"; + }; diff --git a/Documentation/devicetree/bindings/display/imx/fsl,imx8qxp-dc-filter.yaml b/Documentation/devicetree/bindings/display/imx/fsl,imx8qxp-dc-filter.yaml new file mode 100644 index 000000000000..5c54d5179ee3 --- /dev/null +++ b/Documentation/devicetree/bindings/display/imx/fsl,imx8qxp-dc-filter.yaml @@ -0,0 +1,43 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/display/imx/fsl,imx8qxp-dc-filter.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Freescale i.MX8qxp Display Controller Filter Unit + +description: | + 5x5 FIR filter with 25 programmable coefficients. + + Typical applications are image blurring, sharpening or support for edge + detection algorithms. + +maintainers: + - Liu Ying <victor.liu@nxp.com> + +properties: + compatible: + const: fsl,imx8qxp-dc-filter + + reg: + maxItems: 2 + + reg-names: + items: + - const: pec + - const: cfg + +required: + - compatible + - reg + - reg-names + +additionalProperties: false + +examples: + - | + filter@56180900 { + compatible = "fsl,imx8qxp-dc-filter"; + reg = <0x56180900 0x10>, <0x56183800 0x30>; + reg-names = "pec", "cfg"; + }; diff --git a/Documentation/devicetree/bindings/display/imx/fsl,imx8qxp-dc-framegen.yaml b/Documentation/devicetree/bindings/display/imx/fsl,imx8qxp-dc-framegen.yaml new file mode 100644 index 000000000000..9d1dc3a9de90 --- /dev/null +++ b/Documentation/devicetree/bindings/display/imx/fsl,imx8qxp-dc-framegen.yaml @@ -0,0 +1,64 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/display/imx/fsl,imx8qxp-dc-framegen.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Freescale i.MX8qxp Display Controller Frame Generator + +description: + The Frame Generator (FrameGen) module generates a programmable video timing + and optionally allows to synchronize the generated video timing to external + synchronization signals. + +maintainers: + - Liu Ying <victor.liu@nxp.com> + +properties: + compatible: + const: fsl,imx8qxp-dc-framegen + + reg: + maxItems: 1 + + clocks: + maxItems: 1 + + interrupts: + maxItems: 8 + + interrupt-names: + items: + - const: int0 + - const: int1 + - const: int2 + - const: int3 + - const: primsync_on + - const: primsync_off + - const: secsync_on + - const: secsync_off + +required: + - compatible + - reg + - clocks + - interrupts + - interrupt-names + +additionalProperties: false + +examples: + - | + #include <dt-bindings/clock/imx8-lpcg.h> + #include <dt-bindings/firmware/imx/rsrc.h> + + framegen@5618b800 { + compatible = "fsl,imx8qxp-dc-framegen"; + reg = <0x5618b800 0x98>; + clocks = <&dc0_disp_lpcg IMX_LPCG_CLK_0>; + interrupt-parent = <&dc0_intc>; + interrupts = <18>, <19>, <20>, <21>, <41>, <42>, <43>, <44>; + interrupt-names = "int0", "int1", "int2", "int3", + "primsync_on", "primsync_off", + "secsync_on", "secsync_off"; + }; diff --git a/Documentation/devicetree/bindings/display/imx/fsl,imx8qxp-dc-gammacor.yaml b/Documentation/devicetree/bindings/display/imx/fsl,imx8qxp-dc-gammacor.yaml new file mode 100644 index 000000000000..25ad85742912 --- /dev/null +++ b/Documentation/devicetree/bindings/display/imx/fsl,imx8qxp-dc-gammacor.yaml @@ -0,0 +1,32 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/display/imx/fsl,imx8qxp-dc-gammacor.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Freescale i.MX8qxp Display Controller Gamma Correction Unit + +description: The unit supports non-linear color transformation. + +maintainers: + - Liu Ying <victor.liu@nxp.com> + +properties: + compatible: + const: fsl,imx8qxp-dc-gammacor + + reg: + maxItems: 1 + +required: + - compatible + - reg + +additionalProperties: false + +examples: + - | + gammacor@5618c000 { + compatible = "fsl,imx8qxp-dc-gammacor"; + reg = <0x5618c000 0x20>; + }; diff --git a/Documentation/devicetree/bindings/display/imx/fsl,imx8qxp-dc-layerblend.yaml b/Documentation/devicetree/bindings/display/imx/fsl,imx8qxp-dc-layerblend.yaml new file mode 100644 index 000000000000..2a6ab8a0ed7f --- /dev/null +++ b/Documentation/devicetree/bindings/display/imx/fsl,imx8qxp-dc-layerblend.yaml @@ -0,0 +1,39 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/display/imx/fsl,imx8qxp-dc-layerblend.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Freescale i.MX8qxp Display Controller Layer Blend Unit + +description: Combines two input frames to a single output frame. + +maintainers: + - Liu Ying <victor.liu@nxp.com> + +properties: + compatible: + const: fsl,imx8qxp-dc-layerblend + + reg: + maxItems: 2 + + reg-names: + items: + - const: pec + - const: cfg + +required: + - compatible + - reg + - reg-names + +additionalProperties: false + +examples: + - | + layerblend@56180ba0 { + compatible = "fsl,imx8qxp-dc-layerblend"; + reg = <0x56180ba0 0x10>, <0x5618a400 0x20>; + reg-names = "pec", "cfg"; + }; diff --git a/Documentation/devicetree/bindings/display/imx/fsl,imx8qxp-dc-matrix.yaml b/Documentation/devicetree/bindings/display/imx/fsl,imx8qxp-dc-matrix.yaml new file mode 100644 index 000000000000..d773389dd0dc --- /dev/null +++ b/Documentation/devicetree/bindings/display/imx/fsl,imx8qxp-dc-matrix.yaml @@ -0,0 +1,44 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/display/imx/fsl,imx8qxp-dc-matrix.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Freescale i.MX8qxp Display Controller Color Matrix + +description: + The unit supports linear color transformation, alpha pre-multiply and + alpha masking. + +maintainers: + - Liu Ying <victor.liu@nxp.com> + +properties: + compatible: + const: fsl,imx8qxp-dc-matrix + + reg: + minItems: 1 + maxItems: 2 + + reg-names: + oneOf: + - const: cfg # matrix in display engine + - items: # matrix in pixel engine + - const: pec + - const: cfg + +required: + - compatible + - reg + - reg-names + +additionalProperties: false + +examples: + - | + matrix@5618bc00 { + compatible = "fsl,imx8qxp-dc-matrix"; + reg = <0x5618bc00 0x3c>; + reg-names = "cfg"; + }; diff --git a/Documentation/devicetree/bindings/display/imx/fsl,imx8qxp-dc-pixel-engine.yaml b/Documentation/devicetree/bindings/display/imx/fsl,imx8qxp-dc-pixel-engine.yaml new file mode 100644 index 000000000000..633443a6cc38 --- /dev/null +++ b/Documentation/devicetree/bindings/display/imx/fsl,imx8qxp-dc-pixel-engine.yaml @@ -0,0 +1,250 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/display/imx/fsl,imx8qxp-dc-pixel-engine.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Freescale i.MX8qxp Display Controller Pixel Engine + +description: + All Processing Units that operate in the AXI bus clock domain. Pixel + pipelines have the ability to stall when a destination is busy. Implements + all communication to memory resources and most of the image processing + functions. Interconnection of Processing Units is re-configurable. + +maintainers: + - Liu Ying <victor.liu@nxp.com> + +properties: + compatible: + const: fsl,imx8qxp-dc-pixel-engine + + reg: + maxItems: 1 + + clocks: + maxItems: 1 + + "#address-cells": + const: 1 + + "#size-cells": + const: 1 + + ranges: true + +patternProperties: + "^blit-engine@[0-9a-f]+$": + type: object + additionalProperties: true + + properties: + compatible: + const: fsl,imx8qxp-dc-blit-engine + + "^constframe@[0-9a-f]+$": + type: object + additionalProperties: true + + properties: + compatible: + const: fsl,imx8qxp-dc-constframe + + "^extdst@[0-9a-f]+$": + type: object + additionalProperties: true + + properties: + compatible: + const: fsl,imx8qxp-dc-extdst + + "^fetchdecode@[0-9a-f]+$": + type: object + additionalProperties: true + + properties: + compatible: + const: fsl,imx8qxp-dc-fetchdecode + + "^fetcheco@[0-9a-f]+$": + type: object + additionalProperties: true + + properties: + compatible: + const: fsl,imx8qxp-dc-fetcheco + + "^fetchlayer@[0-9a-f]+$": + type: object + additionalProperties: true + + properties: + compatible: + const: fsl,imx8qxp-dc-fetchlayer + + "^fetchwarp@[0-9a-f]+$": + type: object + additionalProperties: true + + properties: + compatible: + const: fsl,imx8qxp-dc-fetchwarp + + "^hscaler@[0-9a-f]+$": + type: object + additionalProperties: true + + properties: + compatible: + const: fsl,imx8qxp-dc-hscaler + + "^layerblend@[0-9a-f]+$": + type: object + additionalProperties: true + + properties: + compatible: + const: fsl,imx8qxp-dc-layerblend + + "^matrix@[0-9a-f]+$": + type: object + additionalProperties: true + + properties: + compatible: + const: fsl,imx8qxp-dc-matrix + + "^safety@[0-9a-f]+$": + type: object + additionalProperties: true + + properties: + compatible: + const: fsl,imx8qxp-dc-safety + + "^vscaler@[0-9a-f]+$": + type: object + additionalProperties: true + + properties: + compatible: + const: fsl,imx8qxp-dc-vscaler + +required: + - compatible + - reg + - clocks + - "#address-cells" + - "#size-cells" + - ranges + +additionalProperties: false + +examples: + - | + #include <dt-bindings/clock/imx8-lpcg.h> + + pixel-engine@56180800 { + compatible = "fsl,imx8qxp-dc-pixel-engine"; + reg = <0x56180800 0xac00>; + clocks = <&dc0_lpcg IMX_LPCG_CLK_5>; + #address-cells = <1>; + #size-cells = <1>; + ranges; + + constframe@56180960 { + compatible = "fsl,imx8qxp-dc-constframe"; + reg = <0x56180960 0xc>, <0x56184400 0x20>; + reg-names = "pec", "cfg"; + }; + + extdst@56180980 { + compatible = "fsl,imx8qxp-dc-extdst"; + reg = <0x56180980 0x1c>, <0x56184800 0x28>; + reg-names = "pec", "cfg"; + interrupt-parent = <&dc0_intc>; + interrupts = <3>, <4>, <5>; + interrupt-names = "shdload", "framecomplete", "seqcomplete"; + }; + + constframe@561809a0 { + compatible = "fsl,imx8qxp-dc-constframe"; + reg = <0x561809a0 0xc>, <0x56184c00 0x20>; + reg-names = "pec", "cfg"; + }; + + extdst@561809c0 { + compatible = "fsl,imx8qxp-dc-extdst"; + reg = <0x561809c0 0x1c>, <0x56185000 0x28>; + reg-names = "pec", "cfg"; + interrupt-parent = <&dc0_intc>; + interrupts = <6>, <7>, <8>; + interrupt-names = "shdload", "framecomplete", "seqcomplete"; + }; + + constframe@561809e0 { + compatible = "fsl,imx8qxp-dc-constframe"; + reg = <0x561809e0 0xc>, <0x56185400 0x20>; + reg-names = "pec", "cfg"; + }; + + extdst@56180a00 { + compatible = "fsl,imx8qxp-dc-extdst"; + reg = <0x56180a00 0x1c>, <0x56185800 0x28>; + reg-names = "pec", "cfg"; + interrupt-parent = <&dc0_intc>; + interrupts = <9>, <10>, <11>; + interrupt-names = "shdload", "framecomplete", "seqcomplete"; + }; + + constframe@56180a20 { + compatible = "fsl,imx8qxp-dc-constframe"; + reg = <0x56180a20 0xc>, <0x56185c00 0x20>; + reg-names = "pec", "cfg"; + }; + + extdst@56180a40 { + compatible = "fsl,imx8qxp-dc-extdst"; + reg = <0x56180a40 0x1c>, <0x56186000 0x28>; + reg-names = "pec", "cfg"; + interrupt-parent = <&dc0_intc>; + interrupts = <12>, <13>, <14>; + interrupt-names = "shdload", "framecomplete", "seqcomplete"; + }; + + fetchwarp@56180a60 { + compatible = "fsl,imx8qxp-dc-fetchwarp"; + reg = <0x56180a60 0x10>, <0x56186400 0x190>; + reg-names = "pec", "cfg"; + }; + + fetchlayer@56180ac0 { + compatible = "fsl,imx8qxp-dc-fetchlayer"; + reg = <0x56180ac0 0xc>, <0x56188400 0x404>; + reg-names = "pec", "cfg"; + }; + + layerblend@56180ba0 { + compatible = "fsl,imx8qxp-dc-layerblend"; + reg = <0x56180ba0 0x10>, <0x5618a400 0x20>; + reg-names = "pec", "cfg"; + }; + + layerblend@56180bc0 { + compatible = "fsl,imx8qxp-dc-layerblend"; + reg = <0x56180bc0 0x10>, <0x5618a800 0x20>; + reg-names = "pec", "cfg"; + }; + + layerblend@56180be0 { + compatible = "fsl,imx8qxp-dc-layerblend"; + reg = <0x56180be0 0x10>, <0x5618ac00 0x20>; + reg-names = "pec", "cfg"; + }; + + layerblend@56180c00 { + compatible = "fsl,imx8qxp-dc-layerblend"; + reg = <0x56180c00 0x10>, <0x5618b000 0x20>; + reg-names = "pec", "cfg"; + }; + }; diff --git a/Documentation/devicetree/bindings/display/imx/fsl,imx8qxp-dc-rop.yaml b/Documentation/devicetree/bindings/display/imx/fsl,imx8qxp-dc-rop.yaml new file mode 100644 index 000000000000..7115950ecae0 --- /dev/null +++ b/Documentation/devicetree/bindings/display/imx/fsl,imx8qxp-dc-rop.yaml @@ -0,0 +1,43 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/display/imx/fsl,imx8qxp-dc-rop.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Freescale i.MX8qxp Display Controller Raster Operation Unit + +description: | + The unit can combine up to three input frames to a single output frame, all + having the same dimension. + + The unit supports logic operations, arithmetic operations and packing. + +maintainers: + - Liu Ying <victor.liu@nxp.com> + +properties: + compatible: + const: fsl,imx8qxp-dc-rop + + reg: + maxItems: 2 + + reg-names: + items: + - const: pec + - const: cfg + +required: + - compatible + - reg + - reg-names + +additionalProperties: false + +examples: + - | + rop@56180860 { + compatible = "fsl,imx8qxp-dc-rop"; + reg = <0x56180860 0x10>, <0x56182000 0x20>; + reg-names = "pec", "cfg"; + }; diff --git a/Documentation/devicetree/bindings/display/imx/fsl,imx8qxp-dc-safety.yaml b/Documentation/devicetree/bindings/display/imx/fsl,imx8qxp-dc-safety.yaml new file mode 100644 index 000000000000..66c12948ab09 --- /dev/null +++ b/Documentation/devicetree/bindings/display/imx/fsl,imx8qxp-dc-safety.yaml @@ -0,0 +1,34 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/display/imx/fsl,imx8qxp-dc-safety.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Freescale i.MX8qxp Display Controller Safety Unit + +description: + The unit allows corresponding processing units to be configured in a path + leading to multiple endpoints. + +maintainers: + - Liu Ying <victor.liu@nxp.com> + +properties: + compatible: + const: fsl,imx8qxp-dc-safety + + reg: + maxItems: 1 + +required: + - compatible + - reg + +additionalProperties: false + +examples: + - | + safety@56180800 { + compatible = "fsl,imx8qxp-dc-safety"; + reg = <0x56180800 0x1c>; + }; diff --git a/Documentation/devicetree/bindings/display/imx/fsl,imx8qxp-dc-scaling-engine.yaml b/Documentation/devicetree/bindings/display/imx/fsl,imx8qxp-dc-scaling-engine.yaml new file mode 100644 index 000000000000..76cbe11a6364 --- /dev/null +++ b/Documentation/devicetree/bindings/display/imx/fsl,imx8qxp-dc-scaling-engine.yaml @@ -0,0 +1,83 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/display/imx/fsl,imx8qxp-dc-scaling-engine.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Freescale i.MX8qxp Display Controller Scaling Engine + +description: | + The unit can change the dimension of the input frame by nearest or linear + re-sampling with 1/32 sub pixel precision. + + Internally it consist of two independent blocks for horizontal and vertical + scaling. The sequence of both operations is arbitrary. + + Any frame dimensions between 1 and 16384 pixels in width and height are + supported, except that the vertical scaler has a frame width maximum + depending of the system's functional limitations. + + In general all scale factors are supported inside the supported frame + dimensions. In range of scale factors 1/16..16 the filtered output colors + are LSBit precise (e.g. DC ripple free). + + +-----------+ + | Line | + | Buffer | + +-----------+ + ^ + | + V + |\ +-----------+ + ------+ | | | + | | +-->| Vertical |---- + | ----+ | | Scaler | | + | | |/ +-----------+ | + | | | + | | | + | | | |\ + | ------------- -------------+-----+ | + Input --+ X | +--> Output + | ------------- -------------+-----+ | + | | | |/ + | | | + | | |\ +-----------+ | + | ----+ | | | | + | | +-->| Horizontal|---- + ------+ | | Scaler | + |/ +-----------+ + + The unit supports downscaling, upscaling, sub pixel translation and bob + de-interlacing. + +maintainers: + - Liu Ying <victor.liu@nxp.com> + +properties: + compatible: + enum: + - fsl,imx8qxp-dc-hscaler + - fsl,imx8qxp-dc-vscaler + + reg: + maxItems: 2 + + reg-names: + items: + - const: pec + - const: cfg + +required: + - compatible + - reg + - reg-names + +additionalProperties: false + +examples: + - | + hscaler@561808c0 { + compatible = "fsl,imx8qxp-dc-hscaler"; + reg = <0x561808c0 0x10>, <0x56183000 0x18>; + reg-names = "pec", "cfg"; + }; diff --git a/Documentation/devicetree/bindings/display/imx/fsl,imx8qxp-dc-signature.yaml b/Documentation/devicetree/bindings/display/imx/fsl,imx8qxp-dc-signature.yaml new file mode 100644 index 000000000000..c495822fdc80 --- /dev/null +++ b/Documentation/devicetree/bindings/display/imx/fsl,imx8qxp-dc-signature.yaml @@ -0,0 +1,53 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/display/imx/fsl,imx8qxp-dc-signature.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Freescale i.MX8qxp Display Controller Signature Unit + +description: | + In order to control the correctness of display output, signature values can + be computed for each frame and compared against reference values. In case of + a mismatch (signature violation) a HW event can be triggered, for example a + SW interrupt. + + This unit supports signature computation, reference check, evaluation windows, + alpha masking and panic modes. + +maintainers: + - Liu Ying <victor.liu@nxp.com> + +properties: + compatible: + const: fsl,imx8qxp-dc-signature + + reg: + maxItems: 1 + + interrupts: + maxItems: 3 + + interrupt-names: + items: + - const: shdload + - const: valid + - const: error + +required: + - compatible + - reg + - interrupts + - interrupt-names + +additionalProperties: false + +examples: + - | + signature@5618d000 { + compatible = "fsl,imx8qxp-dc-signature"; + reg = <0x5618d000 0x140>; + interrupt-parent = <&dc0_intc>; + interrupts = <22>, <23>, <24>; + interrupt-names = "shdload", "valid", "error"; + }; diff --git a/Documentation/devicetree/bindings/display/imx/fsl,imx8qxp-dc-store.yaml b/Documentation/devicetree/bindings/display/imx/fsl,imx8qxp-dc-store.yaml new file mode 100644 index 000000000000..42d1b10906be --- /dev/null +++ b/Documentation/devicetree/bindings/display/imx/fsl,imx8qxp-dc-store.yaml @@ -0,0 +1,96 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/display/imx/fsl,imx8qxp-dc-store.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Freescale i.MX8qxp Display Controller Store Unit + +description: | + The Store unit is the interface between the internal pixel processing + pipeline, which is 30-bit RGB plus 8-bit Alpha, and the AXI bus for + destination buffer access. It is used for the destination of Blit Engines. + It comprises a set of built-in functions to generate a wide range of buffer + formats. Note, that these are exactly inverse to corresponding functions in + the Fetch Unit. + + +------X-------------------------+ + | | Store Unit | + | V | + | +-------+ | + | | Gamma | Gamma apply | + | +-------+ | + | | | + | V | + | +-------+ | + | | Color | RGB to YUV | + | +-------+ | + | | | + | V | + | +-------+ | + | | Chroma| YUV444 to 422 | + | +-------+ | + | | | + | V | + | +-------+ | + | | Reduce| Bit width reduction | + | | | dithering | + | +-------+ | + | | | + | V | + | +-------+ | + | | Pack | RGBA/YUV to RAW | + | | Encode| or Compression | + | +-------+ | + | | | + | V | + +------X-------------------------+ + +maintainers: + - Liu Ying <victor.liu@nxp.com> + +properties: + compatible: + const: fsl,imx8qxp-dc-store + + reg: + maxItems: 2 + + reg-names: + items: + - const: pec + - const: cfg + + interrupts: + maxItems: 3 + + interrupt-names: + items: + - const: shdload + - const: framecomplete + - const: seqcomplete + + fsl,lts: + $ref: /schemas/types.yaml#/definitions/phandle + description: + Optional Linear Tile Store associated with the Store Unit. + +required: + - compatible + - reg + - reg-names + - interrupts + - interrupt-names + +additionalProperties: false + +examples: + - | + store@56180940 { + compatible = "fsl,imx8qxp-dc-store"; + reg = <0x56180940 0x1c>, <0x56184000 0x5c>; + reg-names = "pec", "cfg"; + interrupt-parent = <&dc0_intc>; + interrupts = <0>, <1>, <2>; + interrupt-names = "shdload", "framecomplete", "seqcomplete"; + }; diff --git a/Documentation/devicetree/bindings/display/imx/fsl,imx8qxp-dc-tcon.yaml b/Documentation/devicetree/bindings/display/imx/fsl,imx8qxp-dc-tcon.yaml new file mode 100644 index 000000000000..7a3b77ea92c7 --- /dev/null +++ b/Documentation/devicetree/bindings/display/imx/fsl,imx8qxp-dc-tcon.yaml @@ -0,0 +1,45 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/display/imx/fsl,imx8qxp-dc-tcon.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Freescale i.MX8qxp Display Controller Timing Controller + +description: + The TCon can generate a wide range of customized synchronization signals and + does the mapping of the color bits to the output. + +maintainers: + - Liu Ying <victor.liu@nxp.com> + +properties: + compatible: + const: fsl,imx8qxp-dc-tcon + + reg: + maxItems: 1 + + port: + $ref: /schemas/graph.yaml#/properties/port + description: video output + +required: + - compatible + - reg + - port + +additionalProperties: false + +examples: + - | + tcon@5618c800 { + compatible = "fsl,imx8qxp-dc-tcon"; + reg = <0x5618c800 0x588>; + + port { + dc0_disp0_dc0_pixel_combiner_ch0: endpoint { + remote-endpoint = <&dc0_pixel_combiner_ch0_dc0_disp0>; + }; + }; + }; diff --git a/Documentation/devicetree/bindings/display/imx/fsl,imx8qxp-dc.yaml b/Documentation/devicetree/bindings/display/imx/fsl,imx8qxp-dc.yaml new file mode 100644 index 000000000000..0a72f9f0b5fd --- /dev/null +++ b/Documentation/devicetree/bindings/display/imx/fsl,imx8qxp-dc.yaml @@ -0,0 +1,236 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/display/imx/fsl,imx8qxp-dc.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Freescale i.MX8qxp Display Controller + +description: | + The Freescale i.MX8qxp Display Controller(DC) is comprised of three main + components that include a blit engine for 2D graphics accelerations, display + controller for display output processing, as well as a command sequencer. + + Display buffers Source buffers + (AXI read master) (AXI read master) + | .......... | | | | + +---------------------------+------------+------------------+-+-+------+ + | Display Controller (DC) | .......... | | | | | + | | | | | | | + | @@@@@@@@@@@ +----------+------------+------------+ | | | | + A | | Command | | V V | | | | | + X <-+->| Sequencer | | @@@@@@@@@@@@@@@@@@@@@@@@@@@@ | V V V | + I | | (AXI CLK) | | | | | @@@@@@@@@@ | + | @@@@@@@@@@@ | | Pixel Engine | | | | | + | | | | (AXI CLK) | | | | | + | V | @@@@@@@@@@@@@@@@@@@@@@@@@@@@ | | | | + A | *********** | | | | | | | Blit | | + H <-+->| Configure | | V V V V | | Engine | | + B | | (CFG CLK) | | 00000000000 11111111111 | | (AXI CLK)| | + | *********** | | Display | | Display | | | | | + | | | Engine | | Engine | | | | | + | | | (Disp CLK)| | (Disp CLK)| | | | | + | @@@@@@@@@@@ | 00000000000 11111111111 | @@@@@@@@@@ | + I | | Common | | | | | | | + R <-+--| Control | | | Display | | | | + Q | | (AXI CLK) | | | Controller | | | | + | @@@@@@@@@@@ +------------------------------------+ | | + | | | ^ | | + +--------------------------+----------------+-------+---------+--------+ + ^ | | | | + | V V | V + Clocks & Resets Display Display Panic Destination + Output0 Output1 Control buffer + (AXI write master) + +maintainers: + - Liu Ying <victor.liu@nxp.com> + +properties: + compatible: + const: fsl,imx8qxp-dc + + reg: + maxItems: 1 + + clocks: + maxItems: 1 + + resets: + maxItems: 2 + + reset-names: + items: + - const: axi + - const: cfg + + power-domains: + maxItems: 1 + + "#address-cells": + const: 1 + + "#size-cells": + const: 1 + + ranges: true + +patternProperties: + "^command-sequencer@[0-9a-f]+$": + type: object + additionalProperties: true + + properties: + compatible: + const: fsl,imx8qxp-dc-command-sequencer + + "^display-engine@[0-9a-f]+$": + type: object + additionalProperties: true + + properties: + compatible: + const: fsl,imx8qxp-dc-display-engine + + "^interrupt-controller@[0-9a-f]+$": + type: object + additionalProperties: true + + properties: + compatible: + const: fsl,imx8qxp-dc-intc + + "^pixel-engine@[0-9a-f]+$": + type: object + additionalProperties: true + + properties: + compatible: + const: fsl,imx8qxp-dc-pixel-engine + + "^pmu@[0-9a-f]+$": + type: object + additionalProperties: true + + properties: + compatible: + const: fsl,imx8qxp-dc-axi-performance-counter + +required: + - compatible + - reg + - clocks + - power-domains + - "#address-cells" + - "#size-cells" + - ranges + +additionalProperties: false + +examples: + - | + #include <dt-bindings/clock/imx8-lpcg.h> + #include <dt-bindings/firmware/imx/rsrc.h> + + display-controller@56180000 { + compatible = "fsl,imx8qxp-dc"; + reg = <0x56180000 0x40000>; + clocks = <&dc0_lpcg IMX_LPCG_CLK_4>; + power-domains = <&pd IMX_SC_R_DC_0>; + #address-cells = <1>; + #size-cells = <1>; + ranges; + + interrupt-controller@56180040 { + compatible = "fsl,imx8qxp-dc-intc"; + reg = <0x56180040 0x60>; + clocks = <&dc0_lpcg IMX_LPCG_CLK_5>; + interrupt-controller; + interrupt-parent = <&dc0_irqsteer>; + #interrupt-cells = <1>; + interrupts = <448>, <449>, <450>, <64>, + <65>, <66>, <67>, <68>, + <69>, <70>, <193>, <194>, + <195>, <196>, <197>, <72>, + <73>, <74>, <75>, <76>, + <77>, <78>, <79>, <80>, + <81>, <199>, <200>, <201>, + <202>, <203>, <204>, <205>, + <206>, <207>, <208>, <5>, + <0>, <1>, <2>, <3>, + <4>, <82>, <83>, <84>, + <85>, <209>, <210>, <211>, + <212>; + interrupt-names = "store9_shdload", + "store9_framecomplete", + "store9_seqcomplete", + "extdst0_shdload", + "extdst0_framecomplete", + "extdst0_seqcomplete", + "extdst4_shdload", + "extdst4_framecomplete", + "extdst4_seqcomplete", + "extdst1_shdload", + "extdst1_framecomplete", + "extdst1_seqcomplete", + "extdst5_shdload", + "extdst5_framecomplete", + "extdst5_seqcomplete", + "disengcfg_shdload0", + "disengcfg_framecomplete0", + "disengcfg_seqcomplete0", + "framegen0_int0", + "framegen0_int1", + "framegen0_int2", + "framegen0_int3", + "sig0_shdload", + "sig0_valid", + "sig0_error", + "disengcfg_shdload1", + "disengcfg_framecomplete1", + "disengcfg_seqcomplete1", + "framegen1_int0", + "framegen1_int1", + "framegen1_int2", + "framegen1_int3", + "sig1_shdload", + "sig1_valid", + "sig1_error", + "reserved", + "cmdseq_error", + "comctrl_sw0", + "comctrl_sw1", + "comctrl_sw2", + "comctrl_sw3", + "framegen0_primsync_on", + "framegen0_primsync_off", + "framegen0_secsync_on", + "framegen0_secsync_off", + "framegen1_primsync_on", + "framegen1_primsync_off", + "framegen1_secsync_on", + "framegen1_secsync_off"; + }; + + pixel-engine@56180800 { + compatible = "fsl,imx8qxp-dc-pixel-engine"; + reg = <0x56180800 0xac00>; + clocks = <&dc0_lpcg IMX_LPCG_CLK_5>; + #address-cells = <1>; + #size-cells = <1>; + ranges; + }; + + display-engine@5618b400 { + compatible = "fsl,imx8qxp-dc-display-engine"; + reg = <0x5618b400 0x14>, <0x5618b800 0x1c00>; + reg-names = "top", "cfg"; + interrupt-parent = <&dc0_intc>; + interrupts = <15>, <16>, <17>; + interrupt-names = "shdload", "framecomplete", "seqcomplete"; + power-domains = <&pd IMX_SC_R_DC_0_PLL_0>; + #address-cells = <1>; + #size-cells = <1>; + ranges; + }; + }; diff --git a/Documentation/devicetree/bindings/display/panel/himax,hx8394.yaml b/Documentation/devicetree/bindings/display/panel/himax,hx8394.yaml index 75ccabff308b..5725a587e35c 100644 --- a/Documentation/devicetree/bindings/display/panel/himax,hx8394.yaml +++ b/Documentation/devicetree/bindings/display/panel/himax,hx8394.yaml @@ -17,12 +17,17 @@ description: properties: compatible: - items: - - enum: - - hannstar,hsd060bhw4 - - microchip,ac40t08a-mipi-panel - - powkiddy,x55-panel - - const: himax,hx8394 + oneOf: + - items: + - enum: + - hannstar,hsd060bhw4 + - microchip,ac40t08a-mipi-panel + - powkiddy,x55-panel + - const: himax,hx8394 + - items: + - enum: + - huiling,hl055fhav028c + - const: himax,hx8399c reg: maxItems: 1 diff --git a/Documentation/devicetree/bindings/display/panel/ilitek,ili9881c.yaml b/Documentation/devicetree/bindings/display/panel/ilitek,ili9881c.yaml index baf5dfe5f5eb..a51af61d4846 100644 --- a/Documentation/devicetree/bindings/display/panel/ilitek,ili9881c.yaml +++ b/Documentation/devicetree/bindings/display/panel/ilitek,ili9881c.yaml @@ -19,6 +19,7 @@ properties: - ampire,am8001280g - bananapi,lhr050h41 - feixin,k101-im2byl02 + - raspberrypi,dsi-7inch - startek,kd050hdfia020 - tdo,tl050hdv35 - wanchanglong,w552946aba diff --git a/Documentation/devicetree/bindings/display/panel/panel-simple.yaml b/Documentation/devicetree/bindings/display/panel/panel-simple.yaml index 5542c9229d54..1ac1f0219079 100644 --- a/Documentation/devicetree/bindings/display/panel/panel-simple.yaml +++ b/Documentation/devicetree/bindings/display/panel/panel-simple.yaml @@ -57,6 +57,8 @@ properties: - auo,g121ean01 # AU Optronics Corporation 15.6" (1366x768) TFT LCD panel - auo,g156xtn01 + # AU Optronics Corporation 23.8" FHD (1920x1080) TFT LCD panel + - auo,p238han01 # AU Optronics Corporation 31.5" FHD (1920x1080) TFT LCD panel - auo,p320hvn03 # AU Optronics Corporation 21.5" FHD (1920x1080) color TFT LCD panel diff --git a/Documentation/devicetree/bindings/display/panel/renesas,r61307.yaml b/Documentation/devicetree/bindings/display/panel/renesas,r61307.yaml new file mode 100644 index 000000000000..90cce221c0d1 --- /dev/null +++ b/Documentation/devicetree/bindings/display/panel/renesas,r61307.yaml @@ -0,0 +1,94 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/display/panel/renesas,r61307.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Renesas R61307 based DSI Display Panel + +maintainers: + - Svyatoslav Ryhel <clamor95@gmail.com> + +description: + The Renesas R61307 is a generic DSI Panel IC used to control LCD panels. + +allOf: + - $ref: panel-common.yaml# + +properties: + compatible: + items: + - enum: + # KOE/HITACHI TX13D100VM0EAA 5.0" XGA TFT LCD panel + - hit,tx13d100vm0eaa + - koe,tx13d100vm0eaa + - const: renesas,r61307 + + reg: + maxItems: 1 + + vcc-supply: + description: Regulator for main power supply. + + iovcc-supply: + description: Regulator for 1.8V IO power supply. + + backlight: true + + renesas,gamma: + $ref: /schemas/types.yaml#/definitions/uint32 + description: + 0 - disabled + 1-3 - gamma setting A presets + enum: [0, 1, 2, 3] + + renesas,column-inversion: + type: boolean + description: switch between line and column inversion. The line + inversion is set by default. + + renesas,contrast: + type: boolean + description: digital contrast adjustment + + reset-gpios: true + port: true + +required: + - compatible + - port + - backlight + +additionalProperties: false + +examples: + - | + #include <dt-bindings/gpio/gpio.h> + + dsi { + #address-cells = <1>; + #size-cells = <0>; + + panel@1 { + compatible = "koe,tx13d100vm0eaa", "renesas,r61307"; + reg = <1>; + + reset-gpios = <&gpio 176 GPIO_ACTIVE_LOW>; + + renesas,gamma = <3>; + renesas,column-inversion; + renesas,contrast; + + vcc-supply = <&vcc_3v0_lcd>; + iovcc-supply = <&iovcc_1v8_lcd>; + + backlight = <&backlight>; + + port { + panel_in: endpoint { + remote-endpoint = <&dsi_out>; + }; + }; + }; + }; +... diff --git a/Documentation/devicetree/bindings/display/panel/renesas,r69328.yaml b/Documentation/devicetree/bindings/display/panel/renesas,r69328.yaml new file mode 100644 index 000000000000..1cd219b510ee --- /dev/null +++ b/Documentation/devicetree/bindings/display/panel/renesas,r69328.yaml @@ -0,0 +1,73 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/display/panel/renesas,r69328.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Renesas R69328 based DSI Display Panel + +maintainers: + - Svyatoslav Ryhel <clamor95@gmail.com> + +description: + The Renesas R69328 is a generic DSI Panel IC used to control LCD panels. + +allOf: + - $ref: panel-common.yaml# + +properties: + compatible: + items: + - enum: + # JDI DX12D100VM0EAA 4.7" WXGA TFT LCD panel + - jdi,dx12d100vm0eaa + - const: renesas,r69328 + + reg: + maxItems: 1 + + vdd-supply: + description: Regulator for main power supply. + + vddio-supply: + description: Regulator for 1.8V IO power supply. + + backlight: true + + reset-gpios: true + port: true + +required: + - compatible + - port + - backlight + +additionalProperties: false + +examples: + - | + #include <dt-bindings/gpio/gpio.h> + + dsi { + #address-cells = <1>; + #size-cells = <0>; + + panel@1 { + compatible = "jdi,dx12d100vm0eaa", "renesas,r69328"; + reg = <1>; + + reset-gpios = <&gpio 176 GPIO_ACTIVE_LOW>; + + vdd-supply = <&vdd_3v0_lcd>; + vddio-supply = <&vdd_1v8_io>; + + backlight = <&backlight>; + + port { + panel_in: endpoint { + remote-endpoint = <&dsi_out>; + }; + }; + }; + }; +... diff --git a/Documentation/devicetree/bindings/display/panel/sitronix,st7701.yaml b/Documentation/devicetree/bindings/display/panel/sitronix,st7701.yaml index b07f3eca669b..1e434240ea3f 100644 --- a/Documentation/devicetree/bindings/display/panel/sitronix,st7701.yaml +++ b/Documentation/devicetree/bindings/display/panel/sitronix,st7701.yaml @@ -29,6 +29,7 @@ properties: - densitron,dmt028vghmcmi-1a - elida,kd50t048a - techstar,ts8550b + - winstar,wf40eswaa6mnn0 - const: sitronix,st7701 reg: diff --git a/Documentation/devicetree/bindings/display/panel/visionox,rm69299.yaml b/Documentation/devicetree/bindings/display/panel/visionox,rm69299.yaml index 30047a62fc11..f0a82f0ff790 100644 --- a/Documentation/devicetree/bindings/display/panel/visionox,rm69299.yaml +++ b/Documentation/devicetree/bindings/display/panel/visionox,rm69299.yaml @@ -18,7 +18,9 @@ allOf: properties: compatible: - const: visionox,rm69299-1080p-display + enum: + - visionox,rm69299-1080p-display + - visionox,rm69299-shift reg: maxItems: 1 diff --git a/Documentation/devicetree/bindings/display/renesas,rzg2l-du.yaml b/Documentation/devicetree/bindings/display/renesas,rzg2l-du.yaml index 95e3d5e74b87..1e32d14b6edb 100644 --- a/Documentation/devicetree/bindings/display/renesas,rzg2l-du.yaml +++ b/Documentation/devicetree/bindings/display/renesas,rzg2l-du.yaml @@ -20,6 +20,7 @@ properties: - enum: - renesas,r9a07g043u-du # RZ/G2UL - renesas,r9a07g044-du # RZ/G2{L,LC} + - renesas,r9a09g057-du # RZ/V2H(P) - items: - enum: - renesas,r9a07g054-du # RZ/V2L @@ -101,7 +102,12 @@ allOf: required: - port@0 - else: + - if: + properties: + compatible: + contains: + const: renesas,r9a07g044-du + then: properties: ports: properties: @@ -113,6 +119,21 @@ allOf: required: - port@0 - port@1 + - if: + properties: + compatible: + contains: + const: renesas,r9a09g057-du + then: + properties: + ports: + properties: + port@0: + description: DSI + port@1: false + + required: + - port@0 examples: # RZ/G2L DU diff --git a/Documentation/devicetree/bindings/display/ti/ti,am65x-dss.yaml b/Documentation/devicetree/bindings/display/ti/ti,am65x-dss.yaml index 31c4ffcb599c..a5b13cb7bc73 100644 --- a/Documentation/devicetree/bindings/display/ti/ti,am65x-dss.yaml +++ b/Documentation/devicetree/bindings/display/ti/ti,am65x-dss.yaml @@ -12,18 +12,25 @@ maintainers: - Tomi Valkeinen <tomi.valkeinen@ti.com> description: | - The AM625 and AM65x TI Keystone Display SubSystem with two output + The AM625 and AM65x TI Keystone Display SubSystem has two output ports and two video planes. In AM65x DSS, the first video port supports 1 OLDI TX and in AM625 DSS, the first video port output is internally routed to 2 OLDI TXes. The second video port supports DPI format. The first plane is full video plane with all features and the second is a "lite plane" without scaling support. + The AM62L display subsystem has a single output port which supports DPI + format but it only supports single video "lite plane" which does not support + scaling. The output port is routed to SoC boundary via DPI interface and same + DPI signals are also routed internally to DSI Tx controller present within the + SoC. Due to clocking limitations only one of the interface i.e. either DSI or + DPI can be used at once. properties: compatible: enum: - ti,am625-dss - ti,am62a7-dss + - ti,am62l-dss - ti,am65x-dss reg: @@ -91,6 +98,8 @@ properties: For AM625 DSS, the internal DPI output port node from video port 1. For AM62A7 DSS, the port is tied off inside the SoC. + For AM62L DSS, the DSS DPI output port node from video port 1 + or DSI Tx controller node connected to video port 1. port@1: $ref: /schemas/graph.yaml#/properties/port @@ -123,6 +132,16 @@ allOf: ports: properties: port@0: false + - if: + properties: + compatible: + contains: + const: ti,am62l-dss + then: + properties: + ports: + properties: + port@1: false required: - compatible diff --git a/Documentation/devicetree/bindings/gpu/arm,mali-utgard.yaml b/Documentation/devicetree/bindings/gpu/arm,mali-utgard.yaml index 9318817ea135..c8d0d9192d92 100644 --- a/Documentation/devicetree/bindings/gpu/arm,mali-utgard.yaml +++ b/Documentation/devicetree/bindings/gpu/arm,mali-utgard.yaml @@ -47,6 +47,7 @@ properties: - hisilicon,hi6220-mali - mediatek,mt7623-mali - rockchip,rk3328-mali + - rockchip,rk3528-mali - const: arm,mali-450 # "arm,mali-300" @@ -148,6 +149,7 @@ allOf: - rockchip,rk3188-mali - rockchip,rk3228-mali - rockchip,rk3328-mali + - rockchip,rk3528-mali then: required: - resets diff --git a/Documentation/devicetree/bindings/interrupt-controller/fsl,imx8qxp-dc-intc.yaml b/Documentation/devicetree/bindings/interrupt-controller/fsl,imx8qxp-dc-intc.yaml new file mode 100644 index 000000000000..6985ee644a25 --- /dev/null +++ b/Documentation/devicetree/bindings/interrupt-controller/fsl,imx8qxp-dc-intc.yaml @@ -0,0 +1,318 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/interrupt-controller/fsl,imx8qxp-dc-intc.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Freescale i.MX8qxp Display Controller interrupt controller + +description: | + The Display Controller has a built-in interrupt controller with the following + features for all relevant HW events: + + * Enable bit (mask) + * Status bit (set by an HW event) + * Preset bit (can be used by SW to set status) + * Clear bit (used by SW to reset the status) + + Each interrupt can be connected as IRQ (maskable) and/or NMI (non-maskable). + Alternatively the un-masked trigger signals for all HW events are provided, + allowing it to use a global interrupt controller instead. + + Each interrupt can be protected against SW running in user mode. In that case, + only privileged AHB access can control the interrupt status. + +maintainers: + - Liu Ying <victor.liu@nxp.com> + +properties: + compatible: + const: fsl,imx8qxp-dc-intc + + reg: + maxItems: 1 + + clocks: + maxItems: 1 + + interrupt-controller: true + + "#interrupt-cells": + const: 1 + + interrupts: + items: + - description: store9 shadow load interrupt(blit engine) + - description: store9 frame complete interrupt(blit engine) + - description: store9 sequence complete interrupt(blit engine) + - description: + extdst0 shadow load interrupt + (display controller, content stream 0) + - description: + extdst0 frame complete interrupt + (display controller, content stream 0) + - description: + extdst0 sequence complete interrupt + (display controller, content stream 0) + - description: + extdst4 shadow load interrupt + (display controller, safety stream 0) + - description: + extdst4 frame complete interrupt + (display controller, safety stream 0) + - description: + extdst4 sequence complete interrupt + (display controller, safety stream 0) + - description: + extdst1 shadow load interrupt + (display controller, content stream 1) + - description: + extdst1 frame complete interrupt + (display controller, content stream 1) + - description: + extdst1 sequence complete interrupt + (display controller, content stream 1) + - description: + extdst5 shadow load interrupt + (display controller, safety stream 1) + - description: + extdst5 frame complete interrupt + (display controller, safety stream 1) + - description: + extdst5 sequence complete interrupt + (display controller, safety stream 1) + - description: + disengcfg0 shadow load interrupt + (display controller, display stream 0) + - description: + disengcfg0 frame complete interrupt + (display controller, display stream 0) + - description: + disengcfg0 sequence complete interrupt + (display controller, display stream 0) + - description: + framegen0 programmable interrupt0 + (display controller, display stream 0) + - description: + framegen0 programmable interrupt1 + (display controller, display stream 0) + - description: + framegen0 programmable interrupt2 + (display controller, display stream 0) + - description: + framegen0 programmable interrupt3 + (display controller, display stream 0) + - description: + signature0 shadow load interrupt + (display controller, display stream 0) + - description: + signature0 measurement valid interrupt + (display controller, display stream 0) + - description: + signature0 error condition interrupt + (display controller, display stream 0) + - description: + disengcfg1 shadow load interrupt + (display controller, display stream 1) + - description: + disengcfg1 frame complete interrupt + (display controller, display stream 1) + - description: + disengcfg1 sequence complete interrupt + (display controller, display stream 1) + - description: + framegen1 programmable interrupt0 + (display controller, display stream 1) + - description: + framegen1 programmable interrupt1 + (display controller, display stream 1) + - description: + framegen1 programmable interrupt2 + (display controller, display stream 1) + - description: + framegen1 programmable interrupt3 + (display controller, display stream 1) + - description: + signature1 shadow load interrupt + (display controller, display stream 1) + - description: + signature1 measurement valid interrupt + (display controller, display stream 1) + - description: + signature1 error condition interrupt + (display controller, display stream 1) + - description: reserved + - description: + command sequencer error condition interrupt(command sequencer) + - description: + common control software interrupt0(common control) + - description: + common control software interrupt1(common control) + - description: + common control software interrupt2(common control) + - description: + common control software interrupt3(common control) + - description: + framegen0 synchronization status activated interrupt + (display controller, safety stream 0) + - description: + framegen0 synchronization status deactivated interrupt + (display controller, safety stream 0) + - description: + framegen0 synchronization status activated interrupt + (display controller, content stream 0) + - description: + framegen0 synchronization status deactivated interrupt + (display controller, content stream 0) + - description: + framegen1 synchronization status activated interrupt + (display controller, safety stream 1) + - description: + framegen1 synchronization status deactivated interrupt + (display controller, safety stream 1) + - description: + framegen1 synchronization status activated interrupt + (display controller, content stream 1) + - description: + framegen1 synchronization status deactivated interrupt + (display controller, content stream 1) + minItems: 49 + + interrupt-names: + items: + - const: store9_shdload + - const: store9_framecomplete + - const: store9_seqcomplete + - const: extdst0_shdload + - const: extdst0_framecomplete + - const: extdst0_seqcomplete + - const: extdst4_shdload + - const: extdst4_framecomplete + - const: extdst4_seqcomplete + - const: extdst1_shdload + - const: extdst1_framecomplete + - const: extdst1_seqcomplete + - const: extdst5_shdload + - const: extdst5_framecomplete + - const: extdst5_seqcomplete + - const: disengcfg_shdload0 + - const: disengcfg_framecomplete0 + - const: disengcfg_seqcomplete0 + - const: framegen0_int0 + - const: framegen0_int1 + - const: framegen0_int2 + - const: framegen0_int3 + - const: sig0_shdload + - const: sig0_valid + - const: sig0_error + - const: disengcfg_shdload1 + - const: disengcfg_framecomplete1 + - const: disengcfg_seqcomplete1 + - const: framegen1_int0 + - const: framegen1_int1 + - const: framegen1_int2 + - const: framegen1_int3 + - const: sig1_shdload + - const: sig1_valid + - const: sig1_error + - const: reserved + - const: cmdseq_error + - const: comctrl_sw0 + - const: comctrl_sw1 + - const: comctrl_sw2 + - const: comctrl_sw3 + - const: framegen0_primsync_on + - const: framegen0_primsync_off + - const: framegen0_secsync_on + - const: framegen0_secsync_off + - const: framegen1_primsync_on + - const: framegen1_primsync_off + - const: framegen1_secsync_on + - const: framegen1_secsync_off + minItems: 49 + +required: + - compatible + - reg + - clocks + - interrupt-controller + - "#interrupt-cells" + - interrupts + - interrupt-names + +additionalProperties: false + +examples: + - | + #include <dt-bindings/clock/imx8-lpcg.h> + + interrupt-controller@56180040 { + compatible = "fsl,imx8qxp-dc-intc"; + reg = <0x56180040 0x60>; + clocks = <&dc0_lpcg IMX_LPCG_CLK_5>; + interrupt-controller; + interrupt-parent = <&dc0_irqsteer>; + #interrupt-cells = <1>; + interrupts = <448>, <449>, <450>, <64>, + <65>, <66>, <67>, <68>, + <69>, <70>, <193>, <194>, + <195>, <196>, <197>, <72>, + <73>, <74>, <75>, <76>, + <77>, <78>, <79>, <80>, + <81>, <199>, <200>, <201>, + <202>, <203>, <204>, <205>, + <206>, <207>, <208>, <5>, + <0>, <1>, <2>, <3>, + <4>, <82>, <83>, <84>, + <85>, <209>, <210>, <211>, + <212>; + interrupt-names = "store9_shdload", + "store9_framecomplete", + "store9_seqcomplete", + "extdst0_shdload", + "extdst0_framecomplete", + "extdst0_seqcomplete", + "extdst4_shdload", + "extdst4_framecomplete", + "extdst4_seqcomplete", + "extdst1_shdload", + "extdst1_framecomplete", + "extdst1_seqcomplete", + "extdst5_shdload", + "extdst5_framecomplete", + "extdst5_seqcomplete", + "disengcfg_shdload0", + "disengcfg_framecomplete0", + "disengcfg_seqcomplete0", + "framegen0_int0", + "framegen0_int1", + "framegen0_int2", + "framegen0_int3", + "sig0_shdload", + "sig0_valid", + "sig0_error", + "disengcfg_shdload1", + "disengcfg_framecomplete1", + "disengcfg_seqcomplete1", + "framegen1_int0", + "framegen1_int1", + "framegen1_int2", + "framegen1_int3", + "sig1_shdload", + "sig1_valid", + "sig1_error", + "reserved", + "cmdseq_error", + "comctrl_sw0", + "comctrl_sw1", + "comctrl_sw2", + "comctrl_sw3", + "framegen0_primsync_on", + "framegen0_primsync_off", + "framegen0_secsync_on", + "framegen0_secsync_off", + "framegen1_primsync_on", + "framegen1_primsync_off", + "framegen1_secsync_on", + "framegen1_secsync_off"; + }; diff --git a/Documentation/devicetree/bindings/vendor-prefixes.yaml b/Documentation/devicetree/bindings/vendor-prefixes.yaml index 5d2a7a8d3ac6..7a264a372789 100644 --- a/Documentation/devicetree/bindings/vendor-prefixes.yaml +++ b/Documentation/devicetree/bindings/vendor-prefixes.yaml @@ -672,6 +672,8 @@ patternProperties: description: Huawei Technologies Co., Ltd. "^hugsun,.*": description: Shenzhen Hugsun Technology Co. Ltd. + "^huiling,.*": + description: Shenzhen Huiling Information Technology Co., Ltd. "^hwacom,.*": description: HwaCom Systems Inc. "^hxt,.*": diff --git a/Documentation/gpu/drm-uapi.rst b/Documentation/gpu/drm-uapi.rst index 69f72e71a96e..4863a4deb0ee 100644 --- a/Documentation/gpu/drm-uapi.rst +++ b/Documentation/gpu/drm-uapi.rst @@ -693,3 +693,22 @@ dma-buf interoperability Please see Documentation/userspace-api/dma-buf-alloc-exchange.rst for information on how dma-buf is integrated and exposed within DRM. + + +Trace events +============ + +See Documentation/trace/tracepoints.rst for information about using +Linux Kernel Tracepoints. +In the DRM subsystem, some events are considered stable uAPI to avoid +breaking tools (e.g.: GPUVis, umr) relying on them. Stable means that fields +cannot be removed, nor their formatting updated. Adding new fields is +possible, under the normal uAPI requirements. + +Stable uAPI events +------------------ + +From ``drivers/gpu/drm/scheduler/gpu_scheduler_trace.h`` + +.. kernel-doc:: drivers/gpu/drm/scheduler/gpu_scheduler_trace.h + :doc: uAPI trace events
\ No newline at end of file diff --git a/Documentation/gpu/todo.rst b/Documentation/gpu/todo.rst index c57777a24e03..be8637da3fe9 100644 --- a/Documentation/gpu/todo.rst +++ b/Documentation/gpu/todo.rst @@ -515,6 +515,21 @@ Contact: Douglas Anderson <dianders@chromium.org> Level: Starter +Remove devm_drm_put_bridge() +---------------------------- + +Due to how the panel bridge handles the drm_bridge object lifetime, special +care must be taken to dispose of the drm_bridge object when the +panel_bridge is removed. This is currently managed using +devm_drm_put_bridge(), but that is an unsafe, temporary workaround. To fix +that, the DRM panel lifetime needs to be reworked. After the rework is +done, remove devm_drm_put_bridge() and the TODO in +drm_panel_bridge_remove(). + +Contact: Maxime Ripard <mripard@kernel.org>, + Luca Ceresoli <luca.ceresoli@bootlin.com> + +Level: Intermediate Core refactorings ================= diff --git a/Documentation/gpu/vkms.rst b/Documentation/gpu/vkms.rst index ba04ac7c2167..8a8b1002931f 100644 --- a/Documentation/gpu/vkms.rst +++ b/Documentation/gpu/vkms.rst @@ -89,6 +89,17 @@ You can also run subtests if you do not want to run the entire test:: sudo ./build/tests/kms_flip --run-subtest basic-plain-flip --device "sys:/sys/devices/platform/vkms" sudo IGT_DEVICE="sys:/sys/devices/platform/vkms" ./build/tests/kms_flip --run-subtest basic-plain-flip +Testing With KUnit +================== + +KUnit (Kernel unit testing framework) provides a common framework for unit tests +within the Linux kernel. +More information in ../dev-tools/kunit/index.rst . + +To run the VKMS KUnit tests:: + + tools/testing/kunit/kunit.py run --kunitconfig=drivers/gpu/drm/vkms/tests + TODO ==== @@ -122,8 +133,8 @@ There's lots of plane features we could add support for: - Scaling. -- Additional buffer formats, especially YUV formats for video like NV12. - Low/high bpp RGB formats would also be interesting. +- Additional buffer formats. Low/high bpp RGB formats would be interesting + [Good to get started]. - Async updates (currently only possible on cursor plane using the legacy cursor api). diff --git a/MAINTAINERS b/MAINTAINERS index 0c1d245bf7b8..7e7515a412e9 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -8064,6 +8064,14 @@ F: Documentation/devicetree/bindings/display/imx/ F: drivers/gpu/drm/imx/ipuv3/ F: drivers/gpu/ipu-v3/ +DRM DRIVERS FOR FREESCALE IMX8 DISPLAY CONTROLLER +M: Liu Ying <victor.liu@nxp.com> +L: dri-devel@lists.freedesktop.org +S: Maintained +T: git https://gitlab.freedesktop.org/drm/misc/kernel.git +F: Documentation/devicetree/bindings/display/imx/fsl,imx8qxp-dc*.yaml +F: drivers/gpu/drm/imx/dc/ + DRM DRIVERS FOR FREESCALE IMX BRIDGE M: Liu Ying <victor.liu@nxp.com> L: dri-devel@lists.freedesktop.org @@ -8307,9 +8315,17 @@ T: git https://gitlab.freedesktop.org/drm/misc/kernel.git F: drivers/gpu/drm/scheduler/ F: include/drm/gpu_scheduler.h +DRM LOG +M: Jocelyn Falempe <jfalempe@redhat.com> +M: Javier Martinez Canillas <javierm@redhat.com> +L: dri-devel@lists.freedesktop.org +S: Supported +T: git https://gitlab.freedesktop.org/drm/misc/kernel.git +F: drivers/gpu/drm/clients/drm_log.c + DRM PANEL DRIVERS M: Neil Armstrong <neil.armstrong@linaro.org> -R: Jessica Zhang <quic_jesszhan@quicinc.com> +R: Jessica Zhang <jessica.zhang@oss.qualcomm.com> L: dri-devel@lists.freedesktop.org S: Maintained T: git https://gitlab.freedesktop.org/drm/misc/kernel.git @@ -8318,6 +8334,26 @@ F: drivers/gpu/drm/drm_panel.c F: drivers/gpu/drm/panel/ F: include/drm/drm_panel.h +DRM PANIC +M: Jocelyn Falempe <jfalempe@redhat.com> +M: Javier Martinez Canillas <javierm@redhat.com> +L: dri-devel@lists.freedesktop.org +S: Supported +T: git https://gitlab.freedesktop.org/drm/misc/kernel.git +F: drivers/gpu/drm/drm_draw.c +F: drivers/gpu/drm/drm_draw_internal.h +F: drivers/gpu/drm/drm_panic*.c +F: include/drm/drm_panic* + +DRM PANIC QR CODE +M: Jocelyn Falempe <jfalempe@redhat.com> +M: Javier Martinez Canillas <javierm@redhat.com> +L: dri-devel@lists.freedesktop.org +L: rust-for-linux@vger.kernel.org +S: Supported +T: git https://gitlab.freedesktop.org/drm/misc/kernel.git +F: drivers/gpu/drm/drm_panic_qr.rs + DRM PRIVACY-SCREEN CLASS M: Hans de Goede <hdegoede@redhat.com> L: dri-devel@lists.freedesktop.org @@ -20365,6 +20401,7 @@ L: linux-arm-msm@vger.kernel.org L: dri-devel@lists.freedesktop.org S: Supported T: git https://gitlab.freedesktop.org/drm/misc/kernel.git +F: Documentation/ABI/testing/sysfs-driver-qaic F: Documentation/accel/qaic/ F: drivers/accel/qaic/ F: include/uapi/drm/qaic_accel.h diff --git a/drivers/accel/amdxdna/aie2_ctx.c b/drivers/accel/amdxdna/aie2_ctx.c index e04549f64d69..3e38a5f637ea 100644 --- a/drivers/accel/amdxdna/aie2_ctx.c +++ b/drivers/accel/amdxdna/aie2_ctx.c @@ -848,7 +848,8 @@ int aie2_cmd_submit(struct amdxdna_hwctx *hwctx, struct amdxdna_sched_job *job, goto up_sem; } - ret = drm_sched_job_init(&job->base, &hwctx->priv->entity, 1, hwctx); + ret = drm_sched_job_init(&job->base, &hwctx->priv->entity, 1, hwctx, + hwctx->client->filp->client_id); if (ret) { XDNA_ERR(xdna, "DRM job init failed, ret %d", ret); goto free_chain; diff --git a/drivers/accel/habanalabs/common/device.c b/drivers/accel/habanalabs/common/device.c index 68eebed3b050..80fa08bf57bd 100644 --- a/drivers/accel/habanalabs/common/device.c +++ b/drivers/accel/habanalabs/common/device.c @@ -1066,28 +1066,11 @@ static bool is_pci_link_healthy(struct hl_device *hdev) return (device_id == hdev->pdev->device); } -static void stringify_time_of_last_heartbeat(struct hl_device *hdev, char *time_str, size_t size, - bool is_pq_hb) -{ - time64_t seconds = is_pq_hb ? hdev->heartbeat_debug_info.last_pq_heartbeat_ts - : hdev->heartbeat_debug_info.last_eq_heartbeat_ts; - struct tm tm; - - if (!seconds) - return; - - time64_to_tm(seconds, 0, &tm); - - snprintf(time_str, size, "%ld-%02d-%02d %02d:%02d:%02d (UTC)", - tm.tm_year + 1900, tm.tm_mon, tm.tm_mday, tm.tm_hour, tm.tm_min, tm.tm_sec); -} - static bool hl_device_eq_heartbeat_received(struct hl_device *hdev) { struct eq_heartbeat_debug_info *heartbeat_debug_info = &hdev->heartbeat_debug_info; u32 cpu_q_id = heartbeat_debug_info->cpu_queue_id, pq_pi_mask = (HL_QUEUE_LENGTH << 1) - 1; struct asic_fixed_properties *prop = &hdev->asic_prop; - char pq_time_str[64] = "N/A", eq_time_str[64] = "N/A"; if (!prop->cpucp_info.eq_health_check_supported) return true; @@ -1095,17 +1078,15 @@ static bool hl_device_eq_heartbeat_received(struct hl_device *hdev) if (!hdev->eq_heartbeat_received) { dev_err(hdev->dev, "EQ heartbeat event was not received!\n"); - stringify_time_of_last_heartbeat(hdev, pq_time_str, sizeof(pq_time_str), true); - stringify_time_of_last_heartbeat(hdev, eq_time_str, sizeof(eq_time_str), false); dev_err(hdev->dev, - "EQ: {CI %u, HB counter %u, last HB time: %s}, PQ: {PI: %u, CI: %u (%u), last HB time: %s}\n", + "EQ: {CI %u, HB counter %u, last HB time: %ptTs}, PQ: {PI: %u, CI: %u (%u), last HB time: %ptTs}\n", hdev->event_queue.ci, heartbeat_debug_info->heartbeat_event_counter, - eq_time_str, + &hdev->heartbeat_debug_info.last_eq_heartbeat_ts, hdev->kernel_queues[cpu_q_id].pi, atomic_read(&hdev->kernel_queues[cpu_q_id].ci), atomic_read(&hdev->kernel_queues[cpu_q_id].ci) & pq_pi_mask, - pq_time_str); + &hdev->heartbeat_debug_info.last_pq_heartbeat_ts); hl_eq_dump(hdev, &hdev->event_queue); diff --git a/drivers/accel/ivpu/ivpu_drv.h b/drivers/accel/ivpu/ivpu_drv.h index 5497e7030e91..b6d6b3238b59 100644 --- a/drivers/accel/ivpu/ivpu_drv.h +++ b/drivers/accel/ivpu/ivpu_drv.h @@ -165,6 +165,7 @@ struct ivpu_device { int boot; int jsm; int tdr; + int inference; int autosuspend; int d0i3_entry_msg; int state_dump_msg; diff --git a/drivers/accel/ivpu/ivpu_hw.c b/drivers/accel/ivpu/ivpu_hw.c index 633160470c93..08dcc31b56f4 100644 --- a/drivers/accel/ivpu/ivpu_hw.c +++ b/drivers/accel/ivpu/ivpu_hw.c @@ -94,12 +94,14 @@ static void timeouts_init(struct ivpu_device *vdev) vdev->timeout.boot = -1; vdev->timeout.jsm = -1; vdev->timeout.tdr = -1; + vdev->timeout.inference = -1; vdev->timeout.autosuspend = -1; vdev->timeout.d0i3_entry_msg = -1; } else if (ivpu_is_fpga(vdev)) { vdev->timeout.boot = 50; vdev->timeout.jsm = 15000; vdev->timeout.tdr = 30000; + vdev->timeout.inference = 900000; vdev->timeout.autosuspend = -1; vdev->timeout.d0i3_entry_msg = 500; vdev->timeout.state_dump_msg = 10000; @@ -107,6 +109,7 @@ static void timeouts_init(struct ivpu_device *vdev) vdev->timeout.boot = 50; vdev->timeout.jsm = 500; vdev->timeout.tdr = 10000; + vdev->timeout.inference = 300000; vdev->timeout.autosuspend = 100; vdev->timeout.d0i3_entry_msg = 100; vdev->timeout.state_dump_msg = 10; @@ -114,6 +117,7 @@ static void timeouts_init(struct ivpu_device *vdev) vdev->timeout.boot = 1000; vdev->timeout.jsm = 500; vdev->timeout.tdr = 2000; + vdev->timeout.inference = 60000; if (ivpu_hw_ip_gen(vdev) == IVPU_HW_IP_37XX) vdev->timeout.autosuspend = 10; else diff --git a/drivers/accel/ivpu/ivpu_pm.c b/drivers/accel/ivpu/ivpu_pm.c index ea30db181cd7..eacda1dbe840 100644 --- a/drivers/accel/ivpu/ivpu_pm.c +++ b/drivers/accel/ivpu/ivpu_pm.c @@ -33,8 +33,11 @@ static unsigned long ivpu_tdr_timeout_ms; module_param_named(tdr_timeout_ms, ivpu_tdr_timeout_ms, ulong, 0644); MODULE_PARM_DESC(tdr_timeout_ms, "Timeout for device hang detection, in milliseconds, 0 - default"); +static unsigned long ivpu_inference_timeout_ms; +module_param_named(inference_timeout_ms, ivpu_inference_timeout_ms, ulong, 0644); +MODULE_PARM_DESC(inference_timeout_ms, "Inference maximum duration, in milliseconds, 0 - default"); + #define PM_RESCHEDULE_LIMIT 5 -#define PM_TDR_HEARTBEAT_LIMIT 30 static void ivpu_pm_prepare_cold_boot(struct ivpu_device *vdev) { @@ -191,6 +194,10 @@ static void ivpu_job_timeout_work(struct work_struct *work) { struct ivpu_pm_info *pm = container_of(work, struct ivpu_pm_info, job_timeout_work.work); struct ivpu_device *vdev = pm->vdev; + unsigned long timeout_ms = ivpu_tdr_timeout_ms ? ivpu_tdr_timeout_ms : vdev->timeout.tdr; + unsigned long inference_timeout_ms = ivpu_inference_timeout_ms ? ivpu_inference_timeout_ms : + vdev->timeout.inference; + u64 inference_max_retries; u64 heartbeat; if (ivpu_jsm_get_heartbeat(vdev, 0, &heartbeat) || heartbeat <= vdev->fw->last_heartbeat) { @@ -198,8 +205,10 @@ static void ivpu_job_timeout_work(struct work_struct *work) goto recovery; } - if (atomic_fetch_inc(&vdev->job_timeout_counter) > PM_TDR_HEARTBEAT_LIMIT) { - ivpu_err(vdev, "Job timeout detected, heartbeat limit exceeded\n"); + inference_max_retries = DIV_ROUND_UP(inference_timeout_ms, timeout_ms); + if (atomic_fetch_inc(&vdev->job_timeout_counter) >= inference_max_retries) { + ivpu_err(vdev, "Job timeout detected, heartbeat limit (%lld) exceeded\n", + inference_max_retries); goto recovery; } diff --git a/drivers/accel/qaic/Makefile b/drivers/accel/qaic/Makefile index 35e883515629..1106b876f737 100644 --- a/drivers/accel/qaic/Makefile +++ b/drivers/accel/qaic/Makefile @@ -10,6 +10,7 @@ qaic-y := \ qaic_control.o \ qaic_data.o \ qaic_drv.o \ + qaic_ras.o \ qaic_timesync.o \ sahara.o diff --git a/drivers/accel/qaic/qaic.h b/drivers/accel/qaic/qaic.h index 0dbb8e32e4b9..c31081e42cee 100644 --- a/drivers/accel/qaic/qaic.h +++ b/drivers/accel/qaic/qaic.h @@ -167,6 +167,14 @@ struct qaic_device { struct workqueue_struct *bootlog_wq; /* Synchronizes access of pages in MHI bootlog device */ struct mutex bootlog_mutex; + /* MHI RAS channel device */ + struct mhi_device *ras_ch; + /* Correctable error count */ + unsigned int ce_count; + /* Un-correctable error count */ + unsigned int ue_count; + /* Un-correctable non-fatal error count */ + unsigned int ue_nf_count; }; struct qaic_drm_device { @@ -213,8 +221,6 @@ struct qaic_bo { bool sliced; /* Request ID of this BO if it is queued for execution */ u16 req_id; - /* Handle assigned to this BO */ - u32 handle; /* Wait on this for completion of DMA transfer of this BO */ struct completion xfer_done; /* diff --git a/drivers/accel/qaic/qaic_data.c b/drivers/accel/qaic/qaic_data.c index 1bce1af7c72c..797289e9d780 100644 --- a/drivers/accel/qaic/qaic_data.c +++ b/drivers/accel/qaic/qaic_data.c @@ -731,7 +731,6 @@ int qaic_create_bo_ioctl(struct drm_device *dev, void *data, struct drm_file *fi if (ret) goto free_bo; - bo->handle = args->handle; drm_gem_object_put(obj); srcu_read_unlock(&qdev->dev_lock, qdev_rcu_id); srcu_read_unlock(&usr->qddev_lock, usr_rcu_id); diff --git a/drivers/accel/qaic/qaic_drv.c b/drivers/accel/qaic/qaic_drv.c index 3b415e2c9431..e31bcb0ecfc9 100644 --- a/drivers/accel/qaic/qaic_drv.c +++ b/drivers/accel/qaic/qaic_drv.c @@ -29,6 +29,7 @@ #include "mhi_controller.h" #include "qaic.h" #include "qaic_debugfs.h" +#include "qaic_ras.h" #include "qaic_timesync.h" #include "sahara.h" @@ -695,6 +696,10 @@ static int __init qaic_init(void) if (ret) pr_debug("qaic: qaic_bootlog_register failed %d\n", ret); + ret = qaic_ras_register(); + if (ret) + pr_debug("qaic: qaic_ras_register failed %d\n", ret); + return 0; free_mhi: @@ -722,6 +727,7 @@ static void __exit qaic_exit(void) * reinitializing the link_up state after the cleanup is done. */ link_up = true; + qaic_ras_unregister(); qaic_bootlog_unregister(); qaic_timesync_deinit(); sahara_unregister(); diff --git a/drivers/accel/qaic/qaic_ras.c b/drivers/accel/qaic/qaic_ras.c new file mode 100644 index 000000000000..914ffc4a9970 --- /dev/null +++ b/drivers/accel/qaic/qaic_ras.c @@ -0,0 +1,642 @@ +// SPDX-License-Identifier: GPL-2.0-only + +/* Copyright (c) 2020-2021, The Linux Foundation. All rights reserved. */ +/* Copyright (c) 2022-2024 Qualcomm Innovation Center, Inc. All rights reserved. */ +/* Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. */ + +#include <asm/byteorder.h> +#include <linux/device.h> +#include <linux/kernel.h> +#include <linux/mhi.h> + +#include "qaic.h" +#include "qaic_ras.h" + +#define MAGIC 0x55AA +#define VERSION 0x2 +#define HDR_SZ 12 +#define NUM_TEMP_LVL 3 +#define POWER_BREAK BIT(0) + +enum msg_type { + MSG_PUSH, /* async push from device */ + MSG_REQ, /* sync request to device */ + MSG_RESP, /* sync response from device */ +}; + +enum err_type { + CE, /* correctable error */ + UE, /* uncorrectable error */ + UE_NF, /* uncorrectable error that is non-fatal, expect a disruption */ + ERR_TYPE_MAX, +}; + +static const char * const err_type_str[] = { + [CE] = "Correctable", + [UE] = "Uncorrectable", + [UE_NF] = "Uncorrectable Non-Fatal", +}; + +static const char * const err_class_str[] = { + [CE] = "Warning", + [UE] = "Fatal", + [UE_NF] = "Warning", +}; + +enum err_source { + SOC_MEM, + PCIE, + DDR, + SYS_BUS1, + SYS_BUS2, + NSP_MEM, + TSENS, +}; + +static const char * const err_src_str[TSENS + 1] = { + [SOC_MEM] = "SoC Memory", + [PCIE] = "PCIE", + [DDR] = "DDR", + [SYS_BUS1] = "System Bus source 1", + [SYS_BUS2] = "System Bus source 2", + [NSP_MEM] = "NSP Memory", + [TSENS] = "Temperature Sensors", +}; + +struct ras_data { + /* header start */ + /* Magic number to validate the message */ + u16 magic; + /* RAS version number */ + u16 ver; + u32 seq_num; + /* RAS message type */ + u8 type; + u8 id; + /* Size of RAS message without the header in byte */ + u16 len; + /* header end */ + s32 result; + /* + * Error source + * 0 : SoC Memory + * 1 : PCIE + * 2 : DDR + * 3 : System Bus source 1 + * 4 : System Bus source 2 + * 5 : NSP Memory + * 6 : Temperature Sensors + */ + u32 source; + /* + * Stores the error type, there are three types of error in RAS + * 0 : correctable error (CE) + * 1 : uncorrectable error (UE) + * 2 : uncorrectable error that is non-fatal (UE_NF) + */ + u32 err_type; + u32 err_threshold; + u32 ce_count; + u32 ue_count; + u32 intr_num; + /* Data specific to error source */ + u8 syndrome[64]; +} __packed; + +struct soc_mem_syndrome { + u64 error_address[8]; +} __packed; + +struct nsp_mem_syndrome { + u32 error_address[8]; + u8 nsp_id; +} __packed; + +struct ddr_syndrome { + u32 count; + u32 irq_status; + u32 data_31_0[2]; + u32 data_63_32[2]; + u32 data_95_64[2]; + u32 data_127_96[2]; + u32 addr_lsb; + u16 addr_msb; + u16 parity_bits; + u16 instance; + u16 err_type; +} __packed; + +struct tsens_syndrome { + u32 threshold_type; + s32 temp; +} __packed; + +struct sysbus1_syndrome { + u32 slave; + u32 err_type; + u16 addr[8]; + u8 instance; +} __packed; + +struct sysbus2_syndrome { + u32 lsb3; + u32 msb3; + u32 lsb2; + u32 msb2; + u32 ext_id; + u16 path; + u16 op_type; + u16 len; + u16 redirect; + u8 valid; + u8 word_error; + u8 non_secure; + u8 opc; + u8 error_code; + u8 trans_type; + u8 addr_space; + u8 instance; +} __packed; + +struct pcie_syndrome { + /* CE info */ + u32 bad_tlp; + u32 bad_dllp; + u32 replay_rollover; + u32 replay_timeout; + u32 rx_err; + u32 internal_ce_count; + /* UE_NF info */ + u32 fc_timeout; + u32 poison_tlp; + u32 ecrc_err; + u32 unsupported_req; + u32 completer_abort; + u32 completion_timeout; + /* UE info */ + u32 addr; + u8 index; + /* + * Flag to indicate specific event of PCIe + * BIT(0): Power break (low power) + * BIT(1) to BIT(7): Reserved + */ + u8 flag; +} __packed; + +static const char * const threshold_type_str[NUM_TEMP_LVL] = { + [0] = "lower", + [1] = "upper", + [2] = "critical", +}; + +static void ras_msg_to_cpu(struct ras_data *msg) +{ + struct sysbus1_syndrome *sysbus1_syndrome = (struct sysbus1_syndrome *)&msg->syndrome[0]; + struct sysbus2_syndrome *sysbus2_syndrome = (struct sysbus2_syndrome *)&msg->syndrome[0]; + struct soc_mem_syndrome *soc_syndrome = (struct soc_mem_syndrome *)&msg->syndrome[0]; + struct nsp_mem_syndrome *nsp_syndrome = (struct nsp_mem_syndrome *)&msg->syndrome[0]; + struct tsens_syndrome *tsens_syndrome = (struct tsens_syndrome *)&msg->syndrome[0]; + struct pcie_syndrome *pcie_syndrome = (struct pcie_syndrome *)&msg->syndrome[0]; + struct ddr_syndrome *ddr_syndrome = (struct ddr_syndrome *)&msg->syndrome[0]; + int i; + + le16_to_cpus(&msg->magic); + le16_to_cpus(&msg->ver); + le32_to_cpus(&msg->seq_num); + le16_to_cpus(&msg->len); + le32_to_cpus(&msg->result); + le32_to_cpus(&msg->source); + le32_to_cpus(&msg->err_type); + le32_to_cpus(&msg->err_threshold); + le32_to_cpus(&msg->ce_count); + le32_to_cpus(&msg->ue_count); + le32_to_cpus(&msg->intr_num); + + switch (msg->source) { + case SOC_MEM: + for (i = 0; i < 8; i++) + le64_to_cpus(&soc_syndrome->error_address[i]); + break; + case PCIE: + le32_to_cpus(&pcie_syndrome->bad_tlp); + le32_to_cpus(&pcie_syndrome->bad_dllp); + le32_to_cpus(&pcie_syndrome->replay_rollover); + le32_to_cpus(&pcie_syndrome->replay_timeout); + le32_to_cpus(&pcie_syndrome->rx_err); + le32_to_cpus(&pcie_syndrome->internal_ce_count); + le32_to_cpus(&pcie_syndrome->fc_timeout); + le32_to_cpus(&pcie_syndrome->poison_tlp); + le32_to_cpus(&pcie_syndrome->ecrc_err); + le32_to_cpus(&pcie_syndrome->unsupported_req); + le32_to_cpus(&pcie_syndrome->completer_abort); + le32_to_cpus(&pcie_syndrome->completion_timeout); + le32_to_cpus(&pcie_syndrome->addr); + break; + case DDR: + le16_to_cpus(&ddr_syndrome->instance); + le16_to_cpus(&ddr_syndrome->err_type); + le32_to_cpus(&ddr_syndrome->count); + le32_to_cpus(&ddr_syndrome->irq_status); + le32_to_cpus(&ddr_syndrome->data_31_0[0]); + le32_to_cpus(&ddr_syndrome->data_31_0[1]); + le32_to_cpus(&ddr_syndrome->data_63_32[0]); + le32_to_cpus(&ddr_syndrome->data_63_32[1]); + le32_to_cpus(&ddr_syndrome->data_95_64[0]); + le32_to_cpus(&ddr_syndrome->data_95_64[1]); + le32_to_cpus(&ddr_syndrome->data_127_96[0]); + le32_to_cpus(&ddr_syndrome->data_127_96[1]); + le16_to_cpus(&ddr_syndrome->parity_bits); + le16_to_cpus(&ddr_syndrome->addr_msb); + le32_to_cpus(&ddr_syndrome->addr_lsb); + break; + case SYS_BUS1: + le32_to_cpus(&sysbus1_syndrome->slave); + le32_to_cpus(&sysbus1_syndrome->err_type); + for (i = 0; i < 8; i++) + le16_to_cpus(&sysbus1_syndrome->addr[i]); + break; + case SYS_BUS2: + le16_to_cpus(&sysbus2_syndrome->op_type); + le16_to_cpus(&sysbus2_syndrome->len); + le16_to_cpus(&sysbus2_syndrome->redirect); + le16_to_cpus(&sysbus2_syndrome->path); + le32_to_cpus(&sysbus2_syndrome->ext_id); + le32_to_cpus(&sysbus2_syndrome->lsb2); + le32_to_cpus(&sysbus2_syndrome->msb2); + le32_to_cpus(&sysbus2_syndrome->lsb3); + le32_to_cpus(&sysbus2_syndrome->msb3); + break; + case NSP_MEM: + for (i = 0; i < 8; i++) + le32_to_cpus(&nsp_syndrome->error_address[i]); + break; + case TSENS: + le32_to_cpus(&tsens_syndrome->threshold_type); + le32_to_cpus(&tsens_syndrome->temp); + break; + } +} + +static void decode_ras_msg(struct qaic_device *qdev, struct ras_data *msg) +{ + struct sysbus1_syndrome *sysbus1_syndrome = (struct sysbus1_syndrome *)&msg->syndrome[0]; + struct sysbus2_syndrome *sysbus2_syndrome = (struct sysbus2_syndrome *)&msg->syndrome[0]; + struct soc_mem_syndrome *soc_syndrome = (struct soc_mem_syndrome *)&msg->syndrome[0]; + struct nsp_mem_syndrome *nsp_syndrome = (struct nsp_mem_syndrome *)&msg->syndrome[0]; + struct tsens_syndrome *tsens_syndrome = (struct tsens_syndrome *)&msg->syndrome[0]; + struct pcie_syndrome *pcie_syndrome = (struct pcie_syndrome *)&msg->syndrome[0]; + struct ddr_syndrome *ddr_syndrome = (struct ddr_syndrome *)&msg->syndrome[0]; + char *class; + char *level; + + if (msg->magic != MAGIC) { + pci_warn(qdev->pdev, "Dropping RAS message with invalid magic %x\n", msg->magic); + return; + } + + if (!msg->ver || msg->ver > VERSION) { + pci_warn(qdev->pdev, "Dropping RAS message with invalid version %d\n", msg->ver); + return; + } + + if (msg->type != MSG_PUSH) { + pci_warn(qdev->pdev, "Dropping non-PUSH RAS message\n"); + return; + } + + if (msg->len != sizeof(*msg) - HDR_SZ) { + pci_warn(qdev->pdev, "Dropping RAS message with invalid len %d\n", msg->len); + return; + } + + if (msg->err_type >= ERR_TYPE_MAX) { + pci_warn(qdev->pdev, "Dropping RAS message with err type %d\n", msg->err_type); + return; + } + + if (msg->err_type == UE) + level = KERN_ERR; + else + level = KERN_WARNING; + + switch (msg->source) { + case SOC_MEM: + dev_printk(level, &qdev->pdev->dev, "RAS event.\nClass:%s\nDescription:%s %s %s\nError Threshold for this report %d\nSyndrome:\n 0x%llx\n 0x%llx\n 0x%llx\n 0x%llx\n 0x%llx\n 0x%llx\n 0x%llx\n 0x%llx\n", + err_class_str[msg->err_type], + err_type_str[msg->err_type], + "error from", + err_src_str[msg->source], + msg->err_threshold, + soc_syndrome->error_address[0], + soc_syndrome->error_address[1], + soc_syndrome->error_address[2], + soc_syndrome->error_address[3], + soc_syndrome->error_address[4], + soc_syndrome->error_address[5], + soc_syndrome->error_address[6], + soc_syndrome->error_address[7]); + break; + case PCIE: + dev_printk(level, &qdev->pdev->dev, "RAS event.\nClass:%s\nDescription:%s %s %s\nError Threshold for this report %d\n", + err_class_str[msg->err_type], + err_type_str[msg->err_type], + "error from", + err_src_str[msg->source], + msg->err_threshold); + + switch (msg->err_type) { + case CE: + /* + * Modeled after AER prints. This continues the dev_printk() from a few + * lines up. We reduce duplication of code, but also avoid re-printing the + * PCI device info so that the end result looks uniform to the log user. + */ + printk(KERN_WARNING pr_fmt("Syndrome:\n Bad TLP count %d\n Bad DLLP count %d\n Replay Rollover count %d\n Replay Timeout count %d\n Recv Error count %d\n Internal CE count %d\n"), + pcie_syndrome->bad_tlp, + pcie_syndrome->bad_dllp, + pcie_syndrome->replay_rollover, + pcie_syndrome->replay_timeout, + pcie_syndrome->rx_err, + pcie_syndrome->internal_ce_count); + if (msg->ver > 0x1) + pr_warn(" Power break %s\n", + pcie_syndrome->flag & POWER_BREAK ? "ON" : "OFF"); + break; + case UE: + printk(KERN_ERR pr_fmt("Syndrome:\n Index %d\n Address 0x%x\n"), + pcie_syndrome->index, pcie_syndrome->addr); + break; + case UE_NF: + printk(KERN_WARNING pr_fmt("Syndrome:\n FC timeout count %d\n Poisoned TLP count %d\n ECRC error count %d\n Unsupported request count %d\n Completer abort count %d\n Completion timeout count %d\n"), + pcie_syndrome->fc_timeout, + pcie_syndrome->poison_tlp, + pcie_syndrome->ecrc_err, + pcie_syndrome->unsupported_req, + pcie_syndrome->completer_abort, + pcie_syndrome->completion_timeout); + break; + default: + break; + } + break; + case DDR: + dev_printk(level, &qdev->pdev->dev, "RAS event.\nClass:%s\nDescription:%s %s %s\nError Threshold for this report %d\nSyndrome:\n Instance %d\n Count %d\n Data 31_0 0x%x 0x%x\n Data 63_32 0x%x 0x%x\n Data 95_64 0x%x 0x%x\n Data 127_96 0x%x 0x%x\n Parity bits 0x%x\n Address msb 0x%x\n Address lsb 0x%x\n", + err_class_str[msg->err_type], + err_type_str[msg->err_type], + "error from", + err_src_str[msg->source], + msg->err_threshold, + ddr_syndrome->instance, + ddr_syndrome->count, + ddr_syndrome->data_31_0[1], + ddr_syndrome->data_31_0[0], + ddr_syndrome->data_63_32[1], + ddr_syndrome->data_63_32[0], + ddr_syndrome->data_95_64[1], + ddr_syndrome->data_95_64[0], + ddr_syndrome->data_127_96[1], + ddr_syndrome->data_127_96[0], + ddr_syndrome->parity_bits, + ddr_syndrome->addr_msb, + ddr_syndrome->addr_lsb); + break; + case SYS_BUS1: + dev_printk(level, &qdev->pdev->dev, "RAS event.\nClass:%s\nDescription:%s %s %s\nError Threshold for this report %d\nSyndrome:\n instance %d\n %s\n err_type %d\n address0 0x%x\n address1 0x%x\n address2 0x%x\n address3 0x%x\n address4 0x%x\n address5 0x%x\n address6 0x%x\n address7 0x%x\n", + err_class_str[msg->err_type], + err_type_str[msg->err_type], + "error from", + err_src_str[msg->source], + msg->err_threshold, + sysbus1_syndrome->instance, + sysbus1_syndrome->slave ? "Slave" : "Master", + sysbus1_syndrome->err_type, + sysbus1_syndrome->addr[0], + sysbus1_syndrome->addr[1], + sysbus1_syndrome->addr[2], + sysbus1_syndrome->addr[3], + sysbus1_syndrome->addr[4], + sysbus1_syndrome->addr[5], + sysbus1_syndrome->addr[6], + sysbus1_syndrome->addr[7]); + break; + case SYS_BUS2: + dev_printk(level, &qdev->pdev->dev, "RAS event.\nClass:%s\nDescription:%s %s %s\nError Threshold for this report %d\nSyndrome:\n instance %d\n valid %d\n word error %d\n non-secure %d\n opc %d\n error code %d\n transaction type %d\n address space %d\n operation type %d\n len %d\n redirect %d\n path %d\n ext_id %d\n lsb2 %d\n msb2 %d\n lsb3 %d\n msb3 %d\n", + err_class_str[msg->err_type], + err_type_str[msg->err_type], + "error from", + err_src_str[msg->source], + msg->err_threshold, + sysbus2_syndrome->instance, + sysbus2_syndrome->valid, + sysbus2_syndrome->word_error, + sysbus2_syndrome->non_secure, + sysbus2_syndrome->opc, + sysbus2_syndrome->error_code, + sysbus2_syndrome->trans_type, + sysbus2_syndrome->addr_space, + sysbus2_syndrome->op_type, + sysbus2_syndrome->len, + sysbus2_syndrome->redirect, + sysbus2_syndrome->path, + sysbus2_syndrome->ext_id, + sysbus2_syndrome->lsb2, + sysbus2_syndrome->msb2, + sysbus2_syndrome->lsb3, + sysbus2_syndrome->msb3); + break; + case NSP_MEM: + dev_printk(level, &qdev->pdev->dev, "RAS event.\nClass:%s\nDescription:%s %s %s\nError Threshold for this report %d\nSyndrome:\n NSP ID %d\n 0x%x\n 0x%x\n 0x%x\n 0x%x\n 0x%x\n 0x%x\n 0x%x\n 0x%x\n", + err_class_str[msg->err_type], + err_type_str[msg->err_type], + "error from", + err_src_str[msg->source], + msg->err_threshold, + nsp_syndrome->nsp_id, + nsp_syndrome->error_address[0], + nsp_syndrome->error_address[1], + nsp_syndrome->error_address[2], + nsp_syndrome->error_address[3], + nsp_syndrome->error_address[4], + nsp_syndrome->error_address[5], + nsp_syndrome->error_address[6], + nsp_syndrome->error_address[7]); + break; + case TSENS: + if (tsens_syndrome->threshold_type >= NUM_TEMP_LVL) { + pci_warn(qdev->pdev, "Dropping RAS message with invalid temp threshold %d\n", + tsens_syndrome->threshold_type); + break; + } + + if (msg->err_type) + class = "Fatal"; + else if (tsens_syndrome->threshold_type) + class = "Critical"; + else + class = "Warning"; + + dev_printk(level, &qdev->pdev->dev, "RAS event.\nClass:%s\nDescription:%s %s %s\nError Threshold for this report %d\nSyndrome:\n %s threshold\n %d deg C\n", + class, + err_type_str[msg->err_type], + "error from", + err_src_str[msg->source], + msg->err_threshold, + threshold_type_str[tsens_syndrome->threshold_type], + tsens_syndrome->temp); + break; + } + + /* Uncorrectable errors are fatal */ + if (msg->err_type == UE) + mhi_soc_reset(qdev->mhi_cntrl); + + switch (msg->err_type) { + case CE: + if (qdev->ce_count != UINT_MAX) + qdev->ce_count++; + break; + case UE: + if (qdev->ce_count != UINT_MAX) + qdev->ue_count++; + break; + case UE_NF: + if (qdev->ce_count != UINT_MAX) + qdev->ue_nf_count++; + break; + default: + /* not possible */ + break; + } +} + +static ssize_t ce_count_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct qaic_device *qdev = pci_get_drvdata(to_pci_dev(dev)); + + return snprintf(buf, PAGE_SIZE, "%d\n", qdev->ce_count); +} + +static ssize_t ue_count_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct qaic_device *qdev = pci_get_drvdata(to_pci_dev(dev)); + + return snprintf(buf, PAGE_SIZE, "%d\n", qdev->ue_count); +} + +static ssize_t ue_nonfatal_count_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct qaic_device *qdev = pci_get_drvdata(to_pci_dev(dev)); + + return snprintf(buf, PAGE_SIZE, "%d\n", qdev->ue_nf_count); +} + +static DEVICE_ATTR_RO(ce_count); +static DEVICE_ATTR_RO(ue_count); +static DEVICE_ATTR_RO(ue_nonfatal_count); + +static struct attribute *ras_attrs[] = { + &dev_attr_ce_count.attr, + &dev_attr_ue_count.attr, + &dev_attr_ue_nonfatal_count.attr, + NULL, +}; + +static struct attribute_group ras_group = { + .attrs = ras_attrs, +}; + +static int qaic_ras_mhi_probe(struct mhi_device *mhi_dev, const struct mhi_device_id *id) +{ + struct qaic_device *qdev = pci_get_drvdata(to_pci_dev(mhi_dev->mhi_cntrl->cntrl_dev)); + struct ras_data *resp; + int ret; + + ret = mhi_prepare_for_transfer(mhi_dev); + if (ret) + return ret; + + resp = kzalloc(sizeof(*resp), GFP_KERNEL); + if (!resp) { + mhi_unprepare_from_transfer(mhi_dev); + return -ENOMEM; + } + + ret = mhi_queue_buf(mhi_dev, DMA_FROM_DEVICE, resp, sizeof(*resp), MHI_EOT); + if (ret) { + kfree(resp); + mhi_unprepare_from_transfer(mhi_dev); + return ret; + } + + ret = device_add_group(&qdev->pdev->dev, &ras_group); + if (ret) { + mhi_unprepare_from_transfer(mhi_dev); + pci_dbg(qdev->pdev, "ras add sysfs failed %d\n", ret); + return ret; + } + + dev_set_drvdata(&mhi_dev->dev, qdev); + qdev->ras_ch = mhi_dev; + + return ret; +} + +static void qaic_ras_mhi_remove(struct mhi_device *mhi_dev) +{ + struct qaic_device *qdev; + + qdev = dev_get_drvdata(&mhi_dev->dev); + qdev->ras_ch = NULL; + device_remove_group(&qdev->pdev->dev, &ras_group); + mhi_unprepare_from_transfer(mhi_dev); +} + +static void qaic_ras_mhi_ul_xfer_cb(struct mhi_device *mhi_dev, struct mhi_result *mhi_result) {} + +static void qaic_ras_mhi_dl_xfer_cb(struct mhi_device *mhi_dev, struct mhi_result *mhi_result) +{ + struct qaic_device *qdev = dev_get_drvdata(&mhi_dev->dev); + struct ras_data *msg = mhi_result->buf_addr; + int ret; + + if (mhi_result->transaction_status) { + kfree(msg); + return; + } + + ras_msg_to_cpu(msg); + decode_ras_msg(qdev, msg); + + ret = mhi_queue_buf(qdev->ras_ch, DMA_FROM_DEVICE, msg, sizeof(*msg), MHI_EOT); + if (ret) { + dev_err(&mhi_dev->dev, "Cannot requeue RAS recv buf %d\n", ret); + kfree(msg); + } +} + +static const struct mhi_device_id qaic_ras_mhi_match_table[] = { + { .chan = "QAIC_STATUS", }, + {}, +}; + +static struct mhi_driver qaic_ras_mhi_driver = { + .id_table = qaic_ras_mhi_match_table, + .remove = qaic_ras_mhi_remove, + .probe = qaic_ras_mhi_probe, + .ul_xfer_cb = qaic_ras_mhi_ul_xfer_cb, + .dl_xfer_cb = qaic_ras_mhi_dl_xfer_cb, + .driver = { + .name = "qaic_ras", + }, +}; + +int qaic_ras_register(void) +{ + return mhi_driver_register(&qaic_ras_mhi_driver); +} + +void qaic_ras_unregister(void) +{ + mhi_driver_unregister(&qaic_ras_mhi_driver); +} diff --git a/drivers/accel/qaic/qaic_ras.h b/drivers/accel/qaic/qaic_ras.h new file mode 100644 index 000000000000..d44a4eeeb060 --- /dev/null +++ b/drivers/accel/qaic/qaic_ras.h @@ -0,0 +1,10 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* Copyright (c) 2020, The Linux Foundation. All rights reserved. */ + +#ifndef __QAIC_RAS_H__ +#define __QAIC_RAS_H__ + +int qaic_ras_register(void); +void qaic_ras_unregister(void); + +#endif /* __QAIC_RAS_H__ */ diff --git a/drivers/dma-buf/Kconfig b/drivers/dma-buf/Kconfig index fee04fdb0822..b46eb8a552d7 100644 --- a/drivers/dma-buf/Kconfig +++ b/drivers/dma-buf/Kconfig @@ -36,7 +36,6 @@ config UDMABUF depends on DMA_SHARED_BUFFER depends on MEMFD_CREATE || COMPILE_TEST depends on MMU - select VMAP_PFN help A driver to let userspace turn memfd regions into dma-bufs. Qemu can use this to create host dmabufs for guest framebuffers. diff --git a/drivers/dma-buf/dma-fence-chain.c b/drivers/dma-buf/dma-fence-chain.c index 9663ba1bb6ac..a8a90acf4f34 100644 --- a/drivers/dma-buf/dma-fence-chain.c +++ b/drivers/dma-buf/dma-fence-chain.c @@ -218,7 +218,6 @@ static void dma_fence_chain_set_deadline(struct dma_fence *fence, } const struct dma_fence_ops dma_fence_chain_ops = { - .use_64bit_seqno = true, .get_driver_name = dma_fence_chain_get_driver_name, .get_timeline_name = dma_fence_chain_get_timeline_name, .enable_signaling = dma_fence_chain_enable_signaling, @@ -252,7 +251,7 @@ void dma_fence_chain_init(struct dma_fence_chain *chain, chain->prev_seqno = 0; /* Try to reuse the context of the previous chain node. */ - if (prev_chain && __dma_fence_is_later(seqno, prev->seqno, prev->ops)) { + if (prev_chain && __dma_fence_is_later(prev, seqno, prev->seqno)) { context = prev->context; chain->prev_seqno = prev->seqno; } else { @@ -262,8 +261,8 @@ void dma_fence_chain_init(struct dma_fence_chain *chain, seqno = max(prev->seqno, seqno); } - dma_fence_init(&chain->base, &dma_fence_chain_ops, - &chain->lock, context, seqno); + dma_fence_init64(&chain->base, &dma_fence_chain_ops, &chain->lock, + context, seqno); /* * Chaining dma_fence_chain container together is only allowed through diff --git a/drivers/dma-buf/dma-fence.c b/drivers/dma-buf/dma-fence.c index f0cdd3e99d36..74f9e4b665e3 100644 --- a/drivers/dma-buf/dma-fence.c +++ b/drivers/dma-buf/dma-fence.c @@ -538,8 +538,8 @@ void dma_fence_release(struct kref *kref) if (WARN(!list_empty(&fence->cb_list) && !test_bit(DMA_FENCE_FLAG_SIGNALED_BIT, &fence->flags), "Fence %s:%s:%llx:%llx released with pending signals!\n", - fence->ops->get_driver_name(fence), - fence->ops->get_timeline_name(fence), + dma_fence_driver_name(fence), + dma_fence_timeline_name(fence), fence->context, fence->seqno)) { unsigned long flags; @@ -983,12 +983,32 @@ EXPORT_SYMBOL(dma_fence_set_deadline); void dma_fence_describe(struct dma_fence *fence, struct seq_file *seq) { seq_printf(seq, "%s %s seq %llu %ssignalled\n", - fence->ops->get_driver_name(fence), - fence->ops->get_timeline_name(fence), fence->seqno, + dma_fence_driver_name(fence), + dma_fence_timeline_name(fence), + fence->seqno, dma_fence_is_signaled(fence) ? "" : "un"); } EXPORT_SYMBOL(dma_fence_describe); +static void +__dma_fence_init(struct dma_fence *fence, const struct dma_fence_ops *ops, + spinlock_t *lock, u64 context, u64 seqno, unsigned long flags) +{ + BUG_ON(!lock); + BUG_ON(!ops || !ops->get_driver_name || !ops->get_timeline_name); + + kref_init(&fence->refcount); + fence->ops = ops; + INIT_LIST_HEAD(&fence->cb_list); + fence->lock = lock; + fence->context = context; + fence->seqno = seqno; + fence->flags = flags; + fence->error = 0; + + trace_dma_fence_init(fence); +} + /** * dma_fence_init - Initialize a custom fence. * @fence: the fence to initialize @@ -1008,18 +1028,30 @@ void dma_fence_init(struct dma_fence *fence, const struct dma_fence_ops *ops, spinlock_t *lock, u64 context, u64 seqno) { - BUG_ON(!lock); - BUG_ON(!ops || !ops->get_driver_name || !ops->get_timeline_name); - - kref_init(&fence->refcount); - fence->ops = ops; - INIT_LIST_HEAD(&fence->cb_list); - fence->lock = lock; - fence->context = context; - fence->seqno = seqno; - fence->flags = 0UL; - fence->error = 0; - - trace_dma_fence_init(fence); + __dma_fence_init(fence, ops, lock, context, seqno, 0UL); } EXPORT_SYMBOL(dma_fence_init); + +/** + * dma_fence_init64 - Initialize a custom fence with 64-bit seqno support. + * @fence: the fence to initialize + * @ops: the dma_fence_ops for operations on this fence + * @lock: the irqsafe spinlock to use for locking this fence + * @context: the execution context this fence is run on + * @seqno: a linear increasing sequence number for this context + * + * Initializes an allocated fence, the caller doesn't have to keep its + * refcount after committing with this fence, but it will need to hold a + * refcount again if &dma_fence_ops.enable_signaling gets called. + * + * Context and seqno are used for easy comparison between fences, allowing + * to check which fence is later by simply using dma_fence_later(). + */ +void +dma_fence_init64(struct dma_fence *fence, const struct dma_fence_ops *ops, + spinlock_t *lock, u64 context, u64 seqno) +{ + __dma_fence_init(fence, ops, lock, context, seqno, + BIT(DMA_FENCE_FLAG_SEQNO64_BIT)); +} +EXPORT_SYMBOL(dma_fence_init64); diff --git a/drivers/dma-buf/sw_sync.c b/drivers/dma-buf/sw_sync.c index 4f27ee93a00c..3c20f1d31cf5 100644 --- a/drivers/dma-buf/sw_sync.c +++ b/drivers/dma-buf/sw_sync.c @@ -170,7 +170,7 @@ static bool timeline_fence_signaled(struct dma_fence *fence) { struct sync_timeline *parent = dma_fence_parent(fence); - return !__dma_fence_is_later(fence->seqno, parent->value, fence->ops); + return !__dma_fence_is_later(fence, fence->seqno, parent->value); } static void timeline_fence_set_deadline(struct dma_fence *fence, ktime_t deadline) diff --git a/drivers/dma-buf/sync_file.c b/drivers/dma-buf/sync_file.c index d9b1c1b2a72b..212df4b849fe 100644 --- a/drivers/dma-buf/sync_file.c +++ b/drivers/dma-buf/sync_file.c @@ -137,8 +137,8 @@ char *sync_file_get_name(struct sync_file *sync_file, char *buf, int len) struct dma_fence *fence = sync_file->fence; snprintf(buf, len, "%s-%s%llu-%lld", - fence->ops->get_driver_name(fence), - fence->ops->get_timeline_name(fence), + dma_fence_driver_name(fence), + dma_fence_timeline_name(fence), fence->context, fence->seqno); } @@ -262,9 +262,9 @@ err_put_fd: static int sync_fill_fence_info(struct dma_fence *fence, struct sync_fence_info *info) { - strscpy(info->obj_name, fence->ops->get_timeline_name(fence), + strscpy(info->obj_name, dma_fence_timeline_name(fence), sizeof(info->obj_name)); - strscpy(info->driver_name, fence->ops->get_driver_name(fence), + strscpy(info->driver_name, dma_fence_driver_name(fence), sizeof(info->driver_name)); info->status = dma_fence_get_status(fence); diff --git a/drivers/dma-buf/udmabuf.c b/drivers/dma-buf/udmabuf.c index c9d0c68d2fcb..40399c26e6be 100644 --- a/drivers/dma-buf/udmabuf.c +++ b/drivers/dma-buf/udmabuf.c @@ -109,29 +109,22 @@ static int mmap_udmabuf(struct dma_buf *buf, struct vm_area_struct *vma) static int vmap_udmabuf(struct dma_buf *buf, struct iosys_map *map) { struct udmabuf *ubuf = buf->priv; - unsigned long *pfns; + struct page **pages; void *vaddr; pgoff_t pg; dma_resv_assert_held(buf->resv); - /** - * HVO may free tail pages, so just use pfn to map each folio - * into vmalloc area. - */ - pfns = kvmalloc_array(ubuf->pagecount, sizeof(*pfns), GFP_KERNEL); - if (!pfns) + pages = kvmalloc_array(ubuf->pagecount, sizeof(*pages), GFP_KERNEL); + if (!pages) return -ENOMEM; - for (pg = 0; pg < ubuf->pagecount; pg++) { - unsigned long pfn = folio_pfn(ubuf->folios[pg]); - - pfn += ubuf->offsets[pg] >> PAGE_SHIFT; - pfns[pg] = pfn; - } + for (pg = 0; pg < ubuf->pagecount; pg++) + pages[pg] = folio_page(ubuf->folios[pg], + ubuf->offsets[pg] >> PAGE_SHIFT); - vaddr = vmap_pfn(pfns, ubuf->pagecount, PAGE_KERNEL); - kvfree(pfns); + vaddr = vm_map_ram(pages, ubuf->pagecount, -1); + kvfree(pages); if (!vaddr) return -EINVAL; diff --git a/drivers/gpu/drm/adp/adp-mipi.c b/drivers/gpu/drm/adp/adp-mipi.c index 2b60128e2c69..cba7d32150a9 100644 --- a/drivers/gpu/drm/adp/adp-mipi.c +++ b/drivers/gpu/drm/adp/adp-mipi.c @@ -229,9 +229,10 @@ static int adp_mipi_probe(struct platform_device *pdev) { struct adp_mipi_drv_private *adp; - adp = devm_kzalloc(&pdev->dev, sizeof(*adp), GFP_KERNEL); - if (!adp) - return -ENOMEM; + adp = devm_drm_bridge_alloc(&pdev->dev, struct adp_mipi_drv_private, + bridge, &adp_dsi_bridge_funcs); + if (IS_ERR(adp)) + return PTR_ERR(adp); adp->mipi = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(adp->mipi)) { @@ -241,7 +242,6 @@ static int adp_mipi_probe(struct platform_device *pdev) adp->dsi.dev = &pdev->dev; adp->dsi.ops = &adp_dsi_host_ops; - adp->bridge.funcs = &adp_dsi_bridge_funcs; adp->bridge.of_node = pdev->dev.of_node; adp->bridge.type = DRM_MODE_CONNECTOR_DSI; dev_set_drvdata(&pdev->dev, adp); diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd.c index d8ac4b1051a8..55161e5cdc30 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd.c @@ -642,7 +642,7 @@ int amdgpu_amdkfd_submit_ib(struct amdgpu_device *adev, goto err; } - ret = amdgpu_job_alloc(adev, NULL, NULL, NULL, 1, &job); + ret = amdgpu_job_alloc(adev, NULL, NULL, NULL, 1, &job, 0); if (ret) goto err; diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_cs.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_cs.c index 9ea0d9b71f48..a2adaacf6adb 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_cs.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_cs.c @@ -293,7 +293,8 @@ static int amdgpu_cs_pass1(struct amdgpu_cs_parser *p, for (i = 0; i < p->gang_size; ++i) { ret = amdgpu_job_alloc(p->adev, vm, p->entities[i], vm, - num_ibs[i], &p->jobs[i]); + num_ibs[i], &p->jobs[i], + p->filp->client_id); if (ret) goto free_all_kdata; switch (p->adev->enforce_isolation[fpriv->xcp_id]) { diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_eviction_fence.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_eviction_fence.c index 8b919ad3af29..23d7d0b0d625 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_eviction_fence.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_eviction_fence.c @@ -143,7 +143,6 @@ static bool amdgpu_eviction_fence_enable_signaling(struct dma_fence *f) } static const struct dma_fence_ops amdgpu_eviction_fence_ops = { - .use_64bit_seqno = true, .get_driver_name = amdgpu_eviction_fence_get_driver_name, .get_timeline_name = amdgpu_eviction_fence_get_timeline_name, .enable_signaling = amdgpu_eviction_fence_enable_signaling, @@ -169,9 +168,9 @@ amdgpu_eviction_fence_create(struct amdgpu_eviction_fence_mgr *evf_mgr) ev_fence->evf_mgr = evf_mgr; get_task_comm(ev_fence->timeline_name, current); spin_lock_init(&ev_fence->lock); - dma_fence_init(&ev_fence->base, &amdgpu_eviction_fence_ops, - &ev_fence->lock, evf_mgr->ev_fence_ctx, - atomic_inc_return(&evf_mgr->ev_fence_seq)); + dma_fence_init64(&ev_fence->base, &amdgpu_eviction_fence_ops, + &ev_fence->lock, evf_mgr->ev_fence_ctx, + atomic_inc_return(&evf_mgr->ev_fence_seq)); return ev_fence; } diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_job.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_job.c index acb21fc8b3ce..75262ce8db27 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_job.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_job.c @@ -204,7 +204,8 @@ exit: int amdgpu_job_alloc(struct amdgpu_device *adev, struct amdgpu_vm *vm, struct drm_sched_entity *entity, void *owner, - unsigned int num_ibs, struct amdgpu_job **job) + unsigned int num_ibs, struct amdgpu_job **job, + u64 drm_client_id) { if (num_ibs == 0) return -EINVAL; @@ -222,7 +223,8 @@ int amdgpu_job_alloc(struct amdgpu_device *adev, struct amdgpu_vm *vm, if (!entity) return 0; - return drm_sched_job_init(&(*job)->base, entity, 1, owner); + return drm_sched_job_init(&(*job)->base, entity, 1, owner, + drm_client_id); } int amdgpu_job_alloc_with_ib(struct amdgpu_device *adev, @@ -232,7 +234,7 @@ int amdgpu_job_alloc_with_ib(struct amdgpu_device *adev, { int r; - r = amdgpu_job_alloc(adev, NULL, entity, owner, 1, job); + r = amdgpu_job_alloc(adev, NULL, entity, owner, 1, job, 0); if (r) return r; diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_job.h b/drivers/gpu/drm/amd/amdgpu/amdgpu_job.h index f2c049129661..529045d60412 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_job.h +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_job.h @@ -91,7 +91,8 @@ static inline struct amdgpu_ring *amdgpu_job_ring(struct amdgpu_job *job) int amdgpu_job_alloc(struct amdgpu_device *adev, struct amdgpu_vm *vm, struct drm_sched_entity *entity, void *owner, - unsigned int num_ibs, struct amdgpu_job **job); + unsigned int num_ibs, struct amdgpu_job **job, + u64 drm_client_id); int amdgpu_job_alloc_with_ib(struct amdgpu_device *adev, struct drm_sched_entity *entity, void *owner, size_t size, enum amdgpu_ib_pool_type pool_type, diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_trace.h b/drivers/gpu/drm/amd/amdgpu/amdgpu_trace.h index 11dd2e0f7979..d13e64a69e25 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_trace.h +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_trace.h @@ -167,25 +167,23 @@ TRACE_EVENT(amdgpu_cs_ioctl, TP_PROTO(struct amdgpu_job *job), TP_ARGS(job), TP_STRUCT__entry( - __field(uint64_t, sched_job_id) __string(timeline, AMDGPU_JOB_GET_TIMELINE_NAME(job)) - __field(unsigned int, context) - __field(unsigned int, seqno) + __field(u64, context) + __field(u64, seqno) __field(struct dma_fence *, fence) __string(ring, to_amdgpu_ring(job->base.sched)->name) __field(u32, num_ibs) ), TP_fast_assign( - __entry->sched_job_id = job->base.id; __assign_str(timeline); __entry->context = job->base.s_fence->finished.context; __entry->seqno = job->base.s_fence->finished.seqno; __assign_str(ring); __entry->num_ibs = job->num_ibs; ), - TP_printk("sched_job=%llu, timeline=%s, context=%u, seqno=%u, ring_name=%s, num_ibs=%u", - __entry->sched_job_id, __get_str(timeline), __entry->context, + TP_printk("timeline=%s, fence=%llu:%llu, ring_name=%s, num_ibs=%u", + __get_str(timeline), __entry->context, __entry->seqno, __get_str(ring), __entry->num_ibs) ); @@ -193,24 +191,22 @@ TRACE_EVENT(amdgpu_sched_run_job, TP_PROTO(struct amdgpu_job *job), TP_ARGS(job), TP_STRUCT__entry( - __field(uint64_t, sched_job_id) __string(timeline, AMDGPU_JOB_GET_TIMELINE_NAME(job)) - __field(unsigned int, context) - __field(unsigned int, seqno) + __field(u64, context) + __field(u64, seqno) __string(ring, to_amdgpu_ring(job->base.sched)->name) __field(u32, num_ibs) ), TP_fast_assign( - __entry->sched_job_id = job->base.id; __assign_str(timeline); __entry->context = job->base.s_fence->finished.context; __entry->seqno = job->base.s_fence->finished.seqno; __assign_str(ring); __entry->num_ibs = job->num_ibs; ), - TP_printk("sched_job=%llu, timeline=%s, context=%u, seqno=%u, ring_name=%s, num_ibs=%u", - __entry->sched_job_id, __get_str(timeline), __entry->context, + TP_printk("timeline=%s, fence=%llu:%llu, ring_name=%s, num_ibs=%u", + __get_str(timeline), __entry->context, __entry->seqno, __get_str(ring), __entry->num_ibs) ); @@ -551,23 +547,19 @@ TRACE_EVENT(amdgpu_ib_pipe_sync, TP_ARGS(sched_job, fence), TP_STRUCT__entry( __string(ring, sched_job->base.sched->name) - __field(uint64_t, id) __field(struct dma_fence *, fence) - __field(uint64_t, ctx) - __field(unsigned, seqno) + __field(u64, ctx) + __field(u64, seqno) ), TP_fast_assign( __assign_str(ring); - __entry->id = sched_job->base.id; __entry->fence = fence; __entry->ctx = fence->context; __entry->seqno = fence->seqno; ), - TP_printk("job ring=%s, id=%llu, need pipe sync to fence=%p, context=%llu, seq=%u", - __get_str(ring), __entry->id, - __entry->fence, __entry->ctx, - __entry->seqno) + TP_printk("job ring=%s need pipe sync to fence=%llu:%llu", + __get_str(ring), __entry->ctx, __entry->seqno) ); TRACE_EVENT(amdgpu_reset_reg_dumps, diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_userq_fence.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_userq_fence.c index a86616c6deef..c2a983ff23c9 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_userq_fence.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_userq_fence.c @@ -239,8 +239,8 @@ static int amdgpu_userq_fence_create(struct amdgpu_usermode_queue *userq, fence = &userq_fence->base; userq_fence->fence_drv = fence_drv; - dma_fence_init(fence, &amdgpu_userq_fence_ops, &userq_fence->lock, - fence_drv->context, seq); + dma_fence_init64(fence, &amdgpu_userq_fence_ops, &userq_fence->lock, + fence_drv->context, seq); amdgpu_userq_fence_driver_get(fence_drv); dma_fence_get(fence); @@ -334,7 +334,6 @@ static void amdgpu_userq_fence_release(struct dma_fence *f) } static const struct dma_fence_ops amdgpu_userq_fence_ops = { - .use_64bit_seqno = true, .get_driver_name = amdgpu_userq_fence_get_driver_name, .get_timeline_name = amdgpu_userq_fence_get_timeline_name, .signaled = amdgpu_userq_fence_signaled, diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_vm_tlb_fence.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_vm_tlb_fence.c index 51cddfa3f1e8..5d26797356a3 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_vm_tlb_fence.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_vm_tlb_fence.c @@ -71,7 +71,6 @@ static void amdgpu_tlb_fence_work(struct work_struct *work) } static const struct dma_fence_ops amdgpu_tlb_fence_ops = { - .use_64bit_seqno = true, .get_driver_name = amdgpu_tlb_fence_get_driver_name, .get_timeline_name = amdgpu_tlb_fence_get_timeline_name }; @@ -101,8 +100,8 @@ void amdgpu_vm_tlb_fence_create(struct amdgpu_device *adev, struct amdgpu_vm *vm INIT_WORK(&f->work, amdgpu_tlb_fence_work); spin_lock_init(&f->lock); - dma_fence_init(&f->base, &amdgpu_tlb_fence_ops, &f->lock, - vm->tlb_fence_context, atomic64_read(&vm->tlb_seq)); + dma_fence_init64(&f->base, &amdgpu_tlb_fence_ops, &f->lock, + vm->tlb_fence_context, atomic64_read(&vm->tlb_seq)); /* TODO: We probably need a separate wq here */ dma_fence_get(&f->base); diff --git a/drivers/gpu/drm/arm/hdlcd_crtc.c b/drivers/gpu/drm/arm/hdlcd_crtc.c index 3cfefadc7c9d..806da0aaedf7 100644 --- a/drivers/gpu/drm/arm/hdlcd_crtc.c +++ b/drivers/gpu/drm/arm/hdlcd_crtc.c @@ -11,8 +11,8 @@ #include <linux/clk.h> #include <linux/of_graph.h> -#include <linux/platform_data/simplefb.h> +#include <video/pixel_format.h> #include <video/videomode.h> #include <drm/drm_atomic.h> @@ -73,7 +73,17 @@ static const struct drm_crtc_funcs hdlcd_crtc_funcs = { .disable_vblank = hdlcd_crtc_disable_vblank, }; -static struct simplefb_format supported_formats[] = SIMPLEFB_FORMATS; +static const struct { + u32 fourcc; + struct pixel_format pixel; +} supported_formats[] = { + { DRM_FORMAT_RGB565, PIXEL_FORMAT_RGB565 }, + { DRM_FORMAT_XRGB1555, PIXEL_FORMAT_XRGB1555 }, + { DRM_FORMAT_RGB888, PIXEL_FORMAT_RGB888 }, + { DRM_FORMAT_XRGB8888, PIXEL_FORMAT_XRGB8888 }, + { DRM_FORMAT_XBGR8888, PIXEL_FORMAT_XBGR8888 }, + { DRM_FORMAT_XRGB2101010, PIXEL_FORMAT_XRGB2101010}, +}; /* * Setup the HDLCD registers for decoding the pixels out of the framebuffer @@ -83,15 +93,12 @@ static int hdlcd_set_pxl_fmt(struct drm_crtc *crtc) unsigned int btpp; struct hdlcd_drm_private *hdlcd = crtc_to_hdlcd_priv(crtc); const struct drm_framebuffer *fb = crtc->primary->state->fb; - uint32_t pixel_format; - struct simplefb_format *format = NULL; + const struct pixel_format *format = NULL; int i; - pixel_format = fb->format->format; - for (i = 0; i < ARRAY_SIZE(supported_formats); i++) { - if (supported_formats[i].fourcc == pixel_format) - format = &supported_formats[i]; + if (supported_formats[i].fourcc == fb->format->format) + format = &supported_formats[i].pixel; } if (WARN_ON(!format)) diff --git a/drivers/gpu/drm/ast/ast_drv.c b/drivers/gpu/drm/ast/ast_drv.c index 6fbf62a99c48..054acda41909 100644 --- a/drivers/gpu/drm/ast/ast_drv.c +++ b/drivers/gpu/drm/ast/ast_drv.c @@ -64,7 +64,7 @@ static const struct drm_driver ast_driver = { .minor = DRIVER_MINOR, .patchlevel = DRIVER_PATCHLEVEL, - DRM_GEM_SHMEM_DRIVER_OPS, + DRM_GEM_SHMEM_DRIVER_OPS_NO_MAP_SGT, DRM_FBDEV_SHMEM_DRIVER_OPS, }; diff --git a/drivers/gpu/drm/ast/ast_mode.c b/drivers/gpu/drm/ast/ast_mode.c index 1de832964e92..7908087bcb5a 100644 --- a/drivers/gpu/drm/ast/ast_mode.c +++ b/drivers/gpu/drm/ast/ast_mode.c @@ -34,6 +34,7 @@ #include <drm/drm_atomic.h> #include <drm/drm_atomic_helper.h> +#include <drm/drm_color_mgmt.h> #include <drm/drm_crtc.h> #include <drm/drm_damage_helper.h> #include <drm/drm_format_helper.h> @@ -71,31 +72,44 @@ static unsigned long ast_fb_vram_size(struct ast_device *ast) return cursor_offset - offset; } -static inline void ast_load_palette_index(struct ast_device *ast, - u8 index, u8 red, u8 green, - u8 blue) +static void ast_set_gamma_lut(struct drm_crtc *crtc, unsigned int index, + u16 red, u16 green, u16 blue) { - ast_io_write8(ast, AST_IO_VGADWR, index); + struct drm_device *dev = crtc->dev; + struct ast_device *ast = to_ast_device(dev); + u8 i8 = index & 0xff; + u8 r8 = red >> 8; + u8 g8 = green >> 8; + u8 b8 = blue >> 8; + + if (drm_WARN_ON_ONCE(dev, index != i8)) + return; /* driver bug */ + + ast_io_write8(ast, AST_IO_VGADWR, i8); ast_io_read8(ast, AST_IO_VGASRI); - ast_io_write8(ast, AST_IO_VGAPDR, red); + ast_io_write8(ast, AST_IO_VGAPDR, r8); ast_io_read8(ast, AST_IO_VGASRI); - ast_io_write8(ast, AST_IO_VGAPDR, green); + ast_io_write8(ast, AST_IO_VGAPDR, g8); ast_io_read8(ast, AST_IO_VGASRI); - ast_io_write8(ast, AST_IO_VGAPDR, blue); + ast_io_write8(ast, AST_IO_VGAPDR, b8); ast_io_read8(ast, AST_IO_VGASRI); } -static void ast_crtc_set_gamma_linear(struct ast_device *ast, - const struct drm_format_info *format) +static void ast_crtc_fill_gamma(struct ast_device *ast, + const struct drm_format_info *format) { - int i; + struct drm_crtc *crtc = &ast->crtc; switch (format->format) { - case DRM_FORMAT_C8: /* In this case, gamma table is used as color palette */ + case DRM_FORMAT_C8: + /* gamma table is used as color palette */ + drm_crtc_fill_palette_8(crtc, ast_set_gamma_lut); + break; case DRM_FORMAT_RGB565: + /* also uses 8-bit gamma ramp on low-color modes */ + fallthrough; case DRM_FORMAT_XRGB8888: - for (i = 0; i < AST_LUT_SIZE; i++) - ast_load_palette_index(ast, i, i, i, i); + drm_crtc_fill_gamma_888(crtc, ast_set_gamma_lut); break; default: drm_warn_once(&ast->base, "Unsupported format %p4cc for gamma correction\n", @@ -104,21 +118,22 @@ static void ast_crtc_set_gamma_linear(struct ast_device *ast, } } -static void ast_crtc_set_gamma(struct ast_device *ast, - const struct drm_format_info *format, - struct drm_color_lut *lut) +static void ast_crtc_load_gamma(struct ast_device *ast, + const struct drm_format_info *format, + struct drm_color_lut *lut) { - int i; + struct drm_crtc *crtc = &ast->crtc; switch (format->format) { - case DRM_FORMAT_C8: /* In this case, gamma table is used as color palette */ + case DRM_FORMAT_C8: + /* gamma table is used as color palette */ + drm_crtc_load_palette_8(crtc, lut, ast_set_gamma_lut); + break; case DRM_FORMAT_RGB565: + /* also uses 8-bit gamma ramp on low-color modes */ + fallthrough; case DRM_FORMAT_XRGB8888: - for (i = 0; i < AST_LUT_SIZE; i++) - ast_load_palette_index(ast, i, - lut[i].red >> 8, - lut[i].green >> 8, - lut[i].blue >> 8); + drm_crtc_load_gamma_888(crtc, lut, ast_set_gamma_lut); break; default: drm_warn_once(&ast->base, "Unsupported format %p4cc for gamma correction\n", @@ -811,11 +826,11 @@ ast_crtc_helper_atomic_flush(struct drm_crtc *crtc, */ if (crtc_state->enable && crtc_state->color_mgmt_changed) { if (crtc_state->gamma_lut) - ast_crtc_set_gamma(ast, - ast_crtc_state->format, - crtc_state->gamma_lut->data); + ast_crtc_load_gamma(ast, + ast_crtc_state->format, + crtc_state->gamma_lut->data); else - ast_crtc_set_gamma_linear(ast, ast_crtc_state->format); + ast_crtc_fill_gamma(ast, ast_crtc_state->format); } } diff --git a/drivers/gpu/drm/bridge/adv7511/Kconfig b/drivers/gpu/drm/bridge/adv7511/Kconfig index f46a5e26b5dd..59a5256ce8a6 100644 --- a/drivers/gpu/drm/bridge/adv7511/Kconfig +++ b/drivers/gpu/drm/bridge/adv7511/Kconfig @@ -5,6 +5,9 @@ config DRM_I2C_ADV7511 select DRM_KMS_HELPER select REGMAP_I2C select DRM_MIPI_DSI + select DRM_DISPLAY_HELPER + select DRM_BRIDGE_CONNECTOR + select DRM_DISPLAY_HDMI_STATE_HELPER help Support for the Analog Devices ADV7511(W)/13/33/35 HDMI encoders. @@ -19,7 +22,7 @@ config DRM_I2C_ADV7511_AUDIO config DRM_I2C_ADV7511_CEC bool "ADV7511/33/35 HDMI CEC driver" depends on DRM_I2C_ADV7511 - select CEC_CORE + select DRM_DISPLAY_HDMI_CEC_HELPER default y help When selected the HDMI transmitter will support the CEC feature. diff --git a/drivers/gpu/drm/bridge/adv7511/adv7511.h b/drivers/gpu/drm/bridge/adv7511/adv7511.h index ec0b7f3d889c..71bb64e5f481 100644 --- a/drivers/gpu/drm/bridge/adv7511/adv7511.h +++ b/drivers/gpu/drm/bridge/adv7511/adv7511.h @@ -313,16 +313,11 @@ enum adv7511_csc_scaling { * @csc_enable: Whether to enable color space conversion * @csc_scaling_factor: Color space conversion scaling factor * @csc_coefficents: Color space conversion coefficents - * @hdmi_mode: Whether to use HDMI or DVI output mode - * @avi_infoframe: HDMI infoframe */ struct adv7511_video_config { bool csc_enable; enum adv7511_csc_scaling csc_scaling_factor; const uint16_t *csc_coefficents; - - bool hdmi_mode; - struct hdmi_avi_infoframe avi_infoframe; }; enum adv7511_type { @@ -337,6 +332,7 @@ struct adv7511_chip_info { enum adv7511_type type; unsigned int max_mode_clock_khz; unsigned int max_lane_freq_khz; + const char *name; const char * const *supply_names; unsigned int num_supplies; unsigned int reg_cec_offset; @@ -371,7 +367,7 @@ struct adv7511 { struct work_struct hpd_work; struct drm_bridge bridge; - struct drm_connector connector; + struct drm_connector *cec_connector; bool embedded_sync; enum adv7511_sync_polarity vsync_polarity; @@ -389,9 +385,7 @@ struct adv7511 { bool use_timing_gen; const struct adv7511_chip_info *info; - struct platform_device *audio_pdev; - struct cec_adapter *cec_adap; u8 cec_addr[ADV7511_MAX_ADDRS]; u8 cec_valid_addrs; bool cec_enabled_adap; @@ -399,20 +393,29 @@ struct adv7511 { u32 cec_clk_freq; }; +static inline struct adv7511 *bridge_to_adv7511(struct drm_bridge *bridge) +{ + return container_of(bridge, struct adv7511, bridge); +} + #ifdef CONFIG_DRM_I2C_ADV7511_CEC -int adv7511_cec_init(struct device *dev, struct adv7511 *adv7511); +int adv7511_cec_init(struct drm_connector *connector, + struct drm_bridge *bridge); +int adv7511_cec_enable(struct drm_bridge *bridge, bool enable); +int adv7511_cec_log_addr(struct drm_bridge *bridge, u8 addr); +int adv7511_cec_transmit(struct drm_bridge *bridge, u8 attempts, + u32 signal_free_time, struct cec_msg *msg); int adv7511_cec_irq_process(struct adv7511 *adv7511, unsigned int irq1); #else -static inline int adv7511_cec_init(struct device *dev, struct adv7511 *adv7511) -{ - regmap_write(adv7511->regmap, ADV7511_REG_CEC_CTRL, - ADV7511_CEC_CTRL_POWER_DOWN); - return 0; -} +#define adv7511_cec_init NULL +#define adv7511_cec_enable NULL +#define adv7511_cec_log_addr NULL +#define adv7511_cec_transmit NULL #endif void adv7533_dsi_power_on(struct adv7511 *adv); void adv7533_dsi_power_off(struct adv7511 *adv); +void adv7533_dsi_config_timing_gen(struct adv7511 *adv); enum drm_mode_status adv7533_mode_valid(struct adv7511 *adv, const struct drm_display_mode *mode); int adv7533_patch_registers(struct adv7511 *adv); @@ -421,16 +424,18 @@ int adv7533_attach_dsi(struct adv7511 *adv); int adv7533_parse_dt(struct device_node *np, struct adv7511 *adv); #ifdef CONFIG_DRM_I2C_ADV7511_AUDIO -int adv7511_audio_init(struct device *dev, struct adv7511 *adv7511); -void adv7511_audio_exit(struct adv7511 *adv7511); +int adv7511_hdmi_audio_startup(struct drm_connector *connector, + struct drm_bridge *bridge); +void adv7511_hdmi_audio_shutdown(struct drm_connector *connector, + struct drm_bridge *bridge); +int adv7511_hdmi_audio_prepare(struct drm_connector *connector, + struct drm_bridge *bridge, + struct hdmi_codec_daifmt *fmt, + struct hdmi_codec_params *hparms); #else /*CONFIG_DRM_I2C_ADV7511_AUDIO */ -static inline int adv7511_audio_init(struct device *dev, struct adv7511 *adv7511) -{ - return 0; -} -static inline void adv7511_audio_exit(struct adv7511 *adv7511) -{ -} +#define adv7511_hdmi_audio_startup NULL +#define adv7511_hdmi_audio_shutdown NULL +#define adv7511_hdmi_audio_prepare NULL #endif /* CONFIG_DRM_I2C_ADV7511_AUDIO */ #endif /* __DRM_I2C_ADV7511_H__ */ diff --git a/drivers/gpu/drm/bridge/adv7511/adv7511_audio.c b/drivers/gpu/drm/bridge/adv7511/adv7511_audio.c index 1ff8c815ec79..915c3b967216 100644 --- a/drivers/gpu/drm/bridge/adv7511/adv7511_audio.c +++ b/drivers/gpu/drm/bridge/adv7511/adv7511_audio.c @@ -55,11 +55,12 @@ static int adv7511_update_cts_n(struct adv7511 *adv7511) return 0; } -static int adv7511_hdmi_hw_params(struct device *dev, void *data, - struct hdmi_codec_daifmt *fmt, - struct hdmi_codec_params *hparms) +int adv7511_hdmi_audio_prepare(struct drm_connector *connector, + struct drm_bridge *bridge, + struct hdmi_codec_daifmt *fmt, + struct hdmi_codec_params *hparms) { - struct adv7511 *adv7511 = dev_get_drvdata(dev); + struct adv7511 *adv7511 = bridge_to_adv7511(bridge); unsigned int audio_source, i2s_format = 0; unsigned int invert_clock; unsigned int rate; @@ -167,9 +168,10 @@ static int adv7511_hdmi_hw_params(struct device *dev, void *data, return 0; } -static int audio_startup(struct device *dev, void *data) +int adv7511_hdmi_audio_startup(struct drm_connector *connector, + struct drm_bridge *bridge) { - struct adv7511 *adv7511 = dev_get_drvdata(dev); + struct adv7511 *adv7511 = bridge_to_adv7511(bridge); regmap_update_bits(adv7511->regmap, ADV7511_REG_AUDIO_CONFIG, BIT(7), 0); @@ -204,69 +206,12 @@ static int audio_startup(struct device *dev, void *data) return 0; } -static void audio_shutdown(struct device *dev, void *data) +void adv7511_hdmi_audio_shutdown(struct drm_connector *connector, + struct drm_bridge *bridge) { - struct adv7511 *adv7511 = dev_get_drvdata(dev); + struct adv7511 *adv7511 = bridge_to_adv7511(bridge); if (adv7511->audio_source == ADV7511_AUDIO_SOURCE_SPDIF) regmap_update_bits(adv7511->regmap, ADV7511_REG_AUDIO_CONFIG, BIT(7), 0); } - -static int adv7511_hdmi_i2s_get_dai_id(struct snd_soc_component *component, - struct device_node *endpoint, - void *data) -{ - struct of_endpoint of_ep; - int ret; - - ret = of_graph_parse_endpoint(endpoint, &of_ep); - if (ret < 0) - return ret; - - /* - * HDMI sound should be located as reg = <2> - * Then, it is sound port 0 - */ - if (of_ep.port == 2) - return 0; - - return -EINVAL; -} - -static const struct hdmi_codec_ops adv7511_codec_ops = { - .hw_params = adv7511_hdmi_hw_params, - .audio_shutdown = audio_shutdown, - .audio_startup = audio_startup, - .get_dai_id = adv7511_hdmi_i2s_get_dai_id, -}; - -static const struct hdmi_codec_pdata codec_data = { - .ops = &adv7511_codec_ops, - .i2s_formats = (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE | - SNDRV_PCM_FMTBIT_S24_3LE | SNDRV_PCM_FMTBIT_S24_LE | - SNDRV_PCM_FMTBIT_IEC958_SUBFRAME_LE), - .max_i2s_channels = 2, - .i2s = 1, - .no_i2s_capture = 1, - .spdif = 1, - .no_spdif_capture = 1, -}; - -int adv7511_audio_init(struct device *dev, struct adv7511 *adv7511) -{ - adv7511->audio_pdev = platform_device_register_data(dev, - HDMI_CODEC_DRV_NAME, - PLATFORM_DEVID_AUTO, - &codec_data, - sizeof(codec_data)); - return PTR_ERR_OR_ZERO(adv7511->audio_pdev); -} - -void adv7511_audio_exit(struct adv7511 *adv7511) -{ - if (adv7511->audio_pdev) { - platform_device_unregister(adv7511->audio_pdev); - adv7511->audio_pdev = NULL; - } -} diff --git a/drivers/gpu/drm/bridge/adv7511/adv7511_cec.c b/drivers/gpu/drm/bridge/adv7511/adv7511_cec.c index 2e9c88a2b5ed..822265426f58 100644 --- a/drivers/gpu/drm/bridge/adv7511/adv7511_cec.c +++ b/drivers/gpu/drm/bridge/adv7511/adv7511_cec.c @@ -12,6 +12,8 @@ #include <media/cec.h> +#include <drm/display/drm_hdmi_cec_helper.h> + #include "adv7511.h" static const u8 ADV7511_REG_CEC_RX_FRAME_HDR[] = { @@ -44,8 +46,8 @@ static void adv_cec_tx_raw_status(struct adv7511 *adv7511, u8 tx_raw_status) return; if (tx_raw_status & ADV7511_INT1_CEC_TX_ARBIT_LOST) { - cec_transmit_attempt_done(adv7511->cec_adap, - CEC_TX_STATUS_ARB_LOST); + drm_connector_hdmi_cec_transmit_attempt_done(adv7511->cec_connector, + CEC_TX_STATUS_ARB_LOST); return; } if (tx_raw_status & ADV7511_INT1_CEC_TX_RETRY_TIMEOUT) { @@ -72,12 +74,14 @@ static void adv_cec_tx_raw_status(struct adv7511 *adv7511, u8 tx_raw_status) if (low_drive_cnt) status |= CEC_TX_STATUS_LOW_DRIVE; } - cec_transmit_done(adv7511->cec_adap, status, - 0, nack_cnt, low_drive_cnt, err_cnt); + drm_connector_hdmi_cec_transmit_done(adv7511->cec_connector, status, + 0, nack_cnt, low_drive_cnt, + err_cnt); return; } if (tx_raw_status & ADV7511_INT1_CEC_TX_READY) { - cec_transmit_attempt_done(adv7511->cec_adap, CEC_TX_STATUS_OK); + drm_connector_hdmi_cec_transmit_attempt_done(adv7511->cec_connector, + CEC_TX_STATUS_OK); return; } } @@ -116,7 +120,7 @@ static void adv7511_cec_rx(struct adv7511 *adv7511, int rx_buf) regmap_update_bits(adv7511->regmap_cec, ADV7511_REG_CEC_RX_BUFFERS + offset, BIT(rx_buf), 0); - cec_received_msg(adv7511->cec_adap, &msg); + drm_connector_hdmi_cec_received_msg(adv7511->cec_connector, &msg); } int adv7511_cec_irq_process(struct adv7511 *adv7511, unsigned int irq1) @@ -179,9 +183,9 @@ int adv7511_cec_irq_process(struct adv7511 *adv7511, unsigned int irq1) return IRQ_HANDLED; } -static int adv7511_cec_adap_enable(struct cec_adapter *adap, bool enable) +int adv7511_cec_enable(struct drm_bridge *bridge, bool enable) { - struct adv7511 *adv7511 = cec_get_drvdata(adap); + struct adv7511 *adv7511 = bridge_to_adv7511(bridge); unsigned int offset = adv7511->info->reg_cec_offset; if (adv7511->i2c_cec == NULL) @@ -225,9 +229,9 @@ static int adv7511_cec_adap_enable(struct cec_adapter *adap, bool enable) return 0; } -static int adv7511_cec_adap_log_addr(struct cec_adapter *adap, u8 addr) +int adv7511_cec_log_addr(struct drm_bridge *bridge, u8 addr) { - struct adv7511 *adv7511 = cec_get_drvdata(adap); + struct adv7511 *adv7511 = bridge_to_adv7511(bridge); unsigned int offset = adv7511->info->reg_cec_offset; unsigned int i, free_idx = ADV7511_MAX_ADDRS; @@ -293,10 +297,10 @@ static int adv7511_cec_adap_log_addr(struct cec_adapter *adap, u8 addr) return 0; } -static int adv7511_cec_adap_transmit(struct cec_adapter *adap, u8 attempts, - u32 signal_free_time, struct cec_msg *msg) +int adv7511_cec_transmit(struct drm_bridge *bridge, u8 attempts, + u32 signal_free_time, struct cec_msg *msg) { - struct adv7511 *adv7511 = cec_get_drvdata(adap); + struct adv7511 *adv7511 = bridge_to_adv7511(bridge); unsigned int offset = adv7511->info->reg_cec_offset; u8 len = msg->len; unsigned int i; @@ -328,12 +332,6 @@ static int adv7511_cec_adap_transmit(struct cec_adapter *adap, u8 attempts, return 0; } -static const struct cec_adap_ops adv7511_cec_adap_ops = { - .adap_enable = adv7511_cec_adap_enable, - .adap_log_addr = adv7511_cec_adap_log_addr, - .adap_transmit = adv7511_cec_adap_transmit, -}; - static int adv7511_cec_parse_dt(struct device *dev, struct adv7511 *adv7511) { adv7511->cec_clk = devm_clk_get(dev, "cec"); @@ -348,20 +346,18 @@ static int adv7511_cec_parse_dt(struct device *dev, struct adv7511 *adv7511) return 0; } -int adv7511_cec_init(struct device *dev, struct adv7511 *adv7511) +int adv7511_cec_init(struct drm_connector *connector, + struct drm_bridge *bridge) { + struct adv7511 *adv7511 = bridge_to_adv7511(bridge); + struct device *dev = &adv7511->i2c_main->dev; unsigned int offset = adv7511->info->reg_cec_offset; int ret = adv7511_cec_parse_dt(dev, adv7511); if (ret) goto err_cec_parse_dt; - adv7511->cec_adap = cec_allocate_adapter(&adv7511_cec_adap_ops, - adv7511, dev_name(dev), CEC_CAP_DEFAULTS, ADV7511_MAX_ADDRS); - if (IS_ERR(adv7511->cec_adap)) { - ret = PTR_ERR(adv7511->cec_adap); - goto err_cec_alloc; - } + adv7511->cec_connector = connector; regmap_write(adv7511->regmap, ADV7511_REG_CEC_CTRL, 0); /* cec soft reset */ @@ -378,17 +374,8 @@ int adv7511_cec_init(struct device *dev, struct adv7511 *adv7511) ADV7511_REG_CEC_CLK_DIV + offset, ((adv7511->cec_clk_freq / 750000) - 1) << 2); - ret = cec_register_adapter(adv7511->cec_adap, dev); - if (ret) - goto err_cec_register; return 0; -err_cec_register: - cec_delete_adapter(adv7511->cec_adap); - adv7511->cec_adap = NULL; -err_cec_alloc: - dev_info(dev, "Initializing CEC failed with error %d, disabling CEC\n", - ret); err_cec_parse_dt: regmap_write(adv7511->regmap, ADV7511_REG_CEC_CTRL, ADV7511_CEC_CTRL_POWER_DOWN); diff --git a/drivers/gpu/drm/bridge/adv7511/adv7511_drv.c b/drivers/gpu/drm/bridge/adv7511/adv7511_drv.c index 1257009e850c..9df18a8f2e37 100644 --- a/drivers/gpu/drm/bridge/adv7511/adv7511_drv.c +++ b/drivers/gpu/drm/bridge/adv7511/adv7511_drv.c @@ -12,14 +12,17 @@ #include <linux/of.h> #include <linux/slab.h> -#include <media/cec.h> +#include <sound/pcm.h> #include <drm/drm_atomic.h> #include <drm/drm_atomic_helper.h> +#include <drm/drm_bridge_connector.h> #include <drm/drm_edid.h> #include <drm/drm_of.h> #include <drm/drm_print.h> #include <drm/drm_probe_helper.h> +#include <drm/display/drm_hdmi_helper.h> +#include <drm/display/drm_hdmi_state_helper.h> #include "adv7511.h" @@ -203,62 +206,37 @@ static const uint16_t adv7511_csc_ycbcr_to_rgb[] = { static void adv7511_set_config_csc(struct adv7511 *adv7511, struct drm_connector *connector, - bool rgb, bool hdmi_mode) + bool rgb) { struct adv7511_video_config config; bool output_format_422, output_format_ycbcr; unsigned int mode; - uint8_t infoframe[17]; - - config.hdmi_mode = hdmi_mode; - - hdmi_avi_infoframe_init(&config.avi_infoframe); - - config.avi_infoframe.scan_mode = HDMI_SCAN_MODE_UNDERSCAN; if (rgb) { config.csc_enable = false; - config.avi_infoframe.colorspace = HDMI_COLORSPACE_RGB; + output_format_422 = false; + output_format_ycbcr = false; } else { config.csc_scaling_factor = ADV7511_CSC_SCALING_4; config.csc_coefficents = adv7511_csc_ycbcr_to_rgb; if ((connector->display_info.color_formats & DRM_COLOR_FORMAT_YCBCR422) && - config.hdmi_mode) { + connector->display_info.is_hdmi) { config.csc_enable = false; - config.avi_infoframe.colorspace = - HDMI_COLORSPACE_YUV422; - } else { - config.csc_enable = true; - config.avi_infoframe.colorspace = HDMI_COLORSPACE_RGB; - } - } - - if (config.hdmi_mode) { - mode = ADV7511_HDMI_CFG_MODE_HDMI; - - switch (config.avi_infoframe.colorspace) { - case HDMI_COLORSPACE_YUV444: - output_format_422 = false; - output_format_ycbcr = true; - break; - case HDMI_COLORSPACE_YUV422: output_format_422 = true; output_format_ycbcr = true; - break; - default: + } else { + config.csc_enable = true; output_format_422 = false; output_format_ycbcr = false; - break; } - } else { - mode = ADV7511_HDMI_CFG_MODE_DVI; - output_format_422 = false; - output_format_ycbcr = false; } - adv7511_packet_disable(adv7511, ADV7511_PACKET_ENABLE_AVI_INFOFRAME); + if (connector->display_info.is_hdmi) + mode = ADV7511_HDMI_CFG_MODE_HDMI; + else + mode = ADV7511_HDMI_CFG_MODE_DVI; adv7511_set_colormap(adv7511, config.csc_enable, config.csc_coefficents, @@ -269,15 +247,6 @@ static void adv7511_set_config_csc(struct adv7511 *adv7511, regmap_update_bits(adv7511->regmap, ADV7511_REG_HDCP_HDMI_CFG, ADV7511_HDMI_CFG_MODE_MASK, mode); - - hdmi_avi_infoframe_pack(&config.avi_infoframe, infoframe, - sizeof(infoframe)); - - /* The AVI infoframe id is not configurable */ - regmap_bulk_write(adv7511->regmap, ADV7511_REG_AVI_INFOFRAME_VERSION, - infoframe + 1, sizeof(infoframe) - 1); - - adv7511_packet_enable(adv7511, ADV7511_PACKET_ENABLE_AVI_INFOFRAME); } static void adv7511_set_link_config(struct adv7511 *adv7511, @@ -446,22 +415,16 @@ static void adv7511_hpd_work(struct work_struct *work) * restore its state. */ if (status == connector_status_connected && - adv7511->connector.status == connector_status_disconnected && + adv7511->status == connector_status_disconnected && adv7511->powered) { regcache_mark_dirty(adv7511->regmap); adv7511_power_on(adv7511); } - if (adv7511->connector.status != status) { - adv7511->connector.status = status; + if (adv7511->status != status) { + adv7511->status = status; - if (adv7511->connector.dev) { - if (status == connector_status_disconnected) - cec_phys_addr_invalidate(adv7511->cec_adap); - drm_kms_helper_hotplug_event(adv7511->connector.dev); - } else { - drm_bridge_hpd_notify(&adv7511->bridge, status); - } + drm_bridge_hpd_notify(&adv7511->bridge, status); } } @@ -636,45 +599,11 @@ static const struct drm_edid *adv7511_edid_read(struct adv7511 *adv7511, if (!adv7511->powered) __adv7511_power_off(adv7511); - if (drm_edid) { - /* - * FIXME: The CEC physical address should be set using - * cec_s_phys_addr(adap, - * connector->display_info.source_physical_address, false) from - * a path that has read the EDID and called - * drm_edid_connector_update(). - */ - const struct edid *edid = drm_edid_raw(drm_edid); - - adv7511_set_config_csc(adv7511, connector, adv7511->rgb, - drm_detect_hdmi_monitor(edid)); - - cec_s_phys_addr_from_edid(adv7511->cec_adap, edid); - } else { - cec_s_phys_addr_from_edid(adv7511->cec_adap, NULL); - } - return drm_edid; } -static int adv7511_get_modes(struct adv7511 *adv7511, - struct drm_connector *connector) -{ - const struct drm_edid *drm_edid; - unsigned int count; - - drm_edid = adv7511_edid_read(adv7511, connector); - - drm_edid_connector_update(connector, drm_edid); - count = drm_edid_connector_add_modes(connector); - - drm_edid_free(drm_edid); - - return count; -} - static enum drm_connector_status -adv7511_detect(struct adv7511 *adv7511, struct drm_connector *connector) +adv7511_detect(struct adv7511 *adv7511) { enum drm_connector_status status; unsigned int val; @@ -699,8 +628,6 @@ adv7511_detect(struct adv7511 *adv7511, struct drm_connector *connector) if (status == connector_status_connected && hpd && adv7511->powered) { regcache_mark_dirty(adv7511->regmap); adv7511_power_on(adv7511); - if (connector) - adv7511_get_modes(adv7511, connector); if (adv7511->status == connector_status_connected) status = connector_status_disconnected; } else { @@ -719,17 +646,7 @@ adv7511_detect(struct adv7511 *adv7511, struct drm_connector *connector) return status; } -static enum drm_mode_status adv7511_mode_valid(struct adv7511 *adv7511, - const struct drm_display_mode *mode) -{ - if (mode->clock > 165000) - return MODE_CLOCK_HIGH; - - return MODE_OK; -} - static void adv7511_mode_set(struct adv7511 *adv7511, - const struct drm_display_mode *mode, const struct drm_display_mode *adj_mode) { unsigned int low_refresh_rate; @@ -800,11 +717,11 @@ static void adv7511_mode_set(struct adv7511 *adv7511, vsync_polarity = 1; } - if (drm_mode_vrefresh(mode) <= 24) + if (drm_mode_vrefresh(adj_mode) <= 24) low_refresh_rate = ADV7511_LOW_REFRESH_RATE_24HZ; - else if (drm_mode_vrefresh(mode) <= 25) + else if (drm_mode_vrefresh(adj_mode) <= 25) low_refresh_rate = ADV7511_LOW_REFRESH_RATE_25HZ; - else if (drm_mode_vrefresh(mode) <= 30) + else if (drm_mode_vrefresh(adj_mode) <= 30) low_refresh_rate = ADV7511_LOW_REFRESH_RATE_30HZ; else low_refresh_rate = ADV7511_LOW_REFRESH_RATE_NONE; @@ -821,82 +738,30 @@ static void adv7511_mode_set(struct adv7511 *adv7511, drm_mode_copy(&adv7511->curr_mode, adj_mode); + /* Update horizontal/vertical porch params */ + if (adv7511->info->has_dsi && adv7511->use_timing_gen) + adv7533_dsi_config_timing_gen(adv7511); + /* * TODO Test first order 4:2:2 to 4:4:4 up conversion method, which is * supposed to give better results. */ - adv7511->f_tmds = mode->clock; -} - -/* ----------------------------------------------------------------------------- - * DRM Connector Operations - */ - -static struct adv7511 *connector_to_adv7511(struct drm_connector *connector) -{ - return container_of(connector, struct adv7511, connector); + adv7511->f_tmds = adj_mode->clock; } -static int adv7511_connector_get_modes(struct drm_connector *connector) -{ - struct adv7511 *adv = connector_to_adv7511(connector); - - return adv7511_get_modes(adv, connector); -} - -static enum drm_mode_status -adv7511_connector_mode_valid(struct drm_connector *connector, - const struct drm_display_mode *mode) -{ - struct adv7511 *adv = connector_to_adv7511(connector); - - return adv7511_mode_valid(adv, mode); -} - -static struct drm_connector_helper_funcs adv7511_connector_helper_funcs = { - .get_modes = adv7511_connector_get_modes, - .mode_valid = adv7511_connector_mode_valid, -}; - -static enum drm_connector_status -adv7511_connector_detect(struct drm_connector *connector, bool force) -{ - struct adv7511 *adv = connector_to_adv7511(connector); - - return adv7511_detect(adv, connector); -} - -static const struct drm_connector_funcs adv7511_connector_funcs = { - .fill_modes = drm_helper_probe_single_connector_modes, - .detect = adv7511_connector_detect, - .destroy = drm_connector_cleanup, - .reset = drm_atomic_helper_connector_reset, - .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state, - .atomic_destroy_state = drm_atomic_helper_connector_destroy_state, -}; - static int adv7511_connector_init(struct adv7511 *adv) { struct drm_bridge *bridge = &adv->bridge; - int ret; - - if (adv->i2c_main->irq) - adv->connector.polled = DRM_CONNECTOR_POLL_HPD; - else - adv->connector.polled = DRM_CONNECTOR_POLL_CONNECT | - DRM_CONNECTOR_POLL_DISCONNECT; + struct drm_connector *connector; - ret = drm_connector_init(bridge->dev, &adv->connector, - &adv7511_connector_funcs, - DRM_MODE_CONNECTOR_HDMIA); - if (ret < 0) { + connector = drm_bridge_connector_init(bridge->dev, bridge->encoder); + if (IS_ERR(connector)) { DRM_ERROR("Failed to initialize connector with drm\n"); - return ret; + return PTR_ERR(connector); } - drm_connector_helper_add(&adv->connector, - &adv7511_connector_helper_funcs); - drm_connector_attach_encoder(&adv->connector, bridge->encoder); + + drm_connector_attach_encoder(connector, bridge->encoder); return 0; } @@ -905,7 +770,7 @@ static int adv7511_connector_init(struct adv7511 *adv) * DRM Bridge Operations */ -static struct adv7511 *bridge_to_adv7511(struct drm_bridge *bridge) +static const struct adv7511 *bridge_to_adv7511_const(const struct drm_bridge *bridge) { return container_of(bridge, struct adv7511, bridge); } @@ -914,8 +779,29 @@ static void adv7511_bridge_atomic_enable(struct drm_bridge *bridge, struct drm_atomic_state *state) { struct adv7511 *adv = bridge_to_adv7511(bridge); + struct drm_connector *connector; + struct drm_connector_state *conn_state; + struct drm_crtc_state *crtc_state; adv7511_power_on(adv); + + connector = drm_atomic_get_new_connector_for_encoder(state, bridge->encoder); + if (WARN_ON(!connector)) + return; + + conn_state = drm_atomic_get_new_connector_state(state, connector); + if (WARN_ON(!conn_state)) + return; + + crtc_state = drm_atomic_get_new_crtc_state(state, conn_state->crtc); + if (WARN_ON(!crtc_state)) + return; + + adv7511_set_config_csc(adv, connector, adv->rgb); + + adv7511_mode_set(adv, &crtc_state->adjusted_mode); + + drm_atomic_helper_connector_hdmi_update_infoframes(connector, state); } static void adv7511_bridge_atomic_disable(struct drm_bridge *bridge, @@ -926,13 +812,17 @@ static void adv7511_bridge_atomic_disable(struct drm_bridge *bridge, adv7511_power_off(adv); } -static void adv7511_bridge_mode_set(struct drm_bridge *bridge, - const struct drm_display_mode *mode, - const struct drm_display_mode *adj_mode) +static enum drm_mode_status +adv7511_bridge_hdmi_tmds_char_rate_valid(const struct drm_bridge *bridge, + const struct drm_display_mode *mode, + unsigned long long tmds_rate) { - struct adv7511 *adv = bridge_to_adv7511(bridge); + const struct adv7511 *adv = bridge_to_adv7511_const(bridge); - adv7511_mode_set(adv, mode, adj_mode); + if (tmds_rate > 1000ULL * adv->info->max_mode_clock_khz) + return MODE_CLOCK_HIGH; + + return MODE_OK; } static enum drm_mode_status adv7511_bridge_mode_valid(struct drm_bridge *bridge, @@ -941,10 +831,10 @@ static enum drm_mode_status adv7511_bridge_mode_valid(struct drm_bridge *bridge, { struct adv7511 *adv = bridge_to_adv7511(bridge); - if (adv->info->has_dsi) - return adv7533_mode_valid(adv, mode); - else - return adv7511_mode_valid(adv, mode); + if (!adv->info->has_dsi) + return MODE_OK; + + return adv7533_mode_valid(adv, mode); } static int adv7511_bridge_attach(struct drm_bridge *bridge, @@ -978,7 +868,7 @@ static enum drm_connector_status adv7511_bridge_detect(struct drm_bridge *bridge { struct adv7511 *adv = bridge_to_adv7511(bridge); - return adv7511_detect(adv, NULL); + return adv7511_detect(adv); } static const struct drm_edid *adv7511_bridge_edid_read(struct drm_bridge *bridge, @@ -989,28 +879,71 @@ static const struct drm_edid *adv7511_bridge_edid_read(struct drm_bridge *bridge return adv7511_edid_read(adv, connector); } -static void adv7511_bridge_hpd_notify(struct drm_bridge *bridge, - enum drm_connector_status status) +static int adv7511_bridge_hdmi_clear_infoframe(struct drm_bridge *bridge, + enum hdmi_infoframe_type type) { - struct adv7511 *adv = bridge_to_adv7511(bridge); + struct adv7511 *adv7511 = bridge_to_adv7511(bridge); + + switch (type) { + case HDMI_INFOFRAME_TYPE_AVI: + adv7511_packet_disable(adv7511, ADV7511_PACKET_ENABLE_AVI_INFOFRAME); + break; + default: + drm_dbg_driver(adv7511->bridge.dev, "Unsupported HDMI InfoFrame %x\n", type); + break; + } - if (status == connector_status_disconnected) - cec_phys_addr_invalidate(adv->cec_adap); + return 0; +} + +static int adv7511_bridge_hdmi_write_infoframe(struct drm_bridge *bridge, + enum hdmi_infoframe_type type, + const u8 *buffer, size_t len) +{ + struct adv7511 *adv7511 = bridge_to_adv7511(bridge); + + adv7511_bridge_hdmi_clear_infoframe(bridge, type); + + switch (type) { + case HDMI_INFOFRAME_TYPE_AVI: + /* The AVI infoframe id is not configurable */ + regmap_bulk_write(adv7511->regmap, ADV7511_REG_AVI_INFOFRAME_VERSION, + buffer + 1, len - 1); + + adv7511_packet_enable(adv7511, ADV7511_PACKET_ENABLE_AVI_INFOFRAME); + break; + default: + drm_dbg_driver(adv7511->bridge.dev, "Unsupported HDMI InfoFrame %x\n", type); + break; + } + + return 0; } static const struct drm_bridge_funcs adv7511_bridge_funcs = { - .mode_set = adv7511_bridge_mode_set, .mode_valid = adv7511_bridge_mode_valid, .attach = adv7511_bridge_attach, .detect = adv7511_bridge_detect, .edid_read = adv7511_bridge_edid_read, - .hpd_notify = adv7511_bridge_hpd_notify, .atomic_enable = adv7511_bridge_atomic_enable, .atomic_disable = adv7511_bridge_atomic_disable, .atomic_duplicate_state = drm_atomic_helper_bridge_duplicate_state, .atomic_destroy_state = drm_atomic_helper_bridge_destroy_state, .atomic_reset = drm_atomic_helper_bridge_reset, + + .hdmi_tmds_char_rate_valid = adv7511_bridge_hdmi_tmds_char_rate_valid, + .hdmi_clear_infoframe = adv7511_bridge_hdmi_clear_infoframe, + .hdmi_write_infoframe = adv7511_bridge_hdmi_write_infoframe, + + .hdmi_audio_startup = adv7511_hdmi_audio_startup, + .hdmi_audio_prepare = adv7511_hdmi_audio_prepare, + .hdmi_audio_shutdown = adv7511_hdmi_audio_shutdown, + + .hdmi_cec_init = adv7511_cec_init, + .hdmi_cec_enable = adv7511_cec_enable, + .hdmi_cec_log_addr = adv7511_cec_log_addr, + .hdmi_cec_transmit = adv7511_cec_transmit, }; /* ----------------------------------------------------------------------------- @@ -1224,9 +1157,10 @@ static int adv7511_probe(struct i2c_client *i2c) if (!dev->of_node) return -EINVAL; - adv7511 = devm_kzalloc(dev, sizeof(*adv7511), GFP_KERNEL); - if (!adv7511) - return -ENOMEM; + adv7511 = devm_drm_bridge_alloc(dev, struct adv7511, bridge, + &adv7511_bridge_funcs); + if (IS_ERR(adv7511)) + return PTR_ERR(adv7511); adv7511->i2c_main = i2c; adv7511->powered = false; @@ -1323,22 +1257,43 @@ static int adv7511_probe(struct i2c_client *i2c) if (adv7511->info->link_config) adv7511_set_link_config(adv7511, &link_config); - ret = adv7511_cec_init(dev, adv7511); - if (ret) - goto err_unregister_cec; + regmap_write(adv7511->regmap, ADV7511_REG_CEC_CTRL, + ADV7511_CEC_CTRL_POWER_DOWN); - adv7511->bridge.funcs = &adv7511_bridge_funcs; - adv7511->bridge.ops = DRM_BRIDGE_OP_DETECT | DRM_BRIDGE_OP_EDID; + adv7511->bridge.ops = DRM_BRIDGE_OP_DETECT | + DRM_BRIDGE_OP_EDID | + DRM_BRIDGE_OP_HDMI | + DRM_BRIDGE_OP_HDMI_AUDIO | + DRM_BRIDGE_OP_HDMI_CEC_ADAPTER; if (adv7511->i2c_main->irq) adv7511->bridge.ops |= DRM_BRIDGE_OP_HPD; + adv7511->bridge.vendor = "Analog"; + adv7511->bridge.product = adv7511->info->name; + +#ifdef CONFIG_DRM_I2C_ADV7511_AUDIO + adv7511->bridge.hdmi_audio_dev = dev; + adv7511->bridge.hdmi_audio_max_i2s_playback_channels = 2; + adv7511->bridge.hdmi_audio_i2s_formats = (SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S20_3LE | + SNDRV_PCM_FMTBIT_S24_3LE | + SNDRV_PCM_FMTBIT_S24_LE | + SNDRV_PCM_FMTBIT_IEC958_SUBFRAME_LE), + adv7511->bridge.hdmi_audio_spdif_playback = 1; + adv7511->bridge.hdmi_audio_dai_port = 2; +#endif + +#ifdef CONFIG_DRM_I2C_ADV7511_CEC + adv7511->bridge.hdmi_cec_dev = dev; + adv7511->bridge.hdmi_cec_adapter_name = dev_name(dev); + adv7511->bridge.hdmi_cec_available_las = ADV7511_MAX_ADDRS; +#endif + adv7511->bridge.of_node = dev->of_node; adv7511->bridge.type = DRM_MODE_CONNECTOR_HDMIA; drm_bridge_add(&adv7511->bridge); - adv7511_audio_init(dev, adv7511); - if (i2c->irq) { init_waitqueue_head(&adv7511->wq); @@ -1360,10 +1315,7 @@ static int adv7511_probe(struct i2c_client *i2c) return 0; err_unregister_audio: - adv7511_audio_exit(adv7511); drm_bridge_remove(&adv7511->bridge); -err_unregister_cec: - cec_unregister_adapter(adv7511->cec_adap); i2c_unregister_device(adv7511->i2c_cec); clk_disable_unprepare(adv7511->cec_clk); err_i2c_unregister_packet: @@ -1388,9 +1340,6 @@ static void adv7511_remove(struct i2c_client *i2c) drm_bridge_remove(&adv7511->bridge); - adv7511_audio_exit(adv7511); - - cec_unregister_adapter(adv7511->cec_adap); i2c_unregister_device(adv7511->i2c_cec); clk_disable_unprepare(adv7511->cec_clk); @@ -1400,6 +1349,8 @@ static void adv7511_remove(struct i2c_client *i2c) static const struct adv7511_chip_info adv7511_chip_info = { .type = ADV7511, + .name = "ADV7511", + .max_mode_clock_khz = 165000, .supply_names = adv7511_supply_names, .num_supplies = ARRAY_SIZE(adv7511_supply_names), .link_config = true, @@ -1407,6 +1358,7 @@ static const struct adv7511_chip_info adv7511_chip_info = { static const struct adv7511_chip_info adv7533_chip_info = { .type = ADV7533, + .name = "ADV7533", .max_mode_clock_khz = 80000, .max_lane_freq_khz = 800000, .supply_names = adv7533_supply_names, @@ -1417,6 +1369,7 @@ static const struct adv7511_chip_info adv7533_chip_info = { static const struct adv7511_chip_info adv7535_chip_info = { .type = ADV7535, + .name = "ADV7535", .max_mode_clock_khz = 148500, .max_lane_freq_khz = 891000, .supply_names = adv7533_supply_names, diff --git a/drivers/gpu/drm/bridge/adv7511/adv7533.c b/drivers/gpu/drm/bridge/adv7511/adv7533.c index 122ad91e8a32..188c1093a66e 100644 --- a/drivers/gpu/drm/bridge/adv7511/adv7533.c +++ b/drivers/gpu/drm/bridge/adv7511/adv7533.c @@ -24,7 +24,7 @@ static const struct reg_sequence adv7533_cec_fixed_registers[] = { { 0x05, 0xc8 }, }; -static void adv7511_dsi_config_timing_gen(struct adv7511 *adv) +void adv7533_dsi_config_timing_gen(struct adv7511 *adv) { struct mipi_dsi_device *dsi = adv->dsi; struct drm_display_mode *mode = &adv->curr_mode; @@ -67,9 +67,6 @@ void adv7533_dsi_power_on(struct adv7511 *adv) { struct mipi_dsi_device *dsi = adv->dsi; - if (adv->use_timing_gen) - adv7511_dsi_config_timing_gen(adv); - /* set number of dsi lanes */ regmap_write(adv->regmap_cec, 0x1c, dsi->lanes << 4); @@ -106,10 +103,6 @@ enum drm_mode_status adv7533_mode_valid(struct adv7511 *adv, struct mipi_dsi_device *dsi = adv->dsi; u8 bpp = mipi_dsi_pixel_format_to_bpp(dsi->format); - /* Check max clock for either 7533 or 7535 */ - if (mode->clock > adv->info->max_mode_clock_khz) - return MODE_CLOCK_HIGH; - /* Check max clock for each lane */ if (mode->clock * bpp > adv->info->max_lane_freq_khz * adv->num_dsi_lanes) return MODE_CLOCK_HIGH; diff --git a/drivers/gpu/drm/bridge/analogix/analogix-anx78xx.c b/drivers/gpu/drm/bridge/analogix/analogix-anx78xx.c index a83020d6576f..ba0fc149a9e7 100644 --- a/drivers/gpu/drm/bridge/analogix/analogix-anx78xx.c +++ b/drivers/gpu/drm/bridge/analogix/analogix-anx78xx.c @@ -1193,9 +1193,10 @@ static int anx78xx_i2c_probe(struct i2c_client *client) bool found = false; int err; - anx78xx = devm_kzalloc(&client->dev, sizeof(*anx78xx), GFP_KERNEL); - if (!anx78xx) - return -ENOMEM; + anx78xx = devm_drm_bridge_alloc(&client->dev, struct anx78xx, bridge, + &anx78xx_bridge_funcs); + if (IS_ERR(anx78xx)) + return PTR_ERR(anx78xx); pdata = &anx78xx->pdata; @@ -1306,8 +1307,6 @@ static int anx78xx_i2c_probe(struct i2c_client *client) goto err_poweroff; } - anx78xx->bridge.funcs = &anx78xx_bridge_funcs; - drm_bridge_add(&anx78xx->bridge); /* If cable is pulled out, just poweroff and wait for HPD event */ diff --git a/drivers/gpu/drm/bridge/analogix/anx7625.c b/drivers/gpu/drm/bridge/analogix/anx7625.c index 8a9079c2ed5c..0ac4a82c5a6e 100644 --- a/drivers/gpu/drm/bridge/analogix/anx7625.c +++ b/drivers/gpu/drm/bridge/analogix/anx7625.c @@ -2596,7 +2596,6 @@ static int anx7625_link_bridge(struct drm_dp_aux *aux) return ret; } - platform->bridge.funcs = &anx7625_bridge_funcs; platform->bridge.of_node = dev->of_node; if (!anx7625_of_panel_on_aux_bus(dev)) platform->bridge.ops |= DRM_BRIDGE_OP_EDID; @@ -2630,10 +2629,10 @@ static int anx7625_i2c_probe(struct i2c_client *client) return -ENODEV; } - platform = devm_kzalloc(dev, sizeof(*platform), GFP_KERNEL); - if (!platform) { + platform = devm_drm_bridge_alloc(dev, struct anx7625_data, bridge, &anx7625_bridge_funcs); + if (IS_ERR(platform)) { DRM_DEV_ERROR(dev, "fail to allocate driver data\n"); - return -ENOMEM; + return PTR_ERR(platform); } pdata = &platform->pdata; diff --git a/drivers/gpu/drm/bridge/aux-bridge.c b/drivers/gpu/drm/bridge/aux-bridge.c index c179b86d208f..5b219e3b87b1 100644 --- a/drivers/gpu/drm/bridge/aux-bridge.c +++ b/drivers/gpu/drm/bridge/aux-bridge.c @@ -109,9 +109,10 @@ static int drm_aux_bridge_probe(struct auxiliary_device *auxdev, { struct drm_aux_bridge_data *data; - data = devm_kzalloc(&auxdev->dev, sizeof(*data), GFP_KERNEL); - if (!data) - return -ENOMEM; + data = devm_drm_bridge_alloc(&auxdev->dev, struct drm_aux_bridge_data, + bridge, &drm_aux_bridge_funcs); + if (IS_ERR(data)) + return PTR_ERR(data); data->dev = &auxdev->dev; data->next_bridge = devm_drm_of_get_bridge(&auxdev->dev, auxdev->dev.of_node, 0, 0); @@ -119,7 +120,6 @@ static int drm_aux_bridge_probe(struct auxiliary_device *auxdev, return dev_err_probe(&auxdev->dev, PTR_ERR(data->next_bridge), "failed to acquire drm_bridge\n"); - data->bridge.funcs = &drm_aux_bridge_funcs; data->bridge.of_node = data->dev->of_node; /* passthrough data, allow everything */ diff --git a/drivers/gpu/drm/bridge/aux-hpd-bridge.c b/drivers/gpu/drm/bridge/aux-hpd-bridge.c index b3f588b71a7d..3eb411f874e4 100644 --- a/drivers/gpu/drm/bridge/aux-hpd-bridge.c +++ b/drivers/gpu/drm/bridge/aux-hpd-bridge.c @@ -171,12 +171,13 @@ static int drm_aux_hpd_bridge_probe(struct auxiliary_device *auxdev, { struct drm_aux_hpd_bridge_data *data; - data = devm_kzalloc(&auxdev->dev, sizeof(*data), GFP_KERNEL); - if (!data) - return -ENOMEM; + data = devm_drm_bridge_alloc(&auxdev->dev, + struct drm_aux_hpd_bridge_data, bridge, + &drm_aux_hpd_bridge_funcs); + if (IS_ERR(data)) + return PTR_ERR(data); data->dev = &auxdev->dev; - data->bridge.funcs = &drm_aux_hpd_bridge_funcs; data->bridge.of_node = dev_get_platdata(data->dev); data->bridge.ops = DRM_BRIDGE_OP_HPD; data->bridge.type = id->driver_data; diff --git a/drivers/gpu/drm/bridge/cadence/cdns-dsi-core.c b/drivers/gpu/drm/bridge/cadence/cdns-dsi-core.c index b022dd6e6b6e..a57ca8c3bdae 100644 --- a/drivers/gpu/drm/bridge/cadence/cdns-dsi-core.c +++ b/drivers/gpu/drm/bridge/cadence/cdns-dsi-core.c @@ -670,13 +670,28 @@ cdns_dsi_bridge_mode_valid(struct drm_bridge *bridge, return MODE_OK; } -static void cdns_dsi_bridge_atomic_disable(struct drm_bridge *bridge, - struct drm_atomic_state *state) +static void cdns_dsi_bridge_atomic_post_disable(struct drm_bridge *bridge, + struct drm_atomic_state *state) { struct cdns_dsi_input *input = bridge_to_cdns_dsi_input(bridge); struct cdns_dsi *dsi = input_to_dsi(input); u32 val; + /* + * The cdns-dsi controller needs to be disabled after it's DPI source + * has stopped streaming. If this is not followed, there is a brief + * window before DPI source is disabled and after cdns-dsi controller + * has been disabled where the DPI stream is still on, but the cdns-dsi + * controller is not ready anymore to accept the incoming signals. This + * is one of the reasons why a shift in pixel colors is observed on + * displays that have cdns-dsi as one of the bridges. + * + * To mitigate this, disable this bridge from the bridge post_disable() + * hook, instead of the bridge _disable() hook. The bridge post_disable() + * hook gets called after the CRTC disable, where often many DPI sources + * disable their streams. + */ + val = readl(dsi->regs + MCTL_MAIN_DATA_CTL); val &= ~(IF_VID_SELECT_MASK | IF_VID_MODE | VID_EN | HOST_EOT_GEN | DISP_EOT_GEN); @@ -688,15 +703,6 @@ static void cdns_dsi_bridge_atomic_disable(struct drm_bridge *bridge, if (dsi->platform_ops && dsi->platform_ops->disable) dsi->platform_ops->disable(dsi); - pm_runtime_put(dsi->base.dev); -} - -static void cdns_dsi_bridge_atomic_post_disable(struct drm_bridge *bridge, - struct drm_atomic_state *state) -{ - struct cdns_dsi_input *input = bridge_to_cdns_dsi_input(bridge); - struct cdns_dsi *dsi = input_to_dsi(input); - dsi->phy_initialized = false; dsi->link_initialized = false; phy_power_off(dsi->dphy); @@ -774,8 +780,8 @@ static void cdns_dsi_init_link(struct cdns_dsi *dsi) dsi->link_initialized = true; } -static void cdns_dsi_bridge_atomic_enable(struct drm_bridge *bridge, - struct drm_atomic_state *state) +static void cdns_dsi_bridge_atomic_pre_enable(struct drm_bridge *bridge, + struct drm_atomic_state *state) { struct cdns_dsi_input *input = bridge_to_cdns_dsi_input(bridge); struct cdns_dsi *dsi = input_to_dsi(input); @@ -792,6 +798,21 @@ static void cdns_dsi_bridge_atomic_enable(struct drm_bridge *bridge, u32 tmp, reg_wakeup, div, status; int nlanes; + /* + * The cdns-dsi controller needs to be enabled before it's DPI source + * has begun streaming. If this is not followed, there is a brief window + * after DPI source enable and before cdns-dsi controller enable where + * the DPI stream is on, but the cdns-dsi controller is not ready to + * accept the incoming signals. This is one of the reasons why a shift + * in pixel colors is observed on displays that have cdns-dsi as one of + * the bridges. + * + * To mitigate this, enable this bridge from the bridge pre_enable() + * hook, instead of the bridge _enable() hook. The bridge pre_enable() + * hook gets called before the CRTC enable, where often many DPI sources + * enable their streams. + */ + if (WARN_ON(pm_runtime_get_sync(dsi->base.dev) < 0)) return; @@ -811,8 +832,8 @@ static void cdns_dsi_bridge_atomic_enable(struct drm_bridge *bridge, mode = &crtc_state->adjusted_mode; nlanes = output->dev->lanes; - cdns_dsi_hs_init(dsi); cdns_dsi_init_link(dsi); + cdns_dsi_hs_init(dsi); /* * Now that the DSI Link and DSI Phy are initialized, @@ -941,19 +962,6 @@ static void cdns_dsi_bridge_atomic_enable(struct drm_bridge *bridge, writel(tmp, dsi->regs + MCTL_MAIN_EN); } -static void cdns_dsi_bridge_atomic_pre_enable(struct drm_bridge *bridge, - struct drm_atomic_state *state) -{ - struct cdns_dsi_input *input = bridge_to_cdns_dsi_input(bridge); - struct cdns_dsi *dsi = input_to_dsi(input); - - if (WARN_ON(pm_runtime_get_sync(dsi->base.dev) < 0)) - return; - - cdns_dsi_init_link(dsi); - cdns_dsi_hs_init(dsi); -} - static u32 *cdns_dsi_bridge_get_input_bus_fmts(struct drm_bridge *bridge, struct drm_bridge_state *bridge_state, struct drm_crtc_state *crtc_state, @@ -1048,9 +1056,7 @@ cdns_dsi_bridge_atomic_reset(struct drm_bridge *bridge) static const struct drm_bridge_funcs cdns_dsi_bridge_funcs = { .attach = cdns_dsi_bridge_attach, .mode_valid = cdns_dsi_bridge_mode_valid, - .atomic_disable = cdns_dsi_bridge_atomic_disable, .atomic_pre_enable = cdns_dsi_bridge_atomic_pre_enable, - .atomic_enable = cdns_dsi_bridge_atomic_enable, .atomic_post_disable = cdns_dsi_bridge_atomic_post_disable, .atomic_check = cdns_dsi_bridge_atomic_check, .atomic_reset = cdns_dsi_bridge_atomic_reset, @@ -1289,9 +1295,10 @@ static int cdns_dsi_drm_probe(struct platform_device *pdev) int ret, irq; u32 val; - dsi = devm_kzalloc(&pdev->dev, sizeof(*dsi), GFP_KERNEL); - if (!dsi) - return -ENOMEM; + dsi = devm_drm_bridge_alloc(&pdev->dev, struct cdns_dsi, input.bridge, + &cdns_dsi_bridge_funcs); + if (IS_ERR(dsi)) + return PTR_ERR(dsi); platform_set_drvdata(pdev, dsi); @@ -1349,7 +1356,6 @@ static int cdns_dsi_drm_probe(struct platform_device *pdev) * CDNS_DPI_INPUT. */ input->id = CDNS_DPI_INPUT; - input->bridge.funcs = &cdns_dsi_bridge_funcs; input->bridge.of_node = pdev->dev.of_node; /* Mask all interrupts before registering the IRQ handler. */ diff --git a/drivers/gpu/drm/bridge/cadence/cdns-mhdp8546-core.c b/drivers/gpu/drm/bridge/cadence/cdns-mhdp8546-core.c index b431e7efd1f0..cb5f5a8c539a 100644 --- a/drivers/gpu/drm/bridge/cadence/cdns-mhdp8546-core.c +++ b/drivers/gpu/drm/bridge/cadence/cdns-mhdp8546-core.c @@ -2389,9 +2389,10 @@ static int cdns_mhdp_probe(struct platform_device *pdev) int ret; int irq; - mhdp = devm_kzalloc(dev, sizeof(*mhdp), GFP_KERNEL); - if (!mhdp) - return -ENOMEM; + mhdp = devm_drm_bridge_alloc(dev, struct cdns_mhdp_device, bridge, + &cdns_mhdp_bridge_funcs); + if (IS_ERR(mhdp)) + return PTR_ERR(mhdp); clk = devm_clk_get_enabled(dev, NULL); if (IS_ERR(clk)) { @@ -2481,7 +2482,6 @@ static int cdns_mhdp_probe(struct platform_device *pdev) mhdp->display_fmt.bpc = 8; mhdp->bridge.of_node = pdev->dev.of_node; - mhdp->bridge.funcs = &cdns_mhdp_bridge_funcs; mhdp->bridge.ops = DRM_BRIDGE_OP_DETECT | DRM_BRIDGE_OP_EDID | DRM_BRIDGE_OP_HPD; mhdp->bridge.type = DRM_MODE_CONNECTOR_DisplayPort; diff --git a/drivers/gpu/drm/bridge/chipone-icn6211.c b/drivers/gpu/drm/bridge/chipone-icn6211.c index 634c5b030667..814713c5bea9 100644 --- a/drivers/gpu/drm/bridge/chipone-icn6211.c +++ b/drivers/gpu/drm/bridge/chipone-icn6211.c @@ -691,9 +691,10 @@ static int chipone_common_probe(struct device *dev, struct chipone **icnr) struct chipone *icn; int ret; - icn = devm_kzalloc(dev, sizeof(struct chipone), GFP_KERNEL); - if (!icn) - return -ENOMEM; + icn = devm_drm_bridge_alloc(dev, struct chipone, bridge, + &chipone_bridge_funcs); + if (IS_ERR(icn)) + return PTR_ERR(icn); icn->dev = dev; @@ -701,7 +702,6 @@ static int chipone_common_probe(struct device *dev, struct chipone **icnr) if (ret) return ret; - icn->bridge.funcs = &chipone_bridge_funcs; icn->bridge.type = DRM_MODE_CONNECTOR_DPI; icn->bridge.of_node = dev->of_node; diff --git a/drivers/gpu/drm/bridge/chrontel-ch7033.c b/drivers/gpu/drm/bridge/chrontel-ch7033.c index 210c45c1efd4..ab9274793356 100644 --- a/drivers/gpu/drm/bridge/chrontel-ch7033.c +++ b/drivers/gpu/drm/bridge/chrontel-ch7033.c @@ -536,9 +536,10 @@ static int ch7033_probe(struct i2c_client *client) unsigned int val; int ret; - priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); - if (!priv) - return -ENOMEM; + priv = devm_drm_bridge_alloc(dev, struct ch7033_priv, bridge, + &ch7033_bridge_funcs); + if (IS_ERR(priv)) + return PTR_ERR(priv); dev_set_drvdata(dev, priv); @@ -575,7 +576,6 @@ static int ch7033_probe(struct i2c_client *client) } INIT_LIST_HEAD(&priv->bridge.list); - priv->bridge.funcs = &ch7033_bridge_funcs; priv->bridge.of_node = dev->of_node; drm_bridge_add(&priv->bridge); diff --git a/drivers/gpu/drm/bridge/cros-ec-anx7688.c b/drivers/gpu/drm/bridge/cros-ec-anx7688.c index c8abd9920fee..a35dae9b56e2 100644 --- a/drivers/gpu/drm/bridge/cros-ec-anx7688.c +++ b/drivers/gpu/drm/bridge/cros-ec-anx7688.c @@ -103,9 +103,10 @@ static int cros_ec_anx7688_bridge_probe(struct i2c_client *client) u8 buffer[4]; int ret; - anx7688 = devm_kzalloc(dev, sizeof(*anx7688), GFP_KERNEL); - if (!anx7688) - return -ENOMEM; + anx7688 = devm_drm_bridge_alloc(dev, struct cros_ec_anx7688, bridge, + &cros_ec_anx7688_bridge_funcs); + if (IS_ERR(anx7688)) + return PTR_ERR(anx7688); anx7688->client = client; i2c_set_clientdata(client, anx7688); @@ -153,7 +154,6 @@ static int cros_ec_anx7688_bridge_probe(struct i2c_client *client) DRM_WARN("Old ANX7688 FW version (0x%04x), not filtering\n", fw_version); - anx7688->bridge.funcs = &cros_ec_anx7688_bridge_funcs; drm_bridge_add(&anx7688->bridge); return 0; diff --git a/drivers/gpu/drm/bridge/fsl-ldb.c b/drivers/gpu/drm/bridge/fsl-ldb.c index 2cb6dfc7a6d3..5c3cf37200bc 100644 --- a/drivers/gpu/drm/bridge/fsl-ldb.c +++ b/drivers/gpu/drm/bridge/fsl-ldb.c @@ -298,16 +298,15 @@ static int fsl_ldb_probe(struct platform_device *pdev) struct fsl_ldb *fsl_ldb; int dual_link; - fsl_ldb = devm_kzalloc(dev, sizeof(*fsl_ldb), GFP_KERNEL); - if (!fsl_ldb) - return -ENOMEM; + fsl_ldb = devm_drm_bridge_alloc(dev, struct fsl_ldb, bridge, &funcs); + if (IS_ERR(fsl_ldb)) + return PTR_ERR(fsl_ldb); fsl_ldb->devdata = of_device_get_match_data(dev); if (!fsl_ldb->devdata) return -EINVAL; fsl_ldb->dev = &pdev->dev; - fsl_ldb->bridge.funcs = &funcs; fsl_ldb->bridge.of_node = dev->of_node; fsl_ldb->clk = devm_clk_get(dev, "ldb"); diff --git a/drivers/gpu/drm/bridge/imx/imx-legacy-bridge.c b/drivers/gpu/drm/bridge/imx/imx-legacy-bridge.c index f072c6ed39ef..989bc497b050 100644 --- a/drivers/gpu/drm/bridge/imx/imx-legacy-bridge.c +++ b/drivers/gpu/drm/bridge/imx/imx-legacy-bridge.c @@ -59,9 +59,10 @@ struct drm_bridge *devm_imx_drm_legacy_bridge(struct device *dev, struct imx_legacy_bridge *imx_bridge; int ret; - imx_bridge = devm_kzalloc(dev, sizeof(*imx_bridge), GFP_KERNEL); - if (!imx_bridge) - return ERR_PTR(-ENOMEM); + imx_bridge = devm_drm_bridge_alloc(dev, struct imx_legacy_bridge, + base, &imx_legacy_bridge_funcs); + if (IS_ERR(imx_bridge)) + return ERR_CAST(imx_bridge); ret = of_get_drm_display_mode(np, &imx_bridge->mode, @@ -72,7 +73,6 @@ struct drm_bridge *devm_imx_drm_legacy_bridge(struct device *dev, imx_bridge->mode.type |= DRM_MODE_TYPE_DRIVER; - imx_bridge->base.funcs = &imx_legacy_bridge_funcs; imx_bridge->base.of_node = np; imx_bridge->base.ops = DRM_BRIDGE_OP_MODES; imx_bridge->base.type = type; diff --git a/drivers/gpu/drm/bridge/imx/imx8mp-hdmi-pvi.c b/drivers/gpu/drm/bridge/imx/imx8mp-hdmi-pvi.c index 8a4fd7d77a8d..3a6f8587a257 100644 --- a/drivers/gpu/drm/bridge/imx/imx8mp-hdmi-pvi.c +++ b/drivers/gpu/drm/bridge/imx/imx8mp-hdmi-pvi.c @@ -140,9 +140,10 @@ static int imx8mp_hdmi_pvi_probe(struct platform_device *pdev) struct device_node *remote; struct imx8mp_hdmi_pvi *pvi; - pvi = devm_kzalloc(&pdev->dev, sizeof(*pvi), GFP_KERNEL); - if (!pvi) - return -ENOMEM; + pvi = devm_drm_bridge_alloc(&pdev->dev, struct imx8mp_hdmi_pvi, + bridge, &imx_hdmi_pvi_bridge_funcs); + if (IS_ERR(pvi)) + return PTR_ERR(pvi); platform_set_drvdata(pdev, pvi); pvi->dev = &pdev->dev; @@ -166,7 +167,6 @@ static int imx8mp_hdmi_pvi_probe(struct platform_device *pdev) pm_runtime_enable(&pdev->dev); /* Register the bridge. */ - pvi->bridge.funcs = &imx_hdmi_pvi_bridge_funcs; pvi->bridge.of_node = pdev->dev.of_node; pvi->bridge.timings = pvi->next_bridge->timings; diff --git a/drivers/gpu/drm/bridge/imx/imx8qxp-pixel-combiner.c b/drivers/gpu/drm/bridge/imx/imx8qxp-pixel-combiner.c index 1f6fd488e703..8517b1c953d4 100644 --- a/drivers/gpu/drm/bridge/imx/imx8qxp-pixel-combiner.c +++ b/drivers/gpu/drm/bridge/imx/imx8qxp-pixel-combiner.c @@ -63,12 +63,11 @@ struct imx8qxp_pc_channel { struct drm_bridge *next_bridge; struct imx8qxp_pc *pc; unsigned int stream_id; - bool is_available; }; struct imx8qxp_pc { struct device *dev; - struct imx8qxp_pc_channel ch[2]; + struct imx8qxp_pc_channel *ch[2]; struct clk *clk_apb; void __iomem *base; }; @@ -307,7 +306,14 @@ static int imx8qxp_pc_bridge_probe(struct platform_device *pdev) goto free_child; } - ch = &pc->ch[i]; + ch = devm_drm_bridge_alloc(dev, struct imx8qxp_pc_channel, bridge, + &imx8qxp_pc_bridge_funcs); + if (IS_ERR(ch)) { + ret = PTR_ERR(ch); + goto free_child; + } + + pc->ch[i] = ch; ch->pc = pc; ch->stream_id = i; @@ -333,9 +339,7 @@ static int imx8qxp_pc_bridge_probe(struct platform_device *pdev) of_node_put(remote); ch->bridge.driver_private = ch; - ch->bridge.funcs = &imx8qxp_pc_bridge_funcs; ch->bridge.of_node = child; - ch->is_available = true; drm_bridge_add(&ch->bridge); } @@ -345,8 +349,8 @@ static int imx8qxp_pc_bridge_probe(struct platform_device *pdev) free_child: of_node_put(child); - if (i == 1 && pc->ch[0].next_bridge) - drm_bridge_remove(&pc->ch[0].bridge); + if (i == 1 && pc->ch[0]->next_bridge) + drm_bridge_remove(&pc->ch[0]->bridge); pm_runtime_disable(dev); return ret; @@ -359,13 +363,10 @@ static void imx8qxp_pc_bridge_remove(struct platform_device *pdev) int i; for (i = 0; i < 2; i++) { - ch = &pc->ch[i]; - - if (!ch->is_available) - continue; + ch = pc->ch[i]; - drm_bridge_remove(&ch->bridge); - ch->is_available = false; + if (ch) + drm_bridge_remove(&ch->bridge); } pm_runtime_disable(&pdev->dev); diff --git a/drivers/gpu/drm/bridge/imx/imx8qxp-pixel-link.c b/drivers/gpu/drm/bridge/imx/imx8qxp-pixel-link.c index e092c9ea99b0..e5943506981d 100644 --- a/drivers/gpu/drm/bridge/imx/imx8qxp-pixel-link.c +++ b/drivers/gpu/drm/bridge/imx/imx8qxp-pixel-link.c @@ -327,9 +327,10 @@ static int imx8qxp_pixel_link_bridge_probe(struct platform_device *pdev) struct device_node *np = dev->of_node; int ret; - pl = devm_kzalloc(dev, sizeof(*pl), GFP_KERNEL); - if (!pl) - return -ENOMEM; + pl = devm_drm_bridge_alloc(dev, struct imx8qxp_pixel_link, bridge, + &imx8qxp_pixel_link_bridge_funcs); + if (IS_ERR(pl)) + return PTR_ERR(pl); ret = imx_scu_get_handle(&pl->ipc_handle); if (ret) { @@ -384,7 +385,6 @@ static int imx8qxp_pixel_link_bridge_probe(struct platform_device *pdev) platform_set_drvdata(pdev, pl); pl->bridge.driver_private = pl; - pl->bridge.funcs = &imx8qxp_pixel_link_bridge_funcs; pl->bridge.of_node = np; drm_bridge_add(&pl->bridge); diff --git a/drivers/gpu/drm/bridge/imx/imx8qxp-pxl2dpi.c b/drivers/gpu/drm/bridge/imx/imx8qxp-pxl2dpi.c index da138ab51b3b..111310acab2c 100644 --- a/drivers/gpu/drm/bridge/imx/imx8qxp-pxl2dpi.c +++ b/drivers/gpu/drm/bridge/imx/imx8qxp-pxl2dpi.c @@ -392,9 +392,10 @@ static int imx8qxp_pxl2dpi_bridge_probe(struct platform_device *pdev) struct device_node *np = dev->of_node; int ret; - p2d = devm_kzalloc(dev, sizeof(*p2d), GFP_KERNEL); - if (!p2d) - return -ENOMEM; + p2d = devm_drm_bridge_alloc(dev, struct imx8qxp_pxl2dpi, bridge, + &imx8qxp_pxl2dpi_bridge_funcs); + if (IS_ERR(p2d)) + return PTR_ERR(p2d); p2d->regmap = syscon_node_to_regmap(np->parent); if (IS_ERR(p2d->regmap)) { @@ -441,7 +442,6 @@ static int imx8qxp_pxl2dpi_bridge_probe(struct platform_device *pdev) pm_runtime_enable(dev); p2d->bridge.driver_private = p2d; - p2d->bridge.funcs = &imx8qxp_pxl2dpi_bridge_funcs; p2d->bridge.of_node = np; drm_bridge_add(&p2d->bridge); diff --git a/drivers/gpu/drm/bridge/ite-it6263.c b/drivers/gpu/drm/bridge/ite-it6263.c index a3a63a977b0a..c4eedf643f39 100644 --- a/drivers/gpu/drm/bridge/ite-it6263.c +++ b/drivers/gpu/drm/bridge/ite-it6263.c @@ -816,9 +816,10 @@ static int it6263_probe(struct i2c_client *client) struct it6263 *it; int ret; - it = devm_kzalloc(dev, sizeof(*it), GFP_KERNEL); - if (!it) - return -ENOMEM; + it = devm_drm_bridge_alloc(dev, struct it6263, bridge, + &it6263_bridge_funcs); + if (IS_ERR(it)) + return PTR_ERR(it); it->dev = dev; it->hdmi_i2c = client; @@ -866,7 +867,6 @@ static int it6263_probe(struct i2c_client *client) i2c_set_clientdata(client, it); - it->bridge.funcs = &it6263_bridge_funcs; it->bridge.of_node = dev->of_node; /* IT6263 chip doesn't support HPD interrupt. */ it->bridge.ops = DRM_BRIDGE_OP_DETECT | DRM_BRIDGE_OP_EDID | diff --git a/drivers/gpu/drm/bridge/ite-it6505.c b/drivers/gpu/drm/bridge/ite-it6505.c index 1383d1e21afe..b0dc9280d870 100644 --- a/drivers/gpu/drm/bridge/ite-it6505.c +++ b/drivers/gpu/drm/bridge/ite-it6505.c @@ -3583,9 +3583,10 @@ static int it6505_i2c_probe(struct i2c_client *client) struct extcon_dev *extcon; int err; - it6505 = devm_kzalloc(&client->dev, sizeof(*it6505), GFP_KERNEL); - if (!it6505) - return -ENOMEM; + it6505 = devm_drm_bridge_alloc(&client->dev, struct it6505, bridge, + &it6505_bridge_funcs); + if (IS_ERR(it6505)) + return PTR_ERR(it6505); mutex_init(&it6505->extcon_lock); mutex_init(&it6505->mode_lock); @@ -3660,7 +3661,6 @@ static int it6505_i2c_probe(struct i2c_client *client) it6505->aux.transfer = it6505_aux_transfer; drm_dp_aux_init(&it6505->aux); - it6505->bridge.funcs = &it6505_bridge_funcs; it6505->bridge.type = DRM_MODE_CONNECTOR_DisplayPort; it6505->bridge.ops = DRM_BRIDGE_OP_DETECT | DRM_BRIDGE_OP_EDID | DRM_BRIDGE_OP_HPD; diff --git a/drivers/gpu/drm/bridge/ite-it66121.c b/drivers/gpu/drm/bridge/ite-it66121.c index 7b110ae53291..6494f0842793 100644 --- a/drivers/gpu/drm/bridge/ite-it66121.c +++ b/drivers/gpu/drm/bridge/ite-it66121.c @@ -1516,9 +1516,10 @@ static int it66121_probe(struct i2c_client *client) return -ENXIO; } - ctx = devm_kzalloc(dev, sizeof(*ctx), GFP_KERNEL); - if (!ctx) - return -ENOMEM; + ctx = devm_drm_bridge_alloc(dev, struct it66121_ctx, bridge, + &it66121_bridge_funcs); + if (IS_ERR(ctx)) + return PTR_ERR(ctx); ep = of_graph_get_endpoint_by_regs(dev->of_node, 0, 0); if (!ep) @@ -1577,7 +1578,6 @@ static int it66121_probe(struct i2c_client *client) return -ENODEV; } - ctx->bridge.funcs = &it66121_bridge_funcs; ctx->bridge.of_node = dev->of_node; ctx->bridge.type = DRM_MODE_CONNECTOR_HDMIA; ctx->bridge.ops = DRM_BRIDGE_OP_DETECT | DRM_BRIDGE_OP_EDID; diff --git a/drivers/gpu/drm/bridge/lontium-lt8912b.c b/drivers/gpu/drm/bridge/lontium-lt8912b.c index 3e49d855b364..bd83228b0f0e 100644 --- a/drivers/gpu/drm/bridge/lontium-lt8912b.c +++ b/drivers/gpu/drm/bridge/lontium-lt8912b.c @@ -761,9 +761,10 @@ static int lt8912_probe(struct i2c_client *client) int ret = 0; struct device *dev = &client->dev; - lt = devm_kzalloc(dev, sizeof(struct lt8912), GFP_KERNEL); - if (!lt) - return -ENOMEM; + lt = devm_drm_bridge_alloc(dev, struct lt8912, bridge, + <8912_bridge_funcs); + if (IS_ERR(lt)) + return PTR_ERR(lt); lt->dev = dev; lt->i2c_client[0] = client; @@ -778,7 +779,6 @@ static int lt8912_probe(struct i2c_client *client) i2c_set_clientdata(client, lt); - lt->bridge.funcs = <8912_bridge_funcs; lt->bridge.of_node = dev->of_node; lt->bridge.ops = (DRM_BRIDGE_OP_EDID | DRM_BRIDGE_OP_DETECT); diff --git a/drivers/gpu/drm/bridge/lontium-lt9211.c b/drivers/gpu/drm/bridge/lontium-lt9211.c index 9b2dac9bd63c..399fa7eebd49 100644 --- a/drivers/gpu/drm/bridge/lontium-lt9211.c +++ b/drivers/gpu/drm/bridge/lontium-lt9211.c @@ -727,9 +727,9 @@ static int lt9211_probe(struct i2c_client *client) struct lt9211 *ctx; int ret; - ctx = devm_kzalloc(dev, sizeof(*ctx), GFP_KERNEL); - if (!ctx) - return -ENOMEM; + ctx = devm_drm_bridge_alloc(dev, struct lt9211, bridge, <9211_funcs); + if (IS_ERR(ctx)) + return PTR_ERR(ctx); ctx->dev = dev; @@ -755,7 +755,6 @@ static int lt9211_probe(struct i2c_client *client) dev_set_drvdata(dev, ctx); i2c_set_clientdata(client, ctx); - ctx->bridge.funcs = <9211_funcs; ctx->bridge.of_node = dev->of_node; drm_bridge_add(&ctx->bridge); diff --git a/drivers/gpu/drm/bridge/lontium-lt9611.c b/drivers/gpu/drm/bridge/lontium-lt9611.c index a35a8b8ca89c..d6ee79c1e427 100644 --- a/drivers/gpu/drm/bridge/lontium-lt9611.c +++ b/drivers/gpu/drm/bridge/lontium-lt9611.c @@ -1072,9 +1072,10 @@ static int lt9611_probe(struct i2c_client *client) return -ENODEV; } - lt9611 = devm_kzalloc(dev, sizeof(*lt9611), GFP_KERNEL); - if (!lt9611) - return -ENOMEM; + lt9611 = devm_drm_bridge_alloc(dev, struct lt9611, bridge, + <9611_bridge_funcs); + if (IS_ERR(lt9611)) + return PTR_ERR(lt9611); lt9611->dev = dev; lt9611->client = client; @@ -1127,7 +1128,6 @@ static int lt9611_probe(struct i2c_client *client) /* Disable Audio InfoFrame, enabled by default */ regmap_update_bits(lt9611->regmap, 0x843d, LT9611_INFOFRAME_AUDIO, 0); - lt9611->bridge.funcs = <9611_bridge_funcs; lt9611->bridge.of_node = client->dev.of_node; lt9611->bridge.ops = DRM_BRIDGE_OP_DETECT | DRM_BRIDGE_OP_EDID | DRM_BRIDGE_OP_HPD | DRM_BRIDGE_OP_MODES | diff --git a/drivers/gpu/drm/bridge/lvds-codec.c b/drivers/gpu/drm/bridge/lvds-codec.c index 1646e454e0b0..e6a7147e141b 100644 --- a/drivers/gpu/drm/bridge/lvds-codec.c +++ b/drivers/gpu/drm/bridge/lvds-codec.c @@ -118,9 +118,10 @@ static int lvds_codec_probe(struct platform_device *pdev) u32 val; int ret; - lvds_codec = devm_kzalloc(dev, sizeof(*lvds_codec), GFP_KERNEL); - if (!lvds_codec) - return -ENOMEM; + lvds_codec = devm_drm_bridge_alloc(dev, struct lvds_codec, bridge, + &funcs); + if (IS_ERR(lvds_codec)) + return PTR_ERR(lvds_codec); lvds_codec->dev = &pdev->dev; lvds_codec->connector_type = (uintptr_t)of_device_get_match_data(dev); @@ -156,8 +157,6 @@ static int lvds_codec_probe(struct platform_device *pdev) if (IS_ERR(lvds_codec->panel_bridge)) return PTR_ERR(lvds_codec->panel_bridge); - lvds_codec->bridge.funcs = &funcs; - /* * Decoder input LVDS format is a property of the decoder chip or even * its strapping. Handle data-mapping the same way lvds-panel does. In diff --git a/drivers/gpu/drm/bridge/megachips-stdpxxxx-ge-b850v3-fw.c b/drivers/gpu/drm/bridge/megachips-stdpxxxx-ge-b850v3-fw.c index 15a5a1f644fc..81dde9ed7bcf 100644 --- a/drivers/gpu/drm/bridge/megachips-stdpxxxx-ge-b850v3-fw.c +++ b/drivers/gpu/drm/bridge/megachips-stdpxxxx-ge-b850v3-fw.c @@ -225,13 +225,11 @@ static int ge_b850v3_lvds_init(struct device *dev) if (ge_b850v3_lvds_ptr) goto success; - ge_b850v3_lvds_ptr = devm_kzalloc(dev, - sizeof(*ge_b850v3_lvds_ptr), - GFP_KERNEL); - - if (!ge_b850v3_lvds_ptr) { + ge_b850v3_lvds_ptr = devm_drm_bridge_alloc(dev, struct ge_b850v3_lvds, bridge, + &ge_b850v3_lvds_funcs); + if (IS_ERR(ge_b850v3_lvds_ptr)) { mutex_unlock(&ge_b850v3_lvds_dev_mutex); - return -ENOMEM; + return PTR_ERR(ge_b850v3_lvds_ptr); } success: @@ -264,7 +262,6 @@ static int ge_b850v3_register(void) struct device *dev = &stdp4028_i2c->dev; /* drm bridge initialization */ - ge_b850v3_lvds_ptr->bridge.funcs = &ge_b850v3_lvds_funcs; ge_b850v3_lvds_ptr->bridge.ops = DRM_BRIDGE_OP_DETECT | DRM_BRIDGE_OP_EDID; ge_b850v3_lvds_ptr->bridge.type = DRM_MODE_CONNECTOR_DisplayPort; diff --git a/drivers/gpu/drm/bridge/microchip-lvds.c b/drivers/gpu/drm/bridge/microchip-lvds.c index 1d4ae0097df8..9f4ff82bc6b4 100644 --- a/drivers/gpu/drm/bridge/microchip-lvds.c +++ b/drivers/gpu/drm/bridge/microchip-lvds.c @@ -157,9 +157,10 @@ static int mchp_lvds_probe(struct platform_device *pdev) if (!dev->of_node) return -ENODEV; - lvds = devm_kzalloc(&pdev->dev, sizeof(*lvds), GFP_KERNEL); - if (!lvds) - return -ENOMEM; + lvds = devm_drm_bridge_alloc(&pdev->dev, struct mchp_lvds, bridge, + &mchp_lvds_bridge_funcs); + if (IS_ERR(lvds)) + return PTR_ERR(lvds); lvds->dev = dev; @@ -192,7 +193,6 @@ static int mchp_lvds_probe(struct platform_device *pdev) lvds->bridge.of_node = dev->of_node; lvds->bridge.type = DRM_MODE_CONNECTOR_LVDS; - lvds->bridge.funcs = &mchp_lvds_bridge_funcs; dev_set_drvdata(dev, lvds); ret = devm_pm_runtime_enable(dev); diff --git a/drivers/gpu/drm/bridge/nwl-dsi.c b/drivers/gpu/drm/bridge/nwl-dsi.c index 55912ae11f46..2f7429b24fc2 100644 --- a/drivers/gpu/drm/bridge/nwl-dsi.c +++ b/drivers/gpu/drm/bridge/nwl-dsi.c @@ -1149,9 +1149,10 @@ static int nwl_dsi_probe(struct platform_device *pdev) struct nwl_dsi *dsi; int ret; - dsi = devm_kzalloc(dev, sizeof(*dsi), GFP_KERNEL); - if (!dsi) - return -ENOMEM; + dsi = devm_drm_bridge_alloc(dev, struct nwl_dsi, bridge, + &nwl_dsi_bridge_funcs); + if (IS_ERR(dsi)) + return PTR_ERR(dsi); dsi->dev = dev; @@ -1180,7 +1181,6 @@ static int nwl_dsi_probe(struct platform_device *pdev) dsi->quirks = (uintptr_t)attr->data; dsi->bridge.driver_private = dsi; - dsi->bridge.funcs = &nwl_dsi_bridge_funcs; dsi->bridge.of_node = dev->of_node; dsi->bridge.timings = &nwl_dsi_timings; dsi->bridge.type = DRM_MODE_CONNECTOR_DSI; diff --git a/drivers/gpu/drm/bridge/nxp-ptn3460.c b/drivers/gpu/drm/bridge/nxp-ptn3460.c index 25d7c415478b..7acb11f16dc1 100644 --- a/drivers/gpu/drm/bridge/nxp-ptn3460.c +++ b/drivers/gpu/drm/bridge/nxp-ptn3460.c @@ -261,10 +261,10 @@ static int ptn3460_probe(struct i2c_client *client) struct drm_bridge *panel_bridge; int ret; - ptn_bridge = devm_kzalloc(dev, sizeof(*ptn_bridge), GFP_KERNEL); - if (!ptn_bridge) { - return -ENOMEM; - } + ptn_bridge = devm_drm_bridge_alloc(dev, struct ptn3460_bridge, bridge, + &ptn3460_bridge_funcs); + if (IS_ERR(ptn_bridge)) + return PTR_ERR(ptn_bridge); panel_bridge = devm_drm_of_get_bridge(dev, dev->of_node, 0, 0); if (IS_ERR(panel_bridge)) @@ -300,7 +300,6 @@ static int ptn3460_probe(struct i2c_client *client) return ret; } - ptn_bridge->bridge.funcs = &ptn3460_bridge_funcs; ptn_bridge->bridge.ops = DRM_BRIDGE_OP_EDID; ptn_bridge->bridge.type = DRM_MODE_CONNECTOR_LVDS; ptn_bridge->bridge.of_node = dev->of_node; diff --git a/drivers/gpu/drm/bridge/panel.c b/drivers/gpu/drm/bridge/panel.c index 79b009ab9396..6cbbfb1381a4 100644 --- a/drivers/gpu/drm/bridge/panel.c +++ b/drivers/gpu/drm/bridge/panel.c @@ -287,15 +287,14 @@ struct drm_bridge *drm_panel_bridge_add_typed(struct drm_panel *panel, if (!panel) return ERR_PTR(-EINVAL); - panel_bridge = devm_kzalloc(panel->dev, sizeof(*panel_bridge), - GFP_KERNEL); - if (!panel_bridge) - return ERR_PTR(-ENOMEM); + panel_bridge = devm_drm_bridge_alloc(panel->dev, struct panel_bridge, bridge, + &panel_bridge_bridge_funcs); + if (IS_ERR(panel_bridge)) + return (void *)panel_bridge; panel_bridge->connector_type = connector_type; panel_bridge->panel = panel; - panel_bridge->bridge.funcs = &panel_bridge_bridge_funcs; panel_bridge->bridge.of_node = panel->dev->of_node; panel_bridge->bridge.ops = DRM_BRIDGE_OP_MODES; panel_bridge->bridge.type = connector_type; @@ -327,7 +326,8 @@ void drm_panel_bridge_remove(struct drm_bridge *bridge) panel_bridge = drm_bridge_to_panel_bridge(bridge); drm_bridge_remove(bridge); - devm_kfree(panel_bridge->panel->dev, bridge); + /* TODO remove this after reworking panel_bridge lifetime */ + devm_drm_put_bridge(panel_bridge->panel->dev, bridge); } EXPORT_SYMBOL(drm_panel_bridge_remove); diff --git a/drivers/gpu/drm/bridge/parade-ps8622.c b/drivers/gpu/drm/bridge/parade-ps8622.c index 8726fefc5c65..f879a1df077d 100644 --- a/drivers/gpu/drm/bridge/parade-ps8622.c +++ b/drivers/gpu/drm/bridge/parade-ps8622.c @@ -449,9 +449,10 @@ static int ps8622_probe(struct i2c_client *client) struct drm_bridge *panel_bridge; int ret; - ps8622 = devm_kzalloc(dev, sizeof(*ps8622), GFP_KERNEL); - if (!ps8622) - return -ENOMEM; + ps8622 = devm_drm_bridge_alloc(dev, struct ps8622_bridge, bridge, + &ps8622_bridge_funcs); + if (IS_ERR(ps8622)) + return PTR_ERR(ps8622); panel_bridge = devm_drm_of_get_bridge(dev, dev->of_node, 0, 0); if (IS_ERR(panel_bridge)) @@ -509,7 +510,6 @@ static int ps8622_probe(struct i2c_client *client) ps8622->bl->props.brightness = PS8622_MAX_BRIGHTNESS; } - ps8622->bridge.funcs = &ps8622_bridge_funcs; ps8622->bridge.type = DRM_MODE_CONNECTOR_LVDS; ps8622->bridge.of_node = dev->of_node; drm_bridge_add(&ps8622->bridge); diff --git a/drivers/gpu/drm/bridge/parade-ps8640.c b/drivers/gpu/drm/bridge/parade-ps8640.c index 2422ff68c104..825777a5758f 100644 --- a/drivers/gpu/drm/bridge/parade-ps8640.c +++ b/drivers/gpu/drm/bridge/parade-ps8640.c @@ -636,9 +636,10 @@ static int ps8640_probe(struct i2c_client *client) int ret; u32 i; - ps_bridge = devm_kzalloc(dev, sizeof(*ps_bridge), GFP_KERNEL); - if (!ps_bridge) - return -ENOMEM; + ps_bridge = devm_drm_bridge_alloc(dev, struct ps8640, bridge, + &ps8640_bridge_funcs); + if (IS_ERR(ps_bridge)) + return PTR_ERR(ps_bridge); mutex_init(&ps_bridge->aux_lock); @@ -662,7 +663,6 @@ static int ps8640_probe(struct i2c_client *client) if (IS_ERR(ps_bridge->gpio_reset)) return PTR_ERR(ps_bridge->gpio_reset); - ps_bridge->bridge.funcs = &ps8640_bridge_funcs; ps_bridge->bridge.of_node = dev->of_node; ps_bridge->bridge.type = DRM_MODE_CONNECTOR_eDP; diff --git a/drivers/gpu/drm/bridge/sii902x.c b/drivers/gpu/drm/bridge/sii902x.c index 6de61d9fe064..882973e90062 100644 --- a/drivers/gpu/drm/bridge/sii902x.c +++ b/drivers/gpu/drm/bridge/sii902x.c @@ -1135,7 +1135,6 @@ static int sii902x_init(struct sii902x *sii902x) if (ret) goto err_unreg_audio; - sii902x->bridge.funcs = &sii902x_bridge_funcs; sii902x->bridge.of_node = dev->of_node; sii902x->bridge.timings = &default_sii902x_timings; sii902x->bridge.ops = DRM_BRIDGE_OP_DETECT | DRM_BRIDGE_OP_EDID; @@ -1170,9 +1169,9 @@ static int sii902x_probe(struct i2c_client *client) return -EIO; } - sii902x = devm_kzalloc(dev, sizeof(*sii902x), GFP_KERNEL); - if (!sii902x) - return -ENOMEM; + sii902x = devm_drm_bridge_alloc(dev, struct sii902x, bridge, &sii902x_bridge_funcs); + if (IS_ERR(sii902x)) + return PTR_ERR(sii902x); sii902x->i2c = client; sii902x->regmap = devm_regmap_init_i2c(client, &sii902x_regmap_config); diff --git a/drivers/gpu/drm/bridge/sii9234.c b/drivers/gpu/drm/bridge/sii9234.c index cd7837c9a6e0..bb1bed03eb5b 100644 --- a/drivers/gpu/drm/bridge/sii9234.c +++ b/drivers/gpu/drm/bridge/sii9234.c @@ -888,9 +888,10 @@ static int sii9234_probe(struct i2c_client *client) struct device *dev = &client->dev; int ret; - ctx = devm_kzalloc(dev, sizeof(*ctx), GFP_KERNEL); - if (!ctx) - return -ENOMEM; + ctx = devm_drm_bridge_alloc(dev, struct sii9234, bridge, + &sii9234_bridge_funcs); + if (IS_ERR(ctx)) + return PTR_ERR(ctx); ctx->dev = dev; mutex_init(&ctx->lock); @@ -921,7 +922,6 @@ static int sii9234_probe(struct i2c_client *client) i2c_set_clientdata(client, ctx); - ctx->bridge.funcs = &sii9234_bridge_funcs; ctx->bridge.of_node = dev->of_node; drm_bridge_add(&ctx->bridge); diff --git a/drivers/gpu/drm/bridge/sil-sii8620.c b/drivers/gpu/drm/bridge/sil-sii8620.c index 3af650dc92a1..9e48ad39e1cc 100644 --- a/drivers/gpu/drm/bridge/sil-sii8620.c +++ b/drivers/gpu/drm/bridge/sil-sii8620.c @@ -2291,9 +2291,10 @@ static int sii8620_probe(struct i2c_client *client) struct sii8620 *ctx; int ret; - ctx = devm_kzalloc(dev, sizeof(*ctx), GFP_KERNEL); - if (!ctx) - return -ENOMEM; + ctx = devm_drm_bridge_alloc(dev, struct sii8620, bridge, + &sii8620_bridge_funcs); + if (IS_ERR(ctx)) + return PTR_ERR(ctx); ctx->dev = dev; mutex_init(&ctx->lock); @@ -2336,7 +2337,6 @@ static int sii8620_probe(struct i2c_client *client) i2c_set_clientdata(client, ctx); - ctx->bridge.funcs = &sii8620_bridge_funcs; ctx->bridge.of_node = dev->of_node; drm_bridge_add(&ctx->bridge); diff --git a/drivers/gpu/drm/bridge/simple-bridge.c b/drivers/gpu/drm/bridge/simple-bridge.c index 70db5b99e5bb..c66bd913e33a 100644 --- a/drivers/gpu/drm/bridge/simple-bridge.c +++ b/drivers/gpu/drm/bridge/simple-bridge.c @@ -168,9 +168,10 @@ static int simple_bridge_probe(struct platform_device *pdev) struct simple_bridge *sbridge; struct device_node *remote; - sbridge = devm_kzalloc(&pdev->dev, sizeof(*sbridge), GFP_KERNEL); - if (!sbridge) - return -ENOMEM; + sbridge = devm_drm_bridge_alloc(&pdev->dev, struct simple_bridge, + bridge, &simple_bridge_bridge_funcs); + if (IS_ERR(sbridge)) + return PTR_ERR(sbridge); sbridge->info = of_device_get_match_data(&pdev->dev); @@ -204,7 +205,6 @@ static int simple_bridge_probe(struct platform_device *pdev) "Unable to retrieve enable GPIO\n"); /* Register the bridge. */ - sbridge->bridge.funcs = &simple_bridge_bridge_funcs; sbridge->bridge.of_node = pdev->dev.of_node; sbridge->bridge.timings = sbridge->info->timings; diff --git a/drivers/gpu/drm/bridge/synopsys/dw-hdmi-qp.c b/drivers/gpu/drm/bridge/synopsys/dw-hdmi-qp.c index 5e5f8c2f95be..94dddaf49b3c 100644 --- a/drivers/gpu/drm/bridge/synopsys/dw-hdmi-qp.c +++ b/drivers/gpu/drm/bridge/synopsys/dw-hdmi-qp.c @@ -1045,9 +1045,10 @@ struct dw_hdmi_qp *dw_hdmi_qp_bind(struct platform_device *pdev, return ERR_PTR(-ENODEV); } - hdmi = devm_kzalloc(dev, sizeof(*hdmi), GFP_KERNEL); - if (!hdmi) - return ERR_PTR(-ENOMEM); + hdmi = devm_drm_bridge_alloc(dev, struct dw_hdmi_qp, bridge, + &dw_hdmi_qp_bridge_funcs); + if (IS_ERR(hdmi)) + return ERR_CAST(hdmi); hdmi->dev = dev; @@ -1073,7 +1074,6 @@ struct dw_hdmi_qp *dw_hdmi_qp_bind(struct platform_device *pdev, return ERR_PTR(ret); hdmi->bridge.driver_private = hdmi; - hdmi->bridge.funcs = &dw_hdmi_qp_bridge_funcs; hdmi->bridge.ops = DRM_BRIDGE_OP_DETECT | DRM_BRIDGE_OP_EDID | DRM_BRIDGE_OP_HDMI | diff --git a/drivers/gpu/drm/bridge/synopsys/dw-mipi-dsi.c b/drivers/gpu/drm/bridge/synopsys/dw-mipi-dsi.c index b08ada920a50..c0dc0f2976b9 100644 --- a/drivers/gpu/drm/bridge/synopsys/dw-mipi-dsi.c +++ b/drivers/gpu/drm/bridge/synopsys/dw-mipi-dsi.c @@ -1194,9 +1194,10 @@ __dw_mipi_dsi_probe(struct platform_device *pdev, struct dw_mipi_dsi *dsi; int ret; - dsi = devm_kzalloc(dev, sizeof(*dsi), GFP_KERNEL); - if (!dsi) - return ERR_PTR(-ENOMEM); + dsi = devm_drm_bridge_alloc(dev, struct dw_mipi_dsi, bridge, + &dw_mipi_dsi_bridge_funcs); + if (IS_ERR(dsi)) + return ERR_CAST(dsi); dsi->dev = dev; dsi->plat_data = plat_data; @@ -1265,7 +1266,6 @@ __dw_mipi_dsi_probe(struct platform_device *pdev, } dsi->bridge.driver_private = dsi; - dsi->bridge.funcs = &dw_mipi_dsi_bridge_funcs; dsi->bridge.of_node = pdev->dev.of_node; return dsi; diff --git a/drivers/gpu/drm/bridge/synopsys/dw-mipi-dsi2.c b/drivers/gpu/drm/bridge/synopsys/dw-mipi-dsi2.c index c76f5f2e74d1..fc91aca95d12 100644 --- a/drivers/gpu/drm/bridge/synopsys/dw-mipi-dsi2.c +++ b/drivers/gpu/drm/bridge/synopsys/dw-mipi-dsi2.c @@ -914,9 +914,10 @@ __dw_mipi_dsi2_probe(struct platform_device *pdev, struct dw_mipi_dsi2 *dsi2; int ret; - dsi2 = devm_kzalloc(dev, sizeof(*dsi2), GFP_KERNEL); - if (!dsi2) - return ERR_PTR(-ENOMEM); + dsi2 = devm_drm_bridge_alloc(dev, struct dw_mipi_dsi2, bridge, + &dw_mipi_dsi2_bridge_funcs); + if (IS_ERR(dsi2)) + return ERR_CAST(dsi2); dsi2->dev = dev; dsi2->plat_data = plat_data; @@ -981,7 +982,6 @@ __dw_mipi_dsi2_probe(struct platform_device *pdev, } dsi2->bridge.driver_private = dsi2; - dsi2->bridge.funcs = &dw_mipi_dsi2_bridge_funcs; dsi2->bridge.of_node = pdev->dev.of_node; return dsi2; diff --git a/drivers/gpu/drm/bridge/tc358762.c b/drivers/gpu/drm/bridge/tc358762.c index edf01476f2ef..98df3e667d4a 100644 --- a/drivers/gpu/drm/bridge/tc358762.c +++ b/drivers/gpu/drm/bridge/tc358762.c @@ -265,9 +265,10 @@ static int tc358762_probe(struct mipi_dsi_device *dsi) struct tc358762 *ctx; int ret; - ctx = devm_kzalloc(dev, sizeof(struct tc358762), GFP_KERNEL); - if (!ctx) - return -ENOMEM; + ctx = devm_drm_bridge_alloc(dev, struct tc358762, bridge, + &tc358762_bridge_funcs); + if (IS_ERR(ctx)) + return PTR_ERR(ctx); mipi_dsi_set_drvdata(dsi, ctx); @@ -288,7 +289,6 @@ static int tc358762_probe(struct mipi_dsi_device *dsi) if (ret < 0) return ret; - ctx->bridge.funcs = &tc358762_bridge_funcs; ctx->bridge.type = DRM_MODE_CONNECTOR_DPI; ctx->bridge.of_node = dev->of_node; ctx->bridge.pre_enable_prev_first = true; diff --git a/drivers/gpu/drm/bridge/tc358764.c b/drivers/gpu/drm/bridge/tc358764.c index 3f76c890fad9..084e9d898e22 100644 --- a/drivers/gpu/drm/bridge/tc358764.c +++ b/drivers/gpu/drm/bridge/tc358764.c @@ -347,9 +347,10 @@ static int tc358764_probe(struct mipi_dsi_device *dsi) struct tc358764 *ctx; int ret; - ctx = devm_kzalloc(dev, sizeof(struct tc358764), GFP_KERNEL); - if (!ctx) - return -ENOMEM; + ctx = devm_drm_bridge_alloc(dev, struct tc358764, bridge, + &tc358764_bridge_funcs); + if (IS_ERR(ctx)) + return PTR_ERR(ctx); mipi_dsi_set_drvdata(dsi, ctx); @@ -368,7 +369,6 @@ static int tc358764_probe(struct mipi_dsi_device *dsi) if (ret < 0) return ret; - ctx->bridge.funcs = &tc358764_bridge_funcs; ctx->bridge.of_node = dev->of_node; ctx->bridge.pre_enable_prev_first = true; diff --git a/drivers/gpu/drm/bridge/tc358768.c b/drivers/gpu/drm/bridge/tc358768.c index 063f217a17b6..fbdc44e16229 100644 --- a/drivers/gpu/drm/bridge/tc358768.c +++ b/drivers/gpu/drm/bridge/tc358768.c @@ -1287,9 +1287,10 @@ static int tc358768_i2c_probe(struct i2c_client *client) if (!np) return -ENODEV; - priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); - if (!priv) - return -ENOMEM; + priv = devm_drm_bridge_alloc(dev, struct tc358768_priv, bridge, + &tc358768_bridge_funcs); + if (IS_ERR(priv)) + return PTR_ERR(priv); dev_set_drvdata(dev, priv); priv->dev = dev; @@ -1321,7 +1322,6 @@ static int tc358768_i2c_probe(struct i2c_client *client) priv->dsi_host.dev = dev; priv->dsi_host.ops = &tc358768_dsi_host_ops; - priv->bridge.funcs = &tc358768_bridge_funcs; priv->bridge.timings = &default_tc358768_timings; priv->bridge.of_node = np; diff --git a/drivers/gpu/drm/bridge/tc358775.c b/drivers/gpu/drm/bridge/tc358775.c index 1b10e6ee1724..366b12db0e7c 100644 --- a/drivers/gpu/drm/bridge/tc358775.c +++ b/drivers/gpu/drm/bridge/tc358775.c @@ -659,9 +659,10 @@ static int tc_probe(struct i2c_client *client) struct tc_data *tc; int ret; - tc = devm_kzalloc(dev, sizeof(*tc), GFP_KERNEL); - if (!tc) - return -ENOMEM; + tc = devm_drm_bridge_alloc(dev, struct tc_data, bridge, + &tc_bridge_funcs); + if (IS_ERR(tc)) + return PTR_ERR(tc); tc->dev = dev; tc->i2c = client; @@ -701,7 +702,6 @@ static int tc_probe(struct i2c_client *client) return ret; } - tc->bridge.funcs = &tc_bridge_funcs; tc->bridge.of_node = dev->of_node; tc->bridge.pre_enable_prev_first = true; drm_bridge_add(&tc->bridge); diff --git a/drivers/gpu/drm/bridge/thc63lvd1024.c b/drivers/gpu/drm/bridge/thc63lvd1024.c index e2fc78adebcf..2cb7cd0c0608 100644 --- a/drivers/gpu/drm/bridge/thc63lvd1024.c +++ b/drivers/gpu/drm/bridge/thc63lvd1024.c @@ -181,9 +181,10 @@ static int thc63_probe(struct platform_device *pdev) struct thc63_dev *thc63; int ret; - thc63 = devm_kzalloc(&pdev->dev, sizeof(*thc63), GFP_KERNEL); - if (!thc63) - return -ENOMEM; + thc63 = devm_drm_bridge_alloc(&pdev->dev, struct thc63_dev, bridge, + &thc63_bridge_func); + if (IS_ERR(thc63)) + return PTR_ERR(thc63); thc63->dev = &pdev->dev; platform_set_drvdata(pdev, thc63); @@ -208,7 +209,6 @@ static int thc63_probe(struct platform_device *pdev) thc63->bridge.driver_private = thc63; thc63->bridge.of_node = pdev->dev.of_node; - thc63->bridge.funcs = &thc63_bridge_func; thc63->bridge.timings = &thc63->timings; drm_bridge_add(&thc63->bridge); diff --git a/drivers/gpu/drm/bridge/ti-dlpc3433.c b/drivers/gpu/drm/bridge/ti-dlpc3433.c index 47638d1c96ec..b07f7c9d5890 100644 --- a/drivers/gpu/drm/bridge/ti-dlpc3433.c +++ b/drivers/gpu/drm/bridge/ti-dlpc3433.c @@ -348,9 +348,10 @@ static int dlpc3433_probe(struct i2c_client *client) struct dlpc *dlpc; int ret; - dlpc = devm_kzalloc(dev, sizeof(*dlpc), GFP_KERNEL); - if (!dlpc) - return -ENOMEM; + dlpc = devm_drm_bridge_alloc(dev, struct dlpc, bridge, + &dlpc_bridge_funcs); + if (IS_ERR(dlpc)) + return PTR_ERR(dlpc); dlpc->dev = dev; @@ -365,7 +366,6 @@ static int dlpc3433_probe(struct i2c_client *client) dev_set_drvdata(dev, dlpc); i2c_set_clientdata(client, dlpc); - dlpc->bridge.funcs = &dlpc_bridge_funcs; dlpc->bridge.of_node = dev->of_node; drm_bridge_add(&dlpc->bridge); diff --git a/drivers/gpu/drm/bridge/ti-tdp158.c b/drivers/gpu/drm/bridge/ti-tdp158.c index cca75443f012..27053d020df7 100644 --- a/drivers/gpu/drm/bridge/ti-tdp158.c +++ b/drivers/gpu/drm/bridge/ti-tdp158.c @@ -68,9 +68,10 @@ static int tdp158_probe(struct i2c_client *client) struct tdp158 *tdp158; struct device *dev = &client->dev; - tdp158 = devm_kzalloc(dev, sizeof(*tdp158), GFP_KERNEL); - if (!tdp158) - return -ENOMEM; + tdp158 = devm_drm_bridge_alloc(dev, struct tdp158, bridge, + &tdp158_bridge_funcs); + if (IS_ERR(tdp158)) + return PTR_ERR(tdp158); tdp158->next = devm_drm_of_get_bridge(dev, dev->of_node, 1, 0); if (IS_ERR(tdp158->next)) @@ -89,7 +90,6 @@ static int tdp158_probe(struct i2c_client *client) return dev_err_probe(dev, PTR_ERR(tdp158->enable), "enable"); tdp158->bridge.of_node = dev->of_node; - tdp158->bridge.funcs = &tdp158_bridge_funcs; tdp158->bridge.driver_private = tdp158; tdp158->dev = dev; diff --git a/drivers/gpu/drm/bridge/ti-tfp410.c b/drivers/gpu/drm/bridge/ti-tfp410.c index e15d232ddbac..549e8e8edeb4 100644 --- a/drivers/gpu/drm/bridge/ti-tfp410.c +++ b/drivers/gpu/drm/bridge/ti-tfp410.c @@ -341,14 +341,14 @@ static int tfp410_init(struct device *dev, bool i2c) return -ENXIO; } - dvi = devm_kzalloc(dev, sizeof(*dvi), GFP_KERNEL); - if (!dvi) - return -ENOMEM; + dvi = devm_drm_bridge_alloc(dev, struct tfp410, bridge, + &tfp410_bridge_funcs); + if (IS_ERR(dvi)) + return PTR_ERR(dvi); dvi->dev = dev; dev_set_drvdata(dev, dvi); - dvi->bridge.funcs = &tfp410_bridge_funcs; dvi->bridge.of_node = dev->of_node; dvi->bridge.timings = &dvi->timings; dvi->bridge.type = DRM_MODE_CONNECTOR_DVID; diff --git a/drivers/gpu/drm/bridge/ti-tpd12s015.c b/drivers/gpu/drm/bridge/ti-tpd12s015.c index 1c289051a598..0919364e80d1 100644 --- a/drivers/gpu/drm/bridge/ti-tpd12s015.c +++ b/drivers/gpu/drm/bridge/ti-tpd12s015.c @@ -116,13 +116,13 @@ static int tpd12s015_probe(struct platform_device *pdev) struct gpio_desc *gpio; int ret; - tpd = devm_kzalloc(&pdev->dev, sizeof(*tpd), GFP_KERNEL); - if (!tpd) - return -ENOMEM; + tpd = devm_drm_bridge_alloc(&pdev->dev, struct tpd12s015_device, + bridge, &tpd12s015_bridge_funcs); + if (IS_ERR(tpd)) + return PTR_ERR(tpd); platform_set_drvdata(pdev, tpd); - tpd->bridge.funcs = &tpd12s015_bridge_funcs; tpd->bridge.of_node = pdev->dev.of_node; tpd->bridge.type = DRM_MODE_CONNECTOR_HDMIA; tpd->bridge.ops = DRM_BRIDGE_OP_DETECT; diff --git a/drivers/gpu/drm/display/Kconfig b/drivers/gpu/drm/display/Kconfig index 8d22b7627d41..df09cf9a8ca1 100644 --- a/drivers/gpu/drm/display/Kconfig +++ b/drivers/gpu/drm/display/Kconfig @@ -8,6 +8,7 @@ config DRM_DISPLAY_DP_AUX_BUS config DRM_DISPLAY_HELPER tristate depends on DRM + select CEC_CORE if DRM_DISPLAY_DP_AUX_CEC || DRM_DISPLAY_HDMI_CEC_HELPER || CEC_NOTIFIER help DRM helpers for display adapters. @@ -16,6 +17,7 @@ if DRM_DISPLAY_HELPER config DRM_BRIDGE_CONNECTOR bool select DRM_DISPLAY_HDMI_AUDIO_HELPER + select DRM_DISPLAY_HDMI_CEC_HELPER select DRM_DISPLAY_HDMI_STATE_HELPER help DRM connector implementation terminating DRM bridge chains. @@ -23,7 +25,6 @@ config DRM_BRIDGE_CONNECTOR config DRM_DISPLAY_DP_AUX_CEC bool "Enable DisplayPort CEC-Tunneling-over-AUX HDMI support" select DRM_DISPLAY_DP_HELPER - select CEC_CORE help Choose this option if you want to enable HDMI CEC support for DisplayPort/USB-C to HDMI adapters. @@ -82,6 +83,16 @@ config DRM_DISPLAY_HDMI_AUDIO_HELPER DRM display helpers for HDMI Audio functionality (generic HDMI Codec implementation). +config DRM_DISPLAY_HDMI_CEC_HELPER + bool + help + DRM display helpers for HDMI CEC implementation. + +config DRM_DISPLAY_HDMI_CEC_NOTIFIER_HELPER + def_bool CEC_NOTIFIER + help + DRM display helpers for HDMI CEC notifiers implementation. + config DRM_DISPLAY_HDMI_HELPER bool help diff --git a/drivers/gpu/drm/display/Makefile b/drivers/gpu/drm/display/Makefile index b17879b957d5..0ff4a1ad0222 100644 --- a/drivers/gpu/drm/display/Makefile +++ b/drivers/gpu/drm/display/Makefile @@ -16,6 +16,10 @@ drm_display_helper-$(CONFIG_DRM_DISPLAY_DSC_HELPER) += \ drm_display_helper-$(CONFIG_DRM_DISPLAY_HDCP_HELPER) += drm_hdcp_helper.o drm_display_helper-$(CONFIG_DRM_DISPLAY_HDMI_AUDIO_HELPER) += \ drm_hdmi_audio_helper.o +drm_display_helper-$(CONFIG_DRM_DISPLAY_HDMI_CEC_HELPER) += \ + drm_hdmi_cec_helper.o +drm_display_helper-$(CONFIG_DRM_DISPLAY_HDMI_CEC_NOTIFIER_HELPER) += \ + drm_hdmi_cec_notifier_helper.o drm_display_helper-$(CONFIG_DRM_DISPLAY_HDMI_HELPER) += \ drm_hdmi_helper.o \ drm_scdc_helper.o diff --git a/drivers/gpu/drm/display/drm_bridge_connector.c b/drivers/gpu/drm/display/drm_bridge_connector.c index 7d2e499ea5de..58846e26f1e1 100644 --- a/drivers/gpu/drm/display/drm_bridge_connector.c +++ b/drivers/gpu/drm/display/drm_bridge_connector.c @@ -20,6 +20,7 @@ #include <drm/drm_print.h> #include <drm/drm_probe_helper.h> #include <drm/display/drm_hdmi_audio_helper.h> +#include <drm/display/drm_hdmi_cec_helper.h> #include <drm/display/drm_hdmi_helper.h> #include <drm/display/drm_hdmi_state_helper.h> @@ -113,6 +114,13 @@ struct drm_bridge_connector { * &DRM_BRIDGE_OP_DP_AUDIO). */ struct drm_bridge *bridge_dp_audio; + /** + * @bridge_hdmi_cec: + * + * The bridge in the chain that implements CEC support, if any (see + * DRM_BRIDGE_OP_HDMI_CEC_NOTIFIER). + */ + struct drm_bridge *bridge_hdmi_cec; }; #define to_drm_bridge_connector(x) \ @@ -546,6 +554,65 @@ static const struct drm_connector_hdmi_audio_funcs drm_bridge_connector_hdmi_aud .mute_stream = drm_bridge_connector_audio_mute_stream, }; +static int drm_bridge_connector_hdmi_cec_enable(struct drm_connector *connector, bool enable) +{ + struct drm_bridge_connector *bridge_connector = + to_drm_bridge_connector(connector); + struct drm_bridge *bridge; + + bridge = bridge_connector->bridge_hdmi_cec; + + return bridge->funcs->hdmi_cec_enable(bridge, enable); +} + +static int drm_bridge_connector_hdmi_cec_log_addr(struct drm_connector *connector, u8 logical_addr) +{ + struct drm_bridge_connector *bridge_connector = + to_drm_bridge_connector(connector); + struct drm_bridge *bridge; + + bridge = bridge_connector->bridge_hdmi_cec; + + return bridge->funcs->hdmi_cec_log_addr(bridge, logical_addr); +} + +static int drm_bridge_connector_hdmi_cec_transmit(struct drm_connector *connector, + u8 attempts, + u32 signal_free_time, + struct cec_msg *msg) +{ + struct drm_bridge_connector *bridge_connector = + to_drm_bridge_connector(connector); + struct drm_bridge *bridge; + + bridge = bridge_connector->bridge_hdmi_cec; + + return bridge->funcs->hdmi_cec_transmit(bridge, attempts, + signal_free_time, + msg); +} + +static int drm_bridge_connector_hdmi_cec_init(struct drm_connector *connector) +{ + struct drm_bridge_connector *bridge_connector = + to_drm_bridge_connector(connector); + struct drm_bridge *bridge; + + bridge = bridge_connector->bridge_hdmi_cec; + + if (!bridge->funcs->hdmi_cec_init) + return 0; + + return bridge->funcs->hdmi_cec_init(connector, bridge); +} + +static const struct drm_connector_hdmi_cec_funcs drm_bridge_connector_hdmi_cec_funcs = { + .init = drm_bridge_connector_hdmi_cec_init, + .enable = drm_bridge_connector_hdmi_cec_enable, + .log_addr = drm_bridge_connector_hdmi_cec_log_addr, + .transmit = drm_bridge_connector_hdmi_cec_transmit, +}; + /* ----------------------------------------------------------------------------- * Bridge Connector Initialisation */ @@ -662,6 +729,25 @@ struct drm_connector *drm_bridge_connector_init(struct drm_device *drm, bridge_connector->bridge_dp_audio = bridge; } + if (bridge->ops & DRM_BRIDGE_OP_HDMI_CEC_NOTIFIER) { + if (bridge_connector->bridge_hdmi_cec) + return ERR_PTR(-EBUSY); + + bridge_connector->bridge_hdmi_cec = bridge; + } + + if (bridge->ops & DRM_BRIDGE_OP_HDMI_CEC_ADAPTER) { + if (bridge_connector->bridge_hdmi_cec) + return ERR_PTR(-EBUSY); + + bridge_connector->bridge_hdmi_cec = bridge; + + if (!bridge->funcs->hdmi_cec_enable || + !bridge->funcs->hdmi_cec_log_addr || + !bridge->funcs->hdmi_cec_transmit) + return ERR_PTR(-EINVAL); + } + if (!drm_bridge_get_next_bridge(bridge)) connector_type = bridge->type; @@ -717,12 +803,33 @@ struct drm_connector *drm_bridge_connector_init(struct drm_device *drm, ret = drm_connector_hdmi_audio_init(connector, dev, &drm_bridge_connector_hdmi_audio_funcs, bridge->hdmi_audio_max_i2s_playback_channels, + bridge->hdmi_audio_i2s_formats, bridge->hdmi_audio_spdif_playback, bridge->hdmi_audio_dai_port); if (ret) return ERR_PTR(ret); } + if (bridge_connector->bridge_hdmi_cec && + bridge_connector->bridge_hdmi_cec->ops & DRM_BRIDGE_OP_HDMI_CEC_NOTIFIER) { + ret = drmm_connector_hdmi_cec_notifier_register(connector, + NULL, + bridge->hdmi_cec_dev); + if (ret) + return ERR_PTR(ret); + } + + if (bridge_connector->bridge_hdmi_cec && + bridge_connector->bridge_hdmi_cec->ops & DRM_BRIDGE_OP_HDMI_CEC_ADAPTER) { + ret = drmm_connector_hdmi_cec_register(connector, + &drm_bridge_connector_hdmi_cec_funcs, + bridge->hdmi_cec_adapter_name, + bridge->hdmi_cec_available_las, + bridge->hdmi_cec_dev); + if (ret) + return ERR_PTR(ret); + } + drm_connector_helper_add(connector, &drm_bridge_connector_helper_funcs); if (bridge_connector->bridge_hpd) diff --git a/drivers/gpu/drm/display/drm_hdmi_audio_helper.c b/drivers/gpu/drm/display/drm_hdmi_audio_helper.c index ae8a0cf595fc..f165166afb2d 100644 --- a/drivers/gpu/drm/display/drm_hdmi_audio_helper.c +++ b/drivers/gpu/drm/display/drm_hdmi_audio_helper.c @@ -143,6 +143,7 @@ static const struct hdmi_codec_ops drm_connector_hdmi_audio_ops = { * @hdmi_codec_dev: device to be used as a parent for the HDMI Codec * @funcs: callbacks for this HDMI Codec * @max_i2s_playback_channels: maximum number of playback I2S channels + * @i2s_formats: set of I2S formats (use 0 for a bus-specific set) * @spdif_playback: set if HDMI codec has S/PDIF playback port * @dai_port: sound DAI port, -1 if it is not enabled * @@ -155,6 +156,7 @@ int drm_connector_hdmi_audio_init(struct drm_connector *connector, struct device *hdmi_codec_dev, const struct drm_connector_hdmi_audio_funcs *funcs, unsigned int max_i2s_playback_channels, + u64 i2s_formats, bool spdif_playback, int dai_port) { @@ -162,6 +164,7 @@ int drm_connector_hdmi_audio_init(struct drm_connector *connector, .ops = &drm_connector_hdmi_audio_ops, .max_i2s_channels = max_i2s_playback_channels, .i2s = !!max_i2s_playback_channels, + .i2s_formats = i2s_formats, .spdif = spdif_playback, .no_i2s_capture = true, .no_spdif_capture = true, diff --git a/drivers/gpu/drm/display/drm_hdmi_cec_helper.c b/drivers/gpu/drm/display/drm_hdmi_cec_helper.c new file mode 100644 index 000000000000..a25f60509043 --- /dev/null +++ b/drivers/gpu/drm/display/drm_hdmi_cec_helper.c @@ -0,0 +1,192 @@ +// SPDX-License-Identifier: MIT +/* + * Copyright (c) 2024 Linaro Ltd + */ + +#include <drm/drm_bridge.h> +#include <drm/drm_connector.h> +#include <drm/drm_managed.h> +#include <drm/display/drm_hdmi_cec_helper.h> + +#include <linux/mutex.h> + +#include <media/cec.h> + +struct drm_connector_hdmi_cec_data { + struct cec_adapter *adapter; + const struct drm_connector_hdmi_cec_funcs *funcs; +}; + +static int drm_connector_hdmi_cec_adap_enable(struct cec_adapter *adap, bool enable) +{ + struct drm_connector *connector = cec_get_drvdata(adap); + struct drm_connector_hdmi_cec_data *data = connector->cec.data; + + return data->funcs->enable(connector, enable); +} + +static int drm_connector_hdmi_cec_adap_log_addr(struct cec_adapter *adap, u8 logical_addr) +{ + struct drm_connector *connector = cec_get_drvdata(adap); + struct drm_connector_hdmi_cec_data *data = connector->cec.data; + + return data->funcs->log_addr(connector, logical_addr); +} + +static int drm_connector_hdmi_cec_adap_transmit(struct cec_adapter *adap, u8 attempts, + u32 signal_free_time, struct cec_msg *msg) +{ + struct drm_connector *connector = cec_get_drvdata(adap); + struct drm_connector_hdmi_cec_data *data = connector->cec.data; + + return data->funcs->transmit(connector, attempts, signal_free_time, msg); +} + +static const struct cec_adap_ops drm_connector_hdmi_cec_adap_ops = { + .adap_enable = drm_connector_hdmi_cec_adap_enable, + .adap_log_addr = drm_connector_hdmi_cec_adap_log_addr, + .adap_transmit = drm_connector_hdmi_cec_adap_transmit, +}; + +static void drm_connector_hdmi_cec_adapter_phys_addr_invalidate(struct drm_connector *connector) +{ + struct drm_connector_hdmi_cec_data *data = connector->cec.data; + + cec_phys_addr_invalidate(data->adapter); +} + +static void drm_connector_hdmi_cec_adapter_phys_addr_set(struct drm_connector *connector, + u16 addr) +{ + struct drm_connector_hdmi_cec_data *data = connector->cec.data; + + cec_s_phys_addr(data->adapter, addr, false); +} + +static void drm_connector_hdmi_cec_adapter_unregister(struct drm_device *dev, void *res) +{ + struct drm_connector *connector = res; + struct drm_connector_hdmi_cec_data *data = connector->cec.data; + + cec_delete_adapter(data->adapter); + + if (data->funcs->uninit) + data->funcs->uninit(connector); + + kfree(data); + connector->cec.data = NULL; +} + +static struct drm_connector_cec_funcs drm_connector_hdmi_cec_adapter_funcs = { + .phys_addr_invalidate = drm_connector_hdmi_cec_adapter_phys_addr_invalidate, + .phys_addr_set = drm_connector_hdmi_cec_adapter_phys_addr_set, +}; + +int drmm_connector_hdmi_cec_register(struct drm_connector *connector, + const struct drm_connector_hdmi_cec_funcs *funcs, + const char *name, + u8 available_las, + struct device *dev) +{ + struct drm_connector_hdmi_cec_data *data; + struct cec_connector_info conn_info; + struct cec_adapter *cec_adap; + int ret; + + if (!funcs->init || !funcs->enable || !funcs->log_addr || !funcs->transmit) + return -EINVAL; + + data = kzalloc(sizeof(*data), GFP_KERNEL); + if (!data) + return -ENOMEM; + + data->funcs = funcs; + + cec_adap = cec_allocate_adapter(&drm_connector_hdmi_cec_adap_ops, connector, name, + CEC_CAP_DEFAULTS | CEC_CAP_CONNECTOR_INFO, + available_las ? : CEC_MAX_LOG_ADDRS); + ret = PTR_ERR_OR_ZERO(cec_adap); + if (ret < 0) + goto err_free; + + cec_fill_conn_info_from_drm(&conn_info, connector); + cec_s_conn_info(cec_adap, &conn_info); + + data->adapter = cec_adap; + + mutex_lock(&connector->cec.mutex); + + connector->cec.data = data; + connector->cec.funcs = &drm_connector_hdmi_cec_adapter_funcs; + + ret = funcs->init(connector); + if (ret < 0) + goto err_delete_adapter; + + /* + * NOTE: the CEC adapter will be unregistered by drmm cleanup from + * drm_managed_release(), which is called from drm_dev_release() + * during device unbind. + * + * However, the CEC framework cleans up the CEC adapter only when the + * last user has closed its file descriptor, so we don't need to handle + * it in DRM. + * + * Before that CEC framework makes sure that even if the userspace + * still holds CEC device open, all calls will be shortcut via + * cec_is_registered(), making sure that there is no access to the + * freed memory. + */ + ret = cec_register_adapter(cec_adap, dev); + if (ret < 0) + goto err_delete_adapter; + + mutex_unlock(&connector->cec.mutex); + + return drmm_add_action_or_reset(connector->dev, + drm_connector_hdmi_cec_adapter_unregister, + connector); + +err_delete_adapter: + cec_delete_adapter(cec_adap); + + connector->cec.data = NULL; + + mutex_unlock(&connector->cec.mutex); + +err_free: + kfree(data); + + return ret; +} +EXPORT_SYMBOL(drmm_connector_hdmi_cec_register); + +void drm_connector_hdmi_cec_received_msg(struct drm_connector *connector, + struct cec_msg *msg) +{ + struct drm_connector_hdmi_cec_data *data = connector->cec.data; + + cec_received_msg(data->adapter, msg); +} +EXPORT_SYMBOL(drm_connector_hdmi_cec_received_msg); + +void drm_connector_hdmi_cec_transmit_attempt_done(struct drm_connector *connector, + u8 status) +{ + struct drm_connector_hdmi_cec_data *data = connector->cec.data; + + cec_transmit_attempt_done(data->adapter, status); +} +EXPORT_SYMBOL(drm_connector_hdmi_cec_transmit_attempt_done); + +void drm_connector_hdmi_cec_transmit_done(struct drm_connector *connector, + u8 status, + u8 arb_lost_cnt, u8 nack_cnt, + u8 low_drive_cnt, u8 error_cnt) +{ + struct drm_connector_hdmi_cec_data *data = connector->cec.data; + + cec_transmit_done(data->adapter, status, + arb_lost_cnt, nack_cnt, low_drive_cnt, error_cnt); +} +EXPORT_SYMBOL(drm_connector_hdmi_cec_transmit_done); diff --git a/drivers/gpu/drm/display/drm_hdmi_cec_notifier_helper.c b/drivers/gpu/drm/display/drm_hdmi_cec_notifier_helper.c new file mode 100644 index 000000000000..28f8e008cc59 --- /dev/null +++ b/drivers/gpu/drm/display/drm_hdmi_cec_notifier_helper.c @@ -0,0 +1,64 @@ +// SPDX-License-Identifier: MIT +/* + * Copyright (c) 2024 Linaro Ltd + */ + +#include <drm/drm_bridge.h> +#include <drm/drm_connector.h> +#include <drm/drm_managed.h> +#include <drm/display/drm_hdmi_cec_helper.h> + +#include <linux/mutex.h> + +#include <media/cec.h> +#include <media/cec-notifier.h> + +static void drm_connector_hdmi_cec_notifier_phys_addr_invalidate(struct drm_connector *connector) +{ + cec_notifier_phys_addr_invalidate(connector->cec.data); +} + +static void drm_connector_hdmi_cec_notifier_phys_addr_set(struct drm_connector *connector, + u16 addr) +{ + cec_notifier_set_phys_addr(connector->cec.data, addr); +} + +static void drm_connector_hdmi_cec_notifier_unregister(struct drm_device *dev, void *res) +{ + struct drm_connector *connector = res; + + cec_notifier_conn_unregister(connector->cec.data); + connector->cec.data = NULL; +} + +static const struct drm_connector_cec_funcs drm_connector_cec_notifier_funcs = { + .phys_addr_invalidate = drm_connector_hdmi_cec_notifier_phys_addr_invalidate, + .phys_addr_set = drm_connector_hdmi_cec_notifier_phys_addr_set, +}; + +int drmm_connector_hdmi_cec_notifier_register(struct drm_connector *connector, + const char *port_name, + struct device *dev) +{ + struct cec_connector_info conn_info; + struct cec_notifier *notifier; + + cec_fill_conn_info_from_drm(&conn_info, connector); + + notifier = cec_notifier_conn_register(dev, port_name, &conn_info); + if (!notifier) + return -ENOMEM; + + mutex_lock(&connector->cec.mutex); + + connector->cec.data = notifier; + connector->cec.funcs = &drm_connector_cec_notifier_funcs; + + mutex_unlock(&connector->cec.mutex); + + return drmm_add_action_or_reset(connector->dev, + drm_connector_hdmi_cec_notifier_unregister, + connector); +} +EXPORT_SYMBOL(drmm_connector_hdmi_cec_notifier_register); diff --git a/drivers/gpu/drm/display/drm_hdmi_state_helper.c b/drivers/gpu/drm/display/drm_hdmi_state_helper.c index d9d9948b29e9..2c641add7434 100644 --- a/drivers/gpu/drm/display/drm_hdmi_state_helper.c +++ b/drivers/gpu/drm/display/drm_hdmi_state_helper.c @@ -3,9 +3,11 @@ #include <drm/drm_atomic.h> #include <drm/drm_connector.h> #include <drm/drm_edid.h> +#include <drm/drm_modes.h> #include <drm/drm_print.h> #include <drm/display/drm_hdmi_audio_helper.h> +#include <drm/display/drm_hdmi_cec_helper.h> #include <drm/display/drm_hdmi_helper.h> #include <drm/display/drm_hdmi_state_helper.h> @@ -407,6 +409,11 @@ sink_supports_format_bpc(const struct drm_connector *connector, return false; } + if (drm_mode_is_420_only(info, mode) && format != HDMI_COLORSPACE_YUV420) { + drm_dbg_kms(dev, "Mode can be only supported in YUV420 format.\n"); + return false; + } + switch (format) { case HDMI_COLORSPACE_RGB: drm_dbg_kms(dev, "RGB Format, checking the constraints.\n"); @@ -437,9 +444,36 @@ sink_supports_format_bpc(const struct drm_connector *connector, return true; case HDMI_COLORSPACE_YUV420: - /* TODO: YUV420 is unsupported at the moment. */ - drm_dbg_kms(dev, "YUV420 format isn't supported yet.\n"); - return false; + drm_dbg_kms(dev, "YUV420 format, checking the constraints.\n"); + + if (!(info->color_formats & DRM_COLOR_FORMAT_YCBCR420)) { + drm_dbg_kms(dev, "Sink doesn't support YUV420.\n"); + return false; + } + + if (!drm_mode_is_420(info, mode)) { + drm_dbg_kms(dev, "Mode cannot be supported in YUV420 format.\n"); + return false; + } + + if (bpc == 10 && !(info->hdmi.y420_dc_modes & DRM_EDID_YCBCR420_DC_30)) { + drm_dbg_kms(dev, "10 BPC but sink doesn't support Deep Color 30.\n"); + return false; + } + + if (bpc == 12 && !(info->hdmi.y420_dc_modes & DRM_EDID_YCBCR420_DC_36)) { + drm_dbg_kms(dev, "12 BPC but sink doesn't support Deep Color 36.\n"); + return false; + } + + if (bpc == 16 && !(info->hdmi.y420_dc_modes & DRM_EDID_YCBCR420_DC_48)) { + drm_dbg_kms(dev, "16 BPC but sink doesn't support Deep Color 48.\n"); + return false; + } + + drm_dbg_kms(dev, "YUV420 format supported in that configuration.\n"); + + return true; case HDMI_COLORSPACE_YUV422: drm_dbg_kms(dev, "YUV422 format, checking the constraints.\n"); @@ -545,8 +579,9 @@ hdmi_try_format_bpc(const struct drm_connector *connector, struct drm_device *dev = connector->dev; int ret; - drm_dbg_kms(dev, "Trying %s output format\n", - drm_hdmi_connector_get_output_format_name(fmt)); + drm_dbg_kms(dev, "Trying %s output format with %u bpc\n", + drm_hdmi_connector_get_output_format_name(fmt), + bpc); if (!sink_supports_format_bpc(connector, info, mode, fmt, bpc)) { drm_dbg_kms(dev, "%s output format not supported with %u bpc\n", @@ -563,7 +598,7 @@ hdmi_try_format_bpc(const struct drm_connector *connector, return false; } - drm_dbg_kms(dev, "%s output format supported with %u (TMDS char rate: %llu Hz)\n", + drm_dbg_kms(dev, "%s output format supported with %u bpc (TMDS char rate: %llu Hz)\n", drm_hdmi_connector_get_output_format_name(fmt), bpc, conn_state->hdmi.tmds_char_rate); @@ -571,23 +606,35 @@ hdmi_try_format_bpc(const struct drm_connector *connector, } static int -hdmi_compute_format(const struct drm_connector *connector, - struct drm_connector_state *conn_state, - const struct drm_display_mode *mode, - unsigned int bpc) +hdmi_compute_format_bpc(const struct drm_connector *connector, + struct drm_connector_state *conn_state, + const struct drm_display_mode *mode, + unsigned int max_bpc, enum hdmi_colorspace fmt) { struct drm_device *dev = connector->dev; + unsigned int bpc; + int ret; + + for (bpc = max_bpc; bpc >= 8; bpc -= 2) { + ret = hdmi_try_format_bpc(connector, conn_state, mode, bpc, fmt); + if (!ret) + continue; + + conn_state->hdmi.output_bpc = bpc; + conn_state->hdmi.output_format = fmt; + + drm_dbg_kms(dev, + "Mode %ux%u @ %uHz: Found configuration: bpc: %u, fmt: %s, clock: %llu\n", + mode->hdisplay, mode->vdisplay, drm_mode_vrefresh(mode), + conn_state->hdmi.output_bpc, + drm_hdmi_connector_get_output_format_name(conn_state->hdmi.output_format), + conn_state->hdmi.tmds_char_rate); - /* - * TODO: Add support for YCbCr420 output for HDMI 2.0 capable - * devices, for modes that only support YCbCr420. - */ - if (hdmi_try_format_bpc(connector, conn_state, mode, bpc, HDMI_COLORSPACE_RGB)) { - conn_state->hdmi.output_format = HDMI_COLORSPACE_RGB; return 0; } - drm_dbg_kms(dev, "Failed. No Format Supported for that bpc count.\n"); + drm_dbg_kms(dev, "Failed. %s output format not supported for any bpc count.\n", + drm_hdmi_connector_get_output_format_name(fmt)); return -EINVAL; } @@ -597,33 +644,29 @@ hdmi_compute_config(const struct drm_connector *connector, struct drm_connector_state *conn_state, const struct drm_display_mode *mode) { - struct drm_device *dev = connector->dev; unsigned int max_bpc = clamp_t(unsigned int, conn_state->max_bpc, 8, connector->max_bpc); - unsigned int bpc; int ret; - for (bpc = max_bpc; bpc >= 8; bpc -= 2) { - drm_dbg_kms(dev, "Trying with a %d bpc output\n", bpc); - - ret = hdmi_compute_format(connector, conn_state, mode, bpc); - if (ret) - continue; - - conn_state->hdmi.output_bpc = bpc; - - drm_dbg_kms(dev, - "Mode %ux%u @ %uHz: Found configuration: bpc: %u, fmt: %s, clock: %llu\n", - mode->hdisplay, mode->vdisplay, drm_mode_vrefresh(mode), - conn_state->hdmi.output_bpc, - drm_hdmi_connector_get_output_format_name(conn_state->hdmi.output_format), - conn_state->hdmi.tmds_char_rate); - - return 0; + ret = hdmi_compute_format_bpc(connector, conn_state, mode, max_bpc, + HDMI_COLORSPACE_RGB); + if (ret) { + if (connector->ycbcr_420_allowed) { + ret = hdmi_compute_format_bpc(connector, conn_state, + mode, max_bpc, + HDMI_COLORSPACE_YUV420); + if (ret) + drm_dbg_kms(connector->dev, + "YUV420 output format doesn't work.\n"); + } else { + drm_dbg_kms(connector->dev, + "YUV420 output format not allowed for connector.\n"); + ret = -EINVAL; + } } - return -EINVAL; + return ret; } static int hdmi_generate_avi_infoframe(const struct drm_connector *connector, @@ -798,12 +841,12 @@ int drm_atomic_helper_connector_hdmi_check(struct drm_connector *connector, if (!new_conn_state->crtc || !new_conn_state->best_encoder) return 0; - new_conn_state->hdmi.is_limited_range = hdmi_is_limited_range(connector, new_conn_state); - ret = hdmi_compute_config(connector, new_conn_state, mode); if (ret) return ret; + new_conn_state->hdmi.is_limited_range = hdmi_is_limited_range(connector, new_conn_state); + ret = hdmi_generate_infoframes(connector, new_conn_state); if (ret) return ret; @@ -1081,9 +1124,10 @@ drm_atomic_helper_connector_hdmi_update(struct drm_connector *connector, const struct drm_edid *drm_edid; if (status == connector_status_disconnected) { - // TODO: also handle CEC and scramber, HDMI sink disconnected. + // TODO: also handle scramber, HDMI sink disconnected. drm_connector_hdmi_audio_plugged_notify(connector, false); drm_edid_connector_update(connector, NULL); + drm_connector_cec_phys_addr_invalidate(connector); return; } @@ -1097,8 +1141,9 @@ drm_atomic_helper_connector_hdmi_update(struct drm_connector *connector, drm_edid_free(drm_edid); if (status == connector_status_connected) { - // TODO: also handle CEC and scramber, HDMI sink is now connected. + // TODO: also handle scramber, HDMI sink is now connected. drm_connector_hdmi_audio_plugged_notify(connector, true); + drm_connector_cec_phys_addr_set(connector); } } diff --git a/drivers/gpu/drm/drm_atomic_helper.c b/drivers/gpu/drm/drm_atomic_helper.c index ee64ca1b1bec..2fe6c91910a1 100644 --- a/drivers/gpu/drm/drm_atomic_helper.c +++ b/drivers/gpu/drm/drm_atomic_helper.c @@ -1160,11 +1160,10 @@ crtc_needs_disable(struct drm_crtc_state *old_state, } static void -disable_outputs(struct drm_device *dev, struct drm_atomic_state *state) +encoder_bridge_disable(struct drm_device *dev, struct drm_atomic_state *state) { struct drm_connector *connector; struct drm_connector_state *old_conn_state, *new_conn_state; - struct drm_crtc *crtc; struct drm_crtc_state *old_crtc_state, *new_crtc_state; int i; @@ -1224,9 +1223,15 @@ disable_outputs(struct drm_device *dev, struct drm_atomic_state *state) else if (funcs->dpms) funcs->dpms(encoder, DRM_MODE_DPMS_OFF); } - - drm_atomic_bridge_chain_post_disable(bridge, state); } +} + +static void +crtc_disable(struct drm_device *dev, struct drm_atomic_state *state) +{ + struct drm_crtc *crtc; + struct drm_crtc_state *old_crtc_state, *new_crtc_state; + int i; for_each_oldnew_crtc_in_state(state, crtc, old_crtc_state, new_crtc_state, i) { const struct drm_crtc_helper_funcs *funcs; @@ -1274,6 +1279,68 @@ disable_outputs(struct drm_device *dev, struct drm_atomic_state *state) } } +static void +encoder_bridge_post_disable(struct drm_device *dev, struct drm_atomic_state *state) +{ + struct drm_connector *connector; + struct drm_connector_state *old_conn_state, *new_conn_state; + struct drm_crtc_state *old_crtc_state, *new_crtc_state; + int i; + + for_each_oldnew_connector_in_state(state, connector, old_conn_state, new_conn_state, i) { + struct drm_encoder *encoder; + struct drm_bridge *bridge; + + /* + * Shut down everything that's in the changeset and currently + * still on. So need to check the old, saved state. + */ + if (!old_conn_state->crtc) + continue; + + old_crtc_state = drm_atomic_get_old_crtc_state(state, old_conn_state->crtc); + + if (new_conn_state->crtc) + new_crtc_state = drm_atomic_get_new_crtc_state(state, + new_conn_state->crtc); + else + new_crtc_state = NULL; + + if (!crtc_needs_disable(old_crtc_state, new_crtc_state) || + !drm_atomic_crtc_needs_modeset(old_conn_state->crtc->state)) + continue; + + encoder = old_conn_state->best_encoder; + + /* + * We shouldn't get this far if we didn't previously have + * an encoder.. but WARN_ON() rather than explode. + */ + if (WARN_ON(!encoder)) + continue; + + drm_dbg_atomic(dev, "post-disabling bridges [ENCODER:%d:%s]\n", + encoder->base.id, encoder->name); + + /* + * Each encoder has at most one connector (since we always steal + * it away), so we won't call disable hooks twice. + */ + bridge = drm_bridge_chain_get_first_bridge(encoder); + drm_atomic_bridge_chain_post_disable(bridge, state); + } +} + +static void +disable_outputs(struct drm_device *dev, struct drm_atomic_state *state) +{ + encoder_bridge_disable(dev, state); + + crtc_disable(dev, state); + + encoder_bridge_post_disable(dev, state); +} + /** * drm_atomic_helper_update_legacy_modeset_state - update legacy modeset state * @dev: DRM device @@ -1483,28 +1550,44 @@ static void drm_atomic_helper_commit_writebacks(struct drm_device *dev, } } -/** - * drm_atomic_helper_commit_modeset_enables - modeset commit to enable outputs - * @dev: DRM device - * @state: atomic state object being committed - * - * This function enables all the outputs with the new configuration which had to - * be turned off for the update. - * - * For compatibility with legacy CRTC helpers this should be called after - * drm_atomic_helper_commit_planes(), which is what the default commit function - * does. But drivers with different needs can group the modeset commits together - * and do the plane commits at the end. This is useful for drivers doing runtime - * PM since planes updates then only happen when the CRTC is actually enabled. - */ -void drm_atomic_helper_commit_modeset_enables(struct drm_device *dev, - struct drm_atomic_state *state) +static void +encoder_bridge_pre_enable(struct drm_device *dev, struct drm_atomic_state *state) +{ + struct drm_connector *connector; + struct drm_connector_state *new_conn_state; + int i; + + for_each_new_connector_in_state(state, connector, new_conn_state, i) { + struct drm_encoder *encoder; + struct drm_bridge *bridge; + + if (!new_conn_state->best_encoder) + continue; + + if (!new_conn_state->crtc->state->active || + !drm_atomic_crtc_needs_modeset(new_conn_state->crtc->state)) + continue; + + encoder = new_conn_state->best_encoder; + + drm_dbg_atomic(dev, "pre-enabling bridges [ENCODER:%d:%s]\n", + encoder->base.id, encoder->name); + + /* + * Each encoder has at most one connector (since we always steal + * it away), so we won't call enable hooks twice. + */ + bridge = drm_bridge_chain_get_first_bridge(encoder); + drm_atomic_bridge_chain_pre_enable(bridge, state); + } +} + +static void +crtc_enable(struct drm_device *dev, struct drm_atomic_state *state) { struct drm_crtc *crtc; struct drm_crtc_state *old_crtc_state; struct drm_crtc_state *new_crtc_state; - struct drm_connector *connector; - struct drm_connector_state *new_conn_state; int i; for_each_oldnew_crtc_in_state(state, crtc, old_crtc_state, new_crtc_state, i) { @@ -1528,6 +1611,14 @@ void drm_atomic_helper_commit_modeset_enables(struct drm_device *dev, funcs->commit(crtc); } } +} + +static void +encoder_bridge_enable(struct drm_device *dev, struct drm_atomic_state *state) +{ + struct drm_connector *connector; + struct drm_connector_state *new_conn_state; + int i; for_each_new_connector_in_state(state, connector, new_conn_state, i) { const struct drm_encoder_helper_funcs *funcs; @@ -1552,7 +1643,6 @@ void drm_atomic_helper_commit_modeset_enables(struct drm_device *dev, * it away), so we won't call enable hooks twice. */ bridge = drm_bridge_chain_get_first_bridge(encoder); - drm_atomic_bridge_chain_pre_enable(bridge, state); if (funcs) { if (funcs->atomic_enable) @@ -1565,6 +1655,30 @@ void drm_atomic_helper_commit_modeset_enables(struct drm_device *dev, drm_atomic_bridge_chain_enable(bridge, state); } +} + +/** + * drm_atomic_helper_commit_modeset_enables - modeset commit to enable outputs + * @dev: DRM device + * @state: atomic state object being committed + * + * This function enables all the outputs with the new configuration which had to + * be turned off for the update. + * + * For compatibility with legacy CRTC helpers this should be called after + * drm_atomic_helper_commit_planes(), which is what the default commit function + * does. But drivers with different needs can group the modeset commits together + * and do the plane commits at the end. This is useful for drivers doing runtime + * PM since planes updates then only happen when the CRTC is actually enabled. + */ +void drm_atomic_helper_commit_modeset_enables(struct drm_device *dev, + struct drm_atomic_state *state) +{ + encoder_bridge_pre_enable(dev, state); + + crtc_enable(dev, state); + + encoder_bridge_enable(dev, state); drm_atomic_helper_commit_writebacks(dev, state); } diff --git a/drivers/gpu/drm/drm_auth.c b/drivers/gpu/drm/drm_auth.c index 22aa015df387..d6bf605b4b90 100644 --- a/drivers/gpu/drm/drm_auth.c +++ b/drivers/gpu/drm/drm_auth.c @@ -95,7 +95,7 @@ int drm_getmagic(struct drm_device *dev, void *data, struct drm_file *file_priv) struct drm_auth *auth = data; int ret = 0; - mutex_lock(&dev->master_mutex); + guard(mutex)(&dev->master_mutex); if (!file_priv->magic) { ret = idr_alloc(&file_priv->master->magic_map, file_priv, 1, 0, GFP_KERNEL); @@ -103,7 +103,6 @@ int drm_getmagic(struct drm_device *dev, void *data, struct drm_file *file_priv) file_priv->magic = ret; } auth->magic = file_priv->magic; - mutex_unlock(&dev->master_mutex); drm_dbg_core(dev, "%u\n", auth->magic); @@ -118,13 +117,12 @@ int drm_authmagic(struct drm_device *dev, void *data, drm_dbg_core(dev, "%u\n", auth->magic); - mutex_lock(&dev->master_mutex); + guard(mutex)(&dev->master_mutex); file = idr_find(&file_priv->master->magic_map, auth->magic); if (file) { file->authenticated = 1; idr_replace(&file_priv->master->magic_map, NULL, auth->magic); } - mutex_unlock(&dev->master_mutex); return file ? 0 : -EINVAL; } @@ -248,41 +246,33 @@ int drm_setmaster_ioctl(struct drm_device *dev, void *data, { int ret; - mutex_lock(&dev->master_mutex); + guard(mutex)(&dev->master_mutex); ret = drm_master_check_perm(dev, file_priv); if (ret) - goto out_unlock; + return ret; if (drm_is_current_master_locked(file_priv)) - goto out_unlock; + return ret; - if (dev->master) { - ret = -EBUSY; - goto out_unlock; - } + if (dev->master) + return -EBUSY; - if (!file_priv->master) { - ret = -EINVAL; - goto out_unlock; - } + if (!file_priv->master) + return -EINVAL; - if (!file_priv->is_master) { - ret = drm_new_set_master(dev, file_priv); - goto out_unlock; - } + if (!file_priv->is_master) + return drm_new_set_master(dev, file_priv); if (file_priv->master->lessor != NULL) { drm_dbg_lease(dev, "Attempt to set lessee %d as master\n", file_priv->master->lessee_id); - ret = -EINVAL; - goto out_unlock; + return -EINVAL; } drm_set_master(dev, file_priv, false); -out_unlock: - mutex_unlock(&dev->master_mutex); + return ret; } @@ -299,33 +289,27 @@ int drm_dropmaster_ioctl(struct drm_device *dev, void *data, { int ret; - mutex_lock(&dev->master_mutex); + guard(mutex)(&dev->master_mutex); ret = drm_master_check_perm(dev, file_priv); if (ret) - goto out_unlock; + return ret; - if (!drm_is_current_master_locked(file_priv)) { - ret = -EINVAL; - goto out_unlock; - } + if (!drm_is_current_master_locked(file_priv)) + return -EINVAL; - if (!dev->master) { - ret = -EINVAL; - goto out_unlock; - } + if (!dev->master) + return -EINVAL; if (file_priv->master->lessor != NULL) { drm_dbg_lease(dev, "Attempt to drop lessee %d as master\n", file_priv->master->lessee_id); - ret = -EINVAL; - goto out_unlock; + return -EINVAL; } drm_drop_master(dev, file_priv); -out_unlock: - mutex_unlock(&dev->master_mutex); + return ret; } @@ -337,7 +321,7 @@ int drm_master_open(struct drm_file *file_priv) /* if there is no current master make this fd it, but do not create * any master object for render clients */ - mutex_lock(&dev->master_mutex); + guard(mutex)(&dev->master_mutex); if (!dev->master) { ret = drm_new_set_master(dev, file_priv); } else { @@ -345,7 +329,6 @@ int drm_master_open(struct drm_file *file_priv) file_priv->master = drm_master_get(dev->master); spin_unlock(&file_priv->master_lookup_lock); } - mutex_unlock(&dev->master_mutex); return ret; } @@ -355,7 +338,7 @@ void drm_master_release(struct drm_file *file_priv) struct drm_device *dev = file_priv->minor->dev; struct drm_master *master; - mutex_lock(&dev->master_mutex); + guard(mutex)(&dev->master_mutex); master = file_priv->master; if (file_priv->magic) idr_remove(&file_priv->master->magic_map, file_priv->magic); @@ -376,7 +359,6 @@ out: /* drop the master reference held by the file priv */ if (file_priv->master) drm_master_put(&file_priv->master); - mutex_unlock(&dev->master_mutex); } /** diff --git a/drivers/gpu/drm/drm_bridge.c b/drivers/gpu/drm/drm_bridge.c index b4c89ec01998..bf34cc5ab031 100644 --- a/drivers/gpu/drm/drm_bridge.c +++ b/drivers/gpu/drm/drm_bridge.c @@ -203,6 +203,8 @@ static void __drm_bridge_free(struct kref *kref) { struct drm_bridge *bridge = container_of(kref, struct drm_bridge, refcount); + if (bridge->funcs->destroy) + bridge->funcs->destroy(bridge); kfree(bridge->container); } @@ -1392,6 +1394,23 @@ struct drm_bridge *of_drm_find_bridge(struct device_node *np) EXPORT_SYMBOL(of_drm_find_bridge); #endif +/** + * devm_drm_put_bridge - Release a bridge reference obtained via devm + * @dev: device that got the bridge via devm + * @bridge: pointer to a struct drm_bridge obtained via devm + * + * Same as drm_bridge_put() for bridge pointers obtained via devm functions + * such as devm_drm_bridge_alloc(). + * + * This function is a temporary workaround and MUST NOT be used. Manual + * handling of bridge lifetime is inherently unsafe. + */ +void devm_drm_put_bridge(struct device *dev, struct drm_bridge *bridge) +{ + devm_release_action(dev, drm_bridge_put_void, bridge); +} +EXPORT_SYMBOL(devm_drm_put_bridge); + static void drm_bridge_debugfs_show_bridge(struct drm_printer *p, struct drm_bridge *bridge, unsigned int idx) diff --git a/drivers/gpu/drm/drm_client.c b/drivers/gpu/drm/drm_client.c index f1de7faf9fb4..f7197a07c045 100644 --- a/drivers/gpu/drm/drm_client.c +++ b/drivers/gpu/drm/drm_client.c @@ -303,34 +303,17 @@ EXPORT_SYMBOL(drm_client_buffer_vunmap_local); * Returns: * 0 on success, or a negative errno code otherwise. */ -int -drm_client_buffer_vmap(struct drm_client_buffer *buffer, - struct iosys_map *map_copy) +int drm_client_buffer_vmap(struct drm_client_buffer *buffer, + struct iosys_map *map_copy) { - struct drm_gem_object *gem = buffer->gem; - struct iosys_map *map = &buffer->map; int ret; - drm_gem_lock(gem); - - ret = drm_gem_pin_locked(gem); - if (ret) - goto err_drm_gem_pin_locked; - ret = drm_gem_vmap_locked(gem, map); + ret = drm_gem_vmap(buffer->gem, &buffer->map); if (ret) - goto err_drm_gem_vmap; - - drm_gem_unlock(gem); - - *map_copy = *map; + return ret; + *map_copy = buffer->map; return 0; - -err_drm_gem_vmap: - drm_gem_unpin_locked(buffer->gem); -err_drm_gem_pin_locked: - drm_gem_unlock(gem); - return ret; } EXPORT_SYMBOL(drm_client_buffer_vmap); @@ -344,13 +327,7 @@ EXPORT_SYMBOL(drm_client_buffer_vmap); */ void drm_client_buffer_vunmap(struct drm_client_buffer *buffer) { - struct drm_gem_object *gem = buffer->gem; - struct iosys_map *map = &buffer->map; - - drm_gem_lock(gem); - drm_gem_vunmap_locked(gem, map); - drm_gem_unpin_locked(gem); - drm_gem_unlock(gem); + drm_gem_vunmap(buffer->gem, &buffer->map); } EXPORT_SYMBOL(drm_client_buffer_vunmap); diff --git a/drivers/gpu/drm/drm_color_mgmt.c b/drivers/gpu/drm/drm_color_mgmt.c index 3969dc548cff..5399c111c51f 100644 --- a/drivers/gpu/drm/drm_color_mgmt.c +++ b/drivers/gpu/drm/drm_color_mgmt.c @@ -28,6 +28,7 @@ #include <drm/drm_device.h> #include <drm/drm_drv.h> #include <drm/drm_print.h> +#include <kunit/visibility.h> #include "drm_crtc_internal.h" @@ -494,6 +495,7 @@ const char *drm_get_color_encoding_name(enum drm_color_encoding encoding) return color_encoding_name[encoding]; } +EXPORT_SYMBOL_IF_KUNIT(drm_get_color_encoding_name); /** * drm_get_color_range_name - return a string for color range @@ -509,6 +511,7 @@ const char *drm_get_color_range_name(enum drm_color_range range) return color_range_name[range]; } +EXPORT_SYMBOL_IF_KUNIT(drm_get_color_range_name); /** * drm_plane_create_color_properties - color encoding related plane properties @@ -630,3 +633,209 @@ int drm_color_lut_check(const struct drm_property_blob *lut, u32 tests) return 0; } EXPORT_SYMBOL(drm_color_lut_check); + +/* + * Gamma-LUT programming + */ + +/** + * drm_crtc_load_gamma_888 - Programs gamma ramp for RGB888-like formats + * @crtc: The displaying CRTC + * @lut: The gamma ramp to program + * @set_gamma: Callback for programming the hardware gamma LUT + * + * Programs the gamma ramp specified in @lut to hardware. The input gamma + * ramp must have 256 entries per color component. + */ +void drm_crtc_load_gamma_888(struct drm_crtc *crtc, const struct drm_color_lut *lut, + drm_crtc_set_lut_func set_gamma) +{ + unsigned int i; + + for (i = 0; i < 256; ++i) + set_gamma(crtc, i, lut[i].red, lut[i].green, lut[i].blue); +} +EXPORT_SYMBOL(drm_crtc_load_gamma_888); + +/** + * drm_crtc_load_gamma_565_from_888 - Programs gamma ramp for RGB565-like formats + * @crtc: The displaying CRTC + * @lut: The gamma ramp to program + * @set_gamma: Callback for programming the hardware gamma LUT + * + * Programs the gamma ramp specified in @lut to hardware. The input gamma + * ramp must have 256 entries per color component. The helper interpolates + * the individual color components to reduce the number of entries to 5/6/5. + */ +void drm_crtc_load_gamma_565_from_888(struct drm_crtc *crtc, const struct drm_color_lut *lut, + drm_crtc_set_lut_func set_gamma) +{ + unsigned int i; + u16 r, g, b; + + for (i = 0; i < 32; ++i) { + r = lut[i * 8 + i / 4].red; + g = lut[i * 4 + i / 16].green; + b = lut[i * 8 + i / 4].blue; + set_gamma(crtc, i, r, g, b); + } + /* Green has one more bit, so add padding with 0 for red and blue. */ + for (i = 32; i < 64; ++i) { + g = lut[i * 4 + i / 16].green; + set_gamma(crtc, i, 0, g, 0); + } +} +EXPORT_SYMBOL(drm_crtc_load_gamma_565_from_888); + +/** + * drm_crtc_load_gamma_555_from_888 - Programs gamma ramp for RGB555-like formats + * @crtc: The displaying CRTC + * @lut: The gamma ramp to program + * @set_gamma: Callback for programming the hardware gamma LUT + * + * Programs the gamma ramp specified in @lut to hardware. The input gamma + * ramp must have 256 entries per color component. The helper interpolates + * the individual color components to reduce the number of entries to 5/5/5. + */ +void drm_crtc_load_gamma_555_from_888(struct drm_crtc *crtc, const struct drm_color_lut *lut, + drm_crtc_set_lut_func set_gamma) +{ + unsigned int i; + u16 r, g, b; + + for (i = 0; i < 32; ++i) { + r = lut[i * 8 + i / 4].red; + g = lut[i * 8 + i / 4].green; + b = lut[i * 8 + i / 4].blue; + set_gamma(crtc, i, r, g, b); + } +} +EXPORT_SYMBOL(drm_crtc_load_gamma_555_from_888); + +static void fill_gamma_888(struct drm_crtc *crtc, unsigned int i, u16 r, u16 g, u16 b, + drm_crtc_set_lut_func set_gamma) +{ + r = (r << 8) | r; + g = (g << 8) | g; + b = (b << 8) | b; + + set_gamma(crtc, i, r, g, b); +} + +/** + * drm_crtc_fill_gamma_888 - Programs a default gamma ramp for RGB888-like formats + * @crtc: The displaying CRTC + * @set_gamma: Callback for programming the hardware gamma LUT + * + * Programs a default gamma ramp to hardware. + */ +void drm_crtc_fill_gamma_888(struct drm_crtc *crtc, drm_crtc_set_lut_func set_gamma) +{ + unsigned int i; + + for (i = 0; i < 256; ++i) + fill_gamma_888(crtc, i, i, i, i, set_gamma); +} +EXPORT_SYMBOL(drm_crtc_fill_gamma_888); + +static void fill_gamma_565(struct drm_crtc *crtc, unsigned int i, u16 r, u16 g, u16 b, + drm_crtc_set_lut_func set_gamma) +{ + r = (r << 11) | (r << 6) | (r << 1) | (r >> 4); + g = (g << 10) | (g << 4) | (g >> 2); + b = (b << 11) | (b << 6) | (b << 1) | (b >> 4); + + set_gamma(crtc, i, r, g, b); +} + +/** + * drm_crtc_fill_gamma_565 - Programs a default gamma ramp for RGB565-like formats + * @crtc: The displaying CRTC + * @set_gamma: Callback for programming the hardware gamma LUT + * + * Programs a default gamma ramp to hardware. + */ +void drm_crtc_fill_gamma_565(struct drm_crtc *crtc, drm_crtc_set_lut_func set_gamma) +{ + unsigned int i; + + for (i = 0; i < 32; ++i) + fill_gamma_565(crtc, i, i, i, i, set_gamma); + /* Green has one more bit, so add padding with 0 for red and blue. */ + for (i = 32; i < 64; ++i) + fill_gamma_565(crtc, i, 0, i, 0, set_gamma); +} +EXPORT_SYMBOL(drm_crtc_fill_gamma_565); + +static void fill_gamma_555(struct drm_crtc *crtc, unsigned int i, u16 r, u16 g, u16 b, + drm_crtc_set_lut_func set_gamma) +{ + r = (r << 11) | (r << 6) | (r << 1) | (r >> 4); + g = (g << 11) | (g << 6) | (g << 1) | (g >> 4); + b = (b << 11) | (b << 6) | (b << 1) | (r >> 4); + + set_gamma(crtc, i, r, g, b); +} + +/** + * drm_crtc_fill_gamma_555 - Programs a default gamma ramp for RGB555-like formats + * @crtc: The displaying CRTC + * @set_gamma: Callback for programming the hardware gamma LUT + * + * Programs a default gamma ramp to hardware. + */ +void drm_crtc_fill_gamma_555(struct drm_crtc *crtc, drm_crtc_set_lut_func set_gamma) +{ + unsigned int i; + + for (i = 0; i < 32; ++i) + fill_gamma_555(crtc, i, i, i, i, set_gamma); +} +EXPORT_SYMBOL(drm_crtc_fill_gamma_555); + +/* + * Color-LUT programming + */ + +/** + * drm_crtc_load_palette_8 - Programs palette for C8-like formats + * @crtc: The displaying CRTC + * @lut: The palette to program + * @set_palette: Callback for programming the hardware palette + * + * Programs the palette specified in @lut to hardware. The input palette + * must have 256 entries per color component. + */ +void drm_crtc_load_palette_8(struct drm_crtc *crtc, const struct drm_color_lut *lut, + drm_crtc_set_lut_func set_palette) +{ + unsigned int i; + + for (i = 0; i < 256; ++i) + set_palette(crtc, i, lut[i].red, lut[i].green, lut[i].blue); +} +EXPORT_SYMBOL(drm_crtc_load_palette_8); + +static void fill_palette_8(struct drm_crtc *crtc, unsigned int i, + drm_crtc_set_lut_func set_palette) +{ + u16 Y = (i << 8) | i; // relative luminance + + set_palette(crtc, i, Y, Y, Y); +} + +/** + * drm_crtc_fill_palette_8 - Programs a default palette for C8-like formats + * @crtc: The displaying CRTC + * @set_palette: Callback for programming the hardware gamma LUT + * + * Programs a default palette to hardware. + */ +void drm_crtc_fill_palette_8(struct drm_crtc *crtc, drm_crtc_set_lut_func set_palette) +{ + unsigned int i; + + for (i = 0; i < 256; ++i) + fill_palette_8(crtc, i, set_palette); +} +EXPORT_SYMBOL(drm_crtc_fill_palette_8); diff --git a/drivers/gpu/drm/drm_connector.c b/drivers/gpu/drm/drm_connector.c index 48b08c9611a7..395e1bf006bd 100644 --- a/drivers/gpu/drm/drm_connector.c +++ b/drivers/gpu/drm/drm_connector.c @@ -279,6 +279,7 @@ static int drm_connector_init_only(struct drm_device *dev, INIT_LIST_HEAD(&connector->probed_modes); INIT_LIST_HEAD(&connector->modes); mutex_init(&connector->mutex); + mutex_init(&connector->cec.mutex); mutex_init(&connector->eld_mutex); mutex_init(&connector->edid_override_mutex); mutex_init(&connector->hdmi.infoframes.lock); @@ -702,6 +703,46 @@ static void drm_mode_remove(struct drm_connector *connector, } /** + * drm_connector_cec_phys_addr_invalidate - invalidate CEC physical address + * @connector: connector undergoing CEC operation + * + * Invalidated CEC physical address set for this DRM connector. + */ +void drm_connector_cec_phys_addr_invalidate(struct drm_connector *connector) +{ + mutex_lock(&connector->cec.mutex); + + if (connector->cec.funcs && + connector->cec.funcs->phys_addr_invalidate) + connector->cec.funcs->phys_addr_invalidate(connector); + + mutex_unlock(&connector->cec.mutex); +} +EXPORT_SYMBOL(drm_connector_cec_phys_addr_invalidate); + +/** + * drm_connector_cec_phys_addr_set - propagate CEC physical address + * @connector: connector undergoing CEC operation + * + * Propagate CEC physical address from the display_info to this DRM connector. + */ +void drm_connector_cec_phys_addr_set(struct drm_connector *connector) +{ + u16 addr; + + mutex_lock(&connector->cec.mutex); + + addr = connector->display_info.source_physical_address; + + if (connector->cec.funcs && + connector->cec.funcs->phys_addr_set) + connector->cec.funcs->phys_addr_set(connector, addr); + + mutex_unlock(&connector->cec.mutex); +} +EXPORT_SYMBOL(drm_connector_cec_phys_addr_set); + +/** * drm_connector_cleanup - cleans up an initialised connector * @connector: connector to cleanup * diff --git a/drivers/gpu/drm/drm_debugfs.c b/drivers/gpu/drm/drm_debugfs.c index 3dfd8b34dceb..abceb28b23fc 100644 --- a/drivers/gpu/drm/drm_debugfs.c +++ b/drivers/gpu/drm/drm_debugfs.c @@ -77,14 +77,15 @@ static int drm_clients_info(struct seq_file *m, void *data) kuid_t uid; seq_printf(m, - "%20s %5s %3s master a %5s %10s %*s\n", + "%20s %5s %3s master a %5s %10s %*s %20s\n", "command", "tgid", "dev", "uid", "magic", DRM_CLIENT_NAME_MAX_LEN, - "name"); + "name", + "id"); /* dev->filelist is sorted youngest first, but we want to present * oldest first (i.e. kernel, servers, clients), so walk backwardss. @@ -100,7 +101,7 @@ static int drm_clients_info(struct seq_file *m, void *data) pid = rcu_dereference(priv->pid); task = pid_task(pid, PIDTYPE_TGID); uid = task ? __task_cred(task)->euid : GLOBAL_ROOT_UID; - seq_printf(m, "%20s %5d %3d %c %c %5d %10u %*s\n", + seq_printf(m, "%20s %5d %3d %c %c %5d %10u %*s %20llu\n", task ? task->comm : "<unknown>", pid_vnr(pid), priv->minor->index, @@ -109,7 +110,8 @@ static int drm_clients_info(struct seq_file *m, void *data) from_kuid_munged(seq_user_ns(m), uid), priv->magic, DRM_CLIENT_NAME_MAX_LEN, - priv->client_name ? priv->client_name : "<unset>"); + priv->client_name ? priv->client_name : "<unset>", + priv->client_id); rcu_read_unlock(); mutex_unlock(&priv->client_name_lock); } diff --git a/drivers/gpu/drm/drm_file.c b/drivers/gpu/drm/drm_file.c index 246cf845e2c9..818630ba6e0d 100644 --- a/drivers/gpu/drm/drm_file.c +++ b/drivers/gpu/drm/drm_file.c @@ -1017,8 +1017,10 @@ void drm_file_err(struct drm_file *file_priv, const char *fmt, ...) pid = rcu_dereference(file_priv->pid); task = pid_task(pid, PIDTYPE_TGID); - drm_err(dev, "comm: %s pid: %d client: %s ... %pV", task ? task->comm : "Unset", - task ? task->pid : 0, file_priv->client_name ?: "Unset", &vaf); + drm_err(dev, "comm: %s pid: %d client-id:%llu client: %s ... %pV", + task ? task->comm : "Unset", + task ? task->pid : 0, file_priv->client_id, + file_priv->client_name ?: "Unset", &vaf); va_end(args); rcu_read_unlock(); diff --git a/drivers/gpu/drm/drm_fourcc.c b/drivers/gpu/drm/drm_fourcc.c index 3a94ca211f9c..55ddd99fd7f6 100644 --- a/drivers/gpu/drm/drm_fourcc.c +++ b/drivers/gpu/drm/drm_fourcc.c @@ -346,6 +346,33 @@ const struct drm_format_info *__drm_format_info(u32 format) { .format = DRM_FORMAT_P030, .depth = 0, .num_planes = 2, .char_per_block = { 4, 8, 0 }, .block_w = { 3, 3, 0 }, .block_h = { 1, 1, 0 }, .hsub = 2, .vsub = 2, .is_yuv = true}, + { .format = DRM_FORMAT_S010, .depth = 0, .num_planes = 3, + .char_per_block = { 2, 2, 2 }, .block_w = { 1, 1, 1 }, .block_h = { 1, 1, 1 }, + .hsub = 2, .vsub = 2, .is_yuv = true}, + { .format = DRM_FORMAT_S210, .depth = 0, .num_planes = 3, + .char_per_block = { 2, 2, 2 }, .block_w = { 1, 1, 1 }, .block_h = { 1, 1, 1 }, + .hsub = 2, .vsub = 1, .is_yuv = true}, + { .format = DRM_FORMAT_S410, .depth = 0, .num_planes = 3, + .char_per_block = { 2, 2, 2 }, .block_w = { 1, 1, 1 }, .block_h = { 1, 1, 1 }, + .hsub = 1, .vsub = 1, .is_yuv = true}, + { .format = DRM_FORMAT_S012, .depth = 0, .num_planes = 3, + .char_per_block = { 2, 2, 2 }, .block_w = { 1, 1, 1 }, .block_h = { 1, 1, 1 }, + .hsub = 2, .vsub = 2, .is_yuv = true}, + { .format = DRM_FORMAT_S212, .depth = 0, .num_planes = 3, + .char_per_block = { 2, 2, 2 }, .block_w = { 1, 1, 1 }, .block_h = { 1, 1, 1 }, + .hsub = 2, .vsub = 1, .is_yuv = true}, + { .format = DRM_FORMAT_S412, .depth = 0, .num_planes = 3, + .char_per_block = { 2, 2, 2 }, .block_w = { 1, 1, 1 }, .block_h = { 1, 1, 1 }, + .hsub = 1, .vsub = 1, .is_yuv = true}, + { .format = DRM_FORMAT_S016, .depth = 0, .num_planes = 3, + .char_per_block = { 2, 2, 2 }, .block_w = { 1, 1, 1 }, .block_h = { 1, 1, 1 }, + .hsub = 2, .vsub = 2, .is_yuv = true}, + { .format = DRM_FORMAT_S216, .depth = 0, .num_planes = 3, + .char_per_block = { 2, 2, 2 }, .block_w = { 1, 1, 1 }, .block_h = { 1, 1, 1 }, + .hsub = 2, .vsub = 1, .is_yuv = true}, + { .format = DRM_FORMAT_S416, .depth = 0, .num_planes = 3, + .char_per_block = { 2, 2, 2 }, .block_w = { 1, 1, 1 }, .block_h = { 1, 1, 1 }, + .hsub = 1, .vsub = 1, .is_yuv = true}, }; unsigned int i; diff --git a/drivers/gpu/drm/drm_gem.c b/drivers/gpu/drm/drm_gem.c index 1e659d2660f7..a0a3b6baa569 100644 --- a/drivers/gpu/drm/drm_gem.c +++ b/drivers/gpu/drm/drm_gem.c @@ -1184,38 +1184,6 @@ void drm_gem_print_info(struct drm_printer *p, unsigned int indent, obj->funcs->print_info(p, indent, obj); } -int drm_gem_pin_locked(struct drm_gem_object *obj) -{ - if (obj->funcs->pin) - return obj->funcs->pin(obj); - - return 0; -} - -void drm_gem_unpin_locked(struct drm_gem_object *obj) -{ - if (obj->funcs->unpin) - obj->funcs->unpin(obj); -} - -int drm_gem_pin(struct drm_gem_object *obj) -{ - int ret; - - dma_resv_lock(obj->resv, NULL); - ret = drm_gem_pin_locked(obj); - dma_resv_unlock(obj->resv); - - return ret; -} - -void drm_gem_unpin(struct drm_gem_object *obj) -{ - dma_resv_lock(obj->resv, NULL); - drm_gem_unpin_locked(obj); - dma_resv_unlock(obj->resv); -} - int drm_gem_vmap_locked(struct drm_gem_object *obj, struct iosys_map *map) { int ret; diff --git a/drivers/gpu/drm/drm_gem_framebuffer_helper.c b/drivers/gpu/drm/drm_gem_framebuffer_helper.c index 6f72e7a0f427..baf99a68bdb5 100644 --- a/drivers/gpu/drm/drm_gem_framebuffer_helper.c +++ b/drivers/gpu/drm/drm_gem_framebuffer_helper.c @@ -607,8 +607,11 @@ int drm_gem_fb_afbc_init(struct drm_device *dev, if (ret < 0) return ret; - if (objs[0]->size < afbc_fb->afbc_size) + if (objs[0]->size < afbc_fb->afbc_size) { + drm_dbg_kms(dev, "GEM object size (%zu) smaller than minimum afbc size (%u)\n", + objs[0]->size, afbc_fb->afbc_size); return -EINVAL; + } return 0; } diff --git a/drivers/gpu/drm/drm_gem_shmem_helper.c b/drivers/gpu/drm/drm_gem_shmem_helper.c index aa43265f4f4f..8ac0b1fa5287 100644 --- a/drivers/gpu/drm/drm_gem_shmem_helper.c +++ b/drivers/gpu/drm/drm_gem_shmem_helper.c @@ -348,6 +348,8 @@ int drm_gem_shmem_vmap_locked(struct drm_gem_shmem_object *shmem, struct drm_gem_object *obj = &shmem->base; int ret = 0; + dma_resv_assert_held(obj->resv); + if (drm_gem_is_imported(obj)) { ret = dma_buf_vmap(obj->dma_buf, map); } else { @@ -408,6 +410,8 @@ void drm_gem_shmem_vunmap_locked(struct drm_gem_shmem_object *shmem, { struct drm_gem_object *obj = &shmem->base; + dma_resv_assert_held(obj->resv); + if (drm_gem_is_imported(obj)) { dma_buf_vunmap(obj->dma_buf, map); } else { @@ -800,6 +804,63 @@ drm_gem_shmem_prime_import_sg_table(struct drm_device *dev, } EXPORT_SYMBOL_GPL(drm_gem_shmem_prime_import_sg_table); +/** + * drm_gem_shmem_prime_import_no_map - Import dmabuf without mapping its sg_table + * @dev: Device to import into + * @dma_buf: dma-buf object to import + * + * Drivers that use the shmem helpers but also wants to import dmabuf without + * mapping its sg_table can use this as their &drm_driver.gem_prime_import + * implementation. + */ +struct drm_gem_object *drm_gem_shmem_prime_import_no_map(struct drm_device *dev, + struct dma_buf *dma_buf) +{ + struct dma_buf_attachment *attach; + struct drm_gem_shmem_object *shmem; + struct drm_gem_object *obj; + size_t size; + int ret; + + if (drm_gem_is_prime_exported_dma_buf(dev, dma_buf)) { + /* + * Importing dmabuf exported from our own gem increases + * refcount on gem itself instead of f_count of dmabuf. + */ + obj = dma_buf->priv; + drm_gem_object_get(obj); + return obj; + } + + attach = dma_buf_attach(dma_buf, dev->dev); + if (IS_ERR(attach)) + return ERR_CAST(attach); + + get_dma_buf(dma_buf); + + size = PAGE_ALIGN(attach->dmabuf->size); + + shmem = __drm_gem_shmem_create(dev, size, true, NULL); + if (IS_ERR(shmem)) { + ret = PTR_ERR(shmem); + goto fail_detach; + } + + drm_dbg_prime(dev, "size = %zu\n", size); + + shmem->base.import_attach = attach; + shmem->base.resv = dma_buf->resv; + + return &shmem->base; + +fail_detach: + dma_buf_detach(dma_buf, attach); + dma_buf_put(dma_buf); + + return ERR_PTR(ret); +} +EXPORT_SYMBOL_GPL(drm_gem_shmem_prime_import_no_map); + MODULE_DESCRIPTION("DRM SHMEM memory-management helpers"); MODULE_IMPORT_NS("DMA_BUF"); MODULE_LICENSE("GPL v2"); diff --git a/drivers/gpu/drm/drm_gem_vram_helper.c b/drivers/gpu/drm/drm_gem_vram_helper.c index 22b1fe9c03b8..ead50fef5e7d 100644 --- a/drivers/gpu/drm/drm_gem_vram_helper.c +++ b/drivers/gpu/drm/drm_gem_vram_helper.c @@ -88,11 +88,6 @@ static const struct drm_gem_object_funcs drm_gem_vram_object_funcs; * drmm_vram_helper_init() is a managed interface that installs a * clean-up handler to run during the DRM device's release. * - * For drawing or scanout operations, rsp. buffer objects have to be pinned - * in video RAM. Call drm_gem_vram_pin() with &DRM_GEM_VRAM_PL_FLAG_VRAM or - * &DRM_GEM_VRAM_PL_FLAG_SYSTEM to pin a buffer object in video RAM or system - * memory. Call drm_gem_vram_unpin() to release the pinned object afterwards. - * * A buffer object that is pinned in video RAM has a fixed address within that * memory region. Call drm_gem_vram_offset() to retrieve this value. Typically * it's used to program the hardware's scanout engine for framebuffers, set @@ -299,30 +294,7 @@ out: return 0; } -/** - * drm_gem_vram_pin() - Pins a GEM VRAM object in a region. - * @gbo: the GEM VRAM object - * @pl_flag: a bitmask of possible memory regions - * - * Pinning a buffer object ensures that it is not evicted from - * a memory region. A pinned buffer object has to be unpinned before - * it can be pinned to another region. If the pl_flag argument is 0, - * the buffer is pinned at its current location (video RAM or system - * memory). - * - * Small buffer objects, such as cursor images, can lead to memory - * fragmentation if they are pinned in the middle of video RAM. This - * is especially a problem on devices with only a small amount of - * video RAM. Fragmentation can prevent the primary framebuffer from - * fitting in, even though there's enough memory overall. The modifier - * DRM_GEM_VRAM_PL_FLAG_TOPDOWN marks the buffer object to be pinned - * at the high end of the memory region to avoid fragmentation. - * - * Returns: - * 0 on success, or - * a negative error code otherwise. - */ -int drm_gem_vram_pin(struct drm_gem_vram_object *gbo, unsigned long pl_flag) +static int drm_gem_vram_pin(struct drm_gem_vram_object *gbo, unsigned long pl_flag) { int ret; @@ -334,7 +306,6 @@ int drm_gem_vram_pin(struct drm_gem_vram_object *gbo, unsigned long pl_flag) return ret; } -EXPORT_SYMBOL(drm_gem_vram_pin); static void drm_gem_vram_unpin_locked(struct drm_gem_vram_object *gbo) { @@ -343,15 +314,7 @@ static void drm_gem_vram_unpin_locked(struct drm_gem_vram_object *gbo) ttm_bo_unpin(&gbo->bo); } -/** - * drm_gem_vram_unpin() - Unpins a GEM VRAM object - * @gbo: the GEM VRAM object - * - * Returns: - * 0 on success, or - * a negative error code otherwise. - */ -int drm_gem_vram_unpin(struct drm_gem_vram_object *gbo) +static int drm_gem_vram_unpin(struct drm_gem_vram_object *gbo) { int ret; @@ -364,7 +327,6 @@ int drm_gem_vram_unpin(struct drm_gem_vram_object *gbo) return 0; } -EXPORT_SYMBOL(drm_gem_vram_unpin); /** * drm_gem_vram_vmap() - Pins and maps a GEM VRAM object into kernel address @@ -690,41 +652,6 @@ EXPORT_SYMBOL(drm_gem_vram_plane_helper_cleanup_fb); */ /** - * drm_gem_vram_object_pin() - Implements &struct drm_gem_object_funcs.pin - * @gem: The GEM object to pin - * - * Returns: - * 0 on success, or - * a negative errno code otherwise. - */ -static int drm_gem_vram_object_pin(struct drm_gem_object *gem) -{ - struct drm_gem_vram_object *gbo = drm_gem_vram_of_gem(gem); - - /* - * Fbdev console emulation is the use case of these PRIME - * helpers. This may involve updating a hardware buffer from - * a shadow FB. We pin the buffer to it's current location - * (either video RAM or system memory) to prevent it from - * being relocated during the update operation. If you require - * the buffer to be pinned to VRAM, implement a callback that - * sets the flags accordingly. - */ - return drm_gem_vram_pin_locked(gbo, 0); -} - -/** - * drm_gem_vram_object_unpin() - Implements &struct drm_gem_object_funcs.unpin - * @gem: The GEM object to unpin - */ -static void drm_gem_vram_object_unpin(struct drm_gem_object *gem) -{ - struct drm_gem_vram_object *gbo = drm_gem_vram_of_gem(gem); - - drm_gem_vram_unpin_locked(gbo); -} - -/** * drm_gem_vram_object_vmap() - * Implements &struct drm_gem_object_funcs.vmap * @gem: The GEM object to map @@ -762,8 +689,6 @@ static void drm_gem_vram_object_vunmap(struct drm_gem_object *gem, static const struct drm_gem_object_funcs drm_gem_vram_object_funcs = { .free = drm_gem_vram_object_free, - .pin = drm_gem_vram_object_pin, - .unpin = drm_gem_vram_object_unpin, .vmap = drm_gem_vram_object_vmap, .vunmap = drm_gem_vram_object_vunmap, .mmap = drm_gem_ttm_mmap, diff --git a/drivers/gpu/drm/drm_internal.h b/drivers/gpu/drm/drm_internal.h index e44f28fd81d3..442eb31351dd 100644 --- a/drivers/gpu/drm/drm_internal.h +++ b/drivers/gpu/drm/drm_internal.h @@ -175,10 +175,6 @@ void drm_gem_release(struct drm_device *dev, struct drm_file *file_private); void drm_gem_print_info(struct drm_printer *p, unsigned int indent, const struct drm_gem_object *obj); -int drm_gem_pin_locked(struct drm_gem_object *obj); -void drm_gem_unpin_locked(struct drm_gem_object *obj); -int drm_gem_pin(struct drm_gem_object *obj); -void drm_gem_unpin(struct drm_gem_object *obj); int drm_gem_vmap_locked(struct drm_gem_object *obj, struct iosys_map *map); void drm_gem_vunmap_locked(struct drm_gem_object *obj, struct iosys_map *map); diff --git a/drivers/gpu/drm/drm_panel.c b/drivers/gpu/drm/drm_panel.c index 650de4da0853..805b4151ccef 100644 --- a/drivers/gpu/drm/drm_panel.c +++ b/drivers/gpu/drm/drm_panel.c @@ -473,13 +473,51 @@ int of_drm_get_panel_orientation(const struct device_node *np, EXPORT_SYMBOL(of_drm_get_panel_orientation); #endif +/* Find panel by fwnode. This should be identical to of_drm_find_panel(). */ +static struct drm_panel *find_panel_by_fwnode(const struct fwnode_handle *fwnode) +{ + struct drm_panel *panel; + + if (!fwnode_device_is_available(fwnode)) + return ERR_PTR(-ENODEV); + + mutex_lock(&panel_lock); + + list_for_each_entry(panel, &panel_list, list) { + if (dev_fwnode(panel->dev) == fwnode) { + mutex_unlock(&panel_lock); + return panel; + } + } + + mutex_unlock(&panel_lock); + + return ERR_PTR(-EPROBE_DEFER); +} + +/* Find panel by follower device */ +static struct drm_panel *find_panel_by_dev(struct device *follower_dev) +{ + struct fwnode_handle *fwnode; + struct drm_panel *panel; + + fwnode = fwnode_find_reference(dev_fwnode(follower_dev), "panel", 0); + if (IS_ERR(fwnode)) + return ERR_PTR(-ENODEV); + + panel = find_panel_by_fwnode(fwnode); + fwnode_handle_put(fwnode); + + return panel; +} + /** * drm_is_panel_follower() - Check if the device is a panel follower * @dev: The 'struct device' to check * * This checks to see if a device needs to be power sequenced together with * a panel using the panel follower API. - * At the moment panels can only be followed on device tree enabled systems. + * * The "panel" property of the follower points to the panel to be followed. * * Return: true if we should be power sequenced with a panel; false otherwise. @@ -491,7 +529,7 @@ bool drm_is_panel_follower(struct device *dev) * don't bother trying to parse it here. We just need to know if the * property is there. */ - return of_property_present(dev->of_node, "panel"); + return device_property_present(dev, "panel"); } EXPORT_SYMBOL(drm_is_panel_follower); @@ -508,7 +546,6 @@ EXPORT_SYMBOL(drm_is_panel_follower); * If a follower is added to a panel that's already been turned on, the * follower's prepare callback is called right away. * - * At the moment panels can only be followed on device tree enabled systems. * The "panel" property of the follower points to the panel to be followed. * * Return: 0 or an error code. Note that -ENODEV means that we detected that @@ -518,16 +555,10 @@ EXPORT_SYMBOL(drm_is_panel_follower); int drm_panel_add_follower(struct device *follower_dev, struct drm_panel_follower *follower) { - struct device_node *panel_np; struct drm_panel *panel; int ret; - panel_np = of_parse_phandle(follower_dev->of_node, "panel", 0); - if (!panel_np) - return -ENODEV; - - panel = of_drm_find_panel(panel_np); - of_node_put(panel_np); + panel = find_panel_by_dev(follower_dev); if (IS_ERR(panel)) return PTR_ERR(panel); diff --git a/drivers/gpu/drm/drm_prime.c b/drivers/gpu/drm/drm_prime.c index d828502268b8..b703f83874e1 100644 --- a/drivers/gpu/drm/drm_prime.c +++ b/drivers/gpu/drm/drm_prime.c @@ -599,6 +599,7 @@ int drm_gem_map_attach(struct dma_buf *dma_buf, struct dma_buf_attachment *attach) { struct drm_gem_object *obj = dma_buf->priv; + int ret; /* * drm_gem_map_dma_buf() requires obj->get_sg_table(), but drivers @@ -608,7 +609,16 @@ int drm_gem_map_attach(struct dma_buf *dma_buf, !obj->funcs->get_sg_table) return -ENOSYS; - return drm_gem_pin(obj); + if (!obj->funcs->pin) + return 0; + + ret = dma_resv_lock(obj->resv, NULL); + if (ret) + return ret; + ret = obj->funcs->pin(obj); + dma_resv_unlock(obj->resv); + + return ret; } EXPORT_SYMBOL(drm_gem_map_attach); @@ -625,8 +635,16 @@ void drm_gem_map_detach(struct dma_buf *dma_buf, struct dma_buf_attachment *attach) { struct drm_gem_object *obj = dma_buf->priv; + int ret; + + if (!obj->funcs->unpin) + return; - drm_gem_unpin(obj); + ret = dma_resv_lock(obj->resv, NULL); + if (drm_WARN_ON(obj->dev, ret)) + return; + obj->funcs->unpin(obj); + dma_resv_unlock(obj->resv); } EXPORT_SYMBOL(drm_gem_map_detach); @@ -910,6 +928,26 @@ struct dma_buf *drm_gem_prime_export(struct drm_gem_object *obj, } EXPORT_SYMBOL(drm_gem_prime_export); + +/** + * drm_gem_is_prime_exported_dma_buf - + * checks if the DMA-BUF was exported from a GEM object belonging to @dev. + * @dev: drm_device to check against + * @dma_buf: dma-buf object to import + * + * Return: true if the DMA-BUF was exported from a GEM object belonging + * to @dev, false otherwise. + */ + +bool drm_gem_is_prime_exported_dma_buf(struct drm_device *dev, + struct dma_buf *dma_buf) +{ + struct drm_gem_object *obj = dma_buf->priv; + + return (dma_buf->ops == &drm_gem_prime_dmabuf_ops) && (obj->dev == dev); +} +EXPORT_SYMBOL(drm_gem_is_prime_exported_dma_buf); + /** * drm_gem_prime_import_dev - core implementation of the import callback * @dev: drm_device to import into @@ -933,16 +971,14 @@ struct drm_gem_object *drm_gem_prime_import_dev(struct drm_device *dev, struct drm_gem_object *obj; int ret; - if (dma_buf->ops == &drm_gem_prime_dmabuf_ops) { + if (drm_gem_is_prime_exported_dma_buf(dev, dma_buf)) { + /* + * Importing dmabuf exported from our own gem increases + * refcount on gem itself instead of f_count of dmabuf. + */ obj = dma_buf->priv; - if (obj->dev == dev) { - /* - * Importing dmabuf exported from our own gem increases - * refcount on gem itself instead of f_count of dmabuf. - */ - drm_gem_object_get(obj); - return obj; - } + drm_gem_object_get(obj); + return obj; } if (!dev->driver->gem_prime_import_sg_table) diff --git a/drivers/gpu/drm/etnaviv/etnaviv_gem_submit.c b/drivers/gpu/drm/etnaviv/etnaviv_gem_submit.c index 3c0a5c3e0e3d..76c742328edb 100644 --- a/drivers/gpu/drm/etnaviv/etnaviv_gem_submit.c +++ b/drivers/gpu/drm/etnaviv/etnaviv_gem_submit.c @@ -534,7 +534,7 @@ int etnaviv_ioctl_gem_submit(struct drm_device *dev, void *data, ret = drm_sched_job_init(&submit->sched_job, &ctx->sched_entity[args->pipe], - 1, submit->ctx); + 1, submit->ctx, file->client_id); if (ret) goto err_submit_put; diff --git a/drivers/gpu/drm/hyperv/hyperv_drm.h b/drivers/gpu/drm/hyperv/hyperv_drm.h index d2d8582b36df..9e776112c03e 100644 --- a/drivers/gpu/drm/hyperv/hyperv_drm.h +++ b/drivers/gpu/drm/hyperv/hyperv_drm.h @@ -11,7 +11,9 @@ struct hyperv_drm_device { /* drm */ struct drm_device dev; - struct drm_simple_display_pipe pipe; + struct drm_plane plane; + struct drm_crtc crtc; + struct drm_encoder encoder; struct drm_connector connector; /* mode */ diff --git a/drivers/gpu/drm/hyperv/hyperv_drm_modeset.c b/drivers/gpu/drm/hyperv/hyperv_drm_modeset.c index 6c6b57298797..945b9482bcb3 100644 --- a/drivers/gpu/drm/hyperv/hyperv_drm_modeset.c +++ b/drivers/gpu/drm/hyperv/hyperv_drm_modeset.c @@ -5,6 +5,8 @@ #include <linux/hyperv.h> +#include <drm/drm_atomic.h> +#include <drm/drm_crtc_helper.h> #include <drm/drm_damage_helper.h> #include <drm/drm_drv.h> #include <drm/drm_edid.h> @@ -15,7 +17,8 @@ #include <drm/drm_gem_framebuffer_helper.h> #include <drm/drm_gem_shmem_helper.h> #include <drm/drm_probe_helper.h> -#include <drm/drm_simple_kms_helper.h> +#include <drm/drm_panic.h> +#include <drm/drm_plane.h> #include "hyperv_drm.h" @@ -38,18 +41,6 @@ static int hyperv_blit_to_vram_rect(struct drm_framebuffer *fb, return 0; } -static int hyperv_blit_to_vram_fullscreen(struct drm_framebuffer *fb, - const struct iosys_map *map) -{ - struct drm_rect fullscreen = { - .x1 = 0, - .x2 = fb->width, - .y1 = 0, - .y2 = fb->height, - }; - return hyperv_blit_to_vram_rect(fb, map, &fullscreen); -} - static int hyperv_connector_get_modes(struct drm_connector *connector) { struct hyperv_drm_device *hv = to_hv(connector->dev); @@ -98,30 +89,66 @@ static int hyperv_check_size(struct hyperv_drm_device *hv, int w, int h, return 0; } -static void hyperv_pipe_enable(struct drm_simple_display_pipe *pipe, - struct drm_crtc_state *crtc_state, - struct drm_plane_state *plane_state) +static const uint32_t hyperv_formats[] = { + DRM_FORMAT_XRGB8888, +}; + +static const uint64_t hyperv_modifiers[] = { + DRM_FORMAT_MOD_LINEAR, + DRM_FORMAT_MOD_INVALID +}; + +static void hyperv_crtc_helper_atomic_enable(struct drm_crtc *crtc, + struct drm_atomic_state *state) { - struct hyperv_drm_device *hv = to_hv(pipe->crtc.dev); - struct drm_shadow_plane_state *shadow_plane_state = to_drm_shadow_plane_state(plane_state); + struct hyperv_drm_device *hv = to_hv(crtc->dev); + struct drm_plane *plane = &hv->plane; + struct drm_plane_state *plane_state = plane->state; + struct drm_crtc_state *crtc_state = crtc->state; hyperv_hide_hw_ptr(hv->hdev); hyperv_update_situation(hv->hdev, 1, hv->screen_depth, crtc_state->mode.hdisplay, crtc_state->mode.vdisplay, plane_state->fb->pitches[0]); - hyperv_blit_to_vram_fullscreen(plane_state->fb, &shadow_plane_state->data[0]); } -static int hyperv_pipe_check(struct drm_simple_display_pipe *pipe, - struct drm_plane_state *plane_state, - struct drm_crtc_state *crtc_state) +static const struct drm_crtc_helper_funcs hyperv_crtc_helper_funcs = { + .atomic_check = drm_crtc_helper_atomic_check, + .atomic_enable = hyperv_crtc_helper_atomic_enable, +}; + +static const struct drm_crtc_funcs hyperv_crtc_funcs = { + .reset = drm_atomic_helper_crtc_reset, + .destroy = drm_crtc_cleanup, + .set_config = drm_atomic_helper_set_config, + .page_flip = drm_atomic_helper_page_flip, + .atomic_duplicate_state = drm_atomic_helper_crtc_duplicate_state, + .atomic_destroy_state = drm_atomic_helper_crtc_destroy_state, +}; + +static int hyperv_plane_atomic_check(struct drm_plane *plane, + struct drm_atomic_state *state) { - struct hyperv_drm_device *hv = to_hv(pipe->crtc.dev); + struct drm_plane_state *plane_state = drm_atomic_get_new_plane_state(state, plane); + struct hyperv_drm_device *hv = to_hv(plane->dev); struct drm_framebuffer *fb = plane_state->fb; + struct drm_crtc *crtc = plane_state->crtc; + struct drm_crtc_state *crtc_state = NULL; + int ret; - if (fb->format->format != DRM_FORMAT_XRGB8888) - return -EINVAL; + if (crtc) + crtc_state = drm_atomic_get_new_crtc_state(state, crtc); + + ret = drm_atomic_helper_check_plane_state(plane_state, crtc_state, + DRM_PLANE_NO_SCALING, + DRM_PLANE_NO_SCALING, + false, false); + if (ret) + return ret; + + if (!plane_state->visible) + return 0; if (fb->pitches[0] * fb->height > hv->fb_size) { drm_err(&hv->dev, "fb size requested by %s for %dX%d (pitch %d) greater than %ld\n", @@ -132,53 +159,120 @@ static int hyperv_pipe_check(struct drm_simple_display_pipe *pipe, return 0; } -static void hyperv_pipe_update(struct drm_simple_display_pipe *pipe, - struct drm_plane_state *old_state) +static void hyperv_plane_atomic_update(struct drm_plane *plane, + struct drm_atomic_state *state) { - struct hyperv_drm_device *hv = to_hv(pipe->crtc.dev); - struct drm_plane_state *state = pipe->plane.state; - struct drm_shadow_plane_state *shadow_plane_state = to_drm_shadow_plane_state(state); - struct drm_rect rect; + struct hyperv_drm_device *hv = to_hv(plane->dev); + struct drm_plane_state *old_state = drm_atomic_get_old_plane_state(state, plane); + struct drm_plane_state *new_state = drm_atomic_get_new_plane_state(state, plane); + struct drm_shadow_plane_state *shadow_plane_state = to_drm_shadow_plane_state(new_state); + struct drm_rect damage; + struct drm_rect dst_clip; + struct drm_atomic_helper_damage_iter iter; + + drm_atomic_helper_damage_iter_init(&iter, old_state, new_state); + drm_atomic_for_each_plane_damage(&iter, &damage) { + dst_clip = new_state->dst; + + if (!drm_rect_intersect(&dst_clip, &damage)) + continue; + + hyperv_blit_to_vram_rect(new_state->fb, &shadow_plane_state->data[0], &damage); + hyperv_update_dirt(hv->hdev, &damage); + } +} - if (drm_atomic_helper_damage_merged(old_state, state, &rect)) { - hyperv_blit_to_vram_rect(state->fb, &shadow_plane_state->data[0], &rect); - hyperv_update_dirt(hv->hdev, &rect); +static int hyperv_plane_get_scanout_buffer(struct drm_plane *plane, + struct drm_scanout_buffer *sb) +{ + struct hyperv_drm_device *hv = to_hv(plane->dev); + struct iosys_map map = IOSYS_MAP_INIT_VADDR_IOMEM(hv->vram); + + if (plane->state && plane->state->fb) { + sb->format = plane->state->fb->format; + sb->width = plane->state->fb->width; + sb->height = plane->state->fb->height; + sb->pitch[0] = plane->state->fb->pitches[0]; + sb->map[0] = map; + return 0; } + return -ENODEV; } -static const struct drm_simple_display_pipe_funcs hyperv_pipe_funcs = { - .enable = hyperv_pipe_enable, - .check = hyperv_pipe_check, - .update = hyperv_pipe_update, - DRM_GEM_SIMPLE_DISPLAY_PIPE_SHADOW_PLANE_FUNCS, +static void hyperv_plane_panic_flush(struct drm_plane *plane) +{ + struct hyperv_drm_device *hv = to_hv(plane->dev); + struct drm_rect rect; + + if (!plane->state || !plane->state->fb) + return; + + rect.x1 = 0; + rect.y1 = 0; + rect.x2 = plane->state->fb->width; + rect.y2 = plane->state->fb->height; + + hyperv_update_dirt(hv->hdev, &rect); +} + +static const struct drm_plane_helper_funcs hyperv_plane_helper_funcs = { + DRM_GEM_SHADOW_PLANE_HELPER_FUNCS, + .atomic_check = hyperv_plane_atomic_check, + .atomic_update = hyperv_plane_atomic_update, + .get_scanout_buffer = hyperv_plane_get_scanout_buffer, + .panic_flush = hyperv_plane_panic_flush, }; -static const uint32_t hyperv_formats[] = { - DRM_FORMAT_XRGB8888, +static const struct drm_plane_funcs hyperv_plane_funcs = { + .update_plane = drm_atomic_helper_update_plane, + .disable_plane = drm_atomic_helper_disable_plane, + .destroy = drm_plane_cleanup, + DRM_GEM_SHADOW_PLANE_FUNCS, }; -static const uint64_t hyperv_modifiers[] = { - DRM_FORMAT_MOD_LINEAR, - DRM_FORMAT_MOD_INVALID +static const struct drm_encoder_funcs hyperv_drm_simple_encoder_funcs_cleanup = { + .destroy = drm_encoder_cleanup, }; static inline int hyperv_pipe_init(struct hyperv_drm_device *hv) { + struct drm_device *dev = &hv->dev; + struct drm_encoder *encoder = &hv->encoder; + struct drm_plane *plane = &hv->plane; + struct drm_crtc *crtc = &hv->crtc; + struct drm_connector *connector = &hv->connector; int ret; - ret = drm_simple_display_pipe_init(&hv->dev, - &hv->pipe, - &hyperv_pipe_funcs, - hyperv_formats, - ARRAY_SIZE(hyperv_formats), - hyperv_modifiers, - &hv->connector); + ret = drm_universal_plane_init(dev, plane, 0, + &hyperv_plane_funcs, + hyperv_formats, ARRAY_SIZE(hyperv_formats), + hyperv_modifiers, + DRM_PLANE_TYPE_PRIMARY, NULL); if (ret) return ret; + drm_plane_helper_add(plane, &hyperv_plane_helper_funcs); + drm_plane_enable_fb_damage_clips(plane); - drm_plane_enable_fb_damage_clips(&hv->pipe.plane); + ret = drm_crtc_init_with_planes(dev, crtc, plane, NULL, + &hyperv_crtc_funcs, NULL); + if (ret) + return ret; + drm_crtc_helper_add(crtc, &hyperv_crtc_helper_funcs); - return 0; + encoder->possible_crtcs = drm_crtc_mask(crtc); + ret = drm_encoder_init(dev, encoder, + &hyperv_drm_simple_encoder_funcs_cleanup, + DRM_MODE_ENCODER_NONE, NULL); + if (ret) + return ret; + + ret = hyperv_conn_init(hv); + if (ret) { + drm_err(dev, "Failed to initialized connector.\n"); + return ret; + } + + return drm_connector_attach_encoder(connector, encoder); } static enum drm_mode_status @@ -221,12 +315,6 @@ int hyperv_mode_config_init(struct hyperv_drm_device *hv) dev->mode_config.funcs = &hyperv_mode_config_funcs; - ret = hyperv_conn_init(hv); - if (ret) { - drm_err(dev, "Failed to initialized connector.\n"); - return ret; - } - ret = hyperv_pipe_init(hv); if (ret) { drm_err(dev, "Failed to initialized pipe.\n"); diff --git a/drivers/gpu/drm/i915/display/icl_dsi.c b/drivers/gpu/drm/i915/display/icl_dsi.c index ca7033251e91..aa566ad3f5cd 100644 --- a/drivers/gpu/drm/i915/display/icl_dsi.c +++ b/drivers/gpu/drm/i915/display/icl_dsi.c @@ -1276,6 +1276,8 @@ static void gen11_dsi_enable(struct intel_atomic_state *state, intel_backlight_enable(crtc_state, conn_state); intel_dsi_vbt_exec_sequence(intel_dsi, MIPI_SEQ_BACKLIGHT_ON); + intel_panel_prepare(crtc_state, conn_state); + intel_crtc_vblank_on(crtc_state); } @@ -1409,6 +1411,8 @@ static void gen11_dsi_disable(struct intel_atomic_state *state, { struct intel_dsi *intel_dsi = enc_to_intel_dsi(encoder); + intel_panel_unprepare(old_conn_state); + /* step1: turn off backlight */ intel_dsi_vbt_exec_sequence(intel_dsi, MIPI_SEQ_BACKLIGHT_OFF); intel_backlight_disable(old_conn_state); diff --git a/drivers/gpu/drm/i915/display/intel_connector.c b/drivers/gpu/drm/i915/display/intel_connector.c index 6c81c9f2fd09..18f3a1d26662 100644 --- a/drivers/gpu/drm/i915/display/intel_connector.c +++ b/drivers/gpu/drm/i915/display/intel_connector.c @@ -32,7 +32,6 @@ #include "i915_drv.h" #include "i915_utils.h" -#include "intel_backlight.h" #include "intel_connector.h" #include "intel_display_core.h" #include "intel_display_debugfs.h" @@ -153,36 +152,36 @@ void intel_connector_destroy(struct drm_connector *connector) kfree(connector); } -int intel_connector_register(struct drm_connector *connector) +int intel_connector_register(struct drm_connector *_connector) { - struct intel_connector *intel_connector = to_intel_connector(connector); - struct drm_i915_private *i915 = to_i915(connector->dev); + struct intel_connector *connector = to_intel_connector(_connector); + struct drm_i915_private *i915 = to_i915(_connector->dev); int ret; - ret = intel_backlight_device_register(intel_connector); + ret = intel_panel_register(connector); if (ret) goto err; if (i915_inject_probe_failure(i915)) { ret = -EFAULT; - goto err_backlight; + goto err_panel; } - intel_connector_debugfs_add(intel_connector); + intel_connector_debugfs_add(connector); return 0; -err_backlight: - intel_backlight_device_unregister(intel_connector); +err_panel: + intel_panel_unregister(connector); err: return ret; } -void intel_connector_unregister(struct drm_connector *connector) +void intel_connector_unregister(struct drm_connector *_connector) { - struct intel_connector *intel_connector = to_intel_connector(connector); + struct intel_connector *connector = to_intel_connector(_connector); - intel_backlight_device_unregister(intel_connector); + intel_panel_unregister(connector); } void intel_connector_attach_encoder(struct intel_connector *connector, diff --git a/drivers/gpu/drm/i915/display/intel_display_types.h b/drivers/gpu/drm/i915/display/intel_display_types.h index d6d0440dcee9..79c328d49ef0 100644 --- a/drivers/gpu/drm/i915/display/intel_display_types.h +++ b/drivers/gpu/drm/i915/display/intel_display_types.h @@ -37,6 +37,7 @@ #include <drm/drm_crtc.h> #include <drm/drm_encoder.h> #include <drm/drm_framebuffer.h> +#include <drm/drm_panel.h> #include <drm/drm_rect.h> #include <drm/drm_vblank_work.h> #include <drm/intel/i915_hdcp_interface.h> @@ -384,6 +385,9 @@ struct intel_vbt_panel_data { }; struct intel_panel { + /* Simple drm_panel */ + struct drm_panel *base; + /* Fixed EDID for eDP and LVDS. May hold ERR_PTR for invalid EDID. */ const struct drm_edid *fixed_edid; diff --git a/drivers/gpu/drm/i915/display/intel_panel.c b/drivers/gpu/drm/i915/display/intel_panel.c index f5c972880391..f956919dc648 100644 --- a/drivers/gpu/drm/i915/display/intel_panel.c +++ b/drivers/gpu/drm/i915/display/intel_panel.c @@ -462,3 +462,134 @@ void intel_panel_fini(struct intel_connector *connector) drm_mode_destroy(connector->base.dev, fixed_mode); } } + +/* + * If the panel was already enabled at probe, and we took over the state, the + * panel prepared state is out of sync, and the panel followers won't be + * notified. We need to call drm_panel_prepare() on enabled panels. + * + * It would be natural to handle this e.g. in the connector ->sync_state hook at + * intel_modeset_readout_hw_state(), but that's unfortunately too early. We + * don't have drm_connector::kdev at that time. For now, figure out the state at + * ->late_register, and sync there. + */ +static void intel_panel_sync_state(struct intel_connector *connector) +{ + struct intel_display *display = to_intel_display(connector); + struct drm_connector_state *conn_state; + struct intel_crtc *crtc; + int ret; + + ret = drm_modeset_lock(&display->drm->mode_config.connection_mutex, NULL); + if (ret) + return; + + conn_state = connector->base.state; + + crtc = to_intel_crtc(conn_state->crtc); + if (crtc) { + struct intel_crtc_state *crtc_state; + + crtc_state = to_intel_crtc_state(crtc->base.state); + + if (crtc_state->hw.active) { + drm_dbg_kms(display->drm, "[CONNECTOR:%d:%s] Panel prepare\n", + connector->base.base.id, connector->base.name); + intel_panel_prepare(crtc_state, conn_state); + } + } + + drm_modeset_unlock(&display->drm->mode_config.connection_mutex); +} + +const struct drm_panel_funcs dummy_panel_funcs = { +}; + +int intel_panel_register(struct intel_connector *connector) +{ + struct intel_display *display = to_intel_display(connector); + struct intel_panel *panel = &connector->panel; + int ret; + + ret = intel_backlight_device_register(connector); + if (ret) + return ret; + + if (connector->base.connector_type == DRM_MODE_CONNECTOR_DSI) { + struct device *dev = connector->base.kdev; + struct drm_panel *base; + + /* Sanity check. */ + if (drm_WARN_ON(display->drm, !dev)) + goto out; + + /* + * We need drm_connector::kdev for allocating the panel, to make + * drm_panel_add_follower() lookups work. The kdev is + * initialized in drm_sysfs_connector_add(), just before the + * connector .late_register() hooks. So we can't allocate the + * panel at connector init time, and can't allocate struct + * intel_panel with a drm_panel sub-struct. For now, use + * __devm_drm_panel_alloc() directly. + * + * The lookups also depend on drm_connector::fwnode being set in + * intel_acpi_assign_connector_fwnodes(). However, if that's + * missing, it will gracefully lead to -EPROBE_DEFER in + * drm_panel_add_follower(). + */ + base = __devm_drm_panel_alloc(dev, sizeof(*base), 0, + &dummy_panel_funcs, + connector->base.connector_type); + if (IS_ERR(base)) { + ret = PTR_ERR(base); + goto err; + } + + panel->base = base; + + drm_panel_add(panel->base); + + drm_dbg_kms(display->drm, "[CONNECTOR:%d:%s] Registered panel device '%s', has fwnode: %s\n", + connector->base.base.id, connector->base.name, + dev_name(dev), str_yes_no(dev_fwnode(dev))); + + intel_panel_sync_state(connector); + } + +out: + return 0; + +err: + intel_backlight_device_unregister(connector); + + return ret; +} + +void intel_panel_unregister(struct intel_connector *connector) +{ + struct intel_panel *panel = &connector->panel; + + if (panel->base) + drm_panel_remove(panel->base); + + intel_backlight_device_unregister(connector); +} + +/* Notify followers, if any, about power being up. */ +void intel_panel_prepare(const struct intel_crtc_state *crtc_state, + const struct drm_connector_state *conn_state) +{ + struct intel_connector *connector = to_intel_connector(conn_state->connector); + struct intel_panel *panel = &connector->panel; + + drm_panel_prepare(panel->base); +} + +/* Notify followers, if any, about power going down. */ +void intel_panel_unprepare(const struct drm_connector_state *old_conn_state) +{ + struct intel_connector *connector = to_intel_connector(old_conn_state->connector); + struct intel_panel *panel = &connector->panel; + + drm_panel_unprepare(panel->base); +} diff --git a/drivers/gpu/drm/i915/display/intel_panel.h b/drivers/gpu/drm/i915/display/intel_panel.h index b60d12322e5d..56a6412cf0fb 100644 --- a/drivers/gpu/drm/i915/display/intel_panel.h +++ b/drivers/gpu/drm/i915/display/intel_panel.h @@ -23,6 +23,8 @@ void intel_panel_init_alloc(struct intel_connector *connector); int intel_panel_init(struct intel_connector *connector, const struct drm_edid *fixed_edid); void intel_panel_fini(struct intel_connector *connector); +int intel_panel_register(struct intel_connector *connector); +void intel_panel_unregister(struct intel_connector *connector); enum drm_connector_status intel_panel_detect(struct drm_connector *connector, bool force); bool intel_panel_use_ssc(struct intel_display *display); @@ -51,4 +53,8 @@ void intel_panel_add_vbt_sdvo_fixed_mode(struct intel_connector *connector); void intel_panel_add_encoder_fixed_mode(struct intel_connector *connector, struct intel_encoder *encoder); +void intel_panel_prepare(const struct intel_crtc_state *crtc_state, + const struct drm_connector_state *conn_state); +void intel_panel_unprepare(const struct drm_connector_state *old_conn_state); + #endif /* __INTEL_PANEL_H__ */ diff --git a/drivers/gpu/drm/i915/gt/intel_gt_requests.c b/drivers/gpu/drm/i915/gt/intel_gt_requests.c index d1a382dfaa1d..ae3557ed6c1e 100644 --- a/drivers/gpu/drm/i915/gt/intel_gt_requests.c +++ b/drivers/gpu/drm/i915/gt/intel_gt_requests.c @@ -252,8 +252,8 @@ void intel_gt_watchdog_work(struct work_struct *work) struct dma_fence *f = &rq->fence; pr_notice("Fence expiration time out i915-%s:%s:%llx!\n", - f->ops->get_driver_name(f), - f->ops->get_timeline_name(f), + dma_fence_driver_name(f), + dma_fence_timeline_name(f), f->seqno); i915_request_cancel(rq, -EINTR); } diff --git a/drivers/gpu/drm/i915/i915_request.c b/drivers/gpu/drm/i915/i915_request.c index c3d27eadc0a7..4874c4f1e4ab 100644 --- a/drivers/gpu/drm/i915/i915_request.c +++ b/drivers/gpu/drm/i915/i915_request.c @@ -2184,7 +2184,7 @@ void i915_request_show(struct drm_printer *m, const char *prefix, int indent) { - const char *name = rq->fence.ops->get_timeline_name((struct dma_fence *)&rq->fence); + const char *name = dma_fence_timeline_name((struct dma_fence *)&rq->fence); char buf[80] = ""; int x = 0; diff --git a/drivers/gpu/drm/i915/i915_sw_fence.c b/drivers/gpu/drm/i915/i915_sw_fence.c index a4902ee08b6e..9edf659d18db 100644 --- a/drivers/gpu/drm/i915/i915_sw_fence.c +++ b/drivers/gpu/drm/i915/i915_sw_fence.c @@ -436,8 +436,8 @@ static void timer_i915_sw_fence_wake(struct timer_list *t) return; pr_notice("Asynchronous wait on fence %s:%s:%llx timed out (hint:%ps)\n", - cb->dma->ops->get_driver_name(cb->dma), - cb->dma->ops->get_timeline_name(cb->dma), + dma_fence_driver_name(cb->dma), + dma_fence_timeline_name(cb->dma), cb->dma->seqno, i915_sw_fence_debug_hint(fence)); diff --git a/drivers/gpu/drm/imagination/pvr_job.c b/drivers/gpu/drm/imagination/pvr_job.c index 59b334d094fa..7564b0f21b42 100644 --- a/drivers/gpu/drm/imagination/pvr_job.c +++ b/drivers/gpu/drm/imagination/pvr_job.c @@ -446,7 +446,7 @@ create_job(struct pvr_device *pvr_dev, if (err) goto err_put_job; - err = pvr_queue_job_init(job); + err = pvr_queue_job_init(job, pvr_file->file->client_id); if (err) goto err_put_job; diff --git a/drivers/gpu/drm/imagination/pvr_queue.c b/drivers/gpu/drm/imagination/pvr_queue.c index 5e9bc0992824..5a41ee79fed6 100644 --- a/drivers/gpu/drm/imagination/pvr_queue.c +++ b/drivers/gpu/drm/imagination/pvr_queue.c @@ -1073,6 +1073,7 @@ static int pvr_queue_cleanup_fw_context(struct pvr_queue *queue) /** * pvr_queue_job_init() - Initialize queue related fields in a pvr_job object. * @job: The job to initialize. + * @drm_client_id: drm_file.client_id submitting the job * * Bind the job to a queue and allocate memory to guarantee pvr_queue_job_arm() * and pvr_queue_job_push() can't fail. We also make sure the context type is @@ -1082,7 +1083,7 @@ static int pvr_queue_cleanup_fw_context(struct pvr_queue *queue) * * 0 on success, or * * An error code if something failed. */ -int pvr_queue_job_init(struct pvr_job *job) +int pvr_queue_job_init(struct pvr_job *job, u64 drm_client_id) { /* Fragment jobs need at least one native fence wait on the geometry job fence. */ u32 min_native_dep_count = job->type == DRM_PVR_JOB_TYPE_FRAGMENT ? 1 : 0; @@ -1099,7 +1100,7 @@ int pvr_queue_job_init(struct pvr_job *job) if (!pvr_cccb_cmdseq_can_fit(&queue->cccb, job_cmds_size(job, min_native_dep_count))) return -E2BIG; - err = drm_sched_job_init(&job->base, &queue->entity, 1, THIS_MODULE); + err = drm_sched_job_init(&job->base, &queue->entity, 1, THIS_MODULE, drm_client_id); if (err) return err; diff --git a/drivers/gpu/drm/imagination/pvr_queue.h b/drivers/gpu/drm/imagination/pvr_queue.h index 93fe9ac9f58c..fc1986d73fc8 100644 --- a/drivers/gpu/drm/imagination/pvr_queue.h +++ b/drivers/gpu/drm/imagination/pvr_queue.h @@ -143,7 +143,7 @@ struct pvr_queue { bool pvr_queue_fence_is_ufo_backed(struct dma_fence *f); -int pvr_queue_job_init(struct pvr_job *job); +int pvr_queue_job_init(struct pvr_job *job, u64 drm_client_id); void pvr_queue_job_cleanup(struct pvr_job *job); diff --git a/drivers/gpu/drm/imx/Kconfig b/drivers/gpu/drm/imx/Kconfig index 03535a15dd8f..3e8c6edbc17c 100644 --- a/drivers/gpu/drm/imx/Kconfig +++ b/drivers/gpu/drm/imx/Kconfig @@ -1,5 +1,6 @@ # SPDX-License-Identifier: GPL-2.0-only +source "drivers/gpu/drm/imx/dc/Kconfig" source "drivers/gpu/drm/imx/dcss/Kconfig" source "drivers/gpu/drm/imx/ipuv3/Kconfig" source "drivers/gpu/drm/imx/lcdc/Kconfig" diff --git a/drivers/gpu/drm/imx/Makefile b/drivers/gpu/drm/imx/Makefile index 86f38e7c7422..c7b317640d71 100644 --- a/drivers/gpu/drm/imx/Makefile +++ b/drivers/gpu/drm/imx/Makefile @@ -1,5 +1,6 @@ # SPDX-License-Identifier: GPL-2.0 +obj-$(CONFIG_DRM_IMX8_DC) += dc/ obj-$(CONFIG_DRM_IMX_DCSS) += dcss/ obj-$(CONFIG_DRM_IMX) += ipuv3/ obj-$(CONFIG_DRM_IMX_LCDC) += lcdc/ diff --git a/drivers/gpu/drm/imx/dc/Kconfig b/drivers/gpu/drm/imx/dc/Kconfig new file mode 100644 index 000000000000..415993207f2e --- /dev/null +++ b/drivers/gpu/drm/imx/dc/Kconfig @@ -0,0 +1,13 @@ +config DRM_IMX8_DC + tristate "Freescale i.MX8 Display Controller Graphics" + depends on DRM && COMMON_CLK && OF && (ARCH_MXC || COMPILE_TEST) + select DRM_CLIENT_SELECTION + select DRM_GEM_DMA_HELPER + select DRM_KMS_HELPER + select DRM_DISPLAY_HELPER + select DRM_BRIDGE_CONNECTOR + select GENERIC_IRQ_CHIP + select REGMAP + select REGMAP_MMIO + help + enable Freescale i.MX8 Display Controller(DC) graphics support diff --git a/drivers/gpu/drm/imx/dc/Makefile b/drivers/gpu/drm/imx/dc/Makefile new file mode 100644 index 000000000000..b9d33c074984 --- /dev/null +++ b/drivers/gpu/drm/imx/dc/Makefile @@ -0,0 +1,7 @@ +# SPDX-License-Identifier: GPL-2.0 + +imx8-dc-drm-objs := dc-cf.o dc-crtc.o dc-de.o dc-drv.o dc-ed.o dc-fg.o dc-fl.o \ + dc-fu.o dc-fw.o dc-ic.o dc-kms.o dc-lb.o dc-pe.o \ + dc-plane.o dc-tc.o + +obj-$(CONFIG_DRM_IMX8_DC) += imx8-dc-drm.o diff --git a/drivers/gpu/drm/imx/dc/dc-cf.c b/drivers/gpu/drm/imx/dc/dc-cf.c new file mode 100644 index 000000000000..2f077161e912 --- /dev/null +++ b/drivers/gpu/drm/imx/dc/dc-cf.c @@ -0,0 +1,172 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright 2024 NXP + */ + +#include <linux/bitfield.h> +#include <linux/bits.h> +#include <linux/component.h> +#include <linux/ioport.h> +#include <linux/mod_devicetable.h> +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/regmap.h> + +#include "dc-drv.h" +#include "dc-pe.h" + +#define STATICCONTROL 0x8 + +#define FRAMEDIMENSIONS 0xc +#define HEIGHT(x) FIELD_PREP(GENMASK(29, 16), ((x) - 1)) +#define WIDTH(x) FIELD_PREP(GENMASK(13, 0), ((x) - 1)) + +#define CONSTANTCOLOR 0x10 +#define BLUE(x) FIELD_PREP(GENMASK(15, 8), (x)) + +static const struct dc_subdev_info dc_cf_info[] = { + { .reg_start = 0x56180960, .id = 0, }, + { .reg_start = 0x561809e0, .id = 1, }, + { .reg_start = 0x561809a0, .id = 4, }, + { .reg_start = 0x56180a20, .id = 5, }, +}; + +static const struct regmap_range dc_cf_regmap_ranges[] = { + regmap_reg_range(STATICCONTROL, CONSTANTCOLOR), +}; + +static const struct regmap_access_table dc_cf_regmap_access_table = { + .yes_ranges = dc_cf_regmap_ranges, + .n_yes_ranges = ARRAY_SIZE(dc_cf_regmap_ranges), +}; + +static const struct regmap_config dc_cf_cfg_regmap_config = { + .name = "cfg", + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + .fast_io = true, + .wr_table = &dc_cf_regmap_access_table, + .rd_table = &dc_cf_regmap_access_table, + .max_register = CONSTANTCOLOR, +}; + +static inline void dc_cf_enable_shden(struct dc_cf *cf) +{ + regmap_write(cf->reg_cfg, STATICCONTROL, SHDEN); +} + +enum dc_link_id dc_cf_get_link_id(struct dc_cf *cf) +{ + return cf->link; +} + +void dc_cf_framedimensions(struct dc_cf *cf, unsigned int w, + unsigned int h) +{ + regmap_write(cf->reg_cfg, FRAMEDIMENSIONS, WIDTH(w) | HEIGHT(h)); +} + +void dc_cf_constantcolor_black(struct dc_cf *cf) +{ + regmap_write(cf->reg_cfg, CONSTANTCOLOR, 0); +} + +void dc_cf_constantcolor_blue(struct dc_cf *cf) +{ + regmap_write(cf->reg_cfg, CONSTANTCOLOR, BLUE(0xff)); +} + +void dc_cf_init(struct dc_cf *cf) +{ + dc_cf_enable_shden(cf); +} + +static int dc_cf_bind(struct device *dev, struct device *master, void *data) +{ + struct platform_device *pdev = to_platform_device(dev); + struct dc_drm_device *dc_drm = data; + struct resource *res_pec; + void __iomem *base_cfg; + struct dc_cf *cf; + int id; + + cf = devm_kzalloc(dev, sizeof(*cf), GFP_KERNEL); + if (!cf) + return -ENOMEM; + + res_pec = platform_get_resource(pdev, IORESOURCE_MEM, 0); + + base_cfg = devm_platform_ioremap_resource_byname(pdev, "cfg"); + if (IS_ERR(base_cfg)) + return PTR_ERR(base_cfg); + + cf->reg_cfg = devm_regmap_init_mmio(dev, base_cfg, + &dc_cf_cfg_regmap_config); + if (IS_ERR(cf->reg_cfg)) + return PTR_ERR(cf->reg_cfg); + + id = dc_subdev_get_id(dc_cf_info, ARRAY_SIZE(dc_cf_info), res_pec); + if (id < 0) { + dev_err(dev, "failed to get instance number: %d\n", id); + return id; + } + + switch (id) { + case 0: + cf->link = LINK_ID_CONSTFRAME0; + dc_drm->cf_cont[0] = cf; + break; + case 1: + cf->link = LINK_ID_CONSTFRAME1; + dc_drm->cf_cont[1] = cf; + break; + case 4: + cf->link = LINK_ID_CONSTFRAME4; + dc_drm->cf_safe[0] = cf; + break; + case 5: + cf->link = LINK_ID_CONSTFRAME5; + dc_drm->cf_safe[1] = cf; + break; + } + + return 0; +} + +static const struct component_ops dc_cf_ops = { + .bind = dc_cf_bind, +}; + +static int dc_cf_probe(struct platform_device *pdev) +{ + int ret; + + ret = component_add(&pdev->dev, &dc_cf_ops); + if (ret) + return dev_err_probe(&pdev->dev, ret, + "failed to add component\n"); + + return 0; +} + +static void dc_cf_remove(struct platform_device *pdev) +{ + component_del(&pdev->dev, &dc_cf_ops); +} + +static const struct of_device_id dc_cf_dt_ids[] = { + { .compatible = "fsl,imx8qxp-dc-constframe" }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, dc_cf_dt_ids); + +struct platform_driver dc_cf_driver = { + .probe = dc_cf_probe, + .remove = dc_cf_remove, + .driver = { + .name = "imx8-dc-constframe", + .suppress_bind_attrs = true, + .of_match_table = dc_cf_dt_ids, + }, +}; diff --git a/drivers/gpu/drm/imx/dc/dc-crtc.c b/drivers/gpu/drm/imx/dc/dc-crtc.c new file mode 100644 index 000000000000..31d3a982deaf --- /dev/null +++ b/drivers/gpu/drm/imx/dc/dc-crtc.c @@ -0,0 +1,555 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright 2024 NXP + */ + +#include <linux/completion.h> +#include <linux/container_of.h> +#include <linux/interrupt.h> +#include <linux/irqreturn.h> +#include <linux/pm_runtime.h> +#include <linux/spinlock.h> + +#include <drm/drm_atomic.h> +#include <drm/drm_atomic_helper.h> +#include <drm/drm_atomic_state_helper.h> +#include <drm/drm_crtc.h> +#include <drm/drm_device.h> +#include <drm/drm_drv.h> +#include <drm/drm_modes.h> +#include <drm/drm_modeset_helper_vtables.h> +#include <drm/drm_plane.h> +#include <drm/drm_print.h> +#include <drm/drm_vblank.h> + +#include "dc-de.h" +#include "dc-drv.h" +#include "dc-kms.h" +#include "dc-pe.h" + +#define dc_crtc_dbg(crtc, fmt, ...) \ +do { \ + struct drm_crtc *_crtc = (crtc); \ + drm_dbg_kms(_crtc->dev, "[CRTC:%d:%s] " fmt, \ + _crtc->base.id, _crtc->name, ##__VA_ARGS__); \ +} while (0) + +#define dc_crtc_err(crtc, fmt, ...) \ +do { \ + struct drm_crtc *_crtc = (crtc); \ + drm_err(_crtc->dev, "[CRTC:%d:%s] " fmt, \ + _crtc->base.id, _crtc->name, ##__VA_ARGS__); \ +} while (0) + +#define DC_CRTC_WAIT_FOR_COMPLETION_TIMEOUT(c) \ +do { \ + unsigned long ret; \ + ret = wait_for_completion_timeout(&dc_crtc->c, HZ); \ + if (ret == 0) \ + dc_crtc_err(crtc, "%s: wait for " #c " timeout\n", \ + __func__); \ +} while (0) + +#define DC_CRTC_CHECK_FRAMEGEN_FIFO(fg) \ +do { \ + struct dc_fg *_fg = (fg); \ + if (dc_fg_secondary_requests_to_read_empty_fifo(_fg)) { \ + dc_fg_secondary_clear_channel_status(_fg); \ + dc_crtc_err(crtc, "%s: FrameGen FIFO empty\n", \ + __func__); \ + } \ +} while (0) + +#define DC_CRTC_WAIT_FOR_FRAMEGEN_SECONDARY_SYNCUP(fg) \ +do { \ + if (dc_fg_wait_for_secondary_syncup(fg)) \ + dc_crtc_err(crtc, \ + "%s: FrameGen secondary channel isn't syncup\n",\ + __func__); \ +} while (0) + +static inline struct dc_crtc *to_dc_crtc(struct drm_crtc *crtc) +{ + return container_of(crtc, struct dc_crtc, base); +} + +static u32 dc_crtc_get_vblank_counter(struct drm_crtc *crtc) +{ + struct dc_crtc *dc_crtc = to_dc_crtc(crtc); + + return dc_fg_get_frame_index(dc_crtc->fg); +} + +static int dc_crtc_enable_vblank(struct drm_crtc *crtc) +{ + struct dc_crtc *dc_crtc = to_dc_crtc(crtc); + + enable_irq(dc_crtc->irq_dec_framecomplete); + + return 0; +} + +static void dc_crtc_disable_vblank(struct drm_crtc *crtc) +{ + struct dc_crtc *dc_crtc = to_dc_crtc(crtc); + + /* nosync due to atomic context */ + disable_irq_nosync(dc_crtc->irq_dec_framecomplete); +} + +static const struct drm_crtc_funcs dc_crtc_funcs = { + .reset = drm_atomic_helper_crtc_reset, + .destroy = drm_crtc_cleanup, + .set_config = drm_atomic_helper_set_config, + .page_flip = drm_atomic_helper_page_flip, + .atomic_duplicate_state = drm_atomic_helper_crtc_duplicate_state, + .atomic_destroy_state = drm_atomic_helper_crtc_destroy_state, + .get_vblank_counter = dc_crtc_get_vblank_counter, + .enable_vblank = dc_crtc_enable_vblank, + .disable_vblank = dc_crtc_disable_vblank, + .get_vblank_timestamp = drm_crtc_vblank_helper_get_vblank_timestamp, +}; + +static void dc_crtc_queue_state_event(struct drm_crtc_state *crtc_state) +{ + struct drm_crtc *crtc = crtc_state->crtc; + struct dc_crtc *dc_crtc = to_dc_crtc(crtc); + + spin_lock_irq(&crtc->dev->event_lock); + if (crtc_state->event) { + WARN_ON(drm_crtc_vblank_get(crtc)); + WARN_ON(dc_crtc->event); + dc_crtc->event = crtc_state->event; + crtc_state->event = NULL; + } + spin_unlock_irq(&crtc->dev->event_lock); +} + +static inline enum drm_mode_status +dc_crtc_check_clock(struct dc_crtc *dc_crtc, int clk_khz) +{ + return dc_fg_check_clock(dc_crtc->fg, clk_khz); +} + +static enum drm_mode_status +dc_crtc_mode_valid(struct drm_crtc *crtc, const struct drm_display_mode *mode) +{ + struct dc_crtc *dc_crtc = to_dc_crtc(crtc); + enum drm_mode_status status; + + status = dc_crtc_check_clock(dc_crtc, mode->clock); + if (status != MODE_OK) + return status; + + if (mode->crtc_clock > DC_FRAMEGEN_MAX_CLOCK_KHZ) + return MODE_CLOCK_HIGH; + + return MODE_OK; +} + +static int +dc_crtc_atomic_check(struct drm_crtc *crtc, struct drm_atomic_state *state) +{ + struct drm_crtc_state *new_crtc_state = + drm_atomic_get_new_crtc_state(state, crtc); + struct drm_display_mode *adj = &new_crtc_state->adjusted_mode; + struct dc_crtc *dc_crtc = to_dc_crtc(crtc); + enum drm_mode_status status; + + status = dc_crtc_check_clock(dc_crtc, adj->clock); + if (status != MODE_OK) + return -EINVAL; + + return 0; +} + +static void +dc_crtc_atomic_begin(struct drm_crtc *crtc, struct drm_atomic_state *state) +{ + struct drm_crtc_state *new_crtc_state = + drm_atomic_get_new_crtc_state(state, crtc); + struct dc_drm_device *dc_drm = to_dc_drm_device(crtc->dev); + int idx, ret; + + if (!drm_atomic_crtc_needs_modeset(new_crtc_state) || + !new_crtc_state->active) + return; + + if (!drm_dev_enter(crtc->dev, &idx)) + return; + + /* request pixel engine power-on when CRTC starts to be active */ + ret = pm_runtime_resume_and_get(dc_drm->pe->dev); + if (ret) + dc_crtc_err(crtc, "failed to get DC pixel engine RPM: %d\n", + ret); + + drm_dev_exit(idx); +} + +static void +dc_crtc_atomic_flush(struct drm_crtc *crtc, struct drm_atomic_state *state) +{ + struct drm_crtc_state *old_crtc_state = + drm_atomic_get_old_crtc_state(state, crtc); + struct drm_crtc_state *new_crtc_state = + drm_atomic_get_new_crtc_state(state, crtc); + struct dc_crtc *dc_crtc = to_dc_crtc(crtc); + int idx; + + if (drm_atomic_crtc_needs_modeset(new_crtc_state) || + (!old_crtc_state->active && !new_crtc_state->active)) + return; + + if (!drm_dev_enter(crtc->dev, &idx)) + goto out; + + enable_irq(dc_crtc->irq_ed_cont_shdload); + + /* flush plane update out to display */ + dc_ed_pec_sync_trigger(dc_crtc->ed_cont); + + DC_CRTC_WAIT_FOR_COMPLETION_TIMEOUT(ed_cont_shdload_done); + + disable_irq(dc_crtc->irq_ed_cont_shdload); + + DC_CRTC_CHECK_FRAMEGEN_FIFO(dc_crtc->fg); + + drm_dev_exit(idx); + +out: + dc_crtc_queue_state_event(new_crtc_state); +} + +static void +dc_crtc_atomic_enable(struct drm_crtc *crtc, struct drm_atomic_state *state) +{ + struct drm_crtc_state *new_crtc_state = + drm_atomic_get_new_crtc_state(state, crtc); + struct drm_display_mode *adj = &new_crtc_state->adjusted_mode; + struct dc_crtc *dc_crtc = to_dc_crtc(crtc); + enum dc_link_id cf_link; + int idx, ret; + + dc_crtc_dbg(crtc, "mode " DRM_MODE_FMT "\n", DRM_MODE_ARG(adj)); + + drm_crtc_vblank_on(crtc); + + if (!drm_dev_enter(crtc->dev, &idx)) + goto out; + + /* request display engine power-on when CRTC is enabled */ + ret = pm_runtime_resume_and_get(dc_crtc->de->dev); + if (ret < 0) + dc_crtc_err(crtc, "failed to get DC display engine RPM: %d\n", + ret); + + enable_irq(dc_crtc->irq_dec_shdload); + enable_irq(dc_crtc->irq_ed_cont_shdload); + enable_irq(dc_crtc->irq_ed_safe_shdload); + + dc_fg_cfg_videomode(dc_crtc->fg, adj); + + dc_cf_framedimensions(dc_crtc->cf_cont, + adj->crtc_hdisplay, adj->crtc_vdisplay); + dc_cf_framedimensions(dc_crtc->cf_safe, + adj->crtc_hdisplay, adj->crtc_vdisplay); + + /* constframe in safety stream shows blue frame */ + dc_cf_constantcolor_blue(dc_crtc->cf_safe); + cf_link = dc_cf_get_link_id(dc_crtc->cf_safe); + dc_ed_pec_src_sel(dc_crtc->ed_safe, cf_link); + + /* show CRTC background if no plane is enabled */ + if (new_crtc_state->plane_mask == 0) { + /* constframe in content stream shows black frame */ + dc_cf_constantcolor_black(dc_crtc->cf_cont); + + cf_link = dc_cf_get_link_id(dc_crtc->cf_cont); + dc_ed_pec_src_sel(dc_crtc->ed_cont, cf_link); + } + + dc_fg_enable_clock(dc_crtc->fg); + dc_ed_pec_sync_trigger(dc_crtc->ed_cont); + dc_ed_pec_sync_trigger(dc_crtc->ed_safe); + dc_fg_shdtokgen(dc_crtc->fg); + dc_fg_enable(dc_crtc->fg); + + DC_CRTC_WAIT_FOR_COMPLETION_TIMEOUT(ed_safe_shdload_done); + DC_CRTC_WAIT_FOR_COMPLETION_TIMEOUT(ed_cont_shdload_done); + DC_CRTC_WAIT_FOR_COMPLETION_TIMEOUT(dec_shdload_done); + + disable_irq(dc_crtc->irq_ed_safe_shdload); + disable_irq(dc_crtc->irq_ed_cont_shdload); + disable_irq(dc_crtc->irq_dec_shdload); + + DC_CRTC_WAIT_FOR_FRAMEGEN_SECONDARY_SYNCUP(dc_crtc->fg); + + DC_CRTC_CHECK_FRAMEGEN_FIFO(dc_crtc->fg); + + drm_dev_exit(idx); + +out: + dc_crtc_queue_state_event(new_crtc_state); +} + +static void +dc_crtc_atomic_disable(struct drm_crtc *crtc, struct drm_atomic_state *state) +{ + struct drm_crtc_state *new_crtc_state = + drm_atomic_get_new_crtc_state(state, crtc); + struct dc_drm_device *dc_drm = to_dc_drm_device(crtc->dev); + struct dc_crtc *dc_crtc = to_dc_crtc(crtc); + int idx, ret; + + if (!drm_dev_enter(crtc->dev, &idx)) + goto out; + + enable_irq(dc_crtc->irq_dec_seqcomplete); + dc_fg_disable(dc_crtc->fg); + DC_CRTC_WAIT_FOR_COMPLETION_TIMEOUT(dec_seqcomplete_done); + disable_irq(dc_crtc->irq_dec_seqcomplete); + + dc_fg_disable_clock(dc_crtc->fg); + + /* request pixel engine power-off as plane is off too */ + ret = pm_runtime_put(dc_drm->pe->dev); + if (ret) + dc_crtc_err(crtc, "failed to put DC pixel engine RPM: %d\n", + ret); + + /* request display engine power-off when CRTC is disabled */ + ret = pm_runtime_put(dc_crtc->de->dev); + if (ret < 0) + dc_crtc_err(crtc, "failed to put DC display engine RPM: %d\n", + ret); + + drm_dev_exit(idx); + +out: + drm_crtc_vblank_off(crtc); + + spin_lock_irq(&crtc->dev->event_lock); + if (new_crtc_state->event && !new_crtc_state->active) { + drm_crtc_send_vblank_event(crtc, new_crtc_state->event); + new_crtc_state->event = NULL; + } + spin_unlock_irq(&crtc->dev->event_lock); +} + +static bool dc_crtc_get_scanout_position(struct drm_crtc *crtc, + bool in_vblank_irq, + int *vpos, int *hpos, + ktime_t *stime, ktime_t *etime, + const struct drm_display_mode *mode) +{ + struct dc_crtc *dc_crtc = to_dc_crtc(crtc); + int vdisplay = mode->crtc_vdisplay; + int vtotal = mode->crtc_vtotal; + bool reliable; + int line; + int idx; + + if (stime) + *stime = ktime_get(); + + if (!drm_dev_enter(crtc->dev, &idx)) { + reliable = false; + *vpos = 0; + *hpos = 0; + goto out; + } + + /* line index starts with 0 for the first active output line */ + line = dc_fg_get_line_index(dc_crtc->fg); + + if (line < vdisplay) + /* active scanout area - positive */ + *vpos = line + 1; + else + /* inside vblank - negative */ + *vpos = line - (vtotal - 1); + + *hpos = 0; + + reliable = true; + + drm_dev_exit(idx); +out: + if (etime) + *etime = ktime_get(); + + return reliable; +} + +static const struct drm_crtc_helper_funcs dc_helper_funcs = { + .mode_valid = dc_crtc_mode_valid, + .atomic_check = dc_crtc_atomic_check, + .atomic_begin = dc_crtc_atomic_begin, + .atomic_flush = dc_crtc_atomic_flush, + .atomic_enable = dc_crtc_atomic_enable, + .atomic_disable = dc_crtc_atomic_disable, + .get_scanout_position = dc_crtc_get_scanout_position, +}; + +static irqreturn_t dc_crtc_irq_handler_dec_framecomplete(int irq, void *dev_id) +{ + struct dc_crtc *dc_crtc = dev_id; + struct drm_crtc *crtc = &dc_crtc->base; + unsigned long flags; + + drm_crtc_handle_vblank(crtc); + + spin_lock_irqsave(&crtc->dev->event_lock, flags); + if (dc_crtc->event) { + drm_crtc_send_vblank_event(crtc, dc_crtc->event); + dc_crtc->event = NULL; + drm_crtc_vblank_put(crtc); + } + spin_unlock_irqrestore(&crtc->dev->event_lock, flags); + + return IRQ_HANDLED; +} + +static irqreturn_t +dc_crtc_irq_handler_dec_seqcomplete_done(int irq, void *dev_id) +{ + struct dc_crtc *dc_crtc = dev_id; + + complete(&dc_crtc->dec_seqcomplete_done); + + return IRQ_HANDLED; +} + +static irqreturn_t dc_crtc_irq_handler_dec_shdload_done(int irq, void *dev_id) +{ + struct dc_crtc *dc_crtc = dev_id; + + complete(&dc_crtc->dec_shdload_done); + + return IRQ_HANDLED; +} + +static irqreturn_t +dc_crtc_irq_handler_ed_cont_shdload_done(int irq, void *dev_id) +{ + struct dc_crtc *dc_crtc = dev_id; + + complete(&dc_crtc->ed_cont_shdload_done); + + return IRQ_HANDLED; +} + +static irqreturn_t +dc_crtc_irq_handler_ed_safe_shdload_done(int irq, void *dev_id) +{ + struct dc_crtc *dc_crtc = dev_id; + + complete(&dc_crtc->ed_safe_shdload_done); + + return IRQ_HANDLED; +} + +static int dc_crtc_request_irqs(struct drm_device *drm, struct dc_crtc *dc_crtc) +{ + struct { + struct device *dev; + unsigned int irq; + irqreturn_t (*irq_handler)(int irq, void *dev_id); + } irqs[DC_CRTC_IRQS] = { + { + dc_crtc->de->dev, + dc_crtc->irq_dec_framecomplete, + dc_crtc_irq_handler_dec_framecomplete, + }, { + dc_crtc->de->dev, + dc_crtc->irq_dec_seqcomplete, + dc_crtc_irq_handler_dec_seqcomplete_done, + }, { + dc_crtc->de->dev, + dc_crtc->irq_dec_shdload, + dc_crtc_irq_handler_dec_shdload_done, + }, { + dc_crtc->ed_cont->dev, + dc_crtc->irq_ed_cont_shdload, + dc_crtc_irq_handler_ed_cont_shdload_done, + }, { + dc_crtc->ed_safe->dev, + dc_crtc->irq_ed_safe_shdload, + dc_crtc_irq_handler_ed_safe_shdload_done, + }, + }; + int i, ret; + + for (i = 0; i < DC_CRTC_IRQS; i++) { + struct dc_crtc_irq *irq = &dc_crtc->irqs[i]; + + ret = devm_request_irq(irqs[i].dev, irqs[i].irq, + irqs[i].irq_handler, IRQF_NO_AUTOEN, + dev_name(irqs[i].dev), dc_crtc); + if (ret) { + dev_err(irqs[i].dev, "failed to request irq(%u): %d\n", + irqs[i].irq, ret); + return ret; + } + + irq->dc_crtc = dc_crtc; + irq->irq = irqs[i].irq; + } + + return 0; +} + +int dc_crtc_init(struct dc_drm_device *dc_drm, int crtc_index) +{ + struct dc_crtc *dc_crtc = &dc_drm->dc_crtc[crtc_index]; + struct drm_device *drm = &dc_drm->base; + struct dc_de *de = dc_drm->de[crtc_index]; + struct dc_pe *pe = dc_drm->pe; + struct dc_plane *dc_primary; + int ret; + + dc_crtc->de = de; + + init_completion(&dc_crtc->dec_seqcomplete_done); + init_completion(&dc_crtc->dec_shdload_done); + init_completion(&dc_crtc->ed_cont_shdload_done); + init_completion(&dc_crtc->ed_safe_shdload_done); + + dc_crtc->cf_cont = pe->cf_cont[crtc_index]; + dc_crtc->cf_safe = pe->cf_safe[crtc_index]; + dc_crtc->ed_cont = pe->ed_cont[crtc_index]; + dc_crtc->ed_safe = pe->ed_safe[crtc_index]; + dc_crtc->fg = de->fg; + + dc_crtc->irq_dec_framecomplete = de->irq_framecomplete; + dc_crtc->irq_dec_seqcomplete = de->irq_seqcomplete; + dc_crtc->irq_dec_shdload = de->irq_shdload; + dc_crtc->irq_ed_safe_shdload = dc_crtc->ed_safe->irq_shdload; + dc_crtc->irq_ed_cont_shdload = dc_crtc->ed_cont->irq_shdload; + + dc_primary = &dc_drm->dc_primary[crtc_index]; + ret = dc_plane_init(dc_drm, dc_primary); + if (ret) { + dev_err(de->dev, "failed to initialize primary plane: %d\n", + ret); + return ret; + } + + drm_crtc_helper_add(&dc_crtc->base, &dc_helper_funcs); + + ret = drm_crtc_init_with_planes(drm, &dc_crtc->base, &dc_primary->base, + NULL, &dc_crtc_funcs, NULL); + if (ret) + dev_err(de->dev, "failed to add CRTC: %d\n", ret); + + return ret; +} + +int dc_crtc_post_init(struct dc_drm_device *dc_drm, int crtc_index) +{ + struct dc_crtc *dc_crtc = &dc_drm->dc_crtc[crtc_index]; + struct drm_device *drm = &dc_drm->base; + + return dc_crtc_request_irqs(drm, dc_crtc); +} diff --git a/drivers/gpu/drm/imx/dc/dc-de.c b/drivers/gpu/drm/imx/dc/dc-de.c new file mode 100644 index 000000000000..5a3125596fdf --- /dev/null +++ b/drivers/gpu/drm/imx/dc/dc-de.c @@ -0,0 +1,177 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright 2024 NXP + */ + +#include <linux/component.h> +#include <linux/mod_devicetable.h> +#include <linux/module.h> +#include <linux/of_platform.h> +#include <linux/platform_device.h> +#include <linux/pm.h> +#include <linux/pm_runtime.h> +#include <linux/regmap.h> + +#include "dc-de.h" +#include "dc-drv.h" + +#define POLARITYCTRL 0xc +#define POLEN_HIGH BIT(2) + +static const struct dc_subdev_info dc_de_info[] = { + { .reg_start = 0x5618b400, .id = 0, }, + { .reg_start = 0x5618b420, .id = 1, }, +}; + +static const struct regmap_range dc_de_regmap_ranges[] = { + regmap_reg_range(POLARITYCTRL, POLARITYCTRL), +}; + +static const struct regmap_access_table dc_de_regmap_access_table = { + .yes_ranges = dc_de_regmap_ranges, + .n_yes_ranges = ARRAY_SIZE(dc_de_regmap_ranges), +}; + +static const struct regmap_config dc_de_top_regmap_config = { + .name = "top", + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + .fast_io = true, + .wr_table = &dc_de_regmap_access_table, + .rd_table = &dc_de_regmap_access_table, + .max_register = POLARITYCTRL, +}; + +static inline void dc_dec_init(struct dc_de *de) +{ + regmap_write_bits(de->reg_top, POLARITYCTRL, POLARITYCTRL, POLEN_HIGH); +} + +static int dc_de_bind(struct device *dev, struct device *master, void *data) +{ + struct platform_device *pdev = to_platform_device(dev); + struct dc_drm_device *dc_drm = data; + struct resource *res_top; + void __iomem *base_top; + struct dc_de *de; + int ret, id; + + de = devm_kzalloc(dev, sizeof(*de), GFP_KERNEL); + if (!de) + return -ENOMEM; + + base_top = devm_platform_get_and_ioremap_resource(pdev, 0, &res_top); + if (IS_ERR(base_top)) + return PTR_ERR(base_top); + + de->reg_top = devm_regmap_init_mmio(dev, base_top, + &dc_de_top_regmap_config); + if (IS_ERR(de->reg_top)) + return PTR_ERR(de->reg_top); + + de->irq_shdload = platform_get_irq_byname(pdev, "shdload"); + if (de->irq_shdload < 0) + return de->irq_shdload; + + de->irq_framecomplete = platform_get_irq_byname(pdev, "framecomplete"); + if (de->irq_framecomplete < 0) + return de->irq_framecomplete; + + de->irq_seqcomplete = platform_get_irq_byname(pdev, "seqcomplete"); + if (de->irq_seqcomplete < 0) + return de->irq_seqcomplete; + + de->dev = dev; + + dev_set_drvdata(dev, de); + + ret = devm_pm_runtime_enable(dev); + if (ret) + return ret; + + id = dc_subdev_get_id(dc_de_info, ARRAY_SIZE(dc_de_info), res_top); + if (id < 0) { + dev_err(dev, "failed to get instance number: %d\n", id); + return id; + } + + dc_drm->de[id] = de; + + return 0; +} + +/* + * It's possible to get the child device pointers from the child component + * bind callbacks, but it depends on the component helper behavior to bind + * the display engine component first. To avoid the dependency, post bind + * to get the pointers from dc_drm in a safe manner. + */ +void dc_de_post_bind(struct dc_drm_device *dc_drm) +{ + struct dc_de *de; + int i; + + for (i = 0; i < DC_DISPLAYS; i++) { + de = dc_drm->de[i]; + de->fg = dc_drm->fg[i]; + de->tc = dc_drm->tc[i]; + } +} + +static const struct component_ops dc_de_ops = { + .bind = dc_de_bind, +}; + +static int dc_de_probe(struct platform_device *pdev) +{ + int ret; + + ret = devm_of_platform_populate(&pdev->dev); + if (ret < 0) + return ret; + + ret = component_add(&pdev->dev, &dc_de_ops); + if (ret) + return dev_err_probe(&pdev->dev, ret, + "failed to add component\n"); + + return 0; +} + +static void dc_de_remove(struct platform_device *pdev) +{ + component_del(&pdev->dev, &dc_de_ops); +} + +static int dc_de_runtime_resume(struct device *dev) +{ + struct dc_de *de = dev_get_drvdata(dev); + + dc_dec_init(de); + dc_fg_init(de->fg); + dc_tc_init(de->tc); + + return 0; +} + +static const struct dev_pm_ops dc_de_pm_ops = { + RUNTIME_PM_OPS(NULL, dc_de_runtime_resume, NULL) +}; + +static const struct of_device_id dc_de_dt_ids[] = { + { .compatible = "fsl,imx8qxp-dc-display-engine" }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, dc_de_dt_ids); + +struct platform_driver dc_de_driver = { + .probe = dc_de_probe, + .remove = dc_de_remove, + .driver = { + .name = "imx8-dc-display-engine", + .suppress_bind_attrs = true, + .of_match_table = dc_de_dt_ids, + .pm = pm_sleep_ptr(&dc_de_pm_ops), + }, +}; diff --git a/drivers/gpu/drm/imx/dc/dc-de.h b/drivers/gpu/drm/imx/dc/dc-de.h new file mode 100644 index 000000000000..211f3fcc1a9a --- /dev/null +++ b/drivers/gpu/drm/imx/dc/dc-de.h @@ -0,0 +1,59 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Copyright 2024 NXP + */ + +#ifndef __DC_DISPLAY_ENGINE_H__ +#define __DC_DISPLAY_ENGINE_H__ + +#include <linux/clk.h> +#include <linux/device.h> +#include <linux/regmap.h> +#include <drm/drm_modes.h> + +#define DC_DISPLAYS 2 + +#define DC_FRAMEGEN_MAX_FRAME_INDEX 0x3ffff +#define DC_FRAMEGEN_MAX_CLOCK_KHZ 300000 + +struct dc_fg { + struct device *dev; + struct regmap *reg; + struct clk *clk_disp; +}; + +struct dc_tc { + struct device *dev; + struct regmap *reg; +}; + +struct dc_de { + struct device *dev; + struct regmap *reg_top; + struct dc_fg *fg; + struct dc_tc *tc; + int irq_shdload; + int irq_framecomplete; + int irq_seqcomplete; +}; + +/* Frame Generator Unit */ +void dc_fg_cfg_videomode(struct dc_fg *fg, struct drm_display_mode *m); +void dc_fg_enable(struct dc_fg *fg); +void dc_fg_disable(struct dc_fg *fg); +void dc_fg_shdtokgen(struct dc_fg *fg); +u32 dc_fg_get_frame_index(struct dc_fg *fg); +u32 dc_fg_get_line_index(struct dc_fg *fg); +bool dc_fg_wait_for_frame_index_moving(struct dc_fg *fg); +bool dc_fg_secondary_requests_to_read_empty_fifo(struct dc_fg *fg); +void dc_fg_secondary_clear_channel_status(struct dc_fg *fg); +int dc_fg_wait_for_secondary_syncup(struct dc_fg *fg); +void dc_fg_enable_clock(struct dc_fg *fg); +void dc_fg_disable_clock(struct dc_fg *fg); +enum drm_mode_status dc_fg_check_clock(struct dc_fg *fg, int clk_khz); +void dc_fg_init(struct dc_fg *fg); + +/* Timing Controller Unit */ +void dc_tc_init(struct dc_tc *tc); + +#endif /* __DC_DISPLAY_ENGINE_H__ */ diff --git a/drivers/gpu/drm/imx/dc/dc-drv.c b/drivers/gpu/drm/imx/dc/dc-drv.c new file mode 100644 index 000000000000..04f021d2d6cf --- /dev/null +++ b/drivers/gpu/drm/imx/dc/dc-drv.c @@ -0,0 +1,293 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright 2024 NXP + */ + +#include <linux/clk.h> +#include <linux/component.h> +#include <linux/device.h> +#include <linux/dma-mapping.h> +#include <linux/mod_devicetable.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/of_platform.h> +#include <linux/platform_device.h> +#include <linux/pm.h> +#include <linux/pm_runtime.h> + +#include <drm/clients/drm_client_setup.h> +#include <drm/drm_atomic_helper.h> +#include <drm/drm_drv.h> +#include <drm/drm_fbdev_dma.h> +#include <drm/drm_fourcc.h> +#include <drm/drm_gem_dma_helper.h> +#include <drm/drm_managed.h> +#include <drm/drm_modeset_helper.h> +#include <drm/drm_of.h> + +#include "dc-de.h" +#include "dc-drv.h" +#include "dc-pe.h" + +struct dc_priv { + struct drm_device *drm; + struct clk *clk_cfg; +}; + +DEFINE_DRM_GEM_DMA_FOPS(dc_drm_driver_fops); + +static struct drm_driver dc_drm_driver = { + .driver_features = DRIVER_MODESET | DRIVER_GEM | DRIVER_ATOMIC, + DRM_GEM_DMA_DRIVER_OPS, + DRM_FBDEV_DMA_DRIVER_OPS, + .fops = &dc_drm_driver_fops, + .name = "imx8-dc", + .desc = "i.MX8 DC DRM graphics", + .major = 1, + .minor = 0, + .patchlevel = 0, +}; + +static void +dc_add_components(struct device *dev, struct component_match **matchptr) +{ + struct device_node *child, *grandchild; + + for_each_available_child_of_node(dev->of_node, child) { + /* The interrupt controller is not a component. */ + if (of_device_is_compatible(child, "fsl,imx8qxp-dc-intc")) + continue; + + drm_of_component_match_add(dev, matchptr, component_compare_of, + child); + + for_each_available_child_of_node(child, grandchild) + drm_of_component_match_add(dev, matchptr, + component_compare_of, + grandchild); + } +} + +static int dc_drm_component_bind_all(struct dc_drm_device *dc_drm) +{ + struct drm_device *drm = &dc_drm->base; + int ret; + + ret = component_bind_all(drm->dev, dc_drm); + if (ret) + return ret; + + dc_de_post_bind(dc_drm); + dc_pe_post_bind(dc_drm); + + return 0; +} + +static void dc_drm_component_unbind_all(void *ptr) +{ + struct dc_drm_device *dc_drm = ptr; + struct drm_device *drm = &dc_drm->base; + + component_unbind_all(drm->dev, dc_drm); +} + +static int dc_drm_bind(struct device *dev) +{ + struct dc_priv *priv = dev_get_drvdata(dev); + struct dc_drm_device *dc_drm; + struct drm_device *drm; + int ret; + + dc_drm = devm_drm_dev_alloc(dev, &dc_drm_driver, struct dc_drm_device, + base); + if (IS_ERR(dc_drm)) + return PTR_ERR(dc_drm); + + drm = &dc_drm->base; + + ret = dc_drm_component_bind_all(dc_drm); + if (ret) + return ret; + + ret = devm_add_action_or_reset(dev, dc_drm_component_unbind_all, + dc_drm); + if (ret) + return ret; + + ret = dc_kms_init(dc_drm); + if (ret) + return ret; + + ret = drm_dev_register(drm, 0); + if (ret) { + dev_err(dev, "failed to register drm device: %d\n", ret); + goto err; + } + + drm_client_setup_with_fourcc(drm, DRM_FORMAT_XRGB8888); + + priv->drm = drm; + + return 0; + +err: + dc_kms_uninit(dc_drm); + + return ret; +} + +static void dc_drm_unbind(struct device *dev) +{ + struct dc_priv *priv = dev_get_drvdata(dev); + struct dc_drm_device *dc_drm = to_dc_drm_device(priv->drm); + struct drm_device *drm = &dc_drm->base; + + priv->drm = NULL; + drm_dev_unplug(drm); + dc_kms_uninit(dc_drm); + drm_atomic_helper_shutdown(drm); +} + +static const struct component_master_ops dc_drm_ops = { + .bind = dc_drm_bind, + .unbind = dc_drm_unbind, +}; + +static int dc_probe(struct platform_device *pdev) +{ + struct component_match *match = NULL; + struct dc_priv *priv; + int ret; + + priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + priv->clk_cfg = devm_clk_get(&pdev->dev, NULL); + if (IS_ERR(priv->clk_cfg)) + return dev_err_probe(&pdev->dev, PTR_ERR(priv->clk_cfg), + "failed to get cfg clock\n"); + + dev_set_drvdata(&pdev->dev, priv); + + ret = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(32)); + if (ret) + return ret; + + ret = devm_pm_runtime_enable(&pdev->dev); + if (ret) + return ret; + + ret = devm_of_platform_populate(&pdev->dev); + if (ret) + return ret; + + dc_add_components(&pdev->dev, &match); + + ret = component_master_add_with_match(&pdev->dev, &dc_drm_ops, match); + if (ret) + return dev_err_probe(&pdev->dev, ret, + "failed to add component master\n"); + + return 0; +} + +static void dc_remove(struct platform_device *pdev) +{ + component_master_del(&pdev->dev, &dc_drm_ops); +} + +static int dc_runtime_suspend(struct device *dev) +{ + struct dc_priv *priv = dev_get_drvdata(dev); + + clk_disable_unprepare(priv->clk_cfg); + + return 0; +} + +static int dc_runtime_resume(struct device *dev) +{ + struct dc_priv *priv = dev_get_drvdata(dev); + int ret; + + ret = clk_prepare_enable(priv->clk_cfg); + if (ret) + dev_err(dev, "failed to enable cfg clock: %d\n", ret); + + return ret; +} + +static int dc_suspend(struct device *dev) +{ + struct dc_priv *priv = dev_get_drvdata(dev); + + return drm_mode_config_helper_suspend(priv->drm); +} + +static int dc_resume(struct device *dev) +{ + struct dc_priv *priv = dev_get_drvdata(dev); + + return drm_mode_config_helper_resume(priv->drm); +} + +static void dc_shutdown(struct platform_device *pdev) +{ + struct dc_priv *priv = dev_get_drvdata(&pdev->dev); + + drm_atomic_helper_shutdown(priv->drm); +} + +static const struct dev_pm_ops dc_pm_ops = { + RUNTIME_PM_OPS(dc_runtime_suspend, dc_runtime_resume, NULL) + SYSTEM_SLEEP_PM_OPS(dc_suspend, dc_resume) +}; + +static const struct of_device_id dc_dt_ids[] = { + { .compatible = "fsl,imx8qxp-dc", }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, dc_dt_ids); + +static struct platform_driver dc_driver = { + .probe = dc_probe, + .remove = dc_remove, + .shutdown = dc_shutdown, + .driver = { + .name = "imx8-dc", + .of_match_table = dc_dt_ids, + .pm = pm_sleep_ptr(&dc_pm_ops), + }, +}; + +static struct platform_driver * const dc_drivers[] = { + &dc_cf_driver, + &dc_de_driver, + &dc_ed_driver, + &dc_fg_driver, + &dc_fl_driver, + &dc_fw_driver, + &dc_ic_driver, + &dc_lb_driver, + &dc_pe_driver, + &dc_tc_driver, + &dc_driver, +}; + +static int __init dc_drm_init(void) +{ + return platform_register_drivers(dc_drivers, ARRAY_SIZE(dc_drivers)); +} + +static void __exit dc_drm_exit(void) +{ + platform_unregister_drivers(dc_drivers, ARRAY_SIZE(dc_drivers)); +} + +module_init(dc_drm_init); +module_exit(dc_drm_exit); + +MODULE_DESCRIPTION("i.MX8 Display Controller DRM Driver"); +MODULE_AUTHOR("Liu Ying <victor.liu@nxp.com>"); +MODULE_LICENSE("GPL"); diff --git a/drivers/gpu/drm/imx/dc/dc-drv.h b/drivers/gpu/drm/imx/dc/dc-drv.h new file mode 100644 index 000000000000..eb61b8c76269 --- /dev/null +++ b/drivers/gpu/drm/imx/dc/dc-drv.h @@ -0,0 +1,102 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Copyright 2024 NXP + */ + +#ifndef __DC_DRV_H__ +#define __DC_DRV_H__ + +#include <linux/container_of.h> +#include <linux/ioport.h> +#include <linux/platform_device.h> +#include <linux/types.h> + +#include <drm/drm_device.h> +#include <drm/drm_encoder.h> + +#include "dc-de.h" +#include "dc-kms.h" +#include "dc-pe.h" + +/** + * struct dc_drm_device - DC specific drm_device + */ +struct dc_drm_device { + /** @base: base drm_device structure */ + struct drm_device base; + /** @dc_crtc: DC specific CRTC list */ + struct dc_crtc dc_crtc[DC_DISPLAYS]; + /** @dc_primary: DC specific primary plane list */ + struct dc_plane dc_primary[DC_DISPLAYS]; + /** @encoder: encoder list */ + struct drm_encoder encoder[DC_DISPLAYS]; + /** @cf_safe: constframe list(safety stream) */ + struct dc_cf *cf_safe[DC_DISPLAYS]; + /** @cf_cont: constframe list(content stream) */ + struct dc_cf *cf_cont[DC_DISPLAYS]; + /** @de: display engine list */ + struct dc_de *de[DC_DISPLAYS]; + /** @ed_safe: extdst list(safety stream) */ + struct dc_ed *ed_safe[DC_DISPLAYS]; + /** @ed_cont: extdst list(content stream) */ + struct dc_ed *ed_cont[DC_DISPLAYS]; + /** @fg: framegen list */ + struct dc_fg *fg[DC_DISPLAYS]; + /** @fu_disp: fetchunit list(used by display engine) */ + struct dc_fu *fu_disp[DC_DISP_FU_CNT]; + /** @lb: layerblend list */ + struct dc_lb *lb[DC_LB_CNT]; + /** @pe: pixel engine */ + struct dc_pe *pe; + /** @tc: tcon list */ + struct dc_tc *tc[DC_DISPLAYS]; +}; + +struct dc_subdev_info { + resource_size_t reg_start; + int id; +}; + +static inline struct dc_drm_device *to_dc_drm_device(struct drm_device *drm) +{ + return container_of(drm, struct dc_drm_device, base); +} + +int dc_crtc_init(struct dc_drm_device *dc_drm, int crtc_index); +int dc_crtc_post_init(struct dc_drm_device *dc_drm, int crtc_index); + +int dc_kms_init(struct dc_drm_device *dc_drm); +void dc_kms_uninit(struct dc_drm_device *dc_drm); + +int dc_plane_init(struct dc_drm_device *dc_drm, struct dc_plane *dc_plane); + +extern struct platform_driver dc_cf_driver; +extern struct platform_driver dc_de_driver; +extern struct platform_driver dc_ed_driver; +extern struct platform_driver dc_fg_driver; +extern struct platform_driver dc_fl_driver; +extern struct platform_driver dc_fw_driver; +extern struct platform_driver dc_ic_driver; +extern struct platform_driver dc_lb_driver; +extern struct platform_driver dc_pe_driver; +extern struct platform_driver dc_tc_driver; + +static inline int dc_subdev_get_id(const struct dc_subdev_info *info, + int info_cnt, struct resource *res) +{ + int i; + + if (!res) + return -EINVAL; + + for (i = 0; i < info_cnt; i++) + if (info[i].reg_start == res->start) + return info[i].id; + + return -EINVAL; +} + +void dc_de_post_bind(struct dc_drm_device *dc_drm); +void dc_pe_post_bind(struct dc_drm_device *dc_drm); + +#endif /* __DC_DRV_H__ */ diff --git a/drivers/gpu/drm/imx/dc/dc-ed.c b/drivers/gpu/drm/imx/dc/dc-ed.c new file mode 100644 index 000000000000..86ecc22d0a55 --- /dev/null +++ b/drivers/gpu/drm/imx/dc/dc-ed.c @@ -0,0 +1,288 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright 2024 NXP + */ + +#include <linux/bitfield.h> +#include <linux/bits.h> +#include <linux/component.h> +#include <linux/mod_devicetable.h> +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/regmap.h> + +#include "dc-drv.h" +#include "dc-pe.h" + +#define PIXENGCFG_STATIC 0x8 +#define POWERDOWN BIT(4) +#define SYNC_MODE BIT(8) +#define SINGLE 0 +#define DIV_MASK GENMASK(23, 16) +#define DIV(x) FIELD_PREP(DIV_MASK, (x)) +#define DIV_RESET 0x80 + +#define PIXENGCFG_DYNAMIC 0xc + +#define PIXENGCFG_TRIGGER 0x14 +#define SYNC_TRIGGER BIT(0) + +#define STATICCONTROL 0x8 +#define KICK_MODE BIT(8) +#define EXTERNAL BIT(8) +#define PERFCOUNTMODE BIT(12) + +#define CONTROL 0xc +#define GAMMAAPPLYENABLE BIT(0) + +static const struct dc_subdev_info dc_ed_info[] = { + { .reg_start = 0x56180980, .id = 0, }, + { .reg_start = 0x56180a00, .id = 1, }, + { .reg_start = 0x561809c0, .id = 4, }, + { .reg_start = 0x56180a40, .id = 5, }, +}; + +static const struct regmap_range dc_ed_pec_regmap_write_ranges[] = { + regmap_reg_range(PIXENGCFG_STATIC, PIXENGCFG_STATIC), + regmap_reg_range(PIXENGCFG_DYNAMIC, PIXENGCFG_DYNAMIC), + regmap_reg_range(PIXENGCFG_TRIGGER, PIXENGCFG_TRIGGER), +}; + +static const struct regmap_access_table dc_ed_pec_regmap_write_table = { + .yes_ranges = dc_ed_pec_regmap_write_ranges, + .n_yes_ranges = ARRAY_SIZE(dc_ed_pec_regmap_write_ranges), +}; + +static const struct regmap_range dc_ed_pec_regmap_read_ranges[] = { + regmap_reg_range(PIXENGCFG_STATIC, PIXENGCFG_STATIC), + regmap_reg_range(PIXENGCFG_DYNAMIC, PIXENGCFG_DYNAMIC), +}; + +static const struct regmap_access_table dc_ed_pec_regmap_read_table = { + .yes_ranges = dc_ed_pec_regmap_read_ranges, + .n_yes_ranges = ARRAY_SIZE(dc_ed_pec_regmap_read_ranges), +}; + +static const struct regmap_range dc_ed_pec_regmap_volatile_ranges[] = { + regmap_reg_range(PIXENGCFG_TRIGGER, PIXENGCFG_TRIGGER), +}; + +static const struct regmap_access_table dc_ed_pec_regmap_volatile_table = { + .yes_ranges = dc_ed_pec_regmap_volatile_ranges, + .n_yes_ranges = ARRAY_SIZE(dc_ed_pec_regmap_volatile_ranges), +}; + +static const struct regmap_config dc_ed_pec_regmap_config = { + .name = "pec", + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + .fast_io = true, + .wr_table = &dc_ed_pec_regmap_write_table, + .rd_table = &dc_ed_pec_regmap_read_table, + .volatile_table = &dc_ed_pec_regmap_volatile_table, + .max_register = PIXENGCFG_TRIGGER, +}; + +static const struct regmap_range dc_ed_regmap_ranges[] = { + regmap_reg_range(STATICCONTROL, STATICCONTROL), + regmap_reg_range(CONTROL, CONTROL), +}; + +static const struct regmap_access_table dc_ed_regmap_access_table = { + .yes_ranges = dc_ed_regmap_ranges, + .n_yes_ranges = ARRAY_SIZE(dc_ed_regmap_ranges), +}; + +static const struct regmap_config dc_ed_cfg_regmap_config = { + .name = "cfg", + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + .fast_io = true, + .wr_table = &dc_ed_regmap_access_table, + .rd_table = &dc_ed_regmap_access_table, + .max_register = CONTROL, +}; + +static const enum dc_link_id src_sels[] = { + LINK_ID_NONE, + LINK_ID_CONSTFRAME0, + LINK_ID_CONSTFRAME1, + LINK_ID_CONSTFRAME4, + LINK_ID_CONSTFRAME5, + LINK_ID_LAYERBLEND3, + LINK_ID_LAYERBLEND2, + LINK_ID_LAYERBLEND1, + LINK_ID_LAYERBLEND0, +}; + +static inline void dc_ed_pec_enable_shden(struct dc_ed *ed) +{ + regmap_write_bits(ed->reg_pec, PIXENGCFG_STATIC, SHDEN, SHDEN); +} + +static inline void dc_ed_pec_poweron(struct dc_ed *ed) +{ + regmap_write_bits(ed->reg_pec, PIXENGCFG_STATIC, POWERDOWN, 0); +} + +static inline void dc_ed_pec_sync_mode_single(struct dc_ed *ed) +{ + regmap_write_bits(ed->reg_pec, PIXENGCFG_STATIC, SYNC_MODE, SINGLE); +} + +static inline void dc_ed_pec_div_reset(struct dc_ed *ed) +{ + regmap_write_bits(ed->reg_pec, PIXENGCFG_STATIC, DIV_MASK, + DIV(DIV_RESET)); +} + +void dc_ed_pec_src_sel(struct dc_ed *ed, enum dc_link_id src) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(src_sels); i++) { + if (src_sels[i] == src) { + regmap_write(ed->reg_pec, PIXENGCFG_DYNAMIC, src); + return; + } + } +} + +void dc_ed_pec_sync_trigger(struct dc_ed *ed) +{ + regmap_write(ed->reg_pec, PIXENGCFG_TRIGGER, SYNC_TRIGGER); +} + +static inline void dc_ed_enable_shden(struct dc_ed *ed) +{ + regmap_write_bits(ed->reg_cfg, STATICCONTROL, SHDEN, SHDEN); +} + +static inline void dc_ed_kick_mode_external(struct dc_ed *ed) +{ + regmap_write_bits(ed->reg_cfg, STATICCONTROL, KICK_MODE, EXTERNAL); +} + +static inline void dc_ed_disable_perfcountmode(struct dc_ed *ed) +{ + regmap_write_bits(ed->reg_cfg, STATICCONTROL, PERFCOUNTMODE, 0); +} + +static inline void dc_ed_disable_gamma_apply(struct dc_ed *ed) +{ + regmap_write_bits(ed->reg_cfg, CONTROL, GAMMAAPPLYENABLE, 0); +} + +void dc_ed_init(struct dc_ed *ed) +{ + dc_ed_pec_src_sel(ed, LINK_ID_NONE); + dc_ed_pec_enable_shden(ed); + dc_ed_pec_poweron(ed); + dc_ed_pec_sync_mode_single(ed); + dc_ed_pec_div_reset(ed); + dc_ed_enable_shden(ed); + dc_ed_disable_perfcountmode(ed); + dc_ed_kick_mode_external(ed); + dc_ed_disable_gamma_apply(ed); +} + +static int dc_ed_bind(struct device *dev, struct device *master, void *data) +{ + struct platform_device *pdev = to_platform_device(dev); + struct dc_drm_device *dc_drm = data; + struct resource *res_pec; + void __iomem *base_pec; + void __iomem *base_cfg; + struct dc_ed *ed; + int id; + + ed = devm_kzalloc(dev, sizeof(*ed), GFP_KERNEL); + if (!ed) + return -ENOMEM; + + base_pec = devm_platform_get_and_ioremap_resource(pdev, 0, &res_pec); + if (IS_ERR(base_pec)) + return PTR_ERR(base_pec); + + base_cfg = devm_platform_ioremap_resource_byname(pdev, "cfg"); + if (IS_ERR(base_cfg)) + return PTR_ERR(base_cfg); + + ed->reg_pec = devm_regmap_init_mmio(dev, base_pec, + &dc_ed_pec_regmap_config); + if (IS_ERR(ed->reg_pec)) + return PTR_ERR(ed->reg_pec); + + ed->reg_cfg = devm_regmap_init_mmio(dev, base_cfg, + &dc_ed_cfg_regmap_config); + if (IS_ERR(ed->reg_cfg)) + return PTR_ERR(ed->reg_cfg); + + ed->irq_shdload = platform_get_irq_byname(pdev, "shdload"); + if (ed->irq_shdload < 0) + return ed->irq_shdload; + + ed->dev = dev; + + id = dc_subdev_get_id(dc_ed_info, ARRAY_SIZE(dc_ed_info), res_pec); + if (id < 0) { + dev_err(dev, "failed to get instance number: %d\n", id); + return id; + } + + switch (id) { + case 0: + dc_drm->ed_cont[0] = ed; + break; + case 1: + dc_drm->ed_cont[1] = ed; + break; + case 4: + dc_drm->ed_safe[0] = ed; + break; + case 5: + dc_drm->ed_safe[1] = ed; + break; + } + + return 0; +} + +static const struct component_ops dc_ed_ops = { + .bind = dc_ed_bind, +}; + +static int dc_ed_probe(struct platform_device *pdev) +{ + int ret; + + ret = component_add(&pdev->dev, &dc_ed_ops); + if (ret) + return dev_err_probe(&pdev->dev, ret, + "failed to add component\n"); + + return 0; +} + +static void dc_ed_remove(struct platform_device *pdev) +{ + component_del(&pdev->dev, &dc_ed_ops); +} + +static const struct of_device_id dc_ed_dt_ids[] = { + { .compatible = "fsl,imx8qxp-dc-extdst" }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, dc_ed_dt_ids); + +struct platform_driver dc_ed_driver = { + .probe = dc_ed_probe, + .remove = dc_ed_remove, + .driver = { + .name = "imx8-dc-extdst", + .suppress_bind_attrs = true, + .of_match_table = dc_ed_dt_ids, + }, +}; diff --git a/drivers/gpu/drm/imx/dc/dc-fg.c b/drivers/gpu/drm/imx/dc/dc-fg.c new file mode 100644 index 000000000000..7f6c1852bf72 --- /dev/null +++ b/drivers/gpu/drm/imx/dc/dc-fg.c @@ -0,0 +1,376 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright 2024 NXP + */ + +#include <linux/bitfield.h> +#include <linux/bits.h> +#include <linux/clk.h> +#include <linux/component.h> +#include <linux/device.h> +#include <linux/jiffies.h> +#include <linux/mod_devicetable.h> +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/regmap.h> +#include <linux/units.h> + +#include <drm/drm_modes.h> + +#include "dc-de.h" +#include "dc-drv.h" + +#define FGSTCTRL 0x8 +#define FGSYNCMODE_MASK GENMASK(2, 1) +#define FGSYNCMODE(x) FIELD_PREP(FGSYNCMODE_MASK, (x)) +#define SHDEN BIT(0) + +#define HTCFG1 0xc +#define HTOTAL(x) FIELD_PREP(GENMASK(29, 16), ((x) - 1)) +#define HACT(x) FIELD_PREP(GENMASK(13, 0), (x)) + +#define HTCFG2 0x10 +#define HSEN BIT(31) +#define HSBP(x) FIELD_PREP(GENMASK(29, 16), ((x) - 1)) +#define HSYNC(x) FIELD_PREP(GENMASK(13, 0), ((x) - 1)) + +#define VTCFG1 0x14 +#define VTOTAL(x) FIELD_PREP(GENMASK(29, 16), ((x) - 1)) +#define VACT(x) FIELD_PREP(GENMASK(13, 0), (x)) + +#define VTCFG2 0x18 +#define VSEN BIT(31) +#define VSBP(x) FIELD_PREP(GENMASK(29, 16), ((x) - 1)) +#define VSYNC(x) FIELD_PREP(GENMASK(13, 0), ((x) - 1)) + +#define PKICKCONFIG 0x2c +#define SKICKCONFIG 0x30 +#define EN BIT(31) +#define ROW(x) FIELD_PREP(GENMASK(29, 16), (x)) +#define COL(x) FIELD_PREP(GENMASK(13, 0), (x)) + +#define PACFG 0x54 +#define SACFG 0x58 +#define STARTY(x) FIELD_PREP(GENMASK(29, 16), ((x) + 1)) +#define STARTX(x) FIELD_PREP(GENMASK(13, 0), ((x) + 1)) + +#define FGINCTRL 0x5c +#define FGINCTRLPANIC 0x60 +#define FGDM_MASK GENMASK(2, 0) +#define ENPRIMALPHA BIT(3) +#define ENSECALPHA BIT(4) + +#define FGCCR 0x64 +#define CCGREEN(x) FIELD_PREP(GENMASK(19, 10), (x)) + +#define FGENABLE 0x68 +#define FGEN BIT(0) + +#define FGSLR 0x6c +#define SHDTOKGEN BIT(0) + +#define FGTIMESTAMP 0x74 +#define FRAMEINDEX(x) FIELD_GET(GENMASK(31, 14), (x)) +#define LINEINDEX(x) FIELD_GET(GENMASK(13, 0), (x)) + +#define FGCHSTAT 0x78 +#define SECSYNCSTAT BIT(24) +#define SFIFOEMPTY BIT(16) + +#define FGCHSTATCLR 0x7c +#define CLRSECSTAT BIT(16) + +enum dc_fg_syncmode { + FG_SYNCMODE_OFF, /* No side-by-side synchronization. */ +}; + +enum dc_fg_dm { + FG_DM_CONSTCOL = 0x1, /* Constant Color Background is shown. */ + FG_DM_SEC_ON_TOP = 0x5, /* Both inputs overlaid with secondary on top. */ +}; + +static const struct dc_subdev_info dc_fg_info[] = { + { .reg_start = 0x5618b800, .id = 0, }, + { .reg_start = 0x5618d400, .id = 1, }, +}; + +static const struct regmap_range dc_fg_regmap_write_ranges[] = { + regmap_reg_range(FGSTCTRL, VTCFG2), + regmap_reg_range(PKICKCONFIG, SKICKCONFIG), + regmap_reg_range(PACFG, FGSLR), + regmap_reg_range(FGCHSTATCLR, FGCHSTATCLR), +}; + +static const struct regmap_range dc_fg_regmap_read_ranges[] = { + regmap_reg_range(FGSTCTRL, VTCFG2), + regmap_reg_range(PKICKCONFIG, SKICKCONFIG), + regmap_reg_range(PACFG, FGENABLE), + regmap_reg_range(FGTIMESTAMP, FGCHSTAT), +}; + +static const struct regmap_access_table dc_fg_regmap_write_table = { + .yes_ranges = dc_fg_regmap_write_ranges, + .n_yes_ranges = ARRAY_SIZE(dc_fg_regmap_write_ranges), +}; + +static const struct regmap_access_table dc_fg_regmap_read_table = { + .yes_ranges = dc_fg_regmap_read_ranges, + .n_yes_ranges = ARRAY_SIZE(dc_fg_regmap_read_ranges), +}; + +static const struct regmap_config dc_fg_regmap_config = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + .fast_io = true, + .wr_table = &dc_fg_regmap_write_table, + .rd_table = &dc_fg_regmap_read_table, + .max_register = FGCHSTATCLR, +}; + +static inline void dc_fg_enable_shden(struct dc_fg *fg) +{ + regmap_write_bits(fg->reg, FGSTCTRL, SHDEN, SHDEN); +} + +static inline void dc_fg_syncmode(struct dc_fg *fg, enum dc_fg_syncmode mode) +{ + regmap_write_bits(fg->reg, FGSTCTRL, FGSYNCMODE_MASK, FGSYNCMODE(mode)); +} + +void dc_fg_cfg_videomode(struct dc_fg *fg, struct drm_display_mode *m) +{ + u32 hact, htotal, hsync, hsbp; + u32 vact, vtotal, vsync, vsbp; + u32 kick_row, kick_col; + int ret; + + hact = m->crtc_hdisplay; + htotal = m->crtc_htotal; + hsync = m->crtc_hsync_end - m->crtc_hsync_start; + hsbp = m->crtc_htotal - m->crtc_hsync_start; + + vact = m->crtc_vdisplay; + vtotal = m->crtc_vtotal; + vsync = m->crtc_vsync_end - m->crtc_vsync_start; + vsbp = m->crtc_vtotal - m->crtc_vsync_start; + + /* video mode */ + regmap_write(fg->reg, HTCFG1, HACT(hact) | HTOTAL(htotal)); + regmap_write(fg->reg, HTCFG2, HSYNC(hsync) | HSBP(hsbp) | HSEN); + regmap_write(fg->reg, VTCFG1, VACT(vact) | VTOTAL(vtotal)); + regmap_write(fg->reg, VTCFG2, VSYNC(vsync) | VSBP(vsbp) | VSEN); + + kick_col = hact + 1; + kick_row = vact; + + /* pkickconfig */ + regmap_write(fg->reg, PKICKCONFIG, COL(kick_col) | ROW(kick_row) | EN); + + /* skikconfig */ + regmap_write(fg->reg, SKICKCONFIG, COL(kick_col) | ROW(kick_row) | EN); + + /* primary and secondary area position configuration */ + regmap_write(fg->reg, PACFG, STARTX(0) | STARTY(0)); + regmap_write(fg->reg, SACFG, STARTX(0) | STARTY(0)); + + /* alpha */ + regmap_write_bits(fg->reg, FGINCTRL, ENPRIMALPHA | ENSECALPHA, 0); + regmap_write_bits(fg->reg, FGINCTRLPANIC, ENPRIMALPHA | ENSECALPHA, 0); + + /* constant color is green(used in panic mode) */ + regmap_write(fg->reg, FGCCR, CCGREEN(0x3ff)); + + ret = clk_set_rate(fg->clk_disp, m->clock * HZ_PER_KHZ); + if (ret < 0) + dev_err(fg->dev, "failed to set display clock rate: %d\n", ret); +} + +static inline void dc_fg_displaymode(struct dc_fg *fg, enum dc_fg_dm mode) +{ + regmap_write_bits(fg->reg, FGINCTRL, FGDM_MASK, mode); +} + +static inline void dc_fg_panic_displaymode(struct dc_fg *fg, enum dc_fg_dm mode) +{ + regmap_write_bits(fg->reg, FGINCTRLPANIC, FGDM_MASK, mode); +} + +void dc_fg_enable(struct dc_fg *fg) +{ + regmap_write(fg->reg, FGENABLE, FGEN); +} + +void dc_fg_disable(struct dc_fg *fg) +{ + regmap_write(fg->reg, FGENABLE, 0); +} + +void dc_fg_shdtokgen(struct dc_fg *fg) +{ + regmap_write(fg->reg, FGSLR, SHDTOKGEN); +} + +u32 dc_fg_get_frame_index(struct dc_fg *fg) +{ + u32 val; + + regmap_read(fg->reg, FGTIMESTAMP, &val); + + return FRAMEINDEX(val); +} + +u32 dc_fg_get_line_index(struct dc_fg *fg) +{ + u32 val; + + regmap_read(fg->reg, FGTIMESTAMP, &val); + + return LINEINDEX(val); +} + +bool dc_fg_wait_for_frame_index_moving(struct dc_fg *fg) +{ + unsigned long timeout = jiffies + msecs_to_jiffies(100); + u32 frame_index, last_frame_index; + + frame_index = dc_fg_get_frame_index(fg); + do { + last_frame_index = frame_index; + frame_index = dc_fg_get_frame_index(fg); + } while (last_frame_index == frame_index && + time_before(jiffies, timeout)); + + return last_frame_index != frame_index; +} + +bool dc_fg_secondary_requests_to_read_empty_fifo(struct dc_fg *fg) +{ + u32 val; + + regmap_read(fg->reg, FGCHSTAT, &val); + + return !!(val & SFIFOEMPTY); +} + +void dc_fg_secondary_clear_channel_status(struct dc_fg *fg) +{ + regmap_write(fg->reg, FGCHSTATCLR, CLRSECSTAT); +} + +int dc_fg_wait_for_secondary_syncup(struct dc_fg *fg) +{ + unsigned int val; + + return regmap_read_poll_timeout(fg->reg, FGCHSTAT, val, + val & SECSYNCSTAT, 5, 100000); +} + +void dc_fg_enable_clock(struct dc_fg *fg) +{ + int ret; + + ret = clk_prepare_enable(fg->clk_disp); + if (ret) + dev_err(fg->dev, "failed to enable display clock: %d\n", ret); +} + +void dc_fg_disable_clock(struct dc_fg *fg) +{ + clk_disable_unprepare(fg->clk_disp); +} + +enum drm_mode_status dc_fg_check_clock(struct dc_fg *fg, int clk_khz) +{ + unsigned long rounded_rate; + + rounded_rate = clk_round_rate(fg->clk_disp, clk_khz * HZ_PER_KHZ); + + if (rounded_rate != clk_khz * HZ_PER_KHZ) + return MODE_NOCLOCK; + + return MODE_OK; +} + +void dc_fg_init(struct dc_fg *fg) +{ + dc_fg_enable_shden(fg); + dc_fg_syncmode(fg, FG_SYNCMODE_OFF); + dc_fg_displaymode(fg, FG_DM_SEC_ON_TOP); + dc_fg_panic_displaymode(fg, FG_DM_CONSTCOL); +} + +static int dc_fg_bind(struct device *dev, struct device *master, void *data) +{ + struct platform_device *pdev = to_platform_device(dev); + struct dc_drm_device *dc_drm = data; + struct resource *res; + void __iomem *base; + struct dc_fg *fg; + int id; + + fg = devm_kzalloc(dev, sizeof(*fg), GFP_KERNEL); + if (!fg) + return -ENOMEM; + + base = devm_platform_get_and_ioremap_resource(pdev, 0, &res); + if (IS_ERR(base)) + return PTR_ERR(base); + + fg->reg = devm_regmap_init_mmio(dev, base, &dc_fg_regmap_config); + if (IS_ERR(fg->reg)) + return PTR_ERR(fg->reg); + + fg->clk_disp = devm_clk_get(dev, NULL); + if (IS_ERR(fg->clk_disp)) + return dev_err_probe(dev, PTR_ERR(fg->clk_disp), + "failed to get display clock\n"); + + id = dc_subdev_get_id(dc_fg_info, ARRAY_SIZE(dc_fg_info), res); + if (id < 0) { + dev_err(dev, "failed to get instance number: %d\n", id); + return id; + } + + fg->dev = dev; + dc_drm->fg[id] = fg; + + return 0; +} + +static const struct component_ops dc_fg_ops = { + .bind = dc_fg_bind, +}; + +static int dc_fg_probe(struct platform_device *pdev) +{ + int ret; + + ret = component_add(&pdev->dev, &dc_fg_ops); + if (ret) + return dev_err_probe(&pdev->dev, ret, + "failed to add component\n"); + + return 0; +} + +static void dc_fg_remove(struct platform_device *pdev) +{ + component_del(&pdev->dev, &dc_fg_ops); +} + +static const struct of_device_id dc_fg_dt_ids[] = { + { .compatible = "fsl,imx8qxp-dc-framegen" }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, dc_fg_dt_ids); + +struct platform_driver dc_fg_driver = { + .probe = dc_fg_probe, + .remove = dc_fg_remove, + .driver = { + .name = "imx8-dc-framegen", + .suppress_bind_attrs = true, + .of_match_table = dc_fg_dt_ids, + }, +}; diff --git a/drivers/gpu/drm/imx/dc/dc-fl.c b/drivers/gpu/drm/imx/dc/dc-fl.c new file mode 100644 index 000000000000..3ce24c72aa13 --- /dev/null +++ b/drivers/gpu/drm/imx/dc/dc-fl.c @@ -0,0 +1,185 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright 2024 NXP + */ + +#include <linux/component.h> +#include <linux/ioport.h> +#include <linux/mod_devicetable.h> +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/regmap.h> + +#include <drm/drm_fourcc.h> + +#include "dc-drv.h" +#include "dc-fu.h" + +#define BASEADDRESS(x) (0x10 + FRAC_OFFSET * (x)) +#define SOURCEBUFFERATTRIBUTES(x) (0x14 + FRAC_OFFSET * (x)) +#define SOURCEBUFFERDIMENSION(x) (0x18 + FRAC_OFFSET * (x)) +#define COLORCOMPONENTBITS(x) (0x1c + FRAC_OFFSET * (x)) +#define COLORCOMPONENTSHIFT(x) (0x20 + FRAC_OFFSET * (x)) +#define LAYEROFFSET(x) (0x24 + FRAC_OFFSET * (x)) +#define CLIPWINDOWOFFSET(x) (0x28 + FRAC_OFFSET * (x)) +#define CLIPWINDOWDIMENSIONS(x) (0x2c + FRAC_OFFSET * (x)) +#define CONSTANTCOLOR(x) (0x30 + FRAC_OFFSET * (x)) +#define LAYERPROPERTY(x) (0x34 + FRAC_OFFSET * (x)) +#define FRAMEDIMENSIONS 0x150 + +struct dc_fl { + struct dc_fu fu; +}; + +static const struct dc_subdev_info dc_fl_info[] = { + { .reg_start = 0x56180ac0, .id = 0, }, +}; + +static const struct regmap_range dc_fl_regmap_ranges[] = { + regmap_reg_range(STATICCONTROL, FRAMEDIMENSIONS), +}; + +static const struct regmap_access_table dc_fl_regmap_access_table = { + .yes_ranges = dc_fl_regmap_ranges, + .n_yes_ranges = ARRAY_SIZE(dc_fl_regmap_ranges), +}; + +static const struct regmap_config dc_fl_cfg_regmap_config = { + .name = "cfg", + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + .fast_io = true, + .wr_table = &dc_fl_regmap_access_table, + .rd_table = &dc_fl_regmap_access_table, + .max_register = FRAMEDIMENSIONS, +}; + +static void dc_fl_set_fmt(struct dc_fu *fu, enum dc_fu_frac frac, + const struct drm_format_info *format) +{ + u32 bits = 0, shifts = 0; + + dc_fu_set_src_bpp(fu, frac, format->cpp[0] * 8); + + regmap_write_bits(fu->reg_cfg, LAYERPROPERTY(frac), + YUVCONVERSIONMODE_MASK, + YUVCONVERSIONMODE(YUVCONVERSIONMODE_OFF)); + + dc_fu_get_pixel_format_bits(fu, format->format, &bits); + dc_fu_get_pixel_format_shifts(fu, format->format, &shifts); + + regmap_write(fu->reg_cfg, COLORCOMPONENTBITS(frac), bits); + regmap_write(fu->reg_cfg, COLORCOMPONENTSHIFT(frac), shifts); +} + +static void dc_fl_set_framedimensions(struct dc_fu *fu, int w, int h) +{ + regmap_write(fu->reg_cfg, FRAMEDIMENSIONS, + FRAMEWIDTH(w) | FRAMEHEIGHT(h)); +} + +static void dc_fl_init(struct dc_fu *fu) +{ + dc_fu_common_hw_init(fu); + dc_fu_shdldreq_sticky(fu, 0xff); +} + +static void dc_fl_set_ops(struct dc_fu *fu) +{ + memcpy(&fu->ops, &dc_fu_common_ops, sizeof(dc_fu_common_ops)); + fu->ops.init = dc_fl_init; + fu->ops.set_fmt = dc_fl_set_fmt; + fu->ops.set_framedimensions = dc_fl_set_framedimensions; +} + +static int dc_fl_bind(struct device *dev, struct device *master, void *data) +{ + struct platform_device *pdev = to_platform_device(dev); + struct dc_drm_device *dc_drm = data; + struct resource *res_pec; + void __iomem *base_cfg; + struct dc_fl *fl; + struct dc_fu *fu; + int i, id; + + fl = devm_kzalloc(dev, sizeof(*fl), GFP_KERNEL); + if (!fl) + return -ENOMEM; + + fu = &fl->fu; + + res_pec = platform_get_resource(pdev, IORESOURCE_MEM, 0); + + base_cfg = devm_platform_ioremap_resource_byname(pdev, "cfg"); + if (IS_ERR(base_cfg)) + return PTR_ERR(base_cfg); + + fu->reg_cfg = devm_regmap_init_mmio(dev, base_cfg, + &dc_fl_cfg_regmap_config); + if (IS_ERR(fu->reg_cfg)) + return PTR_ERR(fu->reg_cfg); + + id = dc_subdev_get_id(dc_fl_info, ARRAY_SIZE(dc_fl_info), res_pec); + if (id < 0) { + dev_err(dev, "failed to get instance number: %d\n", id); + return id; + } + + fu->link_id = LINK_ID_FETCHLAYER0; + fu->id = DC_FETCHUNIT_FL0; + for (i = 0; i < DC_FETCHUNIT_FRAC_NUM; i++) { + fu->reg_baseaddr[i] = BASEADDRESS(i); + fu->reg_sourcebufferattributes[i] = SOURCEBUFFERATTRIBUTES(i); + fu->reg_sourcebufferdimension[i] = SOURCEBUFFERDIMENSION(i); + fu->reg_layeroffset[i] = LAYEROFFSET(i); + fu->reg_clipwindowoffset[i] = CLIPWINDOWOFFSET(i); + fu->reg_clipwindowdimensions[i] = CLIPWINDOWDIMENSIONS(i); + fu->reg_constantcolor[i] = CONSTANTCOLOR(i); + fu->reg_layerproperty[i] = LAYERPROPERTY(i); + } + snprintf(fu->name, sizeof(fu->name), "FetchLayer%d", id); + + dc_fl_set_ops(fu); + + dc_drm->fu_disp[fu->id] = fu; + + return 0; +} + +static const struct component_ops dc_fl_ops = { + .bind = dc_fl_bind, +}; + +static int dc_fl_probe(struct platform_device *pdev) +{ + int ret; + + ret = component_add(&pdev->dev, &dc_fl_ops); + if (ret) + return dev_err_probe(&pdev->dev, ret, + "failed to add component\n"); + + return 0; +} + +static void dc_fl_remove(struct platform_device *pdev) +{ + component_del(&pdev->dev, &dc_fl_ops); +} + +static const struct of_device_id dc_fl_dt_ids[] = { + { .compatible = "fsl,imx8qxp-dc-fetchlayer" }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, dc_fl_dt_ids); + +struct platform_driver dc_fl_driver = { + .probe = dc_fl_probe, + .remove = dc_fl_remove, + .driver = { + .name = "imx8-dc-fetchlayer", + .suppress_bind_attrs = true, + .of_match_table = dc_fl_dt_ids, + }, +}; diff --git a/drivers/gpu/drm/imx/dc/dc-fu.c b/drivers/gpu/drm/imx/dc/dc-fu.c new file mode 100644 index 000000000000..f94c591c8158 --- /dev/null +++ b/drivers/gpu/drm/imx/dc/dc-fu.c @@ -0,0 +1,258 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright 2024 NXP + */ + +#include <linux/bitfield.h> +#include <linux/bitops.h> +#include <linux/bits.h> +#include <linux/math.h> + +#include "dc-fu.h" +#include "dc-pe.h" + +/* STATICCONTROL */ +#define SHDLDREQSTICKY_MASK GENMASK(31, 24) +#define SHDLDREQSTICKY(x) FIELD_PREP(SHDLDREQSTICKY_MASK, (x)) +#define BASEADDRESSAUTOUPDATE_MASK GENMASK(23, 16) +#define BASEADDRESSAUTOUPDATE(x) FIELD_PREP(BASEADDRESSAUTOUPDATE_MASK, (x)) + +/* BURSTBUFFERMANAGEMENT */ +#define SETBURSTLENGTH_MASK GENMASK(12, 8) +#define SETBURSTLENGTH(x) FIELD_PREP(SETBURSTLENGTH_MASK, (x)) +#define SETNUMBUFFERS_MASK GENMASK(7, 0) +#define SETNUMBUFFERS(x) FIELD_PREP(SETNUMBUFFERS_MASK, (x)) +#define LINEMODE_MASK BIT(31) + +/* SOURCEBUFFERATTRIBUTES */ +#define BITSPERPIXEL_MASK GENMASK(21, 16) +#define BITSPERPIXEL(x) FIELD_PREP(BITSPERPIXEL_MASK, (x)) +#define STRIDE_MASK GENMASK(15, 0) +#define STRIDE(x) FIELD_PREP(STRIDE_MASK, (x) - 1) + +/* SOURCEBUFFERDIMENSION */ +#define LINEWIDTH(x) FIELD_PREP(GENMASK(13, 0), (x)) +#define LINECOUNT(x) FIELD_PREP(GENMASK(29, 16), (x)) + +/* LAYEROFFSET */ +#define LAYERXOFFSET(x) FIELD_PREP(GENMASK(14, 0), (x)) +#define LAYERYOFFSET(x) FIELD_PREP(GENMASK(30, 16), (x)) + +/* CLIPWINDOWOFFSET */ +#define CLIPWINDOWXOFFSET(x) FIELD_PREP(GENMASK(14, 0), (x)) +#define CLIPWINDOWYOFFSET(x) FIELD_PREP(GENMASK(30, 16), (x)) + +/* CLIPWINDOWDIMENSIONS */ +#define CLIPWINDOWWIDTH(x) FIELD_PREP(GENMASK(13, 0), (x) - 1) +#define CLIPWINDOWHEIGHT(x) FIELD_PREP(GENMASK(29, 16), (x) - 1) + +enum dc_linemode { + /* + * Mandatory setting for operation in the Display Controller. + * Works also for Blit Engine with marginal performance impact. + */ + LINEMODE_DISPLAY = 0, +}; + +struct dc_fu_pixel_format { + u32 pixel_format; + u32 bits; + u32 shifts; +}; + +static const struct dc_fu_pixel_format pixel_formats[] = { + { + DRM_FORMAT_XRGB8888, + R_BITS(8) | G_BITS(8) | B_BITS(8) | A_BITS(0), + R_SHIFT(16) | G_SHIFT(8) | B_SHIFT(0) | A_SHIFT(0), + }, +}; + +void dc_fu_get_pixel_format_bits(struct dc_fu *fu, u32 format, u32 *bits) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(pixel_formats); i++) { + if (pixel_formats[i].pixel_format == format) { + *bits = pixel_formats[i].bits; + return; + } + } +} + +void +dc_fu_get_pixel_format_shifts(struct dc_fu *fu, u32 format, u32 *shifts) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(pixel_formats); i++) { + if (pixel_formats[i].pixel_format == format) { + *shifts = pixel_formats[i].shifts; + return; + } + } +} + +static inline void dc_fu_enable_shden(struct dc_fu *fu) +{ + regmap_write_bits(fu->reg_cfg, STATICCONTROL, SHDEN, SHDEN); +} + +static inline void dc_fu_baddr_autoupdate(struct dc_fu *fu, u8 layer_mask) +{ + regmap_write_bits(fu->reg_cfg, STATICCONTROL, + BASEADDRESSAUTOUPDATE_MASK, + BASEADDRESSAUTOUPDATE(layer_mask)); +} + +void dc_fu_shdldreq_sticky(struct dc_fu *fu, u8 layer_mask) +{ + regmap_write_bits(fu->reg_cfg, STATICCONTROL, SHDLDREQSTICKY_MASK, + SHDLDREQSTICKY(layer_mask)); +} + +static inline void dc_fu_set_linemode(struct dc_fu *fu, enum dc_linemode mode) +{ + regmap_write_bits(fu->reg_cfg, BURSTBUFFERMANAGEMENT, LINEMODE_MASK, + mode); +} + +static inline void dc_fu_set_numbuffers(struct dc_fu *fu, unsigned int num) +{ + regmap_write_bits(fu->reg_cfg, BURSTBUFFERMANAGEMENT, + SETNUMBUFFERS_MASK, SETNUMBUFFERS(num)); +} + +static void dc_fu_set_burstlength(struct dc_fu *fu, dma_addr_t baddr) +{ + unsigned int burst_size, burst_length; + + burst_size = 1 << __ffs(baddr); + burst_size = round_up(burst_size, 8); + burst_size = min(burst_size, 128U); + burst_length = burst_size / 8; + + regmap_write_bits(fu->reg_cfg, BURSTBUFFERMANAGEMENT, + SETBURSTLENGTH_MASK, SETBURSTLENGTH(burst_length)); +} + +static void dc_fu_set_baseaddress(struct dc_fu *fu, enum dc_fu_frac frac, + dma_addr_t baddr) +{ + regmap_write(fu->reg_cfg, fu->reg_baseaddr[frac], baddr); +} + +void dc_fu_set_src_bpp(struct dc_fu *fu, enum dc_fu_frac frac, unsigned int bpp) +{ + regmap_write_bits(fu->reg_cfg, fu->reg_sourcebufferattributes[frac], + BITSPERPIXEL_MASK, BITSPERPIXEL(bpp)); +} + +static void dc_fu_set_src_stride(struct dc_fu *fu, enum dc_fu_frac frac, + unsigned int stride) +{ + regmap_write_bits(fu->reg_cfg, fu->reg_sourcebufferattributes[frac], + STRIDE_MASK, STRIDE(stride)); +} + +static void dc_fu_set_src_buf_dimensions(struct dc_fu *fu, enum dc_fu_frac frac, + int w, int h) +{ + regmap_write(fu->reg_cfg, fu->reg_sourcebufferdimension[frac], + LINEWIDTH(w) | LINECOUNT(h)); +} + +static inline void dc_fu_layeroffset(struct dc_fu *fu, enum dc_fu_frac frac, + unsigned int x, unsigned int y) +{ + regmap_write(fu->reg_cfg, fu->reg_layeroffset[frac], + LAYERXOFFSET(x) | LAYERYOFFSET(y)); +} + +static inline void dc_fu_clipoffset(struct dc_fu *fu, enum dc_fu_frac frac, + unsigned int x, unsigned int y) +{ + regmap_write(fu->reg_cfg, fu->reg_clipwindowoffset[frac], + CLIPWINDOWXOFFSET(x) | CLIPWINDOWYOFFSET(y)); +} + +static inline void dc_fu_clipdimensions(struct dc_fu *fu, enum dc_fu_frac frac, + unsigned int w, unsigned int h) +{ + regmap_write(fu->reg_cfg, fu->reg_clipwindowdimensions[frac], + CLIPWINDOWWIDTH(w) | CLIPWINDOWHEIGHT(h)); +} + +static inline void +dc_fu_set_pixel_blend_mode(struct dc_fu *fu, enum dc_fu_frac frac) +{ + regmap_write(fu->reg_cfg, fu->reg_layerproperty[frac], 0); + regmap_write(fu->reg_cfg, fu->reg_constantcolor[frac], 0); +} + +static void dc_fu_enable_src_buf(struct dc_fu *fu, enum dc_fu_frac frac) +{ + regmap_write_bits(fu->reg_cfg, fu->reg_layerproperty[frac], + SOURCEBUFFERENABLE, SOURCEBUFFERENABLE); +} + +static void dc_fu_disable_src_buf(struct dc_fu *fu, enum dc_fu_frac frac) +{ + regmap_write_bits(fu->reg_cfg, fu->reg_layerproperty[frac], + SOURCEBUFFERENABLE, 0); + + if (fu->lb) { + dc_lb_pec_clken(fu->lb, CLKEN_DISABLE); + dc_lb_mode(fu->lb, LB_NEUTRAL); + } +} + +static void dc_fu_set_layerblend(struct dc_fu *fu, struct dc_lb *lb) +{ + fu->lb = lb; +} + +static enum dc_link_id dc_fu_get_link_id(struct dc_fu *fu) +{ + return fu->link_id; +} + +static const char *dc_fu_get_name(struct dc_fu *fu) +{ + return fu->name; +} + +const struct dc_fu_ops dc_fu_common_ops = { + .set_burstlength = dc_fu_set_burstlength, + .set_baseaddress = dc_fu_set_baseaddress, + .set_src_stride = dc_fu_set_src_stride, + .set_src_buf_dimensions = dc_fu_set_src_buf_dimensions, + .enable_src_buf = dc_fu_enable_src_buf, + .disable_src_buf = dc_fu_disable_src_buf, + .set_layerblend = dc_fu_set_layerblend, + .get_link_id = dc_fu_get_link_id, + .get_name = dc_fu_get_name, +}; + +const struct dc_fu_ops *dc_fu_get_ops(struct dc_fu *fu) +{ + return &fu->ops; +} + +void dc_fu_common_hw_init(struct dc_fu *fu) +{ + enum dc_fu_frac i; + + dc_fu_baddr_autoupdate(fu, 0x0); + dc_fu_enable_shden(fu); + dc_fu_set_linemode(fu, LINEMODE_DISPLAY); + dc_fu_set_numbuffers(fu, 16); + + for (i = DC_FETCHUNIT_FRAC0; i < DC_FETCHUNIT_FRAC_NUM; i++) { + dc_fu_layeroffset(fu, i, 0, 0); + dc_fu_clipoffset(fu, i, 0, 0); + dc_fu_clipdimensions(fu, i, 1, 1); + dc_fu_disable_src_buf(fu, i); + dc_fu_set_pixel_blend_mode(fu, i); + } +} diff --git a/drivers/gpu/drm/imx/dc/dc-fu.h b/drivers/gpu/drm/imx/dc/dc-fu.h new file mode 100644 index 000000000000..e016e1ea5b4e --- /dev/null +++ b/drivers/gpu/drm/imx/dc/dc-fu.h @@ -0,0 +1,129 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Copyright 2024 NXP + */ + +#ifndef __DC_FETCHUNIT_H__ +#define __DC_FETCHUNIT_H__ + +#include <linux/bitfield.h> +#include <linux/bits.h> +#include <linux/regmap.h> +#include <linux/types.h> + +#include <drm/drm_fourcc.h> + +#include "dc-pe.h" + +#define FRAC_OFFSET 0x28 + +#define STATICCONTROL 0x8 +#define BURSTBUFFERMANAGEMENT 0xc + +/* COLORCOMPONENTBITS */ +#define R_BITS(x) FIELD_PREP_CONST(GENMASK(27, 24), (x)) +#define G_BITS(x) FIELD_PREP_CONST(GENMASK(19, 16), (x)) +#define B_BITS(x) FIELD_PREP_CONST(GENMASK(11, 8), (x)) +#define A_BITS(x) FIELD_PREP_CONST(GENMASK(3, 0), (x)) + +/* COLORCOMPONENTSHIFT */ +#define R_SHIFT(x) FIELD_PREP_CONST(GENMASK(28, 24), (x)) +#define G_SHIFT(x) FIELD_PREP_CONST(GENMASK(20, 16), (x)) +#define B_SHIFT(x) FIELD_PREP_CONST(GENMASK(12, 8), (x)) +#define A_SHIFT(x) FIELD_PREP_CONST(GENMASK(4, 0), (x)) + +/* LAYERPROPERTY */ +#define YUVCONVERSIONMODE_MASK GENMASK(18, 17) +#define YUVCONVERSIONMODE(x) FIELD_PREP(YUVCONVERSIONMODE_MASK, (x)) +#define SOURCEBUFFERENABLE BIT(31) + +/* FRAMEDIMENSIONS */ +#define FRAMEWIDTH(x) FIELD_PREP(GENMASK(13, 0), (x)) +#define FRAMEHEIGHT(x) FIELD_PREP(GENMASK(29, 16), (x)) + +/* CONTROL */ +#define INPUTSELECT_MASK GENMASK(4, 3) +#define INPUTSELECT(x) FIELD_PREP(INPUTSELECT_MASK, (x)) +#define RASTERMODE_MASK GENMASK(2, 0) +#define RASTERMODE(x) FIELD_PREP(RASTERMODE_MASK, (x)) + +enum dc_yuvconversionmode { + YUVCONVERSIONMODE_OFF, +}; + +enum dc_inputselect { + INPUTSELECT_INACTIVE, +}; + +enum dc_rastermode { + RASTERMODE_NORMAL, +}; + +enum { + DC_FETCHUNIT_FL0, + DC_FETCHUNIT_FW2, +}; + +enum dc_fu_frac { + DC_FETCHUNIT_FRAC0, + DC_FETCHUNIT_FRAC1, + DC_FETCHUNIT_FRAC2, + DC_FETCHUNIT_FRAC3, + DC_FETCHUNIT_FRAC4, + DC_FETCHUNIT_FRAC5, + DC_FETCHUNIT_FRAC6, + DC_FETCHUNIT_FRAC7, + DC_FETCHUNIT_FRAC_NUM +}; + +struct dc_fu; +struct dc_lb; + +struct dc_fu_ops { + void (*init)(struct dc_fu *fu); + void (*set_burstlength)(struct dc_fu *fu, dma_addr_t baddr); + void (*set_baseaddress)(struct dc_fu *fu, enum dc_fu_frac frac, + dma_addr_t baddr); + void (*set_src_stride)(struct dc_fu *fu, enum dc_fu_frac frac, + unsigned int stride); + void (*set_src_buf_dimensions)(struct dc_fu *fu, enum dc_fu_frac frac, + int w, int h); + void (*set_fmt)(struct dc_fu *fu, enum dc_fu_frac frac, + const struct drm_format_info *format); + void (*enable_src_buf)(struct dc_fu *fu, enum dc_fu_frac frac); + void (*disable_src_buf)(struct dc_fu *fu, enum dc_fu_frac frac); + void (*set_framedimensions)(struct dc_fu *fu, int w, int h); + void (*set_layerblend)(struct dc_fu *fu, struct dc_lb *lb); + enum dc_link_id (*get_link_id)(struct dc_fu *fu); + const char *(*get_name)(struct dc_fu *fu); +}; + +struct dc_fu { + struct regmap *reg_pec; + struct regmap *reg_cfg; + char name[21]; + u32 reg_baseaddr[DC_FETCHUNIT_FRAC_NUM]; + u32 reg_sourcebufferattributes[DC_FETCHUNIT_FRAC_NUM]; + u32 reg_sourcebufferdimension[DC_FETCHUNIT_FRAC_NUM]; + u32 reg_layeroffset[DC_FETCHUNIT_FRAC_NUM]; + u32 reg_clipwindowoffset[DC_FETCHUNIT_FRAC_NUM]; + u32 reg_clipwindowdimensions[DC_FETCHUNIT_FRAC_NUM]; + u32 reg_constantcolor[DC_FETCHUNIT_FRAC_NUM]; + u32 reg_layerproperty[DC_FETCHUNIT_FRAC_NUM]; + unsigned int id; + enum dc_link_id link_id; + struct dc_fu_ops ops; + struct dc_lb *lb; +}; + +extern const struct dc_fu_ops dc_fu_common_ops; + +void dc_fu_get_pixel_format_bits(struct dc_fu *fu, u32 format, u32 *bits); +void dc_fu_get_pixel_format_shifts(struct dc_fu *fu, u32 format, u32 *shifts); +void dc_fu_shdldreq_sticky(struct dc_fu *fu, u8 layer_mask); +void dc_fu_set_src_bpp(struct dc_fu *fu, enum dc_fu_frac frac, unsigned int bpp); +void dc_fu_common_hw_init(struct dc_fu *fu); + +const struct dc_fu_ops *dc_fu_get_ops(struct dc_fu *fu); + +#endif /* __DC_FETCHUNIT_H__ */ diff --git a/drivers/gpu/drm/imx/dc/dc-fw.c b/drivers/gpu/drm/imx/dc/dc-fw.c new file mode 100644 index 000000000000..acb2d4d9e2ec --- /dev/null +++ b/drivers/gpu/drm/imx/dc/dc-fw.c @@ -0,0 +1,222 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright 2024 NXP + */ + +#include <linux/component.h> +#include <linux/mod_devicetable.h> +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/regmap.h> + +#include <drm/drm_fourcc.h> + +#include "dc-drv.h" +#include "dc-fu.h" + +#define PIXENGCFG_DYNAMIC 0x8 + +#define BASEADDRESS(x) (0x10 + FRAC_OFFSET * (x)) +#define SOURCEBUFFERATTRIBUTES(x) (0x14 + FRAC_OFFSET * (x)) +#define SOURCEBUFFERDIMENSION(x) (0x18 + FRAC_OFFSET * (x)) +#define COLORCOMPONENTBITS(x) (0x1c + FRAC_OFFSET * (x)) +#define COLORCOMPONENTSHIFT(x) (0x20 + FRAC_OFFSET * (x)) +#define LAYEROFFSET(x) (0x24 + FRAC_OFFSET * (x)) +#define CLIPWINDOWOFFSET(x) (0x28 + FRAC_OFFSET * (x)) +#define CLIPWINDOWDIMENSIONS(x) (0x2c + FRAC_OFFSET * (x)) +#define CONSTANTCOLOR(x) (0x30 + FRAC_OFFSET * (x)) +#define LAYERPROPERTY(x) (0x34 + FRAC_OFFSET * (x)) +#define FRAMEDIMENSIONS 0x150 +#define CONTROL 0x170 + +struct dc_fw { + struct dc_fu fu; +}; + +static const struct dc_subdev_info dc_fw_info[] = { + { .reg_start = 0x56180a60, .id = 2, }, +}; + +static const struct regmap_range dc_fw_pec_regmap_access_ranges[] = { + regmap_reg_range(PIXENGCFG_DYNAMIC, PIXENGCFG_DYNAMIC), +}; + +static const struct regmap_access_table dc_fw_pec_regmap_access_table = { + .yes_ranges = dc_fw_pec_regmap_access_ranges, + .n_yes_ranges = ARRAY_SIZE(dc_fw_pec_regmap_access_ranges), +}; + +static const struct regmap_config dc_fw_pec_regmap_config = { + .name = "pec", + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + .fast_io = true, + .wr_table = &dc_fw_pec_regmap_access_table, + .rd_table = &dc_fw_pec_regmap_access_table, + .max_register = PIXENGCFG_DYNAMIC, +}; + +static const struct regmap_range dc_fw_regmap_ranges[] = { + regmap_reg_range(STATICCONTROL, FRAMEDIMENSIONS), + regmap_reg_range(CONTROL, CONTROL), +}; + +static const struct regmap_access_table dc_fw_regmap_access_table = { + .yes_ranges = dc_fw_regmap_ranges, + .n_yes_ranges = ARRAY_SIZE(dc_fw_regmap_ranges), +}; + +static const struct regmap_config dc_fw_cfg_regmap_config = { + .name = "cfg", + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + .fast_io = true, + .wr_table = &dc_fw_regmap_access_table, + .rd_table = &dc_fw_regmap_access_table, + .max_register = CONTROL, +}; + +static void dc_fw_set_fmt(struct dc_fu *fu, enum dc_fu_frac frac, + const struct drm_format_info *format) +{ + u32 bits = 0, shifts = 0; + + dc_fu_set_src_bpp(fu, frac, format->cpp[0] * 8); + + regmap_write_bits(fu->reg_cfg, CONTROL, INPUTSELECT_MASK, + INPUTSELECT(INPUTSELECT_INACTIVE)); + regmap_write_bits(fu->reg_cfg, CONTROL, RASTERMODE_MASK, + RASTERMODE(RASTERMODE_NORMAL)); + + regmap_write_bits(fu->reg_cfg, LAYERPROPERTY(frac), + YUVCONVERSIONMODE_MASK, + YUVCONVERSIONMODE(YUVCONVERSIONMODE_OFF)); + + dc_fu_get_pixel_format_bits(fu, format->format, &bits); + dc_fu_get_pixel_format_shifts(fu, format->format, &shifts); + + regmap_write(fu->reg_cfg, COLORCOMPONENTBITS(frac), bits); + regmap_write(fu->reg_cfg, COLORCOMPONENTSHIFT(frac), shifts); +} + +static void dc_fw_set_framedimensions(struct dc_fu *fu, int w, int h) +{ + regmap_write(fu->reg_cfg, FRAMEDIMENSIONS, + FRAMEWIDTH(w) | FRAMEHEIGHT(h)); +} + +static void dc_fw_init(struct dc_fu *fu) +{ + regmap_write(fu->reg_pec, PIXENGCFG_DYNAMIC, LINK_ID_NONE); + dc_fu_common_hw_init(fu); + dc_fu_shdldreq_sticky(fu, 0xff); +} + +static void dc_fw_set_ops(struct dc_fu *fu) +{ + memcpy(&fu->ops, &dc_fu_common_ops, sizeof(dc_fu_common_ops)); + fu->ops.init = dc_fw_init; + fu->ops.set_fmt = dc_fw_set_fmt; + fu->ops.set_framedimensions = dc_fw_set_framedimensions; +} + +static int dc_fw_bind(struct device *dev, struct device *master, void *data) +{ + struct platform_device *pdev = to_platform_device(dev); + struct dc_drm_device *dc_drm = data; + struct resource *res_pec; + void __iomem *base_pec; + void __iomem *base_cfg; + struct dc_fw *fw; + struct dc_fu *fu; + int i, id; + + fw = devm_kzalloc(dev, sizeof(*fw), GFP_KERNEL); + if (!fw) + return -ENOMEM; + + fu = &fw->fu; + + base_pec = devm_platform_get_and_ioremap_resource(pdev, 0, &res_pec); + if (IS_ERR(base_pec)) + return PTR_ERR(base_pec); + + base_cfg = devm_platform_ioremap_resource_byname(pdev, "cfg"); + if (IS_ERR(base_cfg)) + return PTR_ERR(base_cfg); + + fu->reg_pec = devm_regmap_init_mmio(dev, base_pec, + &dc_fw_pec_regmap_config); + if (IS_ERR(fu->reg_pec)) + return PTR_ERR(fu->reg_pec); + + fu->reg_cfg = devm_regmap_init_mmio(dev, base_cfg, + &dc_fw_cfg_regmap_config); + if (IS_ERR(fu->reg_cfg)) + return PTR_ERR(fu->reg_cfg); + + id = dc_subdev_get_id(dc_fw_info, ARRAY_SIZE(dc_fw_info), res_pec); + if (id < 0) { + dev_err(dev, "failed to get instance number: %d\n", id); + return id; + } + + fu->link_id = LINK_ID_FETCHWARP2; + fu->id = DC_FETCHUNIT_FW2; + for (i = 0; i < DC_FETCHUNIT_FRAC_NUM; i++) { + fu->reg_baseaddr[i] = BASEADDRESS(i); + fu->reg_sourcebufferattributes[i] = SOURCEBUFFERATTRIBUTES(i); + fu->reg_sourcebufferdimension[i] = SOURCEBUFFERDIMENSION(i); + fu->reg_layeroffset[i] = LAYEROFFSET(i); + fu->reg_clipwindowoffset[i] = CLIPWINDOWOFFSET(i); + fu->reg_clipwindowdimensions[i] = CLIPWINDOWDIMENSIONS(i); + fu->reg_constantcolor[i] = CONSTANTCOLOR(i); + fu->reg_layerproperty[i] = LAYERPROPERTY(i); + } + snprintf(fu->name, sizeof(fu->name), "FetchWarp%d", id); + + dc_fw_set_ops(fu); + + dc_drm->fu_disp[fu->id] = fu; + + return 0; +} + +static const struct component_ops dc_fw_ops = { + .bind = dc_fw_bind, +}; + +static int dc_fw_probe(struct platform_device *pdev) +{ + int ret; + + ret = component_add(&pdev->dev, &dc_fw_ops); + if (ret) + return dev_err_probe(&pdev->dev, ret, + "failed to add component\n"); + + return 0; +} + +static void dc_fw_remove(struct platform_device *pdev) +{ + component_del(&pdev->dev, &dc_fw_ops); +} + +static const struct of_device_id dc_fw_dt_ids[] = { + { .compatible = "fsl,imx8qxp-dc-fetchwarp" }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, dc_fw_dt_ids); + +struct platform_driver dc_fw_driver = { + .probe = dc_fw_probe, + .remove = dc_fw_remove, + .driver = { + .name = "imx8-dc-fetchwarp", + .suppress_bind_attrs = true, + .of_match_table = dc_fw_dt_ids, + }, +}; diff --git a/drivers/gpu/drm/imx/dc/dc-ic.c b/drivers/gpu/drm/imx/dc/dc-ic.c new file mode 100644 index 000000000000..a270ae4030cd --- /dev/null +++ b/drivers/gpu/drm/imx/dc/dc-ic.c @@ -0,0 +1,282 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright 2024 NXP + */ + +#include <linux/clk.h> +#include <linux/interrupt.h> +#include <linux/irq.h> +#include <linux/irqchip/chained_irq.h> +#include <linux/irqdomain.h> +#include <linux/of.h> +#include <linux/of_irq.h> +#include <linux/platform_device.h> +#include <linux/pm_runtime.h> +#include <linux/regmap.h> + +#define USERINTERRUPTMASK(n) (0x8 + 4 * (n)) +#define INTERRUPTENABLE(n) (0x10 + 4 * (n)) +#define INTERRUPTPRESET(n) (0x18 + 4 * (n)) +#define INTERRUPTCLEAR(n) (0x20 + 4 * (n)) +#define INTERRUPTSTATUS(n) (0x28 + 4 * (n)) +#define USERINTERRUPTENABLE(n) (0x40 + 4 * (n)) +#define USERINTERRUPTPRESET(n) (0x48 + 4 * (n)) +#define USERINTERRUPTCLEAR(n) (0x50 + 4 * (n)) +#define USERINTERRUPTSTATUS(n) (0x58 + 4 * (n)) + +#define IRQ_COUNT 49 +#define IRQ_RESERVED 35 +#define REG_NUM 2 + +struct dc_ic_data { + struct regmap *regs; + struct clk *clk_axi; + int irq[IRQ_COUNT]; + struct irq_domain *domain; +}; + +struct dc_ic_entry { + struct dc_ic_data *data; + int irq; +}; + +static const struct regmap_range dc_ic_regmap_write_ranges[] = { + regmap_reg_range(USERINTERRUPTMASK(0), INTERRUPTCLEAR(1)), + regmap_reg_range(USERINTERRUPTENABLE(0), USERINTERRUPTCLEAR(1)), +}; + +static const struct regmap_access_table dc_ic_regmap_write_table = { + .yes_ranges = dc_ic_regmap_write_ranges, + .n_yes_ranges = ARRAY_SIZE(dc_ic_regmap_write_ranges), +}; + +static const struct regmap_range dc_ic_regmap_read_ranges[] = { + regmap_reg_range(USERINTERRUPTMASK(0), INTERRUPTENABLE(1)), + regmap_reg_range(INTERRUPTSTATUS(0), INTERRUPTSTATUS(1)), + regmap_reg_range(USERINTERRUPTENABLE(0), USERINTERRUPTENABLE(1)), + regmap_reg_range(USERINTERRUPTSTATUS(0), USERINTERRUPTSTATUS(1)), +}; + +static const struct regmap_access_table dc_ic_regmap_read_table = { + .yes_ranges = dc_ic_regmap_read_ranges, + .n_yes_ranges = ARRAY_SIZE(dc_ic_regmap_read_ranges), +}; + +static const struct regmap_range dc_ic_regmap_volatile_ranges[] = { + regmap_reg_range(INTERRUPTPRESET(0), INTERRUPTCLEAR(1)), + regmap_reg_range(USERINTERRUPTPRESET(0), USERINTERRUPTCLEAR(1)), +}; + +static const struct regmap_access_table dc_ic_regmap_volatile_table = { + .yes_ranges = dc_ic_regmap_volatile_ranges, + .n_yes_ranges = ARRAY_SIZE(dc_ic_regmap_volatile_ranges), +}; + +static const struct regmap_config dc_ic_regmap_config = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + .fast_io = true, + .wr_table = &dc_ic_regmap_write_table, + .rd_table = &dc_ic_regmap_read_table, + .volatile_table = &dc_ic_regmap_volatile_table, + .max_register = USERINTERRUPTSTATUS(1), +}; + +static void dc_ic_irq_handler(struct irq_desc *desc) +{ + struct dc_ic_entry *entry = irq_desc_get_handler_data(desc); + struct dc_ic_data *data = entry->data; + unsigned int status, enable; + unsigned int virq; + + chained_irq_enter(irq_desc_get_chip(desc), desc); + + regmap_read(data->regs, USERINTERRUPTSTATUS(entry->irq / 32), &status); + regmap_read(data->regs, USERINTERRUPTENABLE(entry->irq / 32), &enable); + + status &= enable; + + if (status & BIT(entry->irq % 32)) { + virq = irq_find_mapping(data->domain, entry->irq); + if (virq) + generic_handle_irq(virq); + } + + chained_irq_exit(irq_desc_get_chip(desc), desc); +} + +static const unsigned long unused_irq[REG_NUM] = {0x00000000, 0xfffe0008}; + +static int dc_ic_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct irq_chip_generic *gc; + struct dc_ic_entry *entry; + struct irq_chip_type *ct; + struct dc_ic_data *data; + void __iomem *base; + int i, ret; + + data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL); + if (!data) + return -ENOMEM; + + entry = devm_kcalloc(dev, IRQ_COUNT, sizeof(*entry), GFP_KERNEL); + if (!entry) + return -ENOMEM; + + base = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(base)) { + dev_err(dev, "failed to initialize reg\n"); + return PTR_ERR(base); + } + + data->regs = devm_regmap_init_mmio(dev, base, &dc_ic_regmap_config); + if (IS_ERR(data->regs)) + return PTR_ERR(data->regs); + + data->clk_axi = devm_clk_get(dev, NULL); + if (IS_ERR(data->clk_axi)) + return dev_err_probe(dev, PTR_ERR(data->clk_axi), + "failed to get AXI clock\n"); + + for (i = 0; i < IRQ_COUNT; i++) { + /* skip the reserved IRQ */ + if (i == IRQ_RESERVED) + continue; + + ret = platform_get_irq(pdev, i); + if (ret < 0) + return ret; + } + + dev_set_drvdata(dev, data); + + ret = devm_pm_runtime_enable(dev); + if (ret) + return ret; + + ret = pm_runtime_resume_and_get(dev); + if (ret < 0) { + dev_err(dev, "failed to get runtime PM sync: %d\n", ret); + return ret; + } + + for (i = 0; i < REG_NUM; i++) { + /* mask and clear all interrupts */ + regmap_write(data->regs, USERINTERRUPTENABLE(i), 0x0); + regmap_write(data->regs, INTERRUPTENABLE(i), 0x0); + regmap_write(data->regs, USERINTERRUPTCLEAR(i), 0xffffffff); + regmap_write(data->regs, INTERRUPTCLEAR(i), 0xffffffff); + + /* set all interrupts to user mode */ + regmap_write(data->regs, USERINTERRUPTMASK(i), 0xffffffff); + } + + data->domain = irq_domain_add_linear(dev->of_node, IRQ_COUNT, + &irq_generic_chip_ops, data); + if (!data->domain) { + dev_err(dev, "failed to create IRQ domain\n"); + pm_runtime_put(dev); + return -ENOMEM; + } + irq_domain_set_pm_device(data->domain, dev); + + ret = irq_alloc_domain_generic_chips(data->domain, 32, 1, "DC", + handle_level_irq, 0, 0, 0); + if (ret) { + dev_err(dev, "failed to alloc generic IRQ chips: %d\n", ret); + irq_domain_remove(data->domain); + pm_runtime_put(dev); + return ret; + } + + for (i = 0; i < IRQ_COUNT; i += 32) { + gc = irq_get_domain_generic_chip(data->domain, i); + gc->reg_base = base; + gc->unused = unused_irq[i / 32]; + ct = gc->chip_types; + ct->chip.irq_ack = irq_gc_ack_set_bit; + ct->chip.irq_mask = irq_gc_mask_clr_bit; + ct->chip.irq_unmask = irq_gc_mask_set_bit; + ct->regs.ack = USERINTERRUPTCLEAR(i / 32); + ct->regs.mask = USERINTERRUPTENABLE(i / 32); + } + + for (i = 0; i < IRQ_COUNT; i++) { + /* skip the reserved IRQ */ + if (i == IRQ_RESERVED) + continue; + + data->irq[i] = irq_of_parse_and_map(dev->of_node, i); + + entry[i].data = data; + entry[i].irq = i; + + irq_set_chained_handler_and_data(data->irq[i], + dc_ic_irq_handler, &entry[i]); + } + + return 0; +} + +static void dc_ic_remove(struct platform_device *pdev) +{ + struct dc_ic_data *data = dev_get_drvdata(&pdev->dev); + int i; + + for (i = 0; i < IRQ_COUNT; i++) { + if (i == IRQ_RESERVED) + continue; + + irq_set_chained_handler_and_data(data->irq[i], NULL, NULL); + } + + irq_domain_remove(data->domain); + + pm_runtime_put_sync(&pdev->dev); +} + +static int dc_ic_runtime_suspend(struct device *dev) +{ + struct dc_ic_data *data = dev_get_drvdata(dev); + + clk_disable_unprepare(data->clk_axi); + + return 0; +} + +static int dc_ic_runtime_resume(struct device *dev) +{ + struct dc_ic_data *data = dev_get_drvdata(dev); + int ret; + + ret = clk_prepare_enable(data->clk_axi); + if (ret) + dev_err(dev, "failed to enable AXI clock: %d\n", ret); + + return ret; +} + +static const struct dev_pm_ops dc_ic_pm_ops = { + SET_NOIRQ_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, + pm_runtime_force_resume) + RUNTIME_PM_OPS(dc_ic_runtime_suspend, dc_ic_runtime_resume, NULL) +}; + +static const struct of_device_id dc_ic_dt_ids[] = { + { .compatible = "fsl,imx8qxp-dc-intc", }, + { /* sentinel */ } +}; + +struct platform_driver dc_ic_driver = { + .probe = dc_ic_probe, + .remove = dc_ic_remove, + .driver = { + .name = "imx8-dc-intc", + .suppress_bind_attrs = true, + .of_match_table = dc_ic_dt_ids, + .pm = pm_sleep_ptr(&dc_ic_pm_ops), + }, +}; diff --git a/drivers/gpu/drm/imx/dc/dc-kms.c b/drivers/gpu/drm/imx/dc/dc-kms.c new file mode 100644 index 000000000000..2b18aa37a4a8 --- /dev/null +++ b/drivers/gpu/drm/imx/dc/dc-kms.c @@ -0,0 +1,143 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright 2024 NXP + */ + +#include <linux/of.h> +#include <linux/of_graph.h> + +#include <drm/drm_atomic_helper.h> +#include <drm/drm_bridge.h> +#include <drm/drm_bridge_connector.h> +#include <drm/drm_connector.h> +#include <drm/drm_crtc.h> +#include <drm/drm_device.h> +#include <drm/drm_encoder.h> +#include <drm/drm_gem_framebuffer_helper.h> +#include <drm/drm_mode_config.h> +#include <drm/drm_print.h> +#include <drm/drm_probe_helper.h> +#include <drm/drm_simple_kms_helper.h> +#include <drm/drm_vblank.h> + +#include "dc-de.h" +#include "dc-drv.h" +#include "dc-kms.h" + +static const struct drm_mode_config_funcs dc_drm_mode_config_funcs = { + .fb_create = drm_gem_fb_create, + .atomic_check = drm_atomic_helper_check, + .atomic_commit = drm_atomic_helper_commit, +}; + +static int dc_kms_init_encoder_per_crtc(struct dc_drm_device *dc_drm, + int crtc_index) +{ + struct dc_crtc *dc_crtc = &dc_drm->dc_crtc[crtc_index]; + struct drm_device *drm = &dc_drm->base; + struct drm_crtc *crtc = &dc_crtc->base; + struct drm_connector *connector; + struct device *dev = drm->dev; + struct drm_encoder *encoder; + struct drm_bridge *bridge; + int ret; + + bridge = devm_drm_of_get_bridge(dev, dc_crtc->de->tc->dev->of_node, + 0, 0); + if (IS_ERR(bridge)) { + ret = PTR_ERR(bridge); + if (ret == -ENODEV) + return 0; + + return dev_err_probe(dev, ret, + "failed to find bridge for CRTC%u\n", + crtc->index); + } + + encoder = &dc_drm->encoder[crtc_index]; + ret = drm_simple_encoder_init(drm, encoder, DRM_MODE_ENCODER_NONE); + if (ret) { + dev_err(dev, "failed to initialize encoder for CRTC%u: %d\n", + crtc->index, ret); + return ret; + } + + encoder->possible_crtcs = drm_crtc_mask(crtc); + + ret = drm_bridge_attach(encoder, bridge, NULL, + DRM_BRIDGE_ATTACH_NO_CONNECTOR); + if (ret) { + dev_err(dev, + "failed to attach bridge to encoder for CRTC%u: %d\n", + crtc->index, ret); + return ret; + } + + connector = drm_bridge_connector_init(drm, encoder); + if (IS_ERR(connector)) { + ret = PTR_ERR(connector); + dev_err(dev, "failed to init bridge connector for CRTC%u: %d\n", + crtc->index, ret); + return ret; + } + + ret = drm_connector_attach_encoder(connector, encoder); + if (ret) + dev_err(dev, + "failed to attach encoder to connector for CRTC%u: %d\n", + crtc->index, ret); + + return ret; +} + +int dc_kms_init(struct dc_drm_device *dc_drm) +{ + struct drm_device *drm = &dc_drm->base; + int ret, i; + + ret = drmm_mode_config_init(drm); + if (ret) + return ret; + + drm->mode_config.min_width = 60; + drm->mode_config.min_height = 60; + drm->mode_config.max_width = 8192; + drm->mode_config.max_height = 8192; + drm->mode_config.funcs = &dc_drm_mode_config_funcs; + + drm->vblank_disable_immediate = true; + drm->max_vblank_count = DC_FRAMEGEN_MAX_FRAME_INDEX; + + for (i = 0; i < DC_DISPLAYS; i++) { + ret = dc_crtc_init(dc_drm, i); + if (ret) + return ret; + + ret = dc_kms_init_encoder_per_crtc(dc_drm, i); + if (ret) + return ret; + } + + for (i = 0; i < DC_DISPLAYS; i++) { + ret = dc_crtc_post_init(dc_drm, i); + if (ret) + return ret; + } + + ret = drm_vblank_init(drm, DC_DISPLAYS); + if (ret) { + dev_err(drm->dev, "failed to init vblank support: %d\n", ret); + return ret; + } + + drm_mode_config_reset(drm); + + drm_kms_helper_poll_init(drm); + + return 0; +} + +void dc_kms_uninit(struct dc_drm_device *dc_drm) +{ + drm_kms_helper_poll_fini(&dc_drm->base); +} diff --git a/drivers/gpu/drm/imx/dc/dc-kms.h b/drivers/gpu/drm/imx/dc/dc-kms.h new file mode 100644 index 000000000000..cd7860eff986 --- /dev/null +++ b/drivers/gpu/drm/imx/dc/dc-kms.h @@ -0,0 +1,131 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Copyright 2024 NXP + */ + +#ifndef __DC_KMS_H__ +#define __DC_KMS_H__ + +#include <linux/completion.h> + +#include <drm/drm_crtc.h> +#include <drm/drm_plane.h> +#include <drm/drm_vblank.h> + +#include "dc-de.h" +#include "dc-fu.h" +#include "dc-pe.h" + +#define DC_CRTC_IRQS 5 + +struct dc_crtc_irq { + struct dc_crtc *dc_crtc; + unsigned int irq; +}; + +/** + * struct dc_crtc - DC specific drm_crtc + * + * Each display controller contains one content stream and one safety stream. + * In general, the two streams have the same functionality. One stream is + * overlaid on the other by @fg. This driver chooses to generate black constant + * color from the content stream as background color, build plane(s) on the + * content stream by using layerblend(s) and always generate a constant color + * from the safety stream. Note that due to the decoupled timing, the safety + * stream still works to show the constant color properly even when the content + * stream has completely hung up due to mal-function of this driver. + */ +struct dc_crtc { + /** @base: base drm_crtc structure */ + struct drm_crtc base; + /** @de: display engine */ + struct dc_de *de; + /** @cf_cont: content stream constframe */ + struct dc_cf *cf_cont; + /** @cf_safe: safety stream constframe */ + struct dc_cf *cf_safe; + /** @ed_cont: content stream extdst */ + struct dc_ed *ed_cont; + /** @ed_safe: safety stream extdst */ + struct dc_ed *ed_safe; + /** @fg: framegen */ + struct dc_fg *fg; + /** + * @irq_dec_framecomplete: + * + * display engine configuration frame complete interrupt + */ + unsigned int irq_dec_framecomplete; + /** + * @irq_dec_seqcomplete: + * + * display engine configuration sequence complete interrupt + */ + unsigned int irq_dec_seqcomplete; + /** + * @irq_dec_shdload: + * + * display engine configuration shadow load interrupt + */ + unsigned int irq_dec_shdload; + /** + * @irq_ed_cont_shdload: + * + * content stream extdst shadow load interrupt + */ + unsigned int irq_ed_cont_shdload; + /** + * @irq_ed_safe_shdload: + * + * safety stream extdst shadow load interrupt + */ + unsigned int irq_ed_safe_shdload; + /** + * @dec_seqcomplete_done: + * + * display engine configuration sequence completion + */ + struct completion dec_seqcomplete_done; + /** + * @dec_shdload_done: + * + * display engine configuration shadow load completion + */ + struct completion dec_shdload_done; + /** + * @ed_cont_shdload_done: + * + * content stream extdst shadow load completion + */ + struct completion ed_cont_shdload_done; + /** + * @ed_safe_shdload_done: + * + * safety stream extdst shadow load completion + */ + struct completion ed_safe_shdload_done; + /** @event: cached pending vblank event */ + struct drm_pending_vblank_event *event; + /** @irqs: interrupt list */ + struct dc_crtc_irq irqs[DC_CRTC_IRQS]; +}; + +/** + * struct dc_plane - DC specific drm_plane + * + * Build a plane on content stream with a fetchunit and a layerblend. + */ +struct dc_plane { + /** @base: base drm_plane structure */ + struct drm_plane base; + /** @fu: fetchunit */ + struct dc_fu *fu; + /** @cf: content stream constframe */ + struct dc_cf *cf; + /** @lb: layerblend */ + struct dc_lb *lb; + /** @ed: content stream extdst */ + struct dc_ed *ed; +}; + +#endif /* __DC_KMS_H__ */ diff --git a/drivers/gpu/drm/imx/dc/dc-lb.c b/drivers/gpu/drm/imx/dc/dc-lb.c new file mode 100644 index 000000000000..38f966625d38 --- /dev/null +++ b/drivers/gpu/drm/imx/dc/dc-lb.c @@ -0,0 +1,325 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright 2024 NXP + */ + +#include <linux/bitfield.h> +#include <linux/bits.h> +#include <linux/component.h> +#include <linux/mod_devicetable.h> +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/regmap.h> + +#include <drm/drm_blend.h> + +#include "dc-drv.h" +#include "dc-pe.h" + +#define PIXENGCFG_DYNAMIC 0x8 +#define PIXENGCFG_DYNAMIC_PRIM_SEL_MASK GENMASK(5, 0) +#define PIXENGCFG_DYNAMIC_PRIM_SEL(x) \ + FIELD_PREP(PIXENGCFG_DYNAMIC_PRIM_SEL_MASK, (x)) +#define PIXENGCFG_DYNAMIC_SEC_SEL_MASK GENMASK(13, 8) +#define PIXENGCFG_DYNAMIC_SEC_SEL(x) \ + FIELD_PREP(PIXENGCFG_DYNAMIC_SEC_SEL_MASK, (x)) + +#define STATICCONTROL 0x8 +#define SHDTOKSEL_MASK GENMASK(4, 3) +#define SHDTOKSEL(x) FIELD_PREP(SHDTOKSEL_MASK, (x)) +#define SHDLDSEL_MASK GENMASK(2, 1) +#define SHDLDSEL(x) FIELD_PREP(SHDLDSEL_MASK, (x)) + +#define CONTROL 0xc +#define CTRL_MODE_MASK BIT(0) +#define CTRL_MODE(x) FIELD_PREP(CTRL_MODE_MASK, (x)) + +#define BLENDCONTROL 0x10 +#define ALPHA_MASK GENMASK(23, 16) +#define ALPHA(x) FIELD_PREP(ALPHA_MASK, (x)) +#define PRIM_C_BLD_FUNC_MASK GENMASK(2, 0) +#define PRIM_C_BLD_FUNC(x) \ + FIELD_PREP(PRIM_C_BLD_FUNC_MASK, (x)) +#define SEC_C_BLD_FUNC_MASK GENMASK(6, 4) +#define SEC_C_BLD_FUNC(x) \ + FIELD_PREP(SEC_C_BLD_FUNC_MASK, (x)) +#define PRIM_A_BLD_FUNC_MASK GENMASK(10, 8) +#define PRIM_A_BLD_FUNC(x) \ + FIELD_PREP(PRIM_A_BLD_FUNC_MASK, (x)) +#define SEC_A_BLD_FUNC_MASK GENMASK(14, 12) +#define SEC_A_BLD_FUNC(x) \ + FIELD_PREP(SEC_A_BLD_FUNC_MASK, (x)) + +#define POSITION 0x14 +#define XPOS_MASK GENMASK(15, 0) +#define XPOS(x) FIELD_PREP(XPOS_MASK, (x)) +#define YPOS_MASK GENMASK(31, 16) +#define YPOS(x) FIELD_PREP(YPOS_MASK, (x)) + +enum dc_lb_blend_func { + DC_LAYERBLEND_BLEND_ZERO, + DC_LAYERBLEND_BLEND_ONE, + DC_LAYERBLEND_BLEND_PRIM_ALPHA, + DC_LAYERBLEND_BLEND_ONE_MINUS_PRIM_ALPHA, + DC_LAYERBLEND_BLEND_SEC_ALPHA, + DC_LAYERBLEND_BLEND_ONE_MINUS_SEC_ALPHA, + DC_LAYERBLEND_BLEND_CONST_ALPHA, + DC_LAYERBLEND_BLEND_ONE_MINUS_CONST_ALPHA, +}; + +enum dc_lb_shadow_sel { + BOTH = 0x2, +}; + +static const struct dc_subdev_info dc_lb_info[] = { + { .reg_start = 0x56180ba0, .id = 0, }, + { .reg_start = 0x56180bc0, .id = 1, }, + { .reg_start = 0x56180be0, .id = 2, }, + { .reg_start = 0x56180c00, .id = 3, }, +}; + +static const struct regmap_range dc_lb_pec_regmap_access_ranges[] = { + regmap_reg_range(PIXENGCFG_DYNAMIC, PIXENGCFG_DYNAMIC), +}; + +static const struct regmap_access_table dc_lb_pec_regmap_access_table = { + .yes_ranges = dc_lb_pec_regmap_access_ranges, + .n_yes_ranges = ARRAY_SIZE(dc_lb_pec_regmap_access_ranges), +}; + +static const struct regmap_config dc_lb_pec_regmap_config = { + .name = "pec", + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + .fast_io = true, + .wr_table = &dc_lb_pec_regmap_access_table, + .rd_table = &dc_lb_pec_regmap_access_table, + .max_register = PIXENGCFG_DYNAMIC, +}; + +static const struct regmap_range dc_lb_regmap_ranges[] = { + regmap_reg_range(STATICCONTROL, POSITION), +}; + +static const struct regmap_access_table dc_lb_regmap_access_table = { + .yes_ranges = dc_lb_regmap_ranges, + .n_yes_ranges = ARRAY_SIZE(dc_lb_regmap_ranges), +}; + +static const struct regmap_config dc_lb_cfg_regmap_config = { + .name = "cfg", + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + .fast_io = true, + .wr_table = &dc_lb_regmap_access_table, + .rd_table = &dc_lb_regmap_access_table, + .max_register = POSITION, +}; + +static const enum dc_link_id prim_sels[] = { + /* common options */ + LINK_ID_NONE, + LINK_ID_CONSTFRAME0, + LINK_ID_CONSTFRAME1, + LINK_ID_CONSTFRAME4, + LINK_ID_CONSTFRAME5, + /* + * special options: + * layerblend(n) has n special options, + * from layerblend0 to layerblend(n - 1), e.g., + * layerblend3 has 3 special options - + * layerblend0/1/2. + */ + LINK_ID_LAYERBLEND0, + LINK_ID_LAYERBLEND1, + LINK_ID_LAYERBLEND2, + LINK_ID_LAYERBLEND3, +}; + +static const enum dc_link_id sec_sels[] = { + LINK_ID_NONE, + LINK_ID_FETCHWARP2, + LINK_ID_FETCHLAYER0, +}; + +enum dc_link_id dc_lb_get_link_id(struct dc_lb *lb) +{ + return lb->link; +} + +void dc_lb_pec_dynamic_prim_sel(struct dc_lb *lb, enum dc_link_id prim) +{ + int fixed_sels_num = ARRAY_SIZE(prim_sels) - 4; + int i; + + for (i = 0; i < fixed_sels_num + lb->id; i++) { + if (prim_sels[i] == prim) { + regmap_write_bits(lb->reg_pec, PIXENGCFG_DYNAMIC, + PIXENGCFG_DYNAMIC_PRIM_SEL_MASK, + PIXENGCFG_DYNAMIC_PRIM_SEL(prim)); + return; + } + } + + dev_warn(lb->dev, "invalid primary input selection:%d\n", prim); +} + +void dc_lb_pec_dynamic_sec_sel(struct dc_lb *lb, enum dc_link_id sec) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(sec_sels); i++) { + if (sec_sels[i] == sec) { + regmap_write_bits(lb->reg_pec, PIXENGCFG_DYNAMIC, + PIXENGCFG_DYNAMIC_SEC_SEL_MASK, + PIXENGCFG_DYNAMIC_SEC_SEL(sec)); + return; + } + } + + dev_warn(lb->dev, "invalid secondary input selection:%d\n", sec); +} + +void dc_lb_pec_clken(struct dc_lb *lb, enum dc_pec_clken clken) +{ + regmap_write_bits(lb->reg_pec, PIXENGCFG_DYNAMIC, CLKEN_MASK, + CLKEN(clken)); +} + +static inline void dc_lb_enable_shden(struct dc_lb *lb) +{ + regmap_write_bits(lb->reg_cfg, STATICCONTROL, SHDEN, SHDEN); +} + +static inline void dc_lb_shdtoksel(struct dc_lb *lb, enum dc_lb_shadow_sel sel) +{ + regmap_write_bits(lb->reg_cfg, STATICCONTROL, SHDTOKSEL_MASK, + SHDTOKSEL(sel)); +} + +static inline void dc_lb_shdldsel(struct dc_lb *lb, enum dc_lb_shadow_sel sel) +{ + regmap_write_bits(lb->reg_cfg, STATICCONTROL, SHDLDSEL_MASK, + SHDLDSEL(sel)); +} + +void dc_lb_mode(struct dc_lb *lb, enum dc_lb_mode mode) +{ + regmap_write_bits(lb->reg_cfg, CONTROL, CTRL_MODE_MASK, mode); +} + +static inline void dc_lb_blendcontrol(struct dc_lb *lb) +{ + u32 val = PRIM_A_BLD_FUNC(DC_LAYERBLEND_BLEND_ZERO) | + SEC_A_BLD_FUNC(DC_LAYERBLEND_BLEND_ZERO) | + PRIM_C_BLD_FUNC(DC_LAYERBLEND_BLEND_ZERO) | + SEC_C_BLD_FUNC(DC_LAYERBLEND_BLEND_CONST_ALPHA) | + ALPHA(DRM_BLEND_ALPHA_OPAQUE >> 8); + + regmap_write(lb->reg_cfg, BLENDCONTROL, val); +} + +void dc_lb_position(struct dc_lb *lb, int x, int y) +{ + regmap_write(lb->reg_cfg, POSITION, XPOS(x) | YPOS(y)); +} + +int dc_lb_get_id(struct dc_lb *lb) +{ + return lb->id; +} + +void dc_lb_init(struct dc_lb *lb) +{ + dc_lb_pec_dynamic_prim_sel(lb, LINK_ID_NONE); + dc_lb_pec_dynamic_sec_sel(lb, LINK_ID_NONE); + dc_lb_pec_clken(lb, CLKEN_DISABLE); + dc_lb_shdldsel(lb, BOTH); + dc_lb_shdtoksel(lb, BOTH); + dc_lb_blendcontrol(lb); + dc_lb_enable_shden(lb); +} + +static int dc_lb_bind(struct device *dev, struct device *master, void *data) +{ + struct platform_device *pdev = to_platform_device(dev); + struct dc_drm_device *dc_drm = data; + struct resource *res_pec; + void __iomem *base_pec; + void __iomem *base_cfg; + struct dc_lb *lb; + + lb = devm_kzalloc(dev, sizeof(*lb), GFP_KERNEL); + if (!lb) + return -ENOMEM; + + base_pec = devm_platform_get_and_ioremap_resource(pdev, 0, &res_pec); + if (IS_ERR(base_pec)) + return PTR_ERR(base_pec); + + base_cfg = devm_platform_ioremap_resource_byname(pdev, "cfg"); + if (IS_ERR(base_cfg)) + return PTR_ERR(base_cfg); + + lb->reg_pec = devm_regmap_init_mmio(dev, base_pec, + &dc_lb_pec_regmap_config); + if (IS_ERR(lb->reg_pec)) + return PTR_ERR(lb->reg_pec); + + lb->reg_cfg = devm_regmap_init_mmio(dev, base_cfg, + &dc_lb_cfg_regmap_config); + if (IS_ERR(lb->reg_cfg)) + return PTR_ERR(lb->reg_cfg); + + lb->id = dc_subdev_get_id(dc_lb_info, ARRAY_SIZE(dc_lb_info), res_pec); + if (lb->id < 0) { + dev_err(dev, "failed to get instance number: %d\n", lb->id); + return lb->id; + } + + lb->dev = dev; + lb->link = LINK_ID_LAYERBLEND0 + lb->id; + + dc_drm->lb[lb->id] = lb; + + return 0; +} + +static const struct component_ops dc_lb_ops = { + .bind = dc_lb_bind, +}; + +static int dc_lb_probe(struct platform_device *pdev) +{ + int ret; + + ret = component_add(&pdev->dev, &dc_lb_ops); + if (ret) + return dev_err_probe(&pdev->dev, ret, + "failed to add component\n"); + + return 0; +} + +static void dc_lb_remove(struct platform_device *pdev) +{ + component_del(&pdev->dev, &dc_lb_ops); +} + +static const struct of_device_id dc_lb_dt_ids[] = { + { .compatible = "fsl,imx8qxp-dc-layerblend" }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, dc_lb_dt_ids); + +struct platform_driver dc_lb_driver = { + .probe = dc_lb_probe, + .remove = dc_lb_remove, + .driver = { + .name = "imx8-dc-layerblend", + .suppress_bind_attrs = true, + .of_match_table = dc_lb_dt_ids, + }, +}; diff --git a/drivers/gpu/drm/imx/dc/dc-pe.c b/drivers/gpu/drm/imx/dc/dc-pe.c new file mode 100644 index 000000000000..6676c22f3f45 --- /dev/null +++ b/drivers/gpu/drm/imx/dc/dc-pe.c @@ -0,0 +1,158 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright 2024 NXP + */ + +#include <linux/clk.h> +#include <linux/component.h> +#include <linux/mod_devicetable.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/of_platform.h> +#include <linux/platform_device.h> +#include <linux/pm.h> +#include <linux/pm_runtime.h> + +#include "dc-drv.h" +#include "dc-fu.h" +#include "dc-pe.h" + +static int dc_pe_bind(struct device *dev, struct device *master, void *data) +{ + struct dc_drm_device *dc_drm = data; + struct dc_pe *pe; + int ret; + + pe = devm_kzalloc(dev, sizeof(*pe), GFP_KERNEL); + if (!pe) + return -ENOMEM; + + pe->clk_axi = devm_clk_get(dev, NULL); + if (IS_ERR(pe->clk_axi)) + return dev_err_probe(dev, PTR_ERR(pe->clk_axi), + "failed to get AXI clock\n"); + + pe->dev = dev; + + dev_set_drvdata(dev, pe); + + ret = devm_pm_runtime_enable(dev); + if (ret) + return ret; + + dc_drm->pe = pe; + + return 0; +} + +/* + * It's possible to get the child device pointers from the child component + * bind callbacks, but it depends on the component helper behavior to bind + * the pixel engine component first. To avoid the dependency, post bind to + * get the pointers from dc_drm in a safe manner. + */ +void dc_pe_post_bind(struct dc_drm_device *dc_drm) +{ + struct dc_pe *pe = dc_drm->pe; + int i; + + for (i = 0; i < DC_DISPLAYS; i++) { + pe->cf_safe[i] = dc_drm->cf_safe[i]; + pe->cf_cont[i] = dc_drm->cf_cont[i]; + pe->ed_safe[i] = dc_drm->ed_safe[i]; + pe->ed_cont[i] = dc_drm->ed_cont[i]; + } + + for (i = 0; i < DC_DISP_FU_CNT; i++) + pe->fu_disp[i] = dc_drm->fu_disp[i]; + + for (i = 0; i < DC_LB_CNT; i++) + pe->lb[i] = dc_drm->lb[i]; +} + +static const struct component_ops dc_pe_ops = { + .bind = dc_pe_bind, +}; + +static int dc_pe_probe(struct platform_device *pdev) +{ + int ret; + + ret = devm_of_platform_populate(&pdev->dev); + if (ret < 0) + return ret; + + ret = component_add(&pdev->dev, &dc_pe_ops); + if (ret) + return dev_err_probe(&pdev->dev, ret, + "failed to add component\n"); + + return 0; +} + +static void dc_pe_remove(struct platform_device *pdev) +{ + component_del(&pdev->dev, &dc_pe_ops); +} + +static int dc_pe_runtime_suspend(struct device *dev) +{ + struct dc_pe *pe = dev_get_drvdata(dev); + + clk_disable_unprepare(pe->clk_axi); + + return 0; +} + +static int dc_pe_runtime_resume(struct device *dev) +{ + struct dc_pe *pe = dev_get_drvdata(dev); + int i, ret; + + ret = clk_prepare_enable(pe->clk_axi); + if (ret) { + dev_err(dev, "failed to enable AXI clock: %d\n", ret); + return ret; + } + + for (i = 0; i < ARRAY_SIZE(pe->cf_safe); i++) + dc_cf_init(pe->cf_safe[i]); + + for (i = 0; i < ARRAY_SIZE(pe->cf_cont); i++) + dc_cf_init(pe->cf_cont[i]); + + for (i = 0; i < ARRAY_SIZE(pe->ed_safe); i++) + dc_ed_init(pe->ed_safe[i]); + + for (i = 0; i < ARRAY_SIZE(pe->ed_cont); i++) + dc_ed_init(pe->ed_cont[i]); + + for (i = 0; i < ARRAY_SIZE(pe->fu_disp); i++) + pe->fu_disp[i]->ops.init(pe->fu_disp[i]); + + for (i = 0; i < ARRAY_SIZE(pe->lb); i++) + dc_lb_init(pe->lb[i]); + + return 0; +} + +static const struct dev_pm_ops dc_pe_pm_ops = { + RUNTIME_PM_OPS(dc_pe_runtime_suspend, dc_pe_runtime_resume, NULL) +}; + +static const struct of_device_id dc_pe_dt_ids[] = { + { .compatible = "fsl,imx8qxp-dc-pixel-engine", }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, dc_pe_dt_ids); + +struct platform_driver dc_pe_driver = { + .probe = dc_pe_probe, + .remove = dc_pe_remove, + .driver = { + .name = "imx8-dc-pixel-engine", + .suppress_bind_attrs = true, + .of_match_table = dc_pe_dt_ids, + .pm = pm_sleep_ptr(&dc_pe_pm_ops), + }, +}; diff --git a/drivers/gpu/drm/imx/dc/dc-pe.h b/drivers/gpu/drm/imx/dc/dc-pe.h new file mode 100644 index 000000000000..f5e01a6eb9e9 --- /dev/null +++ b/drivers/gpu/drm/imx/dc/dc-pe.h @@ -0,0 +1,101 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Copyright 2024 NXP + */ + +#ifndef __DC_PIXEL_ENGINE_H__ +#define __DC_PIXEL_ENGINE_H__ + +#include <linux/clk.h> +#include <linux/device.h> +#include <linux/regmap.h> + +#include "dc-de.h" + +#define SHDEN BIT(0) + +#define CLKEN_MASK_SHIFT 24 +#define CLKEN_MASK (0x3 << CLKEN_MASK_SHIFT) +#define CLKEN(n) ((n) << CLKEN_MASK_SHIFT) + +#define DC_DISP_FU_CNT 2 +#define DC_LB_CNT 4 + +enum dc_link_id { + LINK_ID_NONE = 0x00, + LINK_ID_CONSTFRAME0 = 0x0c, + LINK_ID_CONSTFRAME4 = 0x0e, + LINK_ID_CONSTFRAME1 = 0x10, + LINK_ID_CONSTFRAME5 = 0x12, + LINK_ID_FETCHWARP2 = 0x14, + LINK_ID_FETCHLAYER0 = 0x1a, + LINK_ID_LAYERBLEND0 = 0x21, + LINK_ID_LAYERBLEND1 = 0x22, + LINK_ID_LAYERBLEND2 = 0x23, + LINK_ID_LAYERBLEND3 = 0x24, +}; + +enum dc_lb_mode { + LB_NEUTRAL, /* Output is same as primary input. */ + LB_BLEND, +}; + +enum dc_pec_clken { + CLKEN_DISABLE, + CLKEN_AUTOMATIC, +}; + +struct dc_cf { + struct regmap *reg_cfg; + enum dc_link_id link; +}; + +struct dc_ed { + struct device *dev; + struct regmap *reg_pec; + struct regmap *reg_cfg; + int irq_shdload; +}; + +struct dc_lb { + struct device *dev; + struct regmap *reg_pec; + struct regmap *reg_cfg; + int id; + enum dc_link_id link; +}; + +struct dc_pe { + struct device *dev; + struct clk *clk_axi; + struct dc_cf *cf_safe[DC_DISPLAYS]; + struct dc_cf *cf_cont[DC_DISPLAYS]; + struct dc_ed *ed_safe[DC_DISPLAYS]; + struct dc_ed *ed_cont[DC_DISPLAYS]; + struct dc_fu *fu_disp[DC_DISP_FU_CNT]; + struct dc_lb *lb[DC_LB_CNT]; +}; + +/* Constant Frame Unit */ +enum dc_link_id dc_cf_get_link_id(struct dc_cf *cf); +void dc_cf_framedimensions(struct dc_cf *cf, unsigned int w, unsigned int h); +void dc_cf_constantcolor_black(struct dc_cf *cf); +void dc_cf_constantcolor_blue(struct dc_cf *cf); +void dc_cf_init(struct dc_cf *cf); + +/* External Destination Unit */ +void dc_ed_pec_src_sel(struct dc_ed *ed, enum dc_link_id src); +void dc_ed_pec_sync_trigger(struct dc_ed *ed); +void dc_ed_init(struct dc_ed *ed); + +/* Layer Blend Unit */ +enum dc_link_id dc_lb_get_link_id(struct dc_lb *lb); +void dc_lb_pec_dynamic_prim_sel(struct dc_lb *lb, enum dc_link_id prim); +void dc_lb_pec_dynamic_sec_sel(struct dc_lb *lb, enum dc_link_id sec); +void dc_lb_pec_clken(struct dc_lb *lb, enum dc_pec_clken clken); +void dc_lb_mode(struct dc_lb *lb, enum dc_lb_mode mode); +void dc_lb_position(struct dc_lb *lb, int x, int y); +int dc_lb_get_id(struct dc_lb *lb); +void dc_lb_init(struct dc_lb *lb); + +#endif /* __DC_PIXEL_ENGINE_H__ */ diff --git a/drivers/gpu/drm/imx/dc/dc-plane.c b/drivers/gpu/drm/imx/dc/dc-plane.c new file mode 100644 index 000000000000..d8b946fb90de --- /dev/null +++ b/drivers/gpu/drm/imx/dc/dc-plane.c @@ -0,0 +1,224 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright 2024 NXP + */ + +#include <linux/container_of.h> + +#include <drm/drm_atomic.h> +#include <drm/drm_atomic_helper.h> +#include <drm/drm_atomic_state_helper.h> +#include <drm/drm_crtc.h> +#include <drm/drm_drv.h> +#include <drm/drm_fb_dma_helper.h> +#include <drm/drm_fourcc.h> +#include <drm/drm_framebuffer.h> +#include <drm/drm_gem_atomic_helper.h> +#include <drm/drm_plane_helper.h> +#include <drm/drm_print.h> + +#include "dc-drv.h" +#include "dc-fu.h" +#include "dc-kms.h" + +#define DC_PLANE_MAX_PITCH 0x10000 +#define DC_PLANE_MAX_PIX_CNT 8192 + +#define dc_plane_dbg(plane, fmt, ...) \ +do { \ + struct drm_plane *_plane = (plane); \ + drm_dbg_kms(_plane->dev, "[PLANE:%d:%s] " fmt, \ + _plane->base.id, _plane->name, ##__VA_ARGS__); \ +} while (0) + +static const uint32_t dc_plane_formats[] = { + DRM_FORMAT_XRGB8888, +}; + +static const struct drm_plane_funcs dc_plane_funcs = { + .update_plane = drm_atomic_helper_update_plane, + .disable_plane = drm_atomic_helper_disable_plane, + .destroy = drm_plane_cleanup, + .reset = drm_atomic_helper_plane_reset, + .atomic_duplicate_state = drm_atomic_helper_plane_duplicate_state, + .atomic_destroy_state = drm_atomic_helper_plane_destroy_state, +}; + +static inline struct dc_plane *to_dc_plane(struct drm_plane *plane) +{ + return container_of(plane, struct dc_plane, base); +} + +static int dc_plane_check_max_source_resolution(struct drm_plane_state *state) +{ + int src_h = drm_rect_height(&state->src) >> 16; + int src_w = drm_rect_width(&state->src) >> 16; + + if (src_w > DC_PLANE_MAX_PIX_CNT || src_h > DC_PLANE_MAX_PIX_CNT) { + dc_plane_dbg(state->plane, "invalid source resolution\n"); + return -EINVAL; + } + + return 0; +} + +static int dc_plane_check_fb(struct drm_plane_state *state) +{ + struct drm_framebuffer *fb = state->fb; + dma_addr_t baseaddr = drm_fb_dma_get_gem_addr(fb, state, 0); + + /* base address alignment */ + if (baseaddr & 0x3) { + dc_plane_dbg(state->plane, "fb bad baddr alignment\n"); + return -EINVAL; + } + + /* pitches[0] range */ + if (fb->pitches[0] > DC_PLANE_MAX_PITCH) { + dc_plane_dbg(state->plane, "fb pitches[0] is out of range\n"); + return -EINVAL; + } + + /* pitches[0] alignment */ + if (fb->pitches[0] & 0x3) { + dc_plane_dbg(state->plane, "fb bad pitches[0] alignment\n"); + return -EINVAL; + } + + return 0; +} + +static int +dc_plane_atomic_check(struct drm_plane *plane, struct drm_atomic_state *state) +{ + struct drm_plane_state *plane_state = + drm_atomic_get_new_plane_state(state, plane); + struct drm_crtc_state *crtc_state; + int ret; + + /* ok to disable */ + if (!plane_state->fb) + return 0; + + if (!plane_state->crtc) { + dc_plane_dbg(plane, "no CRTC in plane state\n"); + return -EINVAL; + } + + crtc_state = + drm_atomic_get_existing_crtc_state(state, plane_state->crtc); + if (WARN_ON(!crtc_state)) + return -EINVAL; + + ret = drm_atomic_helper_check_plane_state(plane_state, crtc_state, + DRM_PLANE_NO_SCALING, + DRM_PLANE_NO_SCALING, + true, false); + if (ret) { + dc_plane_dbg(plane, "failed to check plane state: %d\n", ret); + return ret; + } + + ret = dc_plane_check_max_source_resolution(plane_state); + if (ret) + return ret; + + return dc_plane_check_fb(plane_state); +} + +static void +dc_plane_atomic_update(struct drm_plane *plane, struct drm_atomic_state *state) +{ + struct drm_plane_state *new_state = + drm_atomic_get_new_plane_state(state, plane); + struct dc_plane *dplane = to_dc_plane(plane); + struct drm_framebuffer *fb = new_state->fb; + const struct dc_fu_ops *fu_ops; + struct dc_lb *lb = dplane->lb; + struct dc_fu *fu = dplane->fu; + dma_addr_t baseaddr; + int src_w, src_h; + int idx; + + if (!drm_dev_enter(plane->dev, &idx)) + return; + + src_w = drm_rect_width(&new_state->src) >> 16; + src_h = drm_rect_height(&new_state->src) >> 16; + + baseaddr = drm_fb_dma_get_gem_addr(fb, new_state, 0); + + fu_ops = dc_fu_get_ops(dplane->fu); + + fu_ops->set_layerblend(fu, lb); + fu_ops->set_burstlength(fu, baseaddr); + fu_ops->set_src_stride(fu, DC_FETCHUNIT_FRAC0, fb->pitches[0]); + fu_ops->set_src_buf_dimensions(fu, DC_FETCHUNIT_FRAC0, src_w, src_h); + fu_ops->set_fmt(fu, DC_FETCHUNIT_FRAC0, fb->format); + fu_ops->set_framedimensions(fu, src_w, src_h); + fu_ops->set_baseaddress(fu, DC_FETCHUNIT_FRAC0, baseaddr); + fu_ops->enable_src_buf(fu, DC_FETCHUNIT_FRAC0); + + dc_plane_dbg(plane, "uses %s\n", fu_ops->get_name(fu)); + + dc_lb_pec_dynamic_prim_sel(lb, dc_cf_get_link_id(dplane->cf)); + dc_lb_pec_dynamic_sec_sel(lb, fu_ops->get_link_id(fu)); + dc_lb_mode(lb, LB_BLEND); + dc_lb_position(lb, new_state->dst.x1, new_state->dst.y1); + dc_lb_pec_clken(lb, CLKEN_AUTOMATIC); + + dc_plane_dbg(plane, "uses LayerBlend%d\n", dc_lb_get_id(lb)); + + /* set ExtDst's source to LayerBlend */ + dc_ed_pec_src_sel(dplane->ed, dc_lb_get_link_id(lb)); + + drm_dev_exit(idx); +} + +static void dc_plane_atomic_disable(struct drm_plane *plane, + struct drm_atomic_state *state) +{ + struct dc_plane *dplane = to_dc_plane(plane); + const struct dc_fu_ops *fu_ops; + int idx; + + if (!drm_dev_enter(plane->dev, &idx)) + return; + + /* disable fetchunit in shadow */ + fu_ops = dc_fu_get_ops(dplane->fu); + fu_ops->disable_src_buf(dplane->fu, DC_FETCHUNIT_FRAC0); + + /* set ExtDst's source to ConstFrame */ + dc_ed_pec_src_sel(dplane->ed, dc_cf_get_link_id(dplane->cf)); + + drm_dev_exit(idx); +} + +static const struct drm_plane_helper_funcs dc_plane_helper_funcs = { + .atomic_check = dc_plane_atomic_check, + .atomic_update = dc_plane_atomic_update, + .atomic_disable = dc_plane_atomic_disable, +}; + +int dc_plane_init(struct dc_drm_device *dc_drm, struct dc_plane *dc_plane) +{ + struct drm_plane *plane = &dc_plane->base; + int ret; + + ret = drm_universal_plane_init(&dc_drm->base, plane, 0, &dc_plane_funcs, + dc_plane_formats, + ARRAY_SIZE(dc_plane_formats), + NULL, DRM_PLANE_TYPE_PRIMARY, NULL); + if (ret) + return ret; + + drm_plane_helper_add(plane, &dc_plane_helper_funcs); + + dc_plane->fu = dc_drm->pe->fu_disp[plane->index]; + dc_plane->cf = dc_drm->pe->cf_cont[plane->index]; + dc_plane->lb = dc_drm->pe->lb[plane->index]; + dc_plane->ed = dc_drm->pe->ed_cont[plane->index]; + + return 0; +} diff --git a/drivers/gpu/drm/imx/dc/dc-tc.c b/drivers/gpu/drm/imx/dc/dc-tc.c new file mode 100644 index 000000000000..0bfd381b2cea --- /dev/null +++ b/drivers/gpu/drm/imx/dc/dc-tc.c @@ -0,0 +1,141 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright 2024 NXP + */ + +#include <linux/component.h> +#include <linux/mod_devicetable.h> +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/regmap.h> + +#include "dc-drv.h" +#include "dc-de.h" + +#define TCON_CTRL 0x410 +#define CTRL_RST_VAL 0x01401408 + +/* red: MAPBIT 29-20, green: MAPBIT 19-10, blue: MAPBIT 9-0 */ +#define MAPBIT3_0 0x418 +#define MAPBIT7_4 0x41c +#define MAPBIT11_8 0x420 +#define MAPBIT15_12 0x424 +#define MAPBIT19_16 0x428 +#define MAPBIT23_20 0x42c +#define MAPBIT27_24 0x430 +#define MAPBIT31_28 0x434 + +static const struct dc_subdev_info dc_tc_info[] = { + { .reg_start = 0x5618c800, .id = 0, }, + { .reg_start = 0x5618e400, .id = 1, }, +}; + +static const struct regmap_range dc_tc_regmap_ranges[] = { + regmap_reg_range(TCON_CTRL, TCON_CTRL), + regmap_reg_range(MAPBIT3_0, MAPBIT31_28), +}; + +static const struct regmap_access_table dc_tc_regmap_access_table = { + .yes_ranges = dc_tc_regmap_ranges, + .n_yes_ranges = ARRAY_SIZE(dc_tc_regmap_ranges), +}; + +static const struct regmap_config dc_tc_regmap_config = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + .fast_io = true, + .wr_table = &dc_tc_regmap_access_table, + .rd_table = &dc_tc_regmap_access_table, + .max_register = MAPBIT31_28, +}; + +/* + * The pixels reach TCON are always in 30-bit BGR format. + * The first bridge always receives pixels in 30-bit RGB format. + * So, map the format to MEDIA_BUS_FMT_RGB101010_1X30. + */ +static const u32 dc_tc_mapbit[] = { + 0x17161514, 0x1b1a1918, 0x0b0a1d1c, 0x0f0e0d0c, + 0x13121110, 0x03020100, 0x07060504, 0x00000908, +}; + +void dc_tc_init(struct dc_tc *tc) +{ + /* reset TCON_CTRL to POR default so that TCON works in bypass mode */ + regmap_write(tc->reg, TCON_CTRL, CTRL_RST_VAL); + + /* set format */ + regmap_bulk_write(tc->reg, MAPBIT3_0, dc_tc_mapbit, + ARRAY_SIZE(dc_tc_mapbit)); +} + +static int dc_tc_bind(struct device *dev, struct device *master, void *data) +{ + struct platform_device *pdev = to_platform_device(dev); + struct dc_drm_device *dc_drm = data; + struct resource *res; + void __iomem *base; + struct dc_tc *tc; + int id; + + tc = devm_kzalloc(dev, sizeof(*tc), GFP_KERNEL); + if (!tc) + return -ENOMEM; + + base = devm_platform_get_and_ioremap_resource(pdev, 0, &res); + if (IS_ERR(base)) + return PTR_ERR(base); + + tc->reg = devm_regmap_init_mmio(dev, base, &dc_tc_regmap_config); + if (IS_ERR(tc->reg)) + return PTR_ERR(tc->reg); + + id = dc_subdev_get_id(dc_tc_info, ARRAY_SIZE(dc_tc_info), res); + if (id < 0) { + dev_err(dev, "failed to get instance number: %d\n", id); + return id; + } + + tc->dev = dev; + dc_drm->tc[id] = tc; + + return 0; +} + +static const struct component_ops dc_tc_ops = { + .bind = dc_tc_bind, +}; + +static int dc_tc_probe(struct platform_device *pdev) +{ + int ret; + + ret = component_add(&pdev->dev, &dc_tc_ops); + if (ret) + return dev_err_probe(&pdev->dev, ret, + "failed to add component\n"); + + return 0; +} + +static void dc_tc_remove(struct platform_device *pdev) +{ + component_del(&pdev->dev, &dc_tc_ops); +} + +static const struct of_device_id dc_tc_dt_ids[] = { + { .compatible = "fsl,imx8qxp-dc-tcon" }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, dc_tc_dt_ids); + +struct platform_driver dc_tc_driver = { + .probe = dc_tc_probe, + .remove = dc_tc_remove, + .driver = { + .name = "imx8-dc-tcon", + .suppress_bind_attrs = true, + .of_match_table = dc_tc_dt_ids, + }, +}; diff --git a/drivers/gpu/drm/lima/lima_gem.c b/drivers/gpu/drm/lima/lima_gem.c index 5deec673c11e..9722b847a539 100644 --- a/drivers/gpu/drm/lima/lima_gem.c +++ b/drivers/gpu/drm/lima/lima_gem.c @@ -341,7 +341,7 @@ int lima_gem_submit(struct drm_file *file, struct lima_submit *submit) err = lima_sched_task_init( submit->task, submit->ctx->context + submit->pipe, - bos, submit->nr_bos, vm); + bos, submit->nr_bos, vm, file->client_id); if (err) goto err_out1; diff --git a/drivers/gpu/drm/lima/lima_sched.c b/drivers/gpu/drm/lima/lima_sched.c index 7934098e651b..954f4325b859 100644 --- a/drivers/gpu/drm/lima/lima_sched.c +++ b/drivers/gpu/drm/lima/lima_sched.c @@ -113,7 +113,8 @@ static inline struct lima_sched_pipe *to_lima_pipe(struct drm_gpu_scheduler *sch int lima_sched_task_init(struct lima_sched_task *task, struct lima_sched_context *context, struct lima_bo **bos, int num_bos, - struct lima_vm *vm) + struct lima_vm *vm, + u64 drm_client_id) { int err, i; @@ -124,7 +125,8 @@ int lima_sched_task_init(struct lima_sched_task *task, for (i = 0; i < num_bos; i++) drm_gem_object_get(&bos[i]->base.base); - err = drm_sched_job_init(&task->base, &context->base, 1, vm); + err = drm_sched_job_init(&task->base, &context->base, 1, vm, + drm_client_id); if (err) { kfree(task->bos); return err; diff --git a/drivers/gpu/drm/lima/lima_sched.h b/drivers/gpu/drm/lima/lima_sched.h index 85b23ba901d5..1a08faf8a529 100644 --- a/drivers/gpu/drm/lima/lima_sched.h +++ b/drivers/gpu/drm/lima/lima_sched.h @@ -88,7 +88,8 @@ struct lima_sched_pipe { int lima_sched_task_init(struct lima_sched_task *task, struct lima_sched_context *context, struct lima_bo **bos, int num_bos, - struct lima_vm *vm); + struct lima_vm *vm, + u64 drm_client_id); void lima_sched_task_fini(struct lima_sched_task *task); int lima_sched_context_init(struct lima_sched_pipe *pipe, diff --git a/drivers/gpu/drm/lima/lima_trace.h b/drivers/gpu/drm/lima/lima_trace.h index 3a349d10304e..98a7fb2fa00e 100644 --- a/drivers/gpu/drm/lima/lima_trace.h +++ b/drivers/gpu/drm/lima/lima_trace.h @@ -14,21 +14,19 @@ DECLARE_EVENT_CLASS(lima_task, TP_PROTO(struct lima_sched_task *task), TP_ARGS(task), TP_STRUCT__entry( - __field(uint64_t, task_id) __field(unsigned int, context) __field(unsigned int, seqno) __string(pipe, task->base.sched->name) ), TP_fast_assign( - __entry->task_id = task->base.id; __entry->context = task->base.s_fence->finished.context; __entry->seqno = task->base.s_fence->finished.seqno; __assign_str(pipe); ), - TP_printk("task=%llu, context=%u seqno=%u pipe=%s", - __entry->task_id, __entry->context, __entry->seqno, + TP_printk("context=%u seqno=%u pipe=%s", + __entry->context, __entry->seqno, __get_str(pipe)) ); diff --git a/drivers/gpu/drm/mediatek/mtk_dp.c b/drivers/gpu/drm/mediatek/mtk_dp.c index 58279ddaab3c..a5b10b2545dc 100644 --- a/drivers/gpu/drm/mediatek/mtk_dp.c +++ b/drivers/gpu/drm/mediatek/mtk_dp.c @@ -2725,9 +2725,10 @@ static int mtk_dp_probe(struct platform_device *pdev) struct device *dev = &pdev->dev; int ret; - mtk_dp = devm_kzalloc(dev, sizeof(*mtk_dp), GFP_KERNEL); - if (!mtk_dp) - return -ENOMEM; + mtk_dp = devm_drm_bridge_alloc(dev, struct mtk_dp, bridge, + &mtk_dp_bridge_funcs); + if (IS_ERR(mtk_dp)) + return PTR_ERR(mtk_dp); mtk_dp->dev = dev; mtk_dp->data = (struct mtk_dp_data *)of_device_get_match_data(dev); @@ -2785,7 +2786,6 @@ static int mtk_dp_probe(struct platform_device *pdev) if (ret) return ret; - mtk_dp->bridge.funcs = &mtk_dp_bridge_funcs; mtk_dp->bridge.of_node = dev->of_node; mtk_dp->bridge.type = mtk_dp->data->bridge_type; diff --git a/drivers/gpu/drm/mediatek/mtk_dpi.c b/drivers/gpu/drm/mediatek/mtk_dpi.c index 6fb85bc6487a..8b58421bc39d 100644 --- a/drivers/gpu/drm/mediatek/mtk_dpi.c +++ b/drivers/gpu/drm/mediatek/mtk_dpi.c @@ -1266,9 +1266,10 @@ static int mtk_dpi_probe(struct platform_device *pdev) struct mtk_dpi *dpi; int ret; - dpi = devm_kzalloc(dev, sizeof(*dpi), GFP_KERNEL); - if (!dpi) - return -ENOMEM; + dpi = devm_drm_bridge_alloc(dev, struct mtk_dpi, bridge, + &mtk_dpi_bridge_funcs); + if (IS_ERR(dpi)) + return PTR_ERR(dpi); dpi->dev = dev; dpi->conf = (struct mtk_dpi_conf *)of_device_get_match_data(dev); @@ -1320,7 +1321,6 @@ static int mtk_dpi_probe(struct platform_device *pdev) platform_set_drvdata(pdev, dpi); - dpi->bridge.funcs = &mtk_dpi_bridge_funcs; dpi->bridge.of_node = dev->of_node; dpi->bridge.type = DRM_MODE_CONNECTOR_DPI; diff --git a/drivers/gpu/drm/mediatek/mtk_dsi.c b/drivers/gpu/drm/mediatek/mtk_dsi.c index 4fe1f38a3c4b..d7726091819c 100644 --- a/drivers/gpu/drm/mediatek/mtk_dsi.c +++ b/drivers/gpu/drm/mediatek/mtk_dsi.c @@ -1196,9 +1196,10 @@ static int mtk_dsi_probe(struct platform_device *pdev) int irq_num; int ret; - dsi = devm_kzalloc(dev, sizeof(*dsi), GFP_KERNEL); - if (!dsi) - return -ENOMEM; + dsi = devm_drm_bridge_alloc(dev, struct mtk_dsi, bridge, + &mtk_dsi_bridge_funcs); + if (IS_ERR(dsi)) + return PTR_ERR(dsi); dsi->driver_data = of_device_get_match_data(dev); @@ -1246,7 +1247,6 @@ static int mtk_dsi_probe(struct platform_device *pdev) platform_set_drvdata(pdev, dsi); - dsi->bridge.funcs = &mtk_dsi_bridge_funcs; dsi->bridge.of_node = dev->of_node; dsi->bridge.type = DRM_MODE_CONNECTOR_DSI; diff --git a/drivers/gpu/drm/mediatek/mtk_hdmi.c b/drivers/gpu/drm/mediatek/mtk_hdmi.c index 8803cd4a8bc9..6943cdc77dec 100644 --- a/drivers/gpu/drm/mediatek/mtk_hdmi.c +++ b/drivers/gpu/drm/mediatek/mtk_hdmi.c @@ -1642,9 +1642,10 @@ static int mtk_hdmi_probe(struct platform_device *pdev) struct device *dev = &pdev->dev; int ret; - hdmi = devm_kzalloc(dev, sizeof(*hdmi), GFP_KERNEL); - if (!hdmi) - return -ENOMEM; + hdmi = devm_drm_bridge_alloc(dev, struct mtk_hdmi, bridge, + &mtk_hdmi_bridge_funcs); + if (IS_ERR(hdmi)) + return PTR_ERR(hdmi); hdmi->dev = dev; hdmi->conf = of_device_get_match_data(dev); @@ -1666,7 +1667,6 @@ static int mtk_hdmi_probe(struct platform_device *pdev) return dev_err_probe(dev, ret, "Failed to register audio driver\n"); - hdmi->bridge.funcs = &mtk_hdmi_bridge_funcs; hdmi->bridge.of_node = pdev->dev.of_node; hdmi->bridge.ops = DRM_BRIDGE_OP_DETECT | DRM_BRIDGE_OP_EDID | DRM_BRIDGE_OP_HPD; diff --git a/drivers/gpu/drm/meson/meson_encoder_cvbs.c b/drivers/gpu/drm/meson/meson_encoder_cvbs.c index c9678dc68fa1..dc374bfc5951 100644 --- a/drivers/gpu/drm/meson/meson_encoder_cvbs.c +++ b/drivers/gpu/drm/meson/meson_encoder_cvbs.c @@ -227,9 +227,12 @@ int meson_encoder_cvbs_probe(struct meson_drm *priv) struct device_node *remote; int ret; - meson_encoder_cvbs = devm_kzalloc(priv->dev, sizeof(*meson_encoder_cvbs), GFP_KERNEL); - if (!meson_encoder_cvbs) - return -ENOMEM; + meson_encoder_cvbs = devm_drm_bridge_alloc(priv->dev, + struct meson_encoder_cvbs, + bridge, + &meson_encoder_cvbs_bridge_funcs); + if (IS_ERR(meson_encoder_cvbs)) + return PTR_ERR(meson_encoder_cvbs); /* CVBS Connector Bridge */ remote = of_graph_get_remote_node(priv->dev->of_node, 0, 0); @@ -245,7 +248,6 @@ int meson_encoder_cvbs_probe(struct meson_drm *priv) "Failed to find CVBS Connector bridge\n"); /* CVBS Encoder Bridge */ - meson_encoder_cvbs->bridge.funcs = &meson_encoder_cvbs_bridge_funcs; meson_encoder_cvbs->bridge.of_node = priv->dev->of_node; meson_encoder_cvbs->bridge.type = DRM_MODE_CONNECTOR_Composite; meson_encoder_cvbs->bridge.ops = DRM_BRIDGE_OP_MODES; diff --git a/drivers/gpu/drm/meson/meson_encoder_dsi.c b/drivers/gpu/drm/meson/meson_encoder_dsi.c index 3db518e5f95d..6c6624f9ba24 100644 --- a/drivers/gpu/drm/meson/meson_encoder_dsi.c +++ b/drivers/gpu/drm/meson/meson_encoder_dsi.c @@ -106,9 +106,12 @@ int meson_encoder_dsi_probe(struct meson_drm *priv) struct device_node *remote; int ret; - meson_encoder_dsi = devm_kzalloc(priv->dev, sizeof(*meson_encoder_dsi), GFP_KERNEL); - if (!meson_encoder_dsi) - return -ENOMEM; + meson_encoder_dsi = devm_drm_bridge_alloc(priv->dev, + struct meson_encoder_dsi, + bridge, + &meson_encoder_dsi_bridge_funcs); + if (IS_ERR(meson_encoder_dsi)) + return PTR_ERR(meson_encoder_dsi); /* DSI Transceiver Bridge */ remote = of_graph_get_remote_node(priv->dev->of_node, 2, 0); @@ -123,7 +126,6 @@ int meson_encoder_dsi_probe(struct meson_drm *priv) "Failed to find DSI transceiver bridge\n"); /* DSI Encoder Bridge */ - meson_encoder_dsi->bridge.funcs = &meson_encoder_dsi_bridge_funcs; meson_encoder_dsi->bridge.of_node = priv->dev->of_node; meson_encoder_dsi->bridge.type = DRM_MODE_CONNECTOR_DSI; diff --git a/drivers/gpu/drm/meson/meson_encoder_hdmi.c b/drivers/gpu/drm/meson/meson_encoder_hdmi.c index ab08d690d882..8205ee56a691 100644 --- a/drivers/gpu/drm/meson/meson_encoder_hdmi.c +++ b/drivers/gpu/drm/meson/meson_encoder_hdmi.c @@ -376,9 +376,12 @@ int meson_encoder_hdmi_probe(struct meson_drm *priv) struct device_node *remote; int ret; - meson_encoder_hdmi = devm_kzalloc(priv->dev, sizeof(*meson_encoder_hdmi), GFP_KERNEL); - if (!meson_encoder_hdmi) - return -ENOMEM; + meson_encoder_hdmi = devm_drm_bridge_alloc(priv->dev, + struct meson_encoder_hdmi, + bridge, + &meson_encoder_hdmi_bridge_funcs); + if (IS_ERR(meson_encoder_hdmi)) + return PTR_ERR(meson_encoder_hdmi); /* HDMI Transceiver Bridge */ remote = of_graph_get_remote_node(priv->dev->of_node, 1, 0); @@ -395,7 +398,6 @@ int meson_encoder_hdmi_probe(struct meson_drm *priv) } /* HDMI Encoder Bridge */ - meson_encoder_hdmi->bridge.funcs = &meson_encoder_hdmi_bridge_funcs; meson_encoder_hdmi->bridge.of_node = priv->dev->of_node; meson_encoder_hdmi->bridge.type = DRM_MODE_CONNECTOR_HDMIA; meson_encoder_hdmi->bridge.interlace_allowed = true; diff --git a/drivers/gpu/drm/mgag200/mgag200_drv.h b/drivers/gpu/drm/mgag200/mgag200_drv.h index 819a7e9381e3..f4bf40cd7c88 100644 --- a/drivers/gpu/drm/mgag200/mgag200_drv.h +++ b/drivers/gpu/drm/mgag200/mgag200_drv.h @@ -382,10 +382,10 @@ int mgag200_primary_plane_helper_get_scanout_buffer(struct drm_plane *plane, .destroy = drm_plane_cleanup, \ DRM_GEM_SHADOW_PLANE_FUNCS -void mgag200_crtc_set_gamma_linear(struct mga_device *mdev, const struct drm_format_info *format); -void mgag200_crtc_set_gamma(struct mga_device *mdev, - const struct drm_format_info *format, - struct drm_color_lut *lut); +void mgag200_crtc_fill_gamma(struct mga_device *mdev, const struct drm_format_info *format); +void mgag200_crtc_load_gamma(struct mga_device *mdev, + const struct drm_format_info *format, + struct drm_color_lut *lut); enum drm_mode_status mgag200_crtc_helper_mode_valid(struct drm_crtc *crtc, const struct drm_display_mode *mode); diff --git a/drivers/gpu/drm/mgag200/mgag200_g200er.c b/drivers/gpu/drm/mgag200/mgag200_g200er.c index c20ed0ab50ec..23debc70dc54 100644 --- a/drivers/gpu/drm/mgag200/mgag200_g200er.c +++ b/drivers/gpu/drm/mgag200/mgag200_g200er.c @@ -200,9 +200,9 @@ static void mgag200_g200er_crtc_helper_atomic_enable(struct drm_crtc *crtc, mgag200_g200er_reset_tagfifo(mdev); if (crtc_state->gamma_lut) - mgag200_crtc_set_gamma(mdev, format, crtc_state->gamma_lut->data); + mgag200_crtc_load_gamma(mdev, format, crtc_state->gamma_lut->data); else - mgag200_crtc_set_gamma_linear(mdev, format); + mgag200_crtc_fill_gamma(mdev, format); mgag200_enable_display(mdev); } diff --git a/drivers/gpu/drm/mgag200/mgag200_g200ev.c b/drivers/gpu/drm/mgag200/mgag200_g200ev.c index 78be964eb97c..f8796e2b7a0f 100644 --- a/drivers/gpu/drm/mgag200/mgag200_g200ev.c +++ b/drivers/gpu/drm/mgag200/mgag200_g200ev.c @@ -201,9 +201,9 @@ static void mgag200_g200ev_crtc_helper_atomic_enable(struct drm_crtc *crtc, mgag200_g200ev_set_hiprilvl(mdev); if (crtc_state->gamma_lut) - mgag200_crtc_set_gamma(mdev, format, crtc_state->gamma_lut->data); + mgag200_crtc_load_gamma(mdev, format, crtc_state->gamma_lut->data); else - mgag200_crtc_set_gamma_linear(mdev, format); + mgag200_crtc_fill_gamma(mdev, format); mgag200_enable_display(mdev); } diff --git a/drivers/gpu/drm/mgag200/mgag200_g200se.c b/drivers/gpu/drm/mgag200/mgag200_g200se.c index 7a32d3b1d226..e80da12ba1fe 100644 --- a/drivers/gpu/drm/mgag200/mgag200_g200se.c +++ b/drivers/gpu/drm/mgag200/mgag200_g200se.c @@ -332,9 +332,9 @@ static void mgag200_g200se_crtc_helper_atomic_enable(struct drm_crtc *crtc, mgag200_g200se_set_hiprilvl(mdev, adjusted_mode, format); if (crtc_state->gamma_lut) - mgag200_crtc_set_gamma(mdev, format, crtc_state->gamma_lut->data); + mgag200_crtc_load_gamma(mdev, format, crtc_state->gamma_lut->data); else - mgag200_crtc_set_gamma_linear(mdev, format); + mgag200_crtc_fill_gamma(mdev, format); mgag200_enable_display(mdev); } diff --git a/drivers/gpu/drm/mgag200/mgag200_mode.c b/drivers/gpu/drm/mgag200/mgag200_mode.c index 6067d08aeee3..951d715dea30 100644 --- a/drivers/gpu/drm/mgag200/mgag200_mode.c +++ b/drivers/gpu/drm/mgag200/mgag200_mode.c @@ -13,6 +13,7 @@ #include <drm/drm_atomic.h> #include <drm/drm_atomic_helper.h> +#include <drm/drm_color_mgmt.h> #include <drm/drm_damage_helper.h> #include <drm/drm_edid.h> #include <drm/drm_format_helper.h> @@ -30,35 +31,37 @@ * This file contains setup code for the CRTC. */ -void mgag200_crtc_set_gamma_linear(struct mga_device *mdev, - const struct drm_format_info *format) +static void mgag200_set_gamma_lut(struct drm_crtc *crtc, unsigned int index, + u16 red, u16 green, u16 blue) { - int i; + struct drm_device *dev = crtc->dev; + struct mga_device *mdev = to_mga_device(dev); + u8 i8 = index & 0xff; + u8 r8 = red >> 8; + u8 g8 = green >> 8; + u8 b8 = blue >> 8; + + if (drm_WARN_ON_ONCE(dev, index != i8)) + return; /* driver bug */ + + WREG8(DAC_INDEX + MGA1064_INDEX, i8); + WREG8(DAC_INDEX + MGA1064_COL_PAL, r8); + WREG8(DAC_INDEX + MGA1064_COL_PAL, g8); + WREG8(DAC_INDEX + MGA1064_COL_PAL, b8); +} - WREG8(DAC_INDEX + MGA1064_INDEX, 0); +void mgag200_crtc_fill_gamma(struct mga_device *mdev, + const struct drm_format_info *format) +{ + struct drm_crtc *crtc = &mdev->crtc; switch (format->format) { case DRM_FORMAT_RGB565: - /* Use better interpolation, to take 32 values from 0 to 255 */ - for (i = 0; i < MGAG200_LUT_SIZE / 8; i++) { - WREG8(DAC_INDEX + MGA1064_COL_PAL, i * 8 + i / 4); - WREG8(DAC_INDEX + MGA1064_COL_PAL, i * 4 + i / 16); - WREG8(DAC_INDEX + MGA1064_COL_PAL, i * 8 + i / 4); - } - /* Green has one more bit, so add padding with 0 for red and blue. */ - for (i = MGAG200_LUT_SIZE / 8; i < MGAG200_LUT_SIZE / 4; i++) { - WREG8(DAC_INDEX + MGA1064_COL_PAL, 0); - WREG8(DAC_INDEX + MGA1064_COL_PAL, i * 4 + i / 16); - WREG8(DAC_INDEX + MGA1064_COL_PAL, 0); - } + drm_crtc_fill_gamma_565(crtc, mgag200_set_gamma_lut); break; case DRM_FORMAT_RGB888: case DRM_FORMAT_XRGB8888: - for (i = 0; i < MGAG200_LUT_SIZE; i++) { - WREG8(DAC_INDEX + MGA1064_COL_PAL, i); - WREG8(DAC_INDEX + MGA1064_COL_PAL, i); - WREG8(DAC_INDEX + MGA1064_COL_PAL, i); - } + drm_crtc_fill_gamma_888(crtc, mgag200_set_gamma_lut); break; default: drm_warn_once(&mdev->base, "Unsupported format %p4cc for gamma correction\n", @@ -67,36 +70,19 @@ void mgag200_crtc_set_gamma_linear(struct mga_device *mdev, } } -void mgag200_crtc_set_gamma(struct mga_device *mdev, - const struct drm_format_info *format, - struct drm_color_lut *lut) +void mgag200_crtc_load_gamma(struct mga_device *mdev, + const struct drm_format_info *format, + struct drm_color_lut *lut) { - int i; - - WREG8(DAC_INDEX + MGA1064_INDEX, 0); + struct drm_crtc *crtc = &mdev->crtc; switch (format->format) { case DRM_FORMAT_RGB565: - /* Use better interpolation, to take 32 values from lut[0] to lut[255] */ - for (i = 0; i < MGAG200_LUT_SIZE / 8; i++) { - WREG8(DAC_INDEX + MGA1064_COL_PAL, lut[i * 8 + i / 4].red >> 8); - WREG8(DAC_INDEX + MGA1064_COL_PAL, lut[i * 4 + i / 16].green >> 8); - WREG8(DAC_INDEX + MGA1064_COL_PAL, lut[i * 8 + i / 4].blue >> 8); - } - /* Green has one more bit, so add padding with 0 for red and blue. */ - for (i = MGAG200_LUT_SIZE / 8; i < MGAG200_LUT_SIZE / 4; i++) { - WREG8(DAC_INDEX + MGA1064_COL_PAL, 0); - WREG8(DAC_INDEX + MGA1064_COL_PAL, lut[i * 4 + i / 16].green >> 8); - WREG8(DAC_INDEX + MGA1064_COL_PAL, 0); - } + drm_crtc_load_gamma_565_from_888(crtc, lut, mgag200_set_gamma_lut); break; case DRM_FORMAT_RGB888: case DRM_FORMAT_XRGB8888: - for (i = 0; i < MGAG200_LUT_SIZE; i++) { - WREG8(DAC_INDEX + MGA1064_COL_PAL, lut[i].red >> 8); - WREG8(DAC_INDEX + MGA1064_COL_PAL, lut[i].green >> 8); - WREG8(DAC_INDEX + MGA1064_COL_PAL, lut[i].blue >> 8); - } + drm_crtc_load_gamma_888(crtc, lut, mgag200_set_gamma_lut); break; default: drm_warn_once(&mdev->base, "Unsupported format %p4cc for gamma correction\n", @@ -642,9 +628,9 @@ void mgag200_crtc_helper_atomic_flush(struct drm_crtc *crtc, struct drm_atomic_s const struct drm_format_info *format = mgag200_crtc_state->format; if (crtc_state->gamma_lut) - mgag200_crtc_set_gamma(mdev, format, crtc_state->gamma_lut->data); + mgag200_crtc_load_gamma(mdev, format, crtc_state->gamma_lut->data); else - mgag200_crtc_set_gamma_linear(mdev, format); + mgag200_crtc_fill_gamma(mdev, format); } } @@ -665,9 +651,9 @@ void mgag200_crtc_helper_atomic_enable(struct drm_crtc *crtc, struct drm_atomic_ funcs->pixpllc_atomic_update(crtc, old_state); if (crtc_state->gamma_lut) - mgag200_crtc_set_gamma(mdev, format, crtc_state->gamma_lut->data); + mgag200_crtc_load_gamma(mdev, format, crtc_state->gamma_lut->data); else - mgag200_crtc_set_gamma_linear(mdev, format); + mgag200_crtc_fill_gamma(mdev, format); mgag200_enable_display(mdev); } diff --git a/drivers/gpu/drm/msm/msm_gem_submit.c b/drivers/gpu/drm/msm/msm_gem_submit.c index 3e9aa2cc38ef..d9be0fe3d674 100644 --- a/drivers/gpu/drm/msm/msm_gem_submit.c +++ b/drivers/gpu/drm/msm/msm_gem_submit.c @@ -30,7 +30,7 @@ static struct msm_gem_submit *submit_create(struct drm_device *dev, struct msm_gpu *gpu, struct msm_gpu_submitqueue *queue, uint32_t nr_bos, - uint32_t nr_cmds) + uint32_t nr_cmds, u64 drm_client_id) { static atomic_t ident = ATOMIC_INIT(0); struct msm_gem_submit *submit; @@ -54,7 +54,8 @@ static struct msm_gem_submit *submit_create(struct drm_device *dev, return ERR_PTR(ret); } - ret = drm_sched_job_init(&submit->base, queue->entity, 1, queue); + ret = drm_sched_job_init(&submit->base, queue->entity, 1, queue, + drm_client_id); if (ret) { kfree(submit->hw_fence); kfree(submit); @@ -693,7 +694,8 @@ int msm_ioctl_gem_submit(struct drm_device *dev, void *data, } } - submit = submit_create(dev, gpu, queue, args->nr_bos, args->nr_cmds); + submit = submit_create(dev, gpu, queue, args->nr_bos, args->nr_cmds, + file->client_id); if (IS_ERR(submit)) { ret = PTR_ERR(submit); goto out_post_unlock; diff --git a/drivers/gpu/drm/nouveau/nouveau_fence.c b/drivers/gpu/drm/nouveau/nouveau_fence.c index 6ded8c2b6d3b..d5654e26d5bc 100644 --- a/drivers/gpu/drm/nouveau/nouveau_fence.c +++ b/drivers/gpu/drm/nouveau/nouveau_fence.c @@ -50,10 +50,10 @@ nouveau_fctx(struct nouveau_fence *fence) return container_of(fence->base.lock, struct nouveau_fence_chan, lock); } -static int +static bool nouveau_fence_signal(struct nouveau_fence *fence) { - int drop = 0; + bool drop = false; dma_fence_signal_locked(&fence->base); list_del(&fence->head); @@ -63,7 +63,7 @@ nouveau_fence_signal(struct nouveau_fence *fence) struct nouveau_fence_chan *fctx = nouveau_fctx(fence); if (!--fctx->notify_ref) - drop = 1; + drop = true; } dma_fence_put(&fence->base); @@ -83,13 +83,11 @@ nouveau_local_fence(struct dma_fence *fence, struct nouveau_drm *drm) void nouveau_fence_context_kill(struct nouveau_fence_chan *fctx, int error) { - struct nouveau_fence *fence; + struct nouveau_fence *fence, *tmp; unsigned long flags; spin_lock_irqsave(&fctx->lock, flags); - while (!list_empty(&fctx->pending)) { - fence = list_entry(fctx->pending.next, typeof(*fence), head); - + list_for_each_entry_safe(fence, tmp, &fctx->pending, head) { if (error && !dma_fence_is_signaled_locked(&fence->base)) dma_fence_set_error(&fence->base, error); @@ -127,23 +125,23 @@ nouveau_fence_context_free(struct nouveau_fence_chan *fctx) kref_put(&fctx->fence_ref, nouveau_fence_context_put); } -static int +static void nouveau_fence_update(struct nouveau_channel *chan, struct nouveau_fence_chan *fctx) { - struct nouveau_fence *fence; - int drop = 0; + struct nouveau_fence *fence, *tmp; + bool drop = false; u32 seq = fctx->read(chan); - while (!list_empty(&fctx->pending)) { - fence = list_entry(fctx->pending.next, typeof(*fence), head); - + list_for_each_entry_safe(fence, tmp, &fctx->pending, head) { if ((int)(seq - fence->base.seqno) < 0) break; - drop |= nouveau_fence_signal(fence); + if (nouveau_fence_signal(fence)) + drop = true; } - return drop; + if (drop) + nvif_event_block(&fctx->event); } static void @@ -151,22 +149,16 @@ nouveau_fence_uevent_work(struct work_struct *work) { struct nouveau_fence_chan *fctx = container_of(work, struct nouveau_fence_chan, uevent_work); + struct nouveau_channel *chan; + struct nouveau_fence *fence; unsigned long flags; - int drop = 0; spin_lock_irqsave(&fctx->lock, flags); - if (!list_empty(&fctx->pending)) { - struct nouveau_fence *fence; - struct nouveau_channel *chan; - - fence = list_entry(fctx->pending.next, typeof(*fence), head); + fence = list_first_entry_or_null(&fctx->pending, typeof(*fence), head); + if (fence) { chan = rcu_dereference_protected(fence->channel, lockdep_is_held(&fctx->lock)); - if (nouveau_fence_update(chan, fctx)) - drop = 1; + nouveau_fence_update(chan, fctx); } - if (drop) - nvif_event_block(&fctx->event); - spin_unlock_irqrestore(&fctx->lock, flags); } @@ -246,9 +238,7 @@ nouveau_fence_emit(struct nouveau_fence *fence) return -ENODEV; } - if (nouveau_fence_update(chan, fctx)) - nvif_event_block(&fctx->event); - + nouveau_fence_update(chan, fctx); list_add_tail(&fence->head, &fctx->pending); spin_unlock_irq(&fctx->lock); } @@ -259,21 +249,19 @@ nouveau_fence_emit(struct nouveau_fence *fence) bool nouveau_fence_done(struct nouveau_fence *fence) { - if (fence->base.ops == &nouveau_fence_ops_legacy || - fence->base.ops == &nouveau_fence_ops_uevent) { - struct nouveau_fence_chan *fctx = nouveau_fctx(fence); - struct nouveau_channel *chan; - unsigned long flags; + struct nouveau_fence_chan *fctx = nouveau_fctx(fence); + struct nouveau_channel *chan; + unsigned long flags; - if (test_bit(DMA_FENCE_FLAG_SIGNALED_BIT, &fence->base.flags)) - return true; + if (dma_fence_is_signaled(&fence->base)) + return true; + + spin_lock_irqsave(&fctx->lock, flags); + chan = rcu_dereference_protected(fence->channel, lockdep_is_held(&fctx->lock)); + if (chan) + nouveau_fence_update(chan, fctx); + spin_unlock_irqrestore(&fctx->lock, flags); - spin_lock_irqsave(&fctx->lock, flags); - chan = rcu_dereference_protected(fence->channel, lockdep_is_held(&fctx->lock)); - if (chan && nouveau_fence_update(chan, fctx)) - nvif_event_block(&fctx->event); - spin_unlock_irqrestore(&fctx->lock, flags); - } return dma_fence_is_signaled(&fence->base); } diff --git a/drivers/gpu/drm/nouveau/nouveau_sched.c b/drivers/gpu/drm/nouveau/nouveau_sched.c index d326e55d2d24..460a5fb02412 100644 --- a/drivers/gpu/drm/nouveau/nouveau_sched.c +++ b/drivers/gpu/drm/nouveau/nouveau_sched.c @@ -87,7 +87,8 @@ nouveau_job_init(struct nouveau_job *job, } ret = drm_sched_job_init(&job->base, &sched->entity, - args->credits, NULL); + args->credits, NULL, + job->file_priv->client_id); if (ret) goto err_free_chains; diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/fifo/chan.c b/drivers/gpu/drm/nouveau/nvkm/engine/fifo/chan.c index 4e09985424b6..e5bbd8563007 100644 --- a/drivers/gpu/drm/nouveau/nvkm/engine/fifo/chan.c +++ b/drivers/gpu/drm/nouveau/nvkm/engine/fifo/chan.c @@ -104,7 +104,7 @@ nvkm_chan_cctx_get(struct nvkm_chan *chan, struct nvkm_engn *engn, struct nvkm_c if (cctx) { refcount_inc(&cctx->refs); *pcctx = cctx; - mutex_unlock(&chan->cgrp->mutex); + mutex_unlock(&cgrp->mutex); return 0; } diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/gsp/rm/r535/disp.c b/drivers/gpu/drm/nouveau/nvkm/subdev/gsp/rm/r535/disp.c index 7e9e2d3564da..6e63df816d85 100644 --- a/drivers/gpu/drm/nouveau/nvkm/subdev/gsp/rm/r535/disp.c +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/gsp/rm/r535/disp.c @@ -648,7 +648,7 @@ r535_conn_new(struct nvkm_disp *disp, u32 id) ctrl = nvkm_gsp_rm_ctrl_get(&disp->rm.objcom, NV0073_CTRL_CMD_SPECIFIC_GET_CONNECTOR_DATA, sizeof(*ctrl)); if (IS_ERR(ctrl)) - return (void *)ctrl; + return ERR_CAST(ctrl); ctrl->subDeviceInstance = 0; ctrl->displayId = BIT(id); diff --git a/drivers/gpu/drm/omapdrm/dss/dpi.c b/drivers/gpu/drm/omapdrm/dss/dpi.c index 6eff97a09160..9f86db774c39 100644 --- a/drivers/gpu/drm/omapdrm/dss/dpi.c +++ b/drivers/gpu/drm/omapdrm/dss/dpi.c @@ -562,7 +562,6 @@ static const struct drm_bridge_funcs dpi_bridge_funcs = { static void dpi_bridge_init(struct dpi_data *dpi) { - dpi->bridge.funcs = &dpi_bridge_funcs; dpi->bridge.of_node = dpi->pdev->dev.of_node; dpi->bridge.type = DRM_MODE_CONNECTOR_DPI; @@ -707,9 +706,9 @@ int dpi_init_port(struct dss_device *dss, struct platform_device *pdev, u32 datalines; int r; - dpi = devm_kzalloc(&pdev->dev, sizeof(*dpi), GFP_KERNEL); - if (!dpi) - return -ENOMEM; + dpi = devm_drm_bridge_alloc(&pdev->dev, struct dpi_data, bridge, &dpi_bridge_funcs); + if (IS_ERR(dpi)) + return PTR_ERR(dpi); ep = of_graph_get_next_port_endpoint(port, NULL); if (!ep) diff --git a/drivers/gpu/drm/omapdrm/dss/dsi.c b/drivers/gpu/drm/omapdrm/dss/dsi.c index 91ee63bfe0bc..b129e5a8d791 100644 --- a/drivers/gpu/drm/omapdrm/dss/dsi.c +++ b/drivers/gpu/drm/omapdrm/dss/dsi.c @@ -4701,7 +4701,6 @@ static const struct drm_bridge_funcs dsi_bridge_funcs = { static void dsi_bridge_init(struct dsi_data *dsi) { - dsi->bridge.funcs = &dsi_bridge_funcs; dsi->bridge.of_node = dsi->host.dev->of_node; dsi->bridge.type = DRM_MODE_CONNECTOR_DSI; @@ -4894,9 +4893,9 @@ static int dsi_probe(struct platform_device *pdev) unsigned int i; int r; - dsi = devm_kzalloc(dev, sizeof(*dsi), GFP_KERNEL); - if (!dsi) - return -ENOMEM; + dsi = devm_drm_bridge_alloc(dev, struct dsi_data, bridge, &dsi_bridge_funcs); + if (IS_ERR(dsi)) + return PTR_ERR(dsi); dsi->dev = dev; dev_set_drvdata(dev, dsi); diff --git a/drivers/gpu/drm/omapdrm/dss/hdmi4.c b/drivers/gpu/drm/omapdrm/dss/hdmi4.c index a3b22952fdc3..3cd612af2449 100644 --- a/drivers/gpu/drm/omapdrm/dss/hdmi4.c +++ b/drivers/gpu/drm/omapdrm/dss/hdmi4.c @@ -505,7 +505,6 @@ static const struct drm_bridge_funcs hdmi4_bridge_funcs = { static void hdmi4_bridge_init(struct omap_hdmi *hdmi) { - hdmi->bridge.funcs = &hdmi4_bridge_funcs; hdmi->bridge.of_node = hdmi->pdev->dev.of_node; hdmi->bridge.ops = DRM_BRIDGE_OP_EDID; hdmi->bridge.type = DRM_MODE_CONNECTOR_HDMIA; @@ -761,9 +760,9 @@ static int hdmi4_probe(struct platform_device *pdev) int irq; int r; - hdmi = kzalloc(sizeof(*hdmi), GFP_KERNEL); - if (!hdmi) - return -ENOMEM; + hdmi = devm_drm_bridge_alloc(&pdev->dev, struct omap_hdmi, bridge, &hdmi4_bridge_funcs); + if (IS_ERR(hdmi)) + return PTR_ERR(hdmi); hdmi->pdev = pdev; @@ -774,25 +773,24 @@ static int hdmi4_probe(struct platform_device *pdev) r = hdmi4_probe_of(hdmi); if (r) - goto err_free; + return r; r = hdmi_wp_init(pdev, &hdmi->wp, 4); if (r) - goto err_free; + return r; r = hdmi_phy_init(pdev, &hdmi->phy, 4); if (r) - goto err_free; + return r; r = hdmi4_core_init(pdev, &hdmi->core); if (r) - goto err_free; + return r; irq = platform_get_irq(pdev, 0); if (irq < 0) { DSSERR("platform_get_irq failed\n"); - r = -ENODEV; - goto err_free; + return -ENODEV; } r = devm_request_threaded_irq(&pdev->dev, irq, @@ -800,7 +798,7 @@ static int hdmi4_probe(struct platform_device *pdev) IRQF_ONESHOT, "OMAP HDMI", hdmi); if (r) { DSSERR("HDMI IRQ request failed\n"); - goto err_free; + return r; } hdmi->vdda_reg = devm_regulator_get(&pdev->dev, "vdda"); @@ -808,7 +806,7 @@ static int hdmi4_probe(struct platform_device *pdev) r = PTR_ERR(hdmi->vdda_reg); if (r != -EPROBE_DEFER) DSSERR("can't get VDDA regulator\n"); - goto err_free; + return r; } pm_runtime_enable(&pdev->dev); @@ -827,8 +825,6 @@ err_uninit_output: hdmi4_uninit_output(hdmi); err_pm_disable: pm_runtime_disable(&pdev->dev); -err_free: - kfree(hdmi); return r; } @@ -841,8 +837,6 @@ static void hdmi4_remove(struct platform_device *pdev) hdmi4_uninit_output(hdmi); pm_runtime_disable(&pdev->dev); - - kfree(hdmi); } static const struct of_device_id hdmi_of_match[] = { diff --git a/drivers/gpu/drm/omapdrm/dss/hdmi5.c b/drivers/gpu/drm/omapdrm/dss/hdmi5.c index 0c98444d39a9..5636b3dfec1c 100644 --- a/drivers/gpu/drm/omapdrm/dss/hdmi5.c +++ b/drivers/gpu/drm/omapdrm/dss/hdmi5.c @@ -480,7 +480,6 @@ static const struct drm_bridge_funcs hdmi5_bridge_funcs = { static void hdmi5_bridge_init(struct omap_hdmi *hdmi) { - hdmi->bridge.funcs = &hdmi5_bridge_funcs; hdmi->bridge.of_node = hdmi->pdev->dev.of_node; hdmi->bridge.ops = DRM_BRIDGE_OP_EDID; hdmi->bridge.type = DRM_MODE_CONNECTOR_HDMIA; @@ -727,9 +726,9 @@ static int hdmi5_probe(struct platform_device *pdev) int irq; int r; - hdmi = kzalloc(sizeof(*hdmi), GFP_KERNEL); - if (!hdmi) - return -ENOMEM; + hdmi = devm_drm_bridge_alloc(&pdev->dev, struct omap_hdmi, bridge, &hdmi5_bridge_funcs); + if (IS_ERR(hdmi)) + return PTR_ERR(hdmi); hdmi->pdev = pdev; @@ -740,25 +739,24 @@ static int hdmi5_probe(struct platform_device *pdev) r = hdmi5_probe_of(hdmi); if (r) - goto err_free; + return r; r = hdmi_wp_init(pdev, &hdmi->wp, 5); if (r) - goto err_free; + return r; r = hdmi_phy_init(pdev, &hdmi->phy, 5); if (r) - goto err_free; + return r; r = hdmi5_core_init(pdev, &hdmi->core); if (r) - goto err_free; + return r; irq = platform_get_irq(pdev, 0); if (irq < 0) { DSSERR("platform_get_irq failed\n"); - r = -ENODEV; - goto err_free; + return -ENODEV; } r = devm_request_threaded_irq(&pdev->dev, irq, @@ -766,7 +764,7 @@ static int hdmi5_probe(struct platform_device *pdev) IRQF_ONESHOT, "OMAP HDMI", hdmi); if (r) { DSSERR("HDMI IRQ request failed\n"); - goto err_free; + return r; } hdmi->vdda_reg = devm_regulator_get(&pdev->dev, "vdda"); @@ -774,7 +772,7 @@ static int hdmi5_probe(struct platform_device *pdev) r = PTR_ERR(hdmi->vdda_reg); if (r != -EPROBE_DEFER) DSSERR("can't get VDDA regulator\n"); - goto err_free; + return r; } pm_runtime_enable(&pdev->dev); @@ -793,8 +791,6 @@ err_uninit_output: hdmi5_uninit_output(hdmi); err_pm_disable: pm_runtime_disable(&pdev->dev); -err_free: - kfree(hdmi); return r; } @@ -807,8 +803,6 @@ static void hdmi5_remove(struct platform_device *pdev) hdmi5_uninit_output(hdmi); pm_runtime_disable(&pdev->dev); - - kfree(hdmi); } static const struct of_device_id hdmi_of_match[] = { diff --git a/drivers/gpu/drm/omapdrm/dss/sdi.c b/drivers/gpu/drm/omapdrm/dss/sdi.c index e78826e4b560..df4cbc683e2c 100644 --- a/drivers/gpu/drm/omapdrm/dss/sdi.c +++ b/drivers/gpu/drm/omapdrm/dss/sdi.c @@ -284,7 +284,6 @@ static const struct drm_bridge_funcs sdi_bridge_funcs = { static void sdi_bridge_init(struct sdi_device *sdi) { - sdi->bridge.funcs = &sdi_bridge_funcs; sdi->bridge.of_node = sdi->pdev->dev.of_node; sdi->bridge.type = DRM_MODE_CONNECTOR_LVDS; @@ -344,21 +343,19 @@ int sdi_init_port(struct dss_device *dss, struct platform_device *pdev, u32 datapairs; int r; - sdi = kzalloc(sizeof(*sdi), GFP_KERNEL); - if (!sdi) - return -ENOMEM; + sdi = devm_drm_bridge_alloc(&pdev->dev, struct sdi_device, bridge, &sdi_bridge_funcs); + if (IS_ERR(sdi)) + return PTR_ERR(sdi); ep = of_graph_get_next_port_endpoint(port, NULL); - if (!ep) { - r = 0; - goto err_free; - } + if (!ep) + return 0; r = of_property_read_u32(ep, "datapairs", &datapairs); of_node_put(ep); if (r) { DSSERR("failed to parse datapairs\n"); - goto err_free; + return r; } sdi->datapairs = datapairs; @@ -372,19 +369,14 @@ int sdi_init_port(struct dss_device *dss, struct platform_device *pdev, r = PTR_ERR(sdi->vdds_sdi_reg); if (r != -EPROBE_DEFER) DSSERR("can't get VDDS_SDI regulator\n"); - goto err_free; + return r; } r = sdi_init_output(sdi); if (r) - goto err_free; + return r; return 0; - -err_free: - kfree(sdi); - - return r; } void sdi_uninit_port(struct device_node *port) @@ -395,5 +387,4 @@ void sdi_uninit_port(struct device_node *port) return; sdi_uninit_output(sdi); - kfree(sdi); } diff --git a/drivers/gpu/drm/omapdrm/dss/venc.c b/drivers/gpu/drm/omapdrm/dss/venc.c index 50349518eda1..9b5d53dc361e 100644 --- a/drivers/gpu/drm/omapdrm/dss/venc.c +++ b/drivers/gpu/drm/omapdrm/dss/venc.c @@ -664,7 +664,6 @@ static const struct drm_bridge_funcs venc_bridge_funcs = { static void venc_bridge_init(struct venc_device *venc) { - venc->bridge.funcs = &venc_bridge_funcs; venc->bridge.of_node = venc->pdev->dev.of_node; venc->bridge.ops = DRM_BRIDGE_OP_MODES; venc->bridge.type = DRM_MODE_CONNECTOR_SVIDEO; @@ -809,9 +808,9 @@ static int venc_probe(struct platform_device *pdev) struct venc_device *venc; int r; - venc = kzalloc(sizeof(*venc), GFP_KERNEL); - if (!venc) - return -ENOMEM; + venc = devm_drm_bridge_alloc(&pdev->dev, struct venc_device, bridge, &venc_bridge_funcs); + if (IS_ERR(venc)) + return PTR_ERR(venc); venc->pdev = pdev; @@ -824,26 +823,24 @@ static int venc_probe(struct platform_device *pdev) venc->config = &venc_config_pal_trm; venc->base = devm_platform_ioremap_resource(pdev, 0); - if (IS_ERR(venc->base)) { - r = PTR_ERR(venc->base); - goto err_free; - } + if (IS_ERR(venc->base)) + return PTR_ERR(venc->base); venc->vdda_dac_reg = devm_regulator_get(&pdev->dev, "vdda"); if (IS_ERR(venc->vdda_dac_reg)) { r = PTR_ERR(venc->vdda_dac_reg); if (r != -EPROBE_DEFER) DSSERR("can't get VDDA_DAC regulator\n"); - goto err_free; + return r; } r = venc_get_clocks(venc); if (r) - goto err_free; + return r; r = venc_probe_of(venc); if (r) - goto err_free; + return r; pm_runtime_enable(&pdev->dev); @@ -861,8 +858,6 @@ err_uninit_output: venc_uninit_output(venc); err_pm_disable: pm_runtime_disable(&pdev->dev); -err_free: - kfree(venc); return r; } @@ -875,8 +870,6 @@ static void venc_remove(struct platform_device *pdev) venc_uninit_output(venc); pm_runtime_disable(&pdev->dev); - - kfree(venc); } static __maybe_unused int venc_runtime_suspend(struct device *dev) diff --git a/drivers/gpu/drm/panel/Kconfig b/drivers/gpu/drm/panel/Kconfig index cfebb08e8a62..d5aa1c95c6a4 100644 --- a/drivers/gpu/drm/panel/Kconfig +++ b/drivers/gpu/drm/panel/Kconfig @@ -647,6 +647,32 @@ config DRM_PANEL_RAYDIUM_RM69380 This panel controller can be found in the Lenovo Xiaoxin Pad Pro 2021 in combination with an EDO OLED panel. +config DRM_PANEL_RENESAS_R61307 + tristate "Renesas R61307 DSI video mode panel" + depends on OF + depends on DRM_MIPI_DSI + depends on BACKLIGHT_CLASS_DEVICE + help + Say Y here if you want to enable support for KOE tx13d100vm0eaa + IPS-LCD module with Renesas R69328 IC. The panel has a 1024x768 + resolution and uses 24 bit RGB per pixel. + + This panel controller can be found in LG Optimus Vu P895 smartphone + in combination with LCD panel. + +config DRM_PANEL_RENESAS_R69328 + tristate "Renesas R69328 720x1280 DSI video mode panel" + depends on OF + depends on DRM_MIPI_DSI + depends on BACKLIGHT_CLASS_DEVICE + help + Say Y here if you want to enable support for JDI dx12d100vm0eaa + IPS-LCD module with Renesas R69328 IC. The panel has a 720x1280 + resolution and uses 24 bit RGB per pixel. + + This panel controller can be found in LG Optimus 4X P895 smartphone + in combination with LCD panel. + config DRM_PANEL_RONBO_RB070D30 tristate "Ronbo Electronics RB070D30 panel" depends on OF diff --git a/drivers/gpu/drm/panel/Makefile b/drivers/gpu/drm/panel/Makefile index 714cbac830e3..73a39bc72604 100644 --- a/drivers/gpu/drm/panel/Makefile +++ b/drivers/gpu/drm/panel/Makefile @@ -65,6 +65,8 @@ obj-$(CONFIG_DRM_PANEL_RAYDIUM_RM67200) += panel-raydium-rm67200.o obj-$(CONFIG_DRM_PANEL_RAYDIUM_RM68200) += panel-raydium-rm68200.o obj-$(CONFIG_DRM_PANEL_RAYDIUM_RM692E5) += panel-raydium-rm692e5.o obj-$(CONFIG_DRM_PANEL_RAYDIUM_RM69380) += panel-raydium-rm69380.o +obj-$(CONFIG_DRM_PANEL_RENESAS_R61307) += panel-renesas-r61307.o +obj-$(CONFIG_DRM_PANEL_RENESAS_R69328) += panel-renesas-r69328.o obj-$(CONFIG_DRM_PANEL_RONBO_RB070D30) += panel-ronbo-rb070d30.o obj-$(CONFIG_DRM_PANEL_SAMSUNG_AMS581VF01) += panel-samsung-ams581vf01.o obj-$(CONFIG_DRM_PANEL_SAMSUNG_AMS639RQ08) += panel-samsung-ams639rq08.o diff --git a/drivers/gpu/drm/panel/panel-edp.c b/drivers/gpu/drm/panel/panel-edp.c index 90e8c154a978..6c45c9e879ec 100644 --- a/drivers/gpu/drm/panel/panel-edp.c +++ b/drivers/gpu/drm/panel/panel-edp.c @@ -113,7 +113,7 @@ struct panel_delay { * // do fixed enable delay * // enforce prepare_to_enable min time * - * This is not specified in a standard way on eDP timing diagrams. + * This is usually (T4+T5+T6+T8)-min on eDP timing diagrams. * It is effectively the time from HPD going high till you can * turn on the backlight. */ @@ -1869,6 +1869,7 @@ static const struct edp_panel_entry edp_panels[] = { EDP_PANEL_ENTRY('A', 'U', 'O', 0x235c, &delay_200_500_e50, "B116XTN02.3"), EDP_PANEL_ENTRY('A', 'U', 'O', 0x239b, &delay_200_500_e50, "B116XAN06.1"), EDP_PANEL_ENTRY('A', 'U', 'O', 0x255c, &delay_200_500_e50, "B116XTN02.5"), + EDP_PANEL_ENTRY('A', 'U', 'O', 0x30ed, &delay_200_500_e50, "G156HAN03.0"), EDP_PANEL_ENTRY('A', 'U', 'O', 0x403d, &delay_200_500_e50, "B140HAN04.0"), EDP_PANEL_ENTRY('A', 'U', 'O', 0x405c, &auo_b116xak01.delay, "B116XAN04.0"), EDP_PANEL_ENTRY2('A', 'U', 'O', 0x405c, &auo_b116xak01.delay, "B116XAK01.0", @@ -1923,6 +1924,7 @@ static const struct edp_panel_entry edp_panels[] = { EDP_PANEL_ENTRY('B', 'O', 'E', 0x094b, &delay_200_500_e50, "NT116WHM-N21"), EDP_PANEL_ENTRY('B', 'O', 'E', 0x0951, &delay_200_500_e80, "NV116WHM-N47"), EDP_PANEL_ENTRY('B', 'O', 'E', 0x095f, &delay_200_500_e50, "NE135FBM-N41 v8.1"), + EDP_PANEL_ENTRY('B', 'O', 'E', 0x0964, &delay_200_500_e50, "NV133WUM-N61"), EDP_PANEL_ENTRY('B', 'O', 'E', 0x096e, &delay_200_500_e50_po2e200, "NV116WHM-T07 V8.0"), EDP_PANEL_ENTRY('B', 'O', 'E', 0x0979, &delay_200_500_e50, "NV116WHM-N49 V8.0"), EDP_PANEL_ENTRY('B', 'O', 'E', 0x098d, &boe_nv110wtm_n61.delay, "NV110WTM-N61"), @@ -1973,6 +1975,7 @@ static const struct edp_panel_entry edp_panels[] = { EDP_PANEL_ENTRY('C', 'M', 'N', 0x14d4, &delay_200_500_e80_d50, "N140HCA-EAC"), EDP_PANEL_ENTRY('C', 'M', 'N', 0x14d6, &delay_200_500_e80_d50, "N140BGA-EA4"), EDP_PANEL_ENTRY('C', 'M', 'N', 0x14e5, &delay_200_500_e80_d50, "N140HGA-EA1"), + EDP_PANEL_ENTRY('C', 'M', 'N', 0x162b, &delay_200_500_e80_d50, "N160JCE-ELL"), EDP_PANEL_ENTRY('C', 'S', 'O', 0x1200, &delay_200_500_e50_p2e200, "MNC207QS1-1"), EDP_PANEL_ENTRY('C', 'S', 'O', 0x1413, &delay_200_500_e50_p2e200, "MNE007JA1-2"), @@ -2005,6 +2008,7 @@ static const struct edp_panel_entry edp_panels[] = { EDP_PANEL_ENTRY('K', 'D', 'C', 0x044f, &delay_200_500_e50, "KD116N9-30NH-F3"), EDP_PANEL_ENTRY('K', 'D', 'C', 0x05f1, &delay_200_500_e80_d50, "KD116N5-30NV-G7"), EDP_PANEL_ENTRY('K', 'D', 'C', 0x0809, &delay_200_500_e50, "KD116N2930A15"), + EDP_PANEL_ENTRY('K', 'D', 'C', 0x1220, &delay_200_500_e50, "KD116N3730A05"), EDP_PANEL_ENTRY('L', 'G', 'D', 0x0000, &delay_200_500_e200_d200, "Unknown"), EDP_PANEL_ENTRY('L', 'G', 'D', 0x048d, &delay_200_500_e200_d200, "Unknown"), diff --git a/drivers/gpu/drm/panel/panel-elida-kd35t133.c b/drivers/gpu/drm/panel/panel-elida-kd35t133.c index b904d5437444..1f177834d629 100644 --- a/drivers/gpu/drm/panel/panel-elida-kd35t133.c +++ b/drivers/gpu/drm/panel/panel-elida-kd35t133.c @@ -206,9 +206,10 @@ static int kd35t133_probe(struct mipi_dsi_device *dsi) struct kd35t133 *ctx; int ret; - ctx = devm_kzalloc(dev, sizeof(*ctx), GFP_KERNEL); - if (!ctx) - return -ENOMEM; + ctx = devm_drm_panel_alloc(dev, struct kd35t133, panel, + &kd35t133_funcs, DRM_MODE_CONNECTOR_DSI); + if (IS_ERR(ctx)) + return PTR_ERR(ctx); ctx->reset_gpio = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_LOW); if (IS_ERR(ctx->reset_gpio)) { @@ -248,9 +249,6 @@ static int kd35t133_probe(struct mipi_dsi_device *dsi) MIPI_DSI_MODE_LPM | MIPI_DSI_MODE_NO_EOT_PACKET | MIPI_DSI_CLOCK_NON_CONTINUOUS; - drm_panel_init(&ctx->panel, &dsi->dev, &kd35t133_funcs, - DRM_MODE_CONNECTOR_DSI); - ret = drm_panel_of_backlight(&ctx->panel); if (ret) return ret; diff --git a/drivers/gpu/drm/panel/panel-feixin-k101-im2ba02.c b/drivers/gpu/drm/panel/panel-feixin-k101-im2ba02.c index 986e3e192881..6225501cb174 100644 --- a/drivers/gpu/drm/panel/panel-feixin-k101-im2ba02.c +++ b/drivers/gpu/drm/panel/panel-feixin-k101-im2ba02.c @@ -443,9 +443,11 @@ static int k101_im2ba02_dsi_probe(struct mipi_dsi_device *dsi) unsigned int i; int ret; - ctx = devm_kzalloc(&dsi->dev, sizeof(*ctx), GFP_KERNEL); - if (!ctx) - return -ENOMEM; + ctx = devm_drm_panel_alloc(&dsi->dev, struct k101_im2ba02, panel, + &k101_im2ba02_funcs, + DRM_MODE_CONNECTOR_DSI); + if (IS_ERR(ctx)) + return PTR_ERR(ctx); mipi_dsi_set_drvdata(dsi, ctx); ctx->dsi = dsi; @@ -463,9 +465,6 @@ static int k101_im2ba02_dsi_probe(struct mipi_dsi_device *dsi) return dev_err_probe(&dsi->dev, PTR_ERR(ctx->reset), "Couldn't get our reset GPIO\n"); - drm_panel_init(&ctx->panel, &dsi->dev, &k101_im2ba02_funcs, - DRM_MODE_CONNECTOR_DSI); - ret = drm_panel_of_backlight(&ctx->panel); if (ret) return ret; diff --git a/drivers/gpu/drm/panel/panel-feiyang-fy07024di26a30d.c b/drivers/gpu/drm/panel/panel-feiyang-fy07024di26a30d.c index 48e3acaecdf3..4f8d6d8c07e4 100644 --- a/drivers/gpu/drm/panel/panel-feiyang-fy07024di26a30d.c +++ b/drivers/gpu/drm/panel/panel-feiyang-fy07024di26a30d.c @@ -189,16 +189,14 @@ static int feiyang_dsi_probe(struct mipi_dsi_device *dsi) struct feiyang *ctx; int ret; - ctx = devm_kzalloc(&dsi->dev, sizeof(*ctx), GFP_KERNEL); - if (!ctx) - return -ENOMEM; + ctx = devm_drm_panel_alloc(&dsi->dev, struct feiyang, panel, + &feiyang_funcs, DRM_MODE_CONNECTOR_DSI); + if (IS_ERR(ctx)) + return PTR_ERR(ctx); mipi_dsi_set_drvdata(dsi, ctx); ctx->dsi = dsi; - drm_panel_init(&ctx->panel, &dsi->dev, &feiyang_funcs, - DRM_MODE_CONNECTOR_DSI); - ctx->dvdd = devm_regulator_get(&dsi->dev, "dvdd"); if (IS_ERR(ctx->dvdd)) return dev_err_probe(&dsi->dev, PTR_ERR(ctx->dvdd), diff --git a/drivers/gpu/drm/panel/panel-himax-hx83112a.c b/drivers/gpu/drm/panel/panel-himax-hx83112a.c index 47bce087e339..142cb1cc067a 100644 --- a/drivers/gpu/drm/panel/panel-himax-hx83112a.c +++ b/drivers/gpu/drm/panel/panel-himax-hx83112a.c @@ -269,9 +269,11 @@ static int hx83112a_probe(struct mipi_dsi_device *dsi) struct hx83112a_panel *ctx; int ret; - ctx = devm_kzalloc(dev, sizeof(*ctx), GFP_KERNEL); - if (!ctx) - return -ENOMEM; + ctx = devm_drm_panel_alloc(dev, struct hx83112a_panel, panel, + &hx83112a_panel_funcs, + DRM_MODE_CONNECTOR_DSI); + if (IS_ERR(ctx)) + return PTR_ERR(ctx); ctx->supplies[0].supply = "vdd1"; ctx->supplies[1].supply = "vsn"; @@ -295,8 +297,6 @@ static int hx83112a_probe(struct mipi_dsi_device *dsi) MIPI_DSI_MODE_VIDEO_HSE | MIPI_DSI_CLOCK_NON_CONTINUOUS; - drm_panel_init(&ctx->panel, dev, &hx83112a_panel_funcs, - DRM_MODE_CONNECTOR_DSI); ctx->panel.prepare_prev_first = true; ret = drm_panel_of_backlight(&ctx->panel); diff --git a/drivers/gpu/drm/panel/panel-himax-hx8394.c b/drivers/gpu/drm/panel/panel-himax-hx8394.c index ff994bf0e3cc..c4d3e09a228d 100644 --- a/drivers/gpu/drm/panel/panel-himax-hx8394.c +++ b/drivers/gpu/drm/panel/panel-himax-hx8394.c @@ -477,6 +477,147 @@ static const struct hx8394_panel_desc mchp_ac40t08a_desc = { .init_sequence = mchp_ac40t08a_init_sequence, }; +/* + * HL055FHAV028C is based on Himax HX8399, so datasheet pages are + * slightly different than HX8394 based panels. + */ +static void hl055fhav028c_init_sequence(struct mipi_dsi_multi_context *dsi_ctx) +{ + /* 6.3.6 SETEXTC: Set extension command (B9h) */ + mipi_dsi_dcs_write_seq_multi(dsi_ctx, HX8394_CMD_SETEXTC, + 0xff, 0x83, 0x99); + + /* 6.3.17 SETOFFSET: Set offset voltage (D2h) */ + mipi_dsi_dcs_write_seq_multi(dsi_ctx, HX8394_CMD_SETOFFSET, + 0x77); + + /* 6.3.1 SETPOWER: Set power (B1h) */ + mipi_dsi_dcs_write_seq_multi(dsi_ctx, HX8394_CMD_SETPOWER, + 0x02, 0x04, 0x74, 0x94, 0x01, 0x32, + 0x33, 0x11, 0x11, 0xab, 0x4d, 0x56, + 0x73, 0x02, 0x02); + + /* 6.3.2 SETDISP: Set display related register (B2h) */ + mipi_dsi_dcs_write_seq_multi(dsi_ctx, HX8394_CMD_SETDISP, + 0x00, 0x80, 0x80, 0xae, 0x05, 0x07, + 0x5a, 0x11, 0x00, 0x00, 0x10, 0x1e, + 0x70, 0x03, 0xd4); + + /* 6.3.3 SETCYC: Set display waveform cycles (B4h) */ + mipi_dsi_dcs_write_seq_multi(dsi_ctx, HX8394_CMD_SETCYC, + 0x00, 0xff, 0x02, 0xc0, 0x02, 0xc0, + 0x00, 0x00, 0x08, 0x00, 0x04, 0x06, + 0x00, 0x32, 0x04, 0x0a, 0x08, 0x21, + 0x03, 0x01, 0x00, 0x0f, 0xb8, 0x8b, + 0x02, 0xc0, 0x02, 0xc0, 0x00, 0x00, + 0x08, 0x00, 0x04, 0x06, 0x00, 0x32, + 0x04, 0x0a, 0x08, 0x01, 0x00, 0x0f, + 0xb8, 0x01); + + /* 6.3.18 SETGIP0: Set GIP Option0 (D3h) */ + mipi_dsi_dcs_write_seq_multi(dsi_ctx, HX8394_CMD_SETGIP0, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x06, 0x00, 0x00, 0x10, 0x04, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, + 0x00, 0x05, 0x05, 0x07, 0x00, 0x00, + 0x00, 0x05, 0x40); + + /* 6.3.19 Set GIP Option1 (D5h) */ + mipi_dsi_dcs_write_seq_multi(dsi_ctx, HX8394_CMD_SETGIP1, + 0x18, 0x18, 0x19, 0x19, 0x18, 0x18, + 0x21, 0x20, 0x01, 0x00, 0x07, 0x06, + 0x05, 0x04, 0x03, 0x02, 0x18, 0x18, + 0x18, 0x18, 0x18, 0x18, 0x2f, 0x2f, + 0x30, 0x30, 0x31, 0x31, 0x18, 0x18, + 0x18, 0x18); + + /* 6.3.20 Set GIP Option2 (D6h) */ + mipi_dsi_dcs_write_seq_multi(dsi_ctx, HX8394_CMD_SETGIP2, + 0x18, 0x18, 0x19, 0x19, 0x40, 0x40, + 0x20, 0x21, 0x02, 0x03, 0x04, 0x05, + 0x06, 0x07, 0x00, 0x01, 0x40, 0x40, + 0x40, 0x40, 0x40, 0x40, 0x2f, 0x2f, + 0x30, 0x30, 0x31, 0x31, 0x40, 0x40, + 0x40, 0x40); + + /* 6.3.21 Set GIP Option3 (D8h) */ + mipi_dsi_dcs_write_seq_multi(dsi_ctx, HX8394_CMD_UNKNOWN4, + 0xa2, 0xaa, 0x02, 0xa0, 0xa2, 0xa8, + 0x02, 0xa0, 0xb0, 0x00, 0x00, 0x00, + 0xb0, 0x00, 0x00, 0x00); + + /* 6.3.9 Set register bank (BDh) */ + mipi_dsi_dcs_write_seq_multi(dsi_ctx, HX8394_CMD_SETREGBANK, + 0x01); + + /* 6.3.21 Set GIP Option3 (D8h) */ + mipi_dsi_dcs_write_seq_multi(dsi_ctx, HX8394_CMD_UNKNOWN4, + 0xb0, 0x00, 0x00, 0x00, 0xb0, 0x00, + 0x00, 0x00, 0xe2, 0xaa, 0x03, 0xf0, + 0xe2, 0xaa, 0x03, 0xf0); + + /* 6.3.9 Set register bank (BDh) */ + mipi_dsi_dcs_write_seq_multi(dsi_ctx, HX8394_CMD_SETREGBANK, + 0x02); + + /* 6.3.21 Set GIP Option3 (D8h) */ + mipi_dsi_dcs_write_seq_multi(dsi_ctx, HX8394_CMD_UNKNOWN4, + 0xe2, 0xaa, 0x03, 0xf0, 0xe2, 0xaa, + 0x03, 0xf0); + + /* 6.3.9 Set register bank (BDh) */ + mipi_dsi_dcs_write_seq_multi(dsi_ctx, HX8394_CMD_SETREGBANK, + 0x00); + + /* 6.3.4 SETVCOM: Set VCOM voltage (B6h) */ + mipi_dsi_dcs_write_seq_multi(dsi_ctx, HX8394_CMD_SETVCOM, + 0x7a, 0x7a); + + /* 6.3.26 SETGAMMA: Set gamma curve related setting (E0h) */ + mipi_dsi_dcs_write_seq_multi(dsi_ctx, HX8394_CMD_SETGAMMA, + 0x00, 0x18, 0x27, 0x24, 0x5a, 0x68, + 0x79, 0x78, 0x81, 0x8a, 0x92, 0x99, + 0x9e, 0xa7, 0xaf, 0xb4, 0xb9, 0xc3, + 0xc7, 0xd1, 0xc6, 0xd4, 0xd5, 0x6c, + 0x67, 0x71, 0x77, 0x00, 0x00, 0x18, + 0x27, 0x24, 0x5a, 0x68, 0x79, 0x78, + 0x81, 0x8a, 0x92, 0x99, 0x9e, 0xa7, + 0xaf, 0xb4, 0xb9, 0xc3, 0xc7, 0xd1, + 0xc6, 0xd4, 0xd5, 0x6c, 0x67, 0x77); + + /* Unknown command, not listed in the HX8399-C datasheet (C6h) */ + mipi_dsi_dcs_write_seq_multi(dsi_ctx, HX8394_CMD_UNKNOWN2, + 0xff, 0xf9); + + /* 6.3.16 SETPANEL (CCh) */ + mipi_dsi_dcs_write_seq_multi(dsi_ctx, HX8394_CMD_SETPANEL, + 0x08); +} + +static const struct drm_display_mode hl055fhav028c_mode = { + .hdisplay = 1080, + .hsync_start = 1080 + 32, + .hsync_end = 1080 + 32 + 8, + .htotal = 1080 + 32 + 8 + 32, + .vdisplay = 1920, + .vsync_start = 1920 + 16, + .vsync_end = 1920 + 16 + 2, + .vtotal = 1920 + 16 + 2 + 14, + .clock = 134920, + .flags = DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC, + .width_mm = 70, + .height_mm = 127, +}; + +static const struct hx8394_panel_desc hl055fhav028c_desc = { + .mode = &hl055fhav028c_mode, + .lanes = 4, + .mode_flags = MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_VIDEO_BURST, + .format = MIPI_DSI_FMT_RGB888, + .init_sequence = hl055fhav028c_init_sequence, +}; + static int hx8394_enable(struct drm_panel *panel) { struct hx8394 *ctx = panel_to_hx8394(panel); @@ -611,9 +752,11 @@ static int hx8394_probe(struct mipi_dsi_device *dsi) struct hx8394 *ctx; int ret; - ctx = devm_kzalloc(dev, sizeof(*ctx), GFP_KERNEL); - if (!ctx) - return -ENOMEM; + ctx = devm_drm_panel_alloc(dev, struct hx8394, panel, + &hx8394_drm_funcs, + DRM_MODE_CONNECTOR_DSI); + if (IS_ERR(ctx)) + return PTR_ERR(ctx); ctx->reset_gpio = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_HIGH); if (IS_ERR(ctx->reset_gpio)) @@ -645,9 +788,6 @@ static int hx8394_probe(struct mipi_dsi_device *dsi) return dev_err_probe(dev, PTR_ERR(ctx->iovcc), "Failed to request iovcc regulator\n"); - drm_panel_init(&ctx->panel, dev, &hx8394_drm_funcs, - DRM_MODE_CONNECTOR_DSI); - ret = drm_panel_of_backlight(&ctx->panel); if (ret) return ret; @@ -683,6 +823,7 @@ static void hx8394_remove(struct mipi_dsi_device *dsi) static const struct of_device_id hx8394_of_match[] = { { .compatible = "hannstar,hsd060bhw4", .data = &hsd060bhw4_desc }, + { .compatible = "huiling,hl055fhav028c", .data = &hl055fhav028c_desc }, { .compatible = "powkiddy,x55-panel", .data = &powkiddy_x55_desc }, { .compatible = "microchip,ac40t08a-mipi-panel", .data = &mchp_ac40t08a_desc }, { /* sentinel */ } diff --git a/drivers/gpu/drm/panel/panel-ilitek-ili9322.c b/drivers/gpu/drm/panel/panel-ilitek-ili9322.c index 94b7dfef3b5e..6ed544a83bdd 100644 --- a/drivers/gpu/drm/panel/panel-ilitek-ili9322.c +++ b/drivers/gpu/drm/panel/panel-ilitek-ili9322.c @@ -722,9 +722,10 @@ static int ili9322_probe(struct spi_device *spi) int ret; int i; - ili = devm_kzalloc(dev, sizeof(struct ili9322), GFP_KERNEL); - if (!ili) - return -ENOMEM; + ili = devm_drm_panel_alloc(dev, struct ili9322, panel, + &ili9322_drm_funcs, DRM_MODE_CONNECTOR_DPI); + if (IS_ERR(ili)) + return PTR_ERR(ili); spi_set_drvdata(spi, ili); @@ -883,9 +884,6 @@ static int ili9322_probe(struct spi_device *spi) ili->input = ili->conf->input; } - drm_panel_init(&ili->panel, dev, &ili9322_drm_funcs, - DRM_MODE_CONNECTOR_DPI); - drm_panel_add(&ili->panel); return 0; diff --git a/drivers/gpu/drm/panel/panel-ilitek-ili9341.c b/drivers/gpu/drm/panel/panel-ilitek-ili9341.c index ff39f5dd4097..f7425dfaa50d 100644 --- a/drivers/gpu/drm/panel/panel-ilitek-ili9341.c +++ b/drivers/gpu/drm/panel/panel-ilitek-ili9341.c @@ -173,7 +173,6 @@ struct ili9341_config { }; struct ili9341 { - struct device *dev; const struct ili9341_config *conf; struct drm_panel panel; struct gpio_desc *reset_gpio; @@ -490,9 +489,11 @@ static int ili9341_dpi_probe(struct spi_device *spi, struct gpio_desc *dc, struct ili9341 *ili; int ret; - ili = devm_kzalloc(dev, sizeof(struct ili9341), GFP_KERNEL); - if (!ili) - return -ENOMEM; + ili = devm_drm_panel_alloc(dev, struct ili9341, panel, + &ili9341_dpi_funcs, + DRM_MODE_CONNECTOR_DPI); + if (IS_ERR(ili)) + return PTR_ERR(ili); ili->dbi = devm_kzalloc(dev, sizeof(struct mipi_dbi), GFP_KERNEL); @@ -526,8 +527,6 @@ static int ili9341_dpi_probe(struct spi_device *spi, struct gpio_desc *dc, } ili->max_spi_speed = ili->conf->max_spi_speed; - drm_panel_init(&ili->panel, dev, &ili9341_dpi_funcs, - DRM_MODE_CONNECTOR_DPI); drm_panel_add(&ili->panel); return 0; diff --git a/drivers/gpu/drm/panel/panel-ilitek-ili9805.c b/drivers/gpu/drm/panel/panel-ilitek-ili9805.c index 1cbc25758bd2..e6c483851f1f 100644 --- a/drivers/gpu/drm/panel/panel-ilitek-ili9805.c +++ b/drivers/gpu/drm/panel/panel-ilitek-ili9805.c @@ -307,9 +307,12 @@ static int ili9805_dsi_probe(struct mipi_dsi_device *dsi) struct ili9805 *ctx; int ret; - ctx = devm_kzalloc(&dsi->dev, sizeof(*ctx), GFP_KERNEL); - if (!ctx) - return -ENOMEM; + ctx = devm_drm_panel_alloc(&dsi->dev, struct ili9805, panel, + &ili9805_funcs, + DRM_MODE_CONNECTOR_DSI); + if (IS_ERR(ctx)) + return PTR_ERR(ctx); + mipi_dsi_set_drvdata(dsi, ctx); ctx->dsi = dsi; ctx->desc = of_device_get_match_data(&dsi->dev); @@ -320,9 +323,6 @@ static int ili9805_dsi_probe(struct mipi_dsi_device *dsi) MIPI_DSI_MODE_VIDEO_SYNC_PULSE | MIPI_DSI_MODE_NO_EOT_PACKET; dsi->lanes = 2; - drm_panel_init(&ctx->panel, &dsi->dev, &ili9805_funcs, - DRM_MODE_CONNECTOR_DSI); - ctx->dvdd = devm_regulator_get(&dsi->dev, "dvdd"); if (IS_ERR(ctx->dvdd)) return PTR_ERR(ctx->dvdd); diff --git a/drivers/gpu/drm/panel/panel-ilitek-ili9806e.c b/drivers/gpu/drm/panel/panel-ilitek-ili9806e.c index a3c79ad99d0b..18aa6222b0c5 100644 --- a/drivers/gpu/drm/panel/panel-ilitek-ili9806e.c +++ b/drivers/gpu/drm/panel/panel-ilitek-ili9806e.c @@ -166,9 +166,10 @@ static int ili9806e_dsi_probe(struct mipi_dsi_device *dsi) struct ili9806e_panel *ctx; int i, ret; - ctx = devm_kzalloc(dev, sizeof(*ctx), GFP_KERNEL); - if (!ctx) - return -ENOMEM; + ctx = devm_drm_panel_alloc(dev, struct ili9806e_panel, panel, &ili9806e_funcs, + DRM_MODE_CONNECTOR_DSI); + if (IS_ERR(ctx)) + return PTR_ERR(ctx); ctx->desc = device_get_match_data(dev); @@ -192,9 +193,6 @@ static int ili9806e_dsi_probe(struct mipi_dsi_device *dsi) dsi->format = ctx->desc->format; dsi->lanes = ctx->desc->lanes; - drm_panel_init(&ctx->panel, dev, &ili9806e_funcs, - DRM_MODE_CONNECTOR_DSI); - ret = of_drm_get_panel_orientation(dev->of_node, &ctx->orientation); if (ret) return dev_err_probe(dev, ret, "Failed to get orientation\n"); diff --git a/drivers/gpu/drm/panel/panel-ilitek-ili9881c.c b/drivers/gpu/drm/panel/panel-ilitek-ili9881c.c index 28cd7560e5db..ac433345a179 100644 --- a/drivers/gpu/drm/panel/panel-ilitek-ili9881c.c +++ b/drivers/gpu/drm/panel/panel-ilitek-ili9881c.c @@ -43,6 +43,7 @@ struct ili9881c_desc { const struct drm_display_mode *mode; const unsigned long mode_flags; u8 default_address_mode; + unsigned int lanes; }; struct ili9881c { @@ -1223,6 +1224,199 @@ static const struct ili9881c_instr am8001280g_init[] = { ILI9881C_COMMAND_INSTR(MIPI_DCS_WRITE_POWER_SAVE, 0x00), }; +static const struct ili9881c_instr rpi_7inch_init[] = { + ILI9881C_SWITCH_PAGE_INSTR(3), + ILI9881C_COMMAND_INSTR(0x01, 0x00), + ILI9881C_COMMAND_INSTR(0x02, 0x00), + ILI9881C_COMMAND_INSTR(0x03, 0x73), + ILI9881C_COMMAND_INSTR(0x04, 0x00), + ILI9881C_COMMAND_INSTR(0x05, 0x00), + ILI9881C_COMMAND_INSTR(0x06, 0x0a), + ILI9881C_COMMAND_INSTR(0x07, 0x00), + ILI9881C_COMMAND_INSTR(0x08, 0x00), + ILI9881C_COMMAND_INSTR(0x09, 0x61), + ILI9881C_COMMAND_INSTR(0x0a, 0x00), + ILI9881C_COMMAND_INSTR(0x0b, 0x00), + ILI9881C_COMMAND_INSTR(0x0c, 0x01), + ILI9881C_COMMAND_INSTR(0x0d, 0x00), + ILI9881C_COMMAND_INSTR(0x0e, 0x00), + ILI9881C_COMMAND_INSTR(0x0f, 0x61), + ILI9881C_COMMAND_INSTR(0x10, 0x61), + ILI9881C_COMMAND_INSTR(0x11, 0x00), + ILI9881C_COMMAND_INSTR(0x12, 0x00), + ILI9881C_COMMAND_INSTR(0x13, 0x00), + ILI9881C_COMMAND_INSTR(0x14, 0x00), + ILI9881C_COMMAND_INSTR(0x15, 0x00), + ILI9881C_COMMAND_INSTR(0x16, 0x00), + ILI9881C_COMMAND_INSTR(0x17, 0x00), + ILI9881C_COMMAND_INSTR(0x18, 0x00), + ILI9881C_COMMAND_INSTR(0x19, 0x00), + ILI9881C_COMMAND_INSTR(0x1a, 0x00), + ILI9881C_COMMAND_INSTR(0x1b, 0x00), + ILI9881C_COMMAND_INSTR(0x1c, 0x00), + ILI9881C_COMMAND_INSTR(0x1d, 0x00), + ILI9881C_COMMAND_INSTR(0x1e, 0x40), + ILI9881C_COMMAND_INSTR(0x1f, 0x80), + ILI9881C_COMMAND_INSTR(0x20, 0x06), + ILI9881C_COMMAND_INSTR(0x21, 0x01), + ILI9881C_COMMAND_INSTR(0x22, 0x00), + ILI9881C_COMMAND_INSTR(0x23, 0x00), + ILI9881C_COMMAND_INSTR(0x24, 0x00), + ILI9881C_COMMAND_INSTR(0x25, 0x00), + ILI9881C_COMMAND_INSTR(0x26, 0x00), + ILI9881C_COMMAND_INSTR(0x27, 0x00), + ILI9881C_COMMAND_INSTR(0x28, 0x33), + ILI9881C_COMMAND_INSTR(0x29, 0x03), + ILI9881C_COMMAND_INSTR(0x2a, 0x00), + ILI9881C_COMMAND_INSTR(0x2b, 0x00), + ILI9881C_COMMAND_INSTR(0x2c, 0x00), + ILI9881C_COMMAND_INSTR(0x2d, 0x00), + ILI9881C_COMMAND_INSTR(0x2e, 0x00), + ILI9881C_COMMAND_INSTR(0x2f, 0x00), + ILI9881C_COMMAND_INSTR(0x30, 0x00), + ILI9881C_COMMAND_INSTR(0x31, 0x00), + ILI9881C_COMMAND_INSTR(0x32, 0x00), + ILI9881C_COMMAND_INSTR(0x33, 0x00), + ILI9881C_COMMAND_INSTR(0x34, 0x04), + ILI9881C_COMMAND_INSTR(0x35, 0x00), + ILI9881C_COMMAND_INSTR(0x36, 0x00), + ILI9881C_COMMAND_INSTR(0x37, 0x00), + ILI9881C_COMMAND_INSTR(0x38, 0x3c), + ILI9881C_COMMAND_INSTR(0x39, 0x00), + ILI9881C_COMMAND_INSTR(0x3a, 0x00), + ILI9881C_COMMAND_INSTR(0x3b, 0x00), + ILI9881C_COMMAND_INSTR(0x3c, 0x00), + ILI9881C_COMMAND_INSTR(0x3d, 0x00), + ILI9881C_COMMAND_INSTR(0x3e, 0x00), + ILI9881C_COMMAND_INSTR(0x3f, 0x00), + ILI9881C_COMMAND_INSTR(0x40, 0x00), + ILI9881C_COMMAND_INSTR(0x41, 0x00), + ILI9881C_COMMAND_INSTR(0x42, 0x00), + ILI9881C_COMMAND_INSTR(0x43, 0x00), + ILI9881C_COMMAND_INSTR(0x44, 0x00), + ILI9881C_COMMAND_INSTR(0x50, 0x10), + ILI9881C_COMMAND_INSTR(0x51, 0x32), + ILI9881C_COMMAND_INSTR(0x52, 0x54), + ILI9881C_COMMAND_INSTR(0x53, 0x76), + ILI9881C_COMMAND_INSTR(0x54, 0x98), + ILI9881C_COMMAND_INSTR(0x55, 0xba), + ILI9881C_COMMAND_INSTR(0x56, 0x10), + ILI9881C_COMMAND_INSTR(0x57, 0x32), + ILI9881C_COMMAND_INSTR(0x58, 0x54), + ILI9881C_COMMAND_INSTR(0x59, 0x76), + ILI9881C_COMMAND_INSTR(0x5a, 0x98), + ILI9881C_COMMAND_INSTR(0x5b, 0xba), + ILI9881C_COMMAND_INSTR(0x5c, 0xdc), + ILI9881C_COMMAND_INSTR(0x5d, 0xfe), + ILI9881C_COMMAND_INSTR(0x5e, 0x00), + ILI9881C_COMMAND_INSTR(0x5f, 0x0e), + ILI9881C_COMMAND_INSTR(0x60, 0x0f), + ILI9881C_COMMAND_INSTR(0x61, 0x0c), + ILI9881C_COMMAND_INSTR(0x62, 0x0d), + ILI9881C_COMMAND_INSTR(0x63, 0x06), + ILI9881C_COMMAND_INSTR(0x64, 0x07), + ILI9881C_COMMAND_INSTR(0x65, 0x02), + ILI9881C_COMMAND_INSTR(0x66, 0x02), + ILI9881C_COMMAND_INSTR(0x67, 0x02), + ILI9881C_COMMAND_INSTR(0x68, 0x02), + ILI9881C_COMMAND_INSTR(0x69, 0x01), + ILI9881C_COMMAND_INSTR(0x6a, 0x00), + ILI9881C_COMMAND_INSTR(0x6b, 0x02), + ILI9881C_COMMAND_INSTR(0x6c, 0x15), + ILI9881C_COMMAND_INSTR(0x6d, 0x14), + ILI9881C_COMMAND_INSTR(0x6e, 0x02), + ILI9881C_COMMAND_INSTR(0x6f, 0x02), + ILI9881C_COMMAND_INSTR(0x70, 0x02), + ILI9881C_COMMAND_INSTR(0x71, 0x02), + ILI9881C_COMMAND_INSTR(0x72, 0x02), + ILI9881C_COMMAND_INSTR(0x73, 0x02), + ILI9881C_COMMAND_INSTR(0x74, 0x02), + ILI9881C_COMMAND_INSTR(0x75, 0x0e), + ILI9881C_COMMAND_INSTR(0x76, 0x0f), + ILI9881C_COMMAND_INSTR(0x77, 0x0c), + ILI9881C_COMMAND_INSTR(0x78, 0x0d), + ILI9881C_COMMAND_INSTR(0x79, 0x06), + ILI9881C_COMMAND_INSTR(0x7a, 0x07), + ILI9881C_COMMAND_INSTR(0x7b, 0x02), + ILI9881C_COMMAND_INSTR(0x7c, 0x02), + ILI9881C_COMMAND_INSTR(0x7d, 0x02), + ILI9881C_COMMAND_INSTR(0x7e, 0x02), + ILI9881C_COMMAND_INSTR(0x7f, 0x01), + ILI9881C_COMMAND_INSTR(0x80, 0x00), + ILI9881C_COMMAND_INSTR(0x81, 0x02), + ILI9881C_COMMAND_INSTR(0x82, 0x14), + ILI9881C_COMMAND_INSTR(0x83, 0x15), + ILI9881C_COMMAND_INSTR(0x84, 0x02), + ILI9881C_COMMAND_INSTR(0x85, 0x02), + ILI9881C_COMMAND_INSTR(0x86, 0x02), + ILI9881C_COMMAND_INSTR(0x87, 0x02), + ILI9881C_COMMAND_INSTR(0x88, 0x02), + ILI9881C_COMMAND_INSTR(0x89, 0x02), + ILI9881C_COMMAND_INSTR(0x8A, 0x02), + ILI9881C_SWITCH_PAGE_INSTR(4), + ILI9881C_COMMAND_INSTR(0x6C, 0x15), + ILI9881C_COMMAND_INSTR(0x6E, 0x2A), + ILI9881C_COMMAND_INSTR(0x6F, 0x33), + ILI9881C_COMMAND_INSTR(0x3B, 0x98), + ILI9881C_COMMAND_INSTR(0x3a, 0x94), + ILI9881C_COMMAND_INSTR(0x8D, 0x14), + ILI9881C_COMMAND_INSTR(0x87, 0xBA), + ILI9881C_COMMAND_INSTR(0x26, 0x76), + ILI9881C_COMMAND_INSTR(0xB2, 0xD1), + ILI9881C_COMMAND_INSTR(0xB5, 0x06), + ILI9881C_COMMAND_INSTR(0x38, 0x01), + ILI9881C_COMMAND_INSTR(0x39, 0x00), + ILI9881C_SWITCH_PAGE_INSTR(1), + ILI9881C_COMMAND_INSTR(0x22, 0x0A), + ILI9881C_COMMAND_INSTR(0x31, 0x00), + ILI9881C_COMMAND_INSTR(0x53, 0x7d), + ILI9881C_COMMAND_INSTR(0x55, 0x8f), + ILI9881C_COMMAND_INSTR(0x40, 0x33), + ILI9881C_COMMAND_INSTR(0x50, 0x96), + ILI9881C_COMMAND_INSTR(0x51, 0x96), + ILI9881C_COMMAND_INSTR(0x60, 0x23), + ILI9881C_COMMAND_INSTR(0xA0, 0x08), + ILI9881C_COMMAND_INSTR(0xA1, 0x1d), + ILI9881C_COMMAND_INSTR(0xA2, 0x2a), + ILI9881C_COMMAND_INSTR(0xA3, 0x10), + ILI9881C_COMMAND_INSTR(0xA4, 0x15), + ILI9881C_COMMAND_INSTR(0xA5, 0x28), + ILI9881C_COMMAND_INSTR(0xA6, 0x1c), + ILI9881C_COMMAND_INSTR(0xA7, 0x1d), + ILI9881C_COMMAND_INSTR(0xA8, 0x7e), + ILI9881C_COMMAND_INSTR(0xA9, 0x1d), + ILI9881C_COMMAND_INSTR(0xAA, 0x29), + ILI9881C_COMMAND_INSTR(0xAB, 0x6b), + ILI9881C_COMMAND_INSTR(0xAC, 0x1a), + ILI9881C_COMMAND_INSTR(0xAD, 0x18), + ILI9881C_COMMAND_INSTR(0xAE, 0x4b), + ILI9881C_COMMAND_INSTR(0xAF, 0x20), + ILI9881C_COMMAND_INSTR(0xB0, 0x27), + ILI9881C_COMMAND_INSTR(0xB1, 0x50), + ILI9881C_COMMAND_INSTR(0xB2, 0x64), + ILI9881C_COMMAND_INSTR(0xB3, 0x39), + ILI9881C_COMMAND_INSTR(0xC0, 0x08), + ILI9881C_COMMAND_INSTR(0xC1, 0x1d), + ILI9881C_COMMAND_INSTR(0xC2, 0x2a), + ILI9881C_COMMAND_INSTR(0xC3, 0x10), + ILI9881C_COMMAND_INSTR(0xC4, 0x15), + ILI9881C_COMMAND_INSTR(0xC5, 0x28), + ILI9881C_COMMAND_INSTR(0xC6, 0x1c), + ILI9881C_COMMAND_INSTR(0xC7, 0x1d), + ILI9881C_COMMAND_INSTR(0xC8, 0x7e), + ILI9881C_COMMAND_INSTR(0xC9, 0x1d), + ILI9881C_COMMAND_INSTR(0xCA, 0x29), + ILI9881C_COMMAND_INSTR(0xCB, 0x6b), + ILI9881C_COMMAND_INSTR(0xCC, 0x1a), + ILI9881C_COMMAND_INSTR(0xCD, 0x18), + ILI9881C_COMMAND_INSTR(0xCE, 0x4b), + ILI9881C_COMMAND_INSTR(0xCF, 0x20), + ILI9881C_COMMAND_INSTR(0xD0, 0x27), + ILI9881C_COMMAND_INSTR(0xD1, 0x50), + ILI9881C_COMMAND_INSTR(0xD2, 0x64), + ILI9881C_COMMAND_INSTR(0xD3, 0x39), +}; + static inline struct ili9881c *panel_to_ili9881c(struct drm_panel *panel) { return container_of(panel, struct ili9881c, panel); @@ -1449,6 +1643,23 @@ static const struct drm_display_mode am8001280g_default_mode = { .height_mm = 151, }; +static const struct drm_display_mode rpi_7inch_default_mode = { + .clock = 83330, + + .hdisplay = 720, + .hsync_start = 720 + 239, + .hsync_end = 720 + 239 + 33, + .htotal = 720 + 239 + 33 + 50, + + .vdisplay = 1280, + .vsync_start = 1280 + 20, + .vsync_end = 1280 + 20 + 2, + .vtotal = 1280 + 20 + 2 + 30, + + .width_mm = 90, + .height_mm = 151, +}; + static int ili9881c_get_modes(struct drm_panel *panel, struct drm_connector *connector) { @@ -1506,16 +1717,15 @@ static int ili9881c_dsi_probe(struct mipi_dsi_device *dsi) struct ili9881c *ctx; int ret; - ctx = devm_kzalloc(&dsi->dev, sizeof(*ctx), GFP_KERNEL); - if (!ctx) - return -ENOMEM; + ctx = devm_drm_panel_alloc(&dsi->dev, struct ili9881c, panel, &ili9881c_funcs, + DRM_MODE_CONNECTOR_DSI); + if (IS_ERR(ctx)) + return PTR_ERR(ctx); + mipi_dsi_set_drvdata(dsi, ctx); ctx->dsi = dsi; ctx->desc = of_device_get_match_data(&dsi->dev); - drm_panel_init(&ctx->panel, &dsi->dev, &ili9881c_funcs, - DRM_MODE_CONNECTOR_DSI); - ctx->power = devm_regulator_get(&dsi->dev, "power"); if (IS_ERR(ctx->power)) return dev_err_probe(&dsi->dev, PTR_ERR(ctx->power), @@ -1549,7 +1759,7 @@ static int ili9881c_dsi_probe(struct mipi_dsi_device *dsi) dsi->mode_flags = ctx->desc->mode_flags; dsi->format = MIPI_DSI_FMT_RGB888; - dsi->lanes = 4; + dsi->lanes = ctx->desc->lanes; return mipi_dsi_attach(dsi); } @@ -1567,6 +1777,7 @@ static const struct ili9881c_desc lhr050h41_desc = { .init_length = ARRAY_SIZE(lhr050h41_init), .mode = &lhr050h41_default_mode, .mode_flags = MIPI_DSI_MODE_VIDEO_SYNC_PULSE, + .lanes = 4, }; static const struct ili9881c_desc k101_im2byl02_desc = { @@ -1574,6 +1785,7 @@ static const struct ili9881c_desc k101_im2byl02_desc = { .init_length = ARRAY_SIZE(k101_im2byl02_init), .mode = &k101_im2byl02_default_mode, .mode_flags = MIPI_DSI_MODE_VIDEO_SYNC_PULSE, + .lanes = 4, }; static const struct ili9881c_desc kd050hdfia020_desc = { @@ -1599,6 +1811,7 @@ static const struct ili9881c_desc w552946aba_desc = { .mode = &w552946aba_default_mode, .mode_flags = MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_VIDEO_BURST | MIPI_DSI_MODE_LPM | MIPI_DSI_MODE_NO_EOT_PACKET, + .lanes = 4, }; static const struct ili9881c_desc am8001280g_desc = { @@ -1609,6 +1822,14 @@ static const struct ili9881c_desc am8001280g_desc = { MIPI_DSI_CLOCK_NON_CONTINUOUS | MIPI_DSI_MODE_LPM, }; +static const struct ili9881c_desc rpi_7inch_desc = { + .init = rpi_7inch_init, + .init_length = ARRAY_SIZE(rpi_7inch_init), + .mode = &rpi_7inch_default_mode, + .mode_flags = MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_LPM, + .lanes = 2, +}; + static const struct of_device_id ili9881c_of_match[] = { { .compatible = "bananapi,lhr050h41", .data = &lhr050h41_desc }, { .compatible = "feixin,k101-im2byl02", .data = &k101_im2byl02_desc }, @@ -1616,6 +1837,7 @@ static const struct of_device_id ili9881c_of_match[] = { { .compatible = "tdo,tl050hdv35", .data = &tl050hdv35_desc }, { .compatible = "wanchanglong,w552946aba", .data = &w552946aba_desc }, { .compatible = "ampire,am8001280g", .data = &am8001280g_desc }, + { .compatible = "raspberrypi,dsi-7inch", &rpi_7inch_desc }, { } }; MODULE_DEVICE_TABLE(of, ili9881c_of_match); diff --git a/drivers/gpu/drm/panel/panel-innolux-ej030na.c b/drivers/gpu/drm/panel/panel-innolux-ej030na.c index f85b7a4cbb42..b2309900873b 100644 --- a/drivers/gpu/drm/panel/panel-innolux-ej030na.c +++ b/drivers/gpu/drm/panel/panel-innolux-ej030na.c @@ -204,9 +204,11 @@ static int ej030na_probe(struct spi_device *spi) struct ej030na *priv; int err; - priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); - if (!priv) - return -ENOMEM; + priv = devm_drm_panel_alloc(dev, struct ej030na, panel, + &ej030na_funcs, + DRM_MODE_CONNECTOR_DPI); + if (IS_ERR(priv)) + return PTR_ERR(priv); priv->spi = spi; spi_set_drvdata(spi, priv); @@ -231,9 +233,6 @@ static int ej030na_probe(struct spi_device *spi) return dev_err_probe(dev, PTR_ERR(priv->reset_gpio), "Failed to get reset GPIO\n"); - drm_panel_init(&priv->panel, dev, &ej030na_funcs, - DRM_MODE_CONNECTOR_DPI); - err = drm_panel_of_backlight(&priv->panel); if (err) return err; diff --git a/drivers/gpu/drm/panel/panel-innolux-p079zca.c b/drivers/gpu/drm/panel/panel-innolux-p079zca.c index d95c0d4f3e35..80afeeab9475 100644 --- a/drivers/gpu/drm/panel/panel-innolux-p079zca.c +++ b/drivers/gpu/drm/panel/panel-innolux-p079zca.c @@ -382,9 +382,11 @@ static int innolux_panel_add(struct mipi_dsi_device *dsi, struct device *dev = &dsi->dev; int err, i; - innolux = devm_kzalloc(dev, sizeof(*innolux), GFP_KERNEL); - if (!innolux) - return -ENOMEM; + innolux = devm_drm_panel_alloc(dev, struct innolux_panel, base, + &innolux_panel_funcs, + DRM_MODE_CONNECTOR_DSI); + if (IS_ERR(innolux)) + return PTR_ERR(innolux); innolux->desc = desc; @@ -410,9 +412,6 @@ static int innolux_panel_add(struct mipi_dsi_device *dsi, innolux->enable_gpio = NULL; } - drm_panel_init(&innolux->base, dev, &innolux_panel_funcs, - DRM_MODE_CONNECTOR_DSI); - err = drm_panel_of_backlight(&innolux->base); if (err) return err; diff --git a/drivers/gpu/drm/panel/panel-jadard-jd9365da-h3.c b/drivers/gpu/drm/panel/panel-jadard-jd9365da-h3.c index eb0f8373258c..5c2530598ddb 100644 --- a/drivers/gpu/drm/panel/panel-jadard-jd9365da-h3.c +++ b/drivers/gpu/drm/panel/panel-jadard-jd9365da-h3.c @@ -1120,9 +1120,10 @@ static int jadard_dsi_probe(struct mipi_dsi_device *dsi) struct jadard *jadard; int ret; - jadard = devm_kzalloc(&dsi->dev, sizeof(*jadard), GFP_KERNEL); - if (!jadard) - return -ENOMEM; + jadard = devm_drm_panel_alloc(dev, struct jadard, panel, &jadard_funcs, + DRM_MODE_CONNECTOR_DSI); + if (IS_ERR(jadard)) + return PTR_ERR(jadard); desc = of_device_get_match_data(dev); dsi->mode_flags = MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_VIDEO_BURST | @@ -1148,9 +1149,6 @@ static int jadard_dsi_probe(struct mipi_dsi_device *dsi) return PTR_ERR(jadard->vccio); } - drm_panel_init(&jadard->panel, dev, &jadard_funcs, - DRM_MODE_CONNECTOR_DSI); - ret = of_drm_get_panel_orientation(dev->of_node, &jadard->orientation); if (ret < 0) return dev_err_probe(dev, ret, "failed to get orientation\n"); diff --git a/drivers/gpu/drm/panel/panel-jdi-fhd-r63452.c b/drivers/gpu/drm/panel/panel-jdi-fhd-r63452.c index 4eb71e85e9e9..cbe354b51bce 100644 --- a/drivers/gpu/drm/panel/panel-jdi-fhd-r63452.c +++ b/drivers/gpu/drm/panel/panel-jdi-fhd-r63452.c @@ -175,9 +175,11 @@ static int jdi_fhd_r63452_probe(struct mipi_dsi_device *dsi) struct jdi_fhd_r63452 *ctx; int ret; - ctx = devm_kzalloc(dev, sizeof(*ctx), GFP_KERNEL); - if (!ctx) - return -ENOMEM; + ctx = devm_drm_panel_alloc(dev, struct jdi_fhd_r63452, panel, + &jdi_fhd_r63452_panel_funcs, + DRM_MODE_CONNECTOR_DSI); + if (IS_ERR(ctx)) + return PTR_ERR(ctx); ctx->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_HIGH); if (IS_ERR(ctx->reset_gpio)) @@ -192,8 +194,6 @@ static int jdi_fhd_r63452_probe(struct mipi_dsi_device *dsi) dsi->mode_flags = MIPI_DSI_MODE_VIDEO_BURST | MIPI_DSI_CLOCK_NON_CONTINUOUS; - drm_panel_init(&ctx->panel, dev, &jdi_fhd_r63452_panel_funcs, - DRM_MODE_CONNECTOR_DSI); ctx->panel.prepare_prev_first = true; ret = drm_panel_of_backlight(&ctx->panel); diff --git a/drivers/gpu/drm/panel/panel-leadtek-ltk050h3146w.c b/drivers/gpu/drm/panel/panel-leadtek-ltk050h3146w.c index 77f74e6c467e..0856df5a6ee2 100644 --- a/drivers/gpu/drm/panel/panel-leadtek-ltk050h3146w.c +++ b/drivers/gpu/drm/panel/panel-leadtek-ltk050h3146w.c @@ -548,9 +548,11 @@ static int ltk050h3146w_probe(struct mipi_dsi_device *dsi) struct ltk050h3146w *ctx; int ret; - ctx = devm_kzalloc(dev, sizeof(*ctx), GFP_KERNEL); - if (!ctx) - return -ENOMEM; + ctx = devm_drm_panel_alloc(dev, struct ltk050h3146w, panel, + <k050h3146w_funcs, + DRM_MODE_CONNECTOR_DSI); + if (IS_ERR(ctx)) + return PTR_ERR(ctx); ctx->panel_desc = of_device_get_match_data(dev); if (!ctx->panel_desc) @@ -577,9 +579,6 @@ static int ltk050h3146w_probe(struct mipi_dsi_device *dsi) dsi->format = MIPI_DSI_FMT_RGB888; dsi->mode_flags = ctx->panel_desc->mode_flags; - drm_panel_init(&ctx->panel, &dsi->dev, <k050h3146w_funcs, - DRM_MODE_CONNECTOR_DSI); - ret = drm_panel_of_backlight(&ctx->panel); if (ret) return ret; diff --git a/drivers/gpu/drm/panel/panel-leadtek-ltk500hd1829.c b/drivers/gpu/drm/panel/panel-leadtek-ltk500hd1829.c index 6b18cf00fd4a..7f19fd5b8060 100644 --- a/drivers/gpu/drm/panel/panel-leadtek-ltk500hd1829.c +++ b/drivers/gpu/drm/panel/panel-leadtek-ltk500hd1829.c @@ -604,9 +604,11 @@ static int ltk500hd1829_probe(struct mipi_dsi_device *dsi) struct device *dev = &dsi->dev; int ret; - ctx = devm_kzalloc(&dsi->dev, sizeof(*ctx), GFP_KERNEL); - if (!ctx) - return -ENOMEM; + ctx = devm_drm_panel_alloc(dev, struct ltk500hd1829, panel, + <k500hd1829_funcs, + DRM_MODE_CONNECTOR_DSI); + if (IS_ERR(ctx)) + return PTR_ERR(ctx); ctx->panel_desc = of_device_get_match_data(dev); if (!ctx->panel_desc) @@ -643,9 +645,6 @@ static int ltk500hd1829_probe(struct mipi_dsi_device *dsi) dsi->mode_flags = MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_VIDEO_BURST | MIPI_DSI_MODE_LPM | MIPI_DSI_MODE_NO_EOT_PACKET; - drm_panel_init(&ctx->panel, &dsi->dev, <k500hd1829_funcs, - DRM_MODE_CONNECTOR_DSI); - ret = drm_panel_of_backlight(&ctx->panel); if (ret) return ret; diff --git a/drivers/gpu/drm/panel/panel-lg-lb035q02.c b/drivers/gpu/drm/panel/panel-lg-lb035q02.c index 9d0d4faa3f58..b2be6727bf73 100644 --- a/drivers/gpu/drm/panel/panel-lg-lb035q02.c +++ b/drivers/gpu/drm/panel/panel-lg-lb035q02.c @@ -178,9 +178,10 @@ static int lb035q02_probe(struct spi_device *spi) struct lb035q02_device *lcd; int ret; - lcd = devm_kzalloc(&spi->dev, sizeof(*lcd), GFP_KERNEL); - if (!lcd) - return -ENOMEM; + lcd = devm_drm_panel_alloc(&spi->dev, struct lb035q02_device, panel, + &lb035q02_funcs, DRM_MODE_CONNECTOR_DPI); + if (IS_ERR(lcd)) + return PTR_ERR(lcd); spi_set_drvdata(spi, lcd); lcd->spi = spi; @@ -195,9 +196,6 @@ static int lb035q02_probe(struct spi_device *spi) if (ret < 0) return ret; - drm_panel_init(&lcd->panel, &lcd->spi->dev, &lb035q02_funcs, - DRM_MODE_CONNECTOR_DPI); - drm_panel_add(&lcd->panel); return 0; diff --git a/drivers/gpu/drm/panel/panel-lg-lg4573.c b/drivers/gpu/drm/panel/panel-lg-lg4573.c index cf246d15b7b6..dec619902c15 100644 --- a/drivers/gpu/drm/panel/panel-lg-lg4573.c +++ b/drivers/gpu/drm/panel/panel-lg-lg4573.c @@ -243,9 +243,11 @@ static int lg4573_probe(struct spi_device *spi) struct lg4573 *ctx; int ret; - ctx = devm_kzalloc(&spi->dev, sizeof(*ctx), GFP_KERNEL); - if (!ctx) - return -ENOMEM; + ctx = devm_drm_panel_alloc(&spi->dev, struct lg4573, panel, + &lg4573_drm_funcs, + DRM_MODE_CONNECTOR_DPI); + if (IS_ERR(ctx)) + return PTR_ERR(ctx); ctx->spi = spi; @@ -258,9 +260,6 @@ static int lg4573_probe(struct spi_device *spi) return ret; } - drm_panel_init(&ctx->panel, &spi->dev, &lg4573_drm_funcs, - DRM_MODE_CONNECTOR_DPI); - drm_panel_add(&ctx->panel); return 0; diff --git a/drivers/gpu/drm/panel/panel-lincolntech-lcd197.c b/drivers/gpu/drm/panel/panel-lincolntech-lcd197.c index 032c542aab0f..24b34443ace0 100644 --- a/drivers/gpu/drm/panel/panel-lincolntech-lcd197.c +++ b/drivers/gpu/drm/panel/panel-lincolntech-lcd197.c @@ -190,9 +190,11 @@ static int lincoln_lcd197_panel_probe(struct mipi_dsi_device *dsi) dsi->mode_flags = (MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_VIDEO_BURST); - lcd = devm_kzalloc(&dsi->dev, sizeof(*lcd), GFP_KERNEL); - if (!lcd) - return -ENOMEM; + lcd = devm_drm_panel_alloc(dev, struct lincoln_lcd197_panel, panel, + &lincoln_lcd197_panel_funcs, + DRM_MODE_CONNECTOR_DSI); + if (IS_ERR(lcd)) + return PTR_ERR(lcd); mipi_dsi_set_drvdata(dsi, lcd); lcd->dsi = dsi; @@ -214,9 +216,6 @@ static int lincoln_lcd197_panel_probe(struct mipi_dsi_device *dsi) return dev_err_probe(dev, PTR_ERR(lcd->reset_gpio), "failed to get reset gpio"); - drm_panel_init(&lcd->panel, dev, - &lincoln_lcd197_panel_funcs, DRM_MODE_CONNECTOR_DSI); - err = drm_panel_of_backlight(&lcd->panel); if (err) return err; diff --git a/drivers/gpu/drm/panel/panel-lvds.c b/drivers/gpu/drm/panel/panel-lvds.c index ba6c015aabba..23fd535d8f47 100644 --- a/drivers/gpu/drm/panel/panel-lvds.c +++ b/drivers/gpu/drm/panel/panel-lvds.c @@ -164,9 +164,11 @@ static int panel_lvds_probe(struct platform_device *pdev) struct panel_lvds *lvds; int ret; - lvds = devm_kzalloc(&pdev->dev, sizeof(*lvds), GFP_KERNEL); - if (!lvds) - return -ENOMEM; + lvds = devm_drm_panel_alloc(&pdev->dev, struct panel_lvds, panel, + &panel_lvds_funcs, + DRM_MODE_CONNECTOR_LVDS); + if (IS_ERR(lvds)) + return PTR_ERR(lvds); lvds->dev = &pdev->dev; @@ -214,10 +216,6 @@ static int panel_lvds_probe(struct platform_device *pdev) * driver. */ - /* Register the panel. */ - drm_panel_init(&lvds->panel, lvds->dev, &panel_lvds_funcs, - DRM_MODE_CONNECTOR_LVDS); - ret = drm_panel_of_backlight(&lvds->panel); if (ret) return ret; diff --git a/drivers/gpu/drm/panel/panel-magnachip-d53e6ea8966.c b/drivers/gpu/drm/panel/panel-magnachip-d53e6ea8966.c index 799c2161fc85..cde168ec631c 100644 --- a/drivers/gpu/drm/panel/panel-magnachip-d53e6ea8966.c +++ b/drivers/gpu/drm/panel/panel-magnachip-d53e6ea8966.c @@ -370,9 +370,11 @@ static int d53e6ea8966_probe(struct spi_device *spi) .node = NULL, }; - db = devm_kzalloc(dev, sizeof(*db), GFP_KERNEL); - if (!db) - return -ENOMEM; + db = devm_drm_panel_alloc(dev, struct d53e6ea8966, panel, + &d53e6ea8966_panel_funcs, + DRM_MODE_CONNECTOR_DSI); + if (IS_ERR(db)) + return PTR_ERR(db); spi_set_drvdata(spi, db); @@ -425,9 +427,6 @@ static int d53e6ea8966_probe(struct spi_device *spi) db->dsi_dev->mode_flags = MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_VIDEO_BURST | MIPI_DSI_MODE_LPM | MIPI_DSI_MODE_NO_EOT_PACKET; - drm_panel_init(&db->panel, dev, &d53e6ea8966_panel_funcs, - DRM_MODE_CONNECTOR_DSI); - if (db->panel_info->backlight_register) { ret = db->panel_info->backlight_register(db); if (ret < 0) diff --git a/drivers/gpu/drm/panel/panel-mantix-mlaf057we51.c b/drivers/gpu/drm/panel/panel-mantix-mlaf057we51.c index 4db852ffb0f6..55664f5d5aa5 100644 --- a/drivers/gpu/drm/panel/panel-mantix-mlaf057we51.c +++ b/drivers/gpu/drm/panel/panel-mantix-mlaf057we51.c @@ -234,9 +234,11 @@ static int mantix_probe(struct mipi_dsi_device *dsi) struct mantix *ctx; int ret; - ctx = devm_kzalloc(dev, sizeof(*ctx), GFP_KERNEL); - if (!ctx) - return -ENOMEM; + ctx = devm_drm_panel_alloc(dev, struct mantix, panel, &mantix_drm_funcs, + DRM_MODE_CONNECTOR_DSI); + if (IS_ERR(ctx)) + return PTR_ERR(ctx); + ctx->default_mode = of_device_get_match_data(dev); ctx->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_HIGH); @@ -271,9 +273,6 @@ static int mantix_probe(struct mipi_dsi_device *dsi) if (IS_ERR(ctx->vddi)) return dev_err_probe(dev, PTR_ERR(ctx->vddi), "Failed to request vddi regulator\n"); - drm_panel_init(&ctx->panel, dev, &mantix_drm_funcs, - DRM_MODE_CONNECTOR_DSI); - ret = drm_panel_of_backlight(&ctx->panel); if (ret) return ret; diff --git a/drivers/gpu/drm/panel/panel-nec-nl8048hl11.c b/drivers/gpu/drm/panel/panel-nec-nl8048hl11.c index 81c5c541a351..d5c7210de4af 100644 --- a/drivers/gpu/drm/panel/panel-nec-nl8048hl11.c +++ b/drivers/gpu/drm/panel/panel-nec-nl8048hl11.c @@ -178,9 +178,10 @@ static int nl8048_probe(struct spi_device *spi) struct nl8048_panel *lcd; int ret; - lcd = devm_kzalloc(&spi->dev, sizeof(*lcd), GFP_KERNEL); - if (!lcd) - return -ENOMEM; + lcd = devm_drm_panel_alloc(&spi->dev, struct nl8048_panel, panel, + &nl8048_funcs, DRM_MODE_CONNECTOR_DPI); + if (IS_ERR(lcd)) + return PTR_ERR(lcd); spi_set_drvdata(spi, lcd); lcd->spi = spi; @@ -204,9 +205,6 @@ static int nl8048_probe(struct spi_device *spi) if (ret < 0) return ret; - drm_panel_init(&lcd->panel, &lcd->spi->dev, &nl8048_funcs, - DRM_MODE_CONNECTOR_DPI); - drm_panel_add(&lcd->panel); return 0; diff --git a/drivers/gpu/drm/panel/panel-newvision-nv3051d.c b/drivers/gpu/drm/panel/panel-newvision-nv3051d.c index b6429795e8f5..22560384e48e 100644 --- a/drivers/gpu/drm/panel/panel-newvision-nv3051d.c +++ b/drivers/gpu/drm/panel/panel-newvision-nv3051d.c @@ -361,9 +361,11 @@ static int panel_nv3051d_probe(struct mipi_dsi_device *dsi) struct panel_nv3051d *ctx; int ret; - ctx = devm_kzalloc(dev, sizeof(*ctx), GFP_KERNEL); - if (!ctx) - return -ENOMEM; + ctx = devm_drm_panel_alloc(dev, struct panel_nv3051d, panel, + &panel_nv3051d_funcs, + DRM_MODE_CONNECTOR_DSI); + if (IS_ERR(ctx)) + return PTR_ERR(ctx); ctx->dev = dev; @@ -391,9 +393,6 @@ static int panel_nv3051d_probe(struct mipi_dsi_device *dsi) dsi->format = MIPI_DSI_FMT_RGB888; dsi->mode_flags = ctx->panel_info->mode_flags; - drm_panel_init(&ctx->panel, &dsi->dev, &panel_nv3051d_funcs, - DRM_MODE_CONNECTOR_DSI); - ret = drm_panel_of_backlight(&ctx->panel); if (ret) return ret; diff --git a/drivers/gpu/drm/panel/panel-newvision-nv3052c.c b/drivers/gpu/drm/panel/panel-newvision-nv3052c.c index 06e16a7c14a7..0db9cadd868e 100644 --- a/drivers/gpu/drm/panel/panel-newvision-nv3052c.c +++ b/drivers/gpu/drm/panel/panel-newvision-nv3052c.c @@ -777,9 +777,10 @@ static int nv3052c_probe(struct spi_device *spi) struct nv3052c *priv; int err; - priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); - if (!priv) - return -ENOMEM; + priv = devm_drm_panel_alloc(dev, struct nv3052c, panel, &nv3052c_funcs, + DRM_MODE_CONNECTOR_DPI); + if (IS_ERR(priv)) + return PTR_ERR(priv); priv->dev = dev; @@ -803,9 +804,6 @@ static int nv3052c_probe(struct spi_device *spi) spi_set_drvdata(spi, priv); - drm_panel_init(&priv->panel, dev, &nv3052c_funcs, - DRM_MODE_CONNECTOR_DPI); - err = drm_panel_of_backlight(&priv->panel); if (err) return dev_err_probe(dev, err, "Failed to attach backlight\n"); diff --git a/drivers/gpu/drm/panel/panel-novatek-nt35510.c b/drivers/gpu/drm/panel/panel-novatek-nt35510.c index 549b86f2cc28..3189d89c7ca0 100644 --- a/drivers/gpu/drm/panel/panel-novatek-nt35510.c +++ b/drivers/gpu/drm/panel/panel-novatek-nt35510.c @@ -1087,9 +1087,12 @@ static int nt35510_probe(struct mipi_dsi_device *dsi) struct nt35510 *nt; int ret; - nt = devm_kzalloc(dev, sizeof(struct nt35510), GFP_KERNEL); - if (!nt) - return -ENOMEM; + nt = devm_drm_panel_alloc(dev, struct nt35510, panel, + &nt35510_drm_funcs, + DRM_MODE_CONNECTOR_DSI); + if (IS_ERR(nt)) + return PTR_ERR(nt); + mipi_dsi_set_drvdata(dsi, nt); nt->dev = dev; @@ -1142,9 +1145,6 @@ static int nt35510_probe(struct mipi_dsi_device *dsi) return PTR_ERR(nt->reset_gpio); } - drm_panel_init(&nt->panel, dev, &nt35510_drm_funcs, - DRM_MODE_CONNECTOR_DSI); - /* * First, try to locate an external backlight (such as on GPIO) * if this fails, assume we will want to use the internal backlight diff --git a/drivers/gpu/drm/panel/panel-novatek-nt35560.c b/drivers/gpu/drm/panel/panel-novatek-nt35560.c index 5bbea734123b..98f0782c8411 100644 --- a/drivers/gpu/drm/panel/panel-novatek-nt35560.c +++ b/drivers/gpu/drm/panel/panel-novatek-nt35560.c @@ -456,9 +456,12 @@ static int nt35560_probe(struct mipi_dsi_device *dsi) struct nt35560 *nt; int ret; - nt = devm_kzalloc(dev, sizeof(struct nt35560), GFP_KERNEL); - if (!nt) - return -ENOMEM; + nt = devm_drm_panel_alloc(dev, struct nt35560, panel, + &nt35560_drm_funcs, + DRM_MODE_CONNECTOR_DSI); + if (IS_ERR(nt)) + return PTR_ERR(nt); + nt->video_mode = of_property_read_bool(dev->of_node, "enforce-video-mode"); @@ -502,9 +505,6 @@ static int nt35560_probe(struct mipi_dsi_device *dsi) return dev_err_probe(dev, PTR_ERR(nt->reset_gpio), "failed to request GPIO\n"); - drm_panel_init(&nt->panel, dev, &nt35560_drm_funcs, - DRM_MODE_CONNECTOR_DSI); - nt->panel.backlight = devm_backlight_device_register(dev, "nt35560", dev, nt, &nt35560_bl_ops, &nt35560_bl_props); if (IS_ERR(nt->panel.backlight)) diff --git a/drivers/gpu/drm/panel/panel-novatek-nt35950.c b/drivers/gpu/drm/panel/panel-novatek-nt35950.c index 08b22b592ab0..94aa6489d99f 100644 --- a/drivers/gpu/drm/panel/panel-novatek-nt35950.c +++ b/drivers/gpu/drm/panel/panel-novatek-nt35950.c @@ -449,9 +449,10 @@ static int nt35950_probe(struct mipi_dsi_device *dsi) const struct mipi_dsi_device_info *info; int i, num_dsis = 1, ret; - nt = devm_kzalloc(dev, sizeof(*nt), GFP_KERNEL); - if (!nt) - return -ENOMEM; + nt = devm_drm_panel_alloc(dev, struct nt35950, panel, &nt35950_panel_funcs, + DRM_MODE_CONNECTOR_DSI); + if (IS_ERR(nt)) + return PTR_ERR(nt); ret = nt35950_sharp_init_vregs(nt, dev); if (ret) @@ -491,9 +492,6 @@ static int nt35950_probe(struct mipi_dsi_device *dsi) nt->dsi[0] = dsi; mipi_dsi_set_drvdata(dsi, nt); - drm_panel_init(&nt->panel, dev, &nt35950_panel_funcs, - DRM_MODE_CONNECTOR_DSI); - ret = drm_panel_of_backlight(&nt->panel); if (ret) { if (num_dsis == 2) diff --git a/drivers/gpu/drm/panel/panel-novatek-nt36523.c b/drivers/gpu/drm/panel/panel-novatek-nt36523.c index 116d67bfa114..32cf64c7c18b 100644 --- a/drivers/gpu/drm/panel/panel-novatek-nt36523.c +++ b/drivers/gpu/drm/panel/panel-novatek-nt36523.c @@ -1171,9 +1171,11 @@ static int nt36523_probe(struct mipi_dsi_device *dsi) const struct mipi_dsi_device_info *info; int i, ret; - pinfo = devm_kzalloc(dev, sizeof(*pinfo), GFP_KERNEL); - if (!pinfo) - return -ENOMEM; + pinfo = devm_drm_panel_alloc(dev, struct panel_info, panel, + &nt36523_panel_funcs, + DRM_MODE_CONNECTOR_DSI); + if (IS_ERR(pinfo)) + return PTR_ERR(pinfo); pinfo->vddio = devm_regulator_get(dev, "vddio"); if (IS_ERR(pinfo->vddio)) @@ -1211,7 +1213,6 @@ static int nt36523_probe(struct mipi_dsi_device *dsi) pinfo->dsi[0] = dsi; mipi_dsi_set_drvdata(dsi, pinfo); - drm_panel_init(&pinfo->panel, dev, &nt36523_panel_funcs, DRM_MODE_CONNECTOR_DSI); ret = of_drm_get_panel_orientation(dev->of_node, &pinfo->orientation); if (ret < 0) { diff --git a/drivers/gpu/drm/panel/panel-novatek-nt36672e.c b/drivers/gpu/drm/panel/panel-novatek-nt36672e.c index 8c9e04207ba9..c5e00eb55722 100644 --- a/drivers/gpu/drm/panel/panel-novatek-nt36672e.c +++ b/drivers/gpu/drm/panel/panel-novatek-nt36672e.c @@ -522,9 +522,11 @@ static int nt36672e_panel_probe(struct mipi_dsi_device *dsi) struct nt36672e_panel *ctx; int i, ret = 0; - ctx = devm_kzalloc(dev, sizeof(*ctx), GFP_KERNEL); - if (!ctx) - return -ENOMEM; + ctx = devm_drm_panel_alloc(dev, struct nt36672e_panel, panel, + &nt36672e_drm_funcs, + DRM_MODE_CONNECTOR_DSI); + if (IS_ERR(ctx)) + return PTR_ERR(ctx); ctx->desc = of_device_get_match_data(dev); if (!ctx->desc) { @@ -553,8 +555,6 @@ static int nt36672e_panel_probe(struct mipi_dsi_device *dsi) dsi->format = ctx->desc->format; dsi->mode_flags = ctx->desc->mode_flags; - drm_panel_init(&ctx->panel, dev, &nt36672e_drm_funcs, DRM_MODE_CONNECTOR_DSI); - ret = drm_panel_of_backlight(&ctx->panel); if (ret) return dev_err_probe(dev, ret, "Failed to get backlight\n"); diff --git a/drivers/gpu/drm/panel/panel-novatek-nt39016.c b/drivers/gpu/drm/panel/panel-novatek-nt39016.c index 9fa7654e2b67..a629976bae54 100644 --- a/drivers/gpu/drm/panel/panel-novatek-nt39016.c +++ b/drivers/gpu/drm/panel/panel-novatek-nt39016.c @@ -246,9 +246,10 @@ static int nt39016_probe(struct spi_device *spi) struct nt39016 *panel; int err; - panel = devm_kzalloc(dev, sizeof(*panel), GFP_KERNEL); - if (!panel) - return -ENOMEM; + panel = devm_drm_panel_alloc(dev, struct nt39016, drm_panel, &nt39016_funcs, + DRM_MODE_CONNECTOR_DPI); + if (IS_ERR(panel)) + return PTR_ERR(panel); spi_set_drvdata(spi, panel); @@ -279,9 +280,6 @@ static int nt39016_probe(struct spi_device *spi) return PTR_ERR(panel->map); } - drm_panel_init(&panel->drm_panel, dev, &nt39016_funcs, - DRM_MODE_CONNECTOR_DPI); - err = drm_panel_of_backlight(&panel->drm_panel); if (err) return dev_err_probe(dev, err, "Failed to get backlight handle\n"); diff --git a/drivers/gpu/drm/panel/panel-olimex-lcd-olinuxino.c b/drivers/gpu/drm/panel/panel-olimex-lcd-olinuxino.c index 94ae8c8270b8..66f99982f360 100644 --- a/drivers/gpu/drm/panel/panel-olimex-lcd-olinuxino.c +++ b/drivers/gpu/drm/panel/panel-olimex-lcd-olinuxino.c @@ -175,9 +175,11 @@ static int lcd_olinuxino_probe(struct i2c_client *client) I2C_FUNC_SMBUS_READ_I2C_BLOCK)) return -ENODEV; - lcd = devm_kzalloc(dev, sizeof(*lcd), GFP_KERNEL); - if (!lcd) - return -ENOMEM; + lcd = devm_drm_panel_alloc(dev, struct lcd_olinuxino, panel, + &lcd_olinuxino_funcs, + DRM_MODE_CONNECTOR_DPI); + if (IS_ERR(lcd)) + return PTR_ERR(lcd); i2c_set_clientdata(client, lcd); lcd->dev = dev; @@ -234,9 +236,6 @@ static int lcd_olinuxino_probe(struct i2c_client *client) if (IS_ERR(lcd->enable_gpio)) return PTR_ERR(lcd->enable_gpio); - drm_panel_init(&lcd->panel, dev, &lcd_olinuxino_funcs, - DRM_MODE_CONNECTOR_DPI); - ret = drm_panel_of_backlight(&lcd->panel); if (ret) return ret; diff --git a/drivers/gpu/drm/panel/panel-orisetech-ota5601a.c b/drivers/gpu/drm/panel/panel-orisetech-ota5601a.c index fc87f61d4400..3231e84dc66c 100644 --- a/drivers/gpu/drm/panel/panel-orisetech-ota5601a.c +++ b/drivers/gpu/drm/panel/panel-orisetech-ota5601a.c @@ -237,9 +237,11 @@ static int ota5601a_probe(struct spi_device *spi) struct ota5601a *panel; int err; - panel = devm_kzalloc(dev, sizeof(*panel), GFP_KERNEL); - if (!panel) - return -ENOMEM; + panel = devm_drm_panel_alloc(dev, struct ota5601a, drm_panel, + &ota5601a_funcs, + DRM_MODE_CONNECTOR_DPI); + if (IS_ERR(panel)) + return PTR_ERR(panel); spi_set_drvdata(spi, panel); @@ -273,9 +275,6 @@ static int ota5601a_probe(struct spi_device *spi) return PTR_ERR(panel->map); } - drm_panel_init(&panel->drm_panel, dev, &ota5601a_funcs, - DRM_MODE_CONNECTOR_DPI); - err = drm_panel_of_backlight(&panel->drm_panel); if (err) { if (err != -EPROBE_DEFER) diff --git a/drivers/gpu/drm/panel/panel-orisetech-otm8009a.c b/drivers/gpu/drm/panel/panel-orisetech-otm8009a.c index 87bbb25d119a..a0f58c3b73f6 100644 --- a/drivers/gpu/drm/panel/panel-orisetech-otm8009a.c +++ b/drivers/gpu/drm/panel/panel-orisetech-otm8009a.c @@ -424,9 +424,11 @@ static int otm8009a_probe(struct mipi_dsi_device *dsi) struct otm8009a *ctx; int ret; - ctx = devm_kzalloc(dev, sizeof(*ctx), GFP_KERNEL); - if (!ctx) - return -ENOMEM; + ctx = devm_drm_panel_alloc(dev, struct otm8009a, panel, + &otm8009a_drm_funcs, + DRM_MODE_CONNECTOR_DSI); + if (IS_ERR(ctx)) + return PTR_ERR(ctx); ctx->reset_gpio = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_LOW); if (IS_ERR(ctx->reset_gpio)) { @@ -451,9 +453,6 @@ static int otm8009a_probe(struct mipi_dsi_device *dsi) dsi->mode_flags = MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_VIDEO_BURST | MIPI_DSI_MODE_LPM | MIPI_DSI_CLOCK_NON_CONTINUOUS; - drm_panel_init(&ctx->panel, dev, &otm8009a_drm_funcs, - DRM_MODE_CONNECTOR_DSI); - ctx->bl_dev = devm_backlight_device_register(dev, dev_name(dev), dev, ctx, &otm8009a_backlight_ops, diff --git a/drivers/gpu/drm/panel/panel-raydium-rm67191.c b/drivers/gpu/drm/panel/panel-raydium-rm67191.c index b2029e035635..2af6aa47a551 100644 --- a/drivers/gpu/drm/panel/panel-raydium-rm67191.c +++ b/drivers/gpu/drm/panel/panel-raydium-rm67191.c @@ -527,9 +527,11 @@ static int rad_panel_probe(struct mipi_dsi_device *dsi) int ret; u32 video_mode; - panel = devm_kzalloc(&dsi->dev, sizeof(*panel), GFP_KERNEL); - if (!panel) - return -ENOMEM; + panel = devm_drm_panel_alloc(dev, struct rad_panel, panel, + &rad_panel_funcs, + DRM_MODE_CONNECTOR_DSI); + if (IS_ERR(panel)) + return PTR_ERR(panel); mipi_dsi_set_drvdata(dsi, panel); @@ -586,8 +588,6 @@ static int rad_panel_probe(struct mipi_dsi_device *dsi) if (ret) return ret; - drm_panel_init(&panel->panel, dev, &rad_panel_funcs, - DRM_MODE_CONNECTOR_DSI); dev_set_drvdata(dev, panel); drm_panel_add(&panel->panel); diff --git a/drivers/gpu/drm/panel/panel-raydium-rm67200.c b/drivers/gpu/drm/panel/panel-raydium-rm67200.c index 64b685dc11f6..1da8225e6f7a 100644 --- a/drivers/gpu/drm/panel/panel-raydium-rm67200.c +++ b/drivers/gpu/drm/panel/panel-raydium-rm67200.c @@ -391,9 +391,11 @@ static int raydium_rm67200_probe(struct mipi_dsi_device *dsi) struct raydium_rm67200 *ctx; int ret = 0; - ctx = devm_kzalloc(dev, sizeof(*ctx), GFP_KERNEL); - if (!ctx) - return -ENOMEM; + ctx = devm_drm_panel_alloc(dev, struct raydium_rm67200, panel, + &raydium_rm67200_funcs, + DRM_MODE_CONNECTOR_DSI); + if (IS_ERR(ctx)) + return PTR_ERR(ctx); ctx->panel_info = device_get_match_data(dev); if (!ctx->panel_info) @@ -421,9 +423,6 @@ static int raydium_rm67200_probe(struct mipi_dsi_device *dsi) MIPI_DSI_MODE_LPM; ctx->panel.prepare_prev_first = true; - drm_panel_init(&ctx->panel, dev, &raydium_rm67200_funcs, - DRM_MODE_CONNECTOR_DSI); - ret = drm_panel_of_backlight(&ctx->panel); if (ret) return ret; diff --git a/drivers/gpu/drm/panel/panel-raydium-rm68200.c b/drivers/gpu/drm/panel/panel-raydium-rm68200.c index 7b7fe987e292..669b5f5c1ad9 100644 --- a/drivers/gpu/drm/panel/panel-raydium-rm68200.c +++ b/drivers/gpu/drm/panel/panel-raydium-rm68200.c @@ -327,9 +327,11 @@ static int rm68200_probe(struct mipi_dsi_device *dsi) struct rm68200 *ctx; int ret; - ctx = devm_kzalloc(dev, sizeof(*ctx), GFP_KERNEL); - if (!ctx) - return -ENOMEM; + ctx = devm_drm_panel_alloc(dev, struct rm68200, panel, + &rm68200_drm_funcs, + DRM_MODE_CONNECTOR_DSI); + if (IS_ERR(ctx)) + return PTR_ERR(ctx); ctx->reset_gpio = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_LOW); if (IS_ERR(ctx->reset_gpio)) { @@ -355,9 +357,6 @@ static int rm68200_probe(struct mipi_dsi_device *dsi) dsi->mode_flags = MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_VIDEO_BURST | MIPI_DSI_MODE_LPM | MIPI_DSI_CLOCK_NON_CONTINUOUS; - drm_panel_init(&ctx->panel, dev, &rm68200_drm_funcs, - DRM_MODE_CONNECTOR_DSI); - ret = drm_panel_of_backlight(&ctx->panel); if (ret) return ret; diff --git a/drivers/gpu/drm/panel/panel-raydium-rm692e5.c b/drivers/gpu/drm/panel/panel-raydium-rm692e5.c index ea1b728e85a2..8e9484768657 100644 --- a/drivers/gpu/drm/panel/panel-raydium-rm692e5.c +++ b/drivers/gpu/drm/panel/panel-raydium-rm692e5.c @@ -281,9 +281,11 @@ static int rm692e5_probe(struct mipi_dsi_device *dsi) struct rm692e5_panel *ctx; int ret; - ctx = devm_kzalloc(dev, sizeof(*ctx), GFP_KERNEL); - if (!ctx) - return -ENOMEM; + ctx = devm_drm_panel_alloc(dev, struct rm692e5_panel, panel, + &rm692e5_panel_funcs, + DRM_MODE_CONNECTOR_DSI); + if (IS_ERR(ctx)) + return PTR_ERR(ctx); ctx->supplies[0].supply = "vddio"; ctx->supplies[1].supply = "dvdd"; @@ -306,8 +308,6 @@ static int rm692e5_probe(struct mipi_dsi_device *dsi) dsi->mode_flags = MIPI_DSI_MODE_NO_EOT_PACKET | MIPI_DSI_CLOCK_NON_CONTINUOUS; - drm_panel_init(&ctx->panel, dev, &rm692e5_panel_funcs, - DRM_MODE_CONNECTOR_DSI); ctx->panel.prepare_prev_first = true; ctx->panel.backlight = rm692e5_create_backlight(dsi); diff --git a/drivers/gpu/drm/panel/panel-raydium-rm69380.c b/drivers/gpu/drm/panel/panel-raydium-rm69380.c index d3071c01aaea..86769cadec97 100644 --- a/drivers/gpu/drm/panel/panel-raydium-rm69380.c +++ b/drivers/gpu/drm/panel/panel-raydium-rm69380.c @@ -208,9 +208,11 @@ static int rm69380_probe(struct mipi_dsi_device *dsi) struct device_node *dsi_sec; int ret, i; - ctx = devm_kzalloc(dev, sizeof(*ctx), GFP_KERNEL); - if (!ctx) - return -ENOMEM; + ctx = devm_drm_panel_alloc(dev, struct rm69380_panel, panel, + &rm69380_panel_funcs, + DRM_MODE_CONNECTOR_DSI); + if (IS_ERR(ctx)) + return PTR_ERR(ctx); ctx->supplies[0].supply = "vddio"; ctx->supplies[1].supply = "avdd"; @@ -248,8 +250,6 @@ static int rm69380_probe(struct mipi_dsi_device *dsi) ctx->dsi[0] = dsi; mipi_dsi_set_drvdata(dsi, ctx); - drm_panel_init(&ctx->panel, dev, &rm69380_panel_funcs, - DRM_MODE_CONNECTOR_DSI); ctx->panel.prepare_prev_first = true; ctx->panel.backlight = rm69380_create_backlight(dsi); diff --git a/drivers/gpu/drm/panel/panel-renesas-r61307.c b/drivers/gpu/drm/panel/panel-renesas-r61307.c new file mode 100644 index 000000000000..319415194839 --- /dev/null +++ b/drivers/gpu/drm/panel/panel-renesas-r61307.c @@ -0,0 +1,325 @@ +// SPDX-License-Identifier: GPL-2.0 + +#include <linux/array_size.h> +#include <linux/delay.h> +#include <linux/err.h> +#include <linux/gpio/consumer.h> +#include <linux/mod_devicetable.h> +#include <linux/module.h> +#include <linux/property.h> +#include <linux/regulator/consumer.h> + +#include <video/mipi_display.h> + +#include <drm/drm_mipi_dsi.h> +#include <drm/drm_modes.h> +#include <drm/drm_panel.h> + +#define R61307_MACP 0xb0 /* Manufacturer CMD Protect */ +#define R61307_MACP_ON 0x03 +#define R61307_MACP_OFF 0x04 + +#define R61307_INVERSION 0xc1 +#define R61307_GAMMA_SET_A 0xc8 /* Gamma Setting A */ +#define R61307_GAMMA_SET_B 0xc9 /* Gamma Setting B */ +#define R61307_GAMMA_SET_C 0xca /* Gamma Setting C */ +#define R61307_CONTRAST_SET 0xcc + +struct renesas_r61307 { + struct drm_panel panel; + struct mipi_dsi_device *dsi; + + struct regulator *vcc_supply; + struct regulator *iovcc_supply; + + struct gpio_desc *reset_gpio; + + bool prepared; + + bool dig_cont_adj; + bool inversion; + u32 gamma; +}; + +static const u8 gamma_setting[][25] = { + { /* sentinel */ }, + { + R61307_GAMMA_SET_A, + 0x00, 0x06, 0x0a, 0x0f, + 0x14, 0x1f, 0x1f, 0x17, + 0x12, 0x0c, 0x09, 0x06, + 0x00, 0x06, 0x0a, 0x0f, + 0x14, 0x1f, 0x1f, 0x17, + 0x12, 0x0c, 0x09, 0x06 + }, + { + R61307_GAMMA_SET_A, + 0x00, 0x05, 0x0b, 0x0f, + 0x11, 0x1d, 0x20, 0x18, + 0x18, 0x09, 0x07, 0x06, + 0x00, 0x05, 0x0b, 0x0f, + 0x11, 0x1d, 0x20, 0x18, + 0x18, 0x09, 0x07, 0x06 + }, + { + R61307_GAMMA_SET_A, + 0x0b, 0x0d, 0x10, 0x14, + 0x13, 0x1d, 0x20, 0x18, + 0x12, 0x09, 0x07, 0x06, + 0x0a, 0x0c, 0x10, 0x14, + 0x13, 0x1d, 0x20, 0x18, + 0x12, 0x09, 0x07, 0x06 + }, +}; + +static inline struct renesas_r61307 *to_renesas_r61307(struct drm_panel *panel) +{ + return container_of(panel, struct renesas_r61307, panel); +} + +static void renesas_r61307_reset(struct renesas_r61307 *priv) +{ + gpiod_set_value_cansleep(priv->reset_gpio, 1); + usleep_range(10000, 11000); + gpiod_set_value_cansleep(priv->reset_gpio, 0); + usleep_range(2000, 3000); +} + +static int renesas_r61307_prepare(struct drm_panel *panel) +{ + struct renesas_r61307 *priv = to_renesas_r61307(panel); + struct device *dev = &priv->dsi->dev; + int ret; + + if (priv->prepared) + return 0; + + ret = regulator_enable(priv->vcc_supply); + if (ret) { + dev_err(dev, "failed to enable vcc power supply\n"); + return ret; + } + + usleep_range(2000, 3000); + + ret = regulator_enable(priv->iovcc_supply); + if (ret) { + dev_err(dev, "failed to enable iovcc power supply\n"); + return ret; + } + + usleep_range(2000, 3000); + + renesas_r61307_reset(priv); + + priv->prepared = true; + return 0; +} + +static int renesas_r61307_enable(struct drm_panel *panel) +{ + struct renesas_r61307 *priv = to_renesas_r61307(panel); + struct mipi_dsi_multi_context ctx = { .dsi = priv->dsi }; + + mipi_dsi_dcs_exit_sleep_mode_multi(&ctx); + mipi_dsi_msleep(&ctx, 80); + + mipi_dsi_dcs_write_seq_multi(&ctx, MIPI_DCS_SET_ADDRESS_MODE, 0x00); + mipi_dsi_msleep(&ctx, 20); + + mipi_dsi_dcs_set_pixel_format_multi(&ctx, MIPI_DCS_PIXEL_FMT_24BIT << 4); + + /* MACP Off */ + mipi_dsi_generic_write_seq_multi(&ctx, R61307_MACP, R61307_MACP_OFF); + + if (priv->dig_cont_adj) + mipi_dsi_generic_write_seq_multi(&ctx, R61307_CONTRAST_SET, + 0xdc, 0xb4, 0xff); + + if (priv->gamma) + mipi_dsi_generic_write_multi(&ctx, gamma_setting[priv->gamma], + sizeof(gamma_setting[priv->gamma])); + + if (priv->inversion) + mipi_dsi_generic_write_seq_multi(&ctx, R61307_INVERSION, + 0x00, 0x50, 0x03, 0x22, + 0x16, 0x06, 0x60, 0x11); + else + mipi_dsi_generic_write_seq_multi(&ctx, R61307_INVERSION, + 0x00, 0x10, 0x03, 0x22, + 0x16, 0x06, 0x60, 0x01); + + /* MACP On */ + mipi_dsi_generic_write_seq_multi(&ctx, R61307_MACP, R61307_MACP_ON); + + mipi_dsi_dcs_set_display_on_multi(&ctx); + mipi_dsi_msleep(&ctx, 50); + + return 0; +} + +static int renesas_r61307_disable(struct drm_panel *panel) +{ + struct renesas_r61307 *priv = to_renesas_r61307(panel); + struct mipi_dsi_multi_context ctx = { .dsi = priv->dsi }; + + mipi_dsi_dcs_set_display_off_multi(&ctx); + mipi_dsi_msleep(&ctx, 100); + mipi_dsi_dcs_enter_sleep_mode_multi(&ctx); + + return 0; +} + +static int renesas_r61307_unprepare(struct drm_panel *panel) +{ + struct renesas_r61307 *priv = to_renesas_r61307(panel); + + if (!priv->prepared) + return 0; + + usleep_range(10000, 11000); + + gpiod_set_value_cansleep(priv->reset_gpio, 1); + usleep_range(5000, 6000); + + regulator_disable(priv->iovcc_supply); + usleep_range(2000, 3000); + regulator_disable(priv->vcc_supply); + + priv->prepared = false; + return 0; +} + +static const struct drm_display_mode renesas_r61307_mode = { + .clock = (768 + 116 + 81 + 5) * (1024 + 24 + 8 + 2) * 60 / 1000, + .hdisplay = 768, + .hsync_start = 768 + 116, + .hsync_end = 768 + 116 + 81, + .htotal = 768 + 116 + 81 + 5, + .vdisplay = 1024, + .vsync_start = 1024 + 24, + .vsync_end = 1024 + 24 + 8, + .vtotal = 1024 + 24 + 8 + 2, + .width_mm = 76, + .height_mm = 101, +}; + +static int renesas_r61307_get_modes(struct drm_panel *panel, + struct drm_connector *connector) +{ + struct drm_display_mode *mode; + + mode = drm_mode_duplicate(connector->dev, &renesas_r61307_mode); + if (!mode) + return -ENOMEM; + + drm_mode_set_name(mode); + + mode->type = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED; + connector->display_info.width_mm = mode->width_mm; + connector->display_info.height_mm = mode->height_mm; + drm_mode_probed_add(connector, mode); + + return 1; +} + +static const struct drm_panel_funcs renesas_r61307_panel_funcs = { + .prepare = renesas_r61307_prepare, + .enable = renesas_r61307_enable, + .disable = renesas_r61307_disable, + .unprepare = renesas_r61307_unprepare, + .get_modes = renesas_r61307_get_modes, +}; + +static int renesas_r61307_probe(struct mipi_dsi_device *dsi) +{ + struct device *dev = &dsi->dev; + struct renesas_r61307 *priv; + int ret; + + priv = devm_drm_panel_alloc(dev, struct renesas_r61307, panel, + &renesas_r61307_panel_funcs, + DRM_MODE_CONNECTOR_DSI); + if (IS_ERR(priv)) + return PTR_ERR(priv); + + priv->vcc_supply = devm_regulator_get(dev, "vcc"); + if (IS_ERR(priv->vcc_supply)) + return dev_err_probe(dev, PTR_ERR(priv->vcc_supply), + "Failed to get vcc-supply\n"); + + priv->iovcc_supply = devm_regulator_get(dev, "iovcc"); + if (IS_ERR(priv->iovcc_supply)) + return dev_err_probe(dev, PTR_ERR(priv->iovcc_supply), + "Failed to get iovcc-supply\n"); + + priv->reset_gpio = devm_gpiod_get_optional(dev, "reset", + GPIOD_OUT_HIGH); + if (IS_ERR(priv->reset_gpio)) + return dev_err_probe(dev, PTR_ERR(priv->reset_gpio), + "Failed to get reset gpios\n"); + + if (device_property_read_bool(dev, "renesas,inversion")) + priv->inversion = true; + + if (device_property_read_bool(dev, "renesas,contrast")) + priv->dig_cont_adj = true; + + priv->gamma = 0; + device_property_read_u32(dev, "renesas,gamma", &priv->gamma); + + priv->dsi = dsi; + mipi_dsi_set_drvdata(dsi, priv); + + dsi->lanes = 4; + dsi->format = MIPI_DSI_FMT_RGB888; + dsi->mode_flags = MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_VIDEO_SYNC_PULSE | + MIPI_DSI_CLOCK_NON_CONTINUOUS | MIPI_DSI_MODE_LPM; + + ret = drm_panel_of_backlight(&priv->panel); + if (ret) + return dev_err_probe(dev, ret, "Failed to get backlight\n"); + + drm_panel_add(&priv->panel); + + ret = mipi_dsi_attach(dsi); + if (ret) { + drm_panel_remove(&priv->panel); + return dev_err_probe(dev, ret, "Failed to attach to DSI host\n"); + } + + return 0; +} + +static void renesas_r61307_remove(struct mipi_dsi_device *dsi) +{ + struct renesas_r61307 *priv = mipi_dsi_get_drvdata(dsi); + int ret; + + ret = mipi_dsi_detach(dsi); + if (ret) + dev_err(&dsi->dev, "Failed to detach from DSI host: %d\n", ret); + + drm_panel_remove(&priv->panel); +} + +static const struct of_device_id renesas_r61307_of_match[] = { + { .compatible = "hit,tx13d100vm0eaa" }, + { .compatible = "koe,tx13d100vm0eaa" }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, renesas_r61307_of_match); + +static struct mipi_dsi_driver renesas_r61307_driver = { + .probe = renesas_r61307_probe, + .remove = renesas_r61307_remove, + .driver = { + .name = "panel-renesas-r61307", + .of_match_table = renesas_r61307_of_match, + }, +}; +module_mipi_dsi_driver(renesas_r61307_driver); + +MODULE_AUTHOR("Svyatoslav Ryhel <clamor95@gmail.com>"); +MODULE_DESCRIPTION("Renesas R61307-based panel driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/gpu/drm/panel/panel-renesas-r69328.c b/drivers/gpu/drm/panel/panel-renesas-r69328.c new file mode 100644 index 000000000000..46287ab04c30 --- /dev/null +++ b/drivers/gpu/drm/panel/panel-renesas-r69328.c @@ -0,0 +1,281 @@ +// SPDX-License-Identifier: GPL-2.0 + +#include <linux/array_size.h> +#include <linux/delay.h> +#include <linux/err.h> +#include <linux/gpio/consumer.h> +#include <linux/mod_devicetable.h> +#include <linux/module.h> +#include <linux/property.h> +#include <linux/regulator/consumer.h> + +#include <video/mipi_display.h> + +#include <drm/drm_mipi_dsi.h> +#include <drm/drm_modes.h> +#include <drm/drm_panel.h> + +#define R69328_MACP 0xb0 /* Manufacturer Access CMD Protect */ +#define R69328_MACP_ON 0x03 +#define R69328_MACP_OFF 0x04 + +#define R69328_GAMMA_SET_A 0xc8 /* Gamma Setting A */ +#define R69328_GAMMA_SET_B 0xc9 /* Gamma Setting B */ +#define R69328_GAMMA_SET_C 0xca /* Gamma Setting C */ + +#define R69328_POWER_SET 0xd1 + +struct renesas_r69328 { + struct drm_panel panel; + struct mipi_dsi_device *dsi; + + struct regulator *vdd_supply; + struct regulator *vddio_supply; + struct gpio_desc *reset_gpio; + + bool prepared; +}; + +static inline struct renesas_r69328 *to_renesas_r69328(struct drm_panel *panel) +{ + return container_of(panel, struct renesas_r69328, panel); +} + +static void renesas_r69328_reset(struct renesas_r69328 *priv) +{ + gpiod_set_value_cansleep(priv->reset_gpio, 1); + usleep_range(10000, 11000); + gpiod_set_value_cansleep(priv->reset_gpio, 0); + usleep_range(2000, 3000); +} + +static int renesas_r69328_prepare(struct drm_panel *panel) +{ + struct renesas_r69328 *priv = to_renesas_r69328(panel); + struct device *dev = &priv->dsi->dev; + int ret; + + if (priv->prepared) + return 0; + + ret = regulator_enable(priv->vdd_supply); + if (ret) { + dev_err(dev, "failed to enable vdd power supply\n"); + return ret; + } + + usleep_range(10000, 11000); + + ret = regulator_enable(priv->vddio_supply); + if (ret < 0) { + dev_err(dev, "failed to enable vddio power supply\n"); + return ret; + } + + usleep_range(10000, 11000); + + renesas_r69328_reset(priv); + + priv->prepared = true; + return 0; +} + +static int renesas_r69328_enable(struct drm_panel *panel) +{ + struct renesas_r69328 *priv = to_renesas_r69328(panel); + struct mipi_dsi_multi_context ctx = { .dsi = priv->dsi }; + + /* Set address mode */ + mipi_dsi_dcs_write_seq_multi(&ctx, MIPI_DCS_SET_ADDRESS_MODE, 0x00); + mipi_dsi_dcs_set_pixel_format_multi(&ctx, MIPI_DCS_PIXEL_FMT_24BIT << 4); + mipi_dsi_dcs_exit_sleep_mode_multi(&ctx); + + mipi_dsi_msleep(&ctx, 100); + + /* MACP Off */ + mipi_dsi_generic_write_seq_multi(&ctx, R69328_MACP, R69328_MACP_OFF); + + mipi_dsi_generic_write_seq_multi(&ctx, R69328_POWER_SET, 0x14, 0x1d, + 0x21, 0x67, 0x11, 0x9a); + + mipi_dsi_generic_write_seq_multi(&ctx, R69328_GAMMA_SET_A, 0x00, 0x1a, + 0x20, 0x28, 0x25, 0x24, 0x26, 0x15, 0x13, + 0x11, 0x18, 0x1e, 0x1c, 0x00, 0x00, 0x1a, + 0x20, 0x28, 0x25, 0x24, 0x26, 0x15, 0x13, + 0x11, 0x18, 0x1e, 0x1c, 0x00); + + mipi_dsi_generic_write_seq_multi(&ctx, R69328_GAMMA_SET_B, 0x00, 0x1a, + 0x20, 0x28, 0x25, 0x24, 0x26, 0x15, 0x13, + 0x11, 0x18, 0x1e, 0x1c, 0x00, 0x00, 0x1a, + 0x20, 0x28, 0x25, 0x24, 0x26, 0x15, 0x13, + 0x11, 0x18, 0x1e, 0x1c, 0x00); + + mipi_dsi_generic_write_seq_multi(&ctx, R69328_GAMMA_SET_C, 0x00, 0x1a, + 0x20, 0x28, 0x25, 0x24, 0x26, 0x15, 0x13, + 0x11, 0x18, 0x1e, 0x1c, 0x00, 0x00, 0x1a, + 0x20, 0x28, 0x25, 0x24, 0x26, 0x15, 0x13, + 0x11, 0x18, 0x1e, 0x1c, 0x00); + + /* MACP On */ + mipi_dsi_generic_write_seq_multi(&ctx, R69328_MACP, R69328_MACP_ON); + + mipi_dsi_dcs_set_display_on_multi(&ctx); + mipi_dsi_msleep(&ctx, 50); + + return 0; +} + +static int renesas_r69328_disable(struct drm_panel *panel) +{ + struct renesas_r69328 *priv = to_renesas_r69328(panel); + struct mipi_dsi_multi_context ctx = { .dsi = priv->dsi }; + + mipi_dsi_dcs_set_display_off_multi(&ctx); + mipi_dsi_msleep(&ctx, 60); + mipi_dsi_dcs_enter_sleep_mode_multi(&ctx); + + return 0; +} + +static int renesas_r69328_unprepare(struct drm_panel *panel) +{ + struct renesas_r69328 *priv = to_renesas_r69328(panel); + + if (!priv->prepared) + return 0; + + gpiod_set_value_cansleep(priv->reset_gpio, 1); + + usleep_range(5000, 6000); + + regulator_disable(priv->vddio_supply); + regulator_disable(priv->vdd_supply); + + priv->prepared = false; + return 0; +} + +static const struct drm_display_mode renesas_r69328_mode = { + .clock = (720 + 92 + 62 + 4) * (1280 + 6 + 3 + 1) * 60 / 1000, + .hdisplay = 720, + .hsync_start = 720 + 92, + .hsync_end = 720 + 92 + 62, + .htotal = 720 + 92 + 62 + 4, + .vdisplay = 1280, + .vsync_start = 1280 + 6, + .vsync_end = 1280 + 6 + 3, + .vtotal = 1280 + 6 + 3 + 1, + .width_mm = 59, + .height_mm = 105, +}; + +static int renesas_r69328_get_modes(struct drm_panel *panel, + struct drm_connector *connector) +{ + struct drm_display_mode *mode; + + mode = drm_mode_duplicate(connector->dev, &renesas_r69328_mode); + if (!mode) + return -ENOMEM; + + drm_mode_set_name(mode); + + mode->type = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED; + connector->display_info.width_mm = mode->width_mm; + connector->display_info.height_mm = mode->height_mm; + drm_mode_probed_add(connector, mode); + + return 1; +} + +static const struct drm_panel_funcs renesas_r69328_panel_funcs = { + .prepare = renesas_r69328_prepare, + .enable = renesas_r69328_enable, + .disable = renesas_r69328_disable, + .unprepare = renesas_r69328_unprepare, + .get_modes = renesas_r69328_get_modes, +}; + +static int renesas_r69328_probe(struct mipi_dsi_device *dsi) +{ + struct device *dev = &dsi->dev; + struct renesas_r69328 *priv; + int ret; + + priv = devm_drm_panel_alloc(dev, struct renesas_r69328, panel, + &renesas_r69328_panel_funcs, + DRM_MODE_CONNECTOR_DSI); + if (IS_ERR(priv)) + return PTR_ERR(priv); + + priv->vdd_supply = devm_regulator_get(dev, "vdd"); + if (IS_ERR(priv->vdd_supply)) + return dev_err_probe(dev, PTR_ERR(priv->vdd_supply), + "Failed to get vdd-supply\n"); + + priv->vddio_supply = devm_regulator_get(dev, "vddio"); + if (IS_ERR(priv->vddio_supply)) + return dev_err_probe(dev, PTR_ERR(priv->vddio_supply), + "Failed to get vddio-supply\n"); + + priv->reset_gpio = devm_gpiod_get_optional(dev, "reset", + GPIOD_OUT_LOW); + if (IS_ERR(priv->reset_gpio)) + return dev_err_probe(dev, PTR_ERR(priv->reset_gpio), + "Failed to get reset-gpios\n"); + + priv->dsi = dsi; + mipi_dsi_set_drvdata(dsi, priv); + + dsi->lanes = 4; + dsi->format = MIPI_DSI_FMT_RGB888; + dsi->mode_flags = MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_VIDEO_SYNC_PULSE | + MIPI_DSI_CLOCK_NON_CONTINUOUS | MIPI_DSI_MODE_LPM; + + ret = drm_panel_of_backlight(&priv->panel); + if (ret) + return dev_err_probe(dev, ret, "Failed to get backlight\n"); + + drm_panel_add(&priv->panel); + + ret = mipi_dsi_attach(dsi); + if (ret) { + drm_panel_remove(&priv->panel); + return dev_err_probe(dev, ret, "Failed to attach to DSI host\n"); + } + + return 0; +} + +static void renesas_r69328_remove(struct mipi_dsi_device *dsi) +{ + struct renesas_r69328 *priv = mipi_dsi_get_drvdata(dsi); + int ret; + + ret = mipi_dsi_detach(dsi); + if (ret) + dev_err(&dsi->dev, "Failed to detach from DSI host: %d\n", ret); + + drm_panel_remove(&priv->panel); +} + +static const struct of_device_id renesas_r69328_of_match[] = { + { .compatible = "jdi,dx12d100vm0eaa" }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, renesas_r69328_of_match); + +static struct mipi_dsi_driver renesas_r69328_driver = { + .probe = renesas_r69328_probe, + .remove = renesas_r69328_remove, + .driver = { + .name = "panel-renesas-r69328", + .of_match_table = renesas_r69328_of_match, + }, +}; +module_mipi_dsi_driver(renesas_r69328_driver); + +MODULE_AUTHOR("Maxim Schwalm <maxim.schwalm@gmail.com>"); +MODULE_AUTHOR("Svyatoslav Ryhel <clamor95@gmail.com>"); +MODULE_DESCRIPTION("Renesas R69328-based panel driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/gpu/drm/panel/panel-ronbo-rb070d30.c b/drivers/gpu/drm/panel/panel-ronbo-rb070d30.c index 2ef5ea5eaeeb..ad35d0fb0a16 100644 --- a/drivers/gpu/drm/panel/panel-ronbo-rb070d30.c +++ b/drivers/gpu/drm/panel/panel-ronbo-rb070d30.c @@ -143,9 +143,11 @@ static int rb070d30_panel_dsi_probe(struct mipi_dsi_device *dsi) struct rb070d30_panel *ctx; int ret; - ctx = devm_kzalloc(&dsi->dev, sizeof(*ctx), GFP_KERNEL); - if (!ctx) - return -ENOMEM; + ctx = devm_drm_panel_alloc(&dsi->dev, struct rb070d30_panel, panel, + &rb070d30_panel_funcs, + DRM_MODE_CONNECTOR_DSI); + if (IS_ERR(ctx)) + return PTR_ERR(ctx); ctx->supply = devm_regulator_get(&dsi->dev, "vcc-lcd"); if (IS_ERR(ctx->supply)) @@ -154,9 +156,6 @@ static int rb070d30_panel_dsi_probe(struct mipi_dsi_device *dsi) mipi_dsi_set_drvdata(dsi, ctx); ctx->dsi = dsi; - drm_panel_init(&ctx->panel, &dsi->dev, &rb070d30_panel_funcs, - DRM_MODE_CONNECTOR_DSI); - ctx->gpios.reset = devm_gpiod_get(&dsi->dev, "reset", GPIOD_OUT_LOW); if (IS_ERR(ctx->gpios.reset)) { dev_err(&dsi->dev, "Couldn't get our reset GPIO\n"); diff --git a/drivers/gpu/drm/panel/panel-samsung-ams581vf01.c b/drivers/gpu/drm/panel/panel-samsung-ams581vf01.c index cf6186312252..188dd7cf0297 100644 --- a/drivers/gpu/drm/panel/panel-samsung-ams581vf01.c +++ b/drivers/gpu/drm/panel/panel-samsung-ams581vf01.c @@ -211,9 +211,11 @@ static int ams581vf01_probe(struct mipi_dsi_device *dsi) struct ams581vf01 *ctx; int ret; - ctx = devm_kzalloc(dev, sizeof(*ctx), GFP_KERNEL); - if (!ctx) - return -ENOMEM; + ctx = devm_drm_panel_alloc(&dsi->dev, struct ams581vf01, panel, + &ams581vf01_panel_funcs, + DRM_MODE_CONNECTOR_DSI); + if (IS_ERR(ctx)) + return PTR_ERR(ctx); ret = devm_regulator_bulk_get_const(&dsi->dev, ARRAY_SIZE(ams581vf01_supplies), @@ -235,8 +237,6 @@ static int ams581vf01_probe(struct mipi_dsi_device *dsi) dsi->mode_flags = MIPI_DSI_MODE_VIDEO_BURST | MIPI_DSI_CLOCK_NON_CONTINUOUS | MIPI_DSI_MODE_LPM; - drm_panel_init(&ctx->panel, dev, &ams581vf01_panel_funcs, - DRM_MODE_CONNECTOR_DSI); ctx->panel.prepare_prev_first = true; ctx->panel.backlight = ams581vf01_create_backlight(dsi); diff --git a/drivers/gpu/drm/panel/panel-samsung-ams639rq08.c b/drivers/gpu/drm/panel/panel-samsung-ams639rq08.c index 817365cb5e46..f8ebbd4a530b 100644 --- a/drivers/gpu/drm/panel/panel-samsung-ams639rq08.c +++ b/drivers/gpu/drm/panel/panel-samsung-ams639rq08.c @@ -257,9 +257,11 @@ static int ams639rq08_probe(struct mipi_dsi_device *dsi) struct ams639rq08 *ctx; int ret; - ctx = devm_kzalloc(dev, sizeof(*ctx), GFP_KERNEL); - if (!ctx) - return -ENOMEM; + ctx = devm_drm_panel_alloc(dev, struct ams639rq08, panel, + &ams639rq08_panel_funcs, + DRM_MODE_CONNECTOR_DSI); + if (IS_ERR(ctx)) + return PTR_ERR(ctx); ret = devm_regulator_bulk_get_const(&dsi->dev, ARRAY_SIZE(ams639rq08_supplies), @@ -281,8 +283,6 @@ static int ams639rq08_probe(struct mipi_dsi_device *dsi) dsi->mode_flags = MIPI_DSI_MODE_VIDEO_BURST | MIPI_DSI_CLOCK_NON_CONTINUOUS | MIPI_DSI_MODE_LPM; - drm_panel_init(&ctx->panel, dev, &ams639rq08_panel_funcs, - DRM_MODE_CONNECTOR_DSI); ctx->panel.prepare_prev_first = true; ctx->panel.backlight = ams639rq08_create_backlight(dsi); diff --git a/drivers/gpu/drm/panel/panel-samsung-atna33xc20.c b/drivers/gpu/drm/panel/panel-samsung-atna33xc20.c index 9a482a744b8c..20ec27d2d6c2 100644 --- a/drivers/gpu/drm/panel/panel-samsung-atna33xc20.c +++ b/drivers/gpu/drm/panel/panel-samsung-atna33xc20.c @@ -266,9 +266,12 @@ static int atana33xc20_probe(struct dp_aux_ep_device *aux_ep) struct device *dev = &aux_ep->dev; int ret; - panel = devm_kzalloc(dev, sizeof(*panel), GFP_KERNEL); - if (!panel) - return -ENOMEM; + panel = devm_drm_panel_alloc(dev, struct atana33xc20_panel, base, + &atana33xc20_funcs, + DRM_MODE_CONNECTOR_eDP); + if (IS_ERR(panel)) + return PTR_ERR(panel); + dev_set_drvdata(dev, panel); panel->aux = aux_ep->aux; @@ -301,8 +304,6 @@ static int atana33xc20_probe(struct dp_aux_ep_device *aux_ep) if (ret) return ret; - drm_panel_init(&panel->base, dev, &atana33xc20_funcs, DRM_MODE_CONNECTOR_eDP); - pm_runtime_get_sync(dev); ret = drm_panel_dp_aux_backlight(&panel->base, aux_ep->aux); pm_runtime_mark_last_busy(dev); diff --git a/drivers/gpu/drm/panel/panel-samsung-db7430.c b/drivers/gpu/drm/panel/panel-samsung-db7430.c index 14c6700e37b3..a97182f3c990 100644 --- a/drivers/gpu/drm/panel/panel-samsung-db7430.c +++ b/drivers/gpu/drm/panel/panel-samsung-db7430.c @@ -267,9 +267,11 @@ static int db7430_probe(struct spi_device *spi) struct db7430 *db; int ret; - db = devm_kzalloc(dev, sizeof(*db), GFP_KERNEL); - if (!db) - return -ENOMEM; + db = devm_drm_panel_alloc(dev, struct db7430, panel, &db7430_drm_funcs, + DRM_MODE_CONNECTOR_DPI); + if (IS_ERR(db)) + return PTR_ERR(db); + db->dev = dev; /* @@ -294,9 +296,6 @@ static int db7430_probe(struct spi_device *spi) if (ret) return dev_err_probe(dev, ret, "MIPI DBI init failed\n"); - drm_panel_init(&db->panel, dev, &db7430_drm_funcs, - DRM_MODE_CONNECTOR_DPI); - /* FIXME: if no external backlight, use internal backlight */ ret = drm_panel_of_backlight(&db->panel); if (ret) diff --git a/drivers/gpu/drm/panel/panel-samsung-ld9040.c b/drivers/gpu/drm/panel/panel-samsung-ld9040.c index 9f438683a6f6..c7f2241523a0 100644 --- a/drivers/gpu/drm/panel/panel-samsung-ld9040.c +++ b/drivers/gpu/drm/panel/panel-samsung-ld9040.c @@ -339,9 +339,11 @@ static int ld9040_probe(struct spi_device *spi) struct ld9040 *ctx; int ret; - ctx = devm_kzalloc(dev, sizeof(struct ld9040), GFP_KERNEL); - if (!ctx) - return -ENOMEM; + ctx = devm_drm_panel_alloc(dev, struct ld9040, panel, + &ld9040_drm_funcs, + DRM_MODE_CONNECTOR_DPI); + if (IS_ERR(ctx)) + return PTR_ERR(ctx); spi_set_drvdata(spi, ctx); @@ -373,9 +375,6 @@ static int ld9040_probe(struct spi_device *spi) return ret; } - drm_panel_init(&ctx->panel, dev, &ld9040_drm_funcs, - DRM_MODE_CONNECTOR_DPI); - bldev = devm_backlight_device_register(dev, dev_name(dev), dev, ctx, &ld9040_bl_ops, &ld9040_bl_props); diff --git a/drivers/gpu/drm/panel/panel-samsung-s6d16d0.c b/drivers/gpu/drm/panel/panel-samsung-s6d16d0.c index 79f611963c61..ba1a02000bb9 100644 --- a/drivers/gpu/drm/panel/panel-samsung-s6d16d0.c +++ b/drivers/gpu/drm/panel/panel-samsung-s6d16d0.c @@ -166,9 +166,11 @@ static int s6d16d0_probe(struct mipi_dsi_device *dsi) struct s6d16d0 *s6; int ret; - s6 = devm_kzalloc(dev, sizeof(struct s6d16d0), GFP_KERNEL); - if (!s6) - return -ENOMEM; + s6 = devm_drm_panel_alloc(dev, struct s6d16d0, panel, + &s6d16d0_drm_funcs, + DRM_MODE_CONNECTOR_DSI); + if (IS_ERR(s6)) + return PTR_ERR(s6); mipi_dsi_set_drvdata(dsi, s6); s6->dev = dev; @@ -200,9 +202,6 @@ static int s6d16d0_probe(struct mipi_dsi_device *dsi) return ret; } - drm_panel_init(&s6->panel, dev, &s6d16d0_drm_funcs, - DRM_MODE_CONNECTOR_DSI); - drm_panel_add(&s6->panel); ret = mipi_dsi_attach(dsi); diff --git a/drivers/gpu/drm/panel/panel-samsung-s6d27a1.c b/drivers/gpu/drm/panel/panel-samsung-s6d27a1.c index 2adb223a895c..300dc19bd9d1 100644 --- a/drivers/gpu/drm/panel/panel-samsung-s6d27a1.c +++ b/drivers/gpu/drm/panel/panel-samsung-s6d27a1.c @@ -247,9 +247,11 @@ static int s6d27a1_probe(struct spi_device *spi) struct s6d27a1 *ctx; int ret; - ctx = devm_kzalloc(dev, sizeof(*ctx), GFP_KERNEL); - if (!ctx) - return -ENOMEM; + ctx = devm_drm_panel_alloc(dev, struct s6d27a1, panel, + &s6d27a1_drm_funcs, + DRM_MODE_CONNECTOR_DPI); + if (IS_ERR(ctx)) + return PTR_ERR(ctx); ctx->dev = dev; @@ -277,9 +279,6 @@ static int s6d27a1_probe(struct spi_device *spi) ctx->dbi.read_commands = s6d27a1_dbi_read_commands; - drm_panel_init(&ctx->panel, dev, &s6d27a1_drm_funcs, - DRM_MODE_CONNECTOR_DPI); - ret = drm_panel_of_backlight(&ctx->panel); if (ret) return dev_err_probe(dev, ret, "failed to add backlight\n"); diff --git a/drivers/gpu/drm/panel/panel-samsung-s6d7aa0.c b/drivers/gpu/drm/panel/panel-samsung-s6d7aa0.c index 93f11e2e9398..b5b9e80690f6 100644 --- a/drivers/gpu/drm/panel/panel-samsung-s6d7aa0.c +++ b/drivers/gpu/drm/panel/panel-samsung-s6d7aa0.c @@ -392,9 +392,11 @@ static int s6d7aa0_probe(struct mipi_dsi_device *dsi) struct s6d7aa0 *ctx; int ret; - ctx = devm_kzalloc(dev, sizeof(*ctx), GFP_KERNEL); - if (!ctx) - return -ENOMEM; + ctx = devm_drm_panel_alloc(dev, struct s6d7aa0, panel, + &s6d7aa0_panel_funcs, + DRM_MODE_CONNECTOR_DSI); + if (IS_ERR(ctx)) + return PTR_ERR(ctx); ctx->desc = of_device_get_match_data(dev); if (!ctx->desc) @@ -420,8 +422,6 @@ static int s6d7aa0_probe(struct mipi_dsi_device *dsi) dsi->mode_flags = MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_VIDEO_BURST | ctx->desc->mode_flags; - drm_panel_init(&ctx->panel, dev, &s6d7aa0_panel_funcs, - DRM_MODE_CONNECTOR_DSI); ctx->panel.prepare_prev_first = true; ret = drm_panel_of_backlight(&ctx->panel); diff --git a/drivers/gpu/drm/panel/panel-samsung-s6e3fa7.c b/drivers/gpu/drm/panel/panel-samsung-s6e3fa7.c index 27a059b55ae5..f4d75eca3cdf 100644 --- a/drivers/gpu/drm/panel/panel-samsung-s6e3fa7.c +++ b/drivers/gpu/drm/panel/panel-samsung-s6e3fa7.c @@ -185,9 +185,11 @@ static int s6e3fa7_panel_probe(struct mipi_dsi_device *dsi) struct s6e3fa7_panel *ctx; int ret; - ctx = devm_kzalloc(dev, sizeof(*ctx), GFP_KERNEL); - if (!ctx) - return -ENOMEM; + ctx = devm_drm_panel_alloc(dev, struct s6e3fa7_panel, panel, + &s6e3fa7_panel_funcs, + DRM_MODE_CONNECTOR_DSI); + if (IS_ERR(ctx)) + return PTR_ERR(ctx); ctx->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_HIGH); if (IS_ERR(ctx->reset_gpio)) @@ -202,8 +204,6 @@ static int s6e3fa7_panel_probe(struct mipi_dsi_device *dsi) dsi->mode_flags = MIPI_DSI_MODE_VIDEO_BURST | MIPI_DSI_CLOCK_NON_CONTINUOUS | MIPI_DSI_MODE_LPM; - drm_panel_init(&ctx->panel, dev, &s6e3fa7_panel_funcs, - DRM_MODE_CONNECTOR_DSI); ctx->panel.prepare_prev_first = true; ctx->panel.backlight = s6e3fa7_panel_create_backlight(dsi); diff --git a/drivers/gpu/drm/panel/panel-samsung-s6e3ha2.c b/drivers/gpu/drm/panel/panel-samsung-s6e3ha2.c index ab8b58545284..1db0c63b1131 100644 --- a/drivers/gpu/drm/panel/panel-samsung-s6e3ha2.c +++ b/drivers/gpu/drm/panel/panel-samsung-s6e3ha2.c @@ -681,9 +681,11 @@ static int s6e3ha2_probe(struct mipi_dsi_device *dsi) struct s6e3ha2 *ctx; int ret; - ctx = devm_kzalloc(dev, sizeof(*ctx), GFP_KERNEL); - if (!ctx) - return -ENOMEM; + ctx = devm_drm_panel_alloc(dev, struct s6e3ha2, panel, + &s6e3ha2_drm_funcs, + DRM_MODE_CONNECTOR_DSI); + if (IS_ERR(ctx)) + return PTR_ERR(ctx); mipi_dsi_set_drvdata(dsi, ctx); @@ -731,8 +733,6 @@ static int s6e3ha2_probe(struct mipi_dsi_device *dsi) ctx->bl_dev->props.brightness = S6E3HA2_DEFAULT_BRIGHTNESS; ctx->bl_dev->props.power = BACKLIGHT_POWER_OFF; - drm_panel_init(&ctx->panel, dev, &s6e3ha2_drm_funcs, - DRM_MODE_CONNECTOR_DSI); ctx->panel.prepare_prev_first = true; drm_panel_add(&ctx->panel); diff --git a/drivers/gpu/drm/panel/panel-samsung-s6e3ha8.c b/drivers/gpu/drm/panel/panel-samsung-s6e3ha8.c index 64c6f7d45bed..550e9ef9bb71 100644 --- a/drivers/gpu/drm/panel/panel-samsung-s6e3ha8.c +++ b/drivers/gpu/drm/panel/panel-samsung-s6e3ha8.c @@ -253,9 +253,11 @@ static int s6e3ha8_amb577px01_wqhd_probe(struct mipi_dsi_device *dsi) struct s6e3ha8 *priv; int ret; - priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); - if (!priv) - return -ENOMEM; + priv = devm_drm_panel_alloc(dev, struct s6e3ha8, panel, + &s6e3ha8_amb577px01_wqhd_panel_funcs, + DRM_MODE_CONNECTOR_DSI); + if (IS_ERR(priv)) + return PTR_ERR(priv); ret = devm_regulator_bulk_get_const(dev, ARRAY_SIZE(s6e3ha8_supplies), s6e3ha8_supplies, @@ -279,8 +281,6 @@ static int s6e3ha8_amb577px01_wqhd_probe(struct mipi_dsi_device *dsi) MIPI_DSI_MODE_VIDEO_NO_HFP | MIPI_DSI_MODE_VIDEO_NO_HBP | MIPI_DSI_MODE_VIDEO_NO_HSA | MIPI_DSI_MODE_NO_EOT_PACKET; - drm_panel_init(&priv->panel, dev, &s6e3ha8_amb577px01_wqhd_panel_funcs, - DRM_MODE_CONNECTOR_DSI); priv->panel.prepare_prev_first = true; drm_panel_add(&priv->panel); diff --git a/drivers/gpu/drm/panel/panel-samsung-s6e63j0x03.c b/drivers/gpu/drm/panel/panel-samsung-s6e63j0x03.c index 364f1c9a16d9..6f3d39556f92 100644 --- a/drivers/gpu/drm/panel/panel-samsung-s6e63j0x03.c +++ b/drivers/gpu/drm/panel/panel-samsung-s6e63j0x03.c @@ -437,9 +437,11 @@ static int s6e63j0x03_probe(struct mipi_dsi_device *dsi) struct s6e63j0x03 *ctx; int ret; - ctx = devm_kzalloc(dev, sizeof(struct s6e63j0x03), GFP_KERNEL); - if (!ctx) - return -ENOMEM; + ctx = devm_drm_panel_alloc(dev, struct s6e63j0x03, panel, + &s6e63j0x03_funcs, + DRM_MODE_CONNECTOR_DSI); + if (IS_ERR(ctx)) + return PTR_ERR(ctx); mipi_dsi_set_drvdata(dsi, ctx); @@ -462,8 +464,6 @@ static int s6e63j0x03_probe(struct mipi_dsi_device *dsi) return dev_err_probe(dev, PTR_ERR(ctx->reset_gpio), "cannot get reset-gpio\n"); - drm_panel_init(&ctx->panel, dev, &s6e63j0x03_funcs, - DRM_MODE_CONNECTOR_DSI); ctx->panel.prepare_prev_first = true; ctx->bl_dev = backlight_device_register("s6e63j0x03", dev, ctx, diff --git a/drivers/gpu/drm/panel/panel-samsung-s6e88a0-ams427ap24.c b/drivers/gpu/drm/panel/panel-samsung-s6e88a0-ams427ap24.c index e92e95158d1f..e91f50662997 100644 --- a/drivers/gpu/drm/panel/panel-samsung-s6e88a0-ams427ap24.c +++ b/drivers/gpu/drm/panel/panel-samsung-s6e88a0-ams427ap24.c @@ -687,9 +687,11 @@ static int s6e88a0_ams427ap24_probe(struct mipi_dsi_device *dsi) struct s6e88a0_ams427ap24 *ctx; int ret; - ctx = devm_kzalloc(dev, sizeof(*ctx), GFP_KERNEL); - if (!ctx) - return -ENOMEM; + ctx = devm_drm_panel_alloc(dev, struct s6e88a0_ams427ap24, panel, + &s6e88a0_ams427ap24_panel_funcs, + DRM_MODE_CONNECTOR_DSI); + if (IS_ERR(ctx)) + return PTR_ERR(ctx); ret = devm_regulator_bulk_get_const(dev, ARRAY_SIZE(s6e88a0_ams427ap24_supplies), @@ -711,8 +713,6 @@ static int s6e88a0_ams427ap24_probe(struct mipi_dsi_device *dsi) dsi->mode_flags = MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_VIDEO_BURST | MIPI_DSI_MODE_NO_EOT_PACKET | MIPI_DSI_MODE_VIDEO_NO_HFP; - drm_panel_init(&ctx->panel, dev, &s6e88a0_ams427ap24_panel_funcs, - DRM_MODE_CONNECTOR_DSI); ctx->panel.prepare_prev_first = true; ctx->flip_horizontal = device_property_read_bool(dev, "flip-horizontal"); diff --git a/drivers/gpu/drm/panel/panel-samsung-s6e88a0-ams452ef01.c b/drivers/gpu/drm/panel/panel-samsung-s6e88a0-ams452ef01.c index 57b1a899bbdc..ca5cad41ff1d 100644 --- a/drivers/gpu/drm/panel/panel-samsung-s6e88a0-ams452ef01.c +++ b/drivers/gpu/drm/panel/panel-samsung-s6e88a0-ams452ef01.c @@ -165,9 +165,11 @@ static int s6e88a0_ams452ef01_probe(struct mipi_dsi_device *dsi) struct s6e88a0_ams452ef01 *ctx; int ret; - ctx = devm_kzalloc(dev, sizeof(*ctx), GFP_KERNEL); - if (!ctx) - return -ENOMEM; + ctx = devm_drm_panel_alloc(dev, struct s6e88a0_ams452ef01, panel, + &s6e88a0_ams452ef01_panel_funcs, + DRM_MODE_CONNECTOR_DSI); + if (IS_ERR(ctx)) + return PTR_ERR(ctx); ctx->supplies[0].supply = "vdd3"; ctx->supplies[1].supply = "vci"; @@ -192,9 +194,6 @@ static int s6e88a0_ams452ef01_probe(struct mipi_dsi_device *dsi) dsi->format = MIPI_DSI_FMT_RGB888; dsi->mode_flags = MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_VIDEO_BURST; - drm_panel_init(&ctx->panel, dev, &s6e88a0_ams452ef01_panel_funcs, - DRM_MODE_CONNECTOR_DSI); - drm_panel_add(&ctx->panel); ret = mipi_dsi_attach(dsi); diff --git a/drivers/gpu/drm/panel/panel-samsung-s6e8aa0.c b/drivers/gpu/drm/panel/panel-samsung-s6e8aa0.c index c51d07ec1529..897df195f2f3 100644 --- a/drivers/gpu/drm/panel/panel-samsung-s6e8aa0.c +++ b/drivers/gpu/drm/panel/panel-samsung-s6e8aa0.c @@ -979,9 +979,11 @@ static int s6e8aa0_probe(struct mipi_dsi_device *dsi) struct s6e8aa0 *ctx; int ret; - ctx = devm_kzalloc(dev, sizeof(struct s6e8aa0), GFP_KERNEL); - if (!ctx) - return -ENOMEM; + ctx = devm_drm_panel_alloc(dev, struct s6e8aa0, panel, + &s6e8aa0_drm_funcs, + DRM_MODE_CONNECTOR_DSI); + if (IS_ERR(ctx)) + return PTR_ERR(ctx); mipi_dsi_set_drvdata(dsi, ctx); @@ -1014,8 +1016,6 @@ static int s6e8aa0_probe(struct mipi_dsi_device *dsi) ctx->brightness = GAMMA_LEVEL_NUM - 1; - drm_panel_init(&ctx->panel, dev, &s6e8aa0_drm_funcs, - DRM_MODE_CONNECTOR_DSI); ctx->panel.prepare_prev_first = true; drm_panel_add(&ctx->panel); diff --git a/drivers/gpu/drm/panel/panel-samsung-sofef00.c b/drivers/gpu/drm/panel/panel-samsung-sofef00.c index d92ae6b6100f..064258217d50 100644 --- a/drivers/gpu/drm/panel/panel-samsung-sofef00.c +++ b/drivers/gpu/drm/panel/panel-samsung-sofef00.c @@ -191,9 +191,11 @@ static int sofef00_panel_probe(struct mipi_dsi_device *dsi) struct sofef00_panel *ctx; int ret; - ctx = devm_kzalloc(dev, sizeof(*ctx), GFP_KERNEL); - if (!ctx) - return -ENOMEM; + ctx = devm_drm_panel_alloc(dev, struct sofef00_panel, panel, + &sofef00_panel_panel_funcs, + DRM_MODE_CONNECTOR_DSI); + if (IS_ERR(ctx)) + return PTR_ERR(ctx); ctx->supply = devm_regulator_get(dev, "vddio"); if (IS_ERR(ctx->supply)) @@ -211,9 +213,6 @@ static int sofef00_panel_probe(struct mipi_dsi_device *dsi) dsi->lanes = 4; dsi->format = MIPI_DSI_FMT_RGB888; - drm_panel_init(&ctx->panel, dev, &sofef00_panel_panel_funcs, - DRM_MODE_CONNECTOR_DSI); - ctx->panel.backlight = sofef00_create_backlight(dsi); if (IS_ERR(ctx->panel.backlight)) return dev_err_probe(dev, PTR_ERR(ctx->panel.backlight), diff --git a/drivers/gpu/drm/panel/panel-seiko-43wvf1g.c b/drivers/gpu/drm/panel/panel-seiko-43wvf1g.c index 7d1b421ea9dd..0935d83ee2db 100644 --- a/drivers/gpu/drm/panel/panel-seiko-43wvf1g.c +++ b/drivers/gpu/drm/panel/panel-seiko-43wvf1g.c @@ -204,9 +204,11 @@ static int seiko_panel_probe(struct device *dev, struct seiko_panel *panel; int err; - panel = devm_kzalloc(dev, sizeof(*panel), GFP_KERNEL); - if (!panel) - return -ENOMEM; + panel = devm_drm_panel_alloc(dev, struct seiko_panel, base, + &seiko_panel_funcs, + DRM_MODE_CONNECTOR_DPI); + if (IS_ERR(panel)) + return PTR_ERR(panel); panel->desc = desc; @@ -224,9 +226,6 @@ static int seiko_panel_probe(struct device *dev, return dev_err_probe(dev, PTR_ERR(panel->enable_gpio), "failed to request GPIO\n"); - drm_panel_init(&panel->base, dev, &seiko_panel_funcs, - DRM_MODE_CONNECTOR_DPI); - err = drm_panel_of_backlight(&panel->base); if (err) return err; diff --git a/drivers/gpu/drm/panel/panel-sharp-ls037v7dw01.c b/drivers/gpu/drm/panel/panel-sharp-ls037v7dw01.c index a9673a52b861..938beac4655d 100644 --- a/drivers/gpu/drm/panel/panel-sharp-ls037v7dw01.c +++ b/drivers/gpu/drm/panel/panel-sharp-ls037v7dw01.c @@ -138,9 +138,10 @@ static int ls037v7dw01_probe(struct platform_device *pdev) { struct ls037v7dw01_panel *lcd; - lcd = devm_kzalloc(&pdev->dev, sizeof(*lcd), GFP_KERNEL); - if (!lcd) - return -ENOMEM; + lcd = devm_drm_panel_alloc(&pdev->dev, struct ls037v7dw01_panel, panel, + &ls037v7dw01_funcs, DRM_MODE_CONNECTOR_DPI); + if (IS_ERR(lcd)) + return PTR_ERR(lcd); platform_set_drvdata(pdev, lcd); lcd->pdev = pdev; @@ -181,9 +182,6 @@ static int ls037v7dw01_probe(struct platform_device *pdev) return PTR_ERR(lcd->ud_gpio); } - drm_panel_init(&lcd->panel, &pdev->dev, &ls037v7dw01_funcs, - DRM_MODE_CONNECTOR_DPI); - drm_panel_add(&lcd->panel); return 0; diff --git a/drivers/gpu/drm/panel/panel-sharp-ls060t1sx01.c b/drivers/gpu/drm/panel/panel-sharp-ls060t1sx01.c index 0b4e0983639b..0456f3d705e7 100644 --- a/drivers/gpu/drm/panel/panel-sharp-ls060t1sx01.c +++ b/drivers/gpu/drm/panel/panel-sharp-ls060t1sx01.c @@ -193,9 +193,11 @@ static int sharp_ls060_probe(struct mipi_dsi_device *dsi) struct sharp_ls060 *ctx; int ret; - ctx = devm_kzalloc(dev, sizeof(*ctx), GFP_KERNEL); - if (!ctx) - return -ENOMEM; + ctx = devm_drm_panel_alloc(dev, struct sharp_ls060, panel, + &sharp_ls060_panel_funcs, + DRM_MODE_CONNECTOR_DSI); + if (IS_ERR(ctx)) + return PTR_ERR(ctx); ctx->vddi_supply = devm_regulator_get(dev, "vddi"); if (IS_ERR(ctx->vddi_supply)) @@ -227,9 +229,6 @@ static int sharp_ls060_probe(struct mipi_dsi_device *dsi) MIPI_DSI_MODE_NO_EOT_PACKET | MIPI_DSI_CLOCK_NON_CONTINUOUS; - drm_panel_init(&ctx->panel, dev, &sharp_ls060_panel_funcs, - DRM_MODE_CONNECTOR_DSI); - ret = drm_panel_of_backlight(&ctx->panel); if (ret) return dev_err_probe(dev, ret, "Failed to get backlight\n"); diff --git a/drivers/gpu/drm/panel/panel-simple.c b/drivers/gpu/drm/panel/panel-simple.c index 0a3b26bb4d73..47222d2d8129 100644 --- a/drivers/gpu/drm/panel/panel-simple.c +++ b/drivers/gpu/drm/panel/panel-simple.c @@ -1268,6 +1268,30 @@ static const struct panel_desc auo_g190ean01 = { .connector_type = DRM_MODE_CONNECTOR_LVDS, }; +static const struct display_timing auo_p238han01_timings = { + .pixelclock = { 107400000, 142400000, 180000000 }, + .hactive = { 1920, 1920, 1920 }, + .hfront_porch = { 30, 70, 650 }, + .hback_porch = { 30, 70, 650 }, + .hsync_len = { 20, 40, 136 }, + .vactive = { 1080, 1080, 1080 }, + .vfront_porch = { 5, 19, 318 }, + .vback_porch = { 5, 19, 318 }, + .vsync_len = { 4, 12, 120 }, +}; + +static const struct panel_desc auo_p238han01 = { + .timings = &auo_p238han01_timings, + .num_timings = 1, + .bpc = 8, + .size = { + .width = 527, + .height = 296, + }, + .bus_format = MEDIA_BUS_FMT_RGB888_1X7X4_SPWG, + .connector_type = DRM_MODE_CONNECTOR_LVDS, +}; + static const struct display_timing auo_p320hvn03_timings = { .pixelclock = { 106000000, 148500000, 164000000 }, .hactive = { 1920, 1920, 1920 }, @@ -4939,6 +4963,9 @@ static const struct of_device_id platform_of_match[] = { .compatible = "auo,g190ean01", .data = &auo_g190ean01, }, { + .compatible = "auo,p238han01", + .data = &auo_p238han01, + }, { .compatible = "auo,p320hvn03", .data = &auo_p320hvn03, }, { diff --git a/drivers/gpu/drm/panel/panel-sitronix-st7701.c b/drivers/gpu/drm/panel/panel-sitronix-st7701.c index 1f72ef7ca74c..2f79ec4a2063 100644 --- a/drivers/gpu/drm/panel/panel-sitronix-st7701.c +++ b/drivers/gpu/drm/panel/panel-sitronix-st7701.c @@ -520,6 +520,28 @@ static void rg28xx_gip_sequence(struct st7701 *st7701) st7701_switch_cmd_bkx(st7701, false, 0); } +static void wf40eswaa6mnn0_gip_sequence(struct st7701 *st7701) +{ + ST7701_WRITE(st7701, 0xE0, 0x00, 0x28, 0x02); + ST7701_WRITE(st7701, 0xE1, 0x08, 0xA0, 0x00, 0x00, 0x07, 0xA0, 0x00, + 0x00, 0x00, 0x44, 0x44); + ST7701_WRITE(st7701, 0xE2, 0x11, 0x11, 0x44, 0x44, 0xED, 0xA0, 0x00, + 0x00, 0xEC, 0xA0, 0x00, 0x00); + ST7701_WRITE(st7701, 0xE3, 0x00, 0x00, 0x11, 0x11); + ST7701_WRITE(st7701, 0xE4, 0x44, 0x44); + ST7701_WRITE(st7701, 0xE5, 0x0A, 0xE9, 0xD8, 0xA0, 0x0C, 0xEB, 0xD8, + 0xA0, 0x0E, 0xED, 0xD8, 0xA0, 0x10, 0xEF, 0xD8, 0xA0); + ST7701_WRITE(st7701, 0xE6, 0x00, 0x00, 0x11, 0x11); + ST7701_WRITE(st7701, 0xE7, 0x44, 0x44); + ST7701_WRITE(st7701, 0xE8, 0x09, 0xE8, 0xD8, 0xA0, 0x0B, 0xEA, 0xD8, + 0xA0, 0x0D, 0xEC, 0xD8, 0xA0, 0x0F, 0xEE, 0xD8, 0xA0); + ST7701_WRITE(st7701, 0xEB, 0x00, 0x00, 0xE4, 0xE4, 0x88, 0x00, 0x40); + ST7701_WRITE(st7701, 0xEC, 0x3C, 0x00); + ST7701_WRITE(st7701, 0xED, 0xAB, 0x89, 0x76, 0x54, 0x02, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0x20, 0x45, 0x67, 0x98, 0xBA); + ST7701_WRITE(st7701, MIPI_DCS_SET_ADDRESS_MODE, 0); +} + static int st7701_prepare(struct drm_panel *panel) { struct st7701 *st7701 = panel_to_st7701(panel); @@ -1135,6 +1157,107 @@ static const struct st7701_panel_desc rg28xx_desc = { .gip_sequence = rg28xx_gip_sequence, }; +static const struct drm_display_mode wf40eswaa6mnn0_mode = { + .clock = 18306, + + .hdisplay = 480, + .hsync_start = 480 + 2, + .hsync_end = 480 + 2 + 45, + .htotal = 480 + 2 + 45 + 13, + + .vdisplay = 480, + .vsync_start = 480 + 2, + .vsync_end = 480 + 2 + 70, + .vtotal = 480 + 2 + 70 + 13, + + .width_mm = 72, + .height_mm = 70, + + .flags = DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC, + + .type = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED, +}; + +static const struct st7701_panel_desc wf40eswaa6mnn0_desc = { + .mode = &wf40eswaa6mnn0_mode, + .lanes = 2, + .format = MIPI_DSI_FMT_RGB888, + .panel_sleep_delay = 0, + + .pv_gamma = { + CFIELD_PREP(ST7701_CMD2_BK0_GAMCTRL_AJ_MASK, 0) | + CFIELD_PREP(ST7701_CMD2_BK0_GAMCTRL_VC0_MASK, 0x1), + CFIELD_PREP(ST7701_CMD2_BK0_GAMCTRL_AJ_MASK, 0) | + CFIELD_PREP(ST7701_CMD2_BK0_GAMCTRL_VC4_MASK, 0x08), + CFIELD_PREP(ST7701_CMD2_BK0_GAMCTRL_AJ_MASK, 0) | + CFIELD_PREP(ST7701_CMD2_BK0_GAMCTRL_VC8_MASK, 0x10), + CFIELD_PREP(ST7701_CMD2_BK0_GAMCTRL_VC16_MASK, 0x0c), + + CFIELD_PREP(ST7701_CMD2_BK0_GAMCTRL_AJ_MASK, 0) | + CFIELD_PREP(ST7701_CMD2_BK0_GAMCTRL_VC24_MASK, 0x10), + CFIELD_PREP(ST7701_CMD2_BK0_GAMCTRL_VC52_MASK, 0x08), + CFIELD_PREP(ST7701_CMD2_BK0_GAMCTRL_VC80_MASK, 0x10), + CFIELD_PREP(ST7701_CMD2_BK0_GAMCTRL_VC108_MASK, 0x0c), + + CFIELD_PREP(ST7701_CMD2_BK0_GAMCTRL_VC147_MASK, 0x08), + CFIELD_PREP(ST7701_CMD2_BK0_GAMCTRL_VC175_MASK, 0x22), + CFIELD_PREP(ST7701_CMD2_BK0_GAMCTRL_VC203_MASK, 0x04), + CFIELD_PREP(ST7701_CMD2_BK0_GAMCTRL_AJ_MASK, 0) | + CFIELD_PREP(ST7701_CMD2_BK0_GAMCTRL_VC231_MASK, 0x14), + + CFIELD_PREP(ST7701_CMD2_BK0_GAMCTRL_VC239_MASK, 0x12), + CFIELD_PREP(ST7701_CMD2_BK0_GAMCTRL_AJ_MASK, 0) | + CFIELD_PREP(ST7701_CMD2_BK0_GAMCTRL_VC247_MASK, 0xb3), + CFIELD_PREP(ST7701_CMD2_BK0_GAMCTRL_AJ_MASK, 0) | + CFIELD_PREP(ST7701_CMD2_BK0_GAMCTRL_VC251_MASK, 0x3a), + CFIELD_PREP(ST7701_CMD2_BK0_GAMCTRL_AJ_MASK, 0) | + CFIELD_PREP(ST7701_CMD2_BK0_GAMCTRL_VC255_MASK, 0x1f) + }, + .nv_gamma = { + CFIELD_PREP(ST7701_CMD2_BK0_GAMCTRL_AJ_MASK, 0) | + CFIELD_PREP(ST7701_CMD2_BK0_GAMCTRL_VC4_MASK, 0x13), + CFIELD_PREP(ST7701_CMD2_BK0_GAMCTRL_AJ_MASK, 0) | + CFIELD_PREP(ST7701_CMD2_BK0_GAMCTRL_VC4_MASK, 0x19), + CFIELD_PREP(ST7701_CMD2_BK0_GAMCTRL_AJ_MASK, 0) | + CFIELD_PREP(ST7701_CMD2_BK0_GAMCTRL_VC8_MASK, 0x1f), + CFIELD_PREP(ST7701_CMD2_BK0_GAMCTRL_VC16_MASK, 0x0f), + + CFIELD_PREP(ST7701_CMD2_BK0_GAMCTRL_AJ_MASK, 0) | + CFIELD_PREP(ST7701_CMD2_BK0_GAMCTRL_VC24_MASK, 0x14), + CFIELD_PREP(ST7701_CMD2_BK0_GAMCTRL_VC52_MASK, 0x07), + CFIELD_PREP(ST7701_CMD2_BK0_GAMCTRL_VC80_MASK, 0x07), + CFIELD_PREP(ST7701_CMD2_BK0_GAMCTRL_VC108_MASK, 0x08), + + CFIELD_PREP(ST7701_CMD2_BK0_GAMCTRL_VC147_MASK, 0x07), + CFIELD_PREP(ST7701_CMD2_BK0_GAMCTRL_VC175_MASK, 0x22), + CFIELD_PREP(ST7701_CMD2_BK0_GAMCTRL_VC203_MASK, 0x02), + CFIELD_PREP(ST7701_CMD2_BK0_GAMCTRL_AJ_MASK, 0) | + CFIELD_PREP(ST7701_CMD2_BK0_GAMCTRL_VC231_MASK, 0xf), + + CFIELD_PREP(ST7701_CMD2_BK0_GAMCTRL_VC239_MASK, 0x0f), + CFIELD_PREP(ST7701_CMD2_BK0_GAMCTRL_AJ_MASK, 0) | + CFIELD_PREP(ST7701_CMD2_BK0_GAMCTRL_VC247_MASK, 0xa3), + CFIELD_PREP(ST7701_CMD2_BK0_GAMCTRL_AJ_MASK, 0) | + CFIELD_PREP(ST7701_CMD2_BK0_GAMCTRL_VC251_MASK, 0x29), + CFIELD_PREP(ST7701_CMD2_BK0_GAMCTRL_AJ_MASK, 0) | + CFIELD_PREP(ST7701_CMD2_BK0_GAMCTRL_VC255_MASK, 0x0d) + }, + .nlinv = 3, + .vop_uv = 4737500, + .vcom_uv = 662500, + .vgh_mv = 15000, + .vgl_mv = -10170, + .avdd_mv = 6600, + .avcl_mv = -4600, + .gamma_op_bias = OP_BIAS_MIDDLE, + .input_op_bias = OP_BIAS_MIDDLE, + .output_op_bias = OP_BIAS_MIN, + .t2d_ns = 1600, + .t3d_ns = 10400, + .eot_en = true, + .gip_sequence = wf40eswaa6mnn0_gip_sequence, +}; + static void st7701_cleanup(void *data) { struct st7701 *st7701 = (struct st7701 *)data; @@ -1150,9 +1273,10 @@ static int st7701_probe(struct device *dev, int connector_type) struct st7701 *st7701; int ret; - st7701 = devm_kzalloc(dev, sizeof(*st7701), GFP_KERNEL); - if (!st7701) - return -ENOMEM; + st7701 = devm_drm_panel_alloc(dev, struct st7701, panel, &st7701_funcs, + connector_type); + if (IS_ERR(st7701)) + return PTR_ERR(st7701); desc = of_device_get_match_data(dev); if (!desc) @@ -1176,7 +1300,6 @@ static int st7701_probe(struct device *dev, int connector_type) if (ret < 0) return dev_err_probe(dev, ret, "Failed to get orientation\n"); - drm_panel_init(&st7701->panel, dev, &st7701_funcs, connector_type); st7701->panel.prepare_prev_first = true; /** @@ -1265,6 +1388,7 @@ static const struct of_device_id st7701_dsi_of_match[] = { { .compatible = "densitron,dmt028vghmcmi-1a", .data = &dmt028vghmcmi_1a_desc }, { .compatible = "elida,kd50t048a", .data = &kd50t048a_desc }, { .compatible = "techstar,ts8550b", .data = &ts8550b_desc }, + { .compatible = "winstar,wf40eswaa6mnn0", .data = &wf40eswaa6mnn0_desc }, { } }; MODULE_DEVICE_TABLE(of, st7701_dsi_of_match); diff --git a/drivers/gpu/drm/panel/panel-sitronix-st7703.c b/drivers/gpu/drm/panel/panel-sitronix-st7703.c index 67e8e45498cb..1a007a244d84 100644 --- a/drivers/gpu/drm/panel/panel-sitronix-st7703.c +++ b/drivers/gpu/drm/panel/panel-sitronix-st7703.c @@ -846,9 +846,11 @@ static int st7703_probe(struct mipi_dsi_device *dsi) struct st7703 *ctx; int ret; - ctx = devm_kzalloc(dev, sizeof(*ctx), GFP_KERNEL); - if (!ctx) - return -ENOMEM; + ctx = devm_drm_panel_alloc(dev, struct st7703, panel, + &st7703_drm_funcs, + DRM_MODE_CONNECTOR_DSI); + if (IS_ERR(ctx)) + return PTR_ERR(ctx); ctx->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_LOW); if (IS_ERR(ctx->reset_gpio)) @@ -876,9 +878,6 @@ static int st7703_probe(struct mipi_dsi_device *dsi) if (ret < 0) return dev_err_probe(&dsi->dev, ret, "Failed to get orientation\n"); - drm_panel_init(&ctx->panel, dev, &st7703_drm_funcs, - DRM_MODE_CONNECTOR_DSI); - ret = drm_panel_of_backlight(&ctx->panel); if (ret) return ret; diff --git a/drivers/gpu/drm/panel/panel-sitronix-st7789v.c b/drivers/gpu/drm/panel/panel-sitronix-st7789v.c index 28bfc48a9127..04d91929eedd 100644 --- a/drivers/gpu/drm/panel/panel-sitronix-st7789v.c +++ b/drivers/gpu/drm/panel/panel-sitronix-st7789v.c @@ -612,9 +612,10 @@ static int st7789v_probe(struct spi_device *spi) struct st7789v *ctx; int ret; - ctx = devm_kzalloc(dev, sizeof(*ctx), GFP_KERNEL); - if (!ctx) - return -ENOMEM; + ctx = devm_drm_panel_alloc(dev, struct st7789v, panel, + &st7789v_drm_funcs, DRM_MODE_CONNECTOR_DPI); + if (IS_ERR(ctx)) + return PTR_ERR(ctx); spi_set_drvdata(spi, ctx); ctx->spi = spi; @@ -626,9 +627,6 @@ static int st7789v_probe(struct spi_device *spi) ctx->info = device_get_match_data(&spi->dev); - drm_panel_init(&ctx->panel, dev, &st7789v_drm_funcs, - DRM_MODE_CONNECTOR_DPI); - ctx->power = devm_regulator_get(dev, "power"); ret = PTR_ERR_OR_ZERO(ctx->power); if (ret) diff --git a/drivers/gpu/drm/panel/panel-sony-acx565akm.c b/drivers/gpu/drm/panel/panel-sony-acx565akm.c index d437f5c84f5f..fe043de791b0 100644 --- a/drivers/gpu/drm/panel/panel-sony-acx565akm.c +++ b/drivers/gpu/drm/panel/panel-sony-acx565akm.c @@ -607,9 +607,10 @@ static int acx565akm_probe(struct spi_device *spi) struct acx565akm_panel *lcd; int ret; - lcd = devm_kzalloc(&spi->dev, sizeof(*lcd), GFP_KERNEL); - if (!lcd) - return -ENOMEM; + lcd = devm_drm_panel_alloc(&spi->dev, struct acx565akm_panel, panel, + &acx565akm_funcs, DRM_MODE_CONNECTOR_DPI); + if (IS_ERR(lcd)) + return PTR_ERR(lcd); spi_set_drvdata(spi, lcd); spi->mode = SPI_MODE_3; @@ -635,9 +636,6 @@ static int acx565akm_probe(struct spi_device *spi) return ret; } - drm_panel_init(&lcd->panel, &lcd->spi->dev, &acx565akm_funcs, - DRM_MODE_CONNECTOR_DPI); - drm_panel_add(&lcd->panel); return 0; diff --git a/drivers/gpu/drm/panel/panel-sony-td4353-jdi.c b/drivers/gpu/drm/panel/panel-sony-td4353-jdi.c index 97f4bb4e1029..7c989b70ab51 100644 --- a/drivers/gpu/drm/panel/panel-sony-td4353-jdi.c +++ b/drivers/gpu/drm/panel/panel-sony-td4353-jdi.c @@ -175,9 +175,11 @@ static int sony_td4353_jdi_probe(struct mipi_dsi_device *dsi) struct sony_td4353_jdi *ctx; int ret; - ctx = devm_kzalloc(dev, sizeof(*ctx), GFP_KERNEL); - if (!ctx) - return -ENOMEM; + ctx = devm_drm_panel_alloc(dev, struct sony_td4353_jdi, panel, + &sony_td4353_jdi_panel_funcs, + DRM_MODE_CONNECTOR_DSI); + if (IS_ERR(ctx)) + return PTR_ERR(ctx); ctx->type = (uintptr_t)of_device_get_match_data(dev); @@ -206,9 +208,6 @@ static int sony_td4353_jdi_probe(struct mipi_dsi_device *dsi) dsi->format = MIPI_DSI_FMT_RGB888; dsi->mode_flags = MIPI_DSI_CLOCK_NON_CONTINUOUS; - drm_panel_init(&ctx->panel, dev, &sony_td4353_jdi_panel_funcs, - DRM_MODE_CONNECTOR_DSI); - ret = drm_panel_of_backlight(&ctx->panel); if (ret) return dev_err_probe(dev, ret, "Failed to get backlight\n"); diff --git a/drivers/gpu/drm/panel/panel-sony-tulip-truly-nt35521.c b/drivers/gpu/drm/panel/panel-sony-tulip-truly-nt35521.c index 104b2290560e..216a6ad8696e 100644 --- a/drivers/gpu/drm/panel/panel-sony-tulip-truly-nt35521.c +++ b/drivers/gpu/drm/panel/panel-sony-tulip-truly-nt35521.c @@ -433,9 +433,11 @@ static int truly_nt35521_probe(struct mipi_dsi_device *dsi) struct truly_nt35521 *ctx; int ret; - ctx = devm_kzalloc(dev, sizeof(*ctx), GFP_KERNEL); - if (!ctx) - return -ENOMEM; + ctx = devm_drm_panel_alloc(dev, struct truly_nt35521, panel, + &truly_nt35521_panel_funcs, + DRM_MODE_CONNECTOR_DSI); + if (IS_ERR(ctx)) + return PTR_ERR(ctx); ctx->supplies[0].supply = "positive5"; ctx->supplies[1].supply = "negative5"; @@ -465,9 +467,6 @@ static int truly_nt35521_probe(struct mipi_dsi_device *dsi) MIPI_DSI_MODE_VIDEO_HSE | MIPI_DSI_MODE_NO_EOT_PACKET | MIPI_DSI_CLOCK_NON_CONTINUOUS; - drm_panel_init(&ctx->panel, dev, &truly_nt35521_panel_funcs, - DRM_MODE_CONNECTOR_DSI); - ctx->panel.backlight = truly_nt35521_create_backlight(dsi); if (IS_ERR(ctx->panel.backlight)) return dev_err_probe(dev, PTR_ERR(ctx->panel.backlight), diff --git a/drivers/gpu/drm/panel/panel-summit.c b/drivers/gpu/drm/panel/panel-summit.c index e780faee1857..4854437e2899 100644 --- a/drivers/gpu/drm/panel/panel-summit.c +++ b/drivers/gpu/drm/panel/panel-summit.c @@ -68,9 +68,11 @@ static int summit_probe(struct mipi_dsi_device *dsi) struct summit_data *s_data; int ret; - s_data = devm_kzalloc(dev, sizeof(*s_data), GFP_KERNEL); - if (!s_data) - return -ENOMEM; + s_data = devm_drm_panel_alloc(dev, struct summit_data, panel, + &summit_panel_funcs, + DRM_MODE_CONNECTOR_DSI); + if (IS_ERR(s_data)) + return PTR_ERR(s_data); mipi_dsi_set_drvdata(dsi, s_data); s_data->dsi = dsi; @@ -85,8 +87,6 @@ static int summit_probe(struct mipi_dsi_device *dsi) if (IS_ERR(s_data->bl)) return PTR_ERR(s_data->bl); - drm_panel_init(&s_data->panel, dev, &summit_panel_funcs, - DRM_MODE_CONNECTOR_DSI); drm_panel_add(&s_data->panel); return mipi_dsi_attach(dsi); diff --git a/drivers/gpu/drm/panel/panel-synaptics-r63353.c b/drivers/gpu/drm/panel/panel-synaptics-r63353.c index b148e6cba9bd..3a74d48753d9 100644 --- a/drivers/gpu/drm/panel/panel-synaptics-r63353.c +++ b/drivers/gpu/drm/panel/panel-synaptics-r63353.c @@ -229,9 +229,11 @@ static int r63353_panel_probe(struct mipi_dsi_device *dsi) struct device *dev = &dsi->dev; struct r63353_panel *panel; - panel = devm_kzalloc(&dsi->dev, sizeof(*panel), GFP_KERNEL); - if (!panel) - return -ENOMEM; + panel = devm_drm_panel_alloc(dev, struct r63353_panel, base, + &r63353_panel_funcs, + DRM_MODE_CONNECTOR_DSI); + if (IS_ERR(panel)) + return PTR_ERR(panel); mipi_dsi_set_drvdata(dsi, panel); panel->dsi = dsi; @@ -258,9 +260,6 @@ static int r63353_panel_probe(struct mipi_dsi_device *dsi) return PTR_ERR(panel->reset_gpio); } - drm_panel_init(&panel->base, dev, &r63353_panel_funcs, - DRM_MODE_CONNECTOR_DSI); - panel->base.prepare_prev_first = true; ret = drm_panel_of_backlight(&panel->base); if (ret) diff --git a/drivers/gpu/drm/panel/panel-tpo-td028ttec1.c b/drivers/gpu/drm/panel/panel-tpo-td028ttec1.c index 11d460d2ea19..ee86ff20c1bd 100644 --- a/drivers/gpu/drm/panel/panel-tpo-td028ttec1.c +++ b/drivers/gpu/drm/panel/panel-tpo-td028ttec1.c @@ -318,9 +318,11 @@ static int td028ttec1_probe(struct spi_device *spi) struct td028ttec1_panel *lcd; int ret; - lcd = devm_kzalloc(&spi->dev, sizeof(*lcd), GFP_KERNEL); - if (!lcd) - return -ENOMEM; + lcd = devm_drm_panel_alloc(&spi->dev, struct td028ttec1_panel, panel, + &td028ttec1_funcs, + DRM_MODE_CONNECTOR_DPI); + if (IS_ERR(lcd)) + return PTR_ERR(lcd); spi_set_drvdata(spi, lcd); lcd->spi = spi; @@ -334,9 +336,6 @@ static int td028ttec1_probe(struct spi_device *spi) return ret; } - drm_panel_init(&lcd->panel, &lcd->spi->dev, &td028ttec1_funcs, - DRM_MODE_CONNECTOR_DPI); - ret = drm_panel_of_backlight(&lcd->panel); if (ret) return ret; diff --git a/drivers/gpu/drm/panel/panel-tpo-td043mtea1.c b/drivers/gpu/drm/panel/panel-tpo-td043mtea1.c index cf4609bb9b1d..b18af526b54c 100644 --- a/drivers/gpu/drm/panel/panel-tpo-td043mtea1.c +++ b/drivers/gpu/drm/panel/panel-tpo-td043mtea1.c @@ -421,9 +421,10 @@ static int td043mtea1_probe(struct spi_device *spi) struct td043mtea1_panel *lcd; int ret; - lcd = devm_kzalloc(&spi->dev, sizeof(*lcd), GFP_KERNEL); - if (lcd == NULL) - return -ENOMEM; + lcd = devm_drm_panel_alloc(&spi->dev, struct td043mtea1_panel, panel, + &td043mtea1_funcs, DRM_MODE_CONNECTOR_DPI); + if (IS_ERR(lcd)) + return PTR_ERR(lcd); spi_set_drvdata(spi, lcd); lcd->spi = spi; @@ -455,9 +456,6 @@ static int td043mtea1_probe(struct spi_device *spi) return ret; } - drm_panel_init(&lcd->panel, &lcd->spi->dev, &td043mtea1_funcs, - DRM_MODE_CONNECTOR_DPI); - drm_panel_add(&lcd->panel); return 0; diff --git a/drivers/gpu/drm/panel/panel-tpo-tpg110.c b/drivers/gpu/drm/panel/panel-tpo-tpg110.c index f6a212e542cb..0beba5c08956 100644 --- a/drivers/gpu/drm/panel/panel-tpo-tpg110.c +++ b/drivers/gpu/drm/panel/panel-tpo-tpg110.c @@ -405,9 +405,11 @@ static int tpg110_probe(struct spi_device *spi) struct tpg110 *tpg; int ret; - tpg = devm_kzalloc(dev, sizeof(*tpg), GFP_KERNEL); - if (!tpg) - return -ENOMEM; + tpg = devm_drm_panel_alloc(dev, struct tpg110, panel, + &tpg110_drm_funcs, DRM_MODE_CONNECTOR_DPI); + if (IS_ERR(tpg)) + return PTR_ERR(tpg); + tpg->dev = dev; /* We get the physical display dimensions from the DT */ @@ -438,9 +440,6 @@ static int tpg110_probe(struct spi_device *spi) if (ret) return ret; - drm_panel_init(&tpg->panel, dev, &tpg110_drm_funcs, - DRM_MODE_CONNECTOR_DPI); - ret = drm_panel_of_backlight(&tpg->panel); if (ret) return ret; diff --git a/drivers/gpu/drm/panel/panel-visionox-r66451.c b/drivers/gpu/drm/panel/panel-visionox-r66451.c index 3ea0a86f6e69..690cccedd438 100644 --- a/drivers/gpu/drm/panel/panel-visionox-r66451.c +++ b/drivers/gpu/drm/panel/panel-visionox-r66451.c @@ -255,9 +255,11 @@ static int visionox_r66451_probe(struct mipi_dsi_device *dsi) struct drm_dsc_config *dsc; int ret = 0; - ctx = devm_kzalloc(dev, sizeof(*ctx), GFP_KERNEL); - if (!ctx) - return -ENOMEM; + ctx = devm_drm_panel_alloc(dev, struct visionox_r66451, panel, + &visionox_r66451_funcs, + DRM_MODE_CONNECTOR_DSI); + if (IS_ERR(ctx)) + return PTR_ERR(ctx); dsc = devm_kzalloc(dev, sizeof(*dsc), GFP_KERNEL); if (!dsc) @@ -297,7 +299,6 @@ static int visionox_r66451_probe(struct mipi_dsi_device *dsi) dsi->mode_flags = MIPI_DSI_MODE_LPM | MIPI_DSI_CLOCK_NON_CONTINUOUS; ctx->panel.prepare_prev_first = true; - drm_panel_init(&ctx->panel, dev, &visionox_r66451_funcs, DRM_MODE_CONNECTOR_DSI); ctx->panel.backlight = visionox_r66451_create_backlight(dsi); if (IS_ERR(ctx->panel.backlight)) return dev_err_probe(dev, PTR_ERR(ctx->panel.backlight), diff --git a/drivers/gpu/drm/panel/panel-visionox-rm69299.c b/drivers/gpu/drm/panel/panel-visionox-rm69299.c index be3a9797fbce..909c280eab1f 100644 --- a/drivers/gpu/drm/panel/panel-visionox-rm69299.c +++ b/drivers/gpu/drm/panel/panel-visionox-rm69299.c @@ -5,6 +5,7 @@ #include <linux/delay.h> #include <linux/module.h> +#include <linux/property.h> #include <linux/mod_devicetable.h> #include <linux/gpio/consumer.h> #include <linux/regulator/consumer.h> @@ -15,11 +16,138 @@ #include <drm/drm_modes.h> #include <drm/drm_panel.h> +struct visionox_rm69299_panel_desc { + const struct drm_display_mode *mode; + const u8 *init_seq; + unsigned int init_seq_len; +}; + struct visionox_rm69299 { struct drm_panel panel; - struct regulator_bulk_data supplies[2]; + struct regulator_bulk_data *supplies; struct gpio_desc *reset_gpio; struct mipi_dsi_device *dsi; + const struct visionox_rm69299_panel_desc *desc; +}; + +static const struct regulator_bulk_data visionox_rm69299_supplies[] = { + { .supply = "vdda", .init_load_uA = 32000 }, + { .supply = "vdd3p3", .init_load_uA = 13200 }, +}; + +static const u8 visionox_rm69299_1080x2248_60hz_init_seq[][2] = { + { 0xfe, 0x00 }, { 0xc2, 0x08 }, { 0x35, 0x00 }, { 0x51, 0xff }, +}; + +static const u8 visionox_rm69299_1080x2160_60hz_init_seq[][2] = { + { 0xfe, 0x40 }, { 0x05, 0x04 }, { 0x06, 0x08 }, { 0x08, 0x04 }, + { 0x09, 0x08 }, { 0x0a, 0x07 }, { 0x0b, 0xcc }, { 0x0c, 0x07 }, + { 0x0d, 0x90 }, { 0x0f, 0x87 }, { 0x20, 0x8d }, { 0x21, 0x8d }, + { 0x24, 0x05 }, { 0x26, 0x05 }, { 0x28, 0x05 }, { 0x2a, 0x05 }, + { 0x2d, 0x28 }, { 0x2f, 0x28 }, { 0x30, 0x32 }, { 0x31, 0x32 }, + { 0x37, 0x80 }, { 0x38, 0x30 }, { 0x39, 0xa8 }, { 0x46, 0x48 }, + { 0x47, 0x48 }, { 0x6b, 0x10 }, { 0x6f, 0x02 }, { 0x74, 0x2b }, + { 0x80, 0x1a }, { 0xfe, 0x40 }, { 0x93, 0x10 }, { 0x16, 0x00 }, + { 0x85, 0x07 }, { 0x84, 0x01 }, { 0x86, 0x0f }, { 0x87, 0x05 }, + { 0x8c, 0x00 }, { 0x88, 0x2e }, { 0x89, 0x2e }, { 0x8b, 0x09 }, + { 0x95, 0x00 }, { 0x91, 0x00 }, { 0x90, 0x00 }, { 0x8d, 0xd0 }, + { 0x8a, 0x03 }, { 0xfe, 0xa0 }, { 0x13, 0x00 }, { 0x33, 0x00 }, + { 0x0b, 0x33 }, { 0x36, 0x1e }, { 0x31, 0x88 }, { 0x32, 0x88 }, + { 0x37, 0xf1 }, { 0xfe, 0x50 }, { 0x00, 0x00 }, { 0x01, 0x00 }, + { 0x02, 0x00 }, { 0x03, 0xe9 }, { 0x04, 0x00 }, { 0x05, 0xf6 }, + { 0x06, 0x01 }, { 0x07, 0x2c }, { 0x08, 0x01 }, { 0x09, 0x62 }, + { 0x0a, 0x01 }, { 0x0b, 0x98 }, { 0x0c, 0x01 }, { 0x0d, 0xbf }, + { 0x0e, 0x01 }, { 0x0f, 0xf6 }, { 0x10, 0x02 }, { 0x11, 0x24 }, + { 0x12, 0x02 }, { 0x13, 0x4e }, { 0x14, 0x02 }, { 0x15, 0x70 }, + { 0x16, 0x02 }, { 0x17, 0xaf }, { 0x18, 0x02 }, { 0x19, 0xe2 }, + { 0x1a, 0x03 }, { 0x1b, 0x1f }, { 0x1c, 0x03 }, { 0x1d, 0x52 }, + { 0x1e, 0x03 }, { 0x1f, 0x82 }, { 0x20, 0x03 }, { 0x21, 0xb6 }, + { 0x22, 0x03 }, { 0x23, 0xf0 }, { 0x24, 0x04 }, { 0x25, 0x1f }, + { 0x26, 0x04 }, { 0x27, 0x37 }, { 0x28, 0x04 }, { 0x29, 0x59 }, + { 0x2a, 0x04 }, { 0x2b, 0x68 }, { 0x30, 0x04 }, { 0x31, 0x85 }, + { 0x32, 0x04 }, { 0x33, 0xa2 }, { 0x34, 0x04 }, { 0x35, 0xbc }, + { 0x36, 0x04 }, { 0x37, 0xd8 }, { 0x38, 0x04 }, { 0x39, 0xf4 }, + { 0x3a, 0x05 }, { 0x3b, 0x0e }, { 0x40, 0x05 }, { 0x41, 0x13 }, + { 0x42, 0x05 }, { 0x43, 0x1f }, { 0x44, 0x05 }, { 0x45, 0x1f }, + { 0x46, 0x00 }, { 0x47, 0x00 }, { 0x48, 0x01 }, { 0x49, 0x43 }, + { 0x4a, 0x01 }, { 0x4b, 0x4c }, { 0x4c, 0x01 }, { 0x4d, 0x6f }, + { 0x4e, 0x01 }, { 0x4f, 0x92 }, { 0x50, 0x01 }, { 0x51, 0xb5 }, + { 0x52, 0x01 }, { 0x53, 0xd4 }, { 0x58, 0x02 }, { 0x59, 0x06 }, + { 0x5a, 0x02 }, { 0x5b, 0x33 }, { 0x5c, 0x02 }, { 0x5d, 0x59 }, + { 0x5e, 0x02 }, { 0x5f, 0x7d }, { 0x60, 0x02 }, { 0x61, 0xbd }, + { 0x62, 0x02 }, { 0x63, 0xf7 }, { 0x64, 0x03 }, { 0x65, 0x31 }, + { 0x66, 0x03 }, { 0x67, 0x63 }, { 0x68, 0x03 }, { 0x69, 0x9d }, + { 0x6a, 0x03 }, { 0x6b, 0xd2 }, { 0x6c, 0x04 }, { 0x6d, 0x05 }, + { 0x6e, 0x04 }, { 0x6f, 0x38 }, { 0x70, 0x04 }, { 0x71, 0x51 }, + { 0x72, 0x04 }, { 0x73, 0x70 }, { 0x74, 0x04 }, { 0x75, 0x85 }, + { 0x76, 0x04 }, { 0x77, 0xa1 }, { 0x78, 0x04 }, { 0x79, 0xc0 }, + { 0x7a, 0x04 }, { 0x7b, 0xd8 }, { 0x7c, 0x04 }, { 0x7d, 0xf2 }, + { 0x7e, 0x05 }, { 0x7f, 0x10 }, { 0x80, 0x05 }, { 0x81, 0x21 }, + { 0x82, 0x05 }, { 0x83, 0x2e }, { 0x84, 0x05 }, { 0x85, 0x3a }, + { 0x86, 0x05 }, { 0x87, 0x3e }, { 0x88, 0x00 }, { 0x89, 0x00 }, + { 0x8a, 0x01 }, { 0x8b, 0x86 }, { 0x8c, 0x01 }, { 0x8d, 0x8f }, + { 0x8e, 0x01 }, { 0x8f, 0xb3 }, { 0x90, 0x01 }, { 0x91, 0xd7 }, + { 0x92, 0x01 }, { 0x93, 0xfb }, { 0x94, 0x02 }, { 0x95, 0x18 }, + { 0x96, 0x02 }, { 0x97, 0x4f }, { 0x98, 0x02 }, { 0x99, 0x7e }, + { 0x9a, 0x02 }, { 0x9b, 0xa6 }, { 0x9c, 0x02 }, { 0x9d, 0xcf }, + { 0x9e, 0x03 }, { 0x9f, 0x14 }, { 0xa4, 0x03 }, { 0xa5, 0x52 }, + { 0xa6, 0x03 }, { 0xa7, 0x93 }, { 0xac, 0x03 }, { 0xad, 0xcf }, + { 0xae, 0x04 }, { 0xaf, 0x08 }, { 0xb0, 0x04 }, { 0xb1, 0x42 }, + { 0xb2, 0x04 }, { 0xb3, 0x7f }, { 0xb4, 0x04 }, { 0xb5, 0xb4 }, + { 0xb6, 0x04 }, { 0xb7, 0xcc }, { 0xb8, 0x04 }, { 0xb9, 0xf2 }, + { 0xba, 0x05 }, { 0xbb, 0x0c }, { 0xbc, 0x05 }, { 0xbd, 0x26 }, + { 0xbe, 0x05 }, { 0xbf, 0x4b }, { 0xc0, 0x05 }, { 0xc1, 0x64 }, + { 0xc2, 0x05 }, { 0xc3, 0x83 }, { 0xc4, 0x05 }, { 0xc5, 0xa1 }, + { 0xc6, 0x05 }, { 0xc7, 0xba }, { 0xc8, 0x05 }, { 0xc9, 0xc4 }, + { 0xca, 0x05 }, { 0xcb, 0xd5 }, { 0xcc, 0x05 }, { 0xcd, 0xd5 }, + { 0xce, 0x00 }, { 0xcf, 0xce }, { 0xd0, 0x00 }, { 0xd1, 0xdb }, + { 0xd2, 0x01 }, { 0xd3, 0x32 }, { 0xd4, 0x01 }, { 0xd5, 0x3b }, + { 0xd6, 0x01 }, { 0xd7, 0x74 }, { 0xd8, 0x01 }, { 0xd9, 0x7d }, + { 0xfe, 0x60 }, { 0x00, 0xcc }, { 0x01, 0x0f }, { 0x02, 0xff }, + { 0x03, 0x01 }, { 0x04, 0x00 }, { 0x05, 0x02 }, { 0x06, 0x00 }, + { 0x07, 0x00 }, { 0x09, 0xc4 }, { 0x0a, 0x00 }, { 0x0b, 0x04 }, + { 0x0c, 0x01 }, { 0x0d, 0x00 }, { 0x0e, 0x04 }, { 0x0f, 0x00 }, + { 0x10, 0x71 }, { 0x12, 0xc4 }, { 0x13, 0x00 }, { 0x14, 0x04 }, + { 0x15, 0x01 }, { 0x16, 0x00 }, { 0x17, 0x06 }, { 0x18, 0x00 }, + { 0x19, 0x71 }, { 0x1b, 0xc4 }, { 0x1c, 0x00 }, { 0x1d, 0x02 }, + { 0x1e, 0x00 }, { 0x1f, 0x00 }, { 0x20, 0x08 }, { 0x21, 0x66 }, + { 0x22, 0xb4 }, { 0x24, 0xc4 }, { 0x25, 0x00 }, { 0x26, 0x02 }, + { 0x27, 0x00 }, { 0x28, 0x00 }, { 0x29, 0x07 }, { 0x2a, 0x66 }, + { 0x2b, 0xb4 }, { 0x2f, 0xc4 }, { 0x30, 0x00 }, { 0x31, 0x04 }, + { 0x32, 0x01 }, { 0x33, 0x00 }, { 0x34, 0x03 }, { 0x35, 0x00 }, + { 0x36, 0x71 }, { 0x38, 0xc4 }, { 0x39, 0x00 }, { 0x3a, 0x04 }, + { 0x3b, 0x01 }, { 0x3d, 0x00 }, { 0x3f, 0x05 }, { 0x40, 0x00 }, + { 0x41, 0x71 }, { 0x83, 0xce }, { 0x84, 0x02 }, { 0x85, 0x20 }, + { 0x86, 0xdc }, { 0x87, 0x00 }, { 0x88, 0x04 }, { 0x89, 0x00 }, + { 0x8a, 0xbb }, { 0x8b, 0x80 }, { 0xc7, 0x0e }, { 0xc8, 0x05 }, + { 0xc9, 0x1f }, { 0xca, 0x06 }, { 0xcb, 0x00 }, { 0xcc, 0x03 }, + { 0xcd, 0x04 }, { 0xce, 0x1f }, { 0xcf, 0x1f }, { 0xd0, 0x1f }, + { 0xd1, 0x1f }, { 0xd2, 0x1f }, { 0xd3, 0x1f }, { 0xd4, 0x1f }, + { 0xd5, 0x1f }, { 0xd6, 0x1f }, { 0xd7, 0x17 }, { 0xd8, 0x1f }, + { 0xd9, 0x16 }, { 0xda, 0x1f }, { 0xdb, 0x0e }, { 0xdc, 0x01 }, + { 0xdd, 0x1f }, { 0xde, 0x02 }, { 0xdf, 0x00 }, { 0xe0, 0x03 }, + { 0xe1, 0x04 }, { 0xe2, 0x1f }, { 0xe3, 0x1f }, { 0xe4, 0x1f }, + { 0xe5, 0x1f }, { 0xe6, 0x1f }, { 0xe7, 0x1f }, { 0xe8, 0x1f }, + { 0xe9, 0x1f }, { 0xea, 0x1f }, { 0xeb, 0x17 }, { 0xec, 0x1f }, + { 0xed, 0x16 }, { 0xee, 0x1f }, { 0xef, 0x03 }, { 0xfe, 0x70 }, + { 0x5a, 0x0b }, { 0x5b, 0x0b }, { 0x5c, 0x55 }, { 0x5d, 0x24 }, + { 0xfe, 0x90 }, { 0x12, 0x24 }, { 0x13, 0x49 }, { 0x14, 0x92 }, + { 0x15, 0x86 }, { 0x16, 0x61 }, { 0x17, 0x18 }, { 0x18, 0x24 }, + { 0x19, 0x49 }, { 0x1a, 0x92 }, { 0x1b, 0x86 }, { 0x1c, 0x61 }, + { 0x1d, 0x18 }, { 0x1e, 0x24 }, { 0x1f, 0x49 }, { 0x20, 0x92 }, + { 0x21, 0x86 }, { 0x22, 0x61 }, { 0x23, 0x18 }, { 0xfe, 0x40 }, + { 0x0e, 0x10 }, { 0xfe, 0xa0 }, { 0x04, 0x80 }, { 0x16, 0x00 }, + { 0x26, 0x10 }, { 0x2f, 0x37 }, { 0xfe, 0xd0 }, { 0x06, 0x0f }, + { 0x4b, 0x00 }, { 0x56, 0x4a }, { 0xfe, 0x00 }, { 0xc2, 0x09 }, + { 0x35, 0x00 }, { 0xfe, 0x70 }, { 0x7d, 0x61 }, { 0x7f, 0x00 }, + { 0x7e, 0x4e }, { 0x52, 0x2c }, { 0x49, 0x00 }, { 0x4a, 0x00 }, + { 0x4b, 0x00 }, { 0x4c, 0x00 }, { 0x4d, 0xe8 }, { 0x4e, 0x25 }, + { 0x4f, 0x6e }, { 0x50, 0xae }, { 0x51, 0x2f }, { 0xad, 0xf4 }, + { 0xae, 0x8f }, { 0xaf, 0x00 }, { 0xb0, 0x54 }, { 0xb1, 0x3a }, + { 0xb2, 0x00 }, { 0xb3, 0x00 }, { 0xb4, 0x00 }, { 0xb5, 0x00 }, + { 0xb6, 0x18 }, { 0xb7, 0x30 }, { 0xb8, 0x4a }, { 0xb9, 0x98 }, + { 0xba, 0x30 }, { 0xbb, 0x60 }, { 0xbc, 0x50 }, { 0xbd, 0x00 }, + { 0xbe, 0x00 }, { 0xbf, 0x39 }, { 0xfe, 0x00 }, { 0x51, 0x66 }, }; static inline struct visionox_rm69299 *panel_to_ctx(struct drm_panel *panel) @@ -31,7 +159,8 @@ static int visionox_rm69299_power_on(struct visionox_rm69299 *ctx) { int ret; - ret = regulator_bulk_enable(ARRAY_SIZE(ctx->supplies), ctx->supplies); + ret = regulator_bulk_enable(ARRAY_SIZE(visionox_rm69299_supplies), + ctx->supplies); if (ret < 0) return ret; @@ -54,37 +183,32 @@ static int visionox_rm69299_power_off(struct visionox_rm69299 *ctx) { gpiod_set_value(ctx->reset_gpio, 0); - return regulator_bulk_disable(ARRAY_SIZE(ctx->supplies), ctx->supplies); + return regulator_bulk_disable(ARRAY_SIZE(visionox_rm69299_supplies), + ctx->supplies); } static int visionox_rm69299_unprepare(struct drm_panel *panel) { struct visionox_rm69299 *ctx = panel_to_ctx(panel); - int ret; + struct mipi_dsi_multi_context dsi_ctx = { .dsi = ctx->dsi }; ctx->dsi->mode_flags = 0; - ret = mipi_dsi_dcs_write(ctx->dsi, MIPI_DCS_SET_DISPLAY_OFF, NULL, 0); - if (ret < 0) - dev_err(ctx->panel.dev, "set_display_off cmd failed ret = %d\n", ret); + mipi_dsi_dcs_set_display_off_multi(&dsi_ctx); /* 120ms delay required here as per DCS spec */ - msleep(120); - - ret = mipi_dsi_dcs_write(ctx->dsi, MIPI_DCS_ENTER_SLEEP_MODE, NULL, 0); - if (ret < 0) { - dev_err(ctx->panel.dev, "enter_sleep cmd failed ret = %d\n", ret); - } + mipi_dsi_msleep(&dsi_ctx, 120); - ret = visionox_rm69299_power_off(ctx); + mipi_dsi_dcs_enter_sleep_mode_multi(&dsi_ctx); - return ret; + return visionox_rm69299_power_off(ctx); } static int visionox_rm69299_prepare(struct drm_panel *panel) { struct visionox_rm69299 *ctx = panel_to_ctx(panel); - int ret; + struct mipi_dsi_multi_context dsi_ctx = { .dsi = ctx->dsi }; + int ret, i; ret = visionox_rm69299_power_on(ctx); if (ret < 0) @@ -92,52 +216,20 @@ static int visionox_rm69299_prepare(struct drm_panel *panel) ctx->dsi->mode_flags |= MIPI_DSI_MODE_LPM; - ret = mipi_dsi_dcs_write_buffer(ctx->dsi, (u8[]) { 0xfe, 0x00 }, 2); - if (ret < 0) { - dev_err(ctx->panel.dev, "cmd set tx 0 failed, ret = %d\n", ret); - goto power_off; - } - - ret = mipi_dsi_dcs_write_buffer(ctx->dsi, (u8[]) { 0xc2, 0x08 }, 2); - if (ret < 0) { - dev_err(ctx->panel.dev, "cmd set tx 1 failed, ret = %d\n", ret); - goto power_off; - } + for (i = 0; i < ctx->desc->init_seq_len; i++) + mipi_dsi_dcs_write_buffer_multi(&dsi_ctx, &ctx->desc->init_seq[i * 2], 2); - ret = mipi_dsi_dcs_write_buffer(ctx->dsi, (u8[]) { 0x35, 0x00 }, 2); - if (ret < 0) { - dev_err(ctx->panel.dev, "cmd set tx 2 failed, ret = %d\n", ret); - goto power_off; - } - - ret = mipi_dsi_dcs_write_buffer(ctx->dsi, (u8[]) { 0x51, 0xff }, 2); - if (ret < 0) { - dev_err(ctx->panel.dev, "cmd set tx 3 failed, ret = %d\n", ret); - goto power_off; - } - - ret = mipi_dsi_dcs_write(ctx->dsi, MIPI_DCS_EXIT_SLEEP_MODE, NULL, 0); - if (ret < 0) { - dev_err(ctx->panel.dev, "exit_sleep_mode cmd failed ret = %d\n", ret); - goto power_off; - } + mipi_dsi_dcs_exit_sleep_mode_multi(&dsi_ctx); /* Per DSI spec wait 120ms after sending exit sleep DCS command */ - msleep(120); + mipi_dsi_msleep(&dsi_ctx, 120); - ret = mipi_dsi_dcs_write(ctx->dsi, MIPI_DCS_SET_DISPLAY_ON, NULL, 0); - if (ret < 0) { - dev_err(ctx->panel.dev, "set_display_on cmd failed ret = %d\n", ret); - goto power_off; - } + mipi_dsi_dcs_set_display_on_multi(&dsi_ctx); /* Per DSI spec wait 120ms after sending set_display_on DCS command */ - msleep(120); + mipi_dsi_msleep(&dsi_ctx, 120); - return 0; - -power_off: - return ret; + return dsi_ctx.accum_err; } static const struct drm_display_mode visionox_rm69299_1080x2248_60hz = { @@ -154,14 +246,26 @@ static const struct drm_display_mode visionox_rm69299_1080x2248_60hz = { .flags = 0, }; +static const struct drm_display_mode visionox_rm69299_1080x2160_60hz = { + .clock = 158695, + .hdisplay = 1080, + .hsync_start = 1080 + 26, + .hsync_end = 1080 + 26 + 2, + .htotal = 1080 + 26 + 2 + 36, + .vdisplay = 2160, + .vsync_start = 2160 + 8, + .vsync_end = 2160 + 8 + 4, + .vtotal = 2160 + 8 + 4 + 4, + .flags = 0, +}; + static int visionox_rm69299_get_modes(struct drm_panel *panel, struct drm_connector *connector) { struct visionox_rm69299 *ctx = panel_to_ctx(panel); struct drm_display_mode *mode; - mode = drm_mode_duplicate(connector->dev, - &visionox_rm69299_1080x2248_60hz); + mode = drm_mode_duplicate(connector->dev, ctx->desc->mode); if (!mode) { dev_err(ctx->panel.dev, "failed to create a new display mode\n"); return 0; @@ -187,20 +291,22 @@ static int visionox_rm69299_probe(struct mipi_dsi_device *dsi) struct visionox_rm69299 *ctx; int ret; - ctx = devm_kzalloc(dev, sizeof(*ctx), GFP_KERNEL); - if (!ctx) - return -ENOMEM; + ctx = devm_drm_panel_alloc(dev, struct visionox_rm69299, panel, + &visionox_rm69299_drm_funcs, + DRM_MODE_CONNECTOR_DSI); + if (IS_ERR(ctx)) + return PTR_ERR(ctx); + + ctx->desc = device_get_match_data(dev); + if (!ctx->desc) + return -EINVAL; mipi_dsi_set_drvdata(dsi, ctx); ctx->dsi = dsi; - ctx->supplies[0].supply = "vdda"; - ctx->supplies[0].init_load_uA = 32000; - ctx->supplies[1].supply = "vdd3p3"; - ctx->supplies[1].init_load_uA = 13200; - - ret = devm_regulator_bulk_get(dev, ARRAY_SIZE(ctx->supplies), ctx->supplies); + ret = devm_regulator_bulk_get_const(dev, ARRAY_SIZE(visionox_rm69299_supplies), + visionox_rm69299_supplies, &ctx->supplies); if (ret < 0) return ret; @@ -210,8 +316,6 @@ static int visionox_rm69299_probe(struct mipi_dsi_device *dsi) return PTR_ERR(ctx->reset_gpio); } - drm_panel_init(&ctx->panel, dev, &visionox_rm69299_drm_funcs, - DRM_MODE_CONNECTOR_DSI); drm_panel_add(&ctx->panel); dsi->lanes = 4; @@ -239,8 +343,23 @@ static void visionox_rm69299_remove(struct mipi_dsi_device *dsi) drm_panel_remove(&ctx->panel); } +const struct visionox_rm69299_panel_desc visionox_rm69299_1080p_display_desc = { + .mode = &visionox_rm69299_1080x2248_60hz, + .init_seq = (const u8 *)visionox_rm69299_1080x2248_60hz_init_seq, + .init_seq_len = ARRAY_SIZE(visionox_rm69299_1080x2248_60hz_init_seq), +}; + +const struct visionox_rm69299_panel_desc visionox_rm69299_shift_desc = { + .mode = &visionox_rm69299_1080x2160_60hz, + .init_seq = (const u8 *)visionox_rm69299_1080x2160_60hz_init_seq, + .init_seq_len = ARRAY_SIZE(visionox_rm69299_1080x2160_60hz_init_seq), +}; + static const struct of_device_id visionox_rm69299_of_match[] = { - { .compatible = "visionox,rm69299-1080p-display", }, + { .compatible = "visionox,rm69299-1080p-display", + .data = &visionox_rm69299_1080p_display_desc }, + { .compatible = "visionox,rm69299-shift", + .data = &visionox_rm69299_shift_desc }, { /* sentinel */ } }; MODULE_DEVICE_TABLE(of, visionox_rm69299_of_match); diff --git a/drivers/gpu/drm/panel/panel-visionox-rm692e5.c b/drivers/gpu/drm/panel/panel-visionox-rm692e5.c index 4db7fa8d74c4..e53645d59413 100644 --- a/drivers/gpu/drm/panel/panel-visionox-rm692e5.c +++ b/drivers/gpu/drm/panel/panel-visionox-rm692e5.c @@ -360,9 +360,11 @@ static int visionox_rm692e5_probe(struct mipi_dsi_device *dsi) struct visionox_rm692e5 *ctx; int ret; - ctx = devm_kzalloc(dev, sizeof(*ctx), GFP_KERNEL); - if (!ctx) - return -ENOMEM; + ctx = devm_drm_panel_alloc(dev, struct visionox_rm692e5, panel, + &visionox_rm692e5_panel_funcs, + DRM_MODE_CONNECTOR_DSI); + if (IS_ERR(ctx)) + return PTR_ERR(ctx); ret = devm_regulator_bulk_get_const(&dsi->dev, ARRAY_SIZE(visionox_rm692e5_supplies), @@ -383,8 +385,6 @@ static int visionox_rm692e5_probe(struct mipi_dsi_device *dsi) dsi->format = MIPI_DSI_FMT_RGB888; dsi->mode_flags = MIPI_DSI_CLOCK_NON_CONTINUOUS; - drm_panel_init(&ctx->panel, dev, &visionox_rm692e5_panel_funcs, - DRM_MODE_CONNECTOR_DSI); ctx->panel.prepare_prev_first = true; ctx->panel.backlight = visionox_rm692e5_create_backlight(dsi); diff --git a/drivers/gpu/drm/panel/panel-visionox-vtdr6130.c b/drivers/gpu/drm/panel/panel-visionox-vtdr6130.c index 17b8defe79c1..97a79411e1ec 100644 --- a/drivers/gpu/drm/panel/panel-visionox-vtdr6130.c +++ b/drivers/gpu/drm/panel/panel-visionox-vtdr6130.c @@ -248,9 +248,11 @@ static int visionox_vtdr6130_probe(struct mipi_dsi_device *dsi) struct visionox_vtdr6130 *ctx; int ret; - ctx = devm_kzalloc(dev, sizeof(*ctx), GFP_KERNEL); - if (!ctx) - return -ENOMEM; + ctx = devm_drm_panel_alloc(dev, struct visionox_vtdr6130, panel, + &visionox_vtdr6130_panel_funcs, + DRM_MODE_CONNECTOR_DSI); + if (IS_ERR(ctx)) + return PTR_ERR(ctx); ret = devm_regulator_bulk_get_const(&dsi->dev, ARRAY_SIZE(visionox_vtdr6130_supplies), @@ -273,9 +275,6 @@ static int visionox_vtdr6130_probe(struct mipi_dsi_device *dsi) MIPI_DSI_CLOCK_NON_CONTINUOUS; ctx->panel.prepare_prev_first = true; - drm_panel_init(&ctx->panel, dev, &visionox_vtdr6130_panel_funcs, - DRM_MODE_CONNECTOR_DSI); - ctx->panel.backlight = visionox_vtdr6130_create_backlight(dsi); if (IS_ERR(ctx->panel.backlight)) return dev_err_probe(dev, PTR_ERR(ctx->panel.backlight), diff --git a/drivers/gpu/drm/panel/panel-widechips-ws2401.c b/drivers/gpu/drm/panel/panel-widechips-ws2401.c index 2591ff8f0d4e..dd74610bd2eb 100644 --- a/drivers/gpu/drm/panel/panel-widechips-ws2401.c +++ b/drivers/gpu/drm/panel/panel-widechips-ws2401.c @@ -347,9 +347,11 @@ static int ws2401_probe(struct spi_device *spi) struct ws2401 *ws; int ret; - ws = devm_kzalloc(dev, sizeof(*ws), GFP_KERNEL); - if (!ws) - return -ENOMEM; + ws = devm_drm_panel_alloc(dev, struct ws2401, panel, &ws2401_drm_funcs, + DRM_MODE_CONNECTOR_DPI); + if (IS_ERR(ws)) + return PTR_ERR(ws); + ws->dev = dev; /* @@ -379,9 +381,6 @@ static int ws2401_probe(struct spi_device *spi) ws2401_read_mtp_id(ws); ws2401_power_off(ws); - drm_panel_init(&ws->panel, dev, &ws2401_drm_funcs, - DRM_MODE_CONNECTOR_DPI); - ret = drm_panel_of_backlight(&ws->panel); if (ret) return dev_err_probe(dev, ret, diff --git a/drivers/gpu/drm/panel/panel-xinpeng-xpp055c272.c b/drivers/gpu/drm/panel/panel-xinpeng-xpp055c272.c index 2b91414c2829..fc6516373b5d 100644 --- a/drivers/gpu/drm/panel/panel-xinpeng-xpp055c272.c +++ b/drivers/gpu/drm/panel/panel-xinpeng-xpp055c272.c @@ -241,9 +241,10 @@ static int xpp055c272_probe(struct mipi_dsi_device *dsi) struct xpp055c272 *ctx; int ret; - ctx = devm_kzalloc(dev, sizeof(*ctx), GFP_KERNEL); - if (!ctx) - return -ENOMEM; + ctx = devm_drm_panel_alloc(dev, struct xpp055c272, panel, + &xpp055c272_funcs, DRM_MODE_CONNECTOR_DSI); + if (IS_ERR(ctx)) + return PTR_ERR(ctx); ctx->reset_gpio = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_LOW); if (IS_ERR(ctx->reset_gpio)) @@ -269,9 +270,6 @@ static int xpp055c272_probe(struct mipi_dsi_device *dsi) dsi->mode_flags = MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_VIDEO_BURST | MIPI_DSI_MODE_LPM | MIPI_DSI_MODE_NO_EOT_PACKET; - drm_panel_init(&ctx->panel, &dsi->dev, &xpp055c272_funcs, - DRM_MODE_CONNECTOR_DSI); - ret = drm_panel_of_backlight(&ctx->panel); if (ret) return ret; diff --git a/drivers/gpu/drm/panfrost/panfrost_devfreq.c b/drivers/gpu/drm/panfrost/panfrost_devfreq.c index 3385fd3ef41a..5d0dce10336b 100644 --- a/drivers/gpu/drm/panfrost/panfrost_devfreq.c +++ b/drivers/gpu/drm/panfrost/panfrost_devfreq.c @@ -29,7 +29,7 @@ static void panfrost_devfreq_update_utilization(struct panfrost_devfreq *pfdevfr static int panfrost_devfreq_target(struct device *dev, unsigned long *freq, u32 flags) { - struct panfrost_device *ptdev = dev_get_drvdata(dev); + struct panfrost_device *pfdev = dev_get_drvdata(dev); struct dev_pm_opp *opp; int err; @@ -40,7 +40,7 @@ static int panfrost_devfreq_target(struct device *dev, unsigned long *freq, err = dev_pm_opp_set_rate(dev, *freq); if (!err) - ptdev->pfdevfreq.current_frequency = *freq; + pfdev->pfdevfreq.current_frequency = *freq; return err; } diff --git a/drivers/gpu/drm/panfrost/panfrost_device.c b/drivers/gpu/drm/panfrost/panfrost_device.c index 5d35076b2e6d..04bec27449cb 100644 --- a/drivers/gpu/drm/panfrost/panfrost_device.c +++ b/drivers/gpu/drm/panfrost/panfrost_device.c @@ -209,6 +209,11 @@ int panfrost_device_init(struct panfrost_device *pfdev) spin_lock_init(&pfdev->cycle_counter.lock); +#ifdef CONFIG_DEBUG_FS + mutex_init(&pfdev->debugfs.gems_lock); + INIT_LIST_HEAD(&pfdev->debugfs.gems_list); +#endif + err = panfrost_pm_domain_init(pfdev); if (err) return err; diff --git a/drivers/gpu/drm/panfrost/panfrost_device.h b/drivers/gpu/drm/panfrost/panfrost_device.h index dcff70f905cd..077525a3ad68 100644 --- a/drivers/gpu/drm/panfrost/panfrost_device.h +++ b/drivers/gpu/drm/panfrost/panfrost_device.h @@ -111,6 +111,17 @@ struct panfrost_compatible { u8 gpu_quirks; }; +/** + * struct panfrost_device_debugfs - Device-wide DebugFS tracking structures + */ +struct panfrost_device_debugfs { + /** @gems_list: Device-wide list of GEM objects owned by at least one file. */ + struct list_head gems_list; + + /** @gems_lock: Serializes access to the device-wide list of GEM objects. */ + struct mutex gems_lock; +}; + struct panfrost_device { struct device *dev; struct drm_device *ddev; @@ -164,6 +175,10 @@ struct panfrost_device { atomic_t use_count; spinlock_t lock; } cycle_counter; + +#ifdef CONFIG_DEBUG_FS + struct panfrost_device_debugfs debugfs; +#endif }; struct panfrost_mmu { diff --git a/drivers/gpu/drm/panfrost/panfrost_drv.c b/drivers/gpu/drm/panfrost/panfrost_drv.c index f1ec3b02f15a..842239b43cdf 100644 --- a/drivers/gpu/drm/panfrost/panfrost_drv.c +++ b/drivers/gpu/drm/panfrost/panfrost_drv.c @@ -13,6 +13,7 @@ #include <linux/platform_device.h> #include <linux/pm_runtime.h> #include <drm/panfrost_drm.h> +#include <drm/drm_debugfs.h> #include <drm/drm_drv.h> #include <drm/drm_ioctl.h> #include <drm/drm_syncobj.h> @@ -312,7 +313,7 @@ static int panfrost_ioctl_submit(struct drm_device *dev, void *data, ret = drm_sched_job_init(&job->base, &file_priv->sched_entity[slot], - 1, NULL); + 1, NULL, file->client_id); if (ret) goto out_put_job; @@ -495,6 +496,46 @@ out_put_object: return ret; } +static int panfrost_ioctl_set_label_bo(struct drm_device *ddev, void *data, + struct drm_file *file) +{ + struct drm_panfrost_set_label_bo *args = data; + struct drm_gem_object *obj; + const char *label = NULL; + int ret = 0; + + if (args->pad) + return -EINVAL; + + obj = drm_gem_object_lookup(file, args->handle); + if (!obj) + return -ENOENT; + + if (args->label) { + label = strndup_user(u64_to_user_ptr(args->label), + PANFROST_BO_LABEL_MAXLEN); + if (IS_ERR(label)) { + ret = PTR_ERR(label); + if (ret == -EINVAL) + ret = -E2BIG; + goto err_put_obj; + } + } + + /* + * We treat passing a label of length 0 and passing a NULL label + * differently, because even though they might seem conceptually + * similar, future uses of the BO label might expect a different + * behaviour in each case. + */ + panfrost_gem_set_label(obj, label); + +err_put_obj: + drm_gem_object_put(obj); + + return ret; +} + int panfrost_unstable_ioctl_check(void) { if (!unstable_ioctls) @@ -561,6 +602,7 @@ static const struct drm_ioctl_desc panfrost_drm_driver_ioctls[] = { PANFROST_IOCTL(PERFCNT_ENABLE, perfcnt_enable, DRM_RENDER_ALLOW), PANFROST_IOCTL(PERFCNT_DUMP, perfcnt_dump, DRM_RENDER_ALLOW), PANFROST_IOCTL(MADVISE, madvise, DRM_RENDER_ALLOW), + PANFROST_IOCTL(SET_LABEL_BO, set_label_bo, DRM_RENDER_ALLOW), }; static void panfrost_gpu_show_fdinfo(struct panfrost_device *pfdev, @@ -618,6 +660,37 @@ static const struct file_operations panfrost_drm_driver_fops = { .show_fdinfo = drm_show_fdinfo, }; +#ifdef CONFIG_DEBUG_FS +static int panthor_gems_show(struct seq_file *m, void *data) +{ + struct drm_info_node *node = m->private; + struct drm_device *dev = node->minor->dev; + struct panfrost_device *pfdev = dev->dev_private; + + panfrost_gem_debugfs_print_bos(pfdev, m); + + return 0; +} + +static struct drm_info_list panthor_debugfs_list[] = { + {"gems", panthor_gems_show, 0, NULL}, +}; + +static int panthor_gems_debugfs_init(struct drm_minor *minor) +{ + drm_debugfs_create_files(panthor_debugfs_list, + ARRAY_SIZE(panthor_debugfs_list), + minor->debugfs_root, minor); + + return 0; +} + +static void panfrost_debugfs_init(struct drm_minor *minor) +{ + panthor_gems_debugfs_init(minor); +} +#endif + /* * Panfrost driver version: * - 1.0 - initial interface @@ -625,6 +698,7 @@ static const struct file_operations panfrost_drm_driver_fops = { * - 1.2 - adds AFBC_FEATURES query * - 1.3 - adds JD_REQ_CYCLE_COUNT job requirement for SUBMIT * - adds SYSTEM_TIMESTAMP and SYSTEM_TIMESTAMP_FREQUENCY queries + * - 1.4 - adds SET_LABEL_BO */ static const struct drm_driver panfrost_drm_driver = { .driver_features = DRIVER_RENDER | DRIVER_GEM | DRIVER_SYNCOBJ, @@ -637,10 +711,13 @@ static const struct drm_driver panfrost_drm_driver = { .name = "panfrost", .desc = "panfrost DRM", .major = 1, - .minor = 3, + .minor = 4, .gem_create_object = panfrost_gem_create_object, .gem_prime_import_sg_table = panfrost_gem_prime_import_sg_table, +#ifdef CONFIG_DEBUG_FS + .debugfs_init = panfrost_debugfs_init, +#endif }; static int panfrost_probe(struct platform_device *pdev) diff --git a/drivers/gpu/drm/panfrost/panfrost_gem.c b/drivers/gpu/drm/panfrost/panfrost_gem.c index 963f04ba2de6..bb73f2a68a12 100644 --- a/drivers/gpu/drm/panfrost/panfrost_gem.c +++ b/drivers/gpu/drm/panfrost/panfrost_gem.c @@ -1,6 +1,7 @@ // SPDX-License-Identifier: GPL-2.0 /* Copyright 2019 Linaro, Ltd, Rob Herring <robh@kernel.org> */ +#include <linux/cleanup.h> #include <linux/err.h> #include <linux/slab.h> #include <linux/dma-buf.h> @@ -11,6 +12,36 @@ #include "panfrost_gem.h" #include "panfrost_mmu.h" +#ifdef CONFIG_DEBUG_FS +static void panfrost_gem_debugfs_bo_add(struct panfrost_device *pfdev, + struct panfrost_gem_object *bo) +{ + bo->debugfs.creator.tgid = current->group_leader->pid; + get_task_comm(bo->debugfs.creator.process_name, current->group_leader); + + mutex_lock(&pfdev->debugfs.gems_lock); + list_add_tail(&bo->debugfs.node, &pfdev->debugfs.gems_list); + mutex_unlock(&pfdev->debugfs.gems_lock); +} + +static void panfrost_gem_debugfs_bo_rm(struct panfrost_gem_object *bo) +{ + struct panfrost_device *pfdev = bo->base.base.dev->dev_private; + + if (list_empty(&bo->debugfs.node)) + return; + + mutex_lock(&pfdev->debugfs.gems_lock); + list_del_init(&bo->debugfs.node); + mutex_unlock(&pfdev->debugfs.gems_lock); +} +#else +static void panfrost_gem_debugfs_bo_add(struct panfrost_device *pfdev, + struct panfrost_gem_object *bo) +{} +static void panfrost_gem_debugfs_bo_rm(struct panfrost_gem_object *bo) {} +#endif + /* Called DRM core on the last userspace/kernel unreference of the * BO. */ @@ -35,6 +66,10 @@ static void panfrost_gem_free_object(struct drm_gem_object *obj) */ WARN_ON_ONCE(!list_empty(&bo->mappings.list)); + kfree_const(bo->label.str); + panfrost_gem_debugfs_bo_rm(bo); + mutex_destroy(&bo->label.lock); + if (bo->sgts) { int i; int n_sgt = bo->base.base.size / SZ_2M; @@ -260,6 +295,9 @@ struct drm_gem_object *panfrost_gem_create_object(struct drm_device *dev, size_t mutex_init(&obj->mappings.lock); obj->base.base.funcs = &panfrost_gem_funcs; obj->base.map_wc = !pfdev->coherent; + mutex_init(&obj->label.lock); + + panfrost_gem_debugfs_bo_add(pfdev, obj); return &obj->base.base; } @@ -300,5 +338,153 @@ panfrost_gem_prime_import_sg_table(struct drm_device *dev, bo = to_panfrost_bo(obj); bo->noexec = true; + /* + * We assign this generic label because this function cannot + * be reached through any of the Panfrost UM driver-specific + * code paths, unless one is given by explicitly calling the + * SET_LABEL_BO ioctl. It is therefore preferable to have a + * blanket BO tag that tells us the object was imported from + * another driver than nothing at all. + */ + panfrost_gem_internal_set_label(obj, "GEM PRIME buffer"); + return obj; } + +void +panfrost_gem_set_label(struct drm_gem_object *obj, const char *label) +{ + struct panfrost_gem_object *bo = to_panfrost_bo(obj); + const char *old_label; + + scoped_guard(mutex, &bo->label.lock) { + old_label = bo->label.str; + bo->label.str = label; + } + + kfree_const(old_label); +} + +void +panfrost_gem_internal_set_label(struct drm_gem_object *obj, const char *label) +{ + struct panfrost_gem_object *bo = to_panfrost_bo(obj); + const char *str; + + /* We should never attempt labelling a UM-exposed GEM object */ + if (drm_WARN_ON(bo->base.base.dev, bo->base.base.handle_count > 0)) + return; + + if (!label) + return; + + str = kstrdup_const(label, GFP_KERNEL); + if (!str) { + /* Failing to allocate memory for a label isn't a fatal condition */ + drm_warn(bo->base.base.dev, "Not enough memory to allocate BO label"); + return; + } + + panfrost_gem_set_label(obj, str); +} + +#ifdef CONFIG_DEBUG_FS +struct gem_size_totals { + size_t size; + size_t resident; + size_t reclaimable; +}; + +struct flag_def { + u32 flag; + const char *name; +}; + +static void panfrost_gem_debugfs_print_flag_names(struct seq_file *m) +{ + int len; + int i; + + static const struct flag_def gem_state_flags_names[] = { + {PANFROST_DEBUGFS_GEM_STATE_FLAG_IMPORTED, "imported"}, + {PANFROST_DEBUGFS_GEM_STATE_FLAG_EXPORTED, "exported"}, + {PANFROST_DEBUGFS_GEM_STATE_FLAG_PURGED, "purged"}, + {PANFROST_DEBUGFS_GEM_STATE_FLAG_PURGEABLE, "purgeable"}, + }; + + seq_puts(m, "GEM state flags: "); + for (i = 0, len = ARRAY_SIZE(gem_state_flags_names); i < len; i++) { + seq_printf(m, "%s (0x%x)%s", gem_state_flags_names[i].name, + gem_state_flags_names[i].flag, (i < len - 1) ? ", " : "\n\n"); + } +} + +static void panfrost_gem_debugfs_bo_print(struct panfrost_gem_object *bo, + struct seq_file *m, + struct gem_size_totals *totals) +{ + unsigned int refcount = kref_read(&bo->base.base.refcount); + char creator_info[32] = {}; + size_t resident_size; + u32 gem_state_flags = 0; + + /* Skip BOs being destroyed. */ + if (!refcount) + return; + + resident_size = bo->base.pages ? bo->base.base.size : 0; + + snprintf(creator_info, sizeof(creator_info), + "%s/%d", bo->debugfs.creator.process_name, bo->debugfs.creator.tgid); + seq_printf(m, "%-32s%-16d%-16d%-16zd%-16zd0x%-16lx", + creator_info, + bo->base.base.name, + refcount, + bo->base.base.size, + resident_size, + drm_vma_node_start(&bo->base.base.vma_node)); + + if (bo->base.base.import_attach) + gem_state_flags |= PANFROST_DEBUGFS_GEM_STATE_FLAG_IMPORTED; + if (bo->base.base.dma_buf) + gem_state_flags |= PANFROST_DEBUGFS_GEM_STATE_FLAG_EXPORTED; + + if (bo->base.madv < 0) + gem_state_flags |= PANFROST_DEBUGFS_GEM_STATE_FLAG_PURGED; + else if (bo->base.madv > 0) + gem_state_flags |= PANFROST_DEBUGFS_GEM_STATE_FLAG_PURGEABLE; + + seq_printf(m, "0x%-10x", gem_state_flags); + + scoped_guard(mutex, &bo->label.lock) { + seq_printf(m, "%s\n", bo->label.str ? : ""); + } + + totals->size += bo->base.base.size; + totals->resident += resident_size; + if (bo->base.madv > 0) + totals->reclaimable += resident_size; +} + +void panfrost_gem_debugfs_print_bos(struct panfrost_device *pfdev, + struct seq_file *m) +{ + struct gem_size_totals totals = {0}; + struct panfrost_gem_object *bo; + + panfrost_gem_debugfs_print_flag_names(m); + + seq_puts(m, "created-by global-name refcount size resident-size file-offset state label\n"); + seq_puts(m, "-----------------------------------------------------------------------------------------------------------------------------------\n"); + + scoped_guard(mutex, &pfdev->debugfs.gems_lock) { + list_for_each_entry(bo, &pfdev->debugfs.gems_list, debugfs.node) { + panfrost_gem_debugfs_bo_print(bo, m, &totals); + } + } + + seq_puts(m, "===================================================================================================================================\n"); + seq_printf(m, "Total size: %zd, Total resident: %zd, Total reclaimable: %zd\n", + totals.size, totals.resident, totals.reclaimable); +} +#endif diff --git a/drivers/gpu/drm/panfrost/panfrost_gem.h b/drivers/gpu/drm/panfrost/panfrost_gem.h index 7516b7ecf7fe..8de3e76f2717 100644 --- a/drivers/gpu/drm/panfrost/panfrost_gem.h +++ b/drivers/gpu/drm/panfrost/panfrost_gem.h @@ -8,6 +8,46 @@ #include <drm/drm_mm.h> struct panfrost_mmu; +struct panfrost_device; + +#define PANFROST_BO_LABEL_MAXLEN 4096 + +enum panfrost_debugfs_gem_state_flags { + /** @PANFROST_DEBUGFS_GEM_STATE_FLAG_IMPORTED: GEM BO is PRIME imported. */ + PANFROST_DEBUGFS_GEM_STATE_FLAG_IMPORTED = BIT(0), + + /** @PANFROST_DEBUGFS_GEM_STATE_FLAG_EXPORTED: GEM BO is PRIME exported. */ + PANFROST_DEBUGFS_GEM_STATE_FLAG_EXPORTED = BIT(1), + + /** @PANFROST_DEBUGFS_GEM_STATE_FLAG_PURGED: GEM BO was reclaimed by the shrinker. */ + PANFROST_DEBUGFS_GEM_STATE_FLAG_PURGED = BIT(2), + + /** + * @PANFROST_DEBUGFS_GEM_STATE_FLAG_PURGEABLE: GEM BO pages were marked as no longer + * needed by UM and can be reclaimed by the shrinker. + */ + PANFROST_DEBUGFS_GEM_STATE_FLAG_PURGEABLE = BIT(3), +}; + +/** + * struct panfrost_gem_debugfs - GEM object's DebugFS list information + */ +struct panfrost_gem_debugfs { + /** + * @node: Node used to insert the object in the device-wide list of + * GEM objects, to display information about it through a DebugFS file. + */ + struct list_head node; + + /** @creator: Information about the UM process which created the GEM. */ + struct { + /** @creator.process_name: Group leader name in owning thread's process */ + char process_name[TASK_COMM_LEN]; + + /** @creator.tgid: PID of the thread's group leader within its process */ + pid_t tgid; + } creator; +}; struct panfrost_gem_object { struct drm_gem_shmem_object base; @@ -41,8 +81,26 @@ struct panfrost_gem_object { */ size_t heap_rss_size; + /** + * @label: BO tagging fields. The label can be assigned within the + * driver itself or through a specific IOCTL. + */ + struct { + /** + * @label.str: Pointer to NULL-terminated string, + */ + const char *str; + + /** @lock.str: Protects access to the @label.str field. */ + struct mutex lock; + } label; + bool noexec :1; bool is_heap :1; + +#ifdef CONFIG_DEBUG_FS + struct panfrost_gem_debugfs debugfs; +#endif }; struct panfrost_gem_mapping { @@ -89,4 +147,12 @@ void panfrost_gem_teardown_mappings_locked(struct panfrost_gem_object *bo); int panfrost_gem_shrinker_init(struct drm_device *dev); void panfrost_gem_shrinker_cleanup(struct drm_device *dev); +void panfrost_gem_set_label(struct drm_gem_object *obj, const char *label); +void panfrost_gem_internal_set_label(struct drm_gem_object *obj, const char *label); + +#ifdef CONFIG_DEBUG_FS +void panfrost_gem_debugfs_print_bos(struct panfrost_device *pfdev, + struct seq_file *m); +#endif + #endif /* __PANFROST_GEM_H__ */ diff --git a/drivers/gpu/drm/panfrost/panfrost_perfcnt.c b/drivers/gpu/drm/panfrost/panfrost_perfcnt.c index 52befead08c6..563f16bae543 100644 --- a/drivers/gpu/drm/panfrost/panfrost_perfcnt.c +++ b/drivers/gpu/drm/panfrost/panfrost_perfcnt.c @@ -111,6 +111,8 @@ static int panfrost_perfcnt_enable_locked(struct panfrost_device *pfdev, goto err_put_mapping; perfcnt->buf = map.vaddr; + panfrost_gem_internal_set_label(&bo->base, "Perfcnt sample buffer"); + /* * Invalidate the cache and clear the counters to start from a fresh * state. diff --git a/drivers/gpu/drm/panthor/panthor_device.h b/drivers/gpu/drm/panthor/panthor_device.h index 465d3ab1b79e..4fc7cf2aeed5 100644 --- a/drivers/gpu/drm/panthor/panthor_device.h +++ b/drivers/gpu/drm/panthor/panthor_device.h @@ -230,6 +230,24 @@ struct panthor_file { /** @ptdev: Device attached to this file. */ struct panthor_device *ptdev; + /** @user_mmio: User MMIO related fields. */ + struct { + /** + * @offset: Offset used for user MMIO mappings. + * + * This offset should not be used to check the type of mapping + * except in panthor_mmap(). After that point, MMIO mapping + * offsets have been adjusted to match + * DRM_PANTHOR_USER_MMIO_OFFSET and that macro should be used + * instead. + * Make sure this rule is followed at all times, because + * userspace is in control of the offset, and can change the + * value behind our back. Otherwise it can lead to erroneous + * branching happening in kernel space. + */ + u64 offset; + } user_mmio; + /** @vms: VM pool attached to this file. */ struct panthor_vm_pool *vms; @@ -437,4 +455,75 @@ static int panthor_request_ ## __name ## _irq(struct panthor_device *ptdev, \ extern struct workqueue_struct *panthor_cleanup_wq; +static inline void gpu_write(struct panthor_device *ptdev, u32 reg, u32 data) +{ + writel(data, ptdev->iomem + reg); +} + +static inline u32 gpu_read(struct panthor_device *ptdev, u32 reg) +{ + return readl(ptdev->iomem + reg); +} + +static inline u32 gpu_read_relaxed(struct panthor_device *ptdev, u32 reg) +{ + return readl_relaxed(ptdev->iomem + reg); +} + +static inline void gpu_write64(struct panthor_device *ptdev, u32 reg, u64 data) +{ + gpu_write(ptdev, reg, lower_32_bits(data)); + gpu_write(ptdev, reg + 4, upper_32_bits(data)); +} + +static inline u64 gpu_read64(struct panthor_device *ptdev, u32 reg) +{ + return (gpu_read(ptdev, reg) | ((u64)gpu_read(ptdev, reg + 4) << 32)); +} + +static inline u64 gpu_read64_relaxed(struct panthor_device *ptdev, u32 reg) +{ + return (gpu_read_relaxed(ptdev, reg) | + ((u64)gpu_read_relaxed(ptdev, reg + 4) << 32)); +} + +static inline u64 gpu_read64_counter(struct panthor_device *ptdev, u32 reg) +{ + u32 lo, hi1, hi2; + do { + hi1 = gpu_read(ptdev, reg + 4); + lo = gpu_read(ptdev, reg); + hi2 = gpu_read(ptdev, reg + 4); + } while (hi1 != hi2); + return lo | ((u64)hi2 << 32); +} + +#define gpu_read_poll_timeout(dev, reg, val, cond, delay_us, timeout_us) \ + read_poll_timeout(gpu_read, val, cond, delay_us, timeout_us, false, \ + dev, reg) + +#define gpu_read_poll_timeout_atomic(dev, reg, val, cond, delay_us, \ + timeout_us) \ + read_poll_timeout_atomic(gpu_read, val, cond, delay_us, timeout_us, \ + false, dev, reg) + +#define gpu_read64_poll_timeout(dev, reg, val, cond, delay_us, timeout_us) \ + read_poll_timeout(gpu_read64, val, cond, delay_us, timeout_us, false, \ + dev, reg) + +#define gpu_read64_poll_timeout_atomic(dev, reg, val, cond, delay_us, \ + timeout_us) \ + read_poll_timeout_atomic(gpu_read64, val, cond, delay_us, timeout_us, \ + false, dev, reg) + +#define gpu_read_relaxed_poll_timeout_atomic(dev, reg, val, cond, delay_us, \ + timeout_us) \ + read_poll_timeout_atomic(gpu_read_relaxed, val, cond, delay_us, \ + timeout_us, false, dev, reg) + +#define gpu_read64_relaxed_poll_timeout(dev, reg, val, cond, delay_us, \ + timeout_us) \ + read_poll_timeout(gpu_read64_relaxed, val, cond, delay_us, timeout_us, \ + false, dev, reg) + #endif diff --git a/drivers/gpu/drm/panthor/panthor_drv.c b/drivers/gpu/drm/panthor/panthor_drv.c index 6200cad22563..1116f2d2826e 100644 --- a/drivers/gpu/drm/panthor/panthor_drv.c +++ b/drivers/gpu/drm/panthor/panthor_drv.c @@ -772,8 +772,8 @@ static int panthor_query_timestamp_info(struct panthor_device *ptdev, #else arg->timestamp_frequency = 0; #endif - arg->current_timestamp = panthor_gpu_read_timestamp(ptdev); - arg->timestamp_offset = panthor_gpu_read_timestamp_offset(ptdev); + arg->current_timestamp = gpu_read64_counter(ptdev, GPU_TIMESTAMP); + arg->timestamp_offset = gpu_read64(ptdev, GPU_TIMESTAMP_OFFSET); pm_runtime_put(ptdev->base.dev); return 0; @@ -996,7 +996,8 @@ static int panthor_ioctl_group_submit(struct drm_device *ddev, void *data, const struct drm_panthor_queue_submit *qsubmit = &jobs_args[i]; struct drm_sched_job *job; - job = panthor_job_create(pfile, args->group_handle, qsubmit); + job = panthor_job_create(pfile, args->group_handle, qsubmit, + file->client_id); if (IS_ERR(job)) { ret = PTR_ERR(job); goto out_cleanup_submit_ctx; @@ -1378,6 +1379,20 @@ err_put_obj: return ret; } +static int panthor_ioctl_set_user_mmio_offset(struct drm_device *ddev, + void *data, struct drm_file *file) +{ + struct drm_panthor_set_user_mmio_offset *args = data; + struct panthor_file *pfile = file->driver_priv; + + if (args->offset != DRM_PANTHOR_USER_MMIO_OFFSET_32BIT && + args->offset != DRM_PANTHOR_USER_MMIO_OFFSET_64BIT) + return -EINVAL; + + WRITE_ONCE(pfile->user_mmio.offset, args->offset); + return 0; +} + static int panthor_open(struct drm_device *ddev, struct drm_file *file) { @@ -1395,6 +1410,18 @@ panthor_open(struct drm_device *ddev, struct drm_file *file) } pfile->ptdev = ptdev; + pfile->user_mmio.offset = DRM_PANTHOR_USER_MMIO_OFFSET; + +#ifdef CONFIG_ARM64 + /* + * With 32-bit systems being limited by the 32-bit representation of + * mmap2's pgoffset field, we need to make the MMIO offset arch + * specific. + */ + if (test_tsk_thread_flag(current, TIF_32BIT)) + pfile->user_mmio.offset = DRM_PANTHOR_USER_MMIO_OFFSET_32BIT; +#endif + ret = panthor_vm_pool_create(pfile); if (ret) @@ -1448,6 +1475,7 @@ static const struct drm_ioctl_desc panthor_drm_driver_ioctls[] = { PANTHOR_IOCTL(TILER_HEAP_DESTROY, tiler_heap_destroy, DRM_RENDER_ALLOW), PANTHOR_IOCTL(GROUP_SUBMIT, group_submit, DRM_RENDER_ALLOW), PANTHOR_IOCTL(BO_SET_LABEL, bo_set_label, DRM_RENDER_ALLOW), + PANTHOR_IOCTL(SET_USER_MMIO_OFFSET, set_user_mmio_offset, DRM_RENDER_ALLOW), }; static int panthor_mmap(struct file *filp, struct vm_area_struct *vma) @@ -1456,30 +1484,26 @@ static int panthor_mmap(struct file *filp, struct vm_area_struct *vma) struct panthor_file *pfile = file->driver_priv; struct panthor_device *ptdev = pfile->ptdev; u64 offset = (u64)vma->vm_pgoff << PAGE_SHIFT; + u64 user_mmio_offset; int ret, cookie; if (!drm_dev_enter(file->minor->dev, &cookie)) return -ENODEV; -#ifdef CONFIG_ARM64 - /* - * With 32-bit systems being limited by the 32-bit representation of - * mmap2's pgoffset field, we need to make the MMIO offset arch - * specific. This converts a user MMIO offset into something the kernel - * driver understands. + /* Adjust the user MMIO offset to match the offset used kernel side. + * We use a local variable with a READ_ONCE() here to make sure + * the user_mmio_offset we use for the is_user_mmio_mapping() check + * hasn't changed when we do the offset adjustment. */ - if (test_tsk_thread_flag(current, TIF_32BIT) && - offset >= DRM_PANTHOR_USER_MMIO_OFFSET_32BIT) { - offset += DRM_PANTHOR_USER_MMIO_OFFSET_64BIT - - DRM_PANTHOR_USER_MMIO_OFFSET_32BIT; + user_mmio_offset = READ_ONCE(pfile->user_mmio.offset); + if (offset >= user_mmio_offset) { + offset -= user_mmio_offset; + offset += DRM_PANTHOR_USER_MMIO_OFFSET; vma->vm_pgoff = offset >> PAGE_SHIFT; - } -#endif - - if (offset >= DRM_PANTHOR_USER_MMIO_OFFSET) ret = panthor_device_mmap_io(ptdev, vma); - else + } else { ret = drm_gem_mmap(filp, vma); + } drm_dev_exit(cookie); return ret; @@ -1583,6 +1607,7 @@ static void panthor_debugfs_init(struct drm_minor *minor) * - adds PANTHOR_GROUP_PRIORITY_REALTIME priority * - 1.3 - adds DRM_PANTHOR_GROUP_STATE_INNOCENT flag * - 1.4 - adds DRM_IOCTL_PANTHOR_BO_SET_LABEL ioctl + * - 1.5 - adds DRM_PANTHOR_SET_USER_MMIO_OFFSET ioctl */ static const struct drm_driver panthor_drm_driver = { .driver_features = DRIVER_RENDER | DRIVER_GEM | DRIVER_SYNCOBJ | @@ -1596,7 +1621,7 @@ static const struct drm_driver panthor_drm_driver = { .name = "panthor", .desc = "Panthor DRM driver", .major = 1, - .minor = 4, + .minor = 5, .gem_create_object = panthor_gem_create_object, .gem_prime_import_sg_table = drm_gem_shmem_prime_import_sg_table, diff --git a/drivers/gpu/drm/panthor/panthor_fw.c b/drivers/gpu/drm/panthor/panthor_fw.c index 7bc38e635329..36f1034839c2 100644 --- a/drivers/gpu/drm/panthor/panthor_fw.c +++ b/drivers/gpu/drm/panthor/panthor_fw.c @@ -1063,8 +1063,8 @@ static void panthor_fw_stop(struct panthor_device *ptdev) u32 status; gpu_write(ptdev, MCU_CONTROL, MCU_CONTROL_DISABLE); - if (readl_poll_timeout(ptdev->iomem + MCU_STATUS, status, - status == MCU_STATUS_DISABLED, 10, 100000)) + if (gpu_read_poll_timeout(ptdev, MCU_STATUS, status, + status == MCU_STATUS_DISABLED, 10, 100000)) drm_err(&ptdev->base, "Failed to stop MCU"); } @@ -1089,8 +1089,9 @@ void panthor_fw_pre_reset(struct panthor_device *ptdev, bool on_hang) panthor_fw_update_reqs(glb_iface, req, GLB_HALT, GLB_HALT); gpu_write(ptdev, CSF_DOORBELL(CSF_GLB_DOORBELL_ID), 1); - if (!readl_poll_timeout(ptdev->iomem + MCU_STATUS, status, - status == MCU_STATUS_HALT, 10, 100000)) { + if (!gpu_read_poll_timeout(ptdev, MCU_STATUS, status, + status == MCU_STATUS_HALT, 10, + 100000)) { ptdev->reset.fast = true; } else { drm_warn(&ptdev->base, "Failed to cleanly suspend MCU"); diff --git a/drivers/gpu/drm/panthor/panthor_gpu.c b/drivers/gpu/drm/panthor/panthor_gpu.c index 32d678a0114e..534735518824 100644 --- a/drivers/gpu/drm/panthor/panthor_gpu.c +++ b/drivers/gpu/drm/panthor/panthor_gpu.c @@ -108,14 +108,9 @@ static void panthor_gpu_init_info(struct panthor_device *ptdev) ptdev->gpu_info.as_present = gpu_read(ptdev, GPU_AS_PRESENT); - ptdev->gpu_info.shader_present = gpu_read(ptdev, GPU_SHADER_PRESENT_LO); - ptdev->gpu_info.shader_present |= (u64)gpu_read(ptdev, GPU_SHADER_PRESENT_HI) << 32; - - ptdev->gpu_info.tiler_present = gpu_read(ptdev, GPU_TILER_PRESENT_LO); - ptdev->gpu_info.tiler_present |= (u64)gpu_read(ptdev, GPU_TILER_PRESENT_HI) << 32; - - ptdev->gpu_info.l2_present = gpu_read(ptdev, GPU_L2_PRESENT_LO); - ptdev->gpu_info.l2_present |= (u64)gpu_read(ptdev, GPU_L2_PRESENT_HI) << 32; + ptdev->gpu_info.shader_present = gpu_read64(ptdev, GPU_SHADER_PRESENT); + ptdev->gpu_info.tiler_present = gpu_read64(ptdev, GPU_TILER_PRESENT); + ptdev->gpu_info.l2_present = gpu_read64(ptdev, GPU_L2_PRESENT); arch_major = GPU_ARCH_MAJOR(ptdev->gpu_info.gpu_id); product_major = GPU_PROD_MAJOR(ptdev->gpu_info.gpu_id); @@ -154,8 +149,7 @@ static void panthor_gpu_irq_handler(struct panthor_device *ptdev, u32 status) if (status & GPU_IRQ_FAULT) { u32 fault_status = gpu_read(ptdev, GPU_FAULT_STATUS); - u64 address = ((u64)gpu_read(ptdev, GPU_FAULT_ADDR_HI) << 32) | - gpu_read(ptdev, GPU_FAULT_ADDR_LO); + u64 address = gpu_read64(ptdev, GPU_FAULT_ADDR); drm_warn(&ptdev->base, "GPU Fault 0x%08x (%s) at 0x%016llx\n", fault_status, panthor_exception_name(ptdev, fault_status & 0xFF), @@ -246,45 +240,27 @@ int panthor_gpu_block_power_off(struct panthor_device *ptdev, u32 pwroff_reg, u32 pwrtrans_reg, u64 mask, u32 timeout_us) { - u32 val, i; + u32 val; int ret; - for (i = 0; i < 2; i++) { - u32 mask32 = mask >> (i * 32); - - if (!mask32) - continue; - - ret = readl_relaxed_poll_timeout(ptdev->iomem + pwrtrans_reg + (i * 4), - val, !(mask32 & val), - 100, timeout_us); - if (ret) { - drm_err(&ptdev->base, "timeout waiting on %s:%llx power transition", - blk_name, mask); - return ret; - } + ret = gpu_read64_relaxed_poll_timeout(ptdev, pwrtrans_reg, val, + !(mask & val), 100, timeout_us); + if (ret) { + drm_err(&ptdev->base, + "timeout waiting on %s:%llx power transition", blk_name, + mask); + return ret; } - if (mask & GENMASK(31, 0)) - gpu_write(ptdev, pwroff_reg, mask); + gpu_write64(ptdev, pwroff_reg, mask); - if (mask >> 32) - gpu_write(ptdev, pwroff_reg + 4, mask >> 32); - - for (i = 0; i < 2; i++) { - u32 mask32 = mask >> (i * 32); - - if (!mask32) - continue; - - ret = readl_relaxed_poll_timeout(ptdev->iomem + pwrtrans_reg + (i * 4), - val, !(mask32 & val), - 100, timeout_us); - if (ret) { - drm_err(&ptdev->base, "timeout waiting on %s:%llx power transition", - blk_name, mask); - return ret; - } + ret = gpu_read64_relaxed_poll_timeout(ptdev, pwrtrans_reg, val, + !(mask & val), 100, timeout_us); + if (ret) { + drm_err(&ptdev->base, + "timeout waiting on %s:%llx power transition", blk_name, + mask); + return ret; } return 0; @@ -307,45 +283,26 @@ int panthor_gpu_block_power_on(struct panthor_device *ptdev, u32 pwron_reg, u32 pwrtrans_reg, u32 rdy_reg, u64 mask, u32 timeout_us) { - u32 val, i; + u32 val; int ret; - for (i = 0; i < 2; i++) { - u32 mask32 = mask >> (i * 32); - - if (!mask32) - continue; - - ret = readl_relaxed_poll_timeout(ptdev->iomem + pwrtrans_reg + (i * 4), - val, !(mask32 & val), - 100, timeout_us); - if (ret) { - drm_err(&ptdev->base, "timeout waiting on %s:%llx power transition", - blk_name, mask); - return ret; - } + ret = gpu_read64_relaxed_poll_timeout(ptdev, pwrtrans_reg, val, + !(mask & val), 100, timeout_us); + if (ret) { + drm_err(&ptdev->base, + "timeout waiting on %s:%llx power transition", blk_name, + mask); + return ret; } - if (mask & GENMASK(31, 0)) - gpu_write(ptdev, pwron_reg, mask); - - if (mask >> 32) - gpu_write(ptdev, pwron_reg + 4, mask >> 32); - - for (i = 0; i < 2; i++) { - u32 mask32 = mask >> (i * 32); - - if (!mask32) - continue; + gpu_write64(ptdev, pwron_reg, mask); - ret = readl_relaxed_poll_timeout(ptdev->iomem + rdy_reg + (i * 4), - val, (mask32 & val) == mask32, - 100, timeout_us); - if (ret) { - drm_err(&ptdev->base, "timeout waiting on %s:%llx readiness", - blk_name, mask); - return ret; - } + ret = gpu_read64_relaxed_poll_timeout(ptdev, pwrtrans_reg, val, + !(mask & val), 100, timeout_us); + if (ret) { + drm_err(&ptdev->base, "timeout waiting on %s:%llx readiness", + blk_name, mask); + return ret; } return 0; @@ -494,49 +451,3 @@ void panthor_gpu_resume(struct panthor_device *ptdev) panthor_gpu_l2_power_on(ptdev); } -/** - * panthor_gpu_read_64bit_counter() - Read a 64-bit counter at a given offset. - * @ptdev: Device. - * @reg: The offset of the register to read. - * - * Return: The counter value. - */ -static u64 -panthor_gpu_read_64bit_counter(struct panthor_device *ptdev, u32 reg) -{ - u32 hi, lo; - - do { - hi = gpu_read(ptdev, reg + 0x4); - lo = gpu_read(ptdev, reg); - } while (hi != gpu_read(ptdev, reg + 0x4)); - - return ((u64)hi << 32) | lo; -} - -/** - * panthor_gpu_read_timestamp() - Read the timestamp register. - * @ptdev: Device. - * - * Return: The GPU timestamp value. - */ -u64 panthor_gpu_read_timestamp(struct panthor_device *ptdev) -{ - return panthor_gpu_read_64bit_counter(ptdev, GPU_TIMESTAMP_LO); -} - -/** - * panthor_gpu_read_timestamp_offset() - Read the timestamp offset register. - * @ptdev: Device. - * - * Return: The GPU timestamp offset value. - */ -u64 panthor_gpu_read_timestamp_offset(struct panthor_device *ptdev) -{ - u32 hi, lo; - - hi = gpu_read(ptdev, GPU_TIMESTAMP_OFFSET_HI); - lo = gpu_read(ptdev, GPU_TIMESTAMP_OFFSET_LO); - - return ((u64)hi << 32) | lo; -} diff --git a/drivers/gpu/drm/panthor/panthor_gpu.h b/drivers/gpu/drm/panthor/panthor_gpu.h index 7f6133a66127..7c17a8c06858 100644 --- a/drivers/gpu/drm/panthor/panthor_gpu.h +++ b/drivers/gpu/drm/panthor/panthor_gpu.h @@ -30,9 +30,9 @@ int panthor_gpu_block_power_off(struct panthor_device *ptdev, */ #define panthor_gpu_power_on(ptdev, type, mask, timeout_us) \ panthor_gpu_block_power_on(ptdev, #type, \ - type ## _PWRON_LO, \ - type ## _PWRTRANS_LO, \ - type ## _READY_LO, \ + type ## _PWRON, \ + type ## _PWRTRANS, \ + type ## _READY, \ mask, timeout_us) /** @@ -42,15 +42,13 @@ int panthor_gpu_block_power_off(struct panthor_device *ptdev, */ #define panthor_gpu_power_off(ptdev, type, mask, timeout_us) \ panthor_gpu_block_power_off(ptdev, #type, \ - type ## _PWROFF_LO, \ - type ## _PWRTRANS_LO, \ + type ## _PWROFF, \ + type ## _PWRTRANS, \ mask, timeout_us) int panthor_gpu_l2_power_on(struct panthor_device *ptdev); int panthor_gpu_flush_caches(struct panthor_device *ptdev, u32 l2, u32 lsc, u32 other); int panthor_gpu_soft_reset(struct panthor_device *ptdev); -u64 panthor_gpu_read_timestamp(struct panthor_device *ptdev); -u64 panthor_gpu_read_timestamp_offset(struct panthor_device *ptdev); #endif diff --git a/drivers/gpu/drm/panthor/panthor_mmu.c b/drivers/gpu/drm/panthor/panthor_mmu.c index 6ca9a2642a4e..b39ea6acc6a9 100644 --- a/drivers/gpu/drm/panthor/panthor_mmu.c +++ b/drivers/gpu/drm/panthor/panthor_mmu.c @@ -510,9 +510,9 @@ static int wait_ready(struct panthor_device *ptdev, u32 as_nr) /* Wait for the MMU status to indicate there is no active command, in * case one is pending. */ - ret = readl_relaxed_poll_timeout_atomic(ptdev->iomem + AS_STATUS(as_nr), - val, !(val & AS_STATUS_AS_ACTIVE), - 10, 100000); + ret = gpu_read_relaxed_poll_timeout_atomic(ptdev, AS_STATUS(as_nr), val, + !(val & AS_STATUS_AS_ACTIVE), + 10, 100000); if (ret) { panthor_device_schedule_reset(ptdev); @@ -564,8 +564,7 @@ static void lock_region(struct panthor_device *ptdev, u32 as_nr, region = region_width | region_start; /* Lock the region that needs to be updated */ - gpu_write(ptdev, AS_LOCKADDR_LO(as_nr), lower_32_bits(region)); - gpu_write(ptdev, AS_LOCKADDR_HI(as_nr), upper_32_bits(region)); + gpu_write64(ptdev, AS_LOCKADDR(as_nr), region); write_cmd(ptdev, as_nr, AS_COMMAND_LOCK); } @@ -615,14 +614,9 @@ static int panthor_mmu_as_enable(struct panthor_device *ptdev, u32 as_nr, if (ret) return ret; - gpu_write(ptdev, AS_TRANSTAB_LO(as_nr), lower_32_bits(transtab)); - gpu_write(ptdev, AS_TRANSTAB_HI(as_nr), upper_32_bits(transtab)); - - gpu_write(ptdev, AS_MEMATTR_LO(as_nr), lower_32_bits(memattr)); - gpu_write(ptdev, AS_MEMATTR_HI(as_nr), upper_32_bits(memattr)); - - gpu_write(ptdev, AS_TRANSCFG_LO(as_nr), lower_32_bits(transcfg)); - gpu_write(ptdev, AS_TRANSCFG_HI(as_nr), upper_32_bits(transcfg)); + gpu_write64(ptdev, AS_TRANSTAB(as_nr), transtab); + gpu_write64(ptdev, AS_MEMATTR(as_nr), memattr); + gpu_write64(ptdev, AS_TRANSCFG(as_nr), transcfg); return write_cmd(ptdev, as_nr, AS_COMMAND_UPDATE); } @@ -635,14 +629,9 @@ static int panthor_mmu_as_disable(struct panthor_device *ptdev, u32 as_nr) if (ret) return ret; - gpu_write(ptdev, AS_TRANSTAB_LO(as_nr), 0); - gpu_write(ptdev, AS_TRANSTAB_HI(as_nr), 0); - - gpu_write(ptdev, AS_MEMATTR_LO(as_nr), 0); - gpu_write(ptdev, AS_MEMATTR_HI(as_nr), 0); - - gpu_write(ptdev, AS_TRANSCFG_LO(as_nr), AS_TRANSCFG_ADRMODE_UNMAPPED); - gpu_write(ptdev, AS_TRANSCFG_HI(as_nr), 0); + gpu_write64(ptdev, AS_TRANSTAB(as_nr), 0); + gpu_write64(ptdev, AS_MEMATTR(as_nr), 0); + gpu_write64(ptdev, AS_TRANSCFG(as_nr), AS_TRANSCFG_ADRMODE_UNMAPPED); return write_cmd(ptdev, as_nr, AS_COMMAND_UPDATE); } @@ -1681,8 +1670,7 @@ static void panthor_mmu_irq_handler(struct panthor_device *ptdev, u32 status) u32 source_id; fault_status = gpu_read(ptdev, AS_FAULTSTATUS(as)); - addr = gpu_read(ptdev, AS_FAULTADDRESS_LO(as)); - addr |= (u64)gpu_read(ptdev, AS_FAULTADDRESS_HI(as)) << 32; + addr = gpu_read64(ptdev, AS_FAULTADDRESS(as)); /* decode the fault status */ exception_type = fault_status & 0xFF; @@ -2523,7 +2511,7 @@ panthor_vm_bind_job_create(struct drm_file *file, kref_init(&job->refcount); job->vm = panthor_vm_get(vm); - ret = drm_sched_job_init(&job->base, &vm->entity, 1, vm); + ret = drm_sched_job_init(&job->base, &vm->entity, 1, vm, file->client_id); if (ret) goto err_put_job; diff --git a/drivers/gpu/drm/panthor/panthor_regs.h b/drivers/gpu/drm/panthor/panthor_regs.h index a7a323dc5cf9..48bbfd40138c 100644 --- a/drivers/gpu/drm/panthor/panthor_regs.h +++ b/drivers/gpu/drm/panthor/panthor_regs.h @@ -63,20 +63,16 @@ #define GPU_STATUS_DBG_ENABLED BIT(8) #define GPU_FAULT_STATUS 0x3C -#define GPU_FAULT_ADDR_LO 0x40 -#define GPU_FAULT_ADDR_HI 0x44 +#define GPU_FAULT_ADDR 0x40 #define GPU_PWR_KEY 0x50 #define GPU_PWR_KEY_UNLOCK 0x2968A819 #define GPU_PWR_OVERRIDE0 0x54 #define GPU_PWR_OVERRIDE1 0x58 -#define GPU_TIMESTAMP_OFFSET_LO 0x88 -#define GPU_TIMESTAMP_OFFSET_HI 0x8C -#define GPU_CYCLE_COUNT_LO 0x90 -#define GPU_CYCLE_COUNT_HI 0x94 -#define GPU_TIMESTAMP_LO 0x98 -#define GPU_TIMESTAMP_HI 0x9C +#define GPU_TIMESTAMP_OFFSET 0x88 +#define GPU_CYCLE_COUNT 0x90 +#define GPU_TIMESTAMP 0x98 #define GPU_THREAD_MAX_THREADS 0xA0 #define GPU_THREAD_MAX_WORKGROUP_SIZE 0xA4 @@ -85,47 +81,29 @@ #define GPU_TEXTURE_FEATURES(n) (0xB0 + ((n) * 4)) -#define GPU_SHADER_PRESENT_LO 0x100 -#define GPU_SHADER_PRESENT_HI 0x104 -#define GPU_TILER_PRESENT_LO 0x110 -#define GPU_TILER_PRESENT_HI 0x114 -#define GPU_L2_PRESENT_LO 0x120 -#define GPU_L2_PRESENT_HI 0x124 - -#define SHADER_READY_LO 0x140 -#define SHADER_READY_HI 0x144 -#define TILER_READY_LO 0x150 -#define TILER_READY_HI 0x154 -#define L2_READY_LO 0x160 -#define L2_READY_HI 0x164 - -#define SHADER_PWRON_LO 0x180 -#define SHADER_PWRON_HI 0x184 -#define TILER_PWRON_LO 0x190 -#define TILER_PWRON_HI 0x194 -#define L2_PWRON_LO 0x1A0 -#define L2_PWRON_HI 0x1A4 - -#define SHADER_PWROFF_LO 0x1C0 -#define SHADER_PWROFF_HI 0x1C4 -#define TILER_PWROFF_LO 0x1D0 -#define TILER_PWROFF_HI 0x1D4 -#define L2_PWROFF_LO 0x1E0 -#define L2_PWROFF_HI 0x1E4 - -#define SHADER_PWRTRANS_LO 0x200 -#define SHADER_PWRTRANS_HI 0x204 -#define TILER_PWRTRANS_LO 0x210 -#define TILER_PWRTRANS_HI 0x214 -#define L2_PWRTRANS_LO 0x220 -#define L2_PWRTRANS_HI 0x224 - -#define SHADER_PWRACTIVE_LO 0x240 -#define SHADER_PWRACTIVE_HI 0x244 -#define TILER_PWRACTIVE_LO 0x250 -#define TILER_PWRACTIVE_HI 0x254 -#define L2_PWRACTIVE_LO 0x260 -#define L2_PWRACTIVE_HI 0x264 +#define GPU_SHADER_PRESENT 0x100 +#define GPU_TILER_PRESENT 0x110 +#define GPU_L2_PRESENT 0x120 + +#define SHADER_READY 0x140 +#define TILER_READY 0x150 +#define L2_READY 0x160 + +#define SHADER_PWRON 0x180 +#define TILER_PWRON 0x190 +#define L2_PWRON 0x1A0 + +#define SHADER_PWROFF 0x1C0 +#define TILER_PWROFF 0x1D0 +#define L2_PWROFF 0x1E0 + +#define SHADER_PWRTRANS 0x200 +#define TILER_PWRTRANS 0x210 +#define L2_PWRTRANS 0x220 + +#define SHADER_PWRACTIVE 0x240 +#define TILER_PWRACTIVE 0x250 +#define L2_PWRACTIVE 0x260 #define GPU_REVID 0x280 @@ -168,10 +146,8 @@ #define MMU_AS_SHIFT 6 #define MMU_AS(as) (MMU_BASE + ((as) << MMU_AS_SHIFT)) -#define AS_TRANSTAB_LO(as) (MMU_AS(as) + 0x0) -#define AS_TRANSTAB_HI(as) (MMU_AS(as) + 0x4) -#define AS_MEMATTR_LO(as) (MMU_AS(as) + 0x8) -#define AS_MEMATTR_HI(as) (MMU_AS(as) + 0xC) +#define AS_TRANSTAB(as) (MMU_AS(as) + 0x0) +#define AS_MEMATTR(as) (MMU_AS(as) + 0x8) #define AS_MEMATTR_AARCH64_INNER_ALLOC_IMPL (2 << 2) #define AS_MEMATTR_AARCH64_INNER_ALLOC_EXPL(w, r) ((3 << 2) | \ ((w) ? BIT(0) : 0) | \ @@ -183,8 +159,7 @@ #define AS_MEMATTR_AARCH64_INNER_OUTER_NC (1 << 6) #define AS_MEMATTR_AARCH64_INNER_OUTER_WB (2 << 6) #define AS_MEMATTR_AARCH64_FAULT (3 << 6) -#define AS_LOCKADDR_LO(as) (MMU_AS(as) + 0x10) -#define AS_LOCKADDR_HI(as) (MMU_AS(as) + 0x14) +#define AS_LOCKADDR(as) (MMU_AS(as) + 0x10) #define AS_COMMAND(as) (MMU_AS(as) + 0x18) #define AS_COMMAND_NOP 0 #define AS_COMMAND_UPDATE 1 @@ -199,12 +174,10 @@ #define AS_FAULTSTATUS_ACCESS_TYPE_EX (0x1 << 8) #define AS_FAULTSTATUS_ACCESS_TYPE_READ (0x2 << 8) #define AS_FAULTSTATUS_ACCESS_TYPE_WRITE (0x3 << 8) -#define AS_FAULTADDRESS_LO(as) (MMU_AS(as) + 0x20) -#define AS_FAULTADDRESS_HI(as) (MMU_AS(as) + 0x24) +#define AS_FAULTADDRESS(as) (MMU_AS(as) + 0x20) #define AS_STATUS(as) (MMU_AS(as) + 0x28) #define AS_STATUS_AS_ACTIVE BIT(0) -#define AS_TRANSCFG_LO(as) (MMU_AS(as) + 0x30) -#define AS_TRANSCFG_HI(as) (MMU_AS(as) + 0x34) +#define AS_TRANSCFG(as) (MMU_AS(as) + 0x30) #define AS_TRANSCFG_ADRMODE_UNMAPPED (1 << 0) #define AS_TRANSCFG_ADRMODE_IDENTITY (2 << 0) #define AS_TRANSCFG_ADRMODE_AARCH64_4K (6 << 0) @@ -222,18 +195,11 @@ #define AS_TRANSCFG_DISABLE_AF_FAULT BIT(34) #define AS_TRANSCFG_WXN BIT(35) #define AS_TRANSCFG_XREADABLE BIT(36) -#define AS_FAULTEXTRA_LO(as) (MMU_AS(as) + 0x38) -#define AS_FAULTEXTRA_HI(as) (MMU_AS(as) + 0x3C) +#define AS_FAULTEXTRA(as) (MMU_AS(as) + 0x38) #define CSF_GPU_LATEST_FLUSH_ID 0x10000 #define CSF_DOORBELL(i) (0x80000 + ((i) * 0x10000)) #define CSF_GLB_DOORBELL_ID 0 -#define gpu_write(dev, reg, data) \ - writel(data, (dev)->iomem + (reg)) - -#define gpu_read(dev, reg) \ - readl((dev)->iomem + (reg)) - #endif diff --git a/drivers/gpu/drm/panthor/panthor_sched.c b/drivers/gpu/drm/panthor/panthor_sched.c index 43ee57728de5..a2248f692a03 100644 --- a/drivers/gpu/drm/panthor/panthor_sched.c +++ b/drivers/gpu/drm/panthor/panthor_sched.c @@ -3732,7 +3732,8 @@ struct panthor_vm *panthor_job_vm(struct drm_sched_job *sched_job) struct drm_sched_job * panthor_job_create(struct panthor_file *pfile, u16 group_handle, - const struct drm_panthor_queue_submit *qsubmit) + const struct drm_panthor_queue_submit *qsubmit, + u64 drm_client_id) { struct panthor_group_pool *gpool = pfile->groups; struct panthor_job *job; @@ -3804,7 +3805,7 @@ panthor_job_create(struct panthor_file *pfile, ret = drm_sched_job_init(&job->base, &job->group->queues[job->queue_idx]->entity, - credits, job->group); + credits, job->group, drm_client_id); if (ret) goto err_put_job; diff --git a/drivers/gpu/drm/panthor/panthor_sched.h b/drivers/gpu/drm/panthor/panthor_sched.h index e650a445cf50..742b0b4ff3a3 100644 --- a/drivers/gpu/drm/panthor/panthor_sched.h +++ b/drivers/gpu/drm/panthor/panthor_sched.h @@ -29,7 +29,8 @@ int panthor_group_get_state(struct panthor_file *pfile, struct drm_sched_job * panthor_job_create(struct panthor_file *pfile, u16 group_handle, - const struct drm_panthor_queue_submit *qsubmit); + const struct drm_panthor_queue_submit *qsubmit, + u64 drm_client_id); struct drm_sched_job *panthor_job_get(struct drm_sched_job *job); struct panthor_vm *panthor_job_vm(struct drm_sched_job *sched_job); void panthor_job_put(struct drm_sched_job *job); diff --git a/drivers/gpu/drm/renesas/rcar-du/rcar_du_plane.h b/drivers/gpu/drm/renesas/rcar-du/rcar_du_plane.h index f9893d7d6dfc..e9e59c5e70d5 100644 --- a/drivers/gpu/drm/renesas/rcar-du/rcar_du_plane.h +++ b/drivers/gpu/drm/renesas/rcar-du/rcar_du_plane.h @@ -16,7 +16,7 @@ struct rcar_du_format_info; struct rcar_du_group; /* - * The RCAR DU has 8 hardware planes, shared between primary and overlay planes. + * The R-Car DU has 8 hardware planes, shared between primary and overlay planes. * As using overlay planes requires at least one of the CRTCs being enabled, no * more than 7 overlay planes can be available. We thus create 1 primary plane * per CRTC and 7 overlay planes, for a total of up to 9 KMS planes. diff --git a/drivers/gpu/drm/renesas/rcar-du/rcar_lvds.c b/drivers/gpu/drm/renesas/rcar-du/rcar_lvds.c index a9145253294f..af58b814e588 100644 --- a/drivers/gpu/drm/renesas/rcar-du/rcar_lvds.c +++ b/drivers/gpu/drm/renesas/rcar-du/rcar_lvds.c @@ -878,9 +878,10 @@ static int rcar_lvds_probe(struct platform_device *pdev) struct rcar_lvds *lvds; int ret; - lvds = devm_kzalloc(&pdev->dev, sizeof(*lvds), GFP_KERNEL); - if (lvds == NULL) - return -ENOMEM; + lvds = devm_drm_bridge_alloc(&pdev->dev, struct rcar_lvds, bridge, + &rcar_lvds_bridge_ops); + if (IS_ERR(lvds)) + return PTR_ERR(lvds); platform_set_drvdata(pdev, lvds); @@ -895,7 +896,6 @@ static int rcar_lvds_probe(struct platform_device *pdev) if (ret < 0) return ret; - lvds->bridge.funcs = &rcar_lvds_bridge_ops; lvds->bridge.of_node = pdev->dev.of_node; lvds->mmio = devm_platform_ioremap_resource(pdev, 0); diff --git a/drivers/gpu/drm/renesas/rcar-du/rcar_mipi_dsi.c b/drivers/gpu/drm/renesas/rcar-du/rcar_mipi_dsi.c index 7ab8be46c7f6..1af4c73f7a88 100644 --- a/drivers/gpu/drm/renesas/rcar-du/rcar_mipi_dsi.c +++ b/drivers/gpu/drm/renesas/rcar-du/rcar_mipi_dsi.c @@ -918,7 +918,6 @@ static int rcar_mipi_dsi_host_attach(struct mipi_dsi_host *host, } /* Initialize the DRM bridge. */ - dsi->bridge.funcs = &rcar_mipi_dsi_bridge_ops; dsi->bridge.of_node = dsi->dev->of_node; drm_bridge_add(&dsi->bridge); @@ -1004,9 +1003,10 @@ static int rcar_mipi_dsi_probe(struct platform_device *pdev) struct rcar_mipi_dsi *dsi; int ret; - dsi = devm_kzalloc(&pdev->dev, sizeof(*dsi), GFP_KERNEL); - if (dsi == NULL) - return -ENOMEM; + dsi = devm_drm_bridge_alloc(&pdev->dev, struct rcar_mipi_dsi, bridge, + &rcar_mipi_dsi_bridge_ops); + if (IS_ERR(dsi)) + return PTR_ERR(dsi); platform_set_drvdata(pdev, dsi); diff --git a/drivers/gpu/drm/renesas/rz-du/rzg2l_du_drv.c b/drivers/gpu/drm/renesas/rz-du/rzg2l_du_drv.c index 5e40f0c1e7b0..e1aa6a719529 100644 --- a/drivers/gpu/drm/renesas/rz-du/rzg2l_du_drv.c +++ b/drivers/gpu/drm/renesas/rz-du/rzg2l_du_drv.c @@ -50,9 +50,20 @@ static const struct rzg2l_du_device_info rzg2l_du_r9a07g044_info = { } }; +static const struct rzg2l_du_device_info rzg2l_du_r9a09g057_info = { + .channels_mask = BIT(0), + .routes = { + [RZG2L_DU_OUTPUT_DSI0] = { + .possible_outputs = BIT(0), + .port = 0, + }, + }, +}; + static const struct of_device_id rzg2l_du_of_table[] = { { .compatible = "renesas,r9a07g043u-du", .data = &rzg2l_du_r9a07g043u_info }, { .compatible = "renesas,r9a07g044-du", .data = &rzg2l_du_r9a07g044_info }, + { .compatible = "renesas,r9a09g057-du", .data = &rzg2l_du_r9a09g057_info }, { /* sentinel */ } }; diff --git a/drivers/gpu/drm/renesas/rz-du/rzg2l_du_encoder.c b/drivers/gpu/drm/renesas/rz-du/rzg2l_du_encoder.c index 564ab4cb3d37..5e6dd16705e6 100644 --- a/drivers/gpu/drm/renesas/rz-du/rzg2l_du_encoder.c +++ b/drivers/gpu/drm/renesas/rz-du/rzg2l_du_encoder.c @@ -22,6 +22,26 @@ * Encoder */ +static unsigned int rzg2l_du_encoder_count_ports(struct device_node *node) +{ + struct device_node *ports; + struct device_node *port; + unsigned int num_ports = 0; + + ports = of_get_child_by_name(node, "ports"); + if (!ports) + ports = of_node_get(node); + + for_each_child_of_node(ports, port) { + if (of_node_name_eq(port, "port")) + num_ports++; + } + + of_node_put(ports); + + return num_ports; +} + static const struct drm_encoder_funcs rzg2l_du_encoder_funcs = { }; @@ -50,10 +70,26 @@ int rzg2l_du_encoder_init(struct rzg2l_du_device *rcdu, struct drm_bridge *bridge; int ret; - /* Locate the DRM bridge from the DT node. */ - bridge = of_drm_find_bridge(enc_node); - if (!bridge) - return -EPROBE_DEFER; + /* + * Locate the DRM bridge from the DT node. For the DPAD outputs, if the + * DT node has a single port, assume that it describes a panel and + * create a panel bridge. + */ + if (output == RZG2L_DU_OUTPUT_DPAD0 && rzg2l_du_encoder_count_ports(enc_node) == 1) { + struct drm_panel *panel = of_drm_find_panel(enc_node); + + if (IS_ERR(panel)) + return PTR_ERR(panel); + + bridge = devm_drm_panel_bridge_add_typed(rcdu->dev, panel, + DRM_MODE_CONNECTOR_DPI); + if (IS_ERR(bridge)) + return PTR_ERR(bridge); + } else { + bridge = of_drm_find_bridge(enc_node); + if (!bridge) + return -EPROBE_DEFER; + } dev_dbg(rcdu->dev, "initializing encoder %pOF for output %s\n", enc_node, rzg2l_du_output_name(output)); diff --git a/drivers/gpu/drm/renesas/rz-du/rzg2l_mipi_dsi.c b/drivers/gpu/drm/renesas/rz-du/rzg2l_mipi_dsi.c index dc6ab012cdb6..50ec109aa6ed 100644 --- a/drivers/gpu/drm/renesas/rz-du/rzg2l_mipi_dsi.c +++ b/drivers/gpu/drm/renesas/rz-du/rzg2l_mipi_dsi.c @@ -4,8 +4,11 @@ * * Copyright (C) 2022 Renesas Electronics Corporation */ + +#include <linux/bitfield.h> #include <linux/clk.h> #include <linux/delay.h> +#include <linux/dma-mapping.h> #include <linux/io.h> #include <linux/iopoll.h> #include <linux/module.h> @@ -23,9 +26,12 @@ #include <drm/drm_of.h> #include <drm/drm_panel.h> #include <drm/drm_probe_helper.h> +#include <video/mipi_display.h> #include "rzg2l_mipi_dsi_regs.h" +#define RZG2L_DCS_BUF_SIZE 128 /* Maximum DCS buffer size in external memory. */ + struct rzg2l_mipi_dsi { struct device *dev; void __iomem *mmio; @@ -44,6 +50,10 @@ struct rzg2l_mipi_dsi { unsigned int num_data_lanes; unsigned int lanes; unsigned long mode_flags; + + /* DCS buffer pointers when using external memory. */ + dma_addr_t dcs_buf_phys; + u8 *dcs_buf_virt; }; static inline struct rzg2l_mipi_dsi * @@ -267,6 +277,7 @@ static int rzg2l_mipi_dsi_startup(struct rzg2l_mipi_dsi *dsi, u32 clkbfht; u32 clkstpt; u32 golpbkt; + u32 dsisetr; int ret; /* @@ -328,6 +339,15 @@ static int rzg2l_mipi_dsi_startup(struct rzg2l_mipi_dsi *dsi, lptrnstsetr = LPTRNSTSETR_GOLPBKT(golpbkt); rzg2l_mipi_dsi_link_write(dsi, LPTRNSTSETR, lptrnstsetr); + /* + * Increase MRPSZ as the default value of 1 will result in long read + * commands payload not being saved to memory. + */ + dsisetr = rzg2l_mipi_dsi_link_read(dsi, DSISETR); + dsisetr &= ~DSISETR_MRPSZ; + dsisetr |= FIELD_PREP(DSISETR_MRPSZ, RZG2L_DCS_BUF_SIZE); + rzg2l_mipi_dsi_link_write(dsi, DSISETR, dsisetr); + return 0; err_phy: @@ -532,8 +552,8 @@ static int rzg2l_mipi_dsi_attach(struct drm_bridge *bridge, flags); } -static void rzg2l_mipi_dsi_atomic_enable(struct drm_bridge *bridge, - struct drm_atomic_state *state) +static void rzg2l_mipi_dsi_atomic_pre_enable(struct drm_bridge *bridge, + struct drm_atomic_state *state) { struct rzg2l_mipi_dsi *dsi = bridge_to_rzg2l_mipi_dsi(bridge); const struct drm_display_mode *mode; @@ -550,6 +570,13 @@ static void rzg2l_mipi_dsi_atomic_enable(struct drm_bridge *bridge, return; rzg2l_mipi_dsi_set_display_timing(dsi, mode); +} + +static void rzg2l_mipi_dsi_atomic_enable(struct drm_bridge *bridge, + struct drm_atomic_state *state) +{ + struct rzg2l_mipi_dsi *dsi = bridge_to_rzg2l_mipi_dsi(bridge); + int ret; ret = rzg2l_mipi_dsi_start_hs_clock(dsi); if (ret < 0) @@ -593,6 +620,7 @@ static const struct drm_bridge_funcs rzg2l_mipi_dsi_bridge_ops = { .atomic_duplicate_state = drm_atomic_helper_bridge_duplicate_state, .atomic_destroy_state = drm_atomic_helper_bridge_destroy_state, .atomic_reset = drm_atomic_helper_bridge_reset, + .atomic_pre_enable = rzg2l_mipi_dsi_atomic_pre_enable, .atomic_enable = rzg2l_mipi_dsi_atomic_enable, .atomic_disable = rzg2l_mipi_dsi_atomic_disable, .mode_valid = rzg2l_mipi_dsi_bridge_mode_valid, @@ -651,9 +679,168 @@ static int rzg2l_mipi_dsi_host_detach(struct mipi_dsi_host *host, return 0; } +static ssize_t rzg2l_mipi_dsi_read_response(struct rzg2l_mipi_dsi *dsi, + const struct mipi_dsi_msg *msg) +{ + u8 *msg_rx = msg->rx_buf; + u8 datatype; + u32 result; + u16 size; + + result = rzg2l_mipi_dsi_link_read(dsi, RXRSS0R); + if (result & RXRSS0R_RXPKTDFAIL) { + dev_err(dsi->dev, "packet rx data did not save correctly\n"); + return -EPROTO; + } + + if (result & RXRSS0R_RXFAIL) { + dev_err(dsi->dev, "packet rx failure\n"); + return -EPROTO; + } + + if (!(result & RXRSS0R_RXSUC)) + return -EPROTO; + + datatype = FIELD_GET(RXRSS0R_DT, result); + + switch (datatype) { + case 0: + dev_dbg(dsi->dev, "ACK\n"); + return 0; + case MIPI_DSI_RX_END_OF_TRANSMISSION: + dev_dbg(dsi->dev, "EoTp\n"); + return 0; + case MIPI_DSI_RX_ACKNOWLEDGE_AND_ERROR_REPORT: + dev_dbg(dsi->dev, "Acknowledge and error report: $%02x%02x\n", + (u8)FIELD_GET(RXRSS0R_DATA1, result), + (u8)FIELD_GET(RXRSS0R_DATA0, result)); + return 0; + case MIPI_DSI_RX_DCS_SHORT_READ_RESPONSE_1BYTE: + case MIPI_DSI_RX_GENERIC_SHORT_READ_RESPONSE_1BYTE: + msg_rx[0] = FIELD_GET(RXRSS0R_DATA0, result); + return 1; + case MIPI_DSI_RX_DCS_SHORT_READ_RESPONSE_2BYTE: + case MIPI_DSI_RX_GENERIC_SHORT_READ_RESPONSE_2BYTE: + msg_rx[0] = FIELD_GET(RXRSS0R_DATA0, result); + msg_rx[1] = FIELD_GET(RXRSS0R_DATA1, result); + return 2; + case MIPI_DSI_RX_GENERIC_LONG_READ_RESPONSE: + case MIPI_DSI_RX_DCS_LONG_READ_RESPONSE: + size = FIELD_GET(RXRSS0R_WC, result); + + if (size > msg->rx_len) { + dev_err(dsi->dev, "rx buffer too small"); + return -ENOSPC; + } + + memcpy(msg_rx, dsi->dcs_buf_virt, size); + return size; + default: + dev_err(dsi->dev, "unhandled response type: %02x\n", datatype); + return -EPROTO; + } +} + +static ssize_t rzg2l_mipi_dsi_host_transfer(struct mipi_dsi_host *host, + const struct mipi_dsi_msg *msg) +{ + struct rzg2l_mipi_dsi *dsi = host_to_rzg2l_mipi_dsi(host); + struct mipi_dsi_packet packet; + bool need_bta; + u32 value; + int ret; + + ret = mipi_dsi_create_packet(&packet, msg); + if (ret < 0) + return ret; + + /* Terminate operation after this descriptor is finished */ + value = SQCH0DSC0AR_NXACT_TERM; + + if (msg->flags & MIPI_DSI_MSG_REQ_ACK) { + need_bta = true; /* Message with explicitly requested ACK */ + value |= FIELD_PREP(SQCH0DSC0AR_BTA, SQCH0DSC0AR_BTA_NON_READ); + } else if (msg->rx_buf && msg->rx_len > 0) { + need_bta = true; /* Read request */ + value |= FIELD_PREP(SQCH0DSC0AR_BTA, SQCH0DSC0AR_BTA_READ); + } else { + need_bta = false; + value |= FIELD_PREP(SQCH0DSC0AR_BTA, SQCH0DSC0AR_BTA_NONE); + } + + /* Set transmission speed */ + if (msg->flags & MIPI_DSI_MSG_USE_LPM) + value |= SQCH0DSC0AR_SPD_LOW; + else + value |= SQCH0DSC0AR_SPD_HIGH; + + /* Write TX packet header */ + value |= FIELD_PREP(SQCH0DSC0AR_DT, packet.header[0]) | + FIELD_PREP(SQCH0DSC0AR_DATA0, packet.header[1]) | + FIELD_PREP(SQCH0DSC0AR_DATA1, packet.header[2]); + + if (mipi_dsi_packet_format_is_long(msg->type)) { + value |= SQCH0DSC0AR_FMT_LONG; + + if (packet.payload_length > RZG2L_DCS_BUF_SIZE) { + dev_err(dsi->dev, "Packet Tx payload size (%d) too large", + (unsigned int)packet.payload_length); + return -ENOSPC; + } + + /* Copy TX packet payload data to memory space */ + memcpy(dsi->dcs_buf_virt, packet.payload, packet.payload_length); + } else { + value |= SQCH0DSC0AR_FMT_SHORT; + } + + rzg2l_mipi_dsi_link_write(dsi, SQCH0DSC0AR, value); + + /* + * Write: specify payload data source location, only used for + * long packet. + * Read: specify payload data storage location of response + * packet. Note: a read packet is always a short packet. + * If the response packet is a short packet or a long packet + * with WC = 0 (no payload), DTSEL is meaningless. + */ + rzg2l_mipi_dsi_link_write(dsi, SQCH0DSC0BR, SQCH0DSC0BR_DTSEL_MEM_SPACE); + + /* + * Set SQCHxSR.AACTFIN bit when descriptor actions are finished. + * Read: set Rx result save slot number to 0 (ACTCODE). + */ + rzg2l_mipi_dsi_link_write(dsi, SQCH0DSC0CR, SQCH0DSC0CR_FINACT); + + /* Set rx/tx payload data address, only relevant for long packet. */ + rzg2l_mipi_dsi_link_write(dsi, SQCH0DSC0DR, (u32)dsi->dcs_buf_phys); + + /* Start sequence 0 operation */ + value = rzg2l_mipi_dsi_link_read(dsi, SQCH0SET0R); + value |= SQCH0SET0R_START; + rzg2l_mipi_dsi_link_write(dsi, SQCH0SET0R, value); + + /* Wait for operation to finish */ + ret = read_poll_timeout(rzg2l_mipi_dsi_link_read, + value, value & SQCH0SR_ADESFIN, + 2000, 20000, false, dsi, SQCH0SR); + if (ret == 0) { + /* Success: clear status bit */ + rzg2l_mipi_dsi_link_write(dsi, SQCH0SCR, SQCH0SCR_ADESFIN); + + if (need_bta) + ret = rzg2l_mipi_dsi_read_response(dsi, msg); + else + ret = packet.payload_length; + } + + return ret; +} + static const struct mipi_dsi_host_ops rzg2l_mipi_dsi_host_ops = { .attach = rzg2l_mipi_dsi_host_attach, .detach = rzg2l_mipi_dsi_host_detach, + .transfer = rzg2l_mipi_dsi_host_transfer, }; /* ----------------------------------------------------------------------------- @@ -701,9 +888,10 @@ static int rzg2l_mipi_dsi_probe(struct platform_device *pdev) u32 txsetr; int ret; - dsi = devm_kzalloc(&pdev->dev, sizeof(*dsi), GFP_KERNEL); - if (!dsi) - return -ENOMEM; + dsi = devm_drm_bridge_alloc(&pdev->dev, struct rzg2l_mipi_dsi, bridge, + &rzg2l_mipi_dsi_bridge_ops); + if (IS_ERR(dsi)) + return PTR_ERR(dsi); platform_set_drvdata(pdev, dsi); dsi->dev = &pdev->dev; @@ -761,7 +949,6 @@ static int rzg2l_mipi_dsi_probe(struct platform_device *pdev) pm_runtime_put(dsi->dev); /* Initialize the DRM bridge. */ - dsi->bridge.funcs = &rzg2l_mipi_dsi_bridge_ops; dsi->bridge.of_node = dsi->dev->of_node; /* Init host device */ @@ -771,6 +958,11 @@ static int rzg2l_mipi_dsi_probe(struct platform_device *pdev) if (ret < 0) goto err_pm_disable; + dsi->dcs_buf_virt = dma_alloc_coherent(dsi->host.dev, RZG2L_DCS_BUF_SIZE, + &dsi->dcs_buf_phys, GFP_KERNEL); + if (!dsi->dcs_buf_virt) + return -ENOMEM; + return 0; err_phy: @@ -785,6 +977,8 @@ static void rzg2l_mipi_dsi_remove(struct platform_device *pdev) { struct rzg2l_mipi_dsi *dsi = platform_get_drvdata(pdev); + dma_free_coherent(dsi->host.dev, RZG2L_DCS_BUF_SIZE, dsi->dcs_buf_virt, + dsi->dcs_buf_phys); mipi_dsi_host_unregister(&dsi->host); pm_runtime_disable(&pdev->dev); } diff --git a/drivers/gpu/drm/renesas/rz-du/rzg2l_mipi_dsi_regs.h b/drivers/gpu/drm/renesas/rz-du/rzg2l_mipi_dsi_regs.h index 1dbc16ec64a4..26d8a37ee635 100644 --- a/drivers/gpu/drm/renesas/rz-du/rzg2l_mipi_dsi_regs.h +++ b/drivers/gpu/drm/renesas/rz-du/rzg2l_mipi_dsi_regs.h @@ -81,6 +81,20 @@ #define RSTSR_SWRSTLP (1 << 1) #define RSTSR_SWRSTHS (1 << 0) +/* DSI Set Register */ +#define DSISETR 0x120 +#define DSISETR_MRPSZ GENMASK(15, 0) + +/* Rx Result Save Slot 0 Register */ +#define RXRSS0R 0x240 +#define RXRSS0R_RXPKTDFAIL BIT(28) +#define RXRSS0R_RXFAIL BIT(27) +#define RXRSS0R_RXSUC BIT(25) +#define RXRSS0R_DT GENMASK(21, 16) +#define RXRSS0R_DATA1 GENMASK(15, 8) +#define RXRSS0R_DATA0 GENMASK(7, 0) +#define RXRSS0R_WC GENMASK(15, 0) /* Word count for long packet. */ + /* Clock Lane Stop Time Set Register */ #define CLSTPTSETR 0x314 #define CLSTPTSETR_CLKKPT(x) ((x) << 24) @@ -148,4 +162,44 @@ #define VICH1HPSETR_HFP(x) (((x) & 0x1fff) << 16) #define VICH1HPSETR_HBP(x) (((x) & 0x1fff) << 0) +/* Sequence Channel 0 Set 0 Register */ +#define SQCH0SET0R 0x5c0 +#define SQCH0SET0R_START BIT(0) + +/* Sequence Channel 0 Status Register */ +#define SQCH0SR 0x5d0 +#define SQCH0SR_ADESFIN BIT(8) + +/* Sequence Channel 0 Status Clear Register */ +#define SQCH0SCR 0x5d4 +#define SQCH0SCR_ADESFIN BIT(8) + +/* Sequence Channel 0 Descriptor 0-A Register */ +#define SQCH0DSC0AR 0x780 +#define SQCH0DSC0AR_NXACT_TERM 0 /* Bit 28 */ +#define SQCH0DSC0AR_BTA GENMASK(27, 26) +#define SQCH0DSC0AR_BTA_NONE 0 +#define SQCH0DSC0AR_BTA_NON_READ 1 +#define SQCH0DSC0AR_BTA_READ 2 +#define SQCH0DSC0AR_BTA_ONLY 3 +#define SQCH0DSC0AR_SPD_HIGH 0 +#define SQCH0DSC0AR_SPD_LOW BIT(25) +#define SQCH0DSC0AR_FMT_SHORT 0 +#define SQCH0DSC0AR_FMT_LONG BIT(24) +#define SQCH0DSC0AR_DT GENMASK(21, 16) +#define SQCH0DSC0AR_DATA1 GENMASK(15, 8) +#define SQCH0DSC0AR_DATA0 GENMASK(7, 0) + +/* Sequence Channel 0 Descriptor 0-B Register */ +#define SQCH0DSC0BR 0x784 +#define SQCH0DSC0BR_DTSEL_MEM_SPACE BIT(24) /* Use external memory */ + +/* Sequence Channel 0 Descriptor 0-C Register */ +#define SQCH0DSC0CR 0x788 +#define SQCH0DSC0CR_FINACT BIT(0) +#define SQCH0DSC0CR_AUXOP BIT(22) + +/* Sequence Channel 0 Descriptor 0-D Register */ +#define SQCH0DSC0DR 0x78c + #endif /* __RZG2L_MIPI_DSI_REGS_H__ */ diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_fb.c b/drivers/gpu/drm/rockchip/rockchip_drm_fb.c index dcc1f07632c3..5829ee061c61 100644 --- a/drivers/gpu/drm/rockchip/rockchip_drm_fb.c +++ b/drivers/gpu/drm/rockchip/rockchip_drm_fb.c @@ -52,16 +52,9 @@ rockchip_fb_create(struct drm_device *dev, struct drm_file *file, } if (drm_is_afbc(mode_cmd->modifier[0])) { - int ret, i; - ret = drm_gem_fb_afbc_init(dev, mode_cmd, afbc_fb); if (ret) { - struct drm_gem_object **obj = afbc_fb->base.obj; - - for (i = 0; i < info->num_planes; ++i) - drm_gem_object_put(obj[i]); - - kfree(afbc_fb); + drm_framebuffer_put(&afbc_fb->base); return ERR_PTR(ret); } } diff --git a/drivers/gpu/drm/scheduler/gpu_scheduler_trace.h b/drivers/gpu/drm/scheduler/gpu_scheduler_trace.h index f56e77e7f6d0..261713dd7d5a 100644 --- a/drivers/gpu/drm/scheduler/gpu_scheduler_trace.h +++ b/drivers/gpu/drm/scheduler/gpu_scheduler_trace.h @@ -32,78 +32,123 @@ #define TRACE_SYSTEM gpu_scheduler #define TRACE_INCLUDE_FILE gpu_scheduler_trace +/** + * DOC: uAPI trace events + * + * ``drm_sched_job_queue``, ``drm_sched_job_run``, ``drm_sched_job_add_dep``, + * ``drm_sched_job_done`` and ``drm_sched_job_unschedulable`` are considered + * stable uAPI. + * + * Common trace events attributes: + * + * * ``dev`` - the dev_name() of the device running the job. + * + * * ``ring`` - the hardware ring running the job. Together with ``dev`` it + * uniquely identifies where the job is going to be executed. + * + * * ``fence`` - the &struct dma_fence.context and the &struct dma_fence.seqno of + * &struct drm_sched_fence.finished + * + * All the events depends on drm_sched_job_arm() having been called already for + * the job because they use &struct drm_sched_job.sched or + * &struct drm_sched_job.s_fence. + */ + DECLARE_EVENT_CLASS(drm_sched_job, TP_PROTO(struct drm_sched_job *sched_job, struct drm_sched_entity *entity), TP_ARGS(sched_job, entity), TP_STRUCT__entry( - __field(struct drm_sched_entity *, entity) - __field(struct dma_fence *, fence) __string(name, sched_job->sched->name) - __field(uint64_t, id) __field(u32, job_count) __field(int, hw_job_count) + __string(dev, dev_name(sched_job->sched->dev)) + __field(u64, fence_context) + __field(u64, fence_seqno) + __field(u64, client_id) ), TP_fast_assign( - __entry->entity = entity; - __entry->id = sched_job->id; - __entry->fence = &sched_job->s_fence->finished; __assign_str(name); __entry->job_count = spsc_queue_count(&entity->job_queue); __entry->hw_job_count = atomic_read( &sched_job->sched->credit_count); + __assign_str(dev); + __entry->fence_context = sched_job->s_fence->finished.context; + __entry->fence_seqno = sched_job->s_fence->finished.seqno; + __entry->client_id = sched_job->s_fence->drm_client_id; ), - TP_printk("entity=%p, id=%llu, fence=%p, ring=%s, job count:%u, hw job count:%d", - __entry->entity, __entry->id, - __entry->fence, __get_str(name), - __entry->job_count, __entry->hw_job_count) + TP_printk("dev=%s, fence=%llu:%llu, ring=%s, job count:%u, hw job count:%d, client_id:%llu", + __get_str(dev), + __entry->fence_context, __entry->fence_seqno, __get_str(name), + __entry->job_count, __entry->hw_job_count, __entry->client_id) ); -DEFINE_EVENT(drm_sched_job, drm_sched_job, +DEFINE_EVENT(drm_sched_job, drm_sched_job_queue, TP_PROTO(struct drm_sched_job *sched_job, struct drm_sched_entity *entity), TP_ARGS(sched_job, entity) ); -DEFINE_EVENT(drm_sched_job, drm_run_job, +DEFINE_EVENT(drm_sched_job, drm_sched_job_run, TP_PROTO(struct drm_sched_job *sched_job, struct drm_sched_entity *entity), TP_ARGS(sched_job, entity) ); -TRACE_EVENT(drm_sched_process_job, +TRACE_EVENT(drm_sched_job_done, TP_PROTO(struct drm_sched_fence *fence), TP_ARGS(fence), TP_STRUCT__entry( - __field(struct dma_fence *, fence) + __field(u64, fence_context) + __field(u64, fence_seqno) ), TP_fast_assign( - __entry->fence = &fence->finished; + __entry->fence_context = fence->finished.context; + __entry->fence_seqno = fence->finished.seqno; ), - TP_printk("fence=%p signaled", __entry->fence) + TP_printk("fence=%llu:%llu signaled", + __entry->fence_context, __entry->fence_seqno) ); -TRACE_EVENT(drm_sched_job_wait_dep, +TRACE_EVENT(drm_sched_job_add_dep, + TP_PROTO(struct drm_sched_job *sched_job, struct dma_fence *fence), + TP_ARGS(sched_job, fence), + TP_STRUCT__entry( + __field(u64, fence_context) + __field(u64, fence_seqno) + __field(u64, ctx) + __field(u64, seqno) + ), + + TP_fast_assign( + __entry->fence_context = sched_job->s_fence->finished.context; + __entry->fence_seqno = sched_job->s_fence->finished.seqno; + __entry->ctx = fence->context; + __entry->seqno = fence->seqno; + ), + TP_printk("fence=%llu:%llu depends on fence=%llu:%llu", + __entry->fence_context, __entry->fence_seqno, + __entry->ctx, __entry->seqno) +); + +TRACE_EVENT(drm_sched_job_unschedulable, TP_PROTO(struct drm_sched_job *sched_job, struct dma_fence *fence), TP_ARGS(sched_job, fence), TP_STRUCT__entry( - __string(name, sched_job->sched->name) - __field(uint64_t, id) - __field(struct dma_fence *, fence) - __field(uint64_t, ctx) - __field(unsigned, seqno) + __field(u64, fence_context) + __field(u64, fence_seqno) + __field(u64, ctx) + __field(u64, seqno) ), TP_fast_assign( - __assign_str(name); - __entry->id = sched_job->id; - __entry->fence = fence; + __entry->fence_context = sched_job->s_fence->finished.context; + __entry->fence_seqno = sched_job->s_fence->finished.seqno; __entry->ctx = fence->context; __entry->seqno = fence->seqno; ), - TP_printk("job ring=%s, id=%llu, depends fence=%p, context=%llu, seq=%u", - __get_str(name), __entry->id, - __entry->fence, __entry->ctx, - __entry->seqno) + TP_printk("fence=%llu:%llu depends on unsignalled fence=%llu:%llu", + __entry->fence_context, __entry->fence_seqno, + __entry->ctx, __entry->seqno) ); #endif /* _GPU_SCHED_TRACE_H_ */ diff --git a/drivers/gpu/drm/scheduler/sched_entity.c b/drivers/gpu/drm/scheduler/sched_entity.c index e671aa241720..86214b6e02d7 100644 --- a/drivers/gpu/drm/scheduler/sched_entity.c +++ b/drivers/gpu/drm/scheduler/sched_entity.c @@ -21,7 +21,6 @@ * */ -#include <linux/kthread.h> #include <linux/slab.h> #include <linux/completion.h> @@ -478,10 +477,10 @@ struct drm_sched_job *drm_sched_entity_pop_job(struct drm_sched_entity *entity) while ((entity->dependency = drm_sched_job_dependency(sched_job, entity))) { - trace_drm_sched_job_wait_dep(sched_job, entity->dependency); - - if (drm_sched_entity_add_dependency_cb(entity)) + if (drm_sched_entity_add_dependency_cb(entity)) { + trace_drm_sched_job_unschedulable(sched_job, entity->dependency); return NULL; + } } /* skip jobs from entity that marked guilty */ @@ -546,10 +545,10 @@ void drm_sched_entity_select_rq(struct drm_sched_entity *entity) return; /* - * Only when the queue is empty are we guaranteed that the scheduler - * thread cannot change ->last_scheduled. To enforce ordering we need - * a read barrier here. See drm_sched_entity_pop_job() for the other - * side. + * Only when the queue is empty are we guaranteed that + * drm_sched_run_job_work() cannot change entity->last_scheduled. To + * enforce ordering we need a read barrier here. See + * drm_sched_entity_pop_job() for the other side. */ smp_rmb(); @@ -587,7 +586,15 @@ void drm_sched_entity_push_job(struct drm_sched_job *sched_job) bool first; ktime_t submit_ts; - trace_drm_sched_job(sched_job, entity); + trace_drm_sched_job_queue(sched_job, entity); + + if (trace_drm_sched_job_add_dep_enabled()) { + struct dma_fence *entry; + unsigned long index; + + xa_for_each(&sched_job->dependencies, index, entry) + trace_drm_sched_job_add_dep(sched_job, entry); + } atomic_inc(entity->rq->sched->score); WRITE_ONCE(entity->last_user, current->group_leader); diff --git a/drivers/gpu/drm/scheduler/sched_fence.c b/drivers/gpu/drm/scheduler/sched_fence.c index e971528504a5..725de257d60d 100644 --- a/drivers/gpu/drm/scheduler/sched_fence.c +++ b/drivers/gpu/drm/scheduler/sched_fence.c @@ -21,7 +21,6 @@ * */ -#include <linux/kthread.h> #include <linux/module.h> #include <linux/sched.h> #include <linux/slab.h> @@ -206,7 +205,8 @@ struct drm_sched_fence *to_drm_sched_fence(struct dma_fence *f) EXPORT_SYMBOL(to_drm_sched_fence); struct drm_sched_fence *drm_sched_fence_alloc(struct drm_sched_entity *entity, - void *owner) + void *owner, + u64 drm_client_id) { struct drm_sched_fence *fence = NULL; @@ -215,6 +215,7 @@ struct drm_sched_fence *drm_sched_fence_alloc(struct drm_sched_entity *entity, return NULL; fence->owner = owner; + fence->drm_client_id = drm_client_id; spin_lock_init(&fence->lock); return fence; diff --git a/drivers/gpu/drm/scheduler/sched_internal.h b/drivers/gpu/drm/scheduler/sched_internal.h index 599cf6e1bb74..7ea5a6736f98 100644 --- a/drivers/gpu/drm/scheduler/sched_internal.h +++ b/drivers/gpu/drm/scheduler/sched_internal.h @@ -24,7 +24,7 @@ void drm_sched_entity_select_rq(struct drm_sched_entity *entity); struct drm_sched_job *drm_sched_entity_pop_job(struct drm_sched_entity *entity); struct drm_sched_fence *drm_sched_fence_alloc(struct drm_sched_entity *s_entity, - void *owner); + void *owner, u64 drm_client_id); void drm_sched_fence_init(struct drm_sched_fence *fence, struct drm_sched_entity *entity); void drm_sched_fence_free(struct drm_sched_fence *fence); diff --git a/drivers/gpu/drm/scheduler/sched_main.c b/drivers/gpu/drm/scheduler/sched_main.c index 829579c41c6b..d20726d7adf0 100644 --- a/drivers/gpu/drm/scheduler/sched_main.c +++ b/drivers/gpu/drm/scheduler/sched_main.c @@ -391,7 +391,7 @@ static void drm_sched_run_free_queue(struct drm_gpu_scheduler *sched) * drm_sched_job_done - complete a job * @s_job: pointer to the job which is done * - * Finish the job's fence and wake up the worker thread. + * Finish the job's fence and resubmit the work items. */ static void drm_sched_job_done(struct drm_sched_job *s_job, int result) { @@ -401,7 +401,7 @@ static void drm_sched_job_done(struct drm_sched_job *s_job, int result) atomic_sub(s_job->credits, &sched->credit_count); atomic_dec(sched->score); - trace_drm_sched_process_job(s_fence); + trace_drm_sched_job_done(s_fence); dma_fence_get(&s_fence->finished); drm_sched_fence_finished(s_fence, result); @@ -551,9 +551,10 @@ static void drm_sched_job_timedout(struct work_struct *work) if (job) { /* - * Remove the bad job so it cannot be freed by concurrent - * drm_sched_cleanup_jobs. It will be reinserted back after sched->thread - * is parked at which point it's safe. + * Remove the bad job so it cannot be freed by a concurrent + * &struct drm_sched_backend_ops.free_job. It will be + * reinserted after the scheduler's work items have been + * cancelled, at which point it's safe. */ list_del_init(&job->list); spin_unlock(&sched->job_list_lock); @@ -599,10 +600,10 @@ void drm_sched_stop(struct drm_gpu_scheduler *sched, struct drm_sched_job *bad) /* * Reinsert back the bad job here - now it's safe as - * drm_sched_get_finished_job cannot race against us and release the + * drm_sched_get_finished_job() cannot race against us and release the * bad job at this point - we parked (waited for) any in progress - * (earlier) cleanups and drm_sched_get_finished_job will not be called - * now until the scheduler thread is unparked. + * (earlier) cleanups and drm_sched_get_finished_job() will not be + * called now until the scheduler's work items are submitted again. */ if (bad && bad->sched == sched) /* @@ -615,7 +616,8 @@ void drm_sched_stop(struct drm_gpu_scheduler *sched, struct drm_sched_job *bad) * Iterate the job list from later to earlier one and either deactive * their HW callbacks or remove them from pending list if they already * signaled. - * This iteration is thread safe as sched thread is stopped. + * This iteration is thread safe as the scheduler's work items have been + * cancelled. */ list_for_each_entry_safe_reverse(s_job, tmp, &sched->pending_list, list) { @@ -680,9 +682,9 @@ void drm_sched_start(struct drm_gpu_scheduler *sched, int errno) struct drm_sched_job *s_job, *tmp; /* - * Locking the list is not required here as the sched thread is parked - * so no new jobs are being inserted or removed. Also concurrent - * GPU recovers can't run in parallel. + * Locking the list is not required here as the scheduler's work items + * are currently not running, so no new jobs are being inserted or + * removed. Also concurrent GPU recovers can't run in parallel. */ list_for_each_entry_safe(s_job, tmp, &sched->pending_list, list) { struct dma_fence *fence = s_job->s_fence->parent; @@ -764,6 +766,8 @@ EXPORT_SYMBOL(drm_sched_resubmit_jobs); * @credits: the number of credits this job contributes to the schedulers * credit limit * @owner: job owner for debugging + * @drm_client_id: &struct drm_file.client_id of the owner (used by trace + * events) * * Refer to drm_sched_entity_push_job() documentation * for locking considerations. @@ -784,7 +788,8 @@ EXPORT_SYMBOL(drm_sched_resubmit_jobs); */ int drm_sched_job_init(struct drm_sched_job *job, struct drm_sched_entity *entity, - u32 credits, void *owner) + u32 credits, void *owner, + uint64_t drm_client_id) { if (!entity->rq) { /* This will most likely be followed by missing frames @@ -810,7 +815,7 @@ int drm_sched_job_init(struct drm_sched_job *job, job->entity = entity; job->credits = credits; - job->s_fence = drm_sched_fence_alloc(entity, owner); + job->s_fence = drm_sched_fence_alloc(entity, owner, drm_client_id); if (!job->s_fence) return -ENOMEM; @@ -850,7 +855,6 @@ void drm_sched_job_arm(struct drm_sched_job *job) job->sched = sched; job->s_priority = entity->priority; - job->id = atomic64_inc_return(&sched->job_id_count); drm_sched_fence_init(job->s_fence, job->entity); } @@ -1229,7 +1233,7 @@ static void drm_sched_run_job_work(struct work_struct *w) atomic_add(sched_job->credits, &sched->credit_count); drm_sched_job_begin(sched_job); - trace_drm_run_job(sched_job, entity); + trace_drm_sched_job_run(sched_job, entity); /* * The run_job() callback must by definition return a fence whose * refcount has been incremented for the scheduler already. diff --git a/drivers/gpu/drm/scheduler/tests/mock_scheduler.c b/drivers/gpu/drm/scheduler/tests/mock_scheduler.c index f999c8859cf7..7f947ab9d322 100644 --- a/drivers/gpu/drm/scheduler/tests/mock_scheduler.c +++ b/drivers/gpu/drm/scheduler/tests/mock_scheduler.c @@ -64,7 +64,7 @@ static void drm_mock_sched_job_complete(struct drm_mock_sched_job *job) job->flags |= DRM_MOCK_SCHED_JOB_DONE; list_move_tail(&job->link, &sched->done_list); - dma_fence_signal(&job->hw_fence); + dma_fence_signal_locked(&job->hw_fence); complete(&job->done); } @@ -117,13 +117,13 @@ drm_mock_sched_job_new(struct kunit *test, ret = drm_sched_job_init(&job->base, &entity->base, 1, - NULL); + NULL, + 1); KUNIT_ASSERT_EQ(test, ret, 0); job->test = test; init_completion(&job->done); - spin_lock_init(&job->lock); INIT_LIST_HEAD(&job->link); hrtimer_setup(&job->timer, drm_mock_sched_job_signal_timer, CLOCK_MONOTONIC, HRTIMER_MODE_ABS); @@ -169,7 +169,7 @@ static struct dma_fence *mock_sched_run_job(struct drm_sched_job *sched_job) dma_fence_init(&job->hw_fence, &drm_mock_sched_hw_fence_ops, - &job->lock, + &sched->lock, sched->hw_timeline.context, atomic_inc_return(&sched->hw_timeline.next_seqno)); diff --git a/drivers/gpu/drm/scheduler/tests/sched_tests.h b/drivers/gpu/drm/scheduler/tests/sched_tests.h index 27caf8285fb7..fbba38137f0c 100644 --- a/drivers/gpu/drm/scheduler/tests/sched_tests.h +++ b/drivers/gpu/drm/scheduler/tests/sched_tests.h @@ -106,7 +106,6 @@ struct drm_mock_sched_job { unsigned int duration_us; ktime_t finish_at; - spinlock_t lock; struct dma_fence hw_fence; struct kunit *test; diff --git a/drivers/gpu/drm/sitronix/Kconfig b/drivers/gpu/drm/sitronix/Kconfig index 741d1bb4b83f..6de7d92d9b74 100644 --- a/drivers/gpu/drm/sitronix/Kconfig +++ b/drivers/gpu/drm/sitronix/Kconfig @@ -11,10 +11,6 @@ config DRM_ST7571_I2C if M is selected the module will be called st7571-i2c. -config TINYDRM_ST7586 - tristate - default n - config DRM_ST7586 tristate "DRM support for Sitronix ST7586 display panels" depends on DRM && SPI @@ -22,17 +18,12 @@ config DRM_ST7586 select DRM_KMS_HELPER select DRM_GEM_DMA_HELPER select DRM_MIPI_DBI - default TINYDRM_ST7586 help DRM driver for the following Sitronix ST7586 panels: * LEGO MINDSTORMS EV3 If M is selected the module will be called st7586. -config TINYDRM_ST7735R - tristate - default n - config DRM_ST7735R tristate "DRM support for Sitronix ST7715R/ST7735R display panels" depends on DRM && SPI @@ -41,7 +32,6 @@ config DRM_ST7735R select DRM_GEM_DMA_HELPER select DRM_MIPI_DBI select BACKLIGHT_CLASS_DEVICE - default TINYDRM_ST7735R help DRM driver for Sitronix ST7715R/ST7735R with one of the following LCDs: diff --git a/drivers/gpu/drm/sti/sti_dvo.c b/drivers/gpu/drm/sti/sti_dvo.c index 74a1eef4674e..7484d3c3f4ed 100644 --- a/drivers/gpu/drm/sti/sti_dvo.c +++ b/drivers/gpu/drm/sti/sti_dvo.c @@ -97,7 +97,7 @@ struct sti_dvo { struct dvo_config *config; bool enabled; struct drm_encoder *encoder; - struct drm_bridge *bridge; + struct drm_bridge bridge; }; struct sti_dvo_connector { @@ -439,7 +439,6 @@ static int sti_dvo_bind(struct device *dev, struct device *master, void *data) struct drm_encoder *encoder; struct sti_dvo_connector *connector; struct drm_connector *drm_connector; - struct drm_bridge *bridge; int err; /* Set the drm device handle */ @@ -455,20 +454,14 @@ static int sti_dvo_bind(struct device *dev, struct device *master, void *data) connector->dvo = dvo; - bridge = devm_kzalloc(dev, sizeof(*bridge), GFP_KERNEL); - if (!bridge) - return -ENOMEM; - - bridge->driver_private = dvo; - bridge->funcs = &sti_dvo_bridge_funcs; - bridge->of_node = dvo->dev.of_node; - drm_bridge_add(bridge); + dvo->bridge.driver_private = dvo; + dvo->bridge.of_node = dvo->dev.of_node; + drm_bridge_add(&dvo->bridge); - err = drm_bridge_attach(encoder, bridge, NULL, 0); + err = drm_bridge_attach(encoder, &dvo->bridge, NULL, 0); if (err) return err; - dvo->bridge = bridge; connector->encoder = encoder; dvo->encoder = encoder; @@ -490,7 +483,7 @@ static int sti_dvo_bind(struct device *dev, struct device *master, void *data) return 0; err_sysfs: - drm_bridge_remove(bridge); + drm_bridge_remove(&dvo->bridge); return -EINVAL; } @@ -499,7 +492,7 @@ static void sti_dvo_unbind(struct device *dev, { struct sti_dvo *dvo = dev_get_drvdata(dev); - drm_bridge_remove(dvo->bridge); + drm_bridge_remove(&dvo->bridge); } static const struct component_ops sti_dvo_ops = { @@ -515,10 +508,10 @@ static int sti_dvo_probe(struct platform_device *pdev) DRM_INFO("%s\n", __func__); - dvo = devm_kzalloc(dev, sizeof(*dvo), GFP_KERNEL); - if (!dvo) { - DRM_ERROR("Failed to allocate memory for DVO\n"); - return -ENOMEM; + dvo = devm_drm_bridge_alloc(dev, struct sti_dvo, bridge, &sti_dvo_bridge_funcs); + if (IS_ERR(dvo)) { + DRM_ERROR("Failed to allocate DVO\n"); + return PTR_ERR(dvo); } dvo->dev = pdev->dev; diff --git a/drivers/gpu/drm/stm/lvds.c b/drivers/gpu/drm/stm/lvds.c index a3ae9a93ce66..07788e8d3d83 100644 --- a/drivers/gpu/drm/stm/lvds.c +++ b/drivers/gpu/drm/stm/lvds.c @@ -1049,9 +1049,9 @@ static int lvds_probe(struct platform_device *pdev) dev_dbg(dev, "Probing LVDS driver...\n"); - lvds = devm_kzalloc(dev, sizeof(*lvds), GFP_KERNEL); - if (!lvds) - return -ENOMEM; + lvds = devm_drm_bridge_alloc(dev, struct stm_lvds, lvds_bridge, &lvds_bridge_funcs); + if (IS_ERR(lvds)) + return PTR_ERR(lvds); lvds->dev = dev; @@ -1164,7 +1164,6 @@ static int lvds_probe(struct platform_device *pdev) goto err_lvds_probe; } - lvds->lvds_bridge.funcs = &lvds_bridge_funcs; lvds->lvds_bridge.of_node = dev->of_node; lvds->hw_version = lvds_read(lvds, LVDS_VERR); diff --git a/drivers/gpu/drm/sun4i/sun8i_csc.c b/drivers/gpu/drm/sun4i/sun8i_csc.c index 58480d8e4f70..c100d29b1a89 100644 --- a/drivers/gpu/drm/sun4i/sun8i_csc.c +++ b/drivers/gpu/drm/sun4i/sun8i_csc.c @@ -212,7 +212,7 @@ void sun8i_csc_set_ccsc_coefficients(struct sun8i_mixer *mixer, int layer, { u32 base; - if (mixer->cfg->is_de3) { + if (mixer->cfg->de_type == SUN8I_MIXER_DE3) { sun8i_de3_ccsc_set_coefficients(mixer->engine.regs, layer, mode, encoding, range); return; @@ -228,7 +228,7 @@ void sun8i_csc_enable_ccsc(struct sun8i_mixer *mixer, int layer, bool enable) { u32 base; - if (mixer->cfg->is_de3) { + if (mixer->cfg->de_type == SUN8I_MIXER_DE3) { sun8i_de3_ccsc_enable(mixer->engine.regs, layer, enable); return; } diff --git a/drivers/gpu/drm/sun4i/sun8i_mixer.c b/drivers/gpu/drm/sun4i/sun8i_mixer.c index 8b41d33baa30..31a8409b98f4 100644 --- a/drivers/gpu/drm/sun4i/sun8i_mixer.c +++ b/drivers/gpu/drm/sun4i/sun8i_mixer.c @@ -274,6 +274,7 @@ static void sun8i_mixer_commit(struct sunxi_engine *engine, { struct sun8i_mixer *mixer = engine_to_sun8i_mixer(engine); u32 bld_base = sun8i_blender_base(mixer); + struct regmap *bld_regs = sun8i_blender_regmap(mixer); struct drm_plane_state *plane_state; struct drm_plane *plane; u32 route = 0, pipe_en = 0; @@ -313,12 +314,13 @@ static void sun8i_mixer_commit(struct sunxi_engine *engine, pipe_en |= SUN8I_MIXER_BLEND_PIPE_CTL_EN(zpos); } - regmap_write(mixer->engine.regs, SUN8I_MIXER_BLEND_ROUTE(bld_base), route); - regmap_write(mixer->engine.regs, SUN8I_MIXER_BLEND_PIPE_CTL(bld_base), + regmap_write(bld_regs, SUN8I_MIXER_BLEND_ROUTE(bld_base), route); + regmap_write(bld_regs, SUN8I_MIXER_BLEND_PIPE_CTL(bld_base), pipe_en | SUN8I_MIXER_BLEND_PIPE_CTL_FC_EN(0)); - regmap_write(engine->regs, SUN8I_MIXER_GLOBAL_DBUFF, - SUN8I_MIXER_GLOBAL_DBUFF_ENABLE); + if (mixer->cfg->de_type != SUN8I_MIXER_DE33) + regmap_write(engine->regs, SUN8I_MIXER_GLOBAL_DBUFF, + SUN8I_MIXER_GLOBAL_DBUFF_ENABLE); } static struct drm_plane **sun8i_layers_init(struct drm_device *drm, @@ -367,25 +369,31 @@ static void sun8i_mixer_mode_set(struct sunxi_engine *engine, const struct drm_display_mode *mode) { struct sun8i_mixer *mixer = engine_to_sun8i_mixer(engine); + struct regmap *bld_regs; u32 bld_base, size, val; bool interlaced; bld_base = sun8i_blender_base(mixer); + bld_regs = sun8i_blender_regmap(mixer); interlaced = !!(mode->flags & DRM_MODE_FLAG_INTERLACE); size = SUN8I_MIXER_SIZE(mode->hdisplay, mode->vdisplay); DRM_DEBUG_DRIVER("Updating global size W: %u H: %u\n", mode->hdisplay, mode->vdisplay); - regmap_write(engine->regs, SUN8I_MIXER_GLOBAL_SIZE, size); - regmap_write(engine->regs, SUN8I_MIXER_BLEND_OUTSIZE(bld_base), size); + if (mixer->cfg->de_type == SUN8I_MIXER_DE33) + regmap_write(mixer->top_regs, SUN50I_MIXER_GLOBAL_SIZE, size); + else + regmap_write(mixer->engine.regs, SUN8I_MIXER_GLOBAL_SIZE, size); + + regmap_write(bld_regs, SUN8I_MIXER_BLEND_OUTSIZE(bld_base), size); if (interlaced) val = SUN8I_MIXER_BLEND_OUTCTL_INTERLACED; else val = 0; - regmap_update_bits(engine->regs, SUN8I_MIXER_BLEND_OUTCTL(bld_base), + regmap_update_bits(bld_regs, SUN8I_MIXER_BLEND_OUTCTL(bld_base), SUN8I_MIXER_BLEND_OUTCTL_INTERLACED, val); DRM_DEBUG_DRIVER("Switching display mixer interlaced mode %s\n", @@ -399,12 +407,29 @@ static const struct sunxi_engine_ops sun8i_engine_ops = { }; static const struct regmap_config sun8i_mixer_regmap_config = { + .name = "layers", .reg_bits = 32, .val_bits = 32, .reg_stride = 4, .max_register = 0xffffc, /* guessed */ }; +static const struct regmap_config sun8i_top_regmap_config = { + .name = "top", + .reg_bits = 32, + .val_bits = 32, + .reg_stride = 4, + .max_register = 0x3c, +}; + +static const struct regmap_config sun8i_disp_regmap_config = { + .name = "display", + .reg_bits = 32, + .val_bits = 32, + .reg_stride = 4, + .max_register = 0x20000, +}; + static int sun8i_mixer_of_get_id(struct device_node *node) { struct device_node *ep, *remote; @@ -425,6 +450,50 @@ static int sun8i_mixer_of_get_id(struct device_node *node) return of_ep.id; } +static void sun8i_mixer_init(struct sun8i_mixer *mixer) +{ + struct regmap *top_regs, *disp_regs; + unsigned int base = sun8i_blender_base(mixer); + int plane_cnt, i; + + if (mixer->cfg->de_type == SUN8I_MIXER_DE33) { + top_regs = mixer->top_regs; + disp_regs = mixer->disp_regs; + } else { + top_regs = mixer->engine.regs; + disp_regs = mixer->engine.regs; + } + + /* Enable the mixer */ + regmap_write(top_regs, SUN8I_MIXER_GLOBAL_CTL, + SUN8I_MIXER_GLOBAL_CTL_RT_EN); + + if (mixer->cfg->de_type == SUN8I_MIXER_DE33) + regmap_write(top_regs, SUN50I_MIXER_GLOBAL_CLK, 1); + + /* Set background color to black */ + regmap_write(disp_regs, SUN8I_MIXER_BLEND_BKCOLOR(base), + SUN8I_MIXER_BLEND_COLOR_BLACK); + + /* + * Set fill color of bottom plane to black. Generally not needed + * except when VI plane is at bottom (zpos = 0) and enabled. + */ + regmap_write(disp_regs, SUN8I_MIXER_BLEND_PIPE_CTL(base), + SUN8I_MIXER_BLEND_PIPE_CTL_FC_EN(0)); + regmap_write(disp_regs, SUN8I_MIXER_BLEND_ATTR_FCOLOR(base, 0), + SUN8I_MIXER_BLEND_COLOR_BLACK); + + plane_cnt = mixer->cfg->vi_num + mixer->cfg->ui_num; + for (i = 0; i < plane_cnt; i++) + regmap_write(disp_regs, + SUN8I_MIXER_BLEND_MODE(base, i), + SUN8I_MIXER_BLEND_MODE_DEF); + + regmap_update_bits(disp_regs, SUN8I_MIXER_BLEND_PIPE_CTL(base), + SUN8I_MIXER_BLEND_PIPE_CTL_EN_MSK, 0); +} + static int sun8i_mixer_bind(struct device *dev, struct device *master, void *data) { @@ -433,8 +502,6 @@ static int sun8i_mixer_bind(struct device *dev, struct device *master, struct sun4i_drv *drv = drm->dev_private; struct sun8i_mixer *mixer; void __iomem *regs; - unsigned int base; - int plane_cnt; int i, ret; /* @@ -495,6 +562,30 @@ static int sun8i_mixer_bind(struct device *dev, struct device *master, return PTR_ERR(mixer->engine.regs); } + if (mixer->cfg->de_type == SUN8I_MIXER_DE33) { + regs = devm_platform_ioremap_resource_byname(pdev, "top"); + if (IS_ERR(regs)) + return PTR_ERR(regs); + + mixer->top_regs = devm_regmap_init_mmio(dev, regs, + &sun8i_top_regmap_config); + if (IS_ERR(mixer->top_regs)) { + dev_err(dev, "Couldn't create the top regmap\n"); + return PTR_ERR(mixer->top_regs); + } + + regs = devm_platform_ioremap_resource_byname(pdev, "display"); + if (IS_ERR(regs)) + return PTR_ERR(regs); + + mixer->disp_regs = devm_regmap_init_mmio(dev, regs, + &sun8i_disp_regmap_config); + if (IS_ERR(mixer->disp_regs)) { + dev_err(dev, "Couldn't create the disp regmap\n"); + return PTR_ERR(mixer->disp_regs); + } + } + mixer->reset = devm_reset_control_get(dev, NULL); if (IS_ERR(mixer->reset)) { dev_err(dev, "Couldn't get our reset line\n"); @@ -534,10 +625,8 @@ static int sun8i_mixer_bind(struct device *dev, struct device *master, list_add_tail(&mixer->engine.list, &drv->engine_list); - base = sun8i_blender_base(mixer); - /* Reset registers and disable unused sub-engines */ - if (mixer->cfg->is_de3) { + if (mixer->cfg->de_type == SUN8I_MIXER_DE3) { for (i = 0; i < DE3_MIXER_UNIT_SIZE; i += 4) regmap_write(mixer->engine.regs, i, 0); @@ -551,7 +640,7 @@ static int sun8i_mixer_bind(struct device *dev, struct device *master, regmap_write(mixer->engine.regs, SUN50I_MIXER_FMT_EN, 0); regmap_write(mixer->engine.regs, SUN50I_MIXER_CDC0_EN, 0); regmap_write(mixer->engine.regs, SUN50I_MIXER_CDC1_EN, 0); - } else { + } else if (mixer->cfg->de_type == SUN8I_MIXER_DE2) { for (i = 0; i < DE2_MIXER_UNIT_SIZE; i += 4) regmap_write(mixer->engine.regs, i, 0); @@ -564,31 +653,7 @@ static int sun8i_mixer_bind(struct device *dev, struct device *master, regmap_write(mixer->engine.regs, SUN8I_MIXER_DCSC_EN, 0); } - /* Enable the mixer */ - regmap_write(mixer->engine.regs, SUN8I_MIXER_GLOBAL_CTL, - SUN8I_MIXER_GLOBAL_CTL_RT_EN); - - /* Set background color to black */ - regmap_write(mixer->engine.regs, SUN8I_MIXER_BLEND_BKCOLOR(base), - SUN8I_MIXER_BLEND_COLOR_BLACK); - - /* - * Set fill color of bottom plane to black. Generally not needed - * except when VI plane is at bottom (zpos = 0) and enabled. - */ - regmap_write(mixer->engine.regs, SUN8I_MIXER_BLEND_PIPE_CTL(base), - SUN8I_MIXER_BLEND_PIPE_CTL_FC_EN(0)); - regmap_write(mixer->engine.regs, SUN8I_MIXER_BLEND_ATTR_FCOLOR(base, 0), - SUN8I_MIXER_BLEND_COLOR_BLACK); - - plane_cnt = mixer->cfg->vi_num + mixer->cfg->ui_num; - for (i = 0; i < plane_cnt; i++) - regmap_write(mixer->engine.regs, - SUN8I_MIXER_BLEND_MODE(base, i), - SUN8I_MIXER_BLEND_MODE_DEF); - - regmap_update_bits(mixer->engine.regs, SUN8I_MIXER_BLEND_PIPE_CTL(base), - SUN8I_MIXER_BLEND_PIPE_CTL_EN_MSK, 0); + sun8i_mixer_init(mixer); return 0; @@ -628,6 +693,7 @@ static void sun8i_mixer_remove(struct platform_device *pdev) static const struct sun8i_mixer_cfg sun8i_a83t_mixer0_cfg = { .ccsc = CCSC_MIXER0_LAYOUT, + .de_type = SUN8I_MIXER_DE2, .scaler_mask = 0xf, .scanline_yuv = 2048, .ui_num = 3, @@ -636,6 +702,7 @@ static const struct sun8i_mixer_cfg sun8i_a83t_mixer0_cfg = { static const struct sun8i_mixer_cfg sun8i_a83t_mixer1_cfg = { .ccsc = CCSC_MIXER1_LAYOUT, + .de_type = SUN8I_MIXER_DE2, .scaler_mask = 0x3, .scanline_yuv = 2048, .ui_num = 1, @@ -644,6 +711,7 @@ static const struct sun8i_mixer_cfg sun8i_a83t_mixer1_cfg = { static const struct sun8i_mixer_cfg sun8i_h3_mixer0_cfg = { .ccsc = CCSC_MIXER0_LAYOUT, + .de_type = SUN8I_MIXER_DE2, .mod_rate = 432000000, .scaler_mask = 0xf, .scanline_yuv = 2048, @@ -653,6 +721,7 @@ static const struct sun8i_mixer_cfg sun8i_h3_mixer0_cfg = { static const struct sun8i_mixer_cfg sun8i_r40_mixer0_cfg = { .ccsc = CCSC_MIXER0_LAYOUT, + .de_type = SUN8I_MIXER_DE2, .mod_rate = 297000000, .scaler_mask = 0xf, .scanline_yuv = 2048, @@ -662,6 +731,7 @@ static const struct sun8i_mixer_cfg sun8i_r40_mixer0_cfg = { static const struct sun8i_mixer_cfg sun8i_r40_mixer1_cfg = { .ccsc = CCSC_MIXER1_LAYOUT, + .de_type = SUN8I_MIXER_DE2, .mod_rate = 297000000, .scaler_mask = 0x3, .scanline_yuv = 2048, @@ -670,6 +740,7 @@ static const struct sun8i_mixer_cfg sun8i_r40_mixer1_cfg = { }; static const struct sun8i_mixer_cfg sun8i_v3s_mixer_cfg = { + .de_type = SUN8I_MIXER_DE2, .vi_num = 2, .ui_num = 1, .scaler_mask = 0x3, @@ -680,6 +751,7 @@ static const struct sun8i_mixer_cfg sun8i_v3s_mixer_cfg = { static const struct sun8i_mixer_cfg sun20i_d1_mixer0_cfg = { .ccsc = CCSC_D1_MIXER0_LAYOUT, + .de_type = SUN8I_MIXER_DE2, .mod_rate = 297000000, .scaler_mask = 0x3, .scanline_yuv = 2048, @@ -689,6 +761,7 @@ static const struct sun8i_mixer_cfg sun20i_d1_mixer0_cfg = { static const struct sun8i_mixer_cfg sun20i_d1_mixer1_cfg = { .ccsc = CCSC_MIXER1_LAYOUT, + .de_type = SUN8I_MIXER_DE2, .mod_rate = 297000000, .scaler_mask = 0x1, .scanline_yuv = 1024, @@ -698,6 +771,7 @@ static const struct sun8i_mixer_cfg sun20i_d1_mixer1_cfg = { static const struct sun8i_mixer_cfg sun50i_a64_mixer0_cfg = { .ccsc = CCSC_MIXER0_LAYOUT, + .de_type = SUN8I_MIXER_DE2, .mod_rate = 297000000, .scaler_mask = 0xf, .scanline_yuv = 4096, @@ -707,6 +781,7 @@ static const struct sun8i_mixer_cfg sun50i_a64_mixer0_cfg = { static const struct sun8i_mixer_cfg sun50i_a64_mixer1_cfg = { .ccsc = CCSC_MIXER1_LAYOUT, + .de_type = SUN8I_MIXER_DE2, .mod_rate = 297000000, .scaler_mask = 0x3, .scanline_yuv = 2048, @@ -716,7 +791,7 @@ static const struct sun8i_mixer_cfg sun50i_a64_mixer1_cfg = { static const struct sun8i_mixer_cfg sun50i_h6_mixer0_cfg = { .ccsc = CCSC_MIXER0_LAYOUT, - .is_de3 = true, + .de_type = SUN8I_MIXER_DE3, .mod_rate = 600000000, .scaler_mask = 0xf, .scanline_yuv = 4096, @@ -724,6 +799,17 @@ static const struct sun8i_mixer_cfg sun50i_h6_mixer0_cfg = { .vi_num = 1, }; +static const struct sun8i_mixer_cfg sun50i_h616_mixer0_cfg = { + .ccsc = CCSC_MIXER0_LAYOUT, + .de_type = SUN8I_MIXER_DE33, + .mod_rate = 600000000, + .scaler_mask = 0xf, + .scanline_yuv = 4096, + .ui_num = 3, + .vi_num = 1, + .map = {0, 6, 7, 8}, +}; + static const struct of_device_id sun8i_mixer_of_table[] = { { .compatible = "allwinner,sun8i-a83t-de2-mixer-0", @@ -769,6 +855,10 @@ static const struct of_device_id sun8i_mixer_of_table[] = { .compatible = "allwinner,sun50i-h6-de3-mixer-0", .data = &sun50i_h6_mixer0_cfg, }, + { + .compatible = "allwinner,sun50i-h616-de33-mixer-0", + .data = &sun50i_h616_mixer0_cfg, + }, { } }; MODULE_DEVICE_TABLE(of, sun8i_mixer_of_table); diff --git a/drivers/gpu/drm/sun4i/sun8i_mixer.h b/drivers/gpu/drm/sun4i/sun8i_mixer.h index d7898c9c9cc0..a1c1cbccc654 100644 --- a/drivers/gpu/drm/sun4i/sun8i_mixer.h +++ b/drivers/gpu/drm/sun4i/sun8i_mixer.h @@ -21,6 +21,9 @@ #define SUN8I_MIXER_GLOBAL_DBUFF 0x8 #define SUN8I_MIXER_GLOBAL_SIZE 0xc +#define SUN50I_MIXER_GLOBAL_SIZE 0x8 +#define SUN50I_MIXER_GLOBAL_CLK 0xc + #define SUN8I_MIXER_GLOBAL_CTL_RT_EN BIT(0) #define SUN8I_MIXER_GLOBAL_DBUFF_ENABLE BIT(0) @@ -151,6 +154,12 @@ enum { CCSC_D1_MIXER0_LAYOUT, }; +enum sun8i_mixer_type { + SUN8I_MIXER_DE2, + SUN8I_MIXER_DE3, + SUN8I_MIXER_DE33, +}; + /** * struct sun8i_mixer_cfg - mixer HW configuration * @vi_num: number of VI channels @@ -162,8 +171,9 @@ enum { * @ccsc: select set of CCSC base addresses from the enumeration above. * @mod_rate: module clock rate that needs to be set in order to have * a functional block. - * @is_de3: true, if this is next gen display engine 3.0, false otherwise. + * @de_type: sun8i_mixer_type enum representing the display engine generation. * @scaline_yuv: size of a scanline for VI scaler for YUV formats. + * @map: channel map for DE variants processing YUV separately (DE33) */ struct sun8i_mixer_cfg { int vi_num; @@ -171,8 +181,9 @@ struct sun8i_mixer_cfg { int scaler_mask; int ccsc; unsigned long mod_rate; - unsigned int is_de3 : 1; + unsigned int de_type; unsigned int scanline_yuv; + unsigned int map[6]; }; struct sun8i_mixer { @@ -184,6 +195,9 @@ struct sun8i_mixer { struct clk *bus_clk; struct clk *mod_clk; + + struct regmap *top_regs; + struct regmap *disp_regs; }; enum { @@ -214,13 +228,22 @@ engine_to_sun8i_mixer(struct sunxi_engine *engine) static inline u32 sun8i_blender_base(struct sun8i_mixer *mixer) { - return mixer->cfg->is_de3 ? DE3_BLD_BASE : DE2_BLD_BASE; + return mixer->cfg->de_type == SUN8I_MIXER_DE3 ? DE3_BLD_BASE : DE2_BLD_BASE; +} + +static inline struct regmap * +sun8i_blender_regmap(struct sun8i_mixer *mixer) +{ + return mixer->cfg->de_type == SUN8I_MIXER_DE33 ? + mixer->disp_regs : mixer->engine.regs; } static inline u32 sun8i_channel_base(struct sun8i_mixer *mixer, int channel) { - if (mixer->cfg->is_de3) + if (mixer->cfg->de_type == SUN8I_MIXER_DE33) + return mixer->cfg->map[channel] * 0x20000 + DE2_CH_SIZE; + else if (mixer->cfg->de_type == SUN8I_MIXER_DE3) return DE3_CH_BASE + channel * DE3_CH_SIZE; else return DE2_CH_BASE + channel * DE2_CH_SIZE; diff --git a/drivers/gpu/drm/sun4i/sun8i_ui_layer.c b/drivers/gpu/drm/sun4i/sun8i_ui_layer.c index b90e5edef4e8..f97be0040aab 100644 --- a/drivers/gpu/drm/sun4i/sun8i_ui_layer.c +++ b/drivers/gpu/drm/sun4i/sun8i_ui_layer.c @@ -23,6 +23,7 @@ #include "sun8i_mixer.h" #include "sun8i_ui_layer.h" #include "sun8i_ui_scaler.h" +#include "sun8i_vi_scaler.h" static void sun8i_ui_layer_update_alpha(struct sun8i_mixer *mixer, int channel, int overlay, struct drm_plane *plane) @@ -51,6 +52,7 @@ static int sun8i_ui_layer_update_coord(struct sun8i_mixer *mixer, int channel, { struct drm_plane_state *state = plane->state; u32 src_w, src_h, dst_w, dst_h; + struct regmap *bld_regs; u32 bld_base, ch_base; u32 outsize, insize; u32 hphase, vphase; @@ -59,6 +61,7 @@ static int sun8i_ui_layer_update_coord(struct sun8i_mixer *mixer, int channel, channel, overlay); bld_base = sun8i_blender_base(mixer); + bld_regs = sun8i_blender_regmap(mixer); ch_base = sun8i_channel_base(mixer, channel); src_w = drm_rect_width(&state->src) >> 16; @@ -91,22 +94,34 @@ static int sun8i_ui_layer_update_coord(struct sun8i_mixer *mixer, int channel, hscale = state->src_w / state->crtc_w; vscale = state->src_h / state->crtc_h; - sun8i_ui_scaler_setup(mixer, channel, src_w, src_h, dst_w, - dst_h, hscale, vscale, hphase, vphase); - sun8i_ui_scaler_enable(mixer, channel, true); + if (mixer->cfg->de_type == SUN8I_MIXER_DE33) { + sun8i_vi_scaler_setup(mixer, channel, src_w, src_h, + dst_w, dst_h, hscale, vscale, + hphase, vphase, + state->fb->format); + sun8i_vi_scaler_enable(mixer, channel, true); + } else { + sun8i_ui_scaler_setup(mixer, channel, src_w, src_h, + dst_w, dst_h, hscale, vscale, + hphase, vphase); + sun8i_ui_scaler_enable(mixer, channel, true); + } } else { DRM_DEBUG_DRIVER("HW scaling is not needed\n"); - sun8i_ui_scaler_enable(mixer, channel, false); + if (mixer->cfg->de_type == SUN8I_MIXER_DE33) + sun8i_vi_scaler_enable(mixer, channel, false); + else + sun8i_ui_scaler_enable(mixer, channel, false); } /* Set base coordinates */ DRM_DEBUG_DRIVER("Layer destination coordinates X: %d Y: %d\n", state->dst.x1, state->dst.y1); DRM_DEBUG_DRIVER("Layer destination size W: %d H: %d\n", dst_w, dst_h); - regmap_write(mixer->engine.regs, + regmap_write(bld_regs, SUN8I_MIXER_BLEND_ATTR_COORD(bld_base, zpos), SUN8I_MIXER_COORD(state->dst.x1, state->dst.y1)); - regmap_write(mixer->engine.regs, + regmap_write(bld_regs, SUN8I_MIXER_BLEND_ATTR_INSIZE(bld_base, zpos), outsize); diff --git a/drivers/gpu/drm/sun4i/sun8i_ui_scaler.c b/drivers/gpu/drm/sun4i/sun8i_ui_scaler.c index ae0806bccac7..8b7a58e27517 100644 --- a/drivers/gpu/drm/sun4i/sun8i_ui_scaler.c +++ b/drivers/gpu/drm/sun4i/sun8i_ui_scaler.c @@ -93,7 +93,7 @@ static u32 sun8i_ui_scaler_base(struct sun8i_mixer *mixer, int channel) { int vi_num = mixer->cfg->vi_num; - if (mixer->cfg->is_de3) + if (mixer->cfg->de_type == SUN8I_MIXER_DE3) return DE3_VI_SCALER_UNIT_BASE + DE3_VI_SCALER_UNIT_SIZE * vi_num + DE3_UI_SCALER_UNIT_SIZE * (channel - vi_num); diff --git a/drivers/gpu/drm/sun4i/sun8i_vi_layer.c b/drivers/gpu/drm/sun4i/sun8i_vi_layer.c index 9c09d9c08496..a09ee4097537 100644 --- a/drivers/gpu/drm/sun4i/sun8i_vi_layer.c +++ b/drivers/gpu/drm/sun4i/sun8i_vi_layer.c @@ -25,7 +25,7 @@ static void sun8i_vi_layer_update_alpha(struct sun8i_mixer *mixer, int channel, ch_base = sun8i_channel_base(mixer, channel); - if (mixer->cfg->is_de3) { + if (mixer->cfg->de_type >= SUN8I_MIXER_DE3) { mask = SUN50I_MIXER_CHAN_VI_LAYER_ATTR_ALPHA_MASK | SUN50I_MIXER_CHAN_VI_LAYER_ATTR_ALPHA_MODE_MASK; val = SUN50I_MIXER_CHAN_VI_LAYER_ATTR_ALPHA @@ -55,6 +55,7 @@ static int sun8i_vi_layer_update_coord(struct sun8i_mixer *mixer, int channel, struct drm_plane_state *state = plane->state; const struct drm_format_info *format = state->fb->format; u32 src_w, src_h, dst_w, dst_h; + struct regmap *bld_regs; u32 bld_base, ch_base; u32 outsize, insize; u32 hphase, vphase; @@ -66,6 +67,7 @@ static int sun8i_vi_layer_update_coord(struct sun8i_mixer *mixer, int channel, channel, overlay); bld_base = sun8i_blender_base(mixer); + bld_regs = sun8i_blender_regmap(mixer); ch_base = sun8i_channel_base(mixer, channel); src_w = drm_rect_width(&state->src) >> 16; @@ -183,10 +185,10 @@ static int sun8i_vi_layer_update_coord(struct sun8i_mixer *mixer, int channel, DRM_DEBUG_DRIVER("Layer destination coordinates X: %d Y: %d\n", state->dst.x1, state->dst.y1); DRM_DEBUG_DRIVER("Layer destination size W: %d H: %d\n", dst_w, dst_h); - regmap_write(mixer->engine.regs, + regmap_write(bld_regs, SUN8I_MIXER_BLEND_ATTR_COORD(bld_base, zpos), SUN8I_MIXER_COORD(state->dst.x1, state->dst.y1)); - regmap_write(mixer->engine.regs, + regmap_write(bld_regs, SUN8I_MIXER_BLEND_ATTR_INSIZE(bld_base, zpos), outsize); @@ -483,7 +485,7 @@ struct sun8i_layer *sun8i_vi_layer_init_one(struct drm_device *drm, if (!layer) return ERR_PTR(-ENOMEM); - if (mixer->cfg->is_de3) { + if (mixer->cfg->de_type >= SUN8I_MIXER_DE3) { formats = sun8i_vi_layer_de3_formats; format_count = ARRAY_SIZE(sun8i_vi_layer_de3_formats); } else { @@ -507,7 +509,7 @@ struct sun8i_layer *sun8i_vi_layer_init_one(struct drm_device *drm, plane_cnt = mixer->cfg->ui_num + mixer->cfg->vi_num; - if (mixer->cfg->vi_num == 1 || mixer->cfg->is_de3) { + if (mixer->cfg->vi_num == 1 || mixer->cfg->de_type >= SUN8I_MIXER_DE3) { ret = drm_plane_create_alpha_property(&layer->plane); if (ret) { dev_err(drm->dev, "Couldn't add alpha property\n"); @@ -524,7 +526,7 @@ struct sun8i_layer *sun8i_vi_layer_init_one(struct drm_device *drm, supported_encodings = BIT(DRM_COLOR_YCBCR_BT601) | BIT(DRM_COLOR_YCBCR_BT709); - if (mixer->cfg->is_de3) + if (mixer->cfg->de_type >= SUN8I_MIXER_DE3) supported_encodings |= BIT(DRM_COLOR_YCBCR_BT2020); supported_ranges = BIT(DRM_COLOR_YCBCR_LIMITED_RANGE) | diff --git a/drivers/gpu/drm/sun4i/sun8i_vi_scaler.c b/drivers/gpu/drm/sun4i/sun8i_vi_scaler.c index 7ba75011adf9..82df6244af88 100644 --- a/drivers/gpu/drm/sun4i/sun8i_vi_scaler.c +++ b/drivers/gpu/drm/sun4i/sun8i_vi_scaler.c @@ -835,7 +835,9 @@ static const u32 bicubic4coefftab32[480] = { static u32 sun8i_vi_scaler_base(struct sun8i_mixer *mixer, int channel) { - if (mixer->cfg->is_de3) + if (mixer->cfg->de_type == SUN8I_MIXER_DE33) + return sun8i_channel_base(mixer, channel) + 0x3000; + else if (mixer->cfg->de_type == SUN8I_MIXER_DE3) return DE3_VI_SCALER_UNIT_BASE + DE3_VI_SCALER_UNIT_SIZE * channel; else @@ -956,7 +958,7 @@ void sun8i_vi_scaler_setup(struct sun8i_mixer *mixer, int layer, cvphase = vphase; } - if (mixer->cfg->is_de3) { + if (mixer->cfg->de_type >= SUN8I_MIXER_DE3) { u32 val; if (format->hsub == 1 && format->vsub == 1) diff --git a/drivers/gpu/drm/sysfb/ofdrm.c b/drivers/gpu/drm/sysfb/ofdrm.c index fddfe8bea9f7..c9415f0cb3ed 100644 --- a/drivers/gpu/drm/sysfb/ofdrm.c +++ b/drivers/gpu/drm/sysfb/ofdrm.c @@ -8,6 +8,7 @@ #include <drm/clients/drm_client_setup.h> #include <drm/drm_atomic.h> #include <drm/drm_atomic_state_helper.h> +#include <drm/drm_color_mgmt.h> #include <drm/drm_connector.h> #include <drm/drm_damage_helper.h> #include <drm/drm_device.h> @@ -644,36 +645,36 @@ static void ofdrm_qemu_cmap_write(struct ofdrm_device *odev, unsigned char index writeb(b, data); } -static void ofdrm_device_set_gamma_linear(struct ofdrm_device *odev, - const struct drm_format_info *format) +static void ofdrm_set_gamma_lut(struct drm_crtc *crtc, unsigned int index, + u16 red, u16 green, u16 blue) +{ + struct drm_device *dev = crtc->dev; + struct ofdrm_device *odev = ofdrm_device_of_dev(dev); + u8 i8 = index & 0xff; + u8 r8 = red >> 8; + u8 g8 = green >> 8; + u8 b8 = blue >> 8; + + if (drm_WARN_ON_ONCE(dev, index != i8)) + return; /* driver bug */ + + odev->funcs->cmap_write(odev, i8, r8, g8, b8); +} + +static void ofdrm_device_fill_gamma(struct ofdrm_device *odev, + const struct drm_format_info *format) { struct drm_device *dev = &odev->sysfb.dev; - int i; + struct drm_crtc *crtc = &odev->crtc; switch (format->format) { case DRM_FORMAT_RGB565: case DRM_FORMAT_RGB565 | DRM_FORMAT_BIG_ENDIAN: - /* Use better interpolation, to take 32 values from 0 to 255 */ - for (i = 0; i < OFDRM_GAMMA_LUT_SIZE / 8; i++) { - unsigned char r = i * 8 + i / 4; - unsigned char g = i * 4 + i / 16; - unsigned char b = i * 8 + i / 4; - - odev->funcs->cmap_write(odev, i, r, g, b); - } - /* Green has one more bit, so add padding with 0 for red and blue. */ - for (i = OFDRM_GAMMA_LUT_SIZE / 8; i < OFDRM_GAMMA_LUT_SIZE / 4; i++) { - unsigned char r = 0; - unsigned char g = i * 4 + i / 16; - unsigned char b = 0; - - odev->funcs->cmap_write(odev, i, r, g, b); - } + drm_crtc_fill_gamma_565(crtc, ofdrm_set_gamma_lut); break; case DRM_FORMAT_XRGB8888: case DRM_FORMAT_BGRX8888: - for (i = 0; i < OFDRM_GAMMA_LUT_SIZE; i++) - odev->funcs->cmap_write(odev, i, i, i, i); + drm_crtc_fill_gamma_888(crtc, ofdrm_set_gamma_lut); break; default: drm_warn_once(dev, "Unsupported format %p4cc for gamma correction\n", @@ -682,42 +683,21 @@ static void ofdrm_device_set_gamma_linear(struct ofdrm_device *odev, } } -static void ofdrm_device_set_gamma(struct ofdrm_device *odev, - const struct drm_format_info *format, - struct drm_color_lut *lut) +static void ofdrm_device_load_gamma(struct ofdrm_device *odev, + const struct drm_format_info *format, + struct drm_color_lut *lut) { struct drm_device *dev = &odev->sysfb.dev; - int i; + struct drm_crtc *crtc = &odev->crtc; switch (format->format) { case DRM_FORMAT_RGB565: case DRM_FORMAT_RGB565 | DRM_FORMAT_BIG_ENDIAN: - /* Use better interpolation, to take 32 values from lut[0] to lut[255] */ - for (i = 0; i < OFDRM_GAMMA_LUT_SIZE / 8; i++) { - unsigned char r = lut[i * 8 + i / 4].red >> 8; - unsigned char g = lut[i * 4 + i / 16].green >> 8; - unsigned char b = lut[i * 8 + i / 4].blue >> 8; - - odev->funcs->cmap_write(odev, i, r, g, b); - } - /* Green has one more bit, so add padding with 0 for red and blue. */ - for (i = OFDRM_GAMMA_LUT_SIZE / 8; i < OFDRM_GAMMA_LUT_SIZE / 4; i++) { - unsigned char r = 0; - unsigned char g = lut[i * 4 + i / 16].green >> 8; - unsigned char b = 0; - - odev->funcs->cmap_write(odev, i, r, g, b); - } + drm_crtc_load_gamma_565_from_888(crtc, lut, ofdrm_set_gamma_lut); break; case DRM_FORMAT_XRGB8888: case DRM_FORMAT_BGRX8888: - for (i = 0; i < OFDRM_GAMMA_LUT_SIZE; i++) { - unsigned char r = lut[i].red >> 8; - unsigned char g = lut[i].green >> 8; - unsigned char b = lut[i].blue >> 8; - - odev->funcs->cmap_write(odev, i, r, g, b); - } + drm_crtc_load_gamma_888(crtc, lut, ofdrm_set_gamma_lut); break; default: drm_warn_once(dev, "Unsupported format %p4cc for gamma correction\n", @@ -753,9 +733,9 @@ static void ofdrm_crtc_helper_atomic_flush(struct drm_crtc *crtc, struct drm_ato const struct drm_format_info *format = sysfb_crtc_state->format; if (crtc_state->gamma_lut) - ofdrm_device_set_gamma(odev, format, crtc_state->gamma_lut->data); + ofdrm_device_load_gamma(odev, format, crtc_state->gamma_lut->data); else - ofdrm_device_set_gamma_linear(odev, format); + ofdrm_device_fill_gamma(odev, format); } } diff --git a/drivers/gpu/drm/sysfb/vesadrm.c b/drivers/gpu/drm/sysfb/vesadrm.c index 4d62c78e7d1e..7945544ba73e 100644 --- a/drivers/gpu/drm/sysfb/vesadrm.c +++ b/drivers/gpu/drm/sysfb/vesadrm.c @@ -9,6 +9,7 @@ #include <drm/clients/drm_client_setup.h> #include <drm/drm_atomic.h> #include <drm/drm_atomic_state_helper.h> +#include <drm/drm_color_mgmt.h> #include <drm/drm_connector.h> #include <drm/drm_damage_helper.h> #include <drm/drm_device.h> @@ -87,15 +88,10 @@ static struct vesadrm_device *to_vesadrm_device(struct drm_device *dev) static void vesadrm_vga_cmap_write(struct vesadrm_device *vesa, unsigned int index, u16 red, u16 green, u16 blue) { - u8 i8, r8, g8, b8; - - if (index > 255) - return; - - i8 = index; - r8 = red >> 8; - g8 = green >> 8; - b8 = blue >> 8; + u8 i8 = index; + u8 r8 = red >> 8; + u8 g8 = green >> 8; + u8 b8 = blue >> 8; outb_p(i8, VGA_PEL_IW); outb_p(r8, VGA_PEL_D); @@ -120,9 +116,6 @@ static void vesadrm_pmi_cmap_write(struct vesadrm_device *vesa, unsigned int ind 0x00, }; - if (index > 255) - return; - __asm__ __volatile__ ( "call *(%%esi)" : /* no return value */ @@ -135,43 +128,36 @@ static void vesadrm_pmi_cmap_write(struct vesadrm_device *vesa, unsigned int ind } #endif -static void vesadrm_set_gamma_linear(struct vesadrm_device *vesa, - const struct drm_format_info *format) +static void vesadrm_set_gamma_lut(struct drm_crtc *crtc, unsigned int index, + u16 red, u16 green, u16 blue) +{ + struct drm_device *dev = crtc->dev; + struct vesadrm_device *vesa = to_vesadrm_device(dev); + u8 i8 = index & 0xff; + + if (drm_WARN_ON_ONCE(dev, index != i8)) + return; /* driver bug */ + + vesa->cmap_write(vesa, i8, red, green, blue); +} + +static void vesadrm_fill_gamma_lut(struct vesadrm_device *vesa, + const struct drm_format_info *format) { struct drm_device *dev = &vesa->sysfb.dev; - size_t i; - u16 r16, g16, b16; + struct drm_crtc *crtc = &vesa->crtc; switch (format->format) { case DRM_FORMAT_XRGB1555: - for (i = 0; i < 32; ++i) { - r16 = i * 8 + i / 4; - r16 |= (r16 << 8) | r16; - vesa->cmap_write(vesa, i, r16, r16, r16); - } + drm_crtc_fill_gamma_555(crtc, vesadrm_set_gamma_lut); break; case DRM_FORMAT_RGB565: - for (i = 0; i < 32; ++i) { - r16 = i * 8 + i / 4; - r16 |= (r16 << 8) | r16; - g16 = i * 4 + i / 16; - g16 |= (g16 << 8) | g16; - b16 = r16; - vesa->cmap_write(vesa, i, r16, g16, b16); - } - for (i = 32; i < 64; ++i) { - g16 = i * 4 + i / 16; - g16 |= (g16 << 8) | g16; - vesa->cmap_write(vesa, i, 0, g16, 0); - } + drm_crtc_fill_gamma_565(crtc, vesadrm_set_gamma_lut); break; case DRM_FORMAT_RGB888: case DRM_FORMAT_XRGB8888: case DRM_FORMAT_BGRX8888: - for (i = 0; i < 256; ++i) { - r16 = (i << 8) | i; - vesa->cmap_write(vesa, i, r16, r16, r16); - } + drm_crtc_fill_gamma_888(crtc, vesadrm_set_gamma_lut); break; default: drm_warn_once(dev, "Unsupported format %p4cc for gamma correction\n", @@ -180,38 +166,24 @@ static void vesadrm_set_gamma_linear(struct vesadrm_device *vesa, } } -static void vesadrm_set_gamma_lut(struct vesadrm_device *vesa, - const struct drm_format_info *format, - struct drm_color_lut *lut) +static void vesadrm_load_gamma_lut(struct vesadrm_device *vesa, + const struct drm_format_info *format, + struct drm_color_lut *lut) { struct drm_device *dev = &vesa->sysfb.dev; - size_t i; - u16 r16, g16, b16; + struct drm_crtc *crtc = &vesa->crtc; switch (format->format) { case DRM_FORMAT_XRGB1555: - for (i = 0; i < 32; ++i) { - r16 = lut[i * 8 + i / 4].red; - g16 = lut[i * 8 + i / 4].green; - b16 = lut[i * 8 + i / 4].blue; - vesa->cmap_write(vesa, i, r16, g16, b16); - } + drm_crtc_load_gamma_555_from_888(crtc, lut, vesadrm_set_gamma_lut); break; case DRM_FORMAT_RGB565: - for (i = 0; i < 32; ++i) { - r16 = lut[i * 8 + i / 4].red; - g16 = lut[i * 4 + i / 16].green; - b16 = lut[i * 8 + i / 4].blue; - vesa->cmap_write(vesa, i, r16, g16, b16); - } - for (i = 32; i < 64; ++i) - vesa->cmap_write(vesa, i, 0, lut[i * 4 + i / 16].green, 0); + drm_crtc_load_gamma_565_from_888(crtc, lut, vesadrm_set_gamma_lut); break; case DRM_FORMAT_RGB888: case DRM_FORMAT_XRGB8888: case DRM_FORMAT_BGRX8888: - for (i = 0; i < 256; ++i) - vesa->cmap_write(vesa, i, lut[i].red, lut[i].green, lut[i].blue); + drm_crtc_load_gamma_888(crtc, lut, vesadrm_set_gamma_lut); break; default: drm_warn_once(dev, "Unsupported format %p4cc for gamma correction\n", @@ -253,13 +225,13 @@ static void vesadrm_crtc_helper_atomic_flush(struct drm_crtc *crtc, if (crtc_state->enable && crtc_state->color_mgmt_changed) { if (sysfb_crtc_state->format == sysfb->fb_format) { if (crtc_state->gamma_lut) - vesadrm_set_gamma_lut(vesa, - sysfb_crtc_state->format, - crtc_state->gamma_lut->data); + vesadrm_load_gamma_lut(vesa, + sysfb_crtc_state->format, + crtc_state->gamma_lut->data); else - vesadrm_set_gamma_linear(vesa, sysfb_crtc_state->format); + vesadrm_fill_gamma_lut(vesa, sysfb_crtc_state->format); } else { - vesadrm_set_gamma_linear(vesa, sysfb_crtc_state->format); + vesadrm_fill_gamma_lut(vesa, sysfb_crtc_state->format); } } } diff --git a/drivers/gpu/drm/tests/drm_bridge_test.c b/drivers/gpu/drm/tests/drm_bridge_test.c index ff88ec2e911c..887020141c7f 100644 --- a/drivers/gpu/drm/tests/drm_bridge_test.c +++ b/drivers/gpu/drm/tests/drm_bridge_test.c @@ -8,36 +8,64 @@ #include <drm/drm_bridge_helper.h> #include <drm/drm_kunit_helpers.h> +#include <kunit/device.h> #include <kunit/test.h> +/* + * Mimick the typical "private" struct defined by a bridge driver, which + * embeds a bridge plus other fields. + * + * Having at least one member before @bridge ensures we test non-zero + * @bridge offset. + */ +struct drm_bridge_priv { + unsigned int enable_count; + unsigned int disable_count; + struct drm_bridge bridge; + void *data; +}; + struct drm_bridge_init_priv { struct drm_device drm; + /** @dev: device, only for tests not needing a whole drm_device */ + struct device *dev; struct drm_plane *plane; struct drm_crtc *crtc; struct drm_encoder encoder; - struct drm_bridge bridge; + struct drm_bridge_priv *test_bridge; struct drm_connector *connector; - unsigned int enable_count; - unsigned int disable_count; + bool destroyed; }; +static struct drm_bridge_priv *bridge_to_priv(struct drm_bridge *bridge) +{ + return container_of(bridge, struct drm_bridge_priv, bridge); +} + +static void drm_test_bridge_priv_destroy(struct drm_bridge *bridge) +{ + struct drm_bridge_priv *bridge_priv = bridge_to_priv(bridge); + struct drm_bridge_init_priv *priv = (struct drm_bridge_init_priv *)bridge_priv->data; + + priv->destroyed = true; +} + static void drm_test_bridge_enable(struct drm_bridge *bridge) { - struct drm_bridge_init_priv *priv = - container_of(bridge, struct drm_bridge_init_priv, bridge); + struct drm_bridge_priv *priv = bridge_to_priv(bridge); priv->enable_count++; } static void drm_test_bridge_disable(struct drm_bridge *bridge) { - struct drm_bridge_init_priv *priv = - container_of(bridge, struct drm_bridge_init_priv, bridge); + struct drm_bridge_priv *priv = bridge_to_priv(bridge); priv->disable_count++; } static const struct drm_bridge_funcs drm_test_bridge_legacy_funcs = { + .destroy = drm_test_bridge_priv_destroy, .enable = drm_test_bridge_enable, .disable = drm_test_bridge_disable, }; @@ -45,8 +73,7 @@ static const struct drm_bridge_funcs drm_test_bridge_legacy_funcs = { static void drm_test_bridge_atomic_enable(struct drm_bridge *bridge, struct drm_atomic_state *state) { - struct drm_bridge_init_priv *priv = - container_of(bridge, struct drm_bridge_init_priv, bridge); + struct drm_bridge_priv *priv = bridge_to_priv(bridge); priv->enable_count++; } @@ -54,13 +81,13 @@ static void drm_test_bridge_atomic_enable(struct drm_bridge *bridge, static void drm_test_bridge_atomic_disable(struct drm_bridge *bridge, struct drm_atomic_state *state) { - struct drm_bridge_init_priv *priv = - container_of(bridge, struct drm_bridge_init_priv, bridge); + struct drm_bridge_priv *priv = bridge_to_priv(bridge); priv->disable_count++; } static const struct drm_bridge_funcs drm_test_bridge_atomic_funcs = { + .destroy = drm_test_bridge_priv_destroy, .atomic_enable = drm_test_bridge_atomic_enable, .atomic_disable = drm_test_bridge_atomic_disable, .atomic_destroy_state = drm_atomic_helper_bridge_destroy_state, @@ -102,6 +129,12 @@ drm_test_bridge_init(struct kunit *test, const struct drm_bridge_funcs *funcs) if (IS_ERR(priv)) return ERR_CAST(priv); + priv->test_bridge = devm_drm_bridge_alloc(dev, struct drm_bridge_priv, bridge, funcs); + if (IS_ERR(priv->test_bridge)) + return ERR_CAST(priv->test_bridge); + + priv->test_bridge->data = priv; + drm = &priv->drm; priv->plane = drm_kunit_helper_create_primary_plane(test, drm, NULL, @@ -125,9 +158,8 @@ drm_test_bridge_init(struct kunit *test, const struct drm_bridge_funcs *funcs) enc->possible_crtcs = drm_crtc_mask(priv->crtc); - bridge = &priv->bridge; + bridge = &priv->test_bridge->bridge; bridge->type = DRM_MODE_CONNECTOR_VIRTUAL; - bridge->funcs = funcs; ret = drm_kunit_bridge_add(test, bridge); if (ret) @@ -173,7 +205,7 @@ static void drm_test_drm_bridge_get_current_state_atomic(struct kunit *test) KUNIT_ASSERT_NOT_ERR_OR_NULL(test, state); retry_commit: - bridge = &priv->bridge; + bridge = &priv->test_bridge->bridge; bridge_state = drm_atomic_get_bridge_state(state, bridge); KUNIT_ASSERT_NOT_ERR_OR_NULL(test, bridge_state); @@ -228,7 +260,7 @@ static void drm_test_drm_bridge_get_current_state_legacy(struct kunit *test) * locking. The function would return NULL in all cases anyway, * so we don't really have any concurrency to worry about. */ - bridge = &priv->bridge; + bridge = &priv->test_bridge->bridge; KUNIT_EXPECT_NULL(test, drm_bridge_get_current_state(bridge)); } @@ -253,7 +285,7 @@ static void drm_test_drm_bridge_helper_reset_crtc_atomic(struct kunit *test) struct drm_modeset_acquire_ctx ctx; struct drm_bridge_init_priv *priv; struct drm_display_mode *mode; - struct drm_bridge *bridge; + struct drm_bridge_priv *bridge_priv; int ret; priv = drm_test_bridge_init(test, &drm_test_bridge_atomic_funcs); @@ -279,14 +311,14 @@ retry_commit: drm_modeset_drop_locks(&ctx); drm_modeset_acquire_fini(&ctx); - bridge = &priv->bridge; - KUNIT_ASSERT_EQ(test, priv->enable_count, 1); - KUNIT_ASSERT_EQ(test, priv->disable_count, 0); + bridge_priv = priv->test_bridge; + KUNIT_ASSERT_EQ(test, bridge_priv->enable_count, 1); + KUNIT_ASSERT_EQ(test, bridge_priv->disable_count, 0); drm_modeset_acquire_init(&ctx, 0); retry_reset: - ret = drm_bridge_helper_reset_crtc(bridge, &ctx); + ret = drm_bridge_helper_reset_crtc(&bridge_priv->bridge, &ctx); if (ret == -EDEADLK) { drm_modeset_backoff(&ctx); goto retry_reset; @@ -296,8 +328,8 @@ retry_reset: drm_modeset_drop_locks(&ctx); drm_modeset_acquire_fini(&ctx); - KUNIT_EXPECT_EQ(test, priv->enable_count, 2); - KUNIT_EXPECT_EQ(test, priv->disable_count, 1); + KUNIT_EXPECT_EQ(test, bridge_priv->enable_count, 2); + KUNIT_EXPECT_EQ(test, bridge_priv->disable_count, 1); } /* @@ -309,7 +341,7 @@ static void drm_test_drm_bridge_helper_reset_crtc_atomic_disabled(struct kunit * struct drm_modeset_acquire_ctx ctx; struct drm_bridge_init_priv *priv; struct drm_display_mode *mode; - struct drm_bridge *bridge; + struct drm_bridge_priv *bridge_priv; int ret; priv = drm_test_bridge_init(test, &drm_test_bridge_atomic_funcs); @@ -318,14 +350,14 @@ static void drm_test_drm_bridge_helper_reset_crtc_atomic_disabled(struct kunit * mode = drm_kunit_display_mode_from_cea_vic(test, &priv->drm, 16); KUNIT_ASSERT_NOT_ERR_OR_NULL(test, mode); - bridge = &priv->bridge; - KUNIT_ASSERT_EQ(test, priv->enable_count, 0); - KUNIT_ASSERT_EQ(test, priv->disable_count, 0); + bridge_priv = priv->test_bridge; + KUNIT_ASSERT_EQ(test, bridge_priv->enable_count, 0); + KUNIT_ASSERT_EQ(test, bridge_priv->disable_count, 0); drm_modeset_acquire_init(&ctx, 0); retry_reset: - ret = drm_bridge_helper_reset_crtc(bridge, &ctx); + ret = drm_bridge_helper_reset_crtc(&bridge_priv->bridge, &ctx); if (ret == -EDEADLK) { drm_modeset_backoff(&ctx); goto retry_reset; @@ -335,8 +367,8 @@ retry_reset: drm_modeset_drop_locks(&ctx); drm_modeset_acquire_fini(&ctx); - KUNIT_EXPECT_EQ(test, priv->enable_count, 0); - KUNIT_EXPECT_EQ(test, priv->disable_count, 0); + KUNIT_EXPECT_EQ(test, bridge_priv->enable_count, 0); + KUNIT_EXPECT_EQ(test, bridge_priv->disable_count, 0); } /* @@ -348,7 +380,7 @@ static void drm_test_drm_bridge_helper_reset_crtc_legacy(struct kunit *test) struct drm_modeset_acquire_ctx ctx; struct drm_bridge_init_priv *priv; struct drm_display_mode *mode; - struct drm_bridge *bridge; + struct drm_bridge_priv *bridge_priv; int ret; priv = drm_test_bridge_init(test, &drm_test_bridge_legacy_funcs); @@ -374,14 +406,14 @@ retry_commit: drm_modeset_drop_locks(&ctx); drm_modeset_acquire_fini(&ctx); - bridge = &priv->bridge; - KUNIT_ASSERT_EQ(test, priv->enable_count, 1); - KUNIT_ASSERT_EQ(test, priv->disable_count, 0); + bridge_priv = priv->test_bridge; + KUNIT_ASSERT_EQ(test, bridge_priv->enable_count, 1); + KUNIT_ASSERT_EQ(test, bridge_priv->disable_count, 0); drm_modeset_acquire_init(&ctx, 0); retry_reset: - ret = drm_bridge_helper_reset_crtc(bridge, &ctx); + ret = drm_bridge_helper_reset_crtc(&bridge_priv->bridge, &ctx); if (ret == -EDEADLK) { drm_modeset_backoff(&ctx); goto retry_reset; @@ -391,8 +423,8 @@ retry_reset: drm_modeset_drop_locks(&ctx); drm_modeset_acquire_fini(&ctx); - KUNIT_EXPECT_EQ(test, priv->enable_count, 2); - KUNIT_EXPECT_EQ(test, priv->disable_count, 1); + KUNIT_EXPECT_EQ(test, bridge_priv->enable_count, 2); + KUNIT_EXPECT_EQ(test, bridge_priv->disable_count, 1); } static struct kunit_case drm_bridge_helper_reset_crtc_tests[] = { @@ -407,11 +439,83 @@ static struct kunit_suite drm_bridge_helper_reset_crtc_test_suite = { .test_cases = drm_bridge_helper_reset_crtc_tests, }; +static int drm_test_bridge_alloc_init(struct kunit *test) +{ + struct drm_bridge_init_priv *priv; + + priv = kunit_kzalloc(test, sizeof(*priv), GFP_KERNEL); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, priv); + + priv->dev = kunit_device_register(test, "drm-bridge-dev"); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, priv->dev); + + test->priv = priv; + + priv->test_bridge = devm_drm_bridge_alloc(priv->dev, struct drm_bridge_priv, bridge, + &drm_test_bridge_atomic_funcs); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, priv->test_bridge); + + priv->test_bridge->data = priv; + + KUNIT_ASSERT_FALSE(test, priv->destroyed); + + return 0; +} + +/* + * Test that a bridge is freed when the device is destroyed in lack of + * other drm_bridge_get/put() operations. + */ +static void drm_test_drm_bridge_alloc_basic(struct kunit *test) +{ + struct drm_bridge_init_priv *priv = test->priv; + + KUNIT_ASSERT_FALSE(test, priv->destroyed); + + kunit_device_unregister(test, priv->dev); + KUNIT_EXPECT_TRUE(test, priv->destroyed); +} + +/* + * Test that a bridge is not freed when the device is destroyed when there + * is still a reference to it, and freed when that reference is put. + */ +static void drm_test_drm_bridge_alloc_get_put(struct kunit *test) +{ + struct drm_bridge_init_priv *priv = test->priv; + + KUNIT_ASSERT_FALSE(test, priv->destroyed); + + drm_bridge_get(&priv->test_bridge->bridge); + KUNIT_EXPECT_FALSE(test, priv->destroyed); + + kunit_device_unregister(test, priv->dev); + KUNIT_EXPECT_FALSE(test, priv->destroyed); + + drm_bridge_put(&priv->test_bridge->bridge); + KUNIT_EXPECT_TRUE(test, priv->destroyed); +} + +static struct kunit_case drm_bridge_alloc_tests[] = { + KUNIT_CASE(drm_test_drm_bridge_alloc_basic), + KUNIT_CASE(drm_test_drm_bridge_alloc_get_put), + { } +}; + +static struct kunit_suite drm_bridge_alloc_test_suite = { + .name = "drm_bridge_alloc", + .init = drm_test_bridge_alloc_init, + .test_cases = drm_bridge_alloc_tests, +}; + kunit_test_suites( &drm_bridge_get_current_state_test_suite, &drm_bridge_helper_reset_crtc_test_suite, + &drm_bridge_alloc_test_suite, ); MODULE_AUTHOR("Maxime Ripard <mripard@kernel.org>"); +MODULE_AUTHOR("Luca Ceresoli <luca.ceresoli@bootlin.com>"); + MODULE_DESCRIPTION("Kunit test for drm_bridge functions"); MODULE_LICENSE("GPL"); diff --git a/drivers/gpu/drm/tests/drm_hdmi_state_helper_test.c b/drivers/gpu/drm/tests/drm_hdmi_state_helper_test.c index 7ffd666753b1..8bd412735000 100644 --- a/drivers/gpu/drm/tests/drm_hdmi_state_helper_test.c +++ b/drivers/gpu/drm/tests/drm_hdmi_state_helper_test.c @@ -33,7 +33,7 @@ struct drm_atomic_helper_connector_hdmi_priv { struct drm_encoder encoder; struct drm_connector connector; - const char *current_edid; + const void *current_edid; size_t current_edid_len; }; @@ -56,7 +56,7 @@ static struct drm_display_mode *find_preferred_mode(struct drm_connector *connec } static int set_connector_edid(struct kunit *test, struct drm_connector *connector, - const char *edid, size_t edid_len) + const void *edid, size_t edid_len) { struct drm_atomic_helper_connector_hdmi_priv *priv = connector_to_priv(connector); @@ -89,15 +89,15 @@ static const struct drm_connector_hdmi_funcs reject_connector_hdmi_funcs = { }; static enum drm_mode_status -reject_100MHz_connector_tmds_char_rate_valid(const struct drm_connector *connector, +reject_100mhz_connector_tmds_char_rate_valid(const struct drm_connector *connector, const struct drm_display_mode *mode, unsigned long long tmds_rate) { return (tmds_rate > 100ULL * 1000 * 1000) ? MODE_BAD : MODE_OK; } -static const struct drm_connector_hdmi_funcs reject_100_MHz_connector_hdmi_funcs = { - .tmds_char_rate_valid = reject_100MHz_connector_tmds_char_rate_valid, +static const struct drm_connector_hdmi_funcs reject_100mhz_connector_hdmi_funcs = { + .tmds_char_rate_valid = reject_100mhz_connector_tmds_char_rate_valid, }; static int dummy_connector_get_modes(struct drm_connector *connector) @@ -140,10 +140,11 @@ static const struct drm_connector_funcs dummy_connector_funcs = { static struct drm_atomic_helper_connector_hdmi_priv * -drm_kunit_helper_connector_hdmi_init_funcs(struct kunit *test, - unsigned int formats, - unsigned int max_bpc, - const struct drm_connector_hdmi_funcs *hdmi_funcs) +__connector_hdmi_init(struct kunit *test, + unsigned int formats, + unsigned int max_bpc, + const struct drm_connector_hdmi_funcs *hdmi_funcs, + const void *edid_data, size_t edid_len) { struct drm_atomic_helper_connector_hdmi_priv *priv; struct drm_connector *conn; @@ -182,6 +183,8 @@ drm_kunit_helper_connector_hdmi_init_funcs(struct kunit *test, enc->possible_crtcs = drm_crtc_mask(priv->crtc); conn = &priv->connector; + conn->ycbcr_420_allowed = !!(formats & BIT(HDMI_COLORSPACE_YUV420)); + ret = drmm_connector_hdmi_init(drm, conn, "Vendor", "Product", &dummy_connector_funcs, @@ -197,29 +200,28 @@ drm_kunit_helper_connector_hdmi_init_funcs(struct kunit *test, drm_mode_config_reset(drm); + if (edid_data && edid_len) { + ret = set_connector_edid(test, &priv->connector, edid_data, edid_len); + KUNIT_ASSERT_GT(test, ret, 0); + } + return priv; } +#define drm_kunit_helper_connector_hdmi_init_with_edid_funcs(test, formats, max_bpc, funcs, edid) \ + __connector_hdmi_init(test, formats, max_bpc, funcs, edid, ARRAY_SIZE(edid)) + static struct drm_atomic_helper_connector_hdmi_priv * drm_kunit_helper_connector_hdmi_init(struct kunit *test, unsigned int formats, unsigned int max_bpc) { - struct drm_atomic_helper_connector_hdmi_priv *priv; - int ret; - - priv = drm_kunit_helper_connector_hdmi_init_funcs(test, - formats, max_bpc, - &dummy_connector_hdmi_funcs); - KUNIT_ASSERT_NOT_ERR_OR_NULL(test, priv); - - ret = set_connector_edid(test, &priv->connector, - test_edid_hdmi_1080p_rgb_max_200mhz, - ARRAY_SIZE(test_edid_hdmi_1080p_rgb_max_200mhz)); - KUNIT_ASSERT_GT(test, ret, 0); - - return priv; + return drm_kunit_helper_connector_hdmi_init_with_edid_funcs(test, + formats, + max_bpc, + &dummy_connector_hdmi_funcs, + test_edid_hdmi_1080p_rgb_max_200mhz); } /* @@ -414,7 +416,7 @@ static void drm_test_check_broadcast_rgb_auto_cea_mode(struct kunit *test) ret = drm_atomic_check_only(state); KUNIT_ASSERT_EQ(test, ret, 0); - conn_state = drm_atomic_get_connector_state(state, conn); + conn_state = drm_atomic_get_new_connector_state(state, conn); KUNIT_ASSERT_NOT_ERR_OR_NULL(test, conn_state); KUNIT_EXPECT_TRUE(test, conn_state->hdmi.is_limited_range); @@ -474,7 +476,7 @@ static void drm_test_check_broadcast_rgb_auto_cea_mode_vic_1(struct kunit *test) ret = drm_atomic_check_only(state); KUNIT_ASSERT_EQ(test, ret, 0); - conn_state = drm_atomic_get_connector_state(state, conn); + conn_state = drm_atomic_get_new_connector_state(state, conn); KUNIT_ASSERT_NOT_ERR_OR_NULL(test, conn_state); KUNIT_EXPECT_FALSE(test, conn_state->hdmi.is_limited_range); @@ -533,7 +535,7 @@ static void drm_test_check_broadcast_rgb_full_cea_mode(struct kunit *test) ret = drm_atomic_check_only(state); KUNIT_ASSERT_EQ(test, ret, 0); - conn_state = drm_atomic_get_connector_state(state, conn); + conn_state = drm_atomic_get_new_connector_state(state, conn); KUNIT_ASSERT_NOT_ERR_OR_NULL(test, conn_state); KUNIT_ASSERT_EQ(test, @@ -595,7 +597,7 @@ static void drm_test_check_broadcast_rgb_full_cea_mode_vic_1(struct kunit *test) ret = drm_atomic_check_only(state); KUNIT_ASSERT_EQ(test, ret, 0); - conn_state = drm_atomic_get_connector_state(state, conn); + conn_state = drm_atomic_get_new_connector_state(state, conn); KUNIT_ASSERT_NOT_ERR_OR_NULL(test, conn_state); KUNIT_ASSERT_EQ(test, @@ -658,7 +660,7 @@ static void drm_test_check_broadcast_rgb_limited_cea_mode(struct kunit *test) ret = drm_atomic_check_only(state); KUNIT_ASSERT_EQ(test, ret, 0); - conn_state = drm_atomic_get_connector_state(state, conn); + conn_state = drm_atomic_get_new_connector_state(state, conn); KUNIT_ASSERT_NOT_ERR_OR_NULL(test, conn_state); KUNIT_ASSERT_EQ(test, @@ -720,7 +722,7 @@ static void drm_test_check_broadcast_rgb_limited_cea_mode_vic_1(struct kunit *te ret = drm_atomic_check_only(state); KUNIT_ASSERT_EQ(test, ret, 0); - conn_state = drm_atomic_get_connector_state(state, conn); + conn_state = drm_atomic_get_new_connector_state(state, conn); KUNIT_ASSERT_NOT_ERR_OR_NULL(test, conn_state); KUNIT_ASSERT_EQ(test, @@ -734,6 +736,107 @@ static void drm_test_check_broadcast_rgb_limited_cea_mode_vic_1(struct kunit *te } /* + * Test that for an HDMI connector, with an HDMI monitor, we will + * get a limited RGB Quantization Range with a YUV420 mode, no + * matter what the value of the Broadcast RGB property is set to. + */ +static void drm_test_check_broadcast_rgb_cea_mode_yuv420(struct kunit *test) +{ + struct drm_atomic_helper_connector_hdmi_priv *priv; + enum drm_hdmi_broadcast_rgb broadcast_rgb; + struct drm_modeset_acquire_ctx ctx; + struct drm_connector_state *conn_state; + struct drm_atomic_state *state; + struct drm_display_mode *mode; + struct drm_connector *conn; + struct drm_device *drm; + struct drm_crtc *crtc; + int ret; + + broadcast_rgb = *(enum drm_hdmi_broadcast_rgb *)test->param_value; + + priv = drm_kunit_helper_connector_hdmi_init_with_edid_funcs(test, + BIT(HDMI_COLORSPACE_RGB) | + BIT(HDMI_COLORSPACE_YUV420), + 8, + &dummy_connector_hdmi_funcs, + test_edid_hdmi_1080p_rgb_yuv_4k_yuv420_dc_max_200mhz); + KUNIT_ASSERT_NOT_NULL(test, priv); + + drm = &priv->drm; + crtc = priv->crtc; + conn = &priv->connector; + KUNIT_ASSERT_TRUE(test, conn->display_info.is_hdmi); + + mode = drm_kunit_display_mode_from_cea_vic(test, drm, 95); + KUNIT_ASSERT_NOT_NULL(test, mode); + + drm_modeset_acquire_init(&ctx, 0); + +retry_conn_enable: + ret = drm_kunit_helper_enable_crtc_connector(test, drm, crtc, conn, + mode, &ctx); + if (ret == -EDEADLK) { + ret = drm_modeset_backoff(&ctx); + if (!ret) + goto retry_conn_enable; + } + KUNIT_ASSERT_EQ(test, ret, 0); + + state = drm_kunit_helper_atomic_state_alloc(test, drm, &ctx); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, state); + +retry_conn_state: + conn_state = drm_atomic_get_connector_state(state, conn); + if (PTR_ERR(conn_state) == -EDEADLK) { + drm_atomic_state_clear(state); + ret = drm_modeset_backoff(&ctx); + if (!ret) + goto retry_conn_state; + } + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, conn_state); + + conn_state->hdmi.broadcast_rgb = broadcast_rgb; + + ret = drm_atomic_check_only(state); + if (ret == -EDEADLK) { + drm_atomic_state_clear(state); + ret = drm_modeset_backoff(&ctx); + if (!ret) + goto retry_conn_state; + } + KUNIT_ASSERT_EQ(test, ret, 0); + + conn_state = drm_atomic_get_new_connector_state(state, conn); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, conn_state); + + KUNIT_ASSERT_EQ(test, conn_state->hdmi.broadcast_rgb, broadcast_rgb); + KUNIT_ASSERT_EQ(test, conn_state->hdmi.output_format, HDMI_COLORSPACE_YUV420); + + KUNIT_EXPECT_TRUE(test, conn_state->hdmi.is_limited_range); + + drm_modeset_drop_locks(&ctx); + drm_modeset_acquire_fini(&ctx); +} + +static const enum drm_hdmi_broadcast_rgb check_broadcast_rgb_cea_mode_yuv420_tests[] = { + DRM_HDMI_BROADCAST_RGB_AUTO, + DRM_HDMI_BROADCAST_RGB_FULL, + DRM_HDMI_BROADCAST_RGB_LIMITED, +}; + +static void +check_broadcast_rgb_cea_mode_yuv420_desc(const enum drm_hdmi_broadcast_rgb *broadcast_rgb, + char *desc) +{ + sprintf(desc, "%s", drm_hdmi_connector_get_broadcast_rgb_name(*broadcast_rgb)); +} + +KUNIT_ARRAY_PARAM(check_broadcast_rgb_cea_mode_yuv420, + check_broadcast_rgb_cea_mode_yuv420_tests, + check_broadcast_rgb_cea_mode_yuv420_desc); + +/* * Test that if we change the maximum bpc property to a different value, * we trigger a mode change on the connector's CRTC, which will in turn * disable/enable the connector. @@ -752,19 +855,16 @@ static void drm_test_check_output_bpc_crtc_mode_changed(struct kunit *test) struct drm_crtc *crtc; int ret; - priv = drm_kunit_helper_connector_hdmi_init(test, - BIT(HDMI_COLORSPACE_RGB), - 10); + priv = drm_kunit_helper_connector_hdmi_init_with_edid_funcs(test, + BIT(HDMI_COLORSPACE_RGB), + 10, + &dummy_connector_hdmi_funcs, + test_edid_hdmi_1080p_rgb_yuv_dc_max_200mhz); KUNIT_ASSERT_NOT_NULL(test, priv); drm = &priv->drm; crtc = priv->crtc; conn = &priv->connector; - ret = set_connector_edid(test, conn, - test_edid_hdmi_1080p_rgb_yuv_dc_max_200mhz, - ARRAY_SIZE(test_edid_hdmi_1080p_rgb_yuv_dc_max_200mhz)); - KUNIT_ASSERT_GT(test, ret, 0); - preferred = find_preferred_mode(conn); KUNIT_ASSERT_NOT_NULL(test, preferred); @@ -831,19 +931,16 @@ static void drm_test_check_output_bpc_crtc_mode_not_changed(struct kunit *test) struct drm_crtc *crtc; int ret; - priv = drm_kunit_helper_connector_hdmi_init(test, - BIT(HDMI_COLORSPACE_RGB), - 10); + priv = drm_kunit_helper_connector_hdmi_init_with_edid_funcs(test, + BIT(HDMI_COLORSPACE_RGB), + 10, + &dummy_connector_hdmi_funcs, + test_edid_hdmi_1080p_rgb_yuv_dc_max_200mhz); KUNIT_ASSERT_NOT_NULL(test, priv); drm = &priv->drm; crtc = priv->crtc; conn = &priv->connector; - ret = set_connector_edid(test, conn, - test_edid_hdmi_1080p_rgb_yuv_dc_max_200mhz, - ARRAY_SIZE(test_edid_hdmi_1080p_rgb_yuv_dc_max_200mhz)); - KUNIT_ASSERT_GT(test, ret, 0); - preferred = find_preferred_mode(conn); KUNIT_ASSERT_NOT_NULL(test, preferred); @@ -905,21 +1002,18 @@ static void drm_test_check_output_bpc_dvi(struct kunit *test) struct drm_crtc *crtc; int ret; - priv = drm_kunit_helper_connector_hdmi_init(test, - BIT(HDMI_COLORSPACE_RGB) | - BIT(HDMI_COLORSPACE_YUV422) | - BIT(HDMI_COLORSPACE_YUV444), - 12); + priv = drm_kunit_helper_connector_hdmi_init_with_edid_funcs(test, + BIT(HDMI_COLORSPACE_RGB) | + BIT(HDMI_COLORSPACE_YUV422) | + BIT(HDMI_COLORSPACE_YUV444), + 12, + &dummy_connector_hdmi_funcs, + test_edid_dvi_1080p); KUNIT_ASSERT_NOT_NULL(test, priv); drm = &priv->drm; crtc = priv->crtc; conn = &priv->connector; - ret = set_connector_edid(test, conn, - test_edid_dvi_1080p, - ARRAY_SIZE(test_edid_dvi_1080p)); - KUNIT_ASSERT_GT(test, ret, 0); - info = &conn->display_info; KUNIT_ASSERT_FALSE(test, info->is_hdmi); @@ -959,19 +1053,16 @@ static void drm_test_check_tmds_char_rate_rgb_8bpc(struct kunit *test) struct drm_crtc *crtc; int ret; - priv = drm_kunit_helper_connector_hdmi_init(test, - BIT(HDMI_COLORSPACE_RGB), - 8); + priv = drm_kunit_helper_connector_hdmi_init_with_edid_funcs(test, + BIT(HDMI_COLORSPACE_RGB), + 8, + &dummy_connector_hdmi_funcs, + test_edid_hdmi_1080p_rgb_max_200mhz); KUNIT_ASSERT_NOT_NULL(test, priv); drm = &priv->drm; crtc = priv->crtc; conn = &priv->connector; - ret = set_connector_edid(test, conn, - test_edid_hdmi_1080p_rgb_max_200mhz, - ARRAY_SIZE(test_edid_hdmi_1080p_rgb_max_200mhz)); - KUNIT_ASSERT_GT(test, ret, 0); - preferred = find_preferred_mode(conn); KUNIT_ASSERT_NOT_NULL(test, preferred); KUNIT_ASSERT_FALSE(test, preferred->flags & DRM_MODE_FLAG_DBLCLK); @@ -1011,19 +1102,16 @@ static void drm_test_check_tmds_char_rate_rgb_10bpc(struct kunit *test) struct drm_crtc *crtc; int ret; - priv = drm_kunit_helper_connector_hdmi_init(test, - BIT(HDMI_COLORSPACE_RGB), - 10); + priv = drm_kunit_helper_connector_hdmi_init_with_edid_funcs(test, + BIT(HDMI_COLORSPACE_RGB), + 10, + &dummy_connector_hdmi_funcs, + test_edid_hdmi_1080p_rgb_yuv_dc_max_340mhz); KUNIT_ASSERT_NOT_NULL(test, priv); drm = &priv->drm; crtc = priv->crtc; conn = &priv->connector; - ret = set_connector_edid(test, conn, - test_edid_hdmi_1080p_rgb_yuv_dc_max_340mhz, - ARRAY_SIZE(test_edid_hdmi_1080p_rgb_yuv_dc_max_340mhz)); - KUNIT_ASSERT_GT(test, ret, 0); - preferred = find_preferred_mode(conn); KUNIT_ASSERT_NOT_NULL(test, preferred); KUNIT_ASSERT_FALSE(test, preferred->flags & DRM_MODE_FLAG_DBLCLK); @@ -1063,19 +1151,16 @@ static void drm_test_check_tmds_char_rate_rgb_12bpc(struct kunit *test) struct drm_crtc *crtc; int ret; - priv = drm_kunit_helper_connector_hdmi_init(test, - BIT(HDMI_COLORSPACE_RGB), - 12); + priv = drm_kunit_helper_connector_hdmi_init_with_edid_funcs(test, + BIT(HDMI_COLORSPACE_RGB), + 12, + &dummy_connector_hdmi_funcs, + test_edid_hdmi_1080p_rgb_yuv_dc_max_340mhz); KUNIT_ASSERT_NOT_NULL(test, priv); drm = &priv->drm; crtc = priv->crtc; conn = &priv->connector; - ret = set_connector_edid(test, conn, - test_edid_hdmi_1080p_rgb_yuv_dc_max_340mhz, - ARRAY_SIZE(test_edid_hdmi_1080p_rgb_yuv_dc_max_340mhz)); - KUNIT_ASSERT_GT(test, ret, 0); - preferred = find_preferred_mode(conn); KUNIT_ASSERT_NOT_NULL(test, preferred); KUNIT_ASSERT_FALSE(test, preferred->flags & DRM_MODE_FLAG_DBLCLK); @@ -1168,7 +1253,7 @@ static void drm_test_check_hdmi_funcs_reject_rate(struct kunit *test) * Then we will pick the latter, and the computed TMDS character rate * will be equal to 1.25 times the mode pixel clock. */ -static void drm_test_check_max_tmds_rate_bpc_fallback(struct kunit *test) +static void drm_test_check_max_tmds_rate_bpc_fallback_rgb(struct kunit *test) { struct drm_atomic_helper_connector_hdmi_priv *priv; struct drm_modeset_acquire_ctx ctx; @@ -1181,19 +1266,16 @@ static void drm_test_check_max_tmds_rate_bpc_fallback(struct kunit *test) struct drm_crtc *crtc; int ret; - priv = drm_kunit_helper_connector_hdmi_init(test, - BIT(HDMI_COLORSPACE_RGB), - 12); + priv = drm_kunit_helper_connector_hdmi_init_with_edid_funcs(test, + BIT(HDMI_COLORSPACE_RGB), + 12, + &dummy_connector_hdmi_funcs, + test_edid_hdmi_1080p_rgb_yuv_dc_max_200mhz); KUNIT_ASSERT_NOT_NULL(test, priv); drm = &priv->drm; crtc = priv->crtc; conn = &priv->connector; - ret = set_connector_edid(test, conn, - test_edid_hdmi_1080p_rgb_yuv_dc_max_200mhz, - ARRAY_SIZE(test_edid_hdmi_1080p_rgb_yuv_dc_max_200mhz)); - KUNIT_ASSERT_GT(test, ret, 0); - info = &conn->display_info; KUNIT_ASSERT_TRUE(test, info->is_hdmi); KUNIT_ASSERT_GT(test, info->max_tmds_clock, 0); @@ -1229,6 +1311,80 @@ static void drm_test_check_max_tmds_rate_bpc_fallback(struct kunit *test) /* * Test that if: + * - We have an HDMI connector and a display supporting both RGB and YUV420 + * - The chosen mode can be supported in YUV420 output format only + * - The chosen mode has a TMDS character rate higher than the display + * supports in YUV420/12bpc + * - The chosen mode has a TMDS character rate lower than the display + * supports in YUV420/10bpc. + * + * Then we will pick the latter, and the computed TMDS character rate + * will be equal to 1.25 * 0.5 times the mode pixel clock. + */ +static void drm_test_check_max_tmds_rate_bpc_fallback_yuv420(struct kunit *test) +{ + struct drm_atomic_helper_connector_hdmi_priv *priv; + struct drm_modeset_acquire_ctx ctx; + struct drm_connector_state *conn_state; + struct drm_display_info *info; + struct drm_display_mode *yuv420_only_mode; + unsigned long long rate; + struct drm_connector *conn; + struct drm_device *drm; + struct drm_crtc *crtc; + int ret; + + priv = drm_kunit_helper_connector_hdmi_init_with_edid_funcs(test, + BIT(HDMI_COLORSPACE_RGB) | + BIT(HDMI_COLORSPACE_YUV420), + 12, + &dummy_connector_hdmi_funcs, + test_edid_hdmi_1080p_rgb_yuv_4k_yuv420_dc_max_200mhz); + KUNIT_ASSERT_NOT_NULL(test, priv); + + drm = &priv->drm; + crtc = priv->crtc; + conn = &priv->connector; + info = &conn->display_info; + KUNIT_ASSERT_TRUE(test, info->is_hdmi); + KUNIT_ASSERT_GT(test, info->max_tmds_clock, 0); + KUNIT_ASSERT_TRUE(test, conn->ycbcr_420_allowed); + + yuv420_only_mode = drm_kunit_display_mode_from_cea_vic(test, drm, 95); + KUNIT_ASSERT_NOT_NULL(test, yuv420_only_mode); + KUNIT_ASSERT_TRUE(test, drm_mode_is_420_only(info, yuv420_only_mode)); + + rate = drm_hdmi_compute_mode_clock(yuv420_only_mode, 12, HDMI_COLORSPACE_YUV420); + KUNIT_ASSERT_GT(test, rate, info->max_tmds_clock * 1000); + + rate = drm_hdmi_compute_mode_clock(yuv420_only_mode, 10, HDMI_COLORSPACE_YUV420); + KUNIT_ASSERT_LT(test, rate, info->max_tmds_clock * 1000); + + drm_modeset_acquire_init(&ctx, 0); + +retry_conn_enable: + ret = drm_kunit_helper_enable_crtc_connector(test, drm, crtc, conn, + yuv420_only_mode, &ctx); + if (ret == -EDEADLK) { + ret = drm_modeset_backoff(&ctx); + if (!ret) + goto retry_conn_enable; + } + KUNIT_EXPECT_EQ(test, ret, 0); + + conn_state = conn->state; + KUNIT_ASSERT_NOT_NULL(test, conn_state); + + KUNIT_EXPECT_EQ(test, conn_state->hdmi.output_bpc, 10); + KUNIT_EXPECT_EQ(test, conn_state->hdmi.output_format, HDMI_COLORSPACE_YUV420); + KUNIT_EXPECT_EQ(test, conn_state->hdmi.tmds_char_rate, yuv420_only_mode->clock * 625); + + drm_modeset_drop_locks(&ctx); + drm_modeset_acquire_fini(&ctx); +} + +/* + * Test that if: * - We have an HDMI connector supporting both RGB and YUV422 and up to * 12 bpc * - The chosen mode has a TMDS character rate higher than the display @@ -1240,7 +1396,7 @@ static void drm_test_check_max_tmds_rate_bpc_fallback(struct kunit *test) * Then we will prefer to keep the RGB format with a lower bpc over * picking YUV422. */ -static void drm_test_check_max_tmds_rate_format_fallback(struct kunit *test) +static void drm_test_check_max_tmds_rate_bpc_fallback_ignore_yuv422(struct kunit *test) { struct drm_atomic_helper_connector_hdmi_priv *priv; struct drm_modeset_acquire_ctx ctx; @@ -1253,21 +1409,18 @@ static void drm_test_check_max_tmds_rate_format_fallback(struct kunit *test) struct drm_crtc *crtc; int ret; - priv = drm_kunit_helper_connector_hdmi_init(test, - BIT(HDMI_COLORSPACE_RGB) | - BIT(HDMI_COLORSPACE_YUV422) | - BIT(HDMI_COLORSPACE_YUV444), - 12); + priv = drm_kunit_helper_connector_hdmi_init_with_edid_funcs(test, + BIT(HDMI_COLORSPACE_RGB) | + BIT(HDMI_COLORSPACE_YUV422) | + BIT(HDMI_COLORSPACE_YUV444), + 12, + &dummy_connector_hdmi_funcs, + test_edid_hdmi_1080p_rgb_yuv_dc_max_200mhz); KUNIT_ASSERT_NOT_NULL(test, priv); drm = &priv->drm; crtc = priv->crtc; conn = &priv->connector; - ret = set_connector_edid(test, conn, - test_edid_hdmi_1080p_rgb_yuv_dc_max_200mhz, - ARRAY_SIZE(test_edid_hdmi_1080p_rgb_yuv_dc_max_200mhz)); - KUNIT_ASSERT_GT(test, ret, 0); - info = &conn->display_info; KUNIT_ASSERT_TRUE(test, info->is_hdmi); KUNIT_ASSERT_GT(test, info->max_tmds_clock, 0); @@ -1304,6 +1457,170 @@ static void drm_test_check_max_tmds_rate_format_fallback(struct kunit *test) } /* + * Test that if: + * - We have an HDMI connector supporting both RGB and YUV420 and up to + * 12 bpc + * - The chosen mode has a TMDS character rate higher than the display + * supports in RGB/10bpc but lower than the display supports in + * RGB/8bpc + * - The chosen mode has a TMDS character rate lower than the display + * supports in YUV420/12bpc. + * + * Then we will prefer to keep the RGB format with a lower bpc over + * picking YUV420. + */ +static void drm_test_check_max_tmds_rate_bpc_fallback_ignore_yuv420(struct kunit *test) +{ + struct drm_atomic_helper_connector_hdmi_priv *priv; + struct drm_modeset_acquire_ctx ctx; + struct drm_connector_state *conn_state; + struct drm_display_info *info; + struct drm_display_mode *preferred; + unsigned long long rate; + struct drm_connector *conn; + struct drm_device *drm; + struct drm_crtc *crtc; + int ret; + + priv = drm_kunit_helper_connector_hdmi_init_with_edid_funcs(test, + BIT(HDMI_COLORSPACE_RGB) | + BIT(HDMI_COLORSPACE_YUV420), + 12, + &dummy_connector_hdmi_funcs, + test_edid_hdmi_4k_rgb_yuv420_dc_max_340mhz); + KUNIT_ASSERT_NOT_NULL(test, priv); + + drm = &priv->drm; + crtc = priv->crtc; + conn = &priv->connector; + info = &conn->display_info; + KUNIT_ASSERT_TRUE(test, info->is_hdmi); + KUNIT_ASSERT_GT(test, info->max_tmds_clock, 0); + KUNIT_ASSERT_TRUE(test, conn->ycbcr_420_allowed); + + preferred = find_preferred_mode(conn); + KUNIT_ASSERT_NOT_NULL(test, preferred); + KUNIT_ASSERT_FALSE(test, preferred->flags & DRM_MODE_FLAG_DBLCLK); + KUNIT_ASSERT_TRUE(test, drm_mode_is_420_also(info, preferred)); + + rate = drm_hdmi_compute_mode_clock(preferred, 8, HDMI_COLORSPACE_RGB); + KUNIT_ASSERT_LT(test, rate, info->max_tmds_clock * 1000); + + rate = drm_hdmi_compute_mode_clock(preferred, 10, HDMI_COLORSPACE_RGB); + KUNIT_ASSERT_GT(test, rate, info->max_tmds_clock * 1000); + + rate = drm_hdmi_compute_mode_clock(preferred, 12, HDMI_COLORSPACE_YUV420); + KUNIT_ASSERT_LT(test, rate, info->max_tmds_clock * 1000); + + drm_modeset_acquire_init(&ctx, 0); + +retry_conn_enable: + ret = drm_kunit_helper_enable_crtc_connector(test, drm, crtc, conn, + preferred, &ctx); + if (ret == -EDEADLK) { + ret = drm_modeset_backoff(&ctx); + if (!ret) + goto retry_conn_enable; + } + KUNIT_EXPECT_EQ(test, ret, 0); + + conn_state = conn->state; + KUNIT_ASSERT_NOT_NULL(test, conn_state); + + KUNIT_EXPECT_EQ(test, conn_state->hdmi.output_bpc, 8); + KUNIT_EXPECT_EQ(test, conn_state->hdmi.output_format, HDMI_COLORSPACE_RGB); + + drm_modeset_drop_locks(&ctx); + drm_modeset_acquire_fini(&ctx); +} + +/* + * Test that if a driver supports only RGB, but the chosen mode can be + * supported by the screen only in YUV420 output format, we end up with + * unsuccessful fallback attempts. + */ +static void drm_test_check_driver_unsupported_fallback_yuv420(struct kunit *test) +{ + struct drm_atomic_helper_connector_hdmi_priv *priv; + struct drm_modeset_acquire_ctx ctx; + struct drm_connector_state *conn_state; + struct drm_crtc_state *crtc_state; + struct drm_atomic_state *state; + struct drm_display_info *info; + struct drm_display_mode *preferred, *yuv420_only_mode; + struct drm_connector *conn; + struct drm_device *drm; + struct drm_crtc *crtc; + int ret; + + priv = drm_kunit_helper_connector_hdmi_init_with_edid_funcs(test, + BIT(HDMI_COLORSPACE_RGB), + 12, + &dummy_connector_hdmi_funcs, + test_edid_hdmi_1080p_rgb_yuv_4k_yuv420_dc_max_200mhz); + KUNIT_ASSERT_NOT_NULL(test, priv); + + drm = &priv->drm; + crtc = priv->crtc; + conn = &priv->connector; + info = &conn->display_info; + KUNIT_ASSERT_TRUE(test, info->is_hdmi); + KUNIT_ASSERT_FALSE(test, conn->ycbcr_420_allowed); + + preferred = find_preferred_mode(conn); + KUNIT_ASSERT_NOT_NULL(test, preferred); + KUNIT_ASSERT_FALSE(test, drm_mode_is_420_also(info, preferred)); + + yuv420_only_mode = drm_kunit_display_mode_from_cea_vic(test, drm, 95); + KUNIT_ASSERT_NOT_NULL(test, yuv420_only_mode); + KUNIT_ASSERT_TRUE(test, drm_mode_is_420_only(info, yuv420_only_mode)); + + drm_modeset_acquire_init(&ctx, 0); + +retry_conn_enable: + ret = drm_kunit_helper_enable_crtc_connector(test, drm, crtc, conn, + preferred, &ctx); + if (ret == -EDEADLK) { + ret = drm_modeset_backoff(&ctx); + if (!ret) + goto retry_conn_enable; + } + KUNIT_EXPECT_EQ(test, ret, 0); + + conn_state = conn->state; + KUNIT_ASSERT_NOT_NULL(test, conn_state); + KUNIT_EXPECT_EQ(test, conn_state->hdmi.output_format, HDMI_COLORSPACE_RGB); + + state = drm_kunit_helper_atomic_state_alloc(test, drm, &ctx); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, state); + +retry_crtc_state: + crtc_state = drm_atomic_get_crtc_state(state, crtc); + if (PTR_ERR(crtc_state) == -EDEADLK) { + drm_atomic_state_clear(state); + ret = drm_modeset_backoff(&ctx); + if (!ret) + goto retry_crtc_state; + } + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, crtc_state); + + ret = drm_atomic_set_mode_for_crtc(crtc_state, yuv420_only_mode); + KUNIT_EXPECT_EQ(test, ret, 0); + + ret = drm_atomic_check_only(state); + if (ret == -EDEADLK) { + drm_atomic_state_clear(state); + ret = drm_modeset_backoff(&ctx); + if (!ret) + goto retry_crtc_state; + } + KUNIT_ASSERT_LT(test, ret, 0); + + drm_modeset_drop_locks(&ctx); + drm_modeset_acquire_fini(&ctx); +} + +/* * Test that if a driver and screen supports RGB and YUV formats, and we * try to set the VIC 1 mode, we end up with 8bpc RGB even if we could * have had a higher bpc. @@ -1321,20 +1638,17 @@ static void drm_test_check_output_bpc_format_vic_1(struct kunit *test) struct drm_crtc *crtc; int ret; - priv = drm_kunit_helper_connector_hdmi_init(test, - BIT(HDMI_COLORSPACE_RGB) | - BIT(HDMI_COLORSPACE_YUV422) | - BIT(HDMI_COLORSPACE_YUV444), - 12); + priv = drm_kunit_helper_connector_hdmi_init_with_edid_funcs(test, + BIT(HDMI_COLORSPACE_RGB) | + BIT(HDMI_COLORSPACE_YUV422) | + BIT(HDMI_COLORSPACE_YUV444), + 12, + &dummy_connector_hdmi_funcs, + test_edid_hdmi_1080p_rgb_yuv_dc_max_200mhz); KUNIT_ASSERT_NOT_NULL(test, priv); drm = &priv->drm; conn = &priv->connector; - ret = set_connector_edid(test, conn, - test_edid_hdmi_1080p_rgb_yuv_dc_max_200mhz, - ARRAY_SIZE(test_edid_hdmi_1080p_rgb_yuv_dc_max_200mhz)); - KUNIT_ASSERT_GT(test, ret, 0); - info = &conn->display_info; KUNIT_ASSERT_TRUE(test, info->is_hdmi); KUNIT_ASSERT_GT(test, info->max_tmds_clock, 0); @@ -1388,19 +1702,16 @@ static void drm_test_check_output_bpc_format_driver_rgb_only(struct kunit *test) struct drm_crtc *crtc; int ret; - priv = drm_kunit_helper_connector_hdmi_init(test, - BIT(HDMI_COLORSPACE_RGB), - 12); + priv = drm_kunit_helper_connector_hdmi_init_with_edid_funcs(test, + BIT(HDMI_COLORSPACE_RGB), + 12, + &dummy_connector_hdmi_funcs, + test_edid_hdmi_1080p_rgb_yuv_dc_max_200mhz); KUNIT_ASSERT_NOT_NULL(test, priv); drm = &priv->drm; crtc = priv->crtc; conn = &priv->connector; - ret = set_connector_edid(test, conn, - test_edid_hdmi_1080p_rgb_yuv_dc_max_200mhz, - ARRAY_SIZE(test_edid_hdmi_1080p_rgb_yuv_dc_max_200mhz)); - KUNIT_ASSERT_GT(test, ret, 0); - info = &conn->display_info; KUNIT_ASSERT_TRUE(test, info->is_hdmi); KUNIT_ASSERT_GT(test, info->max_tmds_clock, 0); @@ -1458,21 +1769,18 @@ static void drm_test_check_output_bpc_format_display_rgb_only(struct kunit *test struct drm_crtc *crtc; int ret; - priv = drm_kunit_helper_connector_hdmi_init(test, - BIT(HDMI_COLORSPACE_RGB) | - BIT(HDMI_COLORSPACE_YUV422) | - BIT(HDMI_COLORSPACE_YUV444), - 12); + priv = drm_kunit_helper_connector_hdmi_init_with_edid_funcs(test, + BIT(HDMI_COLORSPACE_RGB) | + BIT(HDMI_COLORSPACE_YUV422) | + BIT(HDMI_COLORSPACE_YUV444), + 12, + &dummy_connector_hdmi_funcs, + test_edid_hdmi_1080p_rgb_max_200mhz); KUNIT_ASSERT_NOT_NULL(test, priv); drm = &priv->drm; crtc = priv->crtc; conn = &priv->connector; - ret = set_connector_edid(test, conn, - test_edid_hdmi_1080p_rgb_max_200mhz, - ARRAY_SIZE(test_edid_hdmi_1080p_rgb_max_200mhz)); - KUNIT_ASSERT_GT(test, ret, 0); - info = &conn->display_info; KUNIT_ASSERT_TRUE(test, info->is_hdmi); KUNIT_ASSERT_GT(test, info->max_tmds_clock, 0); @@ -1531,19 +1839,16 @@ static void drm_test_check_output_bpc_format_driver_8bpc_only(struct kunit *test struct drm_crtc *crtc; int ret; - priv = drm_kunit_helper_connector_hdmi_init(test, - BIT(HDMI_COLORSPACE_RGB), - 8); + priv = drm_kunit_helper_connector_hdmi_init_with_edid_funcs(test, + BIT(HDMI_COLORSPACE_RGB), + 8, + &dummy_connector_hdmi_funcs, + test_edid_hdmi_1080p_rgb_yuv_dc_max_340mhz); KUNIT_ASSERT_NOT_NULL(test, priv); drm = &priv->drm; crtc = priv->crtc; conn = &priv->connector; - ret = set_connector_edid(test, conn, - test_edid_hdmi_1080p_rgb_yuv_dc_max_340mhz, - ARRAY_SIZE(test_edid_hdmi_1080p_rgb_yuv_dc_max_340mhz)); - KUNIT_ASSERT_GT(test, ret, 0); - info = &conn->display_info; KUNIT_ASSERT_TRUE(test, info->is_hdmi); KUNIT_ASSERT_GT(test, info->max_tmds_clock, 0); @@ -1594,21 +1899,18 @@ static void drm_test_check_output_bpc_format_display_8bpc_only(struct kunit *tes struct drm_crtc *crtc; int ret; - priv = drm_kunit_helper_connector_hdmi_init(test, - BIT(HDMI_COLORSPACE_RGB) | - BIT(HDMI_COLORSPACE_YUV422) | - BIT(HDMI_COLORSPACE_YUV444), - 12); + priv = drm_kunit_helper_connector_hdmi_init_with_edid_funcs(test, + BIT(HDMI_COLORSPACE_RGB) | + BIT(HDMI_COLORSPACE_YUV422) | + BIT(HDMI_COLORSPACE_YUV444), + 12, + &dummy_connector_hdmi_funcs, + test_edid_hdmi_1080p_rgb_max_340mhz); KUNIT_ASSERT_NOT_NULL(test, priv); drm = &priv->drm; crtc = priv->crtc; conn = &priv->connector; - ret = set_connector_edid(test, conn, - test_edid_hdmi_1080p_rgb_max_340mhz, - ARRAY_SIZE(test_edid_hdmi_1080p_rgb_max_340mhz)); - KUNIT_ASSERT_GT(test, ret, 0); - info = &conn->display_info; KUNIT_ASSERT_TRUE(test, info->is_hdmi); KUNIT_ASSERT_GT(test, info->max_tmds_clock, 0); @@ -1704,17 +2006,17 @@ static struct kunit_case drm_atomic_helper_connector_hdmi_check_tests[] = { KUNIT_CASE(drm_test_check_broadcast_rgb_full_cea_mode_vic_1), KUNIT_CASE(drm_test_check_broadcast_rgb_limited_cea_mode), KUNIT_CASE(drm_test_check_broadcast_rgb_limited_cea_mode_vic_1), - /* - * TODO: When we'll have YUV output support, we need to check - * that the limited range is always set to limited no matter - * what the value of Broadcast RGB is. - */ + KUNIT_CASE_PARAM(drm_test_check_broadcast_rgb_cea_mode_yuv420, + check_broadcast_rgb_cea_mode_yuv420_gen_params), KUNIT_CASE(drm_test_check_broadcast_rgb_crtc_mode_changed), KUNIT_CASE(drm_test_check_broadcast_rgb_crtc_mode_not_changed), KUNIT_CASE(drm_test_check_disable_connector), KUNIT_CASE(drm_test_check_hdmi_funcs_reject_rate), - KUNIT_CASE(drm_test_check_max_tmds_rate_bpc_fallback), - KUNIT_CASE(drm_test_check_max_tmds_rate_format_fallback), + KUNIT_CASE(drm_test_check_max_tmds_rate_bpc_fallback_rgb), + KUNIT_CASE(drm_test_check_max_tmds_rate_bpc_fallback_yuv420), + KUNIT_CASE(drm_test_check_max_tmds_rate_bpc_fallback_ignore_yuv422), + KUNIT_CASE(drm_test_check_max_tmds_rate_bpc_fallback_ignore_yuv420), + KUNIT_CASE(drm_test_check_driver_unsupported_fallback_yuv420), KUNIT_CASE(drm_test_check_output_bpc_crtc_mode_changed), KUNIT_CASE(drm_test_check_output_bpc_crtc_mode_not_changed), KUNIT_CASE(drm_test_check_output_bpc_dvi), @@ -1927,28 +2229,20 @@ static void drm_test_check_mode_valid(struct kunit *test) static void drm_test_check_mode_valid_reject_rate(struct kunit *test) { struct drm_atomic_helper_connector_hdmi_priv *priv; - struct drm_connector *conn; struct drm_display_mode *preferred; - int ret; - priv = drm_kunit_helper_connector_hdmi_init_funcs(test, - BIT(HDMI_COLORSPACE_RGB), - 8, - &reject_100_MHz_connector_hdmi_funcs); + priv = drm_kunit_helper_connector_hdmi_init_with_edid_funcs(test, + BIT(HDMI_COLORSPACE_RGB), + 8, + &reject_100mhz_connector_hdmi_funcs, + test_edid_hdmi_1080p_rgb_max_200mhz); KUNIT_ASSERT_NOT_NULL(test, priv); - conn = &priv->connector; - - ret = set_connector_edid(test, conn, - test_edid_hdmi_1080p_rgb_max_200mhz, - ARRAY_SIZE(test_edid_hdmi_1080p_rgb_max_200mhz)); - KUNIT_ASSERT_GT(test, ret, 0); - /* * Unlike the drm_test_check_mode_valid() here 1080p is rejected, but * 480p is allowed. */ - preferred = find_preferred_mode(conn); + preferred = find_preferred_mode(&priv->connector); KUNIT_ASSERT_NOT_NULL(test, preferred); KUNIT_EXPECT_EQ(test, preferred->hdisplay, 640); KUNIT_EXPECT_EQ(test, preferred->vdisplay, 480); @@ -1966,12 +2260,14 @@ static void drm_test_check_mode_valid_reject(struct kunit *test) struct drm_atomic_helper_connector_hdmi_priv *priv; struct drm_connector *conn; struct drm_display_mode *preferred; + unsigned char no_edid[] = {}; int ret; - priv = drm_kunit_helper_connector_hdmi_init_funcs(test, - BIT(HDMI_COLORSPACE_RGB), - 8, - &reject_connector_hdmi_funcs); + priv = drm_kunit_helper_connector_hdmi_init_with_edid_funcs(test, + BIT(HDMI_COLORSPACE_RGB), + 8, + &reject_connector_hdmi_funcs, + no_edid); KUNIT_ASSERT_NOT_NULL(test, priv); conn = &priv->connector; @@ -1996,20 +2292,15 @@ static void drm_test_check_mode_valid_reject_max_clock(struct kunit *test) struct drm_atomic_helper_connector_hdmi_priv *priv; struct drm_connector *conn; struct drm_display_mode *preferred; - int ret; - priv = drm_kunit_helper_connector_hdmi_init(test, - BIT(HDMI_COLORSPACE_RGB), - 8); + priv = drm_kunit_helper_connector_hdmi_init_with_edid_funcs(test, + BIT(HDMI_COLORSPACE_RGB), + 8, + &dummy_connector_hdmi_funcs, + test_edid_hdmi_1080p_rgb_max_100mhz); KUNIT_ASSERT_NOT_NULL(test, priv); conn = &priv->connector; - - ret = set_connector_edid(test, conn, - test_edid_hdmi_1080p_rgb_max_100mhz, - ARRAY_SIZE(test_edid_hdmi_1080p_rgb_max_100mhz)); - KUNIT_ASSERT_GT(test, ret, 0); - KUNIT_ASSERT_EQ(test, conn->display_info.max_tmds_clock, 100 * 1000); preferred = find_preferred_mode(conn); diff --git a/drivers/gpu/drm/tests/drm_kunit_edid.h b/drivers/gpu/drm/tests/drm_kunit_edid.h index 6358397a5d7a..02e2761b3b1f 100644 --- a/drivers/gpu/drm/tests/drm_kunit_edid.h +++ b/drivers/gpu/drm/tests/drm_kunit_edid.h @@ -583,4 +583,236 @@ static const unsigned char test_edid_hdmi_1080p_rgb_yuv_dc_max_340mhz[] = { 0x00, 0x00, 0x00, 0x8c }; +/* + * Max resolution: + * - 1920x1080@60Hz with RGB, YUV444, YUV422 + * - 3840x2160@30Hz with YUV420 only + * Max BPC: 16 for all modes + * Max TMDS clock: 200 MHz + * + * edid-decode (hex): + * + * 00 ff ff ff ff ff ff 00 31 d8 34 00 00 00 00 00 + * ff 23 01 03 80 60 36 78 0f ee 91 a3 54 4c 99 26 + * 0f 50 54 20 00 00 01 01 01 01 01 01 01 01 01 01 + * 01 01 01 01 01 01 02 3a 80 18 71 38 2d 40 58 2c + * 45 00 c0 1c 32 00 00 1e 00 00 00 fc 00 54 65 73 + * 74 20 45 44 49 44 0a 20 20 20 00 00 00 fd 00 18 + * 55 18 5e 11 00 0a 20 20 20 20 20 20 00 00 00 10 + * 00 00 00 00 00 00 00 00 00 00 00 00 00 00 01 bb + * + * 02 03 29 31 42 90 5f 6c 03 0c 00 10 00 78 28 20 + * 00 00 01 03 6d d8 5d c4 01 28 80 07 00 00 00 00 + * 00 00 e3 0f 00 00 e2 0e 5f 00 00 00 00 00 00 00 + * 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + * 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + * 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + * 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + * 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ca + * + * ---------------- + * + * Block 0, Base EDID: + * EDID Structure Version & Revision: 1.3 + * Vendor & Product Identification: + * Manufacturer: LNX + * Model: 52 + * Model year: 2025 + * Basic Display Parameters & Features: + * Digital display + * Maximum image size: 96 cm x 54 cm + * Gamma: 2.20 + * RGB color display + * Default (sRGB) color space is primary color space + * First detailed timing is the preferred timing + * Supports GTF timings within operating range + * Color Characteristics: + * Red : 0.6396, 0.3300 + * Green: 0.2998, 0.5996 + * Blue : 0.1503, 0.0595 + * White: 0.3125, 0.3291 + * Established Timings I & II: + * DMT 0x04: 640x480 59.940476 Hz 4:3 31.469 kHz 25.175000 MHz + * Standard Timings: none + * Detailed Timing Descriptors: + * DTD 1: 1920x1080 60.000000 Hz 16:9 67.500 kHz 148.500000 MHz (960 mm x 540 mm) + * Hfront 88 Hsync 44 Hback 148 Hpol P + * Vfront 4 Vsync 5 Vback 36 Vpol P + * Display Product Name: 'Test EDID' + * Display Range Limits: + * Monitor ranges (GTF): 24-85 Hz V, 24-94 kHz H, max dotclock 170 MHz + * Dummy Descriptor: + * Extension blocks: 1 + * Checksum: 0xbb + * + * ---------------- + * + * Block 1, CTA-861 Extension Block: + * Revision: 3 + * Supports YCbCr 4:4:4 + * Supports YCbCr 4:2:2 + * Native detailed modes: 1 + * Video Data Block: + * VIC 16: 1920x1080 60.000000 Hz 16:9 67.500 kHz 148.500000 MHz (native) + * VIC 95: 3840x2160 30.000000 Hz 16:9 67.500 kHz 297.000000 MHz + * Vendor-Specific Data Block (HDMI), OUI 00-0C-03: + * Source physical address: 1.0.0.0 + * DC_48bit + * DC_36bit + * DC_30bit + * DC_Y444 + * Maximum TMDS clock: 200 MHz + * Extended HDMI video details: + * Vendor-Specific Data Block (HDMI Forum), OUI C4-5D-D8: + * Version: 1 + * Maximum TMDS Character Rate: 200 MHz + * SCDC Present + * Supports 16-bits/component Deep Color 4:2:0 Pixel Encoding + * Supports 12-bits/component Deep Color 4:2:0 Pixel Encoding + * Supports 10-bits/component Deep Color 4:2:0 Pixel Encoding + * YCbCr 4:2:0 Capability Map Data Block: + * Empty Capability Map + * YCbCr 4:2:0 Video Data Block: + * VIC 95: 3840x2160 30.000000 Hz 16:9 67.500 kHz 297.000000 MHz + * Checksum: 0xca + */ +static const unsigned char test_edid_hdmi_1080p_rgb_yuv_4k_yuv420_dc_max_200mhz[] = { + 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x31, 0xd8, 0x34, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xff, 0x23, 0x01, 0x03, 0x80, 0x60, 0x36, 0x78, + 0x0f, 0xee, 0x91, 0xa3, 0x54, 0x4c, 0x99, 0x26, 0x0f, 0x50, 0x54, 0x20, + 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x3a, 0x80, 0x18, 0x71, 0x38, + 0x2d, 0x40, 0x58, 0x2c, 0x45, 0x00, 0xc0, 0x1c, 0x32, 0x00, 0x00, 0x1e, + 0x00, 0x00, 0x00, 0xfc, 0x00, 0x54, 0x65, 0x73, 0x74, 0x20, 0x45, 0x44, + 0x49, 0x44, 0x0a, 0x20, 0x20, 0x20, 0x00, 0x00, 0x00, 0xfd, 0x00, 0x18, + 0x55, 0x18, 0x5e, 0x11, 0x00, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xbb, 0x02, 0x03, 0x29, 0x31, + 0x42, 0x90, 0x5f, 0x6c, 0x03, 0x0c, 0x00, 0x10, 0x00, 0x78, 0x28, 0x20, + 0x00, 0x00, 0x01, 0x03, 0x6d, 0xd8, 0x5d, 0xc4, 0x01, 0x28, 0x80, 0x07, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe3, 0x0f, 0x00, 0x00, 0xe2, 0x0e, + 0x5f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0xca +}; + +/* + * Max resolution: 3840x2160@30Hz with RGB, YUV444, YUV422, YUV420 + * Max BPC: 16 for all modes + * Max TMDS clock: 340 MHz + * + * edid-decode (hex): + * + * 00 ff ff ff ff ff ff 00 31 d8 34 00 00 00 00 00 + * ff 23 01 03 80 60 36 78 0f ee 91 a3 54 4c 99 26 + * 0f 50 54 20 00 00 01 01 01 01 01 01 01 01 01 01 + * 01 01 01 01 01 01 04 74 00 30 f2 70 5a 80 b0 58 + * 8a 00 40 84 63 00 00 1e 00 00 00 fc 00 54 65 73 + * 74 20 45 44 49 44 0a 20 20 20 00 00 00 fd 00 18 + * 55 18 5e 22 00 0a 20 20 20 20 20 20 00 00 00 10 + * 00 00 00 00 00 00 00 00 00 00 00 00 00 00 01 ce + * + * 02 03 27 31 41 5f 6c 03 0c 00 10 00 78 44 20 00 + * 00 01 03 6d d8 5d c4 01 44 80 07 00 00 00 00 00 + * 00 e3 0f 01 00 e1 0e 00 00 00 00 00 00 00 00 00 + * 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + * 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + * 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + * 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + * 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 84 + * + * ---------------- + * + * Block 0, Base EDID: + * EDID Structure Version & Revision: 1.3 + * Vendor & Product Identification: + * Manufacturer: LNX + * Model: 52 + * Model year: 2025 + * Basic Display Parameters & Features: + * Digital display + * Maximum image size: 96 cm x 54 cm + * Gamma: 2.20 + * RGB color display + * Default (sRGB) color space is primary color space + * First detailed timing is the preferred timing + * Supports GTF timings within operating range + * Color Characteristics: + * Red : 0.6396, 0.3300 + * Green: 0.2998, 0.5996 + * Blue : 0.1503, 0.0595 + * White: 0.3125, 0.3291 + * Established Timings I & II: + * DMT 0x04: 640x480 59.940476 Hz 4:3 31.469 kHz 25.175000 MHz + * Standard Timings: none + * Detailed Timing Descriptors: + * DTD 1: 3840x2160 30.000000 Hz 16:9 67.500 kHz 297.000000 MHz (1600 mm x 900 mm) + * Hfront 176 Hsync 88 Hback 296 Hpol P + * Vfront 8 Vsync 10 Vback 72 Vpol P + * Display Product Name: 'Test EDID' + * Display Range Limits: + * Monitor ranges (GTF): 24-85 Hz V, 24-94 kHz H, max dotclock 340 MHz + * Dummy Descriptor: + * Extension blocks: 1 + * Checksum: 0xce + * + * ---------------- + * + * Block 1, CTA-861 Extension Block: + * Revision: 3 + * Supports YCbCr 4:4:4 + * Supports YCbCr 4:2:2 + * Native detailed modes: 1 + * Video Data Block: + * VIC 95: 3840x2160 30.000000 Hz 16:9 67.500 kHz 297.000000 MHz + * Vendor-Specific Data Block (HDMI), OUI 00-0C-03: + * Source physical address: 1.0.0.0 + * DC_48bit + * DC_36bit + * DC_30bit + * DC_Y444 + * Maximum TMDS clock: 340 MHz + * Extended HDMI video details: + * Vendor-Specific Data Block (HDMI Forum), OUI C4-5D-D8: + * Version: 1 + * Maximum TMDS Character Rate: 340 MHz + * SCDC Present + * Supports 16-bits/component Deep Color 4:2:0 Pixel Encoding + * Supports 12-bits/component Deep Color 4:2:0 Pixel Encoding + * Supports 10-bits/component Deep Color 4:2:0 Pixel Encoding + * YCbCr 4:2:0 Capability Map Data Block: + * VIC 95: 3840x2160 30.000000 Hz 16:9 67.500 kHz 297.000000 MHz + * YCbCr 4:2:0 Video Data Block: + * Checksum: 0x84 + */ +static const unsigned char test_edid_hdmi_4k_rgb_yuv420_dc_max_340mhz[] = { + 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x31, 0xd8, 0x34, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xff, 0x23, 0x01, 0x03, 0x80, 0x60, 0x36, 0x78, + 0x0f, 0xee, 0x91, 0xa3, 0x54, 0x4c, 0x99, 0x26, 0x0f, 0x50, 0x54, 0x20, + 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x04, 0x74, 0x00, 0x30, 0xf2, 0x70, + 0x5a, 0x80, 0xb0, 0x58, 0x8a, 0x00, 0x40, 0x84, 0x63, 0x00, 0x00, 0x1e, + 0x00, 0x00, 0x00, 0xfc, 0x00, 0x54, 0x65, 0x73, 0x74, 0x20, 0x45, 0x44, + 0x49, 0x44, 0x0a, 0x20, 0x20, 0x20, 0x00, 0x00, 0x00, 0xfd, 0x00, 0x18, + 0x55, 0x18, 0x5e, 0x22, 0x00, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xce, 0x02, 0x03, 0x27, 0x31, + 0x41, 0x5f, 0x6c, 0x03, 0x0c, 0x00, 0x10, 0x00, 0x78, 0x44, 0x20, 0x00, + 0x00, 0x01, 0x03, 0x6d, 0xd8, 0x5d, 0xc4, 0x01, 0x44, 0x80, 0x07, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xe3, 0x0f, 0x01, 0x00, 0xe1, 0x0e, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x84 +}; + #endif // DRM_KUNIT_EDID_H_ diff --git a/drivers/gpu/drm/tidss/tidss_crtc.c b/drivers/gpu/drm/tidss/tidss_crtc.c index 94f8e3178df5..a2f40a5c7703 100644 --- a/drivers/gpu/drm/tidss/tidss_crtc.c +++ b/drivers/gpu/drm/tidss/tidss_crtc.c @@ -130,7 +130,7 @@ static void tidss_crtc_position_planes(struct tidss_device *tidss, !to_tidss_crtc_state(cstate)->plane_pos_changed) return; - for (layer = 0; layer < tidss->feat->num_planes; layer++) { + for (layer = 0; layer < tidss->feat->num_vids ; layer++) { struct drm_plane_state *pstate; struct drm_plane *plane; bool layer_active = false; @@ -271,7 +271,7 @@ static void tidss_crtc_atomic_disable(struct drm_crtc *crtc, * another videoport, the DSS will report sync lost issues. Disable all * the layers here as a work-around. */ - for (u32 layer = 0; layer < tidss->feat->num_planes; layer++) + for (u32 layer = 0; layer < tidss->feat->num_vids; layer++) dispc_ovr_enable_layer(tidss->dispc, tcrtc->hw_videoport, layer, false); diff --git a/drivers/gpu/drm/tidss/tidss_dispc.c b/drivers/gpu/drm/tidss/tidss_dispc.c index cacb5f3d8085..21363ccbd763 100644 --- a/drivers/gpu/drm/tidss/tidss_dispc.c +++ b/drivers/gpu/drm/tidss/tidss_dispc.c @@ -103,9 +103,16 @@ const struct dispc_features dispc_k2g_feats = { }, }, - .num_planes = 1, - .vid_name = { "vid1" }, - .vid_lite = { false }, + .num_vids = 1, + + .vid_info = { + { + .name = "vid1", + .is_lite = false, + .hw_id = 0, + }, + }, + .vid_order = { 0 }, }; @@ -178,11 +185,22 @@ const struct dispc_features dispc_am65x_feats = { }, }, - .num_planes = 2, + .num_vids = 2, /* note: vid is plane_id 0 and vidl1 is plane_id 1 */ - .vid_name = { "vid", "vidl1" }, - .vid_lite = { false, true, }, - .vid_order = { 1, 0 }, + .vid_info = { + { + .name = "vid", + .is_lite = false, + .hw_id = 0, + }, + { + .name = "vidl1", + .is_lite = true, + .hw_id = 1, + }, + }, + + .vid_order = {1, 0}, }; static const u16 tidss_j721e_common_regs[DISPC_COMMON_REG_TABLE_LEN] = { @@ -267,9 +285,32 @@ const struct dispc_features dispc_j721e_feats = { .gamma_type = TIDSS_GAMMA_10BIT, }, }, - .num_planes = 4, - .vid_name = { "vid1", "vidl1", "vid2", "vidl2" }, - .vid_lite = { 0, 1, 0, 1, }, + + .num_vids = 4, + + .vid_info = { + { + .name = "vid1", + .is_lite = false, + .hw_id = 0, + }, + { + .name = "vidl1", + .is_lite = true, + .hw_id = 1, + }, + { + .name = "vid2", + .is_lite = false, + .hw_id = 2, + }, + { + .name = "vidl2", + .is_lite = true, + .hw_id = 3, + }, + }, + .vid_order = { 1, 3, 0, 2 }, }; @@ -315,11 +356,23 @@ const struct dispc_features dispc_am625_feats = { }, }, - .num_planes = 2, + .num_vids = 2, + /* note: vid is plane_id 0 and vidl1 is plane_id 1 */ - .vid_name = { "vid", "vidl1" }, - .vid_lite = { false, true, }, - .vid_order = { 1, 0 }, + .vid_info = { + { + .name = "vid", + .is_lite = false, + .hw_id = 0, + }, + { + .name = "vidl1", + .is_lite = true, + .hw_id = 1, + } + }, + + .vid_order = {1, 0}, }; const struct dispc_features dispc_am62a7_feats = { @@ -369,11 +422,58 @@ const struct dispc_features dispc_am62a7_feats = { }, }, - .num_planes = 2, - /* note: vid is plane_id 0 and vidl1 is plane_id 1 */ - .vid_name = { "vid", "vidl1" }, - .vid_lite = { false, true, }, - .vid_order = { 1, 0 }, + .num_vids = 2, + + .vid_info = { + { + .name = "vid", + .is_lite = false, + .hw_id = 0, + }, + { + .name = "vidl1", + .is_lite = true, + .hw_id = 1, + } + }, + + .vid_order = {1, 0}, +}; + +const struct dispc_features dispc_am62l_feats = { + .max_pclk_khz = { + [DISPC_VP_DPI] = 165000, + }, + + .subrev = DISPC_AM62L, + + .common = "common", + .common_regs = tidss_am65x_common_regs, + + .num_vps = 1, + .vp_name = { "vp1" }, + .ovr_name = { "ovr1" }, + .vpclk_name = { "vp1" }, + .vp_bus_type = { DISPC_VP_DPI }, + + .vp_feat = { .color = { + .has_ctm = true, + .gamma_size = 256, + .gamma_type = TIDSS_GAMMA_8BIT, + }, + }, + + .num_vids = 1, + + .vid_info = { + { + .name = "vidl1", + .is_lite = true, + .hw_id = 1, + } + }, + + .vid_order = {0}, }; static const u16 *dispc_common_regmap; @@ -734,7 +834,8 @@ static void dispc_k3_vp_write_irqstatus(struct dispc_device *dispc, static dispc_irq_t dispc_k3_vid_read_irqstatus(struct dispc_device *dispc, u32 hw_plane) { - u32 stat = dispc_read(dispc, DISPC_VID_IRQSTATUS(hw_plane)); + u32 hw_id = dispc->feat->vid_info[hw_plane].hw_id; + u32 stat = dispc_read(dispc, DISPC_VID_IRQSTATUS(hw_id)); return dispc_vid_irq_from_raw(stat, hw_plane); } @@ -742,9 +843,10 @@ static dispc_irq_t dispc_k3_vid_read_irqstatus(struct dispc_device *dispc, static void dispc_k3_vid_write_irqstatus(struct dispc_device *dispc, u32 hw_plane, dispc_irq_t vidstat) { + u32 hw_id = dispc->feat->vid_info[hw_plane].hw_id; u32 stat = dispc_vid_irq_to_raw(vidstat, hw_plane); - dispc_write(dispc, DISPC_VID_IRQSTATUS(hw_plane), stat); + dispc_write(dispc, DISPC_VID_IRQSTATUS(hw_id), stat); } static dispc_irq_t dispc_k3_vp_read_irqenable(struct dispc_device *dispc, @@ -766,7 +868,8 @@ static void dispc_k3_vp_set_irqenable(struct dispc_device *dispc, static dispc_irq_t dispc_k3_vid_read_irqenable(struct dispc_device *dispc, u32 hw_plane) { - u32 stat = dispc_read(dispc, DISPC_VID_IRQENABLE(hw_plane)); + u32 hw_id = dispc->feat->vid_info[hw_plane].hw_id; + u32 stat = dispc_read(dispc, DISPC_VID_IRQENABLE(hw_id)); return dispc_vid_irq_from_raw(stat, hw_plane); } @@ -774,9 +877,10 @@ static dispc_irq_t dispc_k3_vid_read_irqenable(struct dispc_device *dispc, static void dispc_k3_vid_set_irqenable(struct dispc_device *dispc, u32 hw_plane, dispc_irq_t vidstat) { + u32 hw_id = dispc->feat->vid_info[hw_plane].hw_id; u32 stat = dispc_vid_irq_to_raw(vidstat, hw_plane); - dispc_write(dispc, DISPC_VID_IRQENABLE(hw_plane), stat); + dispc_write(dispc, DISPC_VID_IRQENABLE(hw_id), stat); } static @@ -788,7 +892,8 @@ void dispc_k3_clear_irqstatus(struct dispc_device *dispc, dispc_irq_t clearmask) if (clearmask & DSS_IRQ_VP_MASK(i)) dispc_k3_vp_write_irqstatus(dispc, i, clearmask); } - for (i = 0; i < dispc->feat->num_planes; ++i) { + + for (i = 0; i < dispc->feat->num_vids; ++i) { if (clearmask & DSS_IRQ_PLANE_MASK(i)) dispc_k3_vid_write_irqstatus(dispc, i, clearmask); } @@ -809,7 +914,7 @@ dispc_irq_t dispc_k3_read_and_clear_irqstatus(struct dispc_device *dispc) for (i = 0; i < dispc->feat->num_vps; ++i) status |= dispc_k3_vp_read_irqstatus(dispc, i); - for (i = 0; i < dispc->feat->num_planes; ++i) + for (i = 0; i < dispc->feat->num_vids; ++i) status |= dispc_k3_vid_read_irqstatus(dispc, i); dispc_k3_clear_irqstatus(dispc, status); @@ -825,7 +930,7 @@ static dispc_irq_t dispc_k3_read_irqenable(struct dispc_device *dispc) for (i = 0; i < dispc->feat->num_vps; ++i) enable |= dispc_k3_vp_read_irqenable(dispc, i); - for (i = 0; i < dispc->feat->num_planes; ++i) + for (i = 0; i < dispc->feat->num_vids; ++i) enable |= dispc_k3_vid_read_irqenable(dispc, i); return enable; @@ -851,12 +956,15 @@ static void dispc_k3_set_irqenable(struct dispc_device *dispc, main_disable |= BIT(i); /* VP IRQ */ } - for (i = 0; i < dispc->feat->num_planes; ++i) { + for (i = 0; i < dispc->feat->num_vids; ++i) { + u32 hw_id = dispc->feat->vid_info[i].hw_id; + dispc_k3_vid_set_irqenable(dispc, i, mask); + if (mask & DSS_IRQ_PLANE_MASK(i)) - main_enable |= BIT(i + 4); /* VID IRQ */ + main_enable |= BIT(hw_id + 4); /* VID IRQ */ else - main_disable |= BIT(i + 4); /* VID IRQ */ + main_disable |= BIT(hw_id + 4); /* VID IRQ */ } if (main_enable) @@ -879,6 +987,7 @@ dispc_irq_t dispc_read_and_clear_irqstatus(struct dispc_device *dispc) return dispc_k2g_read_and_clear_irqstatus(dispc); case DISPC_AM625: case DISPC_AM62A7: + case DISPC_AM62L: case DISPC_AM65X: case DISPC_J721E: return dispc_k3_read_and_clear_irqstatus(dispc); @@ -896,6 +1005,7 @@ void dispc_set_irqenable(struct dispc_device *dispc, dispc_irq_t mask) break; case DISPC_AM625: case DISPC_AM62A7: + case DISPC_AM62L: case DISPC_AM65X: case DISPC_J721E: dispc_k3_set_irqenable(dispc, mask); @@ -1358,8 +1468,10 @@ static void dispc_am65x_ovr_set_plane(struct dispc_device *dispc, u32 hw_plane, u32 hw_videoport, u32 x, u32 y, u32 layer) { + u32 hw_id = dispc->feat->vid_info[hw_plane].hw_id; + OVR_REG_FLD_MOD(dispc, hw_videoport, DISPC_OVR_ATTRIBUTES(layer), - hw_plane, 4, 1); + hw_id, 4, 1); OVR_REG_FLD_MOD(dispc, hw_videoport, DISPC_OVR_ATTRIBUTES(layer), x, 17, 6); OVR_REG_FLD_MOD(dispc, hw_videoport, DISPC_OVR_ATTRIBUTES(layer), @@ -1370,8 +1482,10 @@ static void dispc_j721e_ovr_set_plane(struct dispc_device *dispc, u32 hw_plane, u32 hw_videoport, u32 x, u32 y, u32 layer) { + u32 hw_id = dispc->feat->vid_info[hw_plane].hw_id; + OVR_REG_FLD_MOD(dispc, hw_videoport, DISPC_OVR_ATTRIBUTES(layer), - hw_plane, 4, 1); + hw_id, 4, 1); OVR_REG_FLD_MOD(dispc, hw_videoport, DISPC_OVR_ATTRIBUTES2(layer), x, 13, 0); OVR_REG_FLD_MOD(dispc, hw_videoport, DISPC_OVR_ATTRIBUTES2(layer), @@ -1388,6 +1502,7 @@ void dispc_ovr_set_plane(struct dispc_device *dispc, u32 hw_plane, break; case DISPC_AM625: case DISPC_AM62A7: + case DISPC_AM62L: case DISPC_AM65X: dispc_am65x_ovr_set_plane(dispc, hw_plane, hw_videoport, x, y, layer); @@ -2025,7 +2140,7 @@ int dispc_plane_check(struct dispc_device *dispc, u32 hw_plane, const struct drm_plane_state *state, u32 hw_videoport) { - bool lite = dispc->feat->vid_lite[hw_plane]; + bool lite = dispc->feat->vid_info[hw_plane].is_lite; u32 fourcc = state->fb->format->format; bool need_scaling = state->src_w >> 16 != state->crtc_w || state->src_h >> 16 != state->crtc_h; @@ -2096,7 +2211,7 @@ void dispc_plane_setup(struct dispc_device *dispc, u32 hw_plane, const struct drm_plane_state *state, u32 hw_videoport) { - bool lite = dispc->feat->vid_lite[hw_plane]; + bool lite = dispc->feat->vid_info[hw_plane].is_lite; u32 fourcc = state->fb->format->format; u16 cpp = state->fb->format->cpp[0]; u32 fb_width = state->fb->pitches[0] / cpp; @@ -2210,7 +2325,7 @@ static void dispc_k2g_plane_init(struct dispc_device *dispc) /* MFLAG_START = MFLAGNORMALSTARTMODE */ REG_FLD_MOD(dispc, DISPC_GLOBAL_MFLAG_ATTRIBUTE, 0, 6, 6); - for (hw_plane = 0; hw_plane < dispc->feat->num_planes; hw_plane++) { + for (hw_plane = 0; hw_plane < dispc->feat->num_vids; hw_plane++) { u32 size = dispc_vid_get_fifo_size(dispc, hw_plane); u32 thr_low, thr_high; u32 mflag_low, mflag_high; @@ -2226,7 +2341,7 @@ static void dispc_k2g_plane_init(struct dispc_device *dispc) dev_dbg(dispc->dev, "%s: bufsize %u, buf_threshold %u/%u, mflag threshold %u/%u preload %u\n", - dispc->feat->vid_name[hw_plane], + dispc->feat->vid_info[hw_plane].name, size, thr_high, thr_low, mflag_high, mflag_low, @@ -2265,7 +2380,7 @@ static void dispc_k3_plane_init(struct dispc_device *dispc) /* MFLAG_START = MFLAGNORMALSTARTMODE */ REG_FLD_MOD(dispc, DISPC_GLOBAL_MFLAG_ATTRIBUTE, 0, 6, 6); - for (hw_plane = 0; hw_plane < dispc->feat->num_planes; hw_plane++) { + for (hw_plane = 0; hw_plane < dispc->feat->num_vids; hw_plane++) { u32 size = dispc_vid_get_fifo_size(dispc, hw_plane); u32 thr_low, thr_high; u32 mflag_low, mflag_high; @@ -2281,7 +2396,7 @@ static void dispc_k3_plane_init(struct dispc_device *dispc) dev_dbg(dispc->dev, "%s: bufsize %u, buf_threshold %u/%u, mflag threshold %u/%u preload %u\n", - dispc->feat->vid_name[hw_plane], + dispc->feat->vid_info[hw_plane].name, size, thr_high, thr_low, mflag_high, mflag_low, @@ -2308,6 +2423,7 @@ static void dispc_plane_init(struct dispc_device *dispc) break; case DISPC_AM625: case DISPC_AM62A7: + case DISPC_AM62L: case DISPC_AM65X: case DISPC_J721E: dispc_k3_plane_init(dispc); @@ -2416,6 +2532,7 @@ static void dispc_vp_write_gamma_table(struct dispc_device *dispc, break; case DISPC_AM625: case DISPC_AM62A7: + case DISPC_AM62L: case DISPC_AM65X: dispc_am65x_vp_write_gamma_table(dispc, hw_videoport); break; @@ -2898,8 +3015,8 @@ int dispc_init(struct tidss_device *tidss) if (r) return r; - for (i = 0; i < dispc->feat->num_planes; i++) { - r = dispc_iomap_resource(pdev, dispc->feat->vid_name[i], + for (i = 0; i < dispc->feat->num_vids; i++) { + r = dispc_iomap_resource(pdev, dispc->feat->vid_info[i].name, &dispc->base_vid[i]); if (r) return r; diff --git a/drivers/gpu/drm/tidss/tidss_dispc.h b/drivers/gpu/drm/tidss/tidss_dispc.h index 086327d51a90..28958514b8f5 100644 --- a/drivers/gpu/drm/tidss/tidss_dispc.h +++ b/drivers/gpu/drm/tidss/tidss_dispc.h @@ -46,6 +46,12 @@ struct dispc_features_scaling { u32 xinc_max; }; +struct dispc_vid_info { + const char *name; /* Should match dt reg names */ + u32 hw_id; + bool is_lite; +}; + struct dispc_errata { bool i2000; /* DSS Does Not Support YUV Pixel Data Formats */ }; @@ -61,6 +67,7 @@ enum dispc_vp_bus_type { enum dispc_dss_subrevision { DISPC_K2G, DISPC_AM625, + DISPC_AM62L, DISPC_AM62A7, DISPC_AM65X, DISPC_J721E, @@ -82,15 +89,15 @@ struct dispc_features { const char *vpclk_name[TIDSS_MAX_PORTS]; /* Should match dt clk names */ const enum dispc_vp_bus_type vp_bus_type[TIDSS_MAX_PORTS]; struct tidss_vp_feat vp_feat; - u32 num_planes; - const char *vid_name[TIDSS_MAX_PLANES]; /* Should match dt reg names */ - bool vid_lite[TIDSS_MAX_PLANES]; + u32 num_vids; + struct dispc_vid_info vid_info[TIDSS_MAX_PLANES]; u32 vid_order[TIDSS_MAX_PLANES]; }; extern const struct dispc_features dispc_k2g_feats; extern const struct dispc_features dispc_am625_feats; extern const struct dispc_features dispc_am62a7_feats; +extern const struct dispc_features dispc_am62l_feats; extern const struct dispc_features dispc_am65x_feats; extern const struct dispc_features dispc_j721e_feats; diff --git a/drivers/gpu/drm/tidss/tidss_drv.c b/drivers/gpu/drm/tidss/tidss_drv.c index d4652e8cc28c..f2a4f659f574 100644 --- a/drivers/gpu/drm/tidss/tidss_drv.c +++ b/drivers/gpu/drm/tidss/tidss_drv.c @@ -242,6 +242,7 @@ static const struct of_device_id tidss_of_table[] = { { .compatible = "ti,k2g-dss", .data = &dispc_k2g_feats, }, { .compatible = "ti,am625-dss", .data = &dispc_am625_feats, }, { .compatible = "ti,am62a7-dss", .data = &dispc_am62a7_feats, }, + { .compatible = "ti,am62l-dss", .data = &dispc_am62l_feats, }, { .compatible = "ti,am65x-dss", .data = &dispc_am65x_feats, }, { .compatible = "ti,j721e-dss", .data = &dispc_j721e_feats, }, { } diff --git a/drivers/gpu/drm/tidss/tidss_kms.c b/drivers/gpu/drm/tidss/tidss_kms.c index f371518f8697..19432c08ec6b 100644 --- a/drivers/gpu/drm/tidss/tidss_kms.c +++ b/drivers/gpu/drm/tidss/tidss_kms.c @@ -115,7 +115,7 @@ static int tidss_dispc_modeset_init(struct tidss_device *tidss) const struct dispc_features *feat = tidss->feat; u32 max_vps = feat->num_vps; - u32 max_planes = feat->num_planes; + u32 max_planes = feat->num_vids; struct pipe pipes[TIDSS_MAX_PORTS]; u32 num_pipes = 0; diff --git a/drivers/gpu/drm/tidss/tidss_plane.c b/drivers/gpu/drm/tidss/tidss_plane.c index 719412e6c346..142ae81951a0 100644 --- a/drivers/gpu/drm/tidss/tidss_plane.c +++ b/drivers/gpu/drm/tidss/tidss_plane.c @@ -200,7 +200,7 @@ struct tidss_plane *tidss_plane_create(struct tidss_device *tidss, struct tidss_plane *tplane; enum drm_plane_type type; u32 possible_crtcs; - u32 num_planes = tidss->feat->num_planes; + u32 num_planes = tidss->feat->num_vids; u32 color_encodings = (BIT(DRM_COLOR_YCBCR_BT601) | BIT(DRM_COLOR_YCBCR_BT709)); u32 color_ranges = (BIT(DRM_COLOR_YCBCR_FULL_RANGE) | diff --git a/drivers/gpu/drm/ttm/ttm_bo.c b/drivers/gpu/drm/ttm/ttm_bo.c index 08a23ab037cb..0f874f1e2526 100644 --- a/drivers/gpu/drm/ttm/ttm_bo.c +++ b/drivers/gpu/drm/ttm/ttm_bo.c @@ -46,6 +46,7 @@ #include <linux/dma-resv.h> #include "ttm_module.h" +#include "ttm_bo_internal.h" static void ttm_bo_mem_space_debug(struct ttm_buffer_object *bo, struct ttm_placement *placement) diff --git a/drivers/gpu/drm/ttm/ttm_bo_internal.h b/drivers/gpu/drm/ttm/ttm_bo_internal.h new file mode 100644 index 000000000000..9d8b747a34db --- /dev/null +++ b/drivers/gpu/drm/ttm/ttm_bo_internal.h @@ -0,0 +1,58 @@ +/* + * Copyright 2018 Advanced Micro Devices, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * + */ + +#ifndef _TTM_BO_INTERNAL_H_ +#define _TTM_BO_INTERNAL_H_ + +#include <drm/ttm/ttm_bo.h> + +/** + * ttm_bo_get - reference a struct ttm_buffer_object + * + * @bo: The buffer object. + */ +static inline void ttm_bo_get(struct ttm_buffer_object *bo) +{ + kref_get(&bo->kref); +} + +/** + * ttm_bo_get_unless_zero - reference a struct ttm_buffer_object unless + * its refcount has already reached zero. + * @bo: The buffer object. + * + * Used to reference a TTM buffer object in lookups where the object is removed + * from the lookup structure during the destructor and for RCU lookups. + * + * Returns: @bo if the referencing was successful, NULL otherwise. + */ +static inline __must_check struct ttm_buffer_object * +ttm_bo_get_unless_zero(struct ttm_buffer_object *bo) +{ + if (!kref_get_unless_zero(&bo->kref)) + return NULL; + return bo; +} + +#endif diff --git a/drivers/gpu/drm/ttm/ttm_bo_util.c b/drivers/gpu/drm/ttm/ttm_bo_util.c index 15cab9bda17f..b78365dc1fed 100644 --- a/drivers/gpu/drm/ttm/ttm_bo_util.c +++ b/drivers/gpu/drm/ttm/ttm_bo_util.c @@ -37,6 +37,8 @@ #include <drm/drm_cache.h> +#include "ttm_bo_internal.h" + struct ttm_transfer_obj { struct ttm_buffer_object base; struct ttm_buffer_object *bo; diff --git a/drivers/gpu/drm/ttm/ttm_device.c b/drivers/gpu/drm/ttm/ttm_device.c index 02e797fd1891..36075772555a 100644 --- a/drivers/gpu/drm/ttm/ttm_device.c +++ b/drivers/gpu/drm/ttm/ttm_device.c @@ -36,6 +36,7 @@ #include <drm/ttm/ttm_placement.h> #include "ttm_module.h" +#include "ttm_bo_internal.h" /* * ttm_global_mutex - protecting the global state diff --git a/drivers/gpu/drm/ttm/ttm_pool.c b/drivers/gpu/drm/ttm/ttm_pool.c index c2ea865be657..a0436971b6a6 100644 --- a/drivers/gpu/drm/ttm/ttm_pool.c +++ b/drivers/gpu/drm/ttm/ttm_pool.c @@ -1132,7 +1132,9 @@ void ttm_pool_fini(struct ttm_pool *pool) } EXPORT_SYMBOL(ttm_pool_fini); -/* As long as pages are available make sure to release at least one */ +/* Free average pool number of pages. */ +#define TTM_SHRINKER_BATCH ((1 << (MAX_PAGE_ORDER / 2)) * NR_PAGE_ORDERS) + static unsigned long ttm_pool_shrinker_scan(struct shrinker *shrink, struct shrink_control *sc) { @@ -1140,9 +1142,12 @@ static unsigned long ttm_pool_shrinker_scan(struct shrinker *shrink, do num_freed += ttm_pool_shrink(); - while (!num_freed && atomic_long_read(&allocated_pages)); + while (num_freed < sc->nr_to_scan && + atomic_long_read(&allocated_pages)); + + sc->nr_scanned = num_freed; - return num_freed; + return num_freed ?: SHRINK_STOP; } /* Return the number of pages available or SHRINK_EMPTY if we have none */ @@ -1233,7 +1238,7 @@ int ttm_pool_debugfs(struct ttm_pool *pool, struct seq_file *m) { unsigned int i; - if (!pool->use_dma_alloc) { + if (!pool->use_dma_alloc && pool->nid == NUMA_NO_NODE) { seq_puts(m, "unused\n"); return 0; } @@ -1242,7 +1247,12 @@ int ttm_pool_debugfs(struct ttm_pool *pool, struct seq_file *m) spin_lock(&shrinker_lock); for (i = 0; i < TTM_NUM_CACHING_TYPES; ++i) { - seq_puts(m, "DMA "); + if (!ttm_pool_select_type(pool, i, 0)) + continue; + if (pool->use_dma_alloc) + seq_puts(m, "DMA "); + else + seq_printf(m, "N%d ", pool->nid); switch (i) { case ttm_cached: seq_puts(m, "\t:"); @@ -1266,10 +1276,15 @@ EXPORT_SYMBOL(ttm_pool_debugfs); /* Test the shrinker functions and dump the result */ static int ttm_pool_debugfs_shrink_show(struct seq_file *m, void *data) { - struct shrink_control sc = { .gfp_mask = GFP_NOFS }; + struct shrink_control sc = { + .gfp_mask = GFP_NOFS, + .nr_to_scan = TTM_SHRINKER_BATCH, + }; + unsigned long count; fs_reclaim_acquire(GFP_KERNEL); - seq_printf(m, "%lu/%lu\n", ttm_pool_shrinker_count(mm_shrinker, &sc), + count = ttm_pool_shrinker_count(mm_shrinker, &sc); + seq_printf(m, "%lu/%lu\n", count, ttm_pool_shrinker_scan(mm_shrinker, &sc)); fs_reclaim_release(GFP_KERNEL); @@ -1324,6 +1339,7 @@ int ttm_pool_mgr_init(unsigned long num_pages) mm_shrinker->count_objects = ttm_pool_shrinker_count; mm_shrinker->scan_objects = ttm_pool_shrinker_scan; + mm_shrinker->batch = TTM_SHRINKER_BATCH; mm_shrinker->seeks = 1; shrinker_register(mm_shrinker); diff --git a/drivers/gpu/drm/ttm/ttm_resource.c b/drivers/gpu/drm/ttm/ttm_resource.c index 769b0ca9be47..006202fcd3c2 100644 --- a/drivers/gpu/drm/ttm/ttm_resource.c +++ b/drivers/gpu/drm/ttm/ttm_resource.c @@ -557,6 +557,9 @@ int ttm_resource_manager_evict_all(struct ttm_device *bdev, cond_resched(); } while (!ret); + if (ret && ret != -ENOENT) + return ret; + spin_lock(&man->move_lock); fence = dma_fence_get(man->move); spin_unlock(&man->move_lock); diff --git a/drivers/gpu/drm/udl/udl_drv.c b/drivers/gpu/drm/udl/udl_drv.c index 1922988625eb..ce5ae7cacb90 100644 --- a/drivers/gpu/drm/udl/udl_drv.c +++ b/drivers/gpu/drm/udl/udl_drv.c @@ -57,7 +57,7 @@ static const struct drm_driver driver = { /* GEM hooks */ .fops = &udl_driver_fops, - DRM_GEM_SHMEM_DRIVER_OPS, + DRM_GEM_SHMEM_DRIVER_OPS_NO_MAP_SGT, DRM_FBDEV_SHMEM_DRIVER_OPS, .name = DRIVER_NAME, diff --git a/drivers/gpu/drm/v3d/v3d_submit.c b/drivers/gpu/drm/v3d/v3d_submit.c index 4ff5de46fb22..5171ffe9012d 100644 --- a/drivers/gpu/drm/v3d/v3d_submit.c +++ b/drivers/gpu/drm/v3d/v3d_submit.c @@ -169,7 +169,7 @@ v3d_job_init(struct v3d_dev *v3d, struct drm_file *file_priv, job->file = file_priv; ret = drm_sched_job_init(&job->base, &v3d_priv->sched_entity[queue], - 1, v3d_priv); + 1, v3d_priv, file_priv->client_id); if (ret) return ret; diff --git a/drivers/gpu/drm/vc4/vc4_hdmi.c b/drivers/gpu/drm/vc4/vc4_hdmi.c index 163d092bd973..07c91b450f93 100644 --- a/drivers/gpu/drm/vc4/vc4_hdmi.c +++ b/drivers/gpu/drm/vc4/vc4_hdmi.c @@ -560,6 +560,12 @@ static int vc4_hdmi_connector_init(struct drm_device *dev, if (ret) return ret; + ret = drm_connector_hdmi_audio_init(connector, dev->dev, + &vc4_hdmi_audio_funcs, + 8, 0, false, -1); + if (ret) + return ret; + drm_connector_helper_add(connector, &vc4_hdmi_connector_helper_funcs); /* @@ -2286,7 +2292,7 @@ static int vc4_hdmi_audio_init(struct vc4_hdmi *vc4_hdmi) } ret = drm_connector_hdmi_audio_init(&vc4_hdmi->connector, dev, - &vc4_hdmi_audio_funcs, 8, false, + &vc4_hdmi_audio_funcs, 8, 0, false, -1); if (ret) return ret; diff --git a/drivers/gpu/drm/virtio/virtgpu_drv.h b/drivers/gpu/drm/virtio/virtgpu_drv.h index f17660a71a3e..f7def8b42068 100644 --- a/drivers/gpu/drm/virtio/virtgpu_drv.h +++ b/drivers/gpu/drm/virtio/virtgpu_drv.h @@ -88,6 +88,7 @@ struct virtio_gpu_object_params { struct virtio_gpu_object { struct drm_gem_shmem_object base; + struct dma_buf *dma_buf; struct sg_table *sgt; uint32_t hw_res_handle; bool dumb; diff --git a/drivers/gpu/drm/virtio/virtgpu_prime.c b/drivers/gpu/drm/virtio/virtgpu_prime.c index 1118a0250279..722cde5e2d86 100644 --- a/drivers/gpu/drm/virtio/virtgpu_prime.c +++ b/drivers/gpu/drm/virtio/virtgpu_prime.c @@ -206,7 +206,7 @@ static void virtgpu_dma_buf_free_obj(struct drm_gem_object *obj) struct virtio_gpu_device *vgdev = obj->dev->dev_private; if (drm_gem_is_imported(obj)) { - struct dma_buf *dmabuf = obj->dma_buf; + struct dma_buf *dmabuf = bo->dma_buf; dma_resv_lock(dmabuf->resv, NULL); virtgpu_dma_buf_unmap(bo); @@ -332,6 +332,7 @@ struct drm_gem_object *virtgpu_gem_prime_import(struct drm_device *dev, obj->import_attach = attach; get_dma_buf(buf); + bo->dma_buf = buf; ret = virtgpu_dma_buf_init_obj(dev, bo, attach); if (ret < 0) diff --git a/drivers/gpu/drm/vkms/tests/Makefile b/drivers/gpu/drm/vkms/tests/Makefile index 9ded37b67a46..0ee077942ae2 100644 --- a/drivers/gpu/drm/vkms/tests/Makefile +++ b/drivers/gpu/drm/vkms/tests/Makefile @@ -1,3 +1,4 @@ # SPDX-License-Identifier: GPL-2.0-only obj-$(CONFIG_DRM_VKMS_KUNIT_TEST) += vkms_config_test.o +obj-$(CONFIG_DRM_VKMS_KUNIT_TESTS) += vkms_format_test.o diff --git a/drivers/gpu/drm/vkms/tests/vkms_format_test.c b/drivers/gpu/drm/vkms/tests/vkms_format_test.c new file mode 100644 index 000000000000..2e1daef94831 --- /dev/null +++ b/drivers/gpu/drm/vkms/tests/vkms_format_test.c @@ -0,0 +1,280 @@ +// SPDX-License-Identifier: GPL-2.0+ + +#include <kunit/test.h> + +#include <drm/drm_fixed.h> +#include <drm/drm_fourcc.h> + +#include "../../drm_crtc_internal.h" + +#include "../vkms_formats.h" + +#define TEST_BUFF_SIZE 50 + +MODULE_IMPORT_NS("EXPORTED_FOR_KUNIT_TESTING"); + +/** + * struct pixel_yuv_u8 - Internal representation of a pixel color. + * @y: Luma value, stored in 8 bits, without padding, using + * machine endianness + * @u: Blue difference chroma value, stored in 8 bits, without padding, using + * machine endianness + * @v: Red difference chroma value, stored in 8 bits, without padding, using + * machine endianness + */ +struct pixel_yuv_u8 { + u8 y, u, v; +}; + +/* + * struct yuv_u8_to_argb_u16_case - Reference values to test the color + * conversions in VKMS between YUV to ARGB + * + * @encoding: Encoding used to convert RGB to YUV + * @range: Range used to convert RGB to YUV + * @n_colors: Count of test colors in this case + * @format_pair.name: Name used for this color conversion, used to + * clarify the test results + * @format_pair.rgb: RGB color tested + * @format_pair.yuv: Same color as @format_pair.rgb, but converted to + * YUV using @encoding and @range. + */ +struct yuv_u8_to_argb_u16_case { + enum drm_color_encoding encoding; + enum drm_color_range range; + size_t n_colors; + struct format_pair { + char *name; + struct pixel_yuv_u8 yuv; + struct pixel_argb_u16 argb; + } colors[TEST_BUFF_SIZE]; +}; + +/* + * The YUV color representation were acquired via the colour python framework. + * Below are the function calls used for generating each case. + * + * For more information got to the docs: + * https://colour.readthedocs.io/en/master/generated/colour.RGB_to_YCbCr.html + */ +static struct yuv_u8_to_argb_u16_case yuv_u8_to_argb_u16_cases[] = { + /* + * colour.RGB_to_YCbCr(<rgb color in 16 bit form>, + * K=colour.WEIGHTS_YCBCR["ITU-R BT.601"], + * in_bits = 16, + * in_legal = False, + * in_int = True, + * out_bits = 8, + * out_legal = False, + * out_int = True) + * + * Tests cases for color conversion generated by converting RGB + * values to YUV BT601 full range using the ITU-R BT.601 weights. + */ + { + .encoding = DRM_COLOR_YCBCR_BT601, + .range = DRM_COLOR_YCBCR_FULL_RANGE, + .n_colors = 6, + .colors = { + { "white", { 0xff, 0x80, 0x80 }, { 0xffff, 0xffff, 0xffff, 0xffff }}, + { "gray", { 0x80, 0x80, 0x80 }, { 0xffff, 0x8080, 0x8080, 0x8080 }}, + { "black", { 0x00, 0x80, 0x80 }, { 0xffff, 0x0000, 0x0000, 0x0000 }}, + { "red", { 0x4c, 0x55, 0xff }, { 0xffff, 0xffff, 0x0000, 0x0000 }}, + { "green", { 0x96, 0x2c, 0x15 }, { 0xffff, 0x0000, 0xffff, 0x0000 }}, + { "blue", { 0x1d, 0xff, 0x6b }, { 0xffff, 0x0000, 0x0000, 0xffff }}, + }, + }, + /* + * colour.RGB_to_YCbCr(<rgb color in 16 bit form>, + * K=colour.WEIGHTS_YCBCR["ITU-R BT.601"], + * in_bits = 16, + * in_legal = False, + * in_int = True, + * out_bits = 8, + * out_legal = True, + * out_int = True) + * Tests cases for color conversion generated by converting RGB + * values to YUV BT601 limited range using the ITU-R BT.601 weights. + */ + { + .encoding = DRM_COLOR_YCBCR_BT601, + .range = DRM_COLOR_YCBCR_LIMITED_RANGE, + .n_colors = 6, + .colors = { + { "white", { 0xeb, 0x80, 0x80 }, { 0xffff, 0xffff, 0xffff, 0xffff }}, + { "gray", { 0x7e, 0x80, 0x80 }, { 0xffff, 0x8080, 0x8080, 0x8080 }}, + { "black", { 0x10, 0x80, 0x80 }, { 0xffff, 0x0000, 0x0000, 0x0000 }}, + { "red", { 0x51, 0x5a, 0xf0 }, { 0xffff, 0xffff, 0x0000, 0x0000 }}, + { "green", { 0x91, 0x36, 0x22 }, { 0xffff, 0x0000, 0xffff, 0x0000 }}, + { "blue", { 0x29, 0xf0, 0x6e }, { 0xffff, 0x0000, 0x0000, 0xffff }}, + }, + }, + /* + * colour.RGB_to_YCbCr(<rgb color in 16 bit form>, + * K=colour.WEIGHTS_YCBCR["ITU-R BT.709"], + * in_bits = 16, + * in_legal = False, + * in_int = True, + * out_bits = 8, + * out_legal = False, + * out_int = True) + * Tests cases for color conversion generated by converting RGB + * values to YUV BT709 full range using the ITU-R BT.709 weights. + */ + { + .encoding = DRM_COLOR_YCBCR_BT709, + .range = DRM_COLOR_YCBCR_FULL_RANGE, + .n_colors = 6, + .colors = { + { "white", { 0xff, 0x80, 0x80 }, { 0xffff, 0xffff, 0xffff, 0xffff }}, + { "gray", { 0x80, 0x80, 0x80 }, { 0xffff, 0x8080, 0x8080, 0x8080 }}, + { "black", { 0x00, 0x80, 0x80 }, { 0xffff, 0x0000, 0x0000, 0x0000 }}, + { "red", { 0x36, 0x63, 0xff }, { 0xffff, 0xffff, 0x0000, 0x0000 }}, + { "green", { 0xb6, 0x1e, 0x0c }, { 0xffff, 0x0000, 0xffff, 0x0000 }}, + { "blue", { 0x12, 0xff, 0x74 }, { 0xffff, 0x0000, 0x0000, 0xffff }}, + }, + }, + /* + * colour.RGB_to_YCbCr(<rgb color in 16 bit form>, + * K=colour.WEIGHTS_YCBCR["ITU-R BT.709"], + * in_bits = 16, + * int_legal = False, + * in_int = True, + * out_bits = 8, + * out_legal = True, + * out_int = True) + * Tests cases for color conversion generated by converting RGB + * values to YUV BT709 limited range using the ITU-R BT.709 weights. + */ + { + .encoding = DRM_COLOR_YCBCR_BT709, + .range = DRM_COLOR_YCBCR_LIMITED_RANGE, + .n_colors = 6, + .colors = { + { "white", { 0xeb, 0x80, 0x80 }, { 0xffff, 0xffff, 0xffff, 0xffff }}, + { "gray", { 0x7e, 0x80, 0x80 }, { 0xffff, 0x8080, 0x8080, 0x8080 }}, + { "black", { 0x10, 0x80, 0x80 }, { 0xffff, 0x0000, 0x0000, 0x0000 }}, + { "red", { 0x3f, 0x66, 0xf0 }, { 0xffff, 0xffff, 0x0000, 0x0000 }}, + { "green", { 0xad, 0x2a, 0x1a }, { 0xffff, 0x0000, 0xffff, 0x0000 }}, + { "blue", { 0x20, 0xf0, 0x76 }, { 0xffff, 0x0000, 0x0000, 0xffff }}, + }, + }, + /* + * colour.RGB_to_YCbCr(<rgb color in 16 bit form>, + * K=colour.WEIGHTS_YCBCR["ITU-R BT.2020"], + * in_bits = 16, + * in_legal = False, + * in_int = True, + * out_bits = 8, + * out_legal = False, + * out_int = True) + * Tests cases for color conversion generated by converting RGB + * values to YUV BT2020 full range using the ITU-R BT.2020 weights. + */ + { + .encoding = DRM_COLOR_YCBCR_BT2020, + .range = DRM_COLOR_YCBCR_FULL_RANGE, + .n_colors = 6, + .colors = { + { "white", { 0xff, 0x80, 0x80 }, { 0xffff, 0xffff, 0xffff, 0xffff }}, + { "gray", { 0x80, 0x80, 0x80 }, { 0xffff, 0x8080, 0x8080, 0x8080 }}, + { "black", { 0x00, 0x80, 0x80 }, { 0xffff, 0x0000, 0x0000, 0x0000 }}, + { "red", { 0x43, 0x5c, 0xff }, { 0xffff, 0xffff, 0x0000, 0x0000 }}, + { "green", { 0xad, 0x24, 0x0b }, { 0xffff, 0x0000, 0xffff, 0x0000 }}, + { "blue", { 0x0f, 0xff, 0x76 }, { 0xffff, 0x0000, 0x0000, 0xffff }}, + }, + }, + /* + * colour.RGB_to_YCbCr(<rgb color in 16 bit form>, + * K=colour.WEIGHTS_YCBCR["ITU-R BT.2020"], + * in_bits = 16, + * in_legal = False, + * in_int = True, + * out_bits = 8, + * out_legal = True, + * out_int = True) + * Tests cases for color conversion generated by converting RGB + * values to YUV BT2020 limited range using the ITU-R BT.2020 weights. + */ + { + .encoding = DRM_COLOR_YCBCR_BT2020, + .range = DRM_COLOR_YCBCR_LIMITED_RANGE, + .n_colors = 6, + .colors = { + { "white", { 0xeb, 0x80, 0x80 }, { 0xffff, 0xffff, 0xffff, 0xffff }}, + { "gray", { 0x7e, 0x80, 0x80 }, { 0xffff, 0x8080, 0x8080, 0x8080 }}, + { "black", { 0x10, 0x80, 0x80 }, { 0xffff, 0x0000, 0x0000, 0x0000 }}, + { "red", { 0x4a, 0x61, 0xf0 }, { 0xffff, 0xffff, 0x0000, 0x0000 }}, + { "green", { 0xa4, 0x2f, 0x19 }, { 0xffff, 0x0000, 0xffff, 0x0000 }}, + { "blue", { 0x1d, 0xf0, 0x77 }, { 0xffff, 0x0000, 0x0000, 0xffff }}, + }, + }, +}; + +/* + * vkms_format_test_yuv_u8_to_argb_u16 - Testing the conversion between YUV + * colors to ARGB colors in VKMS + * + * This test will use the functions get_conversion_matrix_to_argb_u16 and + * argb_u16_from_yuv888 to convert YUV colors (stored in + * yuv_u8_to_argb_u16_cases) into ARGB colors. + * + * The conversion between YUV and RGB is not totally reversible, so there may be + * some difference between the expected value and the result. + * In addition, there may be some rounding error as the input color is 8 bits + * and output color is 16 bits. + */ +static void vkms_format_test_yuv_u8_to_argb_u16(struct kunit *test) +{ + const struct yuv_u8_to_argb_u16_case *param = test->param_value; + struct pixel_argb_u16 argb; + + for (size_t i = 0; i < param->n_colors; i++) { + const struct format_pair *color = ¶m->colors[i]; + struct conversion_matrix matrix; + + get_conversion_matrix_to_argb_u16 + (DRM_FORMAT_NV12, param->encoding, param->range, &matrix); + + argb = argb_u16_from_yuv888(color->yuv.y, color->yuv.u, color->yuv.v, &matrix); + + KUNIT_EXPECT_LE_MSG(test, abs_diff(argb.a, color->argb.a), 0x1ff, + "On the A channel of the color %s expected 0x%04x, got 0x%04x", + color->name, color->argb.a, argb.a); + KUNIT_EXPECT_LE_MSG(test, abs_diff(argb.r, color->argb.r), 0x1ff, + "On the R channel of the color %s expected 0x%04x, got 0x%04x", + color->name, color->argb.r, argb.r); + KUNIT_EXPECT_LE_MSG(test, abs_diff(argb.g, color->argb.g), 0x1ff, + "On the G channel of the color %s expected 0x%04x, got 0x%04x", + color->name, color->argb.g, argb.g); + KUNIT_EXPECT_LE_MSG(test, abs_diff(argb.b, color->argb.b), 0x1ff, + "On the B channel of the color %s expected 0x%04x, got 0x%04x", + color->name, color->argb.b, argb.b); + } +} + +static void vkms_format_test_yuv_u8_to_argb_u16_case_desc(struct yuv_u8_to_argb_u16_case *t, + char *desc) +{ + snprintf(desc, KUNIT_PARAM_DESC_SIZE, "%s - %s", + drm_get_color_encoding_name(t->encoding), drm_get_color_range_name(t->range)); +} + +KUNIT_ARRAY_PARAM(yuv_u8_to_argb_u16, yuv_u8_to_argb_u16_cases, + vkms_format_test_yuv_u8_to_argb_u16_case_desc +); + +static struct kunit_case vkms_format_test_cases[] = { + KUNIT_CASE_PARAM(vkms_format_test_yuv_u8_to_argb_u16, yuv_u8_to_argb_u16_gen_params), + {} +}; + +static struct kunit_suite vkms_format_test_suite = { + .name = "vkms-format", + .test_cases = vkms_format_test_cases, +}; + +kunit_test_suite(vkms_format_test_suite); + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("Kunit test for vkms format conversion"); diff --git a/drivers/gpu/drm/vkms/vkms_drv.h b/drivers/gpu/drm/vkms/vkms_drv.h index a74a7fc3a056..68b5e3fce455 100644 --- a/drivers/gpu/drm/vkms/vkms_drv.h +++ b/drivers/gpu/drm/vkms/vkms_drv.h @@ -45,6 +45,23 @@ struct vkms_frame_info { unsigned int rotation; }; +/** + * struct pixel_argb_u16 - Internal representation of a pixel color. + * @a: Alpha component value, stored in 16 bits, without padding, using + * machine endianness + * @r: Red component value, stored in 16 bits, without padding, using + * machine endianness + * @g: Green component value, stored in 16 bits, without padding, using + * machine endianness + * @b: Blue component value, stored in 16 bits, without padding, using + * machine endianness + * + * The goal of this structure is to keep enough precision to ensure + * correct composition results in VKMS and simplifying color + * manipulation by splitting each component into its own field. + * Caution: the byte ordering of this structure is machine-dependent, + * you can't cast it directly to AR48 or xR48. + */ struct pixel_argb_u16 { u16 a, r, g, b; }; @@ -103,16 +120,34 @@ typedef void (*pixel_read_line_t)(const struct vkms_plane_state *plane, int x_st struct pixel_argb_u16 out_pixel[]); /** + * struct conversion_matrix - Matrix to use for a specific encoding and range + * + * @matrix: Conversion matrix from yuv to rgb. The matrix is stored in a row-major manner and is + * used to compute rgb values from yuv values: + * [[r],[g],[b]] = @matrix * [[y],[u],[v]] + * OR for yvu formats: + * [[r],[g],[b]] = @matrix * [[y],[v],[u]] + * The values of the matrix are signed fixed-point values with 32 bits fractional part. + * @y_offset: Offset to apply on the y value. + */ +struct conversion_matrix { + s64 matrix[3][3]; + int y_offset; +}; + +/** * struct vkms_plane_state - Driver specific plane state * @base: base plane state * @frame_info: data required for composing computation * @pixel_read_line: function to read a pixel line in this plane. The creator of a * struct vkms_plane_state must ensure that this pointer is valid + * @conversion_matrix: matrix used for yuv formats to convert to rgb */ struct vkms_plane_state { struct drm_shadow_plane_state base; struct vkms_frame_info *frame_info; pixel_read_line_t pixel_read_line; + struct conversion_matrix conversion_matrix; }; struct vkms_plane { diff --git a/drivers/gpu/drm/vkms/vkms_formats.c b/drivers/gpu/drm/vkms/vkms_formats.c index 30a64ecca87c..6d0227c6635a 100644 --- a/drivers/gpu/drm/vkms/vkms_formats.c +++ b/drivers/gpu/drm/vkms/vkms_formats.c @@ -7,6 +7,8 @@ #include <drm/drm_rect.h> #include <drm/drm_fixed.h> +#include <kunit/visibility.h> + #include "vkms_formats.h" /** @@ -140,6 +142,51 @@ static void packed_pixels_addr_1x1(const struct vkms_frame_info *frame_info, *addr = (u8 *)frame_info->map[0].vaddr + offset; } +/** + * get_subsampling() - Get the subsampling divisor value on a specific direction + * + * @format: format to extarct the subsampling from + * @direction: direction of the subsampling requested + */ +static int get_subsampling(const struct drm_format_info *format, + enum pixel_read_direction direction) +{ + switch (direction) { + case READ_BOTTOM_TO_TOP: + case READ_TOP_TO_BOTTOM: + return format->vsub; + case READ_RIGHT_TO_LEFT: + case READ_LEFT_TO_RIGHT: + return format->hsub; + } + WARN_ONCE(true, "Invalid direction for pixel reading: %d\n", direction); + return 1; +} + +/** + * get_subsampling_offset() - An offset for keeping the chroma siting consistent regardless of + * x_start and y_start values + * + * @direction: direction of the reading to properly compute this offset + * @x_start: x coordinate of the starting point of the readed line + * @y_start: y coordinate of the starting point of the readed line + */ +static int get_subsampling_offset(enum pixel_read_direction direction, int x_start, int y_start) +{ + switch (direction) { + case READ_BOTTOM_TO_TOP: + return -y_start - 1; + case READ_TOP_TO_BOTTOM: + return y_start; + case READ_RIGHT_TO_LEFT: + return -x_start - 1; + case READ_LEFT_TO_RIGHT: + return x_start; + } + WARN_ONCE(true, "Invalid direction for pixel reading: %d\n", direction); + return 0; +} + /* * The following functions take pixel data (a, r, g, b, pixel, ...) and convert them to * &struct pixel_argb_u16 @@ -202,11 +249,54 @@ static struct pixel_argb_u16 argb_u16_from_RGB565(const __le16 *pixel) return out_pixel; } +static struct pixel_argb_u16 argb_u16_from_gray8(u8 gray) +{ + return argb_u16_from_u8888(255, gray, gray, gray); +} + +static struct pixel_argb_u16 argb_u16_from_grayu16(u16 gray) +{ + return argb_u16_from_u16161616(0xFFFF, gray, gray, gray); +} + +VISIBLE_IF_KUNIT struct pixel_argb_u16 argb_u16_from_yuv888(u8 y, u8 channel_1, u8 channel_2, + const struct conversion_matrix *matrix) +{ + u16 r, g, b; + s64 fp_y, fp_channel_1, fp_channel_2; + s64 fp_r, fp_g, fp_b; + + fp_y = drm_int2fixp(((int)y - matrix->y_offset) * 257); + fp_channel_1 = drm_int2fixp(((int)channel_1 - 128) * 257); + fp_channel_2 = drm_int2fixp(((int)channel_2 - 128) * 257); + + fp_r = drm_fixp_mul(matrix->matrix[0][0], fp_y) + + drm_fixp_mul(matrix->matrix[0][1], fp_channel_1) + + drm_fixp_mul(matrix->matrix[0][2], fp_channel_2); + fp_g = drm_fixp_mul(matrix->matrix[1][0], fp_y) + + drm_fixp_mul(matrix->matrix[1][1], fp_channel_1) + + drm_fixp_mul(matrix->matrix[1][2], fp_channel_2); + fp_b = drm_fixp_mul(matrix->matrix[2][0], fp_y) + + drm_fixp_mul(matrix->matrix[2][1], fp_channel_1) + + drm_fixp_mul(matrix->matrix[2][2], fp_channel_2); + + fp_r = drm_fixp2int_round(fp_r); + fp_g = drm_fixp2int_round(fp_g); + fp_b = drm_fixp2int_round(fp_b); + + r = clamp(fp_r, 0, 0xffff); + g = clamp(fp_g, 0, 0xffff); + b = clamp(fp_b, 0, 0xffff); + + return argb_u16_from_u16161616(0xffff, r, g, b); +} +EXPORT_SYMBOL_IF_KUNIT(argb_u16_from_yuv888); + /* * The following functions are read_line function for each pixel format supported by VKMS. * * They read a line starting at the point @x_start,@y_start following the @direction. The result - * is stored in @out_pixel and in the format ARGB16161616. + * is stored in @out_pixel and in a 64 bits format, see struct pixel_argb_u16. * * These functions are very repetitive, but the innermost pixel loops must be kept inside these * functions for performance reasons. Some benchmarking was done in [1] where having the innermost @@ -215,6 +305,96 @@ static struct pixel_argb_u16 argb_u16_from_RGB565(const __le16 *pixel) * [1]: https://lore.kernel.org/dri-devel/d258c8dc-78e9-4509-9037-a98f7f33b3a3@riseup.net/ */ +static void Rx_read_line(const struct vkms_plane_state *plane, int x_start, + int y_start, enum pixel_read_direction direction, int count, + struct pixel_argb_u16 out_pixel[]) +{ + struct pixel_argb_u16 *end = out_pixel + count; + int bits_per_pixel = drm_format_info_bpp(plane->frame_info->fb->format, 0); + u8 *src_pixels; + int rem_x, rem_y; + + WARN_ONCE(drm_format_info_block_height(plane->frame_info->fb->format, 0) != 1, + "%s() only support formats with block_h == 1", __func__); + + packed_pixels_addr(plane->frame_info, x_start, y_start, 0, &src_pixels, &rem_x, &rem_y); + int bit_offset = (8 - bits_per_pixel) - rem_x * bits_per_pixel; + int step = get_block_step_bytes(plane->frame_info->fb, direction, 0); + int mask = (0x1 << bits_per_pixel) - 1; + int lum_per_level = 0xFFFF / mask; + + if (direction == READ_LEFT_TO_RIGHT || direction == READ_RIGHT_TO_LEFT) { + int restart_bit_offset; + int step_bit_offset; + + if (direction == READ_LEFT_TO_RIGHT) { + restart_bit_offset = 8 - bits_per_pixel; + step_bit_offset = -bits_per_pixel; + } else { + restart_bit_offset = 0; + step_bit_offset = bits_per_pixel; + } + + while (out_pixel < end) { + u8 val = ((*src_pixels) >> bit_offset) & mask; + + *out_pixel = argb_u16_from_grayu16((int)val * lum_per_level); + + bit_offset += step_bit_offset; + if (bit_offset < 0 || 8 <= bit_offset) { + bit_offset = restart_bit_offset; + src_pixels += step; + } + out_pixel += 1; + } + } else if (direction == READ_TOP_TO_BOTTOM || direction == READ_BOTTOM_TO_TOP) { + while (out_pixel < end) { + u8 val = (*src_pixels >> bit_offset) & mask; + *out_pixel = argb_u16_from_grayu16((int)val * lum_per_level); + src_pixels += step; + out_pixel += 1; + } + } +} + +static void R1_read_line(const struct vkms_plane_state *plane, int x_start, + int y_start, enum pixel_read_direction direction, int count, + struct pixel_argb_u16 out_pixel[]) +{ + Rx_read_line(plane, x_start, y_start, direction, count, out_pixel); +} + +static void R2_read_line(const struct vkms_plane_state *plane, int x_start, + int y_start, enum pixel_read_direction direction, int count, + struct pixel_argb_u16 out_pixel[]) +{ + Rx_read_line(plane, x_start, y_start, direction, count, out_pixel); +} + +static void R4_read_line(const struct vkms_plane_state *plane, int x_start, + int y_start, enum pixel_read_direction direction, int count, + struct pixel_argb_u16 out_pixel[]) +{ + Rx_read_line(plane, x_start, y_start, direction, count, out_pixel); +} + +static void R8_read_line(const struct vkms_plane_state *plane, int x_start, + int y_start, enum pixel_read_direction direction, int count, + struct pixel_argb_u16 out_pixel[]) +{ + struct pixel_argb_u16 *end = out_pixel + count; + u8 *src_pixels; + int step = get_block_step_bytes(plane->frame_info->fb, direction, 0); + + packed_pixels_addr_1x1(plane->frame_info, x_start, y_start, 0, &src_pixels); + + while (out_pixel < end) { + *out_pixel = argb_u16_from_gray8(*src_pixels); + src_pixels += step; + out_pixel += 1; + } +} + static void ARGB8888_read_line(const struct vkms_plane_state *plane, int x_start, int y_start, enum pixel_read_direction direction, int count, struct pixel_argb_u16 out_pixel[]) @@ -332,6 +512,92 @@ static void RGB565_read_line(const struct vkms_plane_state *plane, int x_start, } /* + * This callback can be used for YUV formats where U and V values are + * stored in the same plane (often called semi-planar formats). It will + * correctly handle subsampling as described in the drm_format_info of the plane. + * + * The conversion matrix stored in the @plane is used to: + * - Apply the correct color range and encoding + * - Convert YUV and YVU with the same function (a column swap is needed when setting up + * plane->conversion_matrix) + */ +static void semi_planar_yuv_read_line(const struct vkms_plane_state *plane, int x_start, + int y_start, enum pixel_read_direction direction, int count, + struct pixel_argb_u16 out_pixel[]) +{ + u8 *y_plane; + u8 *uv_plane; + + packed_pixels_addr_1x1(plane->frame_info, x_start, y_start, 0, + &y_plane); + packed_pixels_addr_1x1(plane->frame_info, + x_start / plane->frame_info->fb->format->hsub, + y_start / plane->frame_info->fb->format->vsub, 1, + &uv_plane); + int step_y = get_block_step_bytes(plane->frame_info->fb, direction, 0); + int step_uv = get_block_step_bytes(plane->frame_info->fb, direction, 1); + int subsampling = get_subsampling(plane->frame_info->fb->format, direction); + int subsampling_offset = get_subsampling_offset(direction, x_start, y_start); + const struct conversion_matrix *conversion_matrix = &plane->conversion_matrix; + + for (int i = 0; i < count; i++) { + *out_pixel = argb_u16_from_yuv888(y_plane[0], uv_plane[0], uv_plane[1], + conversion_matrix); + out_pixel += 1; + y_plane += step_y; + if ((i + subsampling_offset + 1) % subsampling == 0) + uv_plane += step_uv; + } +} + +/* + * This callback can be used for YUV format where each color component is + * stored in a different plane (often called planar formats). It will + * correctly handle subsampling as described in the drm_format_info of the plane. + * + * The conversion matrix stored in the @plane is used to: + * - Apply the correct color range and encoding + * - Convert YUV and YVU with the same function (a column swap is needed when setting up + * plane->conversion_matrix) + */ +static void planar_yuv_read_line(const struct vkms_plane_state *plane, int x_start, + int y_start, enum pixel_read_direction direction, int count, + struct pixel_argb_u16 out_pixel[]) +{ + u8 *y_plane; + u8 *channel_1_plane; + u8 *channel_2_plane; + + packed_pixels_addr_1x1(plane->frame_info, x_start, y_start, 0, + &y_plane); + packed_pixels_addr_1x1(plane->frame_info, + x_start / plane->frame_info->fb->format->hsub, + y_start / plane->frame_info->fb->format->vsub, 1, + &channel_1_plane); + packed_pixels_addr_1x1(plane->frame_info, + x_start / plane->frame_info->fb->format->hsub, + y_start / plane->frame_info->fb->format->vsub, 2, + &channel_2_plane); + int step_y = get_block_step_bytes(plane->frame_info->fb, direction, 0); + int step_channel_1 = get_block_step_bytes(plane->frame_info->fb, direction, 1); + int step_channel_2 = get_block_step_bytes(plane->frame_info->fb, direction, 2); + int subsampling = get_subsampling(plane->frame_info->fb->format, direction); + int subsampling_offset = get_subsampling_offset(direction, x_start, y_start); + const struct conversion_matrix *conversion_matrix = &plane->conversion_matrix; + + for (int i = 0; i < count; i++) { + *out_pixel = argb_u16_from_yuv888(*y_plane, *channel_1_plane, *channel_2_plane, + conversion_matrix); + out_pixel += 1; + y_plane += step_y; + if ((i + subsampling_offset + 1) % subsampling == 0) { + channel_1_plane += step_channel_1; + channel_2_plane += step_channel_2; + } + } +} + +/* * The following functions take one &struct pixel_argb_u16 and convert it to a specific format. * The result is stored in @out_pixel. * @@ -456,6 +722,28 @@ pixel_read_line_t get_pixel_read_line_function(u32 format) return &XRGB16161616_read_line; case DRM_FORMAT_RGB565: return &RGB565_read_line; + case DRM_FORMAT_NV12: + case DRM_FORMAT_NV16: + case DRM_FORMAT_NV24: + case DRM_FORMAT_NV21: + case DRM_FORMAT_NV61: + case DRM_FORMAT_NV42: + return &semi_planar_yuv_read_line; + case DRM_FORMAT_YUV420: + case DRM_FORMAT_YUV422: + case DRM_FORMAT_YUV444: + case DRM_FORMAT_YVU420: + case DRM_FORMAT_YVU422: + case DRM_FORMAT_YVU444: + return &planar_yuv_read_line; + case DRM_FORMAT_R1: + return &R1_read_line; + case DRM_FORMAT_R2: + return &R2_read_line; + case DRM_FORMAT_R4: + return &R4_read_line; + case DRM_FORMAT_R8: + return &R8_read_line; default: /* * This is a bug in vkms_plane_atomic_check(). All the supported @@ -469,6 +757,183 @@ pixel_read_line_t get_pixel_read_line_function(u32 format) } } +/* + * Those matrices were generated using the colour python framework + * + * Below are the function calls used to generate each matrix, go to + * https://colour.readthedocs.io/en/develop/generated/colour.matrix_YCbCr.html + * for more info: + * + * numpy.around(colour.matrix_YCbCr(K=colour.WEIGHTS_YCBCR["ITU-R BT.601"], + * is_legal = False, + * bits = 8) * 2**32).astype(int) + */ +static const struct conversion_matrix no_operation = { + .matrix = { + { 4294967296, 0, 0, }, + { 0, 4294967296, 0, }, + { 0, 0, 4294967296, }, + }, + .y_offset = 0, +}; + +static const struct conversion_matrix yuv_bt601_full = { + .matrix = { + { 4294967296, 0, 6021544149 }, + { 4294967296, -1478054095, -3067191994 }, + { 4294967296, 7610682049, 0 }, + }, + .y_offset = 0, +}; + +/* + * numpy.around(colour.matrix_YCbCr(K=colour.WEIGHTS_YCBCR["ITU-R BT.601"], + * is_legal = True, + * bits = 8) * 2**32).astype(int) + */ +static const struct conversion_matrix yuv_bt601_limited = { + .matrix = { + { 5020601039, 0, 6881764740 }, + { 5020601039, -1689204679, -3505362278 }, + { 5020601039, 8697922339, 0 }, + }, + .y_offset = 16, +}; + +/* + * numpy.around(colour.matrix_YCbCr(K=colour.WEIGHTS_YCBCR["ITU-R BT.709"], + * is_legal = False, + * bits = 8) * 2**32).astype(int) + */ +static const struct conversion_matrix yuv_bt709_full = { + .matrix = { + { 4294967296, 0, 6763714498 }, + { 4294967296, -804551626, -2010578443 }, + { 4294967296, 7969741314, 0 }, + }, + .y_offset = 0, +}; + +/* + * numpy.around(colour.matrix_YCbCr(K=colour.WEIGHTS_YCBCR["ITU-R BT.709"], + * is_legal = True, + * bits = 8) * 2**32).astype(int) + */ +static const struct conversion_matrix yuv_bt709_limited = { + .matrix = { + { 5020601039, 0, 7729959424 }, + { 5020601039, -919487572, -2297803934 }, + { 5020601039, 9108275786, 0 }, + }, + .y_offset = 16, +}; + +/* + * numpy.around(colour.matrix_YCbCr(K=colour.WEIGHTS_YCBCR["ITU-R BT.2020"], + * is_legal = False, + * bits = 8) * 2**32).astype(int) + */ +static const struct conversion_matrix yuv_bt2020_full = { + .matrix = { + { 4294967296, 0, 6333358775 }, + { 4294967296, -706750298, -2453942994 }, + { 4294967296, 8080551471, 0 }, + }, + .y_offset = 0, +}; + +/* + * numpy.around(colour.matrix_YCbCr(K=colour.WEIGHTS_YCBCR["ITU-R BT.2020"], + * is_legal = True, + * bits = 8) * 2**32).astype(int) + */ +static const struct conversion_matrix yuv_bt2020_limited = { + .matrix = { + { 5020601039, 0, 7238124312 }, + { 5020601039, -807714626, -2804506279 }, + { 5020601039, 9234915964, 0 }, + }, + .y_offset = 16, +}; + +/** + * swap_uv_columns() - Swap u and v column of a given matrix + * + * @matrix: Matrix in which column are swapped + */ +static void swap_uv_columns(struct conversion_matrix *matrix) +{ + swap(matrix->matrix[0][2], matrix->matrix[0][1]); + swap(matrix->matrix[1][2], matrix->matrix[1][1]); + swap(matrix->matrix[2][2], matrix->matrix[2][1]); +} + +/** + * get_conversion_matrix_to_argb_u16() - Retrieve the correct yuv to rgb conversion matrix for a + * given encoding and range. + * + * @format: DRM_FORMAT_* value for which to obtain a conversion function (see [drm_fourcc.h]) + * @encoding: DRM_COLOR_* value for which to obtain a conversion matrix + * @range: DRM_COLOR_*_RANGE value for which to obtain a conversion matrix + * @matrix: Pointer to store the value into + */ +void get_conversion_matrix_to_argb_u16(u32 format, + enum drm_color_encoding encoding, + enum drm_color_range range, + struct conversion_matrix *matrix) +{ + const struct conversion_matrix *matrix_to_copy; + bool limited_range; + + switch (range) { + case DRM_COLOR_YCBCR_LIMITED_RANGE: + limited_range = true; + break; + case DRM_COLOR_YCBCR_FULL_RANGE: + limited_range = false; + break; + case DRM_COLOR_RANGE_MAX: + limited_range = false; + WARN_ONCE(true, "The requested range is not supported."); + break; + } + + switch (encoding) { + case DRM_COLOR_YCBCR_BT601: + matrix_to_copy = limited_range ? &yuv_bt601_limited : + &yuv_bt601_full; + break; + case DRM_COLOR_YCBCR_BT709: + matrix_to_copy = limited_range ? &yuv_bt709_limited : + &yuv_bt709_full; + break; + case DRM_COLOR_YCBCR_BT2020: + matrix_to_copy = limited_range ? &yuv_bt2020_limited : + &yuv_bt2020_full; + break; + case DRM_COLOR_ENCODING_MAX: + matrix_to_copy = &no_operation; + WARN_ONCE(true, "The requested encoding is not supported."); + break; + } + + memcpy(matrix, matrix_to_copy, sizeof(*matrix_to_copy)); + + switch (format) { + case DRM_FORMAT_YVU420: + case DRM_FORMAT_YVU422: + case DRM_FORMAT_YVU444: + case DRM_FORMAT_NV21: + case DRM_FORMAT_NV61: + case DRM_FORMAT_NV42: + swap_uv_columns(matrix); + break; + default: + break; + } +} +EXPORT_SYMBOL(get_conversion_matrix_to_argb_u16); + /** * get_pixel_write_function() - Retrieve the correct write_pixel function for a specific format. * The returned pointer is NULL for unsupported pixel formats. The caller must ensure that the diff --git a/drivers/gpu/drm/vkms/vkms_formats.h b/drivers/gpu/drm/vkms/vkms_formats.h index 8d2bef95ff79..b4fe62ab9c65 100644 --- a/drivers/gpu/drm/vkms/vkms_formats.h +++ b/drivers/gpu/drm/vkms/vkms_formats.h @@ -9,4 +9,13 @@ pixel_read_line_t get_pixel_read_line_function(u32 format); pixel_write_t get_pixel_write_function(u32 format); +void get_conversion_matrix_to_argb_u16(u32 format, enum drm_color_encoding encoding, + enum drm_color_range range, + struct conversion_matrix *matrix); + +#if IS_ENABLED(CONFIG_KUNIT) +struct pixel_argb_u16 argb_u16_from_yuv888(u8 y, u8 channel_1, u8 channel_2, + const struct conversion_matrix *matrix); +#endif + #endif /* _VKMS_FORMATS_H_ */ diff --git a/drivers/gpu/drm/vkms/vkms_plane.c b/drivers/gpu/drm/vkms/vkms_plane.c index e34f8c7f83c3..e3fdd161d0f0 100644 --- a/drivers/gpu/drm/vkms/vkms_plane.c +++ b/drivers/gpu/drm/vkms/vkms_plane.c @@ -18,7 +18,23 @@ static const u32 vkms_formats[] = { DRM_FORMAT_ABGR8888, DRM_FORMAT_XRGB16161616, DRM_FORMAT_ARGB16161616, - DRM_FORMAT_RGB565 + DRM_FORMAT_RGB565, + DRM_FORMAT_NV12, + DRM_FORMAT_NV16, + DRM_FORMAT_NV24, + DRM_FORMAT_NV21, + DRM_FORMAT_NV61, + DRM_FORMAT_NV42, + DRM_FORMAT_YUV420, + DRM_FORMAT_YUV422, + DRM_FORMAT_YUV444, + DRM_FORMAT_YVU420, + DRM_FORMAT_YVU422, + DRM_FORMAT_YVU444, + DRM_FORMAT_R1, + DRM_FORMAT_R2, + DRM_FORMAT_R4, + DRM_FORMAT_R8, }; static struct drm_plane_state * @@ -119,6 +135,8 @@ static void vkms_plane_atomic_update(struct drm_plane *plane, frame_info->rotation = new_state->rotation; vkms_plane_state->pixel_read_line = get_pixel_read_line_function(fmt); + get_conversion_matrix_to_argb_u16(fmt, new_state->color_encoding, new_state->color_range, + &vkms_plane_state->conversion_matrix); } static int vkms_plane_atomic_check(struct drm_plane *plane, @@ -205,5 +223,14 @@ struct vkms_plane *vkms_plane_init(struct vkms_device *vkmsdev, drm_plane_create_rotation_property(&plane->base, DRM_MODE_ROTATE_0, DRM_MODE_ROTATE_MASK | DRM_MODE_REFLECT_MASK); + drm_plane_create_color_properties(&plane->base, + BIT(DRM_COLOR_YCBCR_BT601) | + BIT(DRM_COLOR_YCBCR_BT709) | + BIT(DRM_COLOR_YCBCR_BT2020), + BIT(DRM_COLOR_YCBCR_LIMITED_RANGE) | + BIT(DRM_COLOR_YCBCR_FULL_RANGE), + DRM_COLOR_YCBCR_BT601, + DRM_COLOR_YCBCR_FULL_RANGE); + return plane; } diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_drv.c b/drivers/gpu/drm/vmwgfx/vmwgfx_drv.c index 0695a342b1ef..37b832e552a4 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_drv.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_drv.c @@ -713,7 +713,7 @@ static int vmw_setup_pci_resources(struct vmw_private *dev, pci_set_master(pdev); - ret = pci_request_regions(pdev, "vmwgfx probe"); + ret = pcim_request_all_regions(pdev, "vmwgfx probe"); if (ret) return ret; @@ -733,7 +733,6 @@ static int vmw_setup_pci_resources(struct vmw_private *dev, if (!dev->rmmio) { drm_err(&dev->drm, "Failed mapping registers mmio memory.\n"); - pci_release_regions(pdev); return -ENOMEM; } } else if (pci_id == VMWGFX_PCI_ID_SVGA2) { @@ -754,11 +753,9 @@ static int vmw_setup_pci_resources(struct vmw_private *dev, if (IS_ERR(dev->fifo_mem)) { drm_err(&dev->drm, "Failed mapping FIFO memory.\n"); - pci_release_regions(pdev); return PTR_ERR(dev->fifo_mem); } } else { - pci_release_regions(pdev); return -EINVAL; } @@ -836,7 +833,6 @@ static int vmw_driver_load(struct vmw_private *dev_priv, u32 pci_id) int ret; enum vmw_res_type i; bool refuse_dma = false; - struct pci_dev *pdev = to_pci_dev(dev_priv->drm.dev); vmw_sw_context_init(dev_priv); @@ -852,7 +848,7 @@ static int vmw_driver_load(struct vmw_private *dev_priv, u32 pci_id) return ret; ret = vmw_detect_version(dev_priv); if (ret) - goto out_no_pci_or_version; + return ret; for (i = vmw_res_context; i < vmw_res_max; ++i) { @@ -1152,15 +1148,13 @@ out_err0: if (dev_priv->ctx.staged_bindings) vmw_binding_state_free(dev_priv->ctx.staged_bindings); -out_no_pci_or_version: - pci_release_regions(pdev); + return ret; } static void vmw_driver_unload(struct drm_device *dev) { struct vmw_private *dev_priv = vmw_priv(dev); - struct pci_dev *pdev = to_pci_dev(dev->dev); enum vmw_res_type i; unregister_pm_notifier(&dev_priv->pm_nb); @@ -1196,8 +1190,6 @@ static void vmw_driver_unload(struct drm_device *dev) idr_destroy(&dev_priv->res_idr[i]); vmw_mksstat_remove_all(dev_priv); - - pci_release_regions(pdev); } static void vmw_postclose(struct drm_device *dev, diff --git a/drivers/gpu/drm/xe/xe_hw_fence.c b/drivers/gpu/drm/xe/xe_hw_fence.c index 0b4f12be3692..03eb8c6d1616 100644 --- a/drivers/gpu/drm/xe/xe_hw_fence.c +++ b/drivers/gpu/drm/xe/xe_hw_fence.c @@ -165,7 +165,7 @@ static bool xe_hw_fence_signaled(struct dma_fence *dma_fence) u32 seqno = xe_map_rd(xe, &fence->seqno_map, 0, u32); return dma_fence->error || - !__dma_fence_is_later(dma_fence->seqno, seqno, dma_fence->ops); + !__dma_fence_is_later(dma_fence, dma_fence->seqno, seqno); } static bool xe_hw_fence_enable_signaling(struct dma_fence *dma_fence) diff --git a/drivers/gpu/drm/xe/xe_sched_job.c b/drivers/gpu/drm/xe/xe_sched_job.c index 1905ca590965..d21bf8f26964 100644 --- a/drivers/gpu/drm/xe/xe_sched_job.c +++ b/drivers/gpu/drm/xe/xe_sched_job.c @@ -113,7 +113,8 @@ struct xe_sched_job *xe_sched_job_create(struct xe_exec_queue *q, kref_init(&job->refcount); xe_exec_queue_get(job->q); - err = drm_sched_job_init(&job->drm, q->entity, 1, NULL); + err = drm_sched_job_init(&job->drm, q->entity, 1, NULL, + q->xef ? q->xef->drm->client_id : 0); if (err) goto err_free; @@ -216,15 +217,17 @@ void xe_sched_job_set_error(struct xe_sched_job *job, int error) bool xe_sched_job_started(struct xe_sched_job *job) { + struct dma_fence *fence = dma_fence_chain_contained(job->fence); struct xe_lrc *lrc = job->q->lrc[0]; - return !__dma_fence_is_later(xe_sched_job_lrc_seqno(job), - xe_lrc_start_seqno(lrc), - dma_fence_chain_contained(job->fence)->ops); + return !__dma_fence_is_later(fence, + xe_sched_job_lrc_seqno(job), + xe_lrc_start_seqno(lrc)); } bool xe_sched_job_completed(struct xe_sched_job *job) { + struct dma_fence *fence = dma_fence_chain_contained(job->fence); struct xe_lrc *lrc = job->q->lrc[0]; /* @@ -232,9 +235,9 @@ bool xe_sched_job_completed(struct xe_sched_job *job) * parallel handshake is done. */ - return !__dma_fence_is_later(xe_sched_job_lrc_seqno(job), - xe_lrc_seqno(lrc), - dma_fence_chain_contained(job->fence)->ops); + return !__dma_fence_is_later(fence, + xe_sched_job_lrc_seqno(job), + xe_lrc_seqno(lrc)); } void xe_sched_job_arm(struct xe_sched_job *job) diff --git a/drivers/gpu/drm/xlnx/zynqmp_dp.c b/drivers/gpu/drm/xlnx/zynqmp_dp.c index 238cbb49963e..02e1feaa6115 100644 --- a/drivers/gpu/drm/xlnx/zynqmp_dp.c +++ b/drivers/gpu/drm/xlnx/zynqmp_dp.c @@ -2439,9 +2439,9 @@ int zynqmp_dp_probe(struct zynqmp_dpsub *dpsub) struct zynqmp_dp *dp; int ret; - dp = kzalloc(sizeof(*dp), GFP_KERNEL); - if (!dp) - return -ENOMEM; + dp = devm_drm_bridge_alloc(&pdev->dev, struct zynqmp_dp, bridge, &zynqmp_dp_bridge_funcs); + if (IS_ERR(dp)) + return PTR_ERR(dp); dp->dev = &pdev->dev; dp->dpsub = dpsub; @@ -2454,31 +2454,25 @@ int zynqmp_dp_probe(struct zynqmp_dpsub *dpsub) /* Acquire all resources (IOMEM, IRQ and PHYs). */ dp->iomem = devm_platform_ioremap_resource_byname(pdev, "dp"); - if (IS_ERR(dp->iomem)) { - ret = PTR_ERR(dp->iomem); - goto err_free; - } + if (IS_ERR(dp->iomem)) + return PTR_ERR(dp->iomem); dp->irq = platform_get_irq(pdev, 0); - if (dp->irq < 0) { - ret = dp->irq; - goto err_free; - } + if (dp->irq < 0) + return dp->irq; dp->reset = devm_reset_control_get(dp->dev, NULL); - if (IS_ERR(dp->reset)) { - ret = dev_err_probe(dp->dev, PTR_ERR(dp->reset), + if (IS_ERR(dp->reset)) + return dev_err_probe(dp->dev, PTR_ERR(dp->reset), "failed to get reset\n"); - goto err_free; - } ret = zynqmp_dp_reset(dp, true); if (ret < 0) - goto err_free; + return ret; ret = zynqmp_dp_reset(dp, false); if (ret < 0) - goto err_free; + return ret; ret = zynqmp_dp_phy_probe(dp); if (ret) @@ -2486,7 +2480,6 @@ int zynqmp_dp_probe(struct zynqmp_dpsub *dpsub) /* Initialize the bridge. */ bridge = &dp->bridge; - bridge->funcs = &zynqmp_dp_bridge_funcs; bridge->ops = DRM_BRIDGE_OP_DETECT | DRM_BRIDGE_OP_EDID | DRM_BRIDGE_OP_HPD; bridge->type = DRM_MODE_CONNECTOR_DisplayPort; @@ -2539,8 +2532,6 @@ err_phy_exit: zynqmp_dp_phy_exit(dp); err_reset: zynqmp_dp_reset(dp, true); -err_free: - kfree(dp); return ret; } diff --git a/drivers/gpu/drm/xlnx/zynqmp_dpsub.c b/drivers/gpu/drm/xlnx/zynqmp_dpsub.c index 3a9544b97bc5..2764c4b17c5e 100644 --- a/drivers/gpu/drm/xlnx/zynqmp_dpsub.c +++ b/drivers/gpu/drm/xlnx/zynqmp_dpsub.c @@ -180,7 +180,6 @@ static int zynqmp_dpsub_parse_dt(struct zynqmp_dpsub *dpsub) void zynqmp_dpsub_release(struct zynqmp_dpsub *dpsub) { kfree(dpsub->disp); - kfree(dpsub->dp); kfree(dpsub); } diff --git a/include/drm/display/drm_hdmi_audio_helper.h b/include/drm/display/drm_hdmi_audio_helper.h index c9a6faef4109..44d910bdc72d 100644 --- a/include/drm/display/drm_hdmi_audio_helper.h +++ b/include/drm/display/drm_hdmi_audio_helper.h @@ -14,6 +14,7 @@ int drm_connector_hdmi_audio_init(struct drm_connector *connector, struct device *hdmi_codec_dev, const struct drm_connector_hdmi_audio_funcs *funcs, unsigned int max_i2s_playback_channels, + u64 i2s_formats, bool spdif_playback, int sound_dai_port); void drm_connector_hdmi_audio_plugged_notify(struct drm_connector *connector, diff --git a/include/drm/display/drm_hdmi_cec_helper.h b/include/drm/display/drm_hdmi_cec_helper.h new file mode 100644 index 000000000000..fd8f4d2f02c1 --- /dev/null +++ b/include/drm/display/drm_hdmi_cec_helper.h @@ -0,0 +1,72 @@ +/* SPDX-License-Identifier: MIT */ + +#ifndef DRM_DISPLAY_HDMI_CEC_HELPER +#define DRM_DISPLAY_HDMI_CEC_HELPER + +#include <linux/types.h> + +struct drm_connector; + +struct cec_msg; +struct device; + +struct drm_connector_hdmi_cec_funcs { + /** + * @init: perform hardware-specific initialization before registering the CEC adapter + */ + int (*init)(struct drm_connector *connector); + + /** + * @uninit: perform hardware-specific teardown for the CEC adapter + */ + void (*uninit)(struct drm_connector *connector); + + /** + * @enable: enable or disable CEC adapter + */ + int (*enable)(struct drm_connector *connector, bool enable); + + /** + * @log_addr: set adapter's logical address, can be called multiple + * times if adapter supports several LAs + */ + int (*log_addr)(struct drm_connector *connector, u8 logical_addr); + + /** + * @transmit: start transmission of the specified CEC message + */ + int (*transmit)(struct drm_connector *connector, u8 attempts, + u32 signal_free_time, struct cec_msg *msg); +}; + +int drmm_connector_hdmi_cec_register(struct drm_connector *connector, + const struct drm_connector_hdmi_cec_funcs *funcs, + const char *name, + u8 available_las, + struct device *dev); + +void drm_connector_hdmi_cec_received_msg(struct drm_connector *connector, + struct cec_msg *msg); + +void drm_connector_hdmi_cec_transmit_done(struct drm_connector *connector, + u8 status, + u8 arb_lost_cnt, u8 nack_cnt, + u8 low_drive_cnt, u8 error_cnt); + +void drm_connector_hdmi_cec_transmit_attempt_done(struct drm_connector *connector, + u8 status); + +#if IS_ENABLED(CONFIG_DRM_DISPLAY_HDMI_CEC_NOTIFIER_HELPER) +int drmm_connector_hdmi_cec_notifier_register(struct drm_connector *connector, + const char *port_name, + struct device *dev); +#else +static inline int drmm_connector_hdmi_cec_notifier_register(struct drm_connector *connector, + const char *port_name, + struct device *dev) +{ + return 0; +} +#endif + +#endif diff --git a/include/drm/drm_bridge.h b/include/drm/drm_bridge.h index 4e418a29a9ff..7f66f9018c10 100644 --- a/include/drm/drm_bridge.h +++ b/include/drm/drm_bridge.h @@ -32,6 +32,7 @@ #include <drm/drm_mode_object.h> #include <drm/drm_modes.h> +struct cec_msg; struct device_node; struct drm_bridge; @@ -77,6 +78,16 @@ struct drm_bridge_funcs { enum drm_bridge_attach_flags flags); /** + * @destroy: + * + * This callback is invoked when the bridge is about to be + * deallocated. + * + * The @destroy callback is optional. + */ + void (*destroy)(struct drm_bridge *bridge); + + /** * @detach: * * This callback is invoked whenever our bridge is being detached from a @@ -164,17 +175,33 @@ struct drm_bridge_funcs { /** * @disable: * - * This callback should disable the bridge. It is called right before - * the preceding element in the display pipe is disabled. If the - * preceding element is a bridge this means it's called before that - * bridge's @disable vfunc. If the preceding element is a &drm_encoder - * it's called right before the &drm_encoder_helper_funcs.disable, - * &drm_encoder_helper_funcs.prepare or &drm_encoder_helper_funcs.dpms - * hook. + * The @disable callback should disable the bridge. * * The bridge can assume that the display pipe (i.e. clocks and timing * signals) feeding it is still running when this callback is called. * + * + * If the preceding element is a &drm_bridge, then this is called before + * that bridge is disabled via one of: + * + * - &drm_bridge_funcs.disable + * - &drm_bridge_funcs.atomic_disable + * + * If the preceding element of the bridge is a display controller, then + * this callback is called before the encoder is disabled via one of: + * + * - &drm_encoder_helper_funcs.atomic_disable + * - &drm_encoder_helper_funcs.prepare + * - &drm_encoder_helper_funcs.disable + * - &drm_encoder_helper_funcs.dpms + * + * and the CRTC is disabled via one of: + * + * - &drm_crtc_helper_funcs.prepare + * - &drm_crtc_helper_funcs.atomic_disable + * - &drm_crtc_helper_funcs.disable + * - &drm_crtc_helper_funcs.dpms. + * * The @disable callback is optional. * * NOTE: @@ -187,17 +214,34 @@ struct drm_bridge_funcs { /** * @post_disable: * - * This callback should disable the bridge. It is called right after the - * preceding element in the display pipe is disabled. If the preceding - * element is a bridge this means it's called after that bridge's - * @post_disable function. If the preceding element is a &drm_encoder - * it's called right after the encoder's - * &drm_encoder_helper_funcs.disable, &drm_encoder_helper_funcs.prepare - * or &drm_encoder_helper_funcs.dpms hook. - * * The bridge must assume that the display pipe (i.e. clocks and timing - * signals) feeding it is no longer running when this callback is - * called. + * signals) feeding this bridge is no longer running when the + * @post_disable is called. + * + * This callback should perform all the actions required by the hardware + * after it has stopped receiving signals from the preceding element. + * + * If the preceding element is a &drm_bridge, then this is called after + * that bridge is post-disabled (unless marked otherwise by the + * @pre_enable_prev_first flag) via one of: + * + * - &drm_bridge_funcs.post_disable + * - &drm_bridge_funcs.atomic_post_disable + * + * If the preceding element of the bridge is a display controller, then + * this callback is called after the encoder is disabled via one of: + * + * - &drm_encoder_helper_funcs.atomic_disable + * - &drm_encoder_helper_funcs.prepare + * - &drm_encoder_helper_funcs.disable + * - &drm_encoder_helper_funcs.dpms + * + * and the CRTC is disabled via one of: + * + * - &drm_crtc_helper_funcs.prepare + * - &drm_crtc_helper_funcs.atomic_disable + * - &drm_crtc_helper_funcs.disable + * - &drm_crtc_helper_funcs.dpms * * The @post_disable callback is optional. * @@ -240,18 +284,30 @@ struct drm_bridge_funcs { /** * @pre_enable: * - * This callback should enable the bridge. It is called right before - * the preceding element in the display pipe is enabled. If the - * preceding element is a bridge this means it's called before that - * bridge's @pre_enable function. If the preceding element is a - * &drm_encoder it's called right before the encoder's - * &drm_encoder_helper_funcs.enable, &drm_encoder_helper_funcs.commit or - * &drm_encoder_helper_funcs.dpms hook. - * * The display pipe (i.e. clocks and timing signals) feeding this bridge - * will not yet be running when this callback is called. The bridge must - * not enable the display link feeding the next bridge in the chain (if - * there is one) when this callback is called. + * will not yet be running when the @pre_enable is called. + * + * This callback should perform all the necessary actions to prepare the + * bridge to accept signals from the preceding element. + * + * If the preceding element is a &drm_bridge, then this is called before + * that bridge is pre-enabled (unless marked otherwise by + * @pre_enable_prev_first flag) via one of: + * + * - &drm_bridge_funcs.pre_enable + * - &drm_bridge_funcs.atomic_pre_enable + * + * If the preceding element of the bridge is a display controller, then + * this callback is called before the CRTC is enabled via one of: + * + * - &drm_crtc_helper_funcs.atomic_enable + * - &drm_crtc_helper_funcs.commit + * + * and the encoder is enabled via one of: + * + * - &drm_encoder_helper_funcs.atomic_enable + * - &drm_encoder_helper_funcs.enable + * - &drm_encoder_helper_funcs.commit * * The @pre_enable callback is optional. * @@ -265,19 +321,31 @@ struct drm_bridge_funcs { /** * @enable: * - * This callback should enable the bridge. It is called right after - * the preceding element in the display pipe is enabled. If the - * preceding element is a bridge this means it's called after that - * bridge's @enable function. If the preceding element is a - * &drm_encoder it's called right after the encoder's - * &drm_encoder_helper_funcs.enable, &drm_encoder_helper_funcs.commit or - * &drm_encoder_helper_funcs.dpms hook. + * The @enable callback should enable the bridge. * * The bridge can assume that the display pipe (i.e. clocks and timing * signals) feeding it is running when this callback is called. This * callback must enable the display link feeding the next bridge in the * chain if there is one. * + * If the preceding element is a &drm_bridge, then this is called after + * that bridge is enabled via one of: + * + * - &drm_bridge_funcs.enable + * - &drm_bridge_funcs.atomic_enable + * + * If the preceding element of the bridge is a display controller, then + * this callback is called after the CRTC is enabled via one of: + * + * - &drm_crtc_helper_funcs.atomic_enable + * - &drm_crtc_helper_funcs.commit + * + * and the encoder is enabled via one of: + * + * - &drm_encoder_helper_funcs.atomic_enable + * - &drm_encoder_helper_funcs.enable + * - drm_encoder_helper_funcs.commit + * * The @enable callback is optional. * * NOTE: @@ -290,17 +358,30 @@ struct drm_bridge_funcs { /** * @atomic_pre_enable: * - * This callback should enable the bridge. It is called right before - * the preceding element in the display pipe is enabled. If the - * preceding element is a bridge this means it's called before that - * bridge's @atomic_pre_enable or @pre_enable function. If the preceding - * element is a &drm_encoder it's called right before the encoder's - * &drm_encoder_helper_funcs.atomic_enable hook. - * * The display pipe (i.e. clocks and timing signals) feeding this bridge - * will not yet be running when this callback is called. The bridge must - * not enable the display link feeding the next bridge in the chain (if - * there is one) when this callback is called. + * will not yet be running when the @atomic_pre_enable is called. + * + * This callback should perform all the necessary actions to prepare the + * bridge to accept signals from the preceding element. + * + * If the preceding element is a &drm_bridge, then this is called before + * that bridge is pre-enabled (unless marked otherwise by + * @pre_enable_prev_first flag) via one of: + * + * - &drm_bridge_funcs.pre_enable + * - &drm_bridge_funcs.atomic_pre_enable + * + * If the preceding element of the bridge is a display controller, then + * this callback is called before the CRTC is enabled via one of: + * + * - &drm_crtc_helper_funcs.atomic_enable + * - &drm_crtc_helper_funcs.commit + * + * and the encoder is enabled via one of: + * + * - &drm_encoder_helper_funcs.atomic_enable + * - &drm_encoder_helper_funcs.enable + * - &drm_encoder_helper_funcs.commit * * The @atomic_pre_enable callback is optional. */ @@ -310,18 +391,31 @@ struct drm_bridge_funcs { /** * @atomic_enable: * - * This callback should enable the bridge. It is called right after - * the preceding element in the display pipe is enabled. If the - * preceding element is a bridge this means it's called after that - * bridge's @atomic_enable or @enable function. If the preceding element - * is a &drm_encoder it's called right after the encoder's - * &drm_encoder_helper_funcs.atomic_enable hook. + * The @atomic_enable callback should enable the bridge. * * The bridge can assume that the display pipe (i.e. clocks and timing * signals) feeding it is running when this callback is called. This * callback must enable the display link feeding the next bridge in the * chain if there is one. * + * If the preceding element is a &drm_bridge, then this is called after + * that bridge is enabled via one of: + * + * - &drm_bridge_funcs.enable + * - &drm_bridge_funcs.atomic_enable + * + * If the preceding element of the bridge is a display controller, then + * this callback is called after the CRTC is enabled via one of: + * + * - &drm_crtc_helper_funcs.atomic_enable + * - &drm_crtc_helper_funcs.commit + * + * and the encoder is enabled via one of: + * + * - &drm_encoder_helper_funcs.atomic_enable + * - &drm_encoder_helper_funcs.enable + * - drm_encoder_helper_funcs.commit + * * The @atomic_enable callback is optional. */ void (*atomic_enable)(struct drm_bridge *bridge, @@ -329,16 +423,32 @@ struct drm_bridge_funcs { /** * @atomic_disable: * - * This callback should disable the bridge. It is called right before - * the preceding element in the display pipe is disabled. If the - * preceding element is a bridge this means it's called before that - * bridge's @atomic_disable or @disable vfunc. If the preceding element - * is a &drm_encoder it's called right before the - * &drm_encoder_helper_funcs.atomic_disable hook. + * The @atomic_disable callback should disable the bridge. * * The bridge can assume that the display pipe (i.e. clocks and timing * signals) feeding it is still running when this callback is called. * + * If the preceding element is a &drm_bridge, then this is called before + * that bridge is disabled via one of: + * + * - &drm_bridge_funcs.disable + * - &drm_bridge_funcs.atomic_disable + * + * If the preceding element of the bridge is a display controller, then + * this callback is called before the encoder is disabled via one of: + * + * - &drm_encoder_helper_funcs.atomic_disable + * - &drm_encoder_helper_funcs.prepare + * - &drm_encoder_helper_funcs.disable + * - &drm_encoder_helper_funcs.dpms + * + * and the CRTC is disabled via one of: + * + * - &drm_crtc_helper_funcs.prepare + * - &drm_crtc_helper_funcs.atomic_disable + * - &drm_crtc_helper_funcs.disable + * - &drm_crtc_helper_funcs.dpms. + * * The @atomic_disable callback is optional. */ void (*atomic_disable)(struct drm_bridge *bridge, @@ -347,16 +457,34 @@ struct drm_bridge_funcs { /** * @atomic_post_disable: * - * This callback should disable the bridge. It is called right after the - * preceding element in the display pipe is disabled. If the preceding - * element is a bridge this means it's called after that bridge's - * @atomic_post_disable or @post_disable function. If the preceding - * element is a &drm_encoder it's called right after the encoder's - * &drm_encoder_helper_funcs.atomic_disable hook. - * * The bridge must assume that the display pipe (i.e. clocks and timing - * signals) feeding it is no longer running when this callback is - * called. + * signals) feeding this bridge is no longer running when the + * @atomic_post_disable is called. + * + * This callback should perform all the actions required by the hardware + * after it has stopped receiving signals from the preceding element. + * + * If the preceding element is a &drm_bridge, then this is called after + * that bridge is post-disabled (unless marked otherwise by the + * @pre_enable_prev_first flag) via one of: + * + * - &drm_bridge_funcs.post_disable + * - &drm_bridge_funcs.atomic_post_disable + * + * If the preceding element of the bridge is a display controller, then + * this callback is called after the encoder is disabled via one of: + * + * - &drm_encoder_helper_funcs.atomic_disable + * - &drm_encoder_helper_funcs.prepare + * - &drm_encoder_helper_funcs.disable + * - &drm_encoder_helper_funcs.dpms + * + * and the CRTC is disabled via one of: + * + * - &drm_crtc_helper_funcs.prepare + * - &drm_crtc_helper_funcs.atomic_disable + * - &drm_crtc_helper_funcs.disable + * - &drm_crtc_helper_funcs.dpms * * The @atomic_post_disable callback is optional. */ @@ -737,6 +865,16 @@ struct drm_bridge_funcs { struct drm_bridge *bridge, bool enable, int direction); + int (*hdmi_cec_init)(struct drm_connector *connector, + struct drm_bridge *bridge); + + int (*hdmi_cec_enable)(struct drm_bridge *bridge, bool enable); + + int (*hdmi_cec_log_addr)(struct drm_bridge *bridge, u8 logical_addr); + + int (*hdmi_cec_transmit)(struct drm_bridge *bridge, u8 attempts, + u32 signal_free_time, struct cec_msg *msg); + /** * @dp_audio_startup: * @@ -907,6 +1045,16 @@ enum drm_bridge_ops { * flag. */ DRM_BRIDGE_OP_DP_AUDIO = BIT(6), + /** + * @DRM_BRIDGE_OP_HDMI_CEC_NOTIFIER: The bridge requires CEC notifier + * to be present. + */ + DRM_BRIDGE_OP_HDMI_CEC_NOTIFIER = BIT(7), + /** + * @DRM_BRIDGE_OP_HDMI_CEC_ADAPTER: The bridge requires CEC notifier + * to be present. + */ + DRM_BRIDGE_OP_HDMI_CEC_ADAPTER = BIT(8), }; /** @@ -977,21 +1125,6 @@ struct drm_bridge { * @ddc: Associated I2C adapter for DDC access, if any. */ struct i2c_adapter *ddc; - /** private: */ - /** - * @hpd_mutex: Protects the @hpd_cb and @hpd_data fields. - */ - struct mutex hpd_mutex; - /** - * @hpd_cb: Hot plug detection callback, registered with - * drm_bridge_hpd_enable(). - */ - void (*hpd_cb)(void *data, enum drm_connector_status status); - /** - * @hpd_data: Private data passed to the Hot plug detection callback - * @hpd_cb. - */ - void *hpd_data; /** * @vendor: Vendor of the product to be used for the SPD InfoFrame @@ -1019,6 +1152,12 @@ struct drm_bridge { unsigned int max_bpc; /** + * @hdmi_cec_dev: device to be used as a containing device for CEC + * functions. + */ + struct device *hdmi_cec_dev; + + /** * @hdmi_audio_dev: device to be used as a parent for the HDMI Codec if * either of @DRM_BRIDGE_OP_HDMI_AUDIO or @DRM_BRIDGE_OP_DP_AUDIO is set. */ @@ -1032,6 +1171,14 @@ struct drm_bridge { int hdmi_audio_max_i2s_playback_channels; /** + * @hdmi_audio_i2s_formats: supported I2S formats, optional. The + * default is to allow all formats supported by the corresponding I2S + * bus driver. This is only used for bridges setting + * @DRM_BRIDGE_OP_HDMI_AUDIO or @DRM_BRIDGE_OP_DP_AUDIO. + */ + u64 hdmi_audio_i2s_formats; + + /** * @hdmi_audio_spdif_playback: set if this bridge has S/PDIF playback * port for @DRM_BRIDGE_OP_HDMI_AUDIO or @DRM_BRIDGE_OP_DP_AUDIO. */ @@ -1043,6 +1190,32 @@ struct drm_bridge { * not used. */ int hdmi_audio_dai_port; + + /** + * @hdmi_cec_adapter_name: the name of the adapter to register + */ + const char *hdmi_cec_adapter_name; + + /** + * @hdmi_cec_available_las: number of logical addresses, CEC_MAX_LOG_ADDRS if unset + */ + u8 hdmi_cec_available_las; + + /** private: */ + /** + * @hpd_mutex: Protects the @hpd_cb and @hpd_data fields. + */ + struct mutex hpd_mutex; + /** + * @hpd_cb: Hot plug detection callback, registered with + * drm_bridge_hpd_enable(). + */ + void (*hpd_cb)(void *data, enum drm_connector_status status); + /** + * @hpd_data: Private data passed to the Hot plug detection callback + * @hpd_cb. + */ + void *hpd_data; }; static inline struct drm_bridge * @@ -1274,6 +1447,8 @@ static inline struct drm_bridge *drmm_of_get_bridge(struct drm_device *drm, } #endif +void devm_drm_put_bridge(struct device *dev, struct drm_bridge *bridge); + void drm_bridge_debugfs_params(struct dentry *root); void drm_bridge_debugfs_encoder_params(struct dentry *root, struct drm_encoder *encoder); diff --git a/include/drm/drm_color_mgmt.h b/include/drm/drm_color_mgmt.h index ed81741036d7..6cb577f6dba6 100644 --- a/include/drm/drm_color_mgmt.h +++ b/include/drm/drm_color_mgmt.h @@ -118,4 +118,31 @@ enum drm_color_lut_tests { }; int drm_color_lut_check(const struct drm_property_blob *lut, u32 tests); + +/* + * Gamma-LUT programming + */ + +typedef void (*drm_crtc_set_lut_func)(struct drm_crtc *, unsigned int, u16, u16, u16); + +void drm_crtc_load_gamma_888(struct drm_crtc *crtc, const struct drm_color_lut *lut, + drm_crtc_set_lut_func set_gamma); +void drm_crtc_load_gamma_565_from_888(struct drm_crtc *crtc, const struct drm_color_lut *lut, + drm_crtc_set_lut_func set_gamma); +void drm_crtc_load_gamma_555_from_888(struct drm_crtc *crtc, const struct drm_color_lut *lut, + drm_crtc_set_lut_func set_gamma); + +void drm_crtc_fill_gamma_888(struct drm_crtc *crtc, drm_crtc_set_lut_func set_gamma); +void drm_crtc_fill_gamma_565(struct drm_crtc *crtc, drm_crtc_set_lut_func set_gamma); +void drm_crtc_fill_gamma_555(struct drm_crtc *crtc, drm_crtc_set_lut_func set_gamma); + +/* + * Color-LUT programming + */ + +void drm_crtc_load_palette_8(struct drm_crtc *crtc, const struct drm_color_lut *lut, + drm_crtc_set_lut_func set_palette); + +void drm_crtc_fill_palette_8(struct drm_crtc *crtc, drm_crtc_set_lut_func set_palette); + #endif diff --git a/include/drm/drm_connector.h b/include/drm/drm_connector.h index f13d597370a3..73903c3c842f 100644 --- a/include/drm/drm_connector.h +++ b/include/drm/drm_connector.h @@ -1191,6 +1191,29 @@ struct drm_connector_hdmi_audio_funcs { bool enable, int direction); }; +void drm_connector_cec_phys_addr_invalidate(struct drm_connector *connector); +void drm_connector_cec_phys_addr_set(struct drm_connector *connector); + +/** + * struct drm_connector_cec_funcs - drm_hdmi_connector control functions + */ +struct drm_connector_cec_funcs { + /** + * @phys_addr_invalidate: mark CEC physical address as invalid + * + * The callback to mark CEC physical address as invalid, abstracting + * the operation. + */ + void (*phys_addr_invalidate)(struct drm_connector *connector); + + /** + * @phys_addr_set: set CEC physical address + * + * The callback to set CEC physical address, abstracting the operation. + */ + void (*phys_addr_set)(struct drm_connector *connector, u16 addr); +}; + /** * struct drm_connector_hdmi_funcs - drm_hdmi_connector control functions */ @@ -1833,6 +1856,26 @@ struct drm_connector_hdmi { }; /** + * struct drm_connector_cec - DRM Connector CEC-related structure + */ +struct drm_connector_cec { + /** + * @mutex: protects all fields in this structure. + */ + struct mutex mutex; + + /** + * @funcs: CEC Control Functions + */ + const struct drm_connector_cec_funcs *funcs; + + /** + * @data: CEC implementation-specific data + */ + void *data; +}; + +/** * struct drm_connector - central DRM connector control structure * * Each connector may be connected to one or more CRTCs, or may be clonable by @@ -2253,6 +2296,11 @@ struct drm_connector { * @hdmi_audio: HDMI codec properties and non-DRM state. */ struct drm_connector_hdmi_audio hdmi_audio; + + /** + * @cec: CEC-related data. + */ + struct drm_connector_cec cec; }; #define obj_to_connector(x) container_of(x, struct drm_connector, base) diff --git a/include/drm/drm_gem.h b/include/drm/drm_gem.h index a3133a08267c..1a79ec3fe45c 100644 --- a/include/drm/drm_gem.h +++ b/include/drm/drm_gem.h @@ -126,7 +126,8 @@ struct drm_gem_object_funcs { /** * @pin: * - * Pin backing buffer in memory. Used by the drm_gem_map_attach() helper. + * Pin backing buffer in memory, such that dma-buf importers can + * access it. Used by the drm_gem_map_attach() helper. * * This callback is optional. */ diff --git a/include/drm/drm_gem_shmem_helper.h b/include/drm/drm_gem_shmem_helper.h index b4f993da3cae..35f7466dca84 100644 --- a/include/drm/drm_gem_shmem_helper.h +++ b/include/drm/drm_gem_shmem_helper.h @@ -287,6 +287,8 @@ drm_gem_shmem_prime_import_sg_table(struct drm_device *dev, struct sg_table *sgt); int drm_gem_shmem_dumb_create(struct drm_file *file, struct drm_device *dev, struct drm_mode_create_dumb *args); +struct drm_gem_object *drm_gem_shmem_prime_import_no_map(struct drm_device *dev, + struct dma_buf *buf); /** * DRM_GEM_SHMEM_DRIVER_OPS - Default shmem GEM operations @@ -298,4 +300,17 @@ int drm_gem_shmem_dumb_create(struct drm_file *file, struct drm_device *dev, .gem_prime_import_sg_table = drm_gem_shmem_prime_import_sg_table, \ .dumb_create = drm_gem_shmem_dumb_create +/** + * DRM_GEM_SHMEM_DRIVER_OPS_NO_MAP_SGT - shmem GEM operations + * without mapping sg_table on + * imported buffer. + * + * This macro provides a shortcut for setting the shmem GEM operations in + * the &drm_driver structure for drivers that do not require a sg_table on + * imported buffers. + */ +#define DRM_GEM_SHMEM_DRIVER_OPS_NO_MAP_SGT \ + .gem_prime_import = drm_gem_shmem_prime_import_no_map, \ + .dumb_create = drm_gem_shmem_dumb_create + #endif /* __DRM_GEM_SHMEM_HELPER_H__ */ diff --git a/include/drm/drm_gem_vram_helper.h b/include/drm/drm_gem_vram_helper.h index 00830b49a3ff..2dd42bed679d 100644 --- a/include/drm/drm_gem_vram_helper.h +++ b/include/drm/drm_gem_vram_helper.h @@ -94,8 +94,6 @@ struct drm_gem_vram_object *drm_gem_vram_create(struct drm_device *dev, unsigned long pg_align); void drm_gem_vram_put(struct drm_gem_vram_object *gbo); s64 drm_gem_vram_offset(struct drm_gem_vram_object *gbo); -int drm_gem_vram_pin(struct drm_gem_vram_object *gbo, unsigned long pl_flag); -int drm_gem_vram_unpin(struct drm_gem_vram_object *gbo); int drm_gem_vram_vmap(struct drm_gem_vram_object *gbo, struct iosys_map *map); void drm_gem_vram_vunmap(struct drm_gem_vram_object *gbo, struct iosys_map *map); diff --git a/include/drm/drm_prime.h b/include/drm/drm_prime.h index fa085c44d4ca..f50f862f0d8b 100644 --- a/include/drm/drm_prime.h +++ b/include/drm/drm_prime.h @@ -100,6 +100,9 @@ struct dma_buf *drm_gem_prime_export(struct drm_gem_object *obj, unsigned long drm_prime_get_contiguous_size(struct sg_table *sgt); /* helper functions for importing */ +bool drm_gem_is_prime_exported_dma_buf(struct drm_device *dev, + struct dma_buf *dma_buf); + struct drm_gem_object *drm_gem_prime_import_dev(struct drm_device *dev, struct dma_buf *dma_buf, struct device *attach_dev); diff --git a/include/drm/gpu_scheduler.h b/include/drm/gpu_scheduler.h index 1a7e377d4cbb..e62a7214e052 100644 --- a/include/drm/gpu_scheduler.h +++ b/include/drm/gpu_scheduler.h @@ -192,7 +192,7 @@ struct drm_sched_entity { * @last_scheduled: * * Points to the finished fence of the last scheduled job. Only written - * by the scheduler thread, can be accessed locklessly from + * by drm_sched_entity_pop_job(). Can be accessed locklessly from * drm_sched_job_arm() if the queue is empty. */ struct dma_fence __rcu *last_scheduled; @@ -305,6 +305,13 @@ struct drm_sched_fence { * @owner: job owner for debugging */ void *owner; + + /** + * @drm_client_id: + * + * The client_id of the drm_file which owns the job. + */ + uint64_t drm_client_id; }; struct drm_sched_fence *to_drm_sched_fence(struct dma_fence *f); @@ -319,7 +326,6 @@ struct drm_sched_fence *to_drm_sched_fence(struct dma_fence *f); * @finish_cb: the callback for the finished fence. * @credits: the number of credits this job contributes to the scheduler * @work: Helper to reschedule job kill to different context. - * @id: a unique id assigned to each job scheduled on the scheduler. * @karma: increment on every hang caused by this job. If this exceeds the hang * limit of the scheduler then the job is marked guilty and will not * be scheduled further. @@ -332,8 +338,6 @@ struct drm_sched_fence *to_drm_sched_fence(struct dma_fence *f); * to schedule the job. */ struct drm_sched_job { - u64 id; - /** * @submit_ts: * @@ -629,7 +633,8 @@ drm_sched_pick_best(struct drm_gpu_scheduler **sched_list, int drm_sched_job_init(struct drm_sched_job *job, struct drm_sched_entity *entity, - u32 credits, void *owner); + u32 credits, void *owner, + u64 drm_client_id); void drm_sched_job_arm(struct drm_sched_job *job); void drm_sched_entity_push_job(struct drm_sched_job *sched_job); int drm_sched_job_add_dependency(struct drm_sched_job *job, diff --git a/include/drm/ttm/ttm_bo.h b/include/drm/ttm/ttm_bo.h index cf027558b6db..8ad6e2713625 100644 --- a/include/drm/ttm/ttm_bo.h +++ b/include/drm/ttm/ttm_bo.h @@ -245,34 +245,6 @@ bool ttm_bo_shrink_suitable(struct ttm_buffer_object *bo, struct ttm_operation_c bool ttm_bo_shrink_avoid_wait(void); /** - * ttm_bo_get - reference a struct ttm_buffer_object - * - * @bo: The buffer object. - */ -static inline void ttm_bo_get(struct ttm_buffer_object *bo) -{ - kref_get(&bo->kref); -} - -/** - * ttm_bo_get_unless_zero - reference a struct ttm_buffer_object unless - * its refcount has already reached zero. - * @bo: The buffer object. - * - * Used to reference a TTM buffer object in lookups where the object is removed - * from the lookup structure during the destructor and for RCU lookups. - * - * Returns: @bo if the referencing was successful, NULL otherwise. - */ -static inline __must_check struct ttm_buffer_object * -ttm_bo_get_unless_zero(struct ttm_buffer_object *bo) -{ - if (!kref_get_unless_zero(&bo->kref)) - return NULL; - return bo; -} - -/** * ttm_bo_reserve: * * @bo: A pointer to a struct ttm_buffer_object. diff --git a/include/linux/dma-fence.h b/include/linux/dma-fence.h index b12776883d14..10a849cb2d3f 100644 --- a/include/linux/dma-fence.h +++ b/include/linux/dma-fence.h @@ -26,6 +26,7 @@ struct dma_fence; struct dma_fence_ops; struct dma_fence_cb; +struct seq_file; /** * struct dma_fence - software synchronization primitive @@ -97,6 +98,7 @@ struct dma_fence { }; enum dma_fence_flag_bits { + DMA_FENCE_FLAG_SEQNO64_BIT, DMA_FENCE_FLAG_SIGNALED_BIT, DMA_FENCE_FLAG_TIMESTAMP_BIT, DMA_FENCE_FLAG_ENABLE_SIGNAL_BIT, @@ -125,14 +127,6 @@ struct dma_fence_cb { */ struct dma_fence_ops { /** - * @use_64bit_seqno: - * - * True if this dma_fence implementation uses 64bit seqno, false - * otherwise. - */ - bool use_64bit_seqno; - - /** * @get_driver_name: * * Returns the driver name. This is a callback to allow drivers to @@ -262,6 +256,9 @@ struct dma_fence_ops { void dma_fence_init(struct dma_fence *fence, const struct dma_fence_ops *ops, spinlock_t *lock, u64 context, u64 seqno); +void dma_fence_init64(struct dma_fence *fence, const struct dma_fence_ops *ops, + spinlock_t *lock, u64 context, u64 seqno); + void dma_fence_release(struct kref *kref); void dma_fence_free(struct dma_fence *fence); void dma_fence_describe(struct dma_fence *fence, struct seq_file *seq); @@ -381,6 +378,16 @@ bool dma_fence_remove_callback(struct dma_fence *fence, struct dma_fence_cb *cb); void dma_fence_enable_sw_signaling(struct dma_fence *fence); +static inline const char *dma_fence_driver_name(struct dma_fence *fence) +{ + return fence->ops->get_driver_name(fence); +} + +static inline const char *dma_fence_timeline_name(struct dma_fence *fence) +{ + return fence->ops->get_timeline_name(fence); +} + /** * dma_fence_is_signaled_locked - Return an indication if the fence * is signaled yet. @@ -441,21 +448,20 @@ dma_fence_is_signaled(struct dma_fence *fence) /** * __dma_fence_is_later - return if f1 is chronologically later than f2 + * @fence: fence in whose context to do the comparison * @f1: the first fence's seqno * @f2: the second fence's seqno from the same context - * @ops: dma_fence_ops associated with the seqno * * Returns true if f1 is chronologically later than f2. Both fences must be * from the same context, since a seqno is not common across contexts. */ -static inline bool __dma_fence_is_later(u64 f1, u64 f2, - const struct dma_fence_ops *ops) +static inline bool __dma_fence_is_later(struct dma_fence *fence, u64 f1, u64 f2) { /* This is for backward compatibility with drivers which can only handle * 32bit sequence numbers. Use a 64bit compare when the driver says to * do so. */ - if (ops->use_64bit_seqno) + if (test_bit(DMA_FENCE_FLAG_SEQNO64_BIT, &fence->flags)) return f1 > f2; return (int)(lower_32_bits(f1) - lower_32_bits(f2)) > 0; @@ -475,7 +481,7 @@ static inline bool dma_fence_is_later(struct dma_fence *f1, if (WARN_ON(f1->context != f2->context)) return false; - return __dma_fence_is_later(f1->seqno, f2->seqno, f1->ops); + return __dma_fence_is_later(f1, f1->seqno, f2->seqno); } /** diff --git a/include/trace/events/dma_fence.h b/include/trace/events/dma_fence.h index a4de3df8500b..84c83074ee81 100644 --- a/include/trace/events/dma_fence.h +++ b/include/trace/events/dma_fence.h @@ -16,8 +16,8 @@ DECLARE_EVENT_CLASS(dma_fence, TP_ARGS(fence), TP_STRUCT__entry( - __string(driver, fence->ops->get_driver_name(fence)) - __string(timeline, fence->ops->get_timeline_name(fence)) + __string(driver, dma_fence_driver_name(fence)) + __string(timeline, dma_fence_timeline_name(fence)) __field(unsigned int, context) __field(unsigned int, seqno) ), diff --git a/include/uapi/drm/drm_fourcc.h b/include/uapi/drm/drm_fourcc.h index 81202a50dc9e..6483f76a2165 100644 --- a/include/uapi/drm/drm_fourcc.h +++ b/include/uapi/drm/drm_fourcc.h @@ -378,6 +378,42 @@ extern "C" { #define DRM_FORMAT_Q401 fourcc_code('Q', '4', '0', '1') /* + * 3 plane YCbCr LSB aligned + * In order to use these formats in a similar fashion to MSB aligned ones + * implementation can multiply the values by 2^6=64. For that reason the padding + * must only contain zeros. + * index 0 = Y plane, [15:0] z:Y [6:10] little endian + * index 1 = Cr plane, [15:0] z:Cr [6:10] little endian + * index 2 = Cb plane, [15:0] z:Cb [6:10] little endian + */ +#define DRM_FORMAT_S010 fourcc_code('S', '0', '1', '0') /* 2x2 subsampled Cb (1) and Cr (2) planes 10 bits per channel */ +#define DRM_FORMAT_S210 fourcc_code('S', '2', '1', '0') /* 2x1 subsampled Cb (1) and Cr (2) planes 10 bits per channel */ +#define DRM_FORMAT_S410 fourcc_code('S', '4', '1', '0') /* non-subsampled Cb (1) and Cr (2) planes 10 bits per channel */ + +/* + * 3 plane YCbCr LSB aligned + * In order to use these formats in a similar fashion to MSB aligned ones + * implementation can multiply the values by 2^4=16. For that reason the padding + * must only contain zeros. + * index 0 = Y plane, [15:0] z:Y [4:12] little endian + * index 1 = Cr plane, [15:0] z:Cr [4:12] little endian + * index 2 = Cb plane, [15:0] z:Cb [4:12] little endian + */ +#define DRM_FORMAT_S012 fourcc_code('S', '0', '1', '2') /* 2x2 subsampled Cb (1) and Cr (2) planes 12 bits per channel */ +#define DRM_FORMAT_S212 fourcc_code('S', '2', '1', '2') /* 2x1 subsampled Cb (1) and Cr (2) planes 12 bits per channel */ +#define DRM_FORMAT_S412 fourcc_code('S', '4', '1', '2') /* non-subsampled Cb (1) and Cr (2) planes 12 bits per channel */ + +/* + * 3 plane YCbCr + * index 0 = Y plane, [15:0] Y little endian + * index 1 = Cr plane, [15:0] Cr little endian + * index 2 = Cb plane, [15:0] Cb little endian + */ +#define DRM_FORMAT_S016 fourcc_code('S', '0', '1', '6') /* 2x2 subsampled Cb (1) and Cr (2) planes 16 bits per channel */ +#define DRM_FORMAT_S216 fourcc_code('S', '2', '1', '6') /* 2x1 subsampled Cb (1) and Cr (2) planes 16 bits per channel */ +#define DRM_FORMAT_S416 fourcc_code('S', '4', '1', '6') /* non-subsampled Cb (1) and Cr (2) planes 16 bits per channel */ + +/* * 3 plane YCbCr * index 0: Y plane, [7:0] Y * index 1: Cb plane, [7:0] Cb diff --git a/include/uapi/drm/panfrost_drm.h b/include/uapi/drm/panfrost_drm.h index 568724be6628..ed67510395bd 100644 --- a/include/uapi/drm/panfrost_drm.h +++ b/include/uapi/drm/panfrost_drm.h @@ -21,6 +21,7 @@ extern "C" { #define DRM_PANFROST_PERFCNT_ENABLE 0x06 #define DRM_PANFROST_PERFCNT_DUMP 0x07 #define DRM_PANFROST_MADVISE 0x08 +#define DRM_PANFROST_SET_LABEL_BO 0x09 #define DRM_IOCTL_PANFROST_SUBMIT DRM_IOW(DRM_COMMAND_BASE + DRM_PANFROST_SUBMIT, struct drm_panfrost_submit) #define DRM_IOCTL_PANFROST_WAIT_BO DRM_IOW(DRM_COMMAND_BASE + DRM_PANFROST_WAIT_BO, struct drm_panfrost_wait_bo) @@ -29,6 +30,7 @@ extern "C" { #define DRM_IOCTL_PANFROST_GET_PARAM DRM_IOWR(DRM_COMMAND_BASE + DRM_PANFROST_GET_PARAM, struct drm_panfrost_get_param) #define DRM_IOCTL_PANFROST_GET_BO_OFFSET DRM_IOWR(DRM_COMMAND_BASE + DRM_PANFROST_GET_BO_OFFSET, struct drm_panfrost_get_bo_offset) #define DRM_IOCTL_PANFROST_MADVISE DRM_IOWR(DRM_COMMAND_BASE + DRM_PANFROST_MADVISE, struct drm_panfrost_madvise) +#define DRM_IOCTL_PANFROST_SET_LABEL_BO DRM_IOWR(DRM_COMMAND_BASE + DRM_PANFROST_SET_LABEL_BO, struct drm_panfrost_set_label_bo) /* * Unstable ioctl(s): only exposed when the unsafe unstable_ioctls module @@ -227,6 +229,25 @@ struct drm_panfrost_madvise { __u32 retained; /* out, whether backing store still exists */ }; +/** + * struct drm_panfrost_set_label_bo - ioctl argument for labelling Panfrost BOs. + */ +struct drm_panfrost_set_label_bo { + /** @handle: Handle of the buffer object to label. */ + __u32 handle; + + /** @pad: MBZ. */ + __u32 pad; + + /** + * @label: User pointer to a NUL-terminated string + * + * Length cannot be greater than 4096. + * NULL is permitted and means clear the label. + */ + __u64 label; +}; + /* Definitions for coredump decoding in user space */ #define PANFROSTDUMP_MAJOR 1 #define PANFROSTDUMP_MINOR 0 diff --git a/include/uapi/drm/panthor_drm.h b/include/uapi/drm/panthor_drm.h index ad9a70afea6c..e1f43deb7eca 100644 --- a/include/uapi/drm/panthor_drm.h +++ b/include/uapi/drm/panthor_drm.h @@ -130,6 +130,20 @@ enum drm_panthor_ioctl_id { /** @DRM_PANTHOR_BO_SET_LABEL: Label a BO. */ DRM_PANTHOR_BO_SET_LABEL, + + /** + * @DRM_PANTHOR_SET_USER_MMIO_OFFSET: Set the offset to use as the user MMIO offset. + * + * The default behavior is to pick the MMIO offset based on the size of the pgoff_t + * type seen by the process that manipulates the FD, such that a 32-bit process can + * always map the user MMIO ranges. But this approach doesn't work well for emulators + * like FEX, where the emulator is an 64-bit binary which might be executing 32-bit + * code. In that case, the kernel thinks it's the 64-bit process and assumes + * DRM_PANTHOR_USER_MMIO_OFFSET_64BIT is in use, but the UMD library expects + * DRM_PANTHOR_USER_MMIO_OFFSET_32BIT, because it can't mmap() anything above the + * pgoff_t size. + */ + DRM_PANTHOR_SET_USER_MMIO_OFFSET, }; /** @@ -296,6 +310,9 @@ struct drm_panthor_gpu_info { /** @as_present: Bitmask encoding the number of address-space exposed by the MMU. */ __u32 as_present; + /** @pad0: MBZ. */ + __u32 pad0; + /** @shader_present: Bitmask encoding the shader cores exposed by the GPU. */ __u64 shader_present; @@ -999,6 +1016,28 @@ struct drm_panthor_bo_set_label { }; /** + * struct drm_panthor_set_user_mmio_offset - Arguments passed to + * DRM_IOCTL_PANTHOR_SET_USER_MMIO_OFFSET + * + * This ioctl is only really useful if you want to support userspace + * CPU emulation environments where the size of an unsigned long differs + * between the host and the guest architectures. + */ +struct drm_panthor_set_user_mmio_offset { + /** + * @offset: User MMIO offset to use. + * + * Must be either DRM_PANTHOR_USER_MMIO_OFFSET_32BIT or + * DRM_PANTHOR_USER_MMIO_OFFSET_64BIT. + * + * Use DRM_PANTHOR_USER_MMIO_OFFSET (which selects OFFSET_32BIT or + * OFFSET_64BIT based on the size of an unsigned long) unless you + * have a very good reason to overrule this decision. + */ + __u64 offset; +}; + +/** * DRM_IOCTL_PANTHOR() - Build a Panthor IOCTL number * @__access: Access type. Must be R, W or RW. * @__id: One of the DRM_PANTHOR_xxx id. @@ -1042,6 +1081,8 @@ enum { DRM_IOCTL_PANTHOR(WR, TILER_HEAP_DESTROY, tiler_heap_destroy), DRM_IOCTL_PANTHOR_BO_SET_LABEL = DRM_IOCTL_PANTHOR(WR, BO_SET_LABEL, bo_set_label), + DRM_IOCTL_PANTHOR_SET_USER_MMIO_OFFSET = + DRM_IOCTL_PANTHOR(WR, SET_USER_MMIO_OFFSET, set_user_mmio_offset), }; #if defined(__cplusplus) |
