Merge tag 'drm-misc-next-2024-11-08' of https://gitlab.freedesktop.org/drm/misc/kernel into drm-next

drm-misc-next for v6.13:

UAPI Changes:
- Add 1X7X5 media-bus formats.

Cross-subsystem Changes:
- Maintainer updates for VKMS and IT6263.
- Add media-bus-fmt for MEDIA_BUS_FMT_RGB101010_1X7X5_*.
- Add IT6263 DT bindings and driver.

Core Changes:
- Add ABGR210101010 support to panic handler.
- Use ATOMIC64_INIT in drm_file.c
- Improve scheduler teardown documentation.

Driver Changes:
- Make mediatek compile on ARM again.
- Add missing drm/drm_bridge.h header include, already in drm-next.
- Small fixes and cleanups to vkms, bridge/it6505, panfrost, panthor.
- Add panic support to nouveau for nv50+.

Signed-off-by: Dave Airlie <airlied@redhat.com>
From: Maarten Lankhorst <maarten.lankhorst@linux.intel.com>
Link: https://patchwork.freedesktop.org/patch/msgid/344afe41-d27b-408a-8542-bfecfd3555f6@linux.intel.com
This commit is contained in:
Dave Airlie 2024-11-11 12:10:48 +10:00
commit 56b70bf9ec
29 changed files with 1754 additions and 348 deletions

View File

@ -0,0 +1,250 @@
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/display/bridge/ite,it6263.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: ITE IT6263 LVDS to HDMI converter
maintainers:
- Liu Ying <victor.liu@nxp.com>
description: |
The IT6263 is a high-performance single-chip De-SSC(De-Spread Spectrum) LVDS
to HDMI converter. Combined with LVDS receiver and HDMI 1.4a transmitter,
the IT6263 supports LVDS input and HDMI 1.4 output by conversion function.
The built-in LVDS receiver can support single-link and dual-link LVDS inputs,
and the built-in HDMI transmitter is fully compliant with HDMI 1.4a/3D, HDCP
1.2 and backward compatible with DVI 1.0 specification.
The IT6263 also encodes and transmits up to 8 channels of I2S digital audio,
with sampling rate up to 192KHz and sample size up to 24 bits. In addition,
an S/PDIF input port takes in compressed audio of up to 192KHz frame rate.
The newly supported High-Bit Rate(HBR) audio by HDMI specifications v1.3 is
provided by the IT6263 in two interfaces: the four I2S input ports or the
S/PDIF input port. With both interfaces the highest possible HBR frame rate
is supported at up to 768KHz.
allOf:
- $ref: /schemas/display/lvds-dual-ports.yaml#
properties:
compatible:
const: ite,it6263
reg:
maxItems: 1
clocks:
maxItems: 1
description: audio master clock
clock-names:
const: mclk
data-mapping:
enum:
- jeida-18
- jeida-24
- jeida-30
- vesa-24
- vesa-30
reset-gpios:
maxItems: 1
ivdd-supply:
description: 1.8V digital logic power
ovdd-supply:
description: 3.3V I/O pin power
txavcc18-supply:
description: 1.8V HDMI analog frontend power
txavcc33-supply:
description: 3.3V HDMI analog frontend power
pvcc1-supply:
description: 1.8V HDMI frontend core PLL power
pvcc2-supply:
description: 1.8V HDMI frontend filter PLL power
avcc-supply:
description: 3.3V LVDS frontend power
anvdd-supply:
description: 1.8V LVDS frontend analog power
apvdd-supply:
description: 1.8V LVDS frontend PLL power
"#sound-dai-cells":
const: 0
ite,i2s-audio-fifo-sources:
$ref: /schemas/types.yaml#/definitions/uint32-array
minItems: 1
maxItems: 4
items:
enum: [0, 1, 2, 3]
description:
Each array element indicates the pin number of an I2S serial data input
line which is connected to an audio FIFO, from audio FIFO0 to FIFO3.
ite,rl-channel-swap-audio-sources:
$ref: /schemas/types.yaml#/definitions/uint32-array
minItems: 1
maxItems: 4
uniqueItems: true
items:
enum: [0, 1, 2, 3]
description:
Each array element indicates an audio source whose right channel and left
channel are swapped by this converter. For I2S, the element is the pin
number of an I2S serial data input line. For S/PDIF, the element is always
0.
ports:
$ref: /schemas/graph.yaml#/properties/ports
properties:
port@0: true
port@1:
oneOf:
- required: [dual-lvds-odd-pixels]
- required: [dual-lvds-even-pixels]
port@2:
$ref: /schemas/graph.yaml#/properties/port
description: video port for the HDMI output
port@3:
$ref: /schemas/graph.yaml#/properties/port
description: sound input port
required:
- port@0
- port@2
required:
- compatible
- reg
- data-mapping
- ivdd-supply
- ovdd-supply
- txavcc18-supply
- txavcc33-supply
- pvcc1-supply
- pvcc2-supply
- avcc-supply
- anvdd-supply
- apvdd-supply
unevaluatedProperties: false
examples:
- |
/* single-link LVDS input */
#include <dt-bindings/gpio/gpio.h>
i2c {
#address-cells = <1>;
#size-cells = <0>;
hdmi@4c {
compatible = "ite,it6263";
reg = <0x4c>;
data-mapping = "jeida-24";
reset-gpios = <&gpio1 10 GPIO_ACTIVE_LOW>;
ivdd-supply = <&reg_buck5>;
ovdd-supply = <&reg_vext_3v3>;
txavcc18-supply = <&reg_buck5>;
txavcc33-supply = <&reg_vext_3v3>;
pvcc1-supply = <&reg_buck5>;
pvcc2-supply = <&reg_buck5>;
avcc-supply = <&reg_vext_3v3>;
anvdd-supply = <&reg_buck5>;
apvdd-supply = <&reg_buck5>;
ports {
#address-cells = <1>;
#size-cells = <0>;
port@0 {
reg = <0>;
it6263_lvds_link1: endpoint {
remote-endpoint = <&ldb_lvds_ch0>;
};
};
port@2 {
reg = <2>;
it6263_out: endpoint {
remote-endpoint = <&hdmi_in>;
};
};
};
};
};
- |
/* dual-link LVDS input */
#include <dt-bindings/gpio/gpio.h>
i2c {
#address-cells = <1>;
#size-cells = <0>;
hdmi@4c {
compatible = "ite,it6263";
reg = <0x4c>;
data-mapping = "jeida-24";
reset-gpios = <&gpio1 10 GPIO_ACTIVE_LOW>;
ivdd-supply = <&reg_buck5>;
ovdd-supply = <&reg_vext_3v3>;
txavcc18-supply = <&reg_buck5>;
txavcc33-supply = <&reg_vext_3v3>;
pvcc1-supply = <&reg_buck5>;
pvcc2-supply = <&reg_buck5>;
avcc-supply = <&reg_vext_3v3>;
anvdd-supply = <&reg_buck5>;
apvdd-supply = <&reg_buck5>;
ports {
#address-cells = <1>;
#size-cells = <0>;
port@0 {
reg = <0>;
dual-lvds-odd-pixels;
it6263_lvds_link1_dual: endpoint {
remote-endpoint = <&ldb_lvds_ch0>;
};
};
port@1 {
reg = <1>;
dual-lvds-even-pixels;
it6263_lvds_link2_dual: endpoint {
remote-endpoint = <&ldb_lvds_ch1>;
};
};
port@2 {
reg = <2>;
it6263_out_dual: endpoint {
remote-endpoint = <&hdmi_in>;
};
};
};
};
};

View File

@ -26,12 +26,17 @@ description: |
Device compatible with those specifications have been marketed under the
FPD-Link and FlatLink brands.
This bindings also supports 30-bit data mapping compatible with JEIDA and
VESA.
properties:
data-mapping:
enum:
- jeida-18
- jeida-24
- jeida-30
- vesa-24
- vesa-30
description: |
The color signals mapping order.
@ -60,6 +65,19 @@ properties:
DATA2 ><_CTL2_><_CTL1_><_CTL0_><__B7__><__B6__><__B5__><__B4__><
DATA3 ><_CTL3_><__B1__><__B0__><__G1__><__G0__><__R1__><__R0__><
- "jeida-30" - 30-bit data mapping compatible with JEIDA and VESA. Data
are transferred as follows on 5 LVDS lanes.
Slot 0 1 2 3 4 5 6
________________ _________________
Clock \_______________________/
______ ______ ______ ______ ______ ______ ______
DATA0 ><__G4__><__R9__><__R8__><__R7__><__R6__><__R5__><__R4__><
DATA1 ><__B5__><__B4__><__G9__><__G8__><__G7__><__G6__><__G5__><
DATA2 ><_CTL2_><_CTL1_><_CTL0_><__B9__><__B8__><__B7__><__B6__><
DATA3 ><_CTL3_><__B3__><__B2__><__G3__><__G2__><__R3__><__R2__><
DATA4 ><_CTL3_><__B1__><__B0__><__G1__><__G0__><__R1__><__R0__><
- "vesa-24" - 24-bit data mapping compatible with the [VESA] specification.
Data are transferred as follows on 4 LVDS lanes.
@ -72,6 +90,19 @@ properties:
DATA2 ><_CTL2_><_CTL1_><_CTL0_><__B5__><__B4__><__B3__><__B2__><
DATA3 ><_CTL3_><__B7__><__B6__><__G7__><__G6__><__R7__><__R6__><
- "vesa-30" - 30-bit data mapping compatible with VESA. Data are
transferred as follows on 5 LVDS lanes.
Slot 0 1 2 3 4 5 6
________________ _________________
Clock \_______________________/
______ ______ ______ ______ ______ ______ ______
DATA0 ><__G0__><__R5__><__R4__><__R3__><__R2__><__R1__><__R0__><
DATA1 ><__B1__><__B0__><__G5__><__G4__><__G3__><__G2__><__G1__><
DATA2 ><_CTL2_><_CTL1_><_CTL0_><__B5__><__B4__><__B3__><__B2__><
DATA3 ><_CTL3_><__B7__><__B6__><__G7__><__G6__><__R7__><__R6__><
DATA4 ><_CTL3_><__B9__><__B8__><__G9__><__G8__><__R9__><__R8__><
Control signals are mapped as follows.
CTL0: HSync

View File

@ -0,0 +1,63 @@
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/display/lvds-dual-ports.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: Dual-link LVDS Display Common Properties
maintainers:
- Liu Ying <victor.liu@nxp.com>
description: |
Common properties for LVDS displays with dual LVDS links. Extend LVDS display
common properties defined in lvds.yaml.
Dual-link LVDS displays receive odd pixels and even pixels separately from
the dual LVDS links. One link receives odd pixels and the other receives
even pixels. Some of those displays may also use only one LVDS link to
receive all pixels, being odd and even agnostic.
allOf:
- $ref: lvds.yaml#
properties:
ports:
$ref: /schemas/graph.yaml#/properties/ports
patternProperties:
'^port@[01]$':
$ref: /schemas/graph.yaml#/$defs/port-base
unevaluatedProperties: false
description: |
port@0 represents the first LVDS input link.
port@1 represents the second LVDS input link.
properties:
dual-lvds-odd-pixels:
type: boolean
description: LVDS input link for odd pixels
dual-lvds-even-pixels:
type: boolean
description: LVDS input link for even pixels
oneOf:
- required: [dual-lvds-odd-pixels]
- required: [dual-lvds-even-pixels]
- properties:
dual-lvds-odd-pixels: false
dual-lvds-even-pixels: false
anyOf:
- required:
- port@0
- required:
- port@1
required:
- ports
additionalProperties: true
...

View File

@ -20,6 +20,7 @@ description: |
dual-lvds-odd-pixels or dual-lvds-even-pixels).
allOf:
- $ref: /schemas/display/lvds-dual-ports.yaml#
- $ref: panel-common.yaml#
properties:
@ -44,22 +45,10 @@ properties:
properties:
port@0:
$ref: /schemas/graph.yaml#/$defs/port-base
unevaluatedProperties: false
description: The sink for odd pixels.
properties:
dual-lvds-odd-pixels: true
required:
- dual-lvds-odd-pixels
port@1:
$ref: /schemas/graph.yaml#/$defs/port-base
unevaluatedProperties: false
description: The sink for even pixels.
properties:
dual-lvds-even-pixels: true
required:
- dual-lvds-even-pixels
@ -75,7 +64,6 @@ required:
- height-mm
- data-mapping
- panel-timing
- ports
examples:
- |+

View File

@ -22,6 +22,7 @@ description: |
If the panel is more advanced a dedicated binding file is required.
allOf:
- $ref: /schemas/display/lvds-dual-ports.yaml#
- $ref: panel-common.yaml#
properties:
@ -55,28 +56,10 @@ properties:
properties:
port@0:
$ref: /schemas/graph.yaml#/$defs/port-base
unevaluatedProperties: false
description: The first sink port.
properties:
dual-lvds-odd-pixels:
type: boolean
description: The first sink port for odd pixels.
required:
- dual-lvds-odd-pixels
port@1:
$ref: /schemas/graph.yaml#/$defs/port-base
unevaluatedProperties: false
description: The second sink port.
properties:
dual-lvds-even-pixels:
type: boolean
description: The second sink port for even pixels.
required:
- dual-lvds-even-pixels
@ -88,7 +71,6 @@ unevaluatedProperties: false
required:
- compatible
- ports
- power-supply
examples:

View File

@ -2225,7 +2225,7 @@ The following table list existing packed 48bit wide RGB formats.
\endgroup
On LVDS buses, usually each sample is transferred serialized in seven
time slots per pixel clock, on three (18-bit) or four (24-bit)
time slots per pixel clock, on three (18-bit) or four (24-bit) or five (30-bit)
differential data pairs at the same time. The remaining bits are used
for control signals as defined by SPWG/PSWG/VESA or JEIDA standards. The
24-bit RGB format serialized in seven time slots on four lanes using
@ -2246,11 +2246,12 @@ JEIDA defined bit mapping will be named
- Code
-
-
- :cspan:`3` Data organization
- :cspan:`4` Data organization
* -
-
- Timeslot
- Lane
- 4
- 3
- 2
- 1
@ -2262,6 +2263,7 @@ JEIDA defined bit mapping will be named
- 0
-
-
-
- d
- b\ :sub:`1`
- g\ :sub:`0`
@ -2270,6 +2272,7 @@ JEIDA defined bit mapping will be named
- 1
-
-
-
- d
- b\ :sub:`0`
- r\ :sub:`5`
@ -2278,6 +2281,7 @@ JEIDA defined bit mapping will be named
- 2
-
-
-
- d
- g\ :sub:`5`
- r\ :sub:`4`
@ -2286,6 +2290,7 @@ JEIDA defined bit mapping will be named
- 3
-
-
-
- b\ :sub:`5`
- g\ :sub:`4`
- r\ :sub:`3`
@ -2294,6 +2299,7 @@ JEIDA defined bit mapping will be named
- 4
-
-
-
- b\ :sub:`4`
- g\ :sub:`3`
- r\ :sub:`2`
@ -2302,6 +2308,7 @@ JEIDA defined bit mapping will be named
- 5
-
-
-
- b\ :sub:`3`
- g\ :sub:`2`
- r\ :sub:`1`
@ -2310,6 +2317,7 @@ JEIDA defined bit mapping will be named
- 6
-
-
-
- b\ :sub:`2`
- g\ :sub:`1`
- r\ :sub:`0`
@ -2319,6 +2327,7 @@ JEIDA defined bit mapping will be named
- 0x1011
- 0
-
-
- d
- d
- b\ :sub:`1`
@ -2327,6 +2336,7 @@ JEIDA defined bit mapping will be named
-
- 1
-
-
- b\ :sub:`7`
- d
- b\ :sub:`0`
@ -2335,6 +2345,7 @@ JEIDA defined bit mapping will be named
-
- 2
-
-
- b\ :sub:`6`
- d
- g\ :sub:`5`
@ -2343,6 +2354,7 @@ JEIDA defined bit mapping will be named
-
- 3
-
-
- g\ :sub:`7`
- b\ :sub:`5`
- g\ :sub:`4`
@ -2351,6 +2363,7 @@ JEIDA defined bit mapping will be named
-
- 4
-
-
- g\ :sub:`6`
- b\ :sub:`4`
- g\ :sub:`3`
@ -2359,6 +2372,7 @@ JEIDA defined bit mapping will be named
-
- 5
-
-
- r\ :sub:`7`
- b\ :sub:`3`
- g\ :sub:`2`
@ -2367,6 +2381,7 @@ JEIDA defined bit mapping will be named
-
- 6
-
-
- r\ :sub:`6`
- b\ :sub:`2`
- g\ :sub:`1`
@ -2377,6 +2392,7 @@ JEIDA defined bit mapping will be named
- 0x1012
- 0
-
-
- d
- d
- b\ :sub:`3`
@ -2385,6 +2401,7 @@ JEIDA defined bit mapping will be named
-
- 1
-
-
- b\ :sub:`1`
- d
- b\ :sub:`2`
@ -2393,6 +2410,7 @@ JEIDA defined bit mapping will be named
-
- 2
-
-
- b\ :sub:`0`
- d
- g\ :sub:`7`
@ -2401,6 +2419,7 @@ JEIDA defined bit mapping will be named
-
- 3
-
-
- g\ :sub:`1`
- b\ :sub:`7`
- g\ :sub:`6`
@ -2409,6 +2428,7 @@ JEIDA defined bit mapping will be named
-
- 4
-
-
- g\ :sub:`0`
- b\ :sub:`6`
- g\ :sub:`5`
@ -2417,6 +2437,7 @@ JEIDA defined bit mapping will be named
-
- 5
-
-
- r\ :sub:`1`
- b\ :sub:`5`
- g\ :sub:`4`
@ -2425,10 +2446,141 @@ JEIDA defined bit mapping will be named
-
- 6
-
-
- r\ :sub:`0`
- b\ :sub:`4`
- g\ :sub:`3`
- r\ :sub:`2`
* .. _MEDIA-BUS-FMT-RGB101010-1X7X5-SPWG:
- MEDIA_BUS_FMT_RGB101010_1X7X5_SPWG
- 0x1026
- 0
-
- d
- d
- d
- b\ :sub:`1`
- g\ :sub:`0`
* -
-
- 1
-
- b\ :sub:`9`
- b\ :sub:`7`
- d
- b\ :sub:`0`
- r\ :sub:`5`
* -
-
- 2
-
- b\ :sub:`8`
- b\ :sub:`6`
- d
- g\ :sub:`5`
- r\ :sub:`4`
* -
-
- 3
-
- g\ :sub:`9`
- g\ :sub:`7`
- b\ :sub:`5`
- g\ :sub:`4`
- r\ :sub:`3`
* -
-
- 4
-
- g\ :sub:`8`
- g\ :sub:`6`
- b\ :sub:`4`
- g\ :sub:`3`
- r\ :sub:`2`
* -
-
- 5
-
- r\ :sub:`9`
- r\ :sub:`7`
- b\ :sub:`3`
- g\ :sub:`2`
- r\ :sub:`1`
* -
-
- 6
-
- r\ :sub:`8`
- r\ :sub:`6`
- b\ :sub:`2`
- g\ :sub:`1`
- r\ :sub:`0`
* .. _MEDIA-BUS-FMT-RGB101010-1X7X5-JEIDA:
- MEDIA_BUS_FMT_RGB101010_1X7X5_JEIDA
- 0x1027
- 0
-
- d
- d
- d
- b\ :sub:`5`
- g\ :sub:`4`
* -
-
- 1
-
- b\ :sub:`1`
- b\ :sub:`3`
- d
- b\ :sub:`4`
- r\ :sub:`9`
* -
-
- 2
-
- b\ :sub:`0`
- b\ :sub:`2`
- d
- g\ :sub:`9`
- r\ :sub:`8`
* -
-
- 3
-
- g\ :sub:`1`
- g\ :sub:`3`
- b\ :sub:`9`
- g\ :sub:`8`
- r\ :sub:`7`
* -
-
- 4
-
- g\ :sub:`0`
- g\ :sub:`2`
- b\ :sub:`8`
- g\ :sub:`7`
- r\ :sub:`6`
* -
-
- 5
-
- r\ :sub:`1`
- r\ :sub:`3`
- b\ :sub:`7`
- g\ :sub:`6`
- r\ :sub:`5`
* -
-
- 6
-
- r\ :sub:`0`
- r\ :sub:`2`
- b\ :sub:`6`
- g\ :sub:`5`
- r\ :sub:`4`
.. raw:: latex

View File

@ -7448,7 +7448,6 @@ T: git https://gitlab.freedesktop.org/drm/misc/kernel.git
F: drivers/gpu/drm/udl/
DRM DRIVER FOR VIRTUAL KERNEL MODESETTING (VKMS)
M: Maíra Canal <mairacanal@riseup.net>
M: Louis Chauvet <louis.chauvet@bootlin.com>
R: Haneen Mohammed <hamohammed.sa@gmail.com>
R: Simona Vetter <simona@ffwll.ch>
@ -12135,6 +12134,14 @@ W: https://linuxtv.org
Q: http://patchwork.linuxtv.org/project/linux-media/list/
F: drivers/media/tuners/it913x*
ITE IT6263 LVDS TO HDMI BRIDGE DRIVER
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/bridge/ite,it6263.yaml
F: drivers/gpu/drm/bridge/ite-it6263.c
ITE IT66121 HDMI BRIDGE DRIVER
M: Phong LE <ple@baylibre.com>
M: Neil Armstrong <neil.armstrong@linaro.org>

View File

@ -90,6 +90,17 @@ config DRM_FSL_LDB
help
Support for i.MX8MP DPI-to-LVDS on-SoC encoder.
config DRM_ITE_IT6263
tristate "ITE IT6263 LVDS/HDMI bridge"
depends on OF
select DRM_DISPLAY_HDMI_STATE_HELPER
select DRM_DISPLAY_HELPER
select DRM_BRIDGE_CONNECTOR
select DRM_KMS_HELPER
select REGMAP_I2C
help
ITE IT6263 LVDS to HDMI bridge chip driver.
config DRM_ITE_IT6505
tristate "ITE IT6505 DisplayPort bridge"
depends on OF

View File

@ -6,6 +6,7 @@ obj-$(CONFIG_DRM_CHRONTEL_CH7033) += chrontel-ch7033.o
obj-$(CONFIG_DRM_CROS_EC_ANX7688) += cros-ec-anx7688.o
obj-$(CONFIG_DRM_DISPLAY_CONNECTOR) += display-connector.o
obj-$(CONFIG_DRM_FSL_LDB) += fsl-ldb.o
obj-$(CONFIG_DRM_ITE_IT6263) += ite-it6263.o
obj-$(CONFIG_DRM_ITE_IT6505) += ite-it6505.o
obj-$(CONFIG_DRM_LONTIUM_LT8912B) += lontium-lt8912b.o
obj-$(CONFIG_DRM_LONTIUM_LT9211) += lontium-lt9211.o

View File

@ -0,0 +1,898 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright 2024 NXP
*/
#include <linux/bitfield.h>
#include <linux/bits.h>
#include <linux/delay.h>
#include <linux/gpio/consumer.h>
#include <linux/hdmi.h>
#include <linux/i2c.h>
#include <linux/media-bus-format.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/regmap.h>
#include <linux/regulator/consumer.h>
#include <drm/display/drm_hdmi_helper.h>
#include <drm/display/drm_hdmi_state_helper.h>
#include <drm/drm_atomic.h>
#include <drm/drm_atomic_helper.h>
#include <drm/drm_atomic_state_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_edid.h>
#include <drm/drm_of.h>
#include <drm/drm_probe_helper.h>
/* -----------------------------------------------------------------------------
* LVDS registers
*/
/* LVDS software reset registers */
#define LVDS_REG_05 0x05
#define REG_SOFT_P_RST BIT(1)
/* LVDS system configuration registers */
/* 0x0b */
#define LVDS_REG_0B 0x0b
#define REG_SSC_PCLK_RF BIT(0)
#define REG_LVDS_IN_SWAP BIT(1)
/* LVDS test pattern gen control registers */
/* 0x2c */
#define LVDS_REG_2C 0x2c
#define REG_COL_DEP GENMASK(1, 0)
#define BIT8 FIELD_PREP(REG_COL_DEP, 1)
#define OUT_MAP BIT(4)
#define JEIDA 0
#define REG_DESSC_ENB BIT(6)
#define DMODE BIT(7)
#define DISO BIT(7)
#define SISO 0
#define LVDS_REG_3C 0x3c
#define LVDS_REG_3F 0x3f
#define LVDS_REG_47 0x47
#define LVDS_REG_48 0x48
#define LVDS_REG_4F 0x4f
#define LVDS_REG_52 0x52
/* -----------------------------------------------------------------------------
* HDMI registers are separated into three banks:
* 1) HDMI register common bank: 0x00 ~ 0x2f
*/
/* HDMI genernal registers */
#define HDMI_REG_SW_RST 0x04
#define SOFTREF_RST BIT(5)
#define SOFTA_RST BIT(4)
#define SOFTV_RST BIT(3)
#define AUD_RST BIT(2)
#define HDCP_RST BIT(0)
#define HDMI_RST_ALL (SOFTREF_RST | SOFTA_RST | SOFTV_RST | \
AUD_RST | HDCP_RST)
#define HDMI_REG_SYS_STATUS 0x0e
#define HPDETECT BIT(6)
#define TXVIDSTABLE BIT(4)
#define HDMI_REG_BANK_CTRL 0x0f
#define REG_BANK_SEL BIT(0)
/* HDMI System DDC control registers */
#define HDMI_REG_DDC_MASTER_CTRL 0x10
#define MASTER_SEL_HOST BIT(0)
#define HDMI_REG_DDC_HEADER 0x11
#define HDMI_REG_DDC_REQOFF 0x12
#define HDMI_REG_DDC_REQCOUNT 0x13
#define HDMI_REG_DDC_EDIDSEG 0x14
#define HDMI_REG_DDC_CMD 0x15
#define DDC_CMD_EDID_READ 0x3
#define DDC_CMD_FIFO_CLR 0x9
#define HDMI_REG_DDC_STATUS 0x16
#define DDC_DONE BIT(7)
#define DDC_NOACK BIT(5)
#define DDC_WAITBUS BIT(4)
#define DDC_ARBILOSE BIT(3)
#define DDC_ERROR (DDC_NOACK | DDC_WAITBUS | DDC_ARBILOSE)
#define HDMI_DDC_FIFO_BYTES 32
#define HDMI_REG_DDC_READFIFO 0x17
#define HDMI_REG_LVDS_PORT 0x1d /* LVDS input control I2C addr */
#define HDMI_REG_LVDS_PORT_EN 0x1e
#define LVDS_INPUT_CTRL_I2C_ADDR 0x33
/* -----------------------------------------------------------------------------
* 2) HDMI register bank0: 0x30 ~ 0xff
*/
/* HDMI AFE registers */
#define HDMI_REG_AFE_DRV_CTRL 0x61
#define AFE_DRV_PWD BIT(5)
#define AFE_DRV_RST BIT(4)
#define HDMI_REG_AFE_XP_CTRL 0x62
#define AFE_XP_GAINBIT BIT(7)
#define AFE_XP_ER0 BIT(4)
#define AFE_XP_RESETB BIT(3)
#define HDMI_REG_AFE_ISW_CTRL 0x63
#define HDMI_REG_AFE_IP_CTRL 0x64
#define AFE_IP_GAINBIT BIT(7)
#define AFE_IP_ER0 BIT(3)
#define AFE_IP_RESETB BIT(2)
/* HDMI input data format registers */
#define HDMI_REG_INPUT_MODE 0x70
#define IN_RGB 0x00
/* HDMI general control registers */
#define HDMI_REG_HDMI_MODE 0xc0
#define TX_HDMI_MODE BIT(0)
#define HDMI_REG_GCP 0xc1
#define AVMUTE BIT(0)
#define HDMI_COLOR_DEPTH GENMASK(6, 4)
#define HDMI_COLOR_DEPTH_24 FIELD_PREP(HDMI_COLOR_DEPTH, 4)
#define HDMI_REG_PKT_GENERAL_CTRL 0xc6
#define HDMI_REG_AVI_INFOFRM_CTRL 0xcd
#define ENABLE_PKT BIT(0)
#define REPEAT_PKT BIT(1)
/* -----------------------------------------------------------------------------
* 3) HDMI register bank1: 0x130 ~ 0x1ff (HDMI packet registers)
*/
/* AVI packet registers */
#define HDMI_REG_AVI_DB1 0x158
#define HDMI_REG_AVI_DB2 0x159
#define HDMI_REG_AVI_DB3 0x15a
#define HDMI_REG_AVI_DB4 0x15b
#define HDMI_REG_AVI_DB5 0x15c
#define HDMI_REG_AVI_CSUM 0x15d
#define HDMI_REG_AVI_DB6 0x15e
#define HDMI_REG_AVI_DB7 0x15f
#define HDMI_REG_AVI_DB8 0x160
#define HDMI_REG_AVI_DB9 0x161
#define HDMI_REG_AVI_DB10 0x162
#define HDMI_REG_AVI_DB11 0x163
#define HDMI_REG_AVI_DB12 0x164
#define HDMI_REG_AVI_DB13 0x165
#define HDMI_AVI_DB_CHUNK1_SIZE (HDMI_REG_AVI_DB5 - HDMI_REG_AVI_DB1 + 1)
#define HDMI_AVI_DB_CHUNK2_SIZE (HDMI_REG_AVI_DB13 - HDMI_REG_AVI_DB6 + 1)
/* IT6263 data sheet Rev0.8: LVDS RX supports input clock rate up to 150MHz. */
#define MAX_PIXEL_CLOCK_KHZ 150000
/* IT6263 programming guide Ver0.90: PCLK_HIGH for TMDS clock over 80MHz. */
#define HIGH_PIXEL_CLOCK_KHZ 80000
/*
* IT6263 data sheet Rev0.8: HDMI TX supports link speeds of up to 2.25Gbps
* (link clock rate of 225MHz).
*/
#define MAX_HDMI_TMDS_CHAR_RATE_HZ 225000000
struct it6263 {
struct device *dev;
struct i2c_client *hdmi_i2c;
struct i2c_client *lvds_i2c;
struct regmap *hdmi_regmap;
struct regmap *lvds_regmap;
struct drm_bridge bridge;
struct drm_bridge *next_bridge;
int lvds_data_mapping;
bool lvds_dual_link;
bool lvds_link12_swap;
};
static inline struct it6263 *bridge_to_it6263(struct drm_bridge *bridge)
{
return container_of(bridge, struct it6263, bridge);
}
static bool it6263_hdmi_writeable_reg(struct device *dev, unsigned int reg)
{
switch (reg) {
case HDMI_REG_SW_RST:
case HDMI_REG_BANK_CTRL:
case HDMI_REG_DDC_MASTER_CTRL:
case HDMI_REG_DDC_HEADER:
case HDMI_REG_DDC_REQOFF:
case HDMI_REG_DDC_REQCOUNT:
case HDMI_REG_DDC_EDIDSEG:
case HDMI_REG_DDC_CMD:
case HDMI_REG_LVDS_PORT:
case HDMI_REG_LVDS_PORT_EN:
case HDMI_REG_AFE_DRV_CTRL:
case HDMI_REG_AFE_XP_CTRL:
case HDMI_REG_AFE_ISW_CTRL:
case HDMI_REG_AFE_IP_CTRL:
case HDMI_REG_INPUT_MODE:
case HDMI_REG_HDMI_MODE:
case HDMI_REG_GCP:
case HDMI_REG_PKT_GENERAL_CTRL:
case HDMI_REG_AVI_INFOFRM_CTRL:
case HDMI_REG_AVI_DB1:
case HDMI_REG_AVI_DB2:
case HDMI_REG_AVI_DB3:
case HDMI_REG_AVI_DB4:
case HDMI_REG_AVI_DB5:
case HDMI_REG_AVI_CSUM:
case HDMI_REG_AVI_DB6:
case HDMI_REG_AVI_DB7:
case HDMI_REG_AVI_DB8:
case HDMI_REG_AVI_DB9:
case HDMI_REG_AVI_DB10:
case HDMI_REG_AVI_DB11:
case HDMI_REG_AVI_DB12:
case HDMI_REG_AVI_DB13:
return true;
default:
return false;
}
}
static bool it6263_hdmi_readable_reg(struct device *dev, unsigned int reg)
{
if (it6263_hdmi_writeable_reg(dev, reg))
return true;
switch (reg) {
case HDMI_REG_SYS_STATUS:
case HDMI_REG_DDC_STATUS:
case HDMI_REG_DDC_READFIFO:
return true;
default:
return false;
}
}
static bool it6263_hdmi_volatile_reg(struct device *dev, unsigned int reg)
{
switch (reg) {
case HDMI_REG_SW_RST:
case HDMI_REG_SYS_STATUS:
case HDMI_REG_DDC_STATUS:
case HDMI_REG_DDC_READFIFO:
return true;
default:
return false;
}
}
static const struct regmap_range_cfg it6263_hdmi_range_cfg = {
.range_min = 0x00,
.range_max = HDMI_REG_AVI_DB13,
.selector_reg = HDMI_REG_BANK_CTRL,
.selector_mask = REG_BANK_SEL,
.selector_shift = 0,
.window_start = 0x00,
.window_len = 0x100,
};
static const struct regmap_config it6263_hdmi_regmap_config = {
.name = "it6263-hdmi",
.reg_bits = 8,
.val_bits = 8,
.writeable_reg = it6263_hdmi_writeable_reg,
.readable_reg = it6263_hdmi_readable_reg,
.volatile_reg = it6263_hdmi_volatile_reg,
.max_register = HDMI_REG_AVI_DB13,
.ranges = &it6263_hdmi_range_cfg,
.num_ranges = 1,
.cache_type = REGCACHE_MAPLE,
};
static bool it6263_lvds_writeable_reg(struct device *dev, unsigned int reg)
{
switch (reg) {
case LVDS_REG_05:
case LVDS_REG_0B:
case LVDS_REG_2C:
case LVDS_REG_3C:
case LVDS_REG_3F:
case LVDS_REG_47:
case LVDS_REG_48:
case LVDS_REG_4F:
case LVDS_REG_52:
return true;
default:
return false;
}
}
static bool it6263_lvds_readable_reg(struct device *dev, unsigned int reg)
{
return it6263_lvds_writeable_reg(dev, reg);
}
static bool it6263_lvds_volatile_reg(struct device *dev, unsigned int reg)
{
return reg == LVDS_REG_05;
}
static const struct regmap_config it6263_lvds_regmap_config = {
.name = "it6263-lvds",
.reg_bits = 8,
.val_bits = 8,
.writeable_reg = it6263_lvds_writeable_reg,
.readable_reg = it6263_lvds_readable_reg,
.volatile_reg = it6263_lvds_volatile_reg,
.max_register = LVDS_REG_52,
.cache_type = REGCACHE_MAPLE,
};
static const char * const it6263_supplies[] = {
"ivdd", "ovdd", "txavcc18", "txavcc33", "pvcc1", "pvcc2",
"avcc", "anvdd", "apvdd"
};
static int it6263_parse_dt(struct it6263 *it)
{
struct device *dev = it->dev;
struct device_node *port0, *port1;
int ret = 0;
it->lvds_data_mapping = drm_of_lvds_get_data_mapping(dev->of_node);
if (it->lvds_data_mapping < 0) {
dev_err(dev, "%pOF: invalid or missing %s DT property: %d\n",
dev->of_node, "data-mapping", it->lvds_data_mapping);
return it->lvds_data_mapping;
}
it->next_bridge = devm_drm_of_get_bridge(dev, dev->of_node, 2, 0);
if (IS_ERR(it->next_bridge))
return dev_err_probe(dev, PTR_ERR(it->next_bridge),
"failed to get next bridge\n");
port0 = of_graph_get_port_by_id(dev->of_node, 0);
port1 = of_graph_get_port_by_id(dev->of_node, 1);
if (port0 && port1) {
int order;
it->lvds_dual_link = true;
order = drm_of_lvds_get_dual_link_pixel_order_sink(port0, port1);
if (order < 0) {
dev_err(dev,
"failed to get dual link pixel order: %d\n",
order);
ret = order;
} else if (order == DRM_LVDS_DUAL_LINK_EVEN_ODD_PIXELS) {
it->lvds_link12_swap = true;
}
} else if (port1) {
ret = -EINVAL;
dev_err(dev, "single input LVDS port1 is not supported\n");
} else if (!port0) {
ret = -EINVAL;
dev_err(dev, "no input LVDS port\n");
}
of_node_put(port0);
of_node_put(port1);
return ret;
}
static inline void it6263_hw_reset(struct gpio_desc *reset_gpio)
{
if (!reset_gpio)
return;
gpiod_set_value_cansleep(reset_gpio, 0);
fsleep(1000);
gpiod_set_value_cansleep(reset_gpio, 1);
/* The chip maker says the low pulse should be at least 40ms. */
fsleep(40000);
gpiod_set_value_cansleep(reset_gpio, 0);
/* addtional time to wait the high voltage to be stable */
fsleep(5000);
}
static inline int it6263_lvds_set_i2c_addr(struct it6263 *it)
{
int ret;
ret = regmap_write(it->hdmi_regmap, HDMI_REG_LVDS_PORT,
LVDS_INPUT_CTRL_I2C_ADDR << 1);
if (ret)
return ret;
return regmap_write(it->hdmi_regmap, HDMI_REG_LVDS_PORT_EN, BIT(0));
}
static inline void it6263_lvds_reset(struct it6263 *it)
{
/* AFE PLL reset */
regmap_write_bits(it->lvds_regmap, LVDS_REG_3C, BIT(0), 0x0);
fsleep(1000);
regmap_write_bits(it->lvds_regmap, LVDS_REG_3C, BIT(0), BIT(0));
/* software pixel clock domain reset */
regmap_write_bits(it->lvds_regmap, LVDS_REG_05, REG_SOFT_P_RST,
REG_SOFT_P_RST);
fsleep(1000);
regmap_write_bits(it->lvds_regmap, LVDS_REG_05, REG_SOFT_P_RST, 0x0);
fsleep(10000);
}
static inline void it6263_lvds_set_interface(struct it6263 *it)
{
/* color depth */
regmap_write_bits(it->lvds_regmap, LVDS_REG_2C, REG_COL_DEP, BIT8);
/* output mapping */
regmap_write_bits(it->lvds_regmap, LVDS_REG_2C, OUT_MAP, JEIDA);
if (it->lvds_dual_link) {
regmap_write_bits(it->lvds_regmap, LVDS_REG_2C, DMODE, DISO);
regmap_write_bits(it->lvds_regmap, LVDS_REG_52, BIT(1), BIT(1));
} else {
regmap_write_bits(it->lvds_regmap, LVDS_REG_2C, DMODE, SISO);
regmap_write_bits(it->lvds_regmap, LVDS_REG_52, BIT(1), 0);
}
}
static inline void it6263_lvds_set_afe(struct it6263 *it)
{
regmap_write(it->lvds_regmap, LVDS_REG_3C, 0xaa);
regmap_write(it->lvds_regmap, LVDS_REG_3F, 0x02);
regmap_write(it->lvds_regmap, LVDS_REG_47, 0xaa);
regmap_write(it->lvds_regmap, LVDS_REG_48, 0x02);
regmap_write(it->lvds_regmap, LVDS_REG_4F, 0x11);
regmap_write_bits(it->lvds_regmap, LVDS_REG_0B, REG_SSC_PCLK_RF,
REG_SSC_PCLK_RF);
regmap_write_bits(it->lvds_regmap, LVDS_REG_3C, 0x07, 0);
regmap_write_bits(it->lvds_regmap, LVDS_REG_2C, REG_DESSC_ENB,
REG_DESSC_ENB);
}
static inline void it6263_lvds_sys_cfg(struct it6263 *it)
{
regmap_write_bits(it->lvds_regmap, LVDS_REG_0B, REG_LVDS_IN_SWAP,
it->lvds_link12_swap ? REG_LVDS_IN_SWAP : 0);
}
static inline void it6263_lvds_config(struct it6263 *it)
{
it6263_lvds_reset(it);
it6263_lvds_set_interface(it);
it6263_lvds_set_afe(it);
it6263_lvds_sys_cfg(it);
}
static inline void it6263_hdmi_config(struct it6263 *it)
{
regmap_write(it->hdmi_regmap, HDMI_REG_SW_RST, HDMI_RST_ALL);
regmap_write(it->hdmi_regmap, HDMI_REG_INPUT_MODE, IN_RGB);
regmap_write_bits(it->hdmi_regmap, HDMI_REG_GCP, HDMI_COLOR_DEPTH,
HDMI_COLOR_DEPTH_24);
}
static enum drm_connector_status it6263_detect(struct it6263 *it)
{
unsigned int val;
regmap_read(it->hdmi_regmap, HDMI_REG_SYS_STATUS, &val);
if (val & HPDETECT)
return connector_status_connected;
else
return connector_status_disconnected;
}
static int it6263_read_edid(void *data, u8 *buf, unsigned int block, size_t len)
{
struct it6263 *it = data;
struct regmap *regmap = it->hdmi_regmap;
unsigned int start = (block % 2) * EDID_LENGTH;
unsigned int segment = block >> 1;
unsigned int count, val;
int ret;
regmap_write(regmap, HDMI_REG_DDC_MASTER_CTRL, MASTER_SEL_HOST);
regmap_write(regmap, HDMI_REG_DDC_HEADER, DDC_ADDR << 1);
regmap_write(regmap, HDMI_REG_DDC_EDIDSEG, segment);
while (len) {
/* clear DDC FIFO */
regmap_write(regmap, HDMI_REG_DDC_CMD, DDC_CMD_FIFO_CLR);
ret = regmap_read_poll_timeout(regmap, HDMI_REG_DDC_STATUS,
val, val & DDC_DONE,
2000, 10000);
if (ret) {
dev_err(it->dev, "failed to clear DDC FIFO:%d\n", ret);
return ret;
}
count = len > HDMI_DDC_FIFO_BYTES ? HDMI_DDC_FIFO_BYTES : len;
/* fire the read command */
regmap_write(regmap, HDMI_REG_DDC_REQOFF, start);
regmap_write(regmap, HDMI_REG_DDC_REQCOUNT, count);
regmap_write(regmap, HDMI_REG_DDC_CMD, DDC_CMD_EDID_READ);
start += count;
len -= count;
ret = regmap_read_poll_timeout(regmap, HDMI_REG_DDC_STATUS, val,
val & (DDC_DONE | DDC_ERROR),
20000, 250000);
if (ret && !(val & DDC_ERROR)) {
dev_err(it->dev, "failed to read EDID:%d\n", ret);
return ret;
}
if (val & DDC_ERROR) {
dev_err(it->dev, "DDC error\n");
return -EIO;
}
/* cache to buffer */
for (; count > 0; count--) {
regmap_read(regmap, HDMI_REG_DDC_READFIFO, &val);
*(buf++) = val;
}
}
return 0;
}
static int it6263_bridge_atomic_check(struct drm_bridge *bridge,
struct drm_bridge_state *bridge_state,
struct drm_crtc_state *crtc_state,
struct drm_connector_state *conn_state)
{
return drm_atomic_helper_connector_hdmi_check(conn_state->connector,
conn_state->state);
}
static void
it6263_bridge_atomic_disable(struct drm_bridge *bridge,
struct drm_bridge_state *old_bridge_state)
{
struct it6263 *it = bridge_to_it6263(bridge);
regmap_write_bits(it->hdmi_regmap, HDMI_REG_GCP, AVMUTE, AVMUTE);
regmap_write(it->hdmi_regmap, HDMI_REG_PKT_GENERAL_CTRL, 0);
regmap_write(it->hdmi_regmap, HDMI_REG_AFE_DRV_CTRL,
AFE_DRV_RST | AFE_DRV_PWD);
}
static void
it6263_bridge_atomic_enable(struct drm_bridge *bridge,
struct drm_bridge_state *old_bridge_state)
{
struct drm_atomic_state *state = old_bridge_state->base.state;
struct it6263 *it = bridge_to_it6263(bridge);
const struct drm_crtc_state *crtc_state;
struct regmap *regmap = it->hdmi_regmap;
const struct drm_display_mode *mode;
struct drm_connector *connector;
bool is_stable = false;
struct drm_crtc *crtc;
unsigned int val;
bool pclk_high;
int i, ret;
connector = drm_atomic_get_new_connector_for_encoder(state,
bridge->encoder);
crtc = drm_atomic_get_new_connector_state(state, connector)->crtc;
crtc_state = drm_atomic_get_new_crtc_state(state, crtc);
mode = &crtc_state->adjusted_mode;
regmap_write(regmap, HDMI_REG_HDMI_MODE, TX_HDMI_MODE);
drm_atomic_helper_connector_hdmi_update_infoframes(connector, state);
/* HDMI AFE setup */
pclk_high = mode->clock > HIGH_PIXEL_CLOCK_KHZ;
regmap_write(regmap, HDMI_REG_AFE_DRV_CTRL, AFE_DRV_RST);
if (pclk_high)
regmap_write(regmap, HDMI_REG_AFE_XP_CTRL,
AFE_XP_GAINBIT | AFE_XP_RESETB);
else
regmap_write(regmap, HDMI_REG_AFE_XP_CTRL,
AFE_XP_ER0 | AFE_XP_RESETB);
regmap_write(regmap, HDMI_REG_AFE_ISW_CTRL, 0x10);
if (pclk_high)
regmap_write(regmap, HDMI_REG_AFE_IP_CTRL,
AFE_IP_GAINBIT | AFE_IP_RESETB);
else
regmap_write(regmap, HDMI_REG_AFE_IP_CTRL,
AFE_IP_ER0 | AFE_IP_RESETB);
/* HDMI software video reset */
regmap_write_bits(regmap, HDMI_REG_SW_RST, SOFTV_RST, SOFTV_RST);
fsleep(1000);
regmap_write_bits(regmap, HDMI_REG_SW_RST, SOFTV_RST, 0);
/* reconfigure LVDS and retry several times in case video is instable */
for (i = 0; i < 3; i++) {
ret = regmap_read_poll_timeout(regmap, HDMI_REG_SYS_STATUS, val,
val & TXVIDSTABLE,
20000, 500000);
if (!ret) {
is_stable = true;
break;
}
it6263_lvds_config(it);
}
if (!is_stable)
dev_warn(it->dev, "failed to wait for video stable\n");
/* HDMI AFE reset release and power up */
regmap_write(regmap, HDMI_REG_AFE_DRV_CTRL, 0);
regmap_write_bits(regmap, HDMI_REG_GCP, AVMUTE, 0);
regmap_write(regmap, HDMI_REG_PKT_GENERAL_CTRL, ENABLE_PKT | REPEAT_PKT);
}
static enum drm_mode_status
it6263_bridge_mode_valid(struct drm_bridge *bridge,
const struct drm_display_info *info,
const struct drm_display_mode *mode)
{
unsigned long long rate;
rate = drm_hdmi_compute_mode_clock(mode, 8, HDMI_COLORSPACE_RGB);
if (rate == 0)
return MODE_NOCLOCK;
return bridge->funcs->hdmi_tmds_char_rate_valid(bridge, mode, rate);
}
static int it6263_bridge_attach(struct drm_bridge *bridge,
enum drm_bridge_attach_flags flags)
{
struct it6263 *it = bridge_to_it6263(bridge);
struct drm_connector *connector;
int ret;
ret = drm_bridge_attach(bridge->encoder, it->next_bridge, bridge,
flags | DRM_BRIDGE_ATTACH_NO_CONNECTOR);
if (ret < 0)
return ret;
if (flags & DRM_BRIDGE_ATTACH_NO_CONNECTOR)
return 0;
connector = drm_bridge_connector_init(bridge->dev, bridge->encoder);
if (IS_ERR(connector)) {
ret = PTR_ERR(connector);
dev_err(it->dev, "failed to initialize bridge connector: %d\n",
ret);
return ret;
}
drm_connector_attach_encoder(connector, bridge->encoder);
return 0;
}
static enum drm_connector_status it6263_bridge_detect(struct drm_bridge *bridge)
{
struct it6263 *it = bridge_to_it6263(bridge);
return it6263_detect(it);
}
static const struct drm_edid *
it6263_bridge_edid_read(struct drm_bridge *bridge,
struct drm_connector *connector)
{
struct it6263 *it = bridge_to_it6263(bridge);
return drm_edid_read_custom(connector, it6263_read_edid, it);
}
static u32 *
it6263_bridge_atomic_get_input_bus_fmts(struct drm_bridge *bridge,
struct drm_bridge_state *bridge_state,
struct drm_crtc_state *crtc_state,
struct drm_connector_state *conn_state,
u32 output_fmt,
unsigned int *num_input_fmts)
{
struct it6263 *it = bridge_to_it6263(bridge);
u32 *input_fmts;
*num_input_fmts = 0;
if (it->lvds_data_mapping != MEDIA_BUS_FMT_RGB888_1X7X4_JEIDA)
return NULL;
input_fmts = kmalloc(sizeof(*input_fmts), GFP_KERNEL);
if (!input_fmts)
return NULL;
input_fmts[0] = MEDIA_BUS_FMT_RGB888_1X7X4_JEIDA;
*num_input_fmts = 1;
return input_fmts;
}
static enum drm_mode_status
it6263_hdmi_tmds_char_rate_valid(const struct drm_bridge *bridge,
const struct drm_display_mode *mode,
unsigned long long tmds_rate)
{
if (mode->clock > MAX_PIXEL_CLOCK_KHZ)
return MODE_CLOCK_HIGH;
if (tmds_rate > MAX_HDMI_TMDS_CHAR_RATE_HZ)
return MODE_CLOCK_HIGH;
return MODE_OK;
}
static int it6263_hdmi_clear_infoframe(struct drm_bridge *bridge,
enum hdmi_infoframe_type type)
{
struct it6263 *it = bridge_to_it6263(bridge);
if (type == HDMI_INFOFRAME_TYPE_AVI)
regmap_write(it->hdmi_regmap, HDMI_REG_AVI_INFOFRM_CTRL, 0);
else
dev_dbg(it->dev, "unsupported HDMI infoframe 0x%x\n", type);
return 0;
}
static int it6263_hdmi_write_infoframe(struct drm_bridge *bridge,
enum hdmi_infoframe_type type,
const u8 *buffer, size_t len)
{
struct it6263 *it = bridge_to_it6263(bridge);
struct regmap *regmap = it->hdmi_regmap;
if (type != HDMI_INFOFRAME_TYPE_AVI) {
dev_dbg(it->dev, "unsupported HDMI infoframe 0x%x\n", type);
return 0;
}
/* write the first AVI infoframe data byte chunk(DB1-DB5) */
regmap_bulk_write(regmap, HDMI_REG_AVI_DB1,
&buffer[HDMI_INFOFRAME_HEADER_SIZE],
HDMI_AVI_DB_CHUNK1_SIZE);
/* write the second AVI infoframe data byte chunk(DB6-DB13) */
regmap_bulk_write(regmap, HDMI_REG_AVI_DB6,
&buffer[HDMI_INFOFRAME_HEADER_SIZE +
HDMI_AVI_DB_CHUNK1_SIZE],
HDMI_AVI_DB_CHUNK2_SIZE);
/* write checksum */
regmap_write(regmap, HDMI_REG_AVI_CSUM, buffer[3]);
regmap_write(regmap, HDMI_REG_AVI_INFOFRM_CTRL, ENABLE_PKT | REPEAT_PKT);
return 0;
}
static const struct drm_bridge_funcs it6263_bridge_funcs = {
.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,
.attach = it6263_bridge_attach,
.mode_valid = it6263_bridge_mode_valid,
.atomic_disable = it6263_bridge_atomic_disable,
.atomic_enable = it6263_bridge_atomic_enable,
.atomic_check = it6263_bridge_atomic_check,
.detect = it6263_bridge_detect,
.edid_read = it6263_bridge_edid_read,
.atomic_get_input_bus_fmts = it6263_bridge_atomic_get_input_bus_fmts,
.hdmi_tmds_char_rate_valid = it6263_hdmi_tmds_char_rate_valid,
.hdmi_clear_infoframe = it6263_hdmi_clear_infoframe,
.hdmi_write_infoframe = it6263_hdmi_write_infoframe,
};
static int it6263_probe(struct i2c_client *client)
{
struct device *dev = &client->dev;
struct gpio_desc *reset_gpio;
struct it6263 *it;
int ret;
it = devm_kzalloc(dev, sizeof(*it), GFP_KERNEL);
if (!it)
return -ENOMEM;
it->dev = dev;
it->hdmi_i2c = client;
it->hdmi_regmap = devm_regmap_init_i2c(client,
&it6263_hdmi_regmap_config);
if (IS_ERR(it->hdmi_regmap))
return dev_err_probe(dev, PTR_ERR(it->hdmi_regmap),
"failed to init I2C regmap for HDMI\n");
reset_gpio = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_LOW);
if (IS_ERR(reset_gpio))
return dev_err_probe(dev, PTR_ERR(reset_gpio),
"failed to get reset gpio\n");
ret = devm_regulator_bulk_get_enable(dev, ARRAY_SIZE(it6263_supplies),
it6263_supplies);
if (ret)
return dev_err_probe(dev, ret, "failed to get power supplies\n");
ret = it6263_parse_dt(it);
if (ret)
return ret;
it6263_hw_reset(reset_gpio);
ret = it6263_lvds_set_i2c_addr(it);
if (ret)
return dev_err_probe(dev, ret, "failed to set I2C addr\n");
it->lvds_i2c = devm_i2c_new_dummy_device(dev, client->adapter,
LVDS_INPUT_CTRL_I2C_ADDR);
if (IS_ERR(it->lvds_i2c))
dev_err_probe(it->dev, PTR_ERR(it->lvds_i2c),
"failed to allocate I2C device for LVDS\n");
it->lvds_regmap = devm_regmap_init_i2c(it->lvds_i2c,
&it6263_lvds_regmap_config);
if (IS_ERR(it->lvds_regmap))
return dev_err_probe(dev, PTR_ERR(it->lvds_regmap),
"failed to init I2C regmap for LVDS\n");
it6263_lvds_config(it);
it6263_hdmi_config(it);
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 |
DRM_BRIDGE_OP_HDMI;
it->bridge.type = DRM_MODE_CONNECTOR_HDMIA;
it->bridge.vendor = "ITE";
it->bridge.product = "IT6263";
return devm_drm_bridge_add(dev, &it->bridge);
}
static const struct of_device_id it6263_of_match[] = {
{ .compatible = "ite,it6263", },
{ }
};
MODULE_DEVICE_TABLE(of, it6263_of_match);
static const struct i2c_device_id it6263_i2c_ids[] = {
{ "it6263", 0 },
{ }
};
MODULE_DEVICE_TABLE(i2c, it6263_i2c_ids);
static struct i2c_driver it6263_driver = {
.probe = it6263_probe,
.driver = {
.name = "it6263",
.of_match_table = it6263_of_match,
},
.id_table = it6263_i2c_ids,
};
module_i2c_driver(it6263_driver);
MODULE_DESCRIPTION("ITE Tech. Inc. IT6263 LVDS/HDMI bridge");
MODULE_AUTHOR("Liu Ying <victor.liu@nxp.com>");
MODULE_LICENSE("GPL");

View File

@ -2614,9 +2614,9 @@ static int it6505_poweron(struct it6505 *it6505)
/* time interval between OVDD and SYSRSTN at least be 10ms */
if (pdata->gpiod_reset) {
usleep_range(10000, 20000);
gpiod_set_value_cansleep(pdata->gpiod_reset, 0);
usleep_range(1000, 2000);
gpiod_set_value_cansleep(pdata->gpiod_reset, 1);
usleep_range(1000, 2000);
gpiod_set_value_cansleep(pdata->gpiod_reset, 0);
usleep_range(25000, 35000);
}
@ -2647,7 +2647,7 @@ static int it6505_poweroff(struct it6505 *it6505)
disable_irq_nosync(it6505->irq);
if (pdata->gpiod_reset)
gpiod_set_value_cansleep(pdata->gpiod_reset, 0);
gpiod_set_value_cansleep(pdata->gpiod_reset, 1);
if (pdata->pwr18) {
err = regulator_disable(pdata->pwr18);
@ -3135,7 +3135,7 @@ static int it6505_init_pdata(struct it6505 *it6505)
return PTR_ERR(pdata->ovdd);
}
pdata->gpiod_reset = devm_gpiod_get(dev, "reset", GPIOD_OUT_LOW);
pdata->gpiod_reset = devm_gpiod_get(dev, "reset", GPIOD_OUT_HIGH);
if (IS_ERR(pdata->gpiod_reset)) {
dev_err(dev, "gpiod_reset gpio not found");
return PTR_ERR(pdata->gpiod_reset);

View File

@ -1,17 +0,0 @@
git+https://gitlab.freedesktop.org/gfx-ci/ci-collate@09e7142715c16f54344ddf97013331ba063b162b
termcolor==2.3.0
# ci-collate dependencies
certifi==2023.7.22
charset-normalizer==3.2.0
idna==3.4
pip==23.3
python-gitlab==3.15.0
requests==2.31.0
requests-toolbelt==1.0.0
ruamel.yaml==0.17.32
ruamel.yaml.clib==0.2.7
setuptools==70.0.0
tenacity==8.2.3
urllib3==2.0.7
wheel==0.41.1

View File

@ -1,204 +0,0 @@
#!/usr/bin/env python3
import argparse
from collections import defaultdict
import difflib
import os
import re
from glcollate import Collate
from termcolor import colored
from urllib.parse import urlparse
def get_canonical_name(job_name):
return re.split(r" \d+/\d+", job_name)[0]
def get_xfails_file_path(job_name, suffix):
canonical_name = get_canonical_name(job_name)
name = canonical_name.replace(":", "-")
script_dir = os.path.dirname(os.path.abspath(__file__))
return os.path.join(script_dir, f"{name}-{suffix}.txt")
def get_unit_test_name_and_results(unit_test):
if "Artifact results/failures.csv not found" in unit_test or '' == unit_test:
return None, None
unit_test_name, unit_test_result = unit_test.strip().split(",")
return unit_test_name, unit_test_result
def read_file(file_path):
try:
with open(file_path, "r") as file:
f = file.readlines()
if len(f):
f[-1] = f[-1].strip() + "\n"
return f
except FileNotFoundError:
return []
def save_file(content, file_path):
# delete file is content is empty
if not content or not any(content):
if os.path.exists(file_path):
os.remove(file_path)
return
with open(file_path, "w") as file:
file.writelines(content)
def is_test_present_on_file(file_content, unit_test_name):
return any(unit_test_name in line for line in file_content)
def is_unit_test_present_in_other_jobs(unit_test, job_ids):
return all(unit_test in job_ids[job_id] for job_id in job_ids)
def remove_unit_test_if_present(lines, unit_test_name):
if not is_test_present_on_file(lines, unit_test_name):
return
lines[:] = [line for line in lines if unit_test_name not in line]
def add_unit_test_if_not_present(lines, unit_test_name, file_name):
# core_getversion is mandatory
if "core_getversion" in unit_test_name:
print("WARNING: core_getversion should pass, not adding it to", os.path.basename(file_name))
elif all(unit_test_name not in line for line in lines):
lines.append(unit_test_name + "\n")
def update_unit_test_result_in_fails_txt(fails_txt, unit_test):
unit_test_name, unit_test_result = get_unit_test_name_and_results(unit_test)
for i, line in enumerate(fails_txt):
if unit_test_name in line:
_, current_result = get_unit_test_name_and_results(line)
fails_txt[i] = unit_test + "\n"
return
def add_unit_test_or_update_result_to_fails_if_present(fails_txt, unit_test, fails_txt_path):
unit_test_name, _ = get_unit_test_name_and_results(unit_test)
if not is_test_present_on_file(fails_txt, unit_test_name):
add_unit_test_if_not_present(fails_txt, unit_test, fails_txt_path)
# if it is present but not with the same result
elif not is_test_present_on_file(fails_txt, unit_test):
update_unit_test_result_in_fails_txt(fails_txt, unit_test)
def split_unit_test_from_collate(xfails):
for job_name in xfails.keys():
for job_id in xfails[job_name].copy().keys():
if "not found" in xfails[job_name][job_id].content_as_str:
del xfails[job_name][job_id]
continue
xfails[job_name][job_id] = xfails[job_name][job_id].content_as_str.splitlines()
def get_xfails_from_pipeline_url(pipeline_url):
parsed_url = urlparse(pipeline_url)
path_components = parsed_url.path.strip("/").split("/")
namespace = path_components[0]
project = path_components[1]
pipeline_id = path_components[-1]
print("Collating from:", namespace, project, pipeline_id)
xfails = (
Collate(namespace=namespace, project=project)
.from_pipeline(pipeline_id)
.get_artifact("results/failures.csv")
)
split_unit_test_from_collate(xfails)
return xfails
def get_xfails_from_pipeline_urls(pipelines_urls):
xfails = defaultdict(dict)
for url in pipelines_urls:
new_xfails = get_xfails_from_pipeline_url(url)
for key in new_xfails:
xfails[key].update(new_xfails[key])
return xfails
def print_diff(old_content, new_content, file_name):
diff = difflib.unified_diff(old_content, new_content, lineterm="", fromfile=file_name, tofile=file_name)
diff = [colored(line, "green") if line.startswith("+") else
colored(line, "red") if line.startswith("-") else line for line in diff]
print("\n".join(diff[:3]))
print("".join(diff[3:]))
def main(pipelines_urls, only_flakes):
xfails = get_xfails_from_pipeline_urls(pipelines_urls)
for job_name in xfails.keys():
fails_txt_path = get_xfails_file_path(job_name, "fails")
flakes_txt_path = get_xfails_file_path(job_name, "flakes")
fails_txt = read_file(fails_txt_path)
flakes_txt = read_file(flakes_txt_path)
fails_txt_original = fails_txt.copy()
flakes_txt_original = flakes_txt.copy()
for job_id in xfails[job_name].keys():
for unit_test in xfails[job_name][job_id]:
unit_test_name, unit_test_result = get_unit_test_name_and_results(unit_test)
if not unit_test_name:
continue
if only_flakes:
remove_unit_test_if_present(fails_txt, unit_test_name)
add_unit_test_if_not_present(flakes_txt, unit_test_name, flakes_txt_path)
continue
# drop it from flakes if it is present to analyze it again
remove_unit_test_if_present(flakes_txt, unit_test_name)
if unit_test_result == "UnexpectedPass":
remove_unit_test_if_present(fails_txt, unit_test_name)
# flake result
if not is_unit_test_present_in_other_jobs(unit_test, xfails[job_name]):
add_unit_test_if_not_present(flakes_txt, unit_test_name, flakes_txt_path)
continue
# flake result
if not is_unit_test_present_in_other_jobs(unit_test, xfails[job_name]):
remove_unit_test_if_present(fails_txt, unit_test_name)
add_unit_test_if_not_present(flakes_txt, unit_test_name, flakes_txt_path)
continue
# consistent result
add_unit_test_or_update_result_to_fails_if_present(fails_txt, unit_test,
fails_txt_path)
fails_txt.sort()
flakes_txt.sort()
if fails_txt != fails_txt_original:
save_file(fails_txt, fails_txt_path)
print_diff(fails_txt_original, fails_txt, os.path.basename(fails_txt_path))
if flakes_txt != flakes_txt_original:
save_file(flakes_txt, flakes_txt_path)
print_diff(flakes_txt_original, flakes_txt, os.path.basename(flakes_txt_path))
if __name__ == "__main__":
parser = argparse.ArgumentParser(description="Update xfails from a given pipeline.")
parser.add_argument("pipeline_urls", nargs="+", type=str, help="URLs to the pipelines to analyze the failures.")
parser.add_argument("--only-flakes", action="store_true", help="Treat every detected failure as a flake, edit *-flakes.txt only.")
args = parser.parse_args()
main(args.pipeline_urls, args.only_flakes)
print("Done.")

View File

@ -129,7 +129,7 @@ bool drm_dev_needs_global_mutex(struct drm_device *dev)
*/
struct drm_file *drm_file_alloc(struct drm_minor *minor)
{
static atomic64_t ident = ATOMIC_INIT(0);
static atomic64_t ident = ATOMIC64_INIT(0);
struct drm_device *dev = minor->dev;
struct drm_file *file;
int ret;

View File

@ -341,8 +341,23 @@ static int drm_of_lvds_get_remote_pixels_type(
return pixels_type;
}
static int __drm_of_lvds_get_dual_link_pixel_order(int p1_pt, int p2_pt)
{
/*
* A valid dual-lVDS bus is found when one port is marked with
* "dual-lvds-even-pixels", and the other port is marked with
* "dual-lvds-odd-pixels", bail out if the markers are not right.
*/
if (p1_pt + p2_pt != DRM_OF_LVDS_EVEN + DRM_OF_LVDS_ODD)
return -EINVAL;
return p1_pt == DRM_OF_LVDS_EVEN ?
DRM_LVDS_DUAL_LINK_EVEN_ODD_PIXELS :
DRM_LVDS_DUAL_LINK_ODD_EVEN_PIXELS;
}
/**
* drm_of_lvds_get_dual_link_pixel_order - Get LVDS dual-link pixel order
* drm_of_lvds_get_dual_link_pixel_order - Get LVDS dual-link source pixel order
* @port1: First DT port node of the Dual-link LVDS source
* @port2: Second DT port node of the Dual-link LVDS source
*
@ -387,20 +402,59 @@ int drm_of_lvds_get_dual_link_pixel_order(const struct device_node *port1,
if (remote_p2_pt < 0)
return remote_p2_pt;
/*
* A valid dual-lVDS bus is found when one remote port is marked with
* "dual-lvds-even-pixels", and the other remote port is marked with
* "dual-lvds-odd-pixels", bail out if the markers are not right.
*/
if (remote_p1_pt + remote_p2_pt != DRM_OF_LVDS_EVEN + DRM_OF_LVDS_ODD)
return -EINVAL;
return remote_p1_pt == DRM_OF_LVDS_EVEN ?
DRM_LVDS_DUAL_LINK_EVEN_ODD_PIXELS :
DRM_LVDS_DUAL_LINK_ODD_EVEN_PIXELS;
return __drm_of_lvds_get_dual_link_pixel_order(remote_p1_pt, remote_p2_pt);
}
EXPORT_SYMBOL_GPL(drm_of_lvds_get_dual_link_pixel_order);
/**
* drm_of_lvds_get_dual_link_pixel_order_sink - Get LVDS dual-link sink pixel order
* @port1: First DT port node of the Dual-link LVDS sink
* @port2: Second DT port node of the Dual-link LVDS sink
*
* An LVDS dual-link connection is made of two links, with even pixels
* transitting on one link, and odd pixels on the other link. This function
* returns, for two ports of an LVDS dual-link sink, which port shall transmit
* the even and odd pixels, based on the requirements of the sink.
*
* The pixel order is determined from the dual-lvds-even-pixels and
* dual-lvds-odd-pixels properties in the sink's DT port nodes. If those
* properties are not present, or if their usage is not valid, this function
* returns -EINVAL.
*
* If either port is not connected, this function returns -EPIPE.
*
* @port1 and @port2 are typically DT sibling nodes, but may have different
* parents when, for instance, two separate LVDS decoders receive the even and
* odd pixels.
*
* Return:
* * DRM_LVDS_DUAL_LINK_EVEN_ODD_PIXELS - @port1 receives even pixels and @port2
* receives odd pixels
* * DRM_LVDS_DUAL_LINK_ODD_EVEN_PIXELS - @port1 receives odd pixels and @port2
* receives even pixels
* * -EINVAL - @port1 or @port2 are NULL
* * -EPIPE - when @port1 or @port2 are not connected
*/
int drm_of_lvds_get_dual_link_pixel_order_sink(struct device_node *port1,
struct device_node *port2)
{
int sink_p1_pt, sink_p2_pt;
if (!port1 || !port2)
return -EINVAL;
sink_p1_pt = drm_of_lvds_get_port_pixels_type(port1);
if (!sink_p1_pt)
return -EPIPE;
sink_p2_pt = drm_of_lvds_get_port_pixels_type(port2);
if (!sink_p2_pt)
return -EPIPE;
return __drm_of_lvds_get_dual_link_pixel_order(sink_p1_pt, sink_p2_pt);
}
EXPORT_SYMBOL_GPL(drm_of_lvds_get_dual_link_pixel_order_sink);
/**
* drm_of_lvds_get_data_mapping - Get LVDS data mapping
* @port: DT port node of the LVDS source or sink
@ -410,7 +464,9 @@ EXPORT_SYMBOL_GPL(drm_of_lvds_get_dual_link_pixel_order);
* Return:
* * MEDIA_BUS_FMT_RGB666_1X7X3_SPWG - data-mapping is "jeida-18"
* * MEDIA_BUS_FMT_RGB888_1X7X4_JEIDA - data-mapping is "jeida-24"
* * MEDIA_BUS_FMT_RGB101010_1X7X5_JEIDA - data-mapping is "jeida-30"
* * MEDIA_BUS_FMT_RGB888_1X7X4_SPWG - data-mapping is "vesa-24"
* * MEDIA_BUS_FMT_RGB101010_1X7X5_SPWG - data-mapping is "vesa-30"
* * -EINVAL - the "data-mapping" property is unsupported
* * -ENODEV - the "data-mapping" property is missing
*/
@ -427,8 +483,12 @@ int drm_of_lvds_get_data_mapping(const struct device_node *port)
return MEDIA_BUS_FMT_RGB666_1X7X3_SPWG;
if (!strcmp(mapping, "jeida-24"))
return MEDIA_BUS_FMT_RGB888_1X7X4_JEIDA;
if (!strcmp(mapping, "jeida-30"))
return MEDIA_BUS_FMT_RGB101010_1X7X5_JEIDA;
if (!strcmp(mapping, "vesa-24"))
return MEDIA_BUS_FMT_RGB888_1X7X4_SPWG;
if (!strcmp(mapping, "vesa-30"))
return MEDIA_BUS_FMT_RGB101010_1X7X5_SPWG;
return -EINVAL;
}

View File

@ -209,6 +209,14 @@ static u32 convert_xrgb8888_to_argb2101010(u32 pix)
return GENMASK(31, 30) /* set alpha bits */ | pix | ((pix >> 8) & 0x00300C03);
}
static u32 convert_xrgb8888_to_abgr2101010(u32 pix)
{
pix = ((pix & 0x00FF0000) >> 14) |
((pix & 0x0000FF00) << 4) |
((pix & 0x000000FF) << 22);
return GENMASK(31, 30) /* set alpha bits */ | pix | ((pix >> 8) & 0x00300C03);
}
/*
* convert_from_xrgb8888 - convert one pixel from xrgb8888 to the desired format
* @color: input color, in xrgb8888 format
@ -242,6 +250,8 @@ static u32 convert_from_xrgb8888(u32 color, u32 format)
return convert_xrgb8888_to_xrgb2101010(color);
case DRM_FORMAT_ARGB2101010:
return convert_xrgb8888_to_argb2101010(color);
case DRM_FORMAT_ABGR2101010:
return convert_xrgb8888_to_abgr2101010(color);
default:
WARN_ONCE(1, "Can't convert to %p4cc\n", &format);
return 0;

View File

@ -2,7 +2,7 @@
config DRM_MEDIATEK
tristate "DRM Support for Mediatek SoCs"
depends on DRM
depends on (ARCH_MEDIATEK && ARM) || COMPILE_TEST
depends on ARCH_MEDIATEK || COMPILE_TEST
depends on COMMON_CLK
depends on HAVE_ARM_SMCCC || COMPILE_TEST
depends on OF

View File

@ -0,0 +1,63 @@
/* SPDX-License-Identifier: MIT */
#ifndef __NV50_TILE_H__
#define __NV50_TILE_H__
#include <linux/types.h>
#include <linux/math.h>
/*
* Tiling parameters for NV50+.
* GOB = Group of bytes, the main unit for tiling blocks.
* Tiling blocks are a power of 2 number of GOB.
* All GOBs and blocks have the same width: 64 bytes (so 16 pixels in 32bits).
* tile_mode is the log2 of the number of GOB per block.
*/
#define NV_TILE_GOB_HEIGHT_TESLA 4 /* 4 x 64 bytes = 256 bytes for a GOB on Tesla*/
#define NV_TILE_GOB_HEIGHT 8 /* 8 x 64 bytes = 512 bytes for a GOB on Fermi and later */
#define NV_TILE_GOB_WIDTH_BYTES 64
/* Number of blocks to cover the width of the framebuffer */
static inline u32 nouveau_get_width_in_blocks(u32 stride)
{
return DIV_ROUND_UP(stride, NV_TILE_GOB_WIDTH_BYTES);
}
/* Return the height in pixel of one GOB */
static inline u32 nouveau_get_gob_height(u16 family)
{
if (family == NV_DEVICE_INFO_V0_TESLA)
return NV_TILE_GOB_HEIGHT_TESLA;
else
return NV_TILE_GOB_HEIGHT;
}
/* Number of blocks to cover the heigth of the framebuffer */
static inline u32 nouveau_get_height_in_blocks(u32 height, u32 gobs_in_block, u16 family)
{
return DIV_ROUND_UP(height, nouveau_get_gob_height(family) * gobs_in_block);
}
/* Return the GOB size in bytes */
static inline u32 nouveau_get_gob_size(u16 family)
{
return nouveau_get_gob_height(family) * NV_TILE_GOB_WIDTH_BYTES;
}
/* Return the number of GOB in a block */
static inline int nouveau_get_gobs_in_block(u32 tile_mode, u16 chipset)
{
if (chipset >= 0xc0)
return 1 << (tile_mode >> 4);
return 1 << tile_mode;
}
/* Return true if tile_mode is invalid */
static inline bool nouveau_check_tile_mode(u32 tile_mode, u16 chipset)
{
if (chipset >= 0xc0)
return (tile_mode & 0xfffff0f);
return (tile_mode & 0xfffffff0);
}
#endif

View File

@ -30,14 +30,20 @@
#include <nvhw/class/cl507e.h>
#include <nvhw/class/clc37e.h>
#include <linux/iosys-map.h>
#include <drm/drm_atomic.h>
#include <drm/drm_atomic_helper.h>
#include <drm/drm_blend.h>
#include <drm/drm_gem_atomic_helper.h>
#include <drm/drm_fourcc.h>
#include <drm/drm_framebuffer.h>
#include <drm/drm_gem_atomic_helper.h>
#include <drm/drm_panic.h>
#include <drm/ttm/ttm_bo.h>
#include "nouveau_bo.h"
#include "nouveau_gem.h"
#include "tile.h"
static void
nv50_wndw_ctxdma_del(struct nv50_wndw_ctxdma *ctxdma)
@ -577,6 +583,114 @@ nv50_wndw_prepare_fb(struct drm_plane *plane, struct drm_plane_state *state)
return 0;
}
/* Only used by drm_panic get_scanout_buffer() and set_pixel(), so it is
* protected by the drm panic spinlock
*/
static u32 nv50_panic_blk_h;
/* Return the framebuffer offset of the start of the block where pixel(x,y) is */
static u32
nv50_get_block_off(unsigned int x, unsigned int y, unsigned int pitch)
{
u32 blk_x, blk_y, blk_columns;
blk_columns = nouveau_get_width_in_blocks(pitch);
blk_x = (x * 4) / NV_TILE_GOB_WIDTH_BYTES;
blk_y = y / nv50_panic_blk_h;
return ((blk_y * blk_columns) + blk_x) * NV_TILE_GOB_WIDTH_BYTES * nv50_panic_blk_h;
}
/* Turing and later have 2 level of tiles inside the block */
static void
nv50_set_pixel_swizzle(struct drm_scanout_buffer *sb, unsigned int x,
unsigned int y, u32 color)
{
u32 blk_off, off, swizzle;
blk_off = nv50_get_block_off(x, y, sb->pitch[0]);
y = y % nv50_panic_blk_h;
/* Inside the block, use the fast address swizzle to compute the offset
* For nvidia blocklinear, bit order is yn..y3 x3 y2 x2 y1 y0 x1 x0
*/
swizzle = (x & 3) | (y & 3) << 2 | (x & 4) << 2 | (y & 4) << 3;
swizzle |= (x & 8) << 3 | (y >> 3) << 7;
off = blk_off + swizzle * 4;
iosys_map_wr(&sb->map[0], off, u32, color);
}
static void
nv50_set_pixel(struct drm_scanout_buffer *sb, unsigned int x, unsigned int y,
u32 color)
{
u32 blk_off, off;
blk_off = nv50_get_block_off(x, y, sb->width);
x = x % (NV_TILE_GOB_WIDTH_BYTES / 4);
y = y % nv50_panic_blk_h;
off = blk_off + x * 4 + y * NV_TILE_GOB_WIDTH_BYTES;
iosys_map_wr(&sb->map[0], off, u32, color);
}
static int
nv50_wndw_get_scanout_buffer(struct drm_plane *plane, struct drm_scanout_buffer *sb)
{
struct drm_framebuffer *fb;
struct nouveau_bo *nvbo;
struct nouveau_drm *drm = nouveau_drm(plane->dev);
u16 chipset = drm->client.device.info.chipset;
u8 family = drm->client.device.info.family;
u32 tile_mode;
u8 kind;
if (!plane->state || !plane->state->fb)
return -EINVAL;
fb = plane->state->fb;
nvbo = nouveau_gem_object(fb->obj[0]);
/* Don't support compressed format, or multiplane yet. */
if (nvbo->comp || fb->format->num_planes != 1)
return -EOPNOTSUPP;
if (nouveau_bo_map(nvbo)) {
drm_warn(plane->dev, "nouveau bo map failed, panic won't be displayed\n");
return -ENOMEM;
}
if (nvbo->kmap.bo_kmap_type & TTM_BO_MAP_IOMEM_MASK)
iosys_map_set_vaddr_iomem(&sb->map[0], (void __iomem *)nvbo->kmap.virtual);
else
iosys_map_set_vaddr(&sb->map[0], nvbo->kmap.virtual);
sb->height = fb->height;
sb->width = fb->width;
sb->pitch[0] = fb->pitches[0];
sb->format = fb->format;
nouveau_framebuffer_get_layout(fb, &tile_mode, &kind);
if (kind) {
/* If tiling is enabled, use set_pixel() to display correctly.
* Only handle 32bits format for now.
*/
if (fb->format->cpp[0] != 4)
return -EOPNOTSUPP;
nv50_panic_blk_h = nouveau_get_gob_height(family) *
nouveau_get_gobs_in_block(tile_mode, chipset);
if (chipset >= 0x160)
sb->set_pixel = nv50_set_pixel_swizzle;
else
sb->set_pixel = nv50_set_pixel;
}
return 0;
}
static const struct drm_plane_helper_funcs
nv50_wndw_helper = {
.prepare_fb = nv50_wndw_prepare_fb,
@ -584,6 +698,14 @@ nv50_wndw_helper = {
.atomic_check = nv50_wndw_atomic_check,
};
static const struct drm_plane_helper_funcs
nv50_wndw_primary_helper = {
.prepare_fb = nv50_wndw_prepare_fb,
.cleanup_fb = nv50_wndw_cleanup_fb,
.atomic_check = nv50_wndw_atomic_check,
.get_scanout_buffer = nv50_wndw_get_scanout_buffer,
};
static void
nv50_wndw_atomic_destroy_state(struct drm_plane *plane,
struct drm_plane_state *state)
@ -732,7 +854,10 @@ nv50_wndw_new_(const struct nv50_wndw_func *func, struct drm_device *dev,
return ret;
}
drm_plane_helper_add(&wndw->plane, &nv50_wndw_helper);
if (type == DRM_PLANE_TYPE_PRIMARY)
drm_plane_helper_add(&wndw->plane, &nv50_wndw_primary_helper);
else
drm_plane_helper_add(&wndw->plane, &nv50_wndw_helper);
if (wndw->func->ilut) {
ret = nv50_lut_init(disp, mmu, &wndw->ilut);

View File

@ -44,6 +44,7 @@
#include <nvif/if0011.h>
#include <nvif/if0013.h>
#include <dispnv50/crc.h>
#include <dispnv50/tile.h>
int
nouveau_display_vblank_enable(struct drm_crtc *crtc)
@ -220,69 +221,29 @@ nouveau_validate_decode_mod(struct nouveau_drm *drm,
return 0;
}
static inline uint32_t
nouveau_get_width_in_blocks(uint32_t stride)
{
/* GOBs per block in the x direction is always one, and GOBs are
* 64 bytes wide
*/
static const uint32_t log_block_width = 6;
return (stride + (1 << log_block_width) - 1) >> log_block_width;
}
static inline uint32_t
nouveau_get_height_in_blocks(struct nouveau_drm *drm,
uint32_t height,
uint32_t log_block_height_in_gobs)
{
uint32_t log_gob_height;
uint32_t log_block_height;
BUG_ON(drm->client.device.info.family < NV_DEVICE_INFO_V0_TESLA);
if (drm->client.device.info.family < NV_DEVICE_INFO_V0_FERMI)
log_gob_height = 2;
else
log_gob_height = 3;
log_block_height = log_block_height_in_gobs + log_gob_height;
return (height + (1 << log_block_height) - 1) >> log_block_height;
}
static int
nouveau_check_bl_size(struct nouveau_drm *drm, struct nouveau_bo *nvbo,
uint32_t offset, uint32_t stride, uint32_t h,
uint32_t tile_mode)
{
uint32_t gob_size, bw, bh;
uint32_t gob_size, bw, bh, gobs_in_block;
uint64_t bl_size;
BUG_ON(drm->client.device.info.family < NV_DEVICE_INFO_V0_TESLA);
if (drm->client.device.info.chipset >= 0xc0) {
if (tile_mode & 0xF)
return -EINVAL;
tile_mode >>= 4;
}
if (tile_mode & 0xFFFFFFF0)
if (nouveau_check_tile_mode(tile_mode, drm->client.device.info.chipset))
return -EINVAL;
if (drm->client.device.info.family < NV_DEVICE_INFO_V0_FERMI)
gob_size = 256;
else
gob_size = 512;
gobs_in_block = nouveau_get_gobs_in_block(tile_mode, drm->client.device.info.chipset);
bw = nouveau_get_width_in_blocks(stride);
bh = nouveau_get_height_in_blocks(drm, h, tile_mode);
bh = nouveau_get_height_in_blocks(h, gobs_in_block, drm->client.device.info.family);
gob_size = nouveau_get_gob_size(drm->client.device.info.family);
bl_size = bw * bh * (1 << tile_mode) * gob_size;
bl_size = bw * bh * gobs_in_block * gob_size;
DRM_DEBUG_KMS("offset=%u stride=%u h=%u tile_mode=0x%02x bw=%u bh=%u gob_size=%u bl_size=%llu size=%zu\n",
offset, stride, h, tile_mode, bw, bh, gob_size, bl_size,
nvbo->bo.base.size);
DRM_DEBUG_KMS("offset=%u stride=%u h=%u gobs_in_block=%u bw=%u bh=%u gob_size=%u bl_size=%llu size=%zu\n",
offset, stride, h, gobs_in_block, bw, bh, gob_size,
bl_size, nvbo->bo.base.size);
if (bl_size + offset > nvbo->bo.base.size)
return -ERANGE;

View File

@ -38,7 +38,7 @@ static int panfrost_devfreq_target(struct device *dev, unsigned long *freq,
return PTR_ERR(opp);
dev_pm_opp_put(opp);
err = dev_pm_opp_set_rate(dev, *freq);
err = dev_pm_opp_set_rate(dev, *freq);
if (!err)
ptdev->pfdevfreq.current_frequency = *freq;
@ -182,6 +182,7 @@ int panfrost_devfreq_init(struct panfrost_device *pfdev)
* if any and will avoid a switch off by regulator_late_cleanup()
*/
ret = dev_pm_opp_set_opp(dev, opp);
dev_pm_opp_put(opp);
if (ret) {
DRM_DEV_ERROR(dev, "Couldn't set recommended OPP\n");
return ret;

View File

@ -163,13 +163,6 @@ int panthor_devfreq_init(struct panthor_device *ptdev)
cur_freq = clk_get_rate(ptdev->clks.core);
opp = devfreq_recommended_opp(dev, &cur_freq, 0);
if (IS_ERR(opp))
return PTR_ERR(opp);
panthor_devfreq_profile.initial_freq = cur_freq;
ptdev->current_frequency = cur_freq;
/* Regulator coupling only takes care of synchronizing/balancing voltage
* updates, but the coupled regulator needs to be enabled manually.
*
@ -200,18 +193,24 @@ int panthor_devfreq_init(struct panthor_device *ptdev)
return ret;
}
opp = devfreq_recommended_opp(dev, &cur_freq, 0);
if (IS_ERR(opp))
return PTR_ERR(opp);
panthor_devfreq_profile.initial_freq = cur_freq;
ptdev->current_frequency = cur_freq;
/*
* Set the recommend OPP this will enable and configure the regulator
* if any and will avoid a switch off by regulator_late_cleanup()
*/
ret = dev_pm_opp_set_opp(dev, opp);
dev_pm_opp_put(opp);
if (ret) {
DRM_DEV_ERROR(dev, "Couldn't set recommended OPP\n");
return ret;
}
dev_pm_opp_put(opp);
/* Find the fastest defined rate */
opp = dev_pm_opp_find_freq_floor(dev, &freq);
if (IS_ERR(opp))

View File

@ -1350,6 +1350,19 @@ EXPORT_SYMBOL(drm_sched_init);
* @sched: scheduler instance
*
* Tears down and cleans up the scheduler.
*
* This stops submission of new jobs to the hardware through
* drm_sched_backend_ops.run_job(). Consequently, drm_sched_backend_ops.free_job()
* will not be called for all jobs still in drm_gpu_scheduler.pending_list.
* There is no solution for this currently. Thus, it is up to the driver to make
* sure that
* a) drm_sched_fini() is only called after for all submitted jobs
* drm_sched_backend_ops.free_job() has been called or that
* b) the jobs for which drm_sched_backend_ops.free_job() has not been called
* after drm_sched_fini() ran are freed manually.
*
* FIXME: Take care of the above problem and prevent this function from leaking
* the jobs in drm_gpu_scheduler.pending_list under any circumstances.
*/
void drm_sched_fini(struct drm_gpu_scheduler *sched)
{
@ -1445,8 +1458,10 @@ EXPORT_SYMBOL(drm_sched_wqueue_ready);
/**
* drm_sched_wqueue_stop - stop scheduler submission
*
* @sched: scheduler instance
*
* Stops the scheduler from pulling new jobs from entities. It also stops
* freeing jobs automatically through drm_sched_backend_ops.free_job().
*/
void drm_sched_wqueue_stop(struct drm_gpu_scheduler *sched)
{
@ -1458,8 +1473,12 @@ EXPORT_SYMBOL(drm_sched_wqueue_stop);
/**
* drm_sched_wqueue_start - start scheduler submission
*
* @sched: scheduler instance
*
* Restarts the scheduler after drm_sched_wqueue_stop() has stopped it.
*
* This function is not necessary for 'conventional' startup. The scheduler is
* fully operational after drm_sched_init() succeeded.
*/
void drm_sched_wqueue_start(struct drm_gpu_scheduler *sched)
{

View File

@ -187,7 +187,7 @@ static void blend(struct vkms_writeback_job *wb,
const struct pixel_argb_u16 background_color = { .a = 0xffff };
size_t crtc_y_limit = crtc_state->base.crtc->mode.vdisplay;
size_t crtc_y_limit = crtc_state->base.mode.vdisplay;
/*
* The planes are composed line-by-line to avoid heavy memory usage. It is a necessary
@ -270,7 +270,7 @@ static int compose_active_planes(struct vkms_writeback_job *active_wb,
if (WARN_ON(check_format_funcs(crtc_state, active_wb)))
return -EINVAL;
line_width = crtc_state->base.crtc->mode.hdisplay;
line_width = crtc_state->base.mode.hdisplay;
stage_buffer.n_pixels = line_width;
output_buffer.n_pixels = line_width;

View File

@ -64,8 +64,6 @@ static int vkms_enable_vblank(struct drm_crtc *crtc)
struct drm_vblank_crtc *vblank = drm_crtc_vblank_crtc(crtc);
struct vkms_output *out = drm_crtc_to_vkms_output(crtc);
drm_calc_timestamping_constants(crtc, &crtc->mode);
hrtimer_init(&out->vblank_hrtimer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
out->vblank_hrtimer.function = &vkms_vblank_simulate;
out->period_ns = ktime_set(0, vblank->framedur_ns);

View File

@ -92,7 +92,7 @@ int vkms_output_init(struct vkms_device *vkmsdev, int index)
DRM_MODE_CONNECTOR_VIRTUAL);
if (ret) {
DRM_ERROR("Failed to init connector\n");
goto err_connector;
return ret;
}
drm_connector_helper_add(connector, &vkms_conn_helper_funcs);
@ -131,8 +131,5 @@ int vkms_output_init(struct vkms_device *vkmsdev, int index)
err_encoder:
drm_connector_cleanup(connector);
err_connector:
drm_crtc_cleanup(crtc);
return ret;
}

View File

@ -131,8 +131,8 @@ static void vkms_wb_atomic_commit(struct drm_connector *conn,
struct drm_connector_state *conn_state = wb_conn->base.state;
struct vkms_crtc_state *crtc_state = output->composer_state;
struct drm_framebuffer *fb = connector_state->writeback_job->fb;
u16 crtc_height = crtc_state->base.crtc->mode.vdisplay;
u16 crtc_width = crtc_state->base.crtc->mode.hdisplay;
u16 crtc_height = crtc_state->base.mode.vdisplay;
u16 crtc_width = crtc_state->base.mode.hdisplay;
struct vkms_writeback_job *active_wb;
struct vkms_frame_info *wb_frame_info;
u32 wb_format = fb->format->format;

View File

@ -52,6 +52,8 @@ int drm_of_find_panel_or_bridge(const struct device_node *np,
struct drm_bridge **bridge);
int drm_of_lvds_get_dual_link_pixel_order(const struct device_node *port1,
const struct device_node *port2);
int drm_of_lvds_get_dual_link_pixel_order_sink(struct device_node *port1,
struct device_node *port2);
int drm_of_lvds_get_data_mapping(const struct device_node *port);
int drm_of_get_data_lanes_count(const struct device_node *endpoint,
const unsigned int min, const unsigned int max);
@ -109,6 +111,13 @@ drm_of_lvds_get_dual_link_pixel_order(const struct device_node *port1,
return -EINVAL;
}
static inline int
drm_of_lvds_get_dual_link_pixel_order_sink(struct device_node *port1,
struct device_node *port2)
{
return -EINVAL;
}
static inline int
drm_of_lvds_get_data_mapping(const struct device_node *port)
{

View File

@ -34,7 +34,7 @@
#define MEDIA_BUS_FMT_FIXED 0x0001
/* RGB - next is 0x1026 */
/* RGB - next is 0x1028 */
#define MEDIA_BUS_FMT_RGB444_1X12 0x1016
#define MEDIA_BUS_FMT_RGB444_2X8_PADHI_BE 0x1001
#define MEDIA_BUS_FMT_RGB444_2X8_PADHI_LE 0x1002
@ -68,6 +68,8 @@
#define MEDIA_BUS_FMT_ARGB8888_1X32 0x100d
#define MEDIA_BUS_FMT_RGB888_1X32_PADHI 0x100f
#define MEDIA_BUS_FMT_RGB101010_1X30 0x1018
#define MEDIA_BUS_FMT_RGB101010_1X7X5_SPWG 0x1026
#define MEDIA_BUS_FMT_RGB101010_1X7X5_JEIDA 0x1027
#define MEDIA_BUS_FMT_RGB666_1X36_CPADLO 0x1020
#define MEDIA_BUS_FMT_RGB888_1X36_CPADLO 0x1021
#define MEDIA_BUS_FMT_RGB121212_1X36 0x1019