drm-misc-next for 6.17:

UAPI Changes:
 
 Cross-subsystem Changes:
 
 Core Changes:
 
 - bridge: More reference counting
 - dp: Implement backlight control helpers
 - fourcc: Add half-float and 32b float formats, RGB161616, BGR161616
 - mipi-dsi: Drop MIPI_DSI_MODE_VSYNC_FLUSH flag
 - ttm: Improve eviction
 
 Driver Changes:
 - i915: Use backlight control helpers for eDP
 - tidss: Add AM65x OLDI bridge support
 
 - panels:
   - panel-edp: Add CMN N116BCJ-EAK support
   - raydium-rm67200: misc cleanups, optional reset
   - new panel: DJN HX83112B
 -----BEGIN PGP SIGNATURE-----
 
 iJUEABMJAB0WIQTkHFbLp4ejekA/qfgnX84Zoj2+dgUCaGY7aQAKCRAnX84Zoj2+
 dncVAYC+7mGk8UDugcIEn51fCLxv92DKeMRq/qsmGPz/x5c3TaXX7sN0/FLo91ek
 bLrwR9ABfjx+Qz+jO21LuwRBxgHv7XH5Bk1sPay1n7+TokndCj55+YG8vCbXISsk
 gsxtheA8Ig==
 =ybn3
 -----END PGP SIGNATURE-----

Merge tag 'drm-misc-next-2025-07-03' of https://gitlab.freedesktop.org/drm/misc/kernel into drm-next

drm-misc-next for 6.17:

UAPI Changes:

Cross-subsystem Changes:

Core Changes:

- bridge: More reference counting
- dp: Implement backlight control helpers
- fourcc: Add half-float and 32b float formats, RGB161616, BGR161616
- mipi-dsi: Drop MIPI_DSI_MODE_VSYNC_FLUSH flag
- ttm: Improve eviction

Driver Changes:
- i915: Use backlight control helpers for eDP
- tidss: Add AM65x OLDI bridge support

- panels:
  - panel-edp: Add CMN N116BCJ-EAK support
  - raydium-rm67200: misc cleanups, optional reset
  - new panel: DJN HX83112B

Signed-off-by: Dave Airlie <airlied@redhat.com>

From: Maxime Ripard <mripard@redhat.com>
Link: https://lore.kernel.org/r/20250703-chirpy-lilac-dalmatian-2c5838@houat
This commit is contained in:
Dave Airlie 2025-07-04 11:54:21 +10:00
commit 17d081ef84
48 changed files with 2030 additions and 473 deletions

View File

@ -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/himax,hx83112b.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: Himax HX83112B-based DSI display panels
maintainers:
- Luca Weiss <luca@lucaweiss.eu>
description:
The Himax HX83112B is a generic DSI Panel IC used to control
LCD panels.
allOf:
- $ref: panel-common.yaml#
properties:
compatible:
contains:
const: djn,98-03057-6598b-i
reg:
maxItems: 1
iovcc-supply:
description: I/O voltage rail
vsn-supply:
description: Positive source voltage rail
vsp-supply:
description: Negative source voltage rail
required:
- compatible
- reg
- reset-gpios
- iovcc-supply
- vsn-supply
- vsp-supply
- port
unevaluatedProperties: false
examples:
- |
#include <dt-bindings/gpio/gpio.h>
dsi {
#address-cells = <1>;
#size-cells = <0>;
panel@0 {
compatible = "djn,98-03057-6598b-i";
reg = <0>;
reset-gpios = <&tlmm 61 GPIO_ACTIVE_LOW>;
iovcc-supply = <&pm8953_l6>;
vsn-supply = <&pmi632_lcdb_ncp>;
vsp-supply = <&pmi632_lcdb_ldo>;
port {
panel_in_0: endpoint {
remote-endpoint = <&dsi0_out>;
};
};
};
};
...

View File

@ -42,7 +42,6 @@ required:
- compatible - compatible
- port - port
- reg - reg
- reset-gpios
additionalProperties: false additionalProperties: false

View File

@ -64,10 +64,10 @@ properties:
- description: Pixel clock for video port 0. - description: Pixel clock for video port 0.
- description: Pixel clock for video port 1. - description: Pixel clock for video port 1.
- description: Pixel clock for video port 2. - description: Pixel clock for video port 2.
- description: Pixel clock for video port 3. - {}
- description: Peripheral(vop grf/dsi) clock. - {}
- description: Alternative pixel clock provided by HDMI0 PHY PLL. - {}
- description: Alternative pixel clock provided by HDMI1 PHY PLL. - {}
clock-names: clock-names:
minItems: 5 minItems: 5
@ -77,10 +77,10 @@ properties:
- const: dclk_vp0 - const: dclk_vp0
- const: dclk_vp1 - const: dclk_vp1
- const: dclk_vp2 - const: dclk_vp2
- const: dclk_vp3 - {}
- const: pclk_vop - {}
- const: pll_hdmiphy0 - {}
- const: pll_hdmiphy1 - {}
rockchip,grf: rockchip,grf:
$ref: /schemas/types.yaml#/definitions/phandle $ref: /schemas/types.yaml#/definitions/phandle
@ -175,10 +175,24 @@ allOf:
then: then:
properties: properties:
clocks: clocks:
maxItems: 5 minItems: 5
items:
- {}
- {}
- {}
- {}
- {}
- description: Alternative pixel clock provided by HDMI PHY PLL.
clock-names: clock-names:
maxItems: 5 minItems: 5
items:
- {}
- {}
- {}
- {}
- {}
- const: pll_hdmiphy0
interrupts: interrupts:
minItems: 4 minItems: 4
@ -208,11 +222,29 @@ allOf:
properties: properties:
clocks: clocks:
minItems: 7 minItems: 7
maxItems: 9 items:
- {}
- {}
- {}
- {}
- {}
- description: Pixel clock for video port 3.
- description: Peripheral(vop grf/dsi) clock.
- description: Alternative pixel clock provided by HDMI0 PHY PLL.
- description: Alternative pixel clock provided by HDMI1 PHY PLL.
clock-names: clock-names:
minItems: 7 minItems: 7
maxItems: 9 items:
- {}
- {}
- {}
- {}
- {}
- const: dclk_vp3
- const: pclk_vop
- const: pll_hdmiphy0
- const: pll_hdmiphy1
interrupts: interrupts:
maxItems: 1 maxItems: 1

View File

@ -0,0 +1,79 @@
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/display/ti/ti,am625-oldi.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: Texas Instruments AM625 OLDI Transmitter
maintainers:
- Tomi Valkeinen <tomi.valkeinen@ideasonboard.com>
- Aradhya Bhatia <aradhya.bhatia@linux.dev>
description:
The AM625 TI Keystone OpenLDI transmitter (OLDI TX) supports serialized RGB
pixel data transmission between host and flat panel display over LVDS (Low
Voltage Differential Sampling) interface. The OLDI TX consists of 7-to-1 data
serializers, and 4-data and 1-clock LVDS outputs. It supports the LVDS output
formats "jeida-18", "jeida-24" and "vesa-18", and can accept 24-bit RGB or
padded and un-padded 18-bit RGB bus formats as input.
properties:
reg:
maxItems: 1
clocks:
maxItems: 1
description: serial clock input for the OLDI transmitters
clock-names:
const: serial
ti,companion-oldi:
$ref: /schemas/types.yaml#/definitions/phandle
description:
phandle to companion OLDI transmitter. This property is required for both
the OLDI TXes if they are expected to work either in dual-lvds mode or in
clone mode. This property should point to the other OLDI TX's phandle.
ti,secondary-oldi:
type: boolean
description:
Boolean property to mark the OLDI transmitter as the secondary one, when the
OLDI hardware is expected to run as a companion HW, in cases of dual-lvds
mode or clone mode. The primary OLDI hardware is responsible for all the
hardware configuration.
ti,oldi-io-ctrl:
$ref: /schemas/types.yaml#/definitions/phandle
description:
phandle to syscon device node mapping OLDI IO_CTRL registers found in the
control MMR region. These registers are required to toggle the I/O lane
power, and control its electrical characteristics.
ports:
$ref: /schemas/graph.yaml#/properties/ports
properties:
port@0:
$ref: /schemas/graph.yaml#/properties/port
description: Parallel RGB input port
port@1:
$ref: /schemas/graph.yaml#/properties/port
description: LVDS output port
required:
- port@0
- port@1
required:
- reg
- clocks
- clock-names
- ti,oldi-io-ctrl
- ports
additionalProperties: false
...

View File

@ -100,6 +100,24 @@ properties:
For AM62A7 DSS, the port is tied off inside the SoC. For AM62A7 DSS, the port is tied off inside the SoC.
For AM62L DSS, the DSS DPI output port node from video port 1 For AM62L DSS, the DSS DPI output port node from video port 1
or DSI Tx controller node connected to video port 1. or DSI Tx controller node connected to video port 1.
properties:
endpoint@0:
$ref: /schemas/graph.yaml#/properties/endpoint
description:
For AM625 DSS, VP Connection to OLDI0.
For AM65X DSS, OLDI output from the SoC.
endpoint@1:
$ref: /schemas/graph.yaml#/properties/endpoint
description:
For AM625 DSS, VP Connection to OLDI1.
anyOf:
- required:
- endpoint
- required:
- endpoint@0
- endpoint@1
port@1: port@1:
$ref: /schemas/graph.yaml#/properties/port $ref: /schemas/graph.yaml#/properties/port
@ -121,6 +139,25 @@ properties:
Input memory (from main memory to dispc) bandwidth limit in Input memory (from main memory to dispc) bandwidth limit in
bytes per second bytes per second
oldi-transmitters:
description:
Child node under the DSS, to describe all the OLDI transmitters connected
to the DSS videoports.
type: object
additionalProperties: false
properties:
"#address-cells":
const: 1
"#size-cells":
const: 0
patternProperties:
'^oldi@[0-1]$':
$ref: ti,am625-oldi.yaml#
description: OLDI transmitters connected to the DSS VPs
allOf: allOf:
- if: - if:
properties: properties:
@ -129,6 +166,7 @@ allOf:
const: ti,am62a7-dss const: ti,am62a7-dss
then: then:
properties: properties:
oldi-transmitters: false
ports: ports:
properties: properties:
port@0: false port@0: false
@ -143,6 +181,22 @@ allOf:
properties: properties:
port@1: false port@1: false
- if:
properties:
compatible:
contains:
enum:
- ti,am62l-dss
- ti,am65x-dss
then:
properties:
oldi-transmitters: false
ports:
properties:
port@0:
properties:
endpoint@1: false
required: required:
- compatible - compatible
- reg - reg
@ -161,32 +215,135 @@ examples:
#include <dt-bindings/soc/ti,sci_pm_domain.h> #include <dt-bindings/soc/ti,sci_pm_domain.h>
dss: dss@4a00000 { dss: dss@4a00000 {
compatible = "ti,am65x-dss"; compatible = "ti,am65x-dss";
reg = <0x04a00000 0x1000>, /* common */ reg = <0x04a00000 0x1000>, /* common */
<0x04a02000 0x1000>, /* vidl1 */ <0x04a02000 0x1000>, /* vidl1 */
<0x04a06000 0x1000>, /* vid */ <0x04a06000 0x1000>, /* vid */
<0x04a07000 0x1000>, /* ovr1 */ <0x04a07000 0x1000>, /* ovr1 */
<0x04a08000 0x1000>, /* ovr2 */ <0x04a08000 0x1000>, /* ovr2 */
<0x04a0a000 0x1000>, /* vp1 */ <0x04a0a000 0x1000>, /* vp1 */
<0x04a0b000 0x1000>, /* vp2 */ <0x04a0b000 0x1000>, /* vp2 */
<0x04a01000 0x1000>; /* common1 */ <0x04a01000 0x1000>; /* common1 */
reg-names = "common", "vidl1", "vid",
"ovr1", "ovr2", "vp1", "vp2", "common1";
ti,am65x-oldi-io-ctrl = <&dss_oldi_io_ctrl>;
power-domains = <&k3_pds 67 TI_SCI_PD_EXCLUSIVE>;
clocks = <&k3_clks 67 1>,
<&k3_clks 216 1>,
<&k3_clks 67 2>;
clock-names = "fck", "vp1", "vp2";
interrupts = <GIC_SPI 166 IRQ_TYPE_EDGE_RISING>;
ports {
#address-cells = <1>;
#size-cells = <0>;
port@0 {
reg = <0>;
oldi_out0: endpoint {
remote-endpoint = <&lcd_in0>;
};
};
};
};
- |
#include <dt-bindings/interrupt-controller/arm-gic.h>
#include <dt-bindings/interrupt-controller/irq.h>
#include <dt-bindings/soc/ti,sci_pm_domain.h>
bus {
#address-cells = <2>;
#size-cells = <2>;
dss1: dss@30200000 {
compatible = "ti,am625-dss";
reg = <0x00 0x30200000 0x00 0x1000>, /* common */
<0x00 0x30202000 0x00 0x1000>, /* vidl1 */
<0x00 0x30206000 0x00 0x1000>, /* vid */
<0x00 0x30207000 0x00 0x1000>, /* ovr1 */
<0x00 0x30208000 0x00 0x1000>, /* ovr2 */
<0x00 0x3020a000 0x00 0x1000>, /* vp1 */
<0x00 0x3020b000 0x00 0x1000>, /* vp2 */
<0x00 0x30201000 0x00 0x1000>; /* common1 */
reg-names = "common", "vidl1", "vid", reg-names = "common", "vidl1", "vid",
"ovr1", "ovr2", "vp1", "vp2", "common1"; "ovr1", "ovr2", "vp1", "vp2", "common1";
ti,am65x-oldi-io-ctrl = <&dss_oldi_io_ctrl>; power-domains = <&k3_pds 186 TI_SCI_PD_EXCLUSIVE>;
power-domains = <&k3_pds 67 TI_SCI_PD_EXCLUSIVE>; clocks = <&k3_clks 186 6>,
clocks = <&k3_clks 67 1>, <&vp1_clock>,
<&k3_clks 216 1>, <&k3_clks 186 2>;
<&k3_clks 67 2>;
clock-names = "fck", "vp1", "vp2"; clock-names = "fck", "vp1", "vp2";
interrupts = <GIC_SPI 166 IRQ_TYPE_EDGE_RISING>; interrupts = <GIC_SPI 84 IRQ_TYPE_LEVEL_HIGH>;
oldi-transmitters {
#address-cells = <1>;
#size-cells = <0>;
oldi0: oldi@0 {
reg = <0>;
clocks = <&k3_clks 186 0>;
clock-names = "serial";
ti,companion-oldi = <&oldi1>;
ti,oldi-io-ctrl = <&dss_oldi_io_ctrl>;
ports {
#address-cells = <1>;
#size-cells = <0>;
port@0 {
reg = <0>;
oldi0_in: endpoint {
remote-endpoint = <&dpi0_out0>;
};
};
port@1 {
reg = <1>;
oldi0_out: endpoint {
remote-endpoint = <&panel_in0>;
};
};
};
};
oldi1: oldi@1 {
reg = <1>;
clocks = <&k3_clks 186 0>;
clock-names = "serial";
ti,secondary-oldi;
ti,companion-oldi = <&oldi0>;
ti,oldi-io-ctrl = <&dss_oldi_io_ctrl>;
ports {
#address-cells = <1>;
#size-cells = <0>;
port@0 {
reg = <0>;
oldi1_in: endpoint {
remote-endpoint = <&dpi0_out1>;
};
};
port@1 {
reg = <1>;
oldi1_out: endpoint {
remote-endpoint = <&panel_in1>;
};
};
};
};
};
ports { ports {
#address-cells = <1>;
#size-cells = <0>;
port@0 {
#address-cells = <1>; #address-cells = <1>;
#size-cells = <0>; #size-cells = <0>;
port@0 { reg = <0>;
reg = <0>; dpi0_out0: endpoint@0 {
oldi_out0: endpoint { reg = <0>;
remote-endpoint = <&lcd_in0>; remote-endpoint = <&oldi0_in>;
};
}; };
dpi0_out1: endpoint@1 {
reg = <1>;
remote-endpoint = <&oldi1_in>;
};
};
port@1 {
reg = <1>;
dpi1_out: endpoint {
remote-endpoint = <&hdmi_bridge>;
};
};
}; };
};
}; };

View File

@ -398,6 +398,8 @@ patternProperties:
description: Diodes, Inc. description: Diodes, Inc.
"^dioo,.*": "^dioo,.*":
description: Dioo Microcircuit Co., Ltd description: Dioo Microcircuit Co., Ltd
"^djn,.*":
description: Shenzhen DJN Optronics Technology Co., Ltd
"^dlc,.*": "^dlc,.*":
description: DLC Display Co., Ltd. description: DLC Display Co., Ltd.
"^dlg,.*": "^dlg,.*":

View File

@ -7499,10 +7499,12 @@ M: Javier Martinez Canillas <javierm@redhat.com>
L: dri-devel@lists.freedesktop.org L: dri-devel@lists.freedesktop.org
S: Maintained S: Maintained
T: git https://gitlab.freedesktop.org/drm/misc/kernel.git T: git https://gitlab.freedesktop.org/drm/misc/kernel.git
F: drivers/firmware/sysfb*.c
F: drivers/gpu/drm/sysfb/ F: drivers/gpu/drm/sysfb/
F: drivers/video/aperture.c F: drivers/video/aperture.c
F: drivers/video/nomodeset.c F: drivers/video/nomodeset.c
F: include/linux/aperture.h F: include/linux/aperture.h
F: include/linux/sysfb.h
F: include/video/nomodeset.h F: include/video/nomodeset.h
DRM DRIVER FOR GENERIC EDP PANELS DRM DRIVER FOR GENERIC EDP PANELS
@ -8232,6 +8234,7 @@ M: Tomi Valkeinen <tomi.valkeinen@ideasonboard.com>
L: dri-devel@lists.freedesktop.org L: dri-devel@lists.freedesktop.org
S: Maintained S: Maintained
T: git https://gitlab.freedesktop.org/drm/misc/kernel.git T: git https://gitlab.freedesktop.org/drm/misc/kernel.git
F: Documentation/devicetree/bindings/display/ti/ti,am625-oldi.yaml
F: Documentation/devicetree/bindings/display/ti/ti,am65x-dss.yaml F: Documentation/devicetree/bindings/display/ti/ti,am65x-dss.yaml
F: Documentation/devicetree/bindings/display/ti/ti,j721e-dss.yaml F: Documentation/devicetree/bindings/display/ti/ti,j721e-dss.yaml
F: Documentation/devicetree/bindings/display/ti/ti,k2g-dss.yaml F: Documentation/devicetree/bindings/display/ti/ti,k2g-dss.yaml

View File

@ -20,6 +20,7 @@
#include <linux/of.h> #include <linux/of.h>
#include <linux/phy/phy.h> #include <linux/phy/phy.h>
#include <linux/platform_device.h> #include <linux/platform_device.h>
#include <linux/units.h>
#include <video/mipi_display.h> #include <video/mipi_display.h>
@ -558,10 +559,6 @@ static void samsung_dsim_reset(struct samsung_dsim *dsi)
samsung_dsim_write(dsi, DSIM_SWRST_REG, reset_val); samsung_dsim_write(dsi, DSIM_SWRST_REG, reset_val);
} }
#ifndef MHZ
#define MHZ (1000 * 1000)
#endif
static unsigned long samsung_dsim_pll_find_pms(struct samsung_dsim *dsi, static unsigned long samsung_dsim_pll_find_pms(struct samsung_dsim *dsi,
unsigned long fin, unsigned long fin,
unsigned long fout, unsigned long fout,
@ -575,8 +572,8 @@ static unsigned long samsung_dsim_pll_find_pms(struct samsung_dsim *dsi,
u16 _m, best_m; u16 _m, best_m;
u8 _s, best_s; u8 _s, best_s;
p_min = DIV_ROUND_UP(fin, (driver_data->pll_fin_max * MHZ)); p_min = DIV_ROUND_UP(fin, (driver_data->pll_fin_max * HZ_PER_MHZ));
p_max = fin / (driver_data->pll_fin_min * MHZ); p_max = fin / (driver_data->pll_fin_min * HZ_PER_MHZ);
for (_p = p_min; _p <= p_max; ++_p) { for (_p = p_min; _p <= p_max; ++_p) {
for (_s = 0; _s <= 5; ++_s) { for (_s = 0; _s <= 5; ++_s) {
@ -591,8 +588,8 @@ static unsigned long samsung_dsim_pll_find_pms(struct samsung_dsim *dsi,
tmp = (u64)_m * fin; tmp = (u64)_m * fin;
do_div(tmp, _p); do_div(tmp, _p);
if (tmp < driver_data->min_freq * MHZ || if (tmp < driver_data->min_freq * HZ_PER_MHZ ||
tmp > driver_data->max_freq * MHZ) tmp > driver_data->max_freq * HZ_PER_MHZ)
continue; continue;
tmp = (u64)_m * fin; tmp = (u64)_m * fin;
@ -635,7 +632,7 @@ static unsigned long samsung_dsim_set_pll(struct samsung_dsim *dsi,
* limit. * limit.
*/ */
fin = clk_get_rate(clk_get_parent(dsi->pll_clk)); fin = clk_get_rate(clk_get_parent(dsi->pll_clk));
while (fin > driver_data->pll_fin_max * MHZ) while (fin > driver_data->pll_fin_max * HZ_PER_MHZ)
fin /= 2; fin /= 2;
clk_set_rate(dsi->pll_clk, fin); clk_set_rate(dsi->pll_clk, fin);
@ -661,10 +658,11 @@ static unsigned long samsung_dsim_set_pll(struct samsung_dsim *dsi,
if (driver_data->has_freqband) { if (driver_data->has_freqband) {
static const unsigned long freq_bands[] = { static const unsigned long freq_bands[] = {
100 * MHZ, 120 * MHZ, 160 * MHZ, 200 * MHZ, 100 * HZ_PER_MHZ, 120 * HZ_PER_MHZ, 160 * HZ_PER_MHZ,
270 * MHZ, 320 * MHZ, 390 * MHZ, 450 * MHZ, 200 * HZ_PER_MHZ, 270 * HZ_PER_MHZ, 320 * HZ_PER_MHZ,
510 * MHZ, 560 * MHZ, 640 * MHZ, 690 * MHZ, 390 * HZ_PER_MHZ, 450 * HZ_PER_MHZ, 510 * HZ_PER_MHZ,
770 * MHZ, 870 * MHZ, 950 * MHZ, 560 * HZ_PER_MHZ, 640 * HZ_PER_MHZ, 690 * HZ_PER_MHZ,
770 * HZ_PER_MHZ, 870 * HZ_PER_MHZ, 950 * HZ_PER_MHZ,
}; };
int band; int band;
@ -724,7 +722,7 @@ static int samsung_dsim_enable_clock(struct samsung_dsim *dsi)
esc_div = DIV_ROUND_UP(byte_clk, dsi->esc_clk_rate); esc_div = DIV_ROUND_UP(byte_clk, dsi->esc_clk_rate);
esc_clk = byte_clk / esc_div; esc_clk = byte_clk / esc_div;
if (esc_clk > 20 * MHZ) { if (esc_clk > 20 * HZ_PER_MHZ) {
++esc_div; ++esc_div;
esc_clk = byte_clk / esc_div; esc_clk = byte_clk / esc_div;
} }
@ -899,8 +897,6 @@ static int samsung_dsim_init_link(struct samsung_dsim *dsi)
* The user manual describes that following bits are ignored in * The user manual describes that following bits are ignored in
* command mode. * command mode.
*/ */
if (!(dsi->mode_flags & MIPI_DSI_MODE_VSYNC_FLUSH))
reg |= DSIM_MFLUSH_VS;
if (dsi->mode_flags & MIPI_DSI_MODE_VIDEO_SYNC_PULSE) if (dsi->mode_flags & MIPI_DSI_MODE_VIDEO_SYNC_PULSE)
reg |= DSIM_SYNC_INFORM; reg |= DSIM_SYNC_INFORM;
if (dsi->mode_flags & MIPI_DSI_MODE_VIDEO_BURST) if (dsi->mode_flags & MIPI_DSI_MODE_VIDEO_BURST)
@ -1236,43 +1232,34 @@ static void samsung_dsim_transfer_start(struct samsung_dsim *dsi)
{ {
unsigned long flags; unsigned long flags;
struct samsung_dsim_transfer *xfer; struct samsung_dsim_transfer *xfer;
bool start = false;
again:
spin_lock_irqsave(&dsi->transfer_lock, flags); spin_lock_irqsave(&dsi->transfer_lock, flags);
if (list_empty(&dsi->transfer_list)) { while (!list_empty(&dsi->transfer_list)) {
xfer = list_first_entry(&dsi->transfer_list,
struct samsung_dsim_transfer, list);
spin_unlock_irqrestore(&dsi->transfer_lock, flags); spin_unlock_irqrestore(&dsi->transfer_lock, flags);
return;
if (xfer->packet.payload_length &&
xfer->tx_done == xfer->packet.payload_length)
/* waiting for RX */
return;
samsung_dsim_send_to_fifo(dsi, xfer);
if (xfer->packet.payload_length || xfer->rx_len)
return;
xfer->result = 0;
complete(&xfer->completed);
spin_lock_irqsave(&dsi->transfer_lock, flags);
list_del_init(&xfer->list);
} }
xfer = list_first_entry(&dsi->transfer_list,
struct samsung_dsim_transfer, list);
spin_unlock_irqrestore(&dsi->transfer_lock, flags); spin_unlock_irqrestore(&dsi->transfer_lock, flags);
if (xfer->packet.payload_length &&
xfer->tx_done == xfer->packet.payload_length)
/* waiting for RX */
return;
samsung_dsim_send_to_fifo(dsi, xfer);
if (xfer->packet.payload_length || xfer->rx_len)
return;
xfer->result = 0;
complete(&xfer->completed);
spin_lock_irqsave(&dsi->transfer_lock, flags);
list_del_init(&xfer->list);
start = !list_empty(&dsi->transfer_list);
spin_unlock_irqrestore(&dsi->transfer_lock, flags);
if (start)
goto again;
} }
static bool samsung_dsim_transfer_finish(struct samsung_dsim *dsi) static bool samsung_dsim_transfer_finish(struct samsung_dsim *dsi)

View File

@ -1677,11 +1677,6 @@ static int ti_sn_bridge_gpio_set(struct gpio_chip *chip, unsigned int offset,
{ {
struct ti_sn65dsi86 *pdata = gpiochip_get_data(chip); struct ti_sn65dsi86 *pdata = gpiochip_get_data(chip);
if (!test_bit(offset, pdata->gchip_output)) {
dev_err(pdata->dev, "Ignoring GPIO set while input\n");
return -EPERM;
}
val &= 1; val &= 1;
return regmap_update_bits(pdata->regmap, SN_GPIO_IO_REG, return regmap_update_bits(pdata->regmap, SN_GPIO_IO_REG,
BIT(SN_GPIO_OUTPUT_SHIFT + offset), BIT(SN_GPIO_OUTPUT_SHIFT + offset),

View File

@ -3957,23 +3957,31 @@ EXPORT_SYMBOL(drm_dp_pcon_convert_rgb_to_ycbcr);
* Returns: %0 on success, negative error code on failure * Returns: %0 on success, negative error code on failure
*/ */
int drm_edp_backlight_set_level(struct drm_dp_aux *aux, const struct drm_edp_backlight_info *bl, int drm_edp_backlight_set_level(struct drm_dp_aux *aux, const struct drm_edp_backlight_info *bl,
u16 level) u32 level)
{ {
int ret; int ret;
u8 buf[2] = { 0 }; unsigned int offset = DP_EDP_BACKLIGHT_BRIGHTNESS_MSB;
u8 buf[3] = { 0 };
/* The panel uses the PWM for controlling brightness levels */ /* The panel uses the PWM for controlling brightness levels */
if (!bl->aux_set) if (!(bl->aux_set || bl->luminance_set))
return 0; return 0;
if (bl->lsb_reg_used) { if (bl->luminance_set) {
level = level * 1000;
level &= 0xffffff;
buf[0] = (level & 0x0000ff);
buf[1] = (level & 0x00ff00) >> 8;
buf[2] = (level & 0xff0000) >> 16;
offset = DP_EDP_PANEL_TARGET_LUMINANCE_VALUE;
} else if (bl->lsb_reg_used) {
buf[0] = (level & 0xff00) >> 8; buf[0] = (level & 0xff00) >> 8;
buf[1] = (level & 0x00ff); buf[1] = (level & 0x00ff);
} else { } else {
buf[0] = level; buf[0] = level;
} }
ret = drm_dp_dpcd_write_data(aux, DP_EDP_BACKLIGHT_BRIGHTNESS_MSB, buf, sizeof(buf)); ret = drm_dp_dpcd_write_data(aux, offset, buf, sizeof(buf));
if (ret < 0) { if (ret < 0) {
drm_err(aux->drm_dev, drm_err(aux->drm_dev,
"%s: Failed to write aux backlight level: %d\n", "%s: Failed to write aux backlight level: %d\n",
@ -4036,7 +4044,7 @@ drm_edp_backlight_set_enable(struct drm_dp_aux *aux, const struct drm_edp_backli
* Returns: %0 on success, negative error code on failure. * Returns: %0 on success, negative error code on failure.
*/ */
int drm_edp_backlight_enable(struct drm_dp_aux *aux, const struct drm_edp_backlight_info *bl, int drm_edp_backlight_enable(struct drm_dp_aux *aux, const struct drm_edp_backlight_info *bl,
const u16 level) const u32 level)
{ {
int ret; int ret;
u8 dpcd_buf; u8 dpcd_buf;
@ -4046,6 +4054,9 @@ int drm_edp_backlight_enable(struct drm_dp_aux *aux, const struct drm_edp_backli
else else
dpcd_buf = DP_EDP_BACKLIGHT_CONTROL_MODE_PWM; dpcd_buf = DP_EDP_BACKLIGHT_CONTROL_MODE_PWM;
if (bl->luminance_set)
dpcd_buf |= DP_EDP_PANEL_LUMINANCE_CONTROL_ENABLE;
if (bl->pwmgen_bit_count) { if (bl->pwmgen_bit_count) {
ret = drm_dp_dpcd_write_byte(aux, DP_EDP_PWMGEN_BIT_COUNT, bl->pwmgen_bit_count); ret = drm_dp_dpcd_write_byte(aux, DP_EDP_PWMGEN_BIT_COUNT, bl->pwmgen_bit_count);
if (ret < 0) if (ret < 0)
@ -4209,7 +4220,7 @@ drm_edp_backlight_probe_state(struct drm_dp_aux *aux, struct drm_edp_backlight_i
u8 *current_mode) u8 *current_mode)
{ {
int ret; int ret;
u8 buf[2]; u8 buf[3];
u8 mode_reg; u8 mode_reg;
ret = drm_dp_dpcd_read_byte(aux, DP_EDP_BACKLIGHT_MODE_SET_REGISTER, &mode_reg); ret = drm_dp_dpcd_read_byte(aux, DP_EDP_BACKLIGHT_MODE_SET_REGISTER, &mode_reg);
@ -4226,17 +4237,37 @@ drm_edp_backlight_probe_state(struct drm_dp_aux *aux, struct drm_edp_backlight_i
if (*current_mode == DP_EDP_BACKLIGHT_CONTROL_MODE_DPCD) { if (*current_mode == DP_EDP_BACKLIGHT_CONTROL_MODE_DPCD) {
int size = 1 + bl->lsb_reg_used; int size = 1 + bl->lsb_reg_used;
ret = drm_dp_dpcd_read_data(aux, DP_EDP_BACKLIGHT_BRIGHTNESS_MSB, buf, size); if (bl->luminance_set) {
if (ret < 0) { ret = drm_dp_dpcd_read_data(aux, DP_EDP_PANEL_TARGET_LUMINANCE_VALUE,
drm_dbg_kms(aux->drm_dev, "%s: Failed to read backlight level: %d\n", buf, sizeof(buf));
aux->name, ret); if (ret < 0) {
return ret; drm_dbg_kms(aux->drm_dev,
"%s: Failed to read backlight level: %d\n",
aux->name, ret);
return ret;
} }
if (bl->lsb_reg_used) /*
return (buf[0] << 8) | buf[1]; * Incase luminance is set we want to send the value back in nits but since
else * DP_EDP_PANEL_TARGET_LUMINANCE stores values in millinits we need to divide
return buf[0]; * by 1000.
*/
return (buf[0] | buf[1] << 8 | buf[2] << 16) / 1000;
} else {
ret = drm_dp_dpcd_read_data(aux, DP_EDP_BACKLIGHT_BRIGHTNESS_MSB,
buf, size);
if (ret < 0) {
drm_dbg_kms(aux->drm_dev,
"%s: Failed to read backlight level: %d\n",
aux->name, ret);
return ret;
}
if (bl->lsb_reg_used)
return (buf[0] << 8) | buf[1];
else
return buf[0];
}
} }
/* /*
@ -4251,10 +4282,12 @@ drm_edp_backlight_probe_state(struct drm_dp_aux *aux, struct drm_edp_backlight_i
* interface. * interface.
* @aux: The DP aux device to use for probing * @aux: The DP aux device to use for probing
* @bl: The &drm_edp_backlight_info struct to fill out with information on the backlight * @bl: The &drm_edp_backlight_info struct to fill out with information on the backlight
* @max_luminance: max luminance when need luminance is set as true
* @driver_pwm_freq_hz: Optional PWM frequency from the driver in hz * @driver_pwm_freq_hz: Optional PWM frequency from the driver in hz
* @edp_dpcd: A cached copy of the eDP DPCD * @edp_dpcd: A cached copy of the eDP DPCD
* @current_level: Where to store the probed brightness level, if any * @current_level: Where to store the probed brightness level, if any
* @current_mode: Where to store the currently set backlight control mode * @current_mode: Where to store the currently set backlight control mode
* @need_luminance: Tells us if a we want to manipulate backlight using luminance values
* *
* Initializes a &drm_edp_backlight_info struct by probing @aux for it's backlight capabilities, * Initializes a &drm_edp_backlight_info struct by probing @aux for it's backlight capabilities,
* along with also probing the current and maximum supported brightness levels. * along with also probing the current and maximum supported brightness levels.
@ -4266,8 +4299,9 @@ drm_edp_backlight_probe_state(struct drm_dp_aux *aux, struct drm_edp_backlight_i
*/ */
int int
drm_edp_backlight_init(struct drm_dp_aux *aux, struct drm_edp_backlight_info *bl, drm_edp_backlight_init(struct drm_dp_aux *aux, struct drm_edp_backlight_info *bl,
u32 max_luminance,
u16 driver_pwm_freq_hz, const u8 edp_dpcd[EDP_DISPLAY_CTL_CAP_SIZE], u16 driver_pwm_freq_hz, const u8 edp_dpcd[EDP_DISPLAY_CTL_CAP_SIZE],
u16 *current_level, u8 *current_mode) u32 *current_level, u8 *current_mode, bool need_luminance)
{ {
int ret; int ret;
@ -4277,18 +4311,26 @@ drm_edp_backlight_init(struct drm_dp_aux *aux, struct drm_edp_backlight_info *bl
bl->aux_set = true; bl->aux_set = true;
if (edp_dpcd[2] & DP_EDP_BACKLIGHT_BRIGHTNESS_BYTE_COUNT) if (edp_dpcd[2] & DP_EDP_BACKLIGHT_BRIGHTNESS_BYTE_COUNT)
bl->lsb_reg_used = true; bl->lsb_reg_used = true;
if ((edp_dpcd[0] & DP_EDP_15) && edp_dpcd[3] &
(DP_EDP_PANEL_LUMINANCE_CONTROL_CAPABLE) && need_luminance)
bl->luminance_set = true;
/* Sanity check caps */ /* Sanity check caps */
if (!bl->aux_set && !(edp_dpcd[2] & DP_EDP_BACKLIGHT_BRIGHTNESS_PWM_PIN_CAP)) { if (!bl->aux_set && !(edp_dpcd[2] & DP_EDP_BACKLIGHT_BRIGHTNESS_PWM_PIN_CAP) &&
!bl->luminance_set) {
drm_dbg_kms(aux->drm_dev, drm_dbg_kms(aux->drm_dev,
"%s: Panel supports neither AUX or PWM brightness control? Aborting\n", "%s: Panel does not support AUX, PWM or luminance-based brightness control. Aborting\n",
aux->name); aux->name);
return -EINVAL; return -EINVAL;
} }
ret = drm_edp_backlight_probe_max(aux, bl, driver_pwm_freq_hz, edp_dpcd); if (bl->luminance_set) {
if (ret < 0) bl->max = max_luminance;
return ret; } else {
ret = drm_edp_backlight_probe_max(aux, bl, driver_pwm_freq_hz, edp_dpcd);
if (ret < 0)
return ret;
}
ret = drm_edp_backlight_probe_state(aux, bl, current_mode); ret = drm_edp_backlight_probe_state(aux, bl, current_mode);
if (ret < 0) if (ret < 0)
@ -4367,7 +4409,7 @@ int drm_panel_dp_aux_backlight(struct drm_panel *panel, struct drm_dp_aux *aux)
{ {
struct dp_aux_backlight *bl; struct dp_aux_backlight *bl;
struct backlight_properties props = { 0 }; struct backlight_properties props = { 0 };
u16 current_level; u32 current_level;
u8 current_mode; u8 current_mode;
u8 edp_dpcd[EDP_DISPLAY_CTL_CAP_SIZE]; u8 edp_dpcd[EDP_DISPLAY_CTL_CAP_SIZE];
int ret; int ret;
@ -4391,8 +4433,8 @@ int drm_panel_dp_aux_backlight(struct drm_panel *panel, struct drm_dp_aux *aux)
bl->aux = aux; bl->aux = aux;
ret = drm_edp_backlight_init(aux, &bl->info, 0, edp_dpcd, ret = drm_edp_backlight_init(aux, &bl->info, 0, 0, edp_dpcd,
&current_level, &current_mode); &current_level, &current_mode, false);
if (ret < 0) if (ret < 0)
return ret; return ret;

View File

@ -295,6 +295,11 @@ EXPORT_SYMBOL(__devm_drm_bridge_alloc);
*/ */
void drm_bridge_add(struct drm_bridge *bridge) void drm_bridge_add(struct drm_bridge *bridge)
{ {
if (!bridge->container)
DRM_WARN("DRM bridge corrupted or not allocated by devm_drm_bridge_alloc()\n");
drm_bridge_get(bridge);
mutex_init(&bridge->hpd_mutex); mutex_init(&bridge->hpd_mutex);
if (bridge->ops & DRM_BRIDGE_OP_HDMI) if (bridge->ops & DRM_BRIDGE_OP_HDMI)
@ -342,6 +347,8 @@ void drm_bridge_remove(struct drm_bridge *bridge)
mutex_unlock(&bridge_lock); mutex_unlock(&bridge_lock);
mutex_destroy(&bridge->hpd_mutex); mutex_destroy(&bridge->hpd_mutex);
drm_bridge_put(bridge);
} }
EXPORT_SYMBOL(drm_bridge_remove); EXPORT_SYMBOL(drm_bridge_remove);
@ -407,11 +414,17 @@ int drm_bridge_attach(struct drm_encoder *encoder, struct drm_bridge *bridge,
if (!encoder || !bridge) if (!encoder || !bridge)
return -EINVAL; return -EINVAL;
if (previous && (!previous->dev || previous->encoder != encoder)) drm_bridge_get(bridge);
return -EINVAL;
if (bridge->dev) if (previous && (!previous->dev || previous->encoder != encoder)) {
return -EBUSY; ret = -EINVAL;
goto err_put_bridge;
}
if (bridge->dev) {
ret = -EBUSY;
goto err_put_bridge;
}
bridge->dev = encoder->dev; bridge->dev = encoder->dev;
bridge->encoder = encoder; bridge->encoder = encoder;
@ -460,6 +473,8 @@ err_reset_bridge:
"failed to attach bridge %pOF to encoder %s\n", "failed to attach bridge %pOF to encoder %s\n",
bridge->of_node, encoder->name); bridge->of_node, encoder->name);
err_put_bridge:
drm_bridge_put(bridge);
return ret; return ret;
} }
EXPORT_SYMBOL(drm_bridge_attach); EXPORT_SYMBOL(drm_bridge_attach);
@ -480,6 +495,7 @@ void drm_bridge_detach(struct drm_bridge *bridge)
list_del(&bridge->chain_node); list_del(&bridge->chain_node);
bridge->dev = NULL; bridge->dev = NULL;
drm_bridge_put(bridge);
} }
/** /**

View File

@ -559,18 +559,6 @@ static void drm_fb_xrgb8888_to_rgb565_line(void *dbuf, const void *sbuf, unsigne
drm_fb_xfrm_line_32to16(dbuf, sbuf, pixels, drm_pixel_xrgb8888_to_rgb565); drm_fb_xfrm_line_32to16(dbuf, sbuf, pixels, drm_pixel_xrgb8888_to_rgb565);
} }
static __always_inline u32 drm_xrgb8888_to_rgb565_swab(u32 pix)
{
return swab16(drm_pixel_xrgb8888_to_rgb565(pix));
}
/* TODO: implement this helper as conversion to RGB565|BIG_ENDIAN */
static void drm_fb_xrgb8888_to_rgb565_swab_line(void *dbuf, const void *sbuf,
unsigned int pixels)
{
drm_fb_xfrm_line_32to16(dbuf, sbuf, pixels, drm_xrgb8888_to_rgb565_swab);
}
/** /**
* drm_fb_xrgb8888_to_rgb565 - Convert XRGB8888 to RGB565 clip buffer * drm_fb_xrgb8888_to_rgb565 - Convert XRGB8888 to RGB565 clip buffer
* @dst: Array of RGB565 destination buffers * @dst: Array of RGB565 destination buffers
@ -580,7 +568,6 @@ static void drm_fb_xrgb8888_to_rgb565_swab_line(void *dbuf, const void *sbuf,
* @fb: DRM framebuffer * @fb: DRM framebuffer
* @clip: Clip rectangle area to copy * @clip: Clip rectangle area to copy
* @state: Transform and conversion state * @state: Transform and conversion state
* @swab: Swap bytes
* *
* This function copies parts of a framebuffer to display memory and converts the * This function copies parts of a framebuffer to display memory and converts the
* color format during the process. Destination and framebuffer formats must match. The * color format during the process. Destination and framebuffer formats must match. The
@ -595,24 +582,57 @@ static void drm_fb_xrgb8888_to_rgb565_swab_line(void *dbuf, const void *sbuf,
*/ */
void drm_fb_xrgb8888_to_rgb565(struct iosys_map *dst, const unsigned int *dst_pitch, void drm_fb_xrgb8888_to_rgb565(struct iosys_map *dst, const unsigned int *dst_pitch,
const struct iosys_map *src, const struct drm_framebuffer *fb, const struct iosys_map *src, const struct drm_framebuffer *fb,
const struct drm_rect *clip, struct drm_format_conv_state *state, const struct drm_rect *clip, struct drm_format_conv_state *state)
bool swab)
{ {
static const u8 dst_pixsize[DRM_FORMAT_MAX_PLANES] = { static const u8 dst_pixsize[DRM_FORMAT_MAX_PLANES] = {
2, 2,
}; };
void (*xfrm_line)(void *dbuf, const void *sbuf, unsigned int npixels); drm_fb_xfrm(dst, dst_pitch, dst_pixsize, src, fb, clip, false, state,
drm_fb_xrgb8888_to_rgb565_line);
if (swab)
xfrm_line = drm_fb_xrgb8888_to_rgb565_swab_line;
else
xfrm_line = drm_fb_xrgb8888_to_rgb565_line;
drm_fb_xfrm(dst, dst_pitch, dst_pixsize, src, fb, clip, false, state, xfrm_line);
} }
EXPORT_SYMBOL(drm_fb_xrgb8888_to_rgb565); EXPORT_SYMBOL(drm_fb_xrgb8888_to_rgb565);
static void drm_fb_xrgb8888_to_rgb565be_line(void *dbuf, const void *sbuf,
unsigned int pixels)
{
drm_fb_xfrm_line_32to16(dbuf, sbuf, pixels, drm_pixel_xrgb8888_to_rgb565be);
}
/**
* drm_fb_xrgb8888_to_rgb565be - Convert XRGB8888 to RGB565|DRM_FORMAT_BIG_ENDIAN clip buffer
* @dst: Array of RGB565BE destination buffers
* @dst_pitch: Array of numbers of bytes between the start of two consecutive scanlines
* within @dst; can be NULL if scanlines are stored next to each other.
* @src: Array of XRGB8888 source buffer
* @fb: DRM framebuffer
* @clip: Clip rectangle area to copy
* @state: Transform and conversion state
*
* This function copies parts of a framebuffer to display memory and converts the
* color format during the process. Destination and framebuffer formats must match. The
* parameters @dst, @dst_pitch and @src refer to arrays. Each array must have at
* least as many entries as there are planes in @fb's format. Each entry stores the
* value for the format's respective color plane at the same index.
*
* This function does not apply clipping on @dst (i.e. the destination is at the
* top-left corner).
*
* Drivers can use this function for RGB565BE devices that don't support XRGB8888 natively.
*/
void drm_fb_xrgb8888_to_rgb565be(struct iosys_map *dst, const unsigned int *dst_pitch,
const struct iosys_map *src, const struct drm_framebuffer *fb,
const struct drm_rect *clip, struct drm_format_conv_state *state)
{
static const u8 dst_pixsize[DRM_FORMAT_MAX_PLANES] = {
2,
};
drm_fb_xfrm(dst, dst_pitch, dst_pixsize, src, fb, clip, false, state,
drm_fb_xrgb8888_to_rgb565be_line);
}
EXPORT_SYMBOL(drm_fb_xrgb8888_to_rgb565be);
static void drm_fb_xrgb8888_to_xrgb1555_line(void *dbuf, const void *sbuf, unsigned int pixels) static void drm_fb_xrgb8888_to_xrgb1555_line(void *dbuf, const void *sbuf, unsigned int pixels)
{ {
drm_fb_xfrm_line_32to16(dbuf, sbuf, pixels, drm_pixel_xrgb8888_to_xrgb1555); drm_fb_xfrm_line_32to16(dbuf, sbuf, pixels, drm_pixel_xrgb8888_to_xrgb1555);
@ -1188,7 +1208,7 @@ int drm_fb_blit(struct iosys_map *dst, const unsigned int *dst_pitch, uint32_t d
return 0; return 0;
} else if (fb_format == DRM_FORMAT_XRGB8888) { } else if (fb_format == DRM_FORMAT_XRGB8888) {
if (dst_format == DRM_FORMAT_RGB565) { if (dst_format == DRM_FORMAT_RGB565) {
drm_fb_xrgb8888_to_rgb565(dst, dst_pitch, src, fb, clip, state, false); drm_fb_xrgb8888_to_rgb565(dst, dst_pitch, src, fb, clip, state);
return 0; return 0;
} else if (dst_format == DRM_FORMAT_XRGB1555) { } else if (dst_format == DRM_FORMAT_XRGB1555) {
drm_fb_xrgb8888_to_xrgb1555(dst, dst_pitch, src, fb, clip, state); drm_fb_xrgb8888_to_xrgb1555(dst, dst_pitch, src, fb, clip, state);

View File

@ -5,6 +5,7 @@
#include <linux/bits.h> #include <linux/bits.h>
#include <linux/types.h> #include <linux/types.h>
#include <linux/swab.h>
/* /*
* Each pixel-format conversion helper takes a raw pixel in a * Each pixel-format conversion helper takes a raw pixel in a
@ -59,6 +60,11 @@ static inline u32 drm_pixel_xrgb8888_to_rgb565(u32 pix)
((pix & 0x000000f8) >> 3); ((pix & 0x000000f8) >> 3);
} }
static inline u32 drm_pixel_xrgb8888_to_rgb565be(u32 pix)
{
return swab16(drm_pixel_xrgb8888_to_rgb565(pix));
}
static inline u32 drm_pixel_xrgb8888_to_rgbx5551(u32 pix) static inline u32 drm_pixel_xrgb8888_to_rgbx5551(u32 pix)
{ {
return ((pix & 0x00f80000) >> 8) | return ((pix & 0x00f80000) >> 8) |

View File

@ -238,6 +238,14 @@ const struct drm_format_info *__drm_format_info(u32 format)
{ .format = DRM_FORMAT_ABGR2101010, .depth = 30, .num_planes = 1, .cpp = { 4, 0, 0 }, .hsub = 1, .vsub = 1, .has_alpha = true }, { .format = DRM_FORMAT_ABGR2101010, .depth = 30, .num_planes = 1, .cpp = { 4, 0, 0 }, .hsub = 1, .vsub = 1, .has_alpha = true },
{ .format = DRM_FORMAT_RGBA1010102, .depth = 30, .num_planes = 1, .cpp = { 4, 0, 0 }, .hsub = 1, .vsub = 1, .has_alpha = true }, { .format = DRM_FORMAT_RGBA1010102, .depth = 30, .num_planes = 1, .cpp = { 4, 0, 0 }, .hsub = 1, .vsub = 1, .has_alpha = true },
{ .format = DRM_FORMAT_BGRA1010102, .depth = 30, .num_planes = 1, .cpp = { 4, 0, 0 }, .hsub = 1, .vsub = 1, .has_alpha = true }, { .format = DRM_FORMAT_BGRA1010102, .depth = 30, .num_planes = 1, .cpp = { 4, 0, 0 }, .hsub = 1, .vsub = 1, .has_alpha = true },
{ .format = DRM_FORMAT_RGB161616, .depth = 0,
.num_planes = 1, .char_per_block = { 6, 0, 0 },
.block_w = { 1, 0, 0 }, .block_h = { 1, 0, 0 },
.hsub = 1, .vsub = 1, .has_alpha = false },
{ .format = DRM_FORMAT_BGR161616, .depth = 0,
.num_planes = 1, .char_per_block = { 6, 0, 0 },
.block_w = { 1, 0, 0 }, .block_h = { 1, 0, 0 },
.hsub = 1, .vsub = 1, .has_alpha = false },
{ .format = DRM_FORMAT_ARGB8888, .depth = 32, .num_planes = 1, .cpp = { 4, 0, 0 }, .hsub = 1, .vsub = 1, .has_alpha = true }, { .format = DRM_FORMAT_ARGB8888, .depth = 32, .num_planes = 1, .cpp = { 4, 0, 0 }, .hsub = 1, .vsub = 1, .has_alpha = true },
{ .format = DRM_FORMAT_ABGR8888, .depth = 32, .num_planes = 1, .cpp = { 4, 0, 0 }, .hsub = 1, .vsub = 1, .has_alpha = true }, { .format = DRM_FORMAT_ABGR8888, .depth = 32, .num_planes = 1, .cpp = { 4, 0, 0 }, .hsub = 1, .vsub = 1, .has_alpha = true },
{ .format = DRM_FORMAT_RGBA8888, .depth = 32, .num_planes = 1, .cpp = { 4, 0, 0 }, .hsub = 1, .vsub = 1, .has_alpha = true }, { .format = DRM_FORMAT_RGBA8888, .depth = 32, .num_planes = 1, .cpp = { 4, 0, 0 }, .hsub = 1, .vsub = 1, .has_alpha = true },

View File

@ -230,7 +230,13 @@ int mipi_dbi_buf_copy(void *dst, struct iosys_map *src, struct drm_framebuffer *
case DRM_FORMAT_XRGB8888: case DRM_FORMAT_XRGB8888:
switch (dbidev->pixel_format) { switch (dbidev->pixel_format) {
case DRM_FORMAT_RGB565: case DRM_FORMAT_RGB565:
drm_fb_xrgb8888_to_rgb565(&dst_map, NULL, src, fb, clip, fmtcnv_state, swap); if (swap) {
drm_fb_xrgb8888_to_rgb565be(&dst_map, NULL, src, fb, clip,
fmtcnv_state);
} else {
drm_fb_xrgb8888_to_rgb565(&dst_map, NULL, src, fb, clip,
fmtcnv_state);
}
break; break;
case DRM_FORMAT_RGB888: case DRM_FORMAT_RGB888:
drm_fb_xrgb8888_to_rgb888(&dst_map, NULL, src, fb, clip, fmtcnv_state); drm_fb_xrgb8888_to_rgb888(&dst_map, NULL, src, fb, clip, fmtcnv_state);

View File

@ -188,8 +188,13 @@ retry:
} else if (format->format == DRM_FORMAT_RGB332) { } else if (format->format == DRM_FORMAT_RGB332) {
drm_fb_xrgb8888_to_rgb332(&dst, NULL, src, fb, rect, fmtcnv_state); drm_fb_xrgb8888_to_rgb332(&dst, NULL, src, fb, rect, fmtcnv_state);
} else if (format->format == DRM_FORMAT_RGB565) { } else if (format->format == DRM_FORMAT_RGB565) {
drm_fb_xrgb8888_to_rgb565(&dst, NULL, src, fb, rect, fmtcnv_state, if (gud_is_big_endian()) {
gud_is_big_endian()); drm_fb_xrgb8888_to_rgb565be(&dst, NULL, src, fb, rect,
fmtcnv_state);
} else {
drm_fb_xrgb8888_to_rgb565(&dst, NULL, src, fb, rect,
fmtcnv_state);
}
} else if (format->format == DRM_FORMAT_RGB888) { } else if (format->format == DRM_FORMAT_RGB888) {
drm_fb_xrgb8888_to_rgb888(&dst, NULL, src, fb, rect, fmtcnv_state); drm_fb_xrgb8888_to_rgb888(&dst, NULL, src, fb, rect, fmtcnv_state);
} else { } else {

View File

@ -475,31 +475,6 @@ static u32 intel_dp_aux_vesa_get_backlight(struct intel_connector *connector, en
return connector->panel.backlight.level; return connector->panel.backlight.level;
} }
static int
intel_dp_aux_vesa_set_luminance(struct intel_connector *connector, u32 level)
{
struct intel_dp *intel_dp = enc_to_intel_dp(connector->encoder);
u8 buf[3];
int ret;
level = level * 1000;
level &= 0xffffff;
buf[0] = (level & 0x0000ff);
buf[1] = (level & 0x00ff00) >> 8;
buf[2] = (level & 0xff0000) >> 16;
ret = drm_dp_dpcd_write(&intel_dp->aux, DP_EDP_PANEL_TARGET_LUMINANCE_VALUE,
buf, sizeof(buf));
if (ret != sizeof(buf)) {
drm_err(intel_dp->aux.drm_dev,
"%s: Failed to set VESA Aux Luminance: %d\n",
intel_dp->aux.name, ret);
return -EINVAL;
} else {
return 0;
}
}
static void static void
intel_dp_aux_vesa_set_backlight(const struct drm_connector_state *conn_state, u32 level) intel_dp_aux_vesa_set_backlight(const struct drm_connector_state *conn_state, u32 level)
{ {
@ -507,11 +482,6 @@ intel_dp_aux_vesa_set_backlight(const struct drm_connector_state *conn_state, u3
struct intel_panel *panel = &connector->panel; struct intel_panel *panel = &connector->panel;
struct intel_dp *intel_dp = enc_to_intel_dp(connector->encoder); struct intel_dp *intel_dp = enc_to_intel_dp(connector->encoder);
if (panel->backlight.edp.vesa.luminance_control_support) {
if (!intel_dp_aux_vesa_set_luminance(connector, level))
return;
}
if (!panel->backlight.edp.vesa.info.aux_set) { if (!panel->backlight.edp.vesa.info.aux_set) {
const u32 pwm_level = intel_backlight_level_to_pwm(connector, level); const u32 pwm_level = intel_backlight_level_to_pwm(connector, level);
@ -528,18 +498,6 @@ intel_dp_aux_vesa_enable_backlight(const struct intel_crtc_state *crtc_state,
struct intel_connector *connector = to_intel_connector(conn_state->connector); struct intel_connector *connector = to_intel_connector(conn_state->connector);
struct intel_panel *panel = &connector->panel; struct intel_panel *panel = &connector->panel;
struct intel_dp *intel_dp = enc_to_intel_dp(connector->encoder); struct intel_dp *intel_dp = enc_to_intel_dp(connector->encoder);
int ret;
if (panel->backlight.edp.vesa.luminance_control_support) {
ret = drm_dp_dpcd_writeb(&intel_dp->aux, DP_EDP_BACKLIGHT_MODE_SET_REGISTER,
DP_EDP_PANEL_LUMINANCE_CONTROL_ENABLE);
if (ret == 1)
return;
if (!intel_dp_aux_vesa_set_luminance(connector, level))
return;
}
if (!panel->backlight.edp.vesa.info.aux_enable) { if (!panel->backlight.edp.vesa.info.aux_enable) {
u32 pwm_level; u32 pwm_level;
@ -580,13 +538,41 @@ static int intel_dp_aux_vesa_setup_backlight(struct intel_connector *connector,
&connector->base.display_info.luminance_range; &connector->base.display_info.luminance_range;
struct intel_dp *intel_dp = intel_attached_dp(connector); struct intel_dp *intel_dp = intel_attached_dp(connector);
struct intel_panel *panel = &connector->panel; struct intel_panel *panel = &connector->panel;
u16 current_level; u32 current_level;
u8 current_mode; u8 current_mode;
int ret; int ret;
if (panel->backlight.edp.vesa.luminance_control_support) { ret = drm_edp_backlight_init(&intel_dp->aux, &panel->backlight.edp.vesa.info,
luminance_range->max_luminance,
panel->vbt.backlight.pwm_freq_hz,
intel_dp->edp_dpcd, &current_level, &current_mode,
false);
if (ret < 0)
return ret;
drm_dbg_kms(display->drm,
"[CONNECTOR:%d:%s] AUX VESA backlight enable is controlled through %s\n",
connector->base.base.id, connector->base.name,
dpcd_vs_pwm_str(panel->backlight.edp.vesa.info.aux_enable));
drm_dbg_kms(display->drm,
"[CONNECTOR:%d:%s] AUX VESA backlight level is controlled through %s\n",
connector->base.base.id, connector->base.name,
dpcd_vs_pwm_str(panel->backlight.edp.vesa.info.aux_set));
if (!panel->backlight.edp.vesa.info.aux_set ||
!panel->backlight.edp.vesa.info.aux_enable) {
ret = panel->backlight.pwm_funcs->setup(connector, pipe);
if (ret < 0) {
drm_err(display->drm,
"[CONNECTOR:%d:%s] Failed to setup PWM backlight controls for eDP backlight: %d\n",
connector->base.base.id, connector->base.name, ret);
return ret;
}
}
if (panel->backlight.edp.vesa.info.luminance_set) {
if (luminance_range->max_luminance) { if (luminance_range->max_luminance) {
panel->backlight.max = luminance_range->max_luminance; panel->backlight.max = panel->backlight.edp.vesa.info.max;
panel->backlight.min = luminance_range->min_luminance; panel->backlight.min = luminance_range->min_luminance;
} else { } else {
panel->backlight.max = 512; panel->backlight.max = 512;
@ -597,54 +583,26 @@ static int intel_dp_aux_vesa_setup_backlight(struct intel_connector *connector,
drm_dbg_kms(display->drm, drm_dbg_kms(display->drm,
"[CONNECTOR:%d:%s] AUX VESA Nits backlight level is controlled through DPCD\n", "[CONNECTOR:%d:%s] AUX VESA Nits backlight level is controlled through DPCD\n",
connector->base.base.id, connector->base.name); connector->base.base.id, connector->base.name);
} else { } else if (panel->backlight.edp.vesa.info.aux_set) {
ret = drm_edp_backlight_init(&intel_dp->aux, &panel->backlight.edp.vesa.info, panel->backlight.max = panel->backlight.edp.vesa.info.max;
panel->vbt.backlight.pwm_freq_hz, intel_dp->edp_dpcd, panel->backlight.min = 0;
&current_level, &current_mode); if (current_mode == DP_EDP_BACKLIGHT_CONTROL_MODE_DPCD) {
if (ret < 0) panel->backlight.level = current_level;
return ret; panel->backlight.enabled = panel->backlight.level != 0;
drm_dbg_kms(display->drm,
"[CONNECTOR:%d:%s] AUX VESA backlight enable is controlled through %s\n",
connector->base.base.id, connector->base.name,
dpcd_vs_pwm_str(panel->backlight.edp.vesa.info.aux_enable));
drm_dbg_kms(display->drm,
"[CONNECTOR:%d:%s] AUX VESA backlight level is controlled through %s\n",
connector->base.base.id, connector->base.name,
dpcd_vs_pwm_str(panel->backlight.edp.vesa.info.aux_set));
if (!panel->backlight.edp.vesa.info.aux_set ||
!panel->backlight.edp.vesa.info.aux_enable) {
ret = panel->backlight.pwm_funcs->setup(connector, pipe);
if (ret < 0) {
drm_err(display->drm,
"[CONNECTOR:%d:%s] Failed to setup PWM backlight controls for eDP backlight: %d\n",
connector->base.base.id, connector->base.name, ret);
return ret;
}
}
if (panel->backlight.edp.vesa.info.aux_set) {
panel->backlight.max = panel->backlight.edp.vesa.info.max;
panel->backlight.min = 0;
if (current_mode == DP_EDP_BACKLIGHT_CONTROL_MODE_DPCD) {
panel->backlight.level = current_level;
panel->backlight.enabled = panel->backlight.level != 0;
} else {
panel->backlight.level = panel->backlight.max;
panel->backlight.enabled = false;
}
} else { } else {
panel->backlight.max = panel->backlight.pwm_level_max; panel->backlight.level = panel->backlight.max;
panel->backlight.min = panel->backlight.pwm_level_min; panel->backlight.enabled = false;
if (current_mode == DP_EDP_BACKLIGHT_CONTROL_MODE_PWM) { }
panel->backlight.level = } else {
panel->backlight.pwm_funcs->get(connector, pipe); panel->backlight.max = panel->backlight.pwm_level_max;
panel->backlight.enabled = panel->backlight.pwm_enabled; panel->backlight.min = panel->backlight.pwm_level_min;
} else { if (current_mode == DP_EDP_BACKLIGHT_CONTROL_MODE_PWM) {
panel->backlight.level = panel->backlight.max; panel->backlight.level =
panel->backlight.enabled = false; panel->backlight.pwm_funcs->get(connector, pipe);
} panel->backlight.enabled = panel->backlight.pwm_enabled;
} else {
panel->backlight.level = panel->backlight.max;
panel->backlight.enabled = false;
} }
} }

View File

@ -1839,7 +1839,7 @@ nv50_sor_atomic_enable(struct drm_encoder *encoder, struct drm_atomic_state *sta
backlight = nv_connector->backlight; backlight = nv_connector->backlight;
if (backlight && backlight->uses_dpcd) if (backlight && backlight->uses_dpcd)
drm_edp_backlight_enable(&nv_connector->aux, &backlight->edp_info, drm_edp_backlight_enable(&nv_connector->aux, &backlight->edp_info,
(u16)backlight->dev->props.brightness); backlight->dev->props.brightness);
#endif #endif
break; break;

View File

@ -245,7 +245,7 @@ nv50_backlight_init(struct nouveau_backlight *bl,
if (nv_conn->type == DCB_CONNECTOR_eDP) { if (nv_conn->type == DCB_CONNECTOR_eDP) {
int ret; int ret;
u16 current_level; u32 current_level;
u8 edp_dpcd[EDP_DISPLAY_CTL_CAP_SIZE]; u8 edp_dpcd[EDP_DISPLAY_CTL_CAP_SIZE];
u8 current_mode; u8 current_mode;
@ -261,8 +261,9 @@ nv50_backlight_init(struct nouveau_backlight *bl,
NV_DEBUG(drm, "DPCD backlight controls supported on %s\n", NV_DEBUG(drm, "DPCD backlight controls supported on %s\n",
nv_conn->base.name); nv_conn->base.name);
ret = drm_edp_backlight_init(&nv_conn->aux, &bl->edp_info, 0, edp_dpcd, ret = drm_edp_backlight_init(&nv_conn->aux, &bl->edp_info,
&current_level, &current_mode); 0, 0, edp_dpcd,
&current_level, &current_mode, false);
if (ret < 0) if (ret < 0)
return ret; return ret;

View File

@ -193,6 +193,16 @@ config DRM_PANEL_HIMAX_HX83112A
Say Y here if you want to enable support for Himax HX83112A-based Say Y here if you want to enable support for Himax HX83112A-based
display panels, such as the one found in the Fairphone 4 smartphone. display panels, such as the one found in the Fairphone 4 smartphone.
config DRM_PANEL_HIMAX_HX83112B
tristate "Himax HX83112B-based DSI panel"
depends on OF
depends on DRM_MIPI_DSI
depends on BACKLIGHT_CLASS_DEVICE
select DRM_KMS_HELPER
help
Say Y here if you want to enable support for Himax HX83112B-based
display panels, such as the one found in the Fairphone 3 smartphone.
config DRM_PANEL_HIMAX_HX8394 config DRM_PANEL_HIMAX_HX8394
tristate "HIMAX HX8394 MIPI-DSI LCD panels" tristate "HIMAX HX8394 MIPI-DSI LCD panels"
depends on OF depends on OF

View File

@ -20,6 +20,7 @@ obj-$(CONFIG_DRM_PANEL_FEIYANG_FY07024DI26A30D) += panel-feiyang-fy07024di26a30d
obj-$(CONFIG_DRM_PANEL_HIMAX_HX8279) += panel-himax-hx8279.o obj-$(CONFIG_DRM_PANEL_HIMAX_HX8279) += panel-himax-hx8279.o
obj-$(CONFIG_DRM_PANEL_HIMAX_HX83102) += panel-himax-hx83102.o obj-$(CONFIG_DRM_PANEL_HIMAX_HX83102) += panel-himax-hx83102.o
obj-$(CONFIG_DRM_PANEL_HIMAX_HX83112A) += panel-himax-hx83112a.o obj-$(CONFIG_DRM_PANEL_HIMAX_HX83112A) += panel-himax-hx83112a.o
obj-$(CONFIG_DRM_PANEL_HIMAX_HX83112B) += panel-himax-hx83112b.o
obj-$(CONFIG_DRM_PANEL_HIMAX_HX8394) += panel-himax-hx8394.o obj-$(CONFIG_DRM_PANEL_HIMAX_HX8394) += panel-himax-hx8394.o
obj-$(CONFIG_DRM_PANEL_ILITEK_IL9322) += panel-ilitek-ili9322.o obj-$(CONFIG_DRM_PANEL_ILITEK_IL9322) += panel-ilitek-ili9322.o
obj-$(CONFIG_DRM_PANEL_ILITEK_ILI9341) += panel-ilitek-ili9341.o obj-$(CONFIG_DRM_PANEL_ILITEK_ILI9341) += panel-ilitek-ili9341.o

View File

@ -1967,6 +1967,7 @@ static const struct edp_panel_entry edp_panels[] = {
EDP_PANEL_ENTRY('C', 'M', 'N', 0x115e, &delay_200_500_e80_d50, "N116BCA-EA1"), EDP_PANEL_ENTRY('C', 'M', 'N', 0x115e, &delay_200_500_e80_d50, "N116BCA-EA1"),
EDP_PANEL_ENTRY('C', 'M', 'N', 0x1160, &delay_200_500_e80_d50, "N116BCJ-EAK"), EDP_PANEL_ENTRY('C', 'M', 'N', 0x1160, &delay_200_500_e80_d50, "N116BCJ-EAK"),
EDP_PANEL_ENTRY('C', 'M', 'N', 0x1161, &delay_200_500_e80, "N116BCP-EA2"), EDP_PANEL_ENTRY('C', 'M', 'N', 0x1161, &delay_200_500_e80, "N116BCP-EA2"),
EDP_PANEL_ENTRY('C', 'M', 'N', 0x1163, &delay_200_500_e80_d50, "N116BCJ-EAK"),
EDP_PANEL_ENTRY('C', 'M', 'N', 0x1247, &delay_200_500_e80_d50, "N120ACA-EA1"), EDP_PANEL_ENTRY('C', 'M', 'N', 0x1247, &delay_200_500_e80_d50, "N120ACA-EA1"),
EDP_PANEL_ENTRY('C', 'M', 'N', 0x142b, &delay_200_500_e80_d50, "N140HCA-EAC"), EDP_PANEL_ENTRY('C', 'M', 'N', 0x142b, &delay_200_500_e80_d50, "N140HCA-EAC"),
EDP_PANEL_ENTRY('C', 'M', 'N', 0x142e, &delay_200_500_e80_d50, "N140BGA-EA4"), EDP_PANEL_ENTRY('C', 'M', 'N', 0x142e, &delay_200_500_e80_d50, "N140BGA-EA4"),

View File

@ -0,0 +1,430 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Generated with linux-mdss-dsi-panel-driver-generator from vendor device tree.
* Copyright (c) 2025 Luca Weiss <luca@lucaweiss.eu>
*/
#include <linux/backlight.h>
#include <linux/delay.h>
#include <linux/gpio/consumer.h>
#include <linux/mod_devicetable.h>
#include <linux/module.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>
#include <drm/drm_probe_helper.h>
/* Manufacturer specific DSI commands */
#define HX83112B_SETPOWER1 0xb1
#define HX83112B_SETDISP 0xb2
#define HX83112B_SETDRV 0xb4
#define HX83112B_SETEXTC 0xb9
#define HX83112B_SETBANK 0xbd
#define HX83112B_SETDGCLUT 0xc1
#define HX83112B_SETDISMO 0xc2
#define HX83112B_UNKNOWN1 0xc6
#define HX83112B_SETPANEL 0xcc
#define HX83112B_UNKNOWN2 0xd1
#define HX83112B_SETPOWER2 0xd2
#define HX83112B_SETGIP0 0xd3
#define HX83112B_SETGIP1 0xd5
#define HX83112B_SETGIP2 0xd6
#define HX83112B_SETGIP3 0xd8
#define HX83112B_SETIDLE 0xdd
#define HX83112B_UNKNOWN3 0xe7
#define HX83112B_UNKNOWN4 0xe9
struct hx83112b_panel {
struct drm_panel panel;
struct mipi_dsi_device *dsi;
struct regulator_bulk_data *supplies;
struct gpio_desc *reset_gpio;
};
static const struct regulator_bulk_data hx83112b_supplies[] = {
{ .supply = "iovcc" },
{ .supply = "vsn" },
{ .supply = "vsp" },
};
static inline struct hx83112b_panel *to_hx83112b_panel(struct drm_panel *panel)
{
return container_of(panel, struct hx83112b_panel, panel);
}
static void hx83112b_reset(struct hx83112b_panel *ctx)
{
gpiod_set_value_cansleep(ctx->reset_gpio, 0);
usleep_range(10000, 11000);
gpiod_set_value_cansleep(ctx->reset_gpio, 1);
usleep_range(10000, 11000);
gpiod_set_value_cansleep(ctx->reset_gpio, 0);
usleep_range(10000, 11000);
}
static int hx83112b_on(struct hx83112b_panel *ctx)
{
struct mipi_dsi_multi_context dsi_ctx = { .dsi = ctx->dsi };
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, HX83112B_SETEXTC, 0x83, 0x11, 0x2b);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, HX83112B_SETBANK, 0x01);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, HX83112B_SETDISMO, 0x08, 0x70);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, HX83112B_SETBANK, 0x03);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, HX83112B_SETDISP, 0x04, 0x38, 0x08, 0x70);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, HX83112B_SETBANK, 0x00);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, HX83112B_SETPOWER1,
0xf8, 0x27, 0x27, 0x00, 0x00, 0x0b, 0x0e,
0x0b, 0x0e, 0x33);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, HX83112B_SETPOWER2, 0x2d, 0x2d);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, HX83112B_SETDISP,
0x80, 0x02, 0x18, 0x80, 0x70, 0x00, 0x08,
0x1c, 0x08, 0x11, 0x05);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, HX83112B_UNKNOWN4, 0xd1);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, HX83112B_SETDISP, 0x00, 0x08);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, HX83112B_UNKNOWN4, 0x00);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, HX83112B_SETBANK, 0x02);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, HX83112B_SETDISP, 0xb5, 0x0a);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, HX83112B_SETBANK, 0x00);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, HX83112B_SETIDLE,
0x00, 0x00, 0x08, 0x1c, 0x08, 0x34, 0x34,
0x88);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, HX83112B_SETDRV,
0x65, 0x6b, 0x00, 0x00, 0xd0, 0xd4, 0x36,
0xcf, 0x06, 0xce, 0x00, 0xce, 0x00, 0x00,
0x00, 0x07, 0x00, 0x2a, 0x07, 0x01, 0x07,
0x00, 0x00, 0x2a);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, HX83112B_SETBANK, 0x03);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, HX83112B_UNKNOWN4, 0xc3);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, HX83112B_SETDRV, 0x01, 0x67, 0x2a);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, HX83112B_UNKNOWN4, 0x00);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, HX83112B_SETBANK, 0x00);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, HX83112B_SETDGCLUT, 0x01);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, HX83112B_SETBANK, 0x01);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, HX83112B_SETDGCLUT,
0xff, 0xfb, 0xf9, 0xf6, 0xf4, 0xf1, 0xef,
0xea, 0xe7, 0xe5, 0xe2, 0xdf, 0xdd, 0xda,
0xd8, 0xd5, 0xd2, 0xcf, 0xcc, 0xc5, 0xbe,
0xb7, 0xb0, 0xa8, 0xa0, 0x98, 0x8e, 0x85,
0x7b, 0x72, 0x69, 0x5e, 0x53, 0x48, 0x3e,
0x35, 0x2b, 0x22, 0x17, 0x0d, 0x09, 0x07,
0x05, 0x01, 0x00, 0x26, 0xf0, 0x86, 0x25,
0x6e, 0xb6, 0xdd, 0xf3, 0xd8, 0xcc, 0x9b,
0x00);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, HX83112B_SETBANK, 0x02);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, HX83112B_SETDGCLUT,
0xff, 0xfb, 0xf9, 0xf6, 0xf4, 0xf1, 0xef,
0xea, 0xe7, 0xe5, 0xe2, 0xdf, 0xdd, 0xda,
0xd8, 0xd5, 0xd2, 0xcf, 0xcc, 0xc5, 0xbe,
0xb7, 0xb0, 0xa8, 0xa0, 0x98, 0x8e, 0x85,
0x7b, 0x72, 0x69, 0x5e, 0x53, 0x48, 0x3e,
0x35, 0x2b, 0x22, 0x17, 0x0d, 0x09, 0x07,
0x05, 0x01, 0x00, 0x26, 0xf0, 0x86, 0x25,
0x6e, 0xb6, 0xdd, 0xf3, 0xd8, 0xcc, 0x9b,
0x00);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, HX83112B_SETBANK, 0x03);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, HX83112B_SETDGCLUT,
0xff, 0xfb, 0xf9, 0xf6, 0xf4, 0xf1, 0xef,
0xea, 0xe7, 0xe5, 0xe2, 0xdf, 0xdd, 0xda,
0xd8, 0xd5, 0xd2, 0xcf, 0xcc, 0xc5, 0xbe,
0xb7, 0xb0, 0xa8, 0xa0, 0x98, 0x8e, 0x85,
0x7b, 0x72, 0x69, 0x5e, 0x53, 0x48, 0x3e,
0x35, 0x2b, 0x22, 0x17, 0x0d, 0x09, 0x07,
0x05, 0x01, 0x00, 0x26, 0xf0, 0x86, 0x25,
0x6e, 0xb6, 0xdd, 0xf3, 0xd8, 0xcc, 0x9b,
0x00);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, HX83112B_SETBANK, 0x00);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, HX83112B_SETDISMO, 0xc8);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, HX83112B_SETPANEL, 0x08);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, HX83112B_SETGIP0,
0x81, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00,
0x04, 0x00, 0x01, 0x13, 0x40, 0x04, 0x09,
0x09, 0x0b, 0x0b, 0x32, 0x10, 0x08, 0x00,
0x08, 0x32, 0x10, 0x08, 0x00, 0x08, 0x32,
0x10, 0x08, 0x00, 0x08, 0x00, 0x00, 0x0a,
0x08, 0x7b);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, HX83112B_UNKNOWN4, 0xc5);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, HX83112B_UNKNOWN1, 0xf7);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, HX83112B_UNKNOWN4, 0x00);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, HX83112B_UNKNOWN4, 0xd4);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, HX83112B_UNKNOWN1, 0x6e);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, HX83112B_UNKNOWN4, 0x00);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, HX83112B_UNKNOWN4, 0xef);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, HX83112B_SETGIP0, 0x0c);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, HX83112B_UNKNOWN4, 0x00);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, HX83112B_SETBANK, 0x01);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, HX83112B_UNKNOWN4, 0xc8);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, HX83112B_SETGIP0, 0xa1);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, HX83112B_UNKNOWN4, 0x00);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, HX83112B_SETBANK, 0x00);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, HX83112B_SETGIP1,
0x18, 0x18, 0x19, 0x18, 0x18, 0x20, 0x18,
0x18, 0x18, 0x10, 0x10, 0x18, 0x18, 0x00,
0x00, 0x18, 0x18, 0x01, 0x01, 0x18, 0x18,
0x28, 0x28, 0x18, 0x18, 0x18, 0x18, 0x18,
0x2f, 0x2f, 0x30, 0x30, 0x31, 0x31, 0x35,
0x35, 0x36, 0x36, 0x37, 0x37, 0x18, 0x18,
0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0xfc,
0xfc, 0x00, 0x00, 0xfc, 0xfc, 0x00, 0x00);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, HX83112B_SETGIP2,
0x18, 0x18, 0x19, 0x18, 0x18, 0x20, 0x19,
0x18, 0x18, 0x10, 0x10, 0x18, 0x18, 0x00,
0x00, 0x18, 0x18, 0x01, 0x01, 0x18, 0x18,
0x28, 0x28, 0x18, 0x18, 0x18, 0x18, 0x18,
0x2f, 0x2f, 0x30, 0x30, 0x31, 0x31, 0x35,
0x35, 0x36, 0x36, 0x37, 0x37, 0x18, 0x18,
0x18, 0x18, 0x18, 0x18, 0x18, 0x18);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, HX83112B_SETGIP3,
0xaa, 0xaa, 0xaa, 0xaf, 0xea, 0xaa, 0xaa,
0xaa, 0xaa, 0xaf, 0xea, 0xaa, 0xaa, 0xaa,
0xab, 0xaf, 0xef, 0xaa, 0xaa, 0xaa, 0xaa,
0xaf, 0xea, 0xaa);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, HX83112B_SETBANK, 0x01);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, HX83112B_SETGIP3,
0xaa, 0xaa, 0xab, 0xaf, 0xea, 0xaa, 0xaa,
0xaa, 0xae, 0xaf, 0xea, 0xaa);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, HX83112B_SETBANK, 0x02);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, HX83112B_SETGIP3,
0xaa, 0xaa, 0xaa, 0xaf, 0xea, 0xaa, 0xaa,
0xaa, 0xaa, 0xaf, 0xea, 0xaa);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, HX83112B_SETBANK, 0x03);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, HX83112B_SETGIP3,
0xba, 0xaa, 0xaa, 0xaf, 0xea, 0xaa, 0xaa,
0xaa, 0xaa, 0xaf, 0xea, 0xaa, 0xba, 0xaa,
0xaa, 0xaf, 0xea, 0xaa, 0xaa, 0xaa, 0xaa,
0xaf, 0xea, 0xaa);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, HX83112B_SETBANK, 0x00);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, HX83112B_UNKNOWN4, 0xe4);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, HX83112B_UNKNOWN3, 0x17, 0x69);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, HX83112B_UNKNOWN4, 0x00);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, HX83112B_UNKNOWN3,
0x09, 0x09, 0x00, 0x07, 0xe8, 0x00, 0x26,
0x00, 0x07, 0x00, 0x00, 0xe8, 0x32, 0x00,
0xe9, 0x0a, 0x0a, 0x00, 0x00, 0x00, 0x01,
0x01, 0x00, 0x12, 0x04);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, HX83112B_SETBANK, 0x01);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, HX83112B_UNKNOWN3,
0x02, 0x00, 0x01, 0x20, 0x01, 0x18, 0x08,
0xa8, 0x09);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, HX83112B_SETBANK, 0x02);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, HX83112B_UNKNOWN3, 0x20, 0x20, 0x00);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, HX83112B_SETBANK, 0x03);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, HX83112B_UNKNOWN3,
0x00, 0xdc, 0x11, 0x70, 0x00, 0x20);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, HX83112B_UNKNOWN4, 0xc9);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, HX83112B_UNKNOWN3,
0x2a, 0xce, 0x02, 0x70, 0x01, 0x04);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, HX83112B_UNKNOWN4, 0x00);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, HX83112B_SETBANK, 0x00);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, HX83112B_UNKNOWN2, 0x27);
mipi_dsi_dcs_exit_sleep_mode_multi(&dsi_ctx);
mipi_dsi_msleep(&dsi_ctx, 120);
mipi_dsi_dcs_set_display_on_multi(&dsi_ctx);
mipi_dsi_msleep(&dsi_ctx, 20);
mipi_dsi_dcs_set_display_brightness_multi(&dsi_ctx, 0x0000);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, MIPI_DCS_WRITE_CONTROL_DISPLAY,
0x24);
mipi_dsi_dcs_set_tear_on_multi(&dsi_ctx, MIPI_DSI_DCS_TEAR_MODE_VBLANK);
return dsi_ctx.accum_err;
}
static int hx83112b_off(struct hx83112b_panel *ctx)
{
struct mipi_dsi_multi_context dsi_ctx = { .dsi = ctx->dsi };
mipi_dsi_dcs_set_display_off_multi(&dsi_ctx);
mipi_dsi_msleep(&dsi_ctx, 20);
mipi_dsi_dcs_enter_sleep_mode_multi(&dsi_ctx);
mipi_dsi_msleep(&dsi_ctx, 120);
return dsi_ctx.accum_err;
}
static int hx83112b_prepare(struct drm_panel *panel)
{
struct hx83112b_panel *ctx = to_hx83112b_panel(panel);
struct device *dev = &ctx->dsi->dev;
int ret;
ret = regulator_bulk_enable(ARRAY_SIZE(hx83112b_supplies), ctx->supplies);
if (ret < 0) {
dev_err(dev, "Failed to enable regulators: %d\n", ret);
return ret;
}
hx83112b_reset(ctx);
ret = hx83112b_on(ctx);
if (ret < 0) {
dev_err(dev, "Failed to initialize panel: %d\n", ret);
gpiod_set_value_cansleep(ctx->reset_gpio, 1);
regulator_bulk_disable(ARRAY_SIZE(hx83112b_supplies), ctx->supplies);
return ret;
}
return 0;
}
static int hx83112b_unprepare(struct drm_panel *panel)
{
struct hx83112b_panel *ctx = to_hx83112b_panel(panel);
struct device *dev = &ctx->dsi->dev;
int ret;
ret = hx83112b_off(ctx);
if (ret < 0)
dev_err(dev, "Failed to un-initialize panel: %d\n", ret);
gpiod_set_value_cansleep(ctx->reset_gpio, 1);
regulator_bulk_disable(ARRAY_SIZE(hx83112b_supplies), ctx->supplies);
return 0;
}
static const struct drm_display_mode hx83112b_mode = {
.clock = (1080 + 40 + 4 + 12) * (2160 + 32 + 2 + 2) * 60 / 1000,
.hdisplay = 1080,
.hsync_start = 1080 + 40,
.hsync_end = 1080 + 40 + 4,
.htotal = 1080 + 40 + 4 + 12,
.vdisplay = 2160,
.vsync_start = 2160 + 32,
.vsync_end = 2160 + 32 + 2,
.vtotal = 2160 + 32 + 2 + 2,
.width_mm = 65,
.height_mm = 128,
.type = DRM_MODE_TYPE_DRIVER,
};
static int hx83112b_get_modes(struct drm_panel *panel,
struct drm_connector *connector)
{
return drm_connector_helper_get_modes_fixed(connector, &hx83112b_mode);
}
static const struct drm_panel_funcs hx83112b_panel_funcs = {
.prepare = hx83112b_prepare,
.unprepare = hx83112b_unprepare,
.get_modes = hx83112b_get_modes,
};
static int hx83112b_bl_update_status(struct backlight_device *bl)
{
struct mipi_dsi_device *dsi = bl_get_data(bl);
u16 brightness = backlight_get_brightness(bl);
int ret;
dsi->mode_flags &= ~MIPI_DSI_MODE_LPM;
ret = mipi_dsi_dcs_set_display_brightness_large(dsi, brightness);
if (ret < 0)
return ret;
dsi->mode_flags |= MIPI_DSI_MODE_LPM;
return 0;
}
static const struct backlight_ops hx83112b_bl_ops = {
.update_status = hx83112b_bl_update_status,
};
static struct backlight_device *
hx83112b_create_backlight(struct mipi_dsi_device *dsi)
{
struct device *dev = &dsi->dev;
const struct backlight_properties props = {
.type = BACKLIGHT_RAW,
.brightness = 4095,
.max_brightness = 4095,
};
return devm_backlight_device_register(dev, dev_name(dev), dev, dsi,
&hx83112b_bl_ops, &props);
}
static int hx83112b_probe(struct mipi_dsi_device *dsi)
{
struct device *dev = &dsi->dev;
struct hx83112b_panel *ctx;
int ret;
ctx = devm_drm_panel_alloc(dev, struct hx83112b_panel, panel,
&hx83112b_panel_funcs,
DRM_MODE_CONNECTOR_DSI);
if (IS_ERR(ctx))
return PTR_ERR(ctx);
ret = devm_regulator_bulk_get_const(dev,
ARRAY_SIZE(hx83112b_supplies),
hx83112b_supplies,
&ctx->supplies);
if (ret < 0)
return ret;
ctx->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_HIGH);
if (IS_ERR(ctx->reset_gpio))
return dev_err_probe(dev, PTR_ERR(ctx->reset_gpio),
"Failed to get reset-gpios\n");
ctx->dsi = dsi;
mipi_dsi_set_drvdata(dsi, ctx);
dsi->lanes = 4;
dsi->format = MIPI_DSI_FMT_RGB888;
dsi->mode_flags = MIPI_DSI_MODE_VIDEO_BURST |
MIPI_DSI_CLOCK_NON_CONTINUOUS |
MIPI_DSI_MODE_VIDEO_NO_HSA | MIPI_DSI_MODE_LPM;
ctx->panel.prepare_prev_first = true;
ctx->panel.backlight = hx83112b_create_backlight(dsi);
if (IS_ERR(ctx->panel.backlight))
return dev_err_probe(dev, PTR_ERR(ctx->panel.backlight),
"Failed to create backlight\n");
drm_panel_add(&ctx->panel);
ret = mipi_dsi_attach(dsi);
if (ret < 0) {
drm_panel_remove(&ctx->panel);
return dev_err_probe(dev, ret, "Failed to attach to DSI host\n");
}
return 0;
}
static void hx83112b_remove(struct mipi_dsi_device *dsi)
{
struct hx83112b_panel *ctx = mipi_dsi_get_drvdata(dsi);
int ret;
ret = mipi_dsi_detach(dsi);
if (ret < 0)
dev_err(&dsi->dev, "Failed to detach from DSI host: %d\n", ret);
drm_panel_remove(&ctx->panel);
}
static const struct of_device_id hx83112b_of_match[] = {
{ .compatible = "djn,98-03057-6598b-i" },
{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(of, hx83112b_of_match);
static struct mipi_dsi_driver hx83112b_driver = {
.probe = hx83112b_probe,
.remove = hx83112b_remove,
.driver = {
.name = "panel-himax-hx83112b",
.of_match_table = hx83112b_of_match,
},
};
module_mipi_dsi_driver(hx83112b_driver);
MODULE_DESCRIPTION("DRM driver for hx83112b-equipped DSI panels");
MODULE_LICENSE("GPL");

View File

@ -36,12 +36,14 @@ static inline struct raydium_rm67200 *to_raydium_rm67200(struct drm_panel *panel
static void raydium_rm67200_reset(struct raydium_rm67200 *ctx) static void raydium_rm67200_reset(struct raydium_rm67200 *ctx)
{ {
gpiod_set_value_cansleep(ctx->reset_gpio, 0); if (ctx->reset_gpio) {
msleep(60); gpiod_set_value_cansleep(ctx->reset_gpio, 0);
gpiod_set_value_cansleep(ctx->reset_gpio, 1); msleep(60);
msleep(60); gpiod_set_value_cansleep(ctx->reset_gpio, 1);
gpiod_set_value_cansleep(ctx->reset_gpio, 0); msleep(60);
msleep(60); gpiod_set_value_cansleep(ctx->reset_gpio, 0);
msleep(60);
}
} }
static void raydium_rm67200_write(struct mipi_dsi_multi_context *ctx, static void raydium_rm67200_write(struct mipi_dsi_multi_context *ctx,
@ -318,6 +320,7 @@ static void w552793baa_setup(struct mipi_dsi_multi_context *ctx)
static int raydium_rm67200_prepare(struct drm_panel *panel) static int raydium_rm67200_prepare(struct drm_panel *panel)
{ {
struct raydium_rm67200 *ctx = to_raydium_rm67200(panel); struct raydium_rm67200 *ctx = to_raydium_rm67200(panel);
struct mipi_dsi_multi_context mctx = { .dsi = ctx->dsi };
int ret; int ret;
ret = regulator_bulk_enable(ctx->num_supplies, ctx->supplies); ret = regulator_bulk_enable(ctx->num_supplies, ctx->supplies);
@ -328,6 +331,12 @@ static int raydium_rm67200_prepare(struct drm_panel *panel)
msleep(60); msleep(60);
ctx->panel_info->panel_setup(&mctx);
mipi_dsi_dcs_exit_sleep_mode_multi(&mctx);
mipi_dsi_msleep(&mctx, 120);
mipi_dsi_dcs_set_display_on_multi(&mctx);
mipi_dsi_msleep(&mctx, 30);
return 0; return 0;
} }
@ -343,20 +352,6 @@ static int raydium_rm67200_unprepare(struct drm_panel *panel)
return 0; return 0;
} }
static int raydium_rm67200_enable(struct drm_panel *panel)
{
struct raydium_rm67200 *rm67200 = to_raydium_rm67200(panel);
struct mipi_dsi_multi_context ctx = { .dsi = rm67200->dsi };
rm67200->panel_info->panel_setup(&ctx);
mipi_dsi_dcs_exit_sleep_mode_multi(&ctx);
mipi_dsi_msleep(&ctx, 120);
mipi_dsi_dcs_set_display_on_multi(&ctx);
mipi_dsi_msleep(&ctx, 30);
return ctx.accum_err;
}
static int raydium_rm67200_disable(struct drm_panel *panel) static int raydium_rm67200_disable(struct drm_panel *panel)
{ {
struct raydium_rm67200 *rm67200 = to_raydium_rm67200(panel); struct raydium_rm67200 *rm67200 = to_raydium_rm67200(panel);
@ -381,7 +376,6 @@ static const struct drm_panel_funcs raydium_rm67200_funcs = {
.prepare = raydium_rm67200_prepare, .prepare = raydium_rm67200_prepare,
.unprepare = raydium_rm67200_unprepare, .unprepare = raydium_rm67200_unprepare,
.get_modes = raydium_rm67200_get_modes, .get_modes = raydium_rm67200_get_modes,
.enable = raydium_rm67200_enable,
.disable = raydium_rm67200_disable, .disable = raydium_rm67200_disable,
}; };
@ -409,7 +403,7 @@ static int raydium_rm67200_probe(struct mipi_dsi_device *dsi)
if (ret < 0) if (ret < 0)
return ret; return ret;
ctx->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_LOW); ctx->reset_gpio = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_LOW);
if (IS_ERR(ctx->reset_gpio)) if (IS_ERR(ctx->reset_gpio))
return dev_err_probe(dev, PTR_ERR(ctx->reset_gpio), return dev_err_probe(dev, PTR_ERR(ctx->reset_gpio),
"Failed to get reset-gpios\n"); "Failed to get reset-gpios\n");
@ -470,6 +464,7 @@ static const struct raydium_rm67200_panel_info w552793baa_info = {
.vtotal = 1952, .vtotal = 1952,
.width_mm = 68, /* 68.04mm */ .width_mm = 68, /* 68.04mm */
.height_mm = 121, /* 120.96mm */ .height_mm = 121, /* 120.96mm */
.flags = DRM_MODE_FLAG_NVSYNC | DRM_MODE_FLAG_NHSYNC,
.type = DRM_MODE_TYPE_DRIVER, .type = DRM_MODE_TYPE_DRIVER,
}, },
.regulators = w552793baa_regulators, .regulators = w552793baa_regulators,

View File

@ -244,7 +244,7 @@ static const struct s6d7aa0_panel_desc s6d7aa0_lsl080al02_desc = {
.init_func = s6d7aa0_lsl080al02_init, .init_func = s6d7aa0_lsl080al02_init,
.off_func = s6d7aa0_lsl080al02_off, .off_func = s6d7aa0_lsl080al02_off,
.drm_mode = &s6d7aa0_lsl080al02_mode, .drm_mode = &s6d7aa0_lsl080al02_mode,
.mode_flags = MIPI_DSI_MODE_VSYNC_FLUSH | MIPI_DSI_MODE_VIDEO_NO_HFP, .mode_flags = MIPI_DSI_MODE_VIDEO_NO_HFP,
.bus_flags = 0, .bus_flags = 0,
.has_backlight = false, .has_backlight = false,

View File

@ -992,7 +992,7 @@ static int s6e8aa0_probe(struct mipi_dsi_device *dsi)
dsi->lanes = 4; dsi->lanes = 4;
dsi->format = MIPI_DSI_FMT_RGB888; dsi->format = MIPI_DSI_FMT_RGB888;
dsi->mode_flags = MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_VIDEO_BURST dsi->mode_flags = MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_VIDEO_BURST
| MIPI_DSI_MODE_VSYNC_FLUSH | MIPI_DSI_MODE_VIDEO_AUTO_VERT; | MIPI_DSI_MODE_VIDEO_AUTO_VERT;
ret = s6e8aa0_parse_dt(ctx); ret = s6e8aa0_parse_dt(ctx);
if (ret < 0) if (ret < 0)

View File

@ -297,8 +297,9 @@ int panthor_gpu_block_power_on(struct panthor_device *ptdev,
gpu_write64(ptdev, pwron_reg, mask); gpu_write64(ptdev, pwron_reg, mask);
ret = gpu_read64_relaxed_poll_timeout(ptdev, pwrtrans_reg, val, ret = gpu_read64_relaxed_poll_timeout(ptdev, rdy_reg, val,
!(mask & val), 100, timeout_us); (mask & val) == val,
100, timeout_us);
if (ret) { if (ret) {
drm_err(&ptdev->base, "timeout waiting on %s:%llx readiness", drm_err(&ptdev->base, "timeout waiting on %s:%llx readiness",
blk_name, mask); blk_name, mask);

View File

@ -200,12 +200,36 @@ static struct dma_fence *mock_sched_run_job(struct drm_sched_job *sched_job)
return &job->hw_fence; return &job->hw_fence;
} }
/*
* Normally, drivers would take appropriate measures in this callback, such as
* killing the entity the faulty job is associated with, resetting the hardware
* and / or resubmitting non-faulty jobs.
*
* For the mock scheduler, there are no hardware rings to be resetted nor jobs
* to be resubmitted. Thus, this function merely ensures that
* a) timedout fences get signaled properly and removed from the pending list
* b) the mock scheduler framework gets informed about the timeout via a flag
* c) The drm_sched_job, not longer needed, gets freed
*/
static enum drm_gpu_sched_stat static enum drm_gpu_sched_stat
mock_sched_timedout_job(struct drm_sched_job *sched_job) mock_sched_timedout_job(struct drm_sched_job *sched_job)
{ {
struct drm_mock_scheduler *sched = drm_sched_to_mock_sched(sched_job->sched);
struct drm_mock_sched_job *job = drm_sched_job_to_mock_job(sched_job); struct drm_mock_sched_job *job = drm_sched_job_to_mock_job(sched_job);
unsigned long flags;
job->flags |= DRM_MOCK_SCHED_JOB_TIMEDOUT; spin_lock_irqsave(&sched->lock, flags);
if (!dma_fence_is_signaled_locked(&job->hw_fence)) {
list_del(&job->link);
job->flags |= DRM_MOCK_SCHED_JOB_TIMEDOUT;
dma_fence_set_error(&job->hw_fence, -ETIMEDOUT);
dma_fence_signal_locked(&job->hw_fence);
}
spin_unlock_irqrestore(&sched->lock, flags);
dma_fence_put(&job->hw_fence);
drm_sched_job_cleanup(sched_job);
/* Mock job itself is freed by the kunit framework. */
return DRM_GPU_SCHED_STAT_NOMINAL; return DRM_GPU_SCHED_STAT_NOMINAL;
} }

View File

@ -735,13 +735,13 @@ static void drm_test_fb_xrgb8888_to_rgb565(struct kunit *test)
NULL : &result->dst_pitch; NULL : &result->dst_pitch;
drm_fb_xrgb8888_to_rgb565(&dst, dst_pitch, &src, &fb, &params->clip, drm_fb_xrgb8888_to_rgb565(&dst, dst_pitch, &src, &fb, &params->clip,
&fmtcnv_state, false); &fmtcnv_state);
buf = le16buf_to_cpu(test, (__force const __le16 *)buf, dst_size / sizeof(__le16)); buf = le16buf_to_cpu(test, (__force const __le16 *)buf, dst_size / sizeof(__le16));
KUNIT_EXPECT_MEMEQ(test, buf, result->expected, dst_size); KUNIT_EXPECT_MEMEQ(test, buf, result->expected, dst_size);
buf = dst.vaddr; /* restore original value of buf */ buf = dst.vaddr; /* restore original value of buf */
drm_fb_xrgb8888_to_rgb565(&dst, &result->dst_pitch, &src, &fb, &params->clip, drm_fb_xrgb8888_to_rgb565be(&dst, &result->dst_pitch, &src, &fb, &params->clip,
&fmtcnv_state, true); &fmtcnv_state);
buf = le16buf_to_cpu(test, (__force const __le16 *)buf, dst_size / sizeof(__le16)); buf = le16buf_to_cpu(test, (__force const __le16 *)buf, dst_size / sizeof(__le16));
KUNIT_EXPECT_MEMEQ(test, buf, result->expected_swab, dst_size); KUNIT_EXPECT_MEMEQ(test, buf, result->expected_swab, dst_size);
@ -749,7 +749,7 @@ static void drm_test_fb_xrgb8888_to_rgb565(struct kunit *test)
memset(buf, 0, dst_size); memset(buf, 0, dst_size);
drm_fb_xrgb8888_to_rgb565(&dst, dst_pitch, &src, &fb, &params->clip, drm_fb_xrgb8888_to_rgb565(&dst, dst_pitch, &src, &fb, &params->clip,
&fmtcnv_state, false); &fmtcnv_state);
buf = le16buf_to_cpu(test, (__force const __le16 *)buf, dst_size / sizeof(__le16)); buf = le16buf_to_cpu(test, (__force const __le16 *)buf, dst_size / sizeof(__le16));
KUNIT_EXPECT_MEMEQ(test, buf, result->expected, dst_size); KUNIT_EXPECT_MEMEQ(test, buf, result->expected, dst_size);
} }

View File

@ -7,6 +7,7 @@ tidss-y := tidss_crtc.o \
tidss_irq.o \ tidss_irq.o \
tidss_plane.o \ tidss_plane.o \
tidss_scale_coefs.o \ tidss_scale_coefs.o \
tidss_dispc.o tidss_dispc.o \
tidss_oldi.o
obj-$(CONFIG_DRM_TIDSS) += tidss.o obj-$(CONFIG_DRM_TIDSS) += tidss.o

View File

@ -146,7 +146,7 @@ static const u16 tidss_am65x_common_regs[DISPC_COMMON_REG_TABLE_LEN] = {
const struct dispc_features dispc_am65x_feats = { const struct dispc_features dispc_am65x_feats = {
.max_pclk_khz = { .max_pclk_khz = {
[DISPC_VP_DPI] = 165000, [DISPC_VP_DPI] = 165000,
[DISPC_VP_OLDI] = 165000, [DISPC_VP_OLDI_AM65X] = 165000,
}, },
.scaling = { .scaling = {
@ -176,7 +176,7 @@ const struct dispc_features dispc_am65x_feats = {
.vp_name = { "vp1", "vp2" }, .vp_name = { "vp1", "vp2" },
.ovr_name = { "ovr1", "ovr2" }, .ovr_name = { "ovr1", "ovr2" },
.vpclk_name = { "vp1", "vp2" }, .vpclk_name = { "vp1", "vp2" },
.vp_bus_type = { DISPC_VP_OLDI, DISPC_VP_DPI }, .vp_bus_type = { DISPC_VP_OLDI_AM65X, DISPC_VP_DPI },
.vp_feat = { .color = { .vp_feat = { .color = {
.has_ctm = true, .has_ctm = true,
@ -491,7 +491,7 @@ struct dispc_device {
void __iomem *base_ovr[TIDSS_MAX_PORTS]; void __iomem *base_ovr[TIDSS_MAX_PORTS];
void __iomem *base_vp[TIDSS_MAX_PORTS]; void __iomem *base_vp[TIDSS_MAX_PORTS];
struct regmap *oldi_io_ctrl; struct regmap *am65x_oldi_io_ctrl;
struct clk *vp_clk[TIDSS_MAX_PORTS]; struct clk *vp_clk[TIDSS_MAX_PORTS];
@ -566,6 +566,29 @@ static u32 dispc_vp_read(struct dispc_device *dispc, u32 hw_videoport, u16 reg)
return ioread32(base + reg); return ioread32(base + reg);
} }
int tidss_configure_oldi(struct tidss_device *tidss, u32 hw_videoport,
u32 oldi_cfg)
{
u32 count = 0;
u32 oldi_reset_bit = BIT(5 + hw_videoport);
dispc_vp_write(tidss->dispc, hw_videoport, DISPC_VP_DSS_OLDI_CFG, oldi_cfg);
while (!(oldi_reset_bit & dispc_read(tidss->dispc, DSS_SYSSTATUS)) &&
count < 10000)
count++;
if (!(oldi_reset_bit & dispc_read(tidss->dispc, DSS_SYSSTATUS)))
return -ETIMEDOUT;
return 0;
}
void tidss_disable_oldi(struct tidss_device *tidss, u32 hw_videoport)
{
dispc_vp_write(tidss->dispc, hw_videoport, DISPC_VP_DSS_OLDI_CFG, 0);
}
/* /*
* TRM gives bitfields as start:end, where start is the higher bit * TRM gives bitfields as start:end, where start is the higher bit
* number. For example 7:0 * number. For example 7:0
@ -1016,13 +1039,11 @@ void dispc_set_irqenable(struct dispc_device *dispc, dispc_irq_t mask)
} }
} }
enum dispc_oldi_mode_reg_val { SPWG_18 = 0, JEIDA_24 = 1, SPWG_24 = 2 };
struct dispc_bus_format { struct dispc_bus_format {
u32 bus_fmt; u32 bus_fmt;
u32 data_width; u32 data_width;
bool is_oldi_fmt; bool is_oldi_fmt;
enum dispc_oldi_mode_reg_val oldi_mode_reg_val; enum oldi_mode_reg_val am65x_oldi_mode_reg_val;
}; };
static const struct dispc_bus_format dispc_bus_formats[] = { static const struct dispc_bus_format dispc_bus_formats[] = {
@ -1066,7 +1087,7 @@ int dispc_vp_bus_check(struct dispc_device *dispc, u32 hw_videoport,
return -EINVAL; return -EINVAL;
} }
if (dispc->feat->vp_bus_type[hw_videoport] != DISPC_VP_OLDI && if (dispc->feat->vp_bus_type[hw_videoport] != DISPC_VP_OLDI_AM65X &&
fmt->is_oldi_fmt) { fmt->is_oldi_fmt) {
dev_dbg(dispc->dev, "%s: %s is not OLDI-port\n", dev_dbg(dispc->dev, "%s: %s is not OLDI-port\n",
__func__, dispc->feat->vp_name[hw_videoport]); __func__, dispc->feat->vp_name[hw_videoport]);
@ -1076,23 +1097,23 @@ int dispc_vp_bus_check(struct dispc_device *dispc, u32 hw_videoport,
return 0; return 0;
} }
static void dispc_oldi_tx_power(struct dispc_device *dispc, bool power) static void dispc_am65x_oldi_tx_power(struct dispc_device *dispc, bool power)
{ {
u32 val = power ? 0 : OLDI_PWRDN_TX; u32 val = power ? 0 : AM65X_OLDI_PWRDN_TX;
if (WARN_ON(!dispc->oldi_io_ctrl)) if (WARN_ON(!dispc->am65x_oldi_io_ctrl))
return; return;
regmap_update_bits(dispc->oldi_io_ctrl, OLDI_DAT0_IO_CTRL, regmap_update_bits(dispc->am65x_oldi_io_ctrl, AM65X_OLDI_DAT0_IO_CTRL,
OLDI_PWRDN_TX, val); AM65X_OLDI_PWRDN_TX, val);
regmap_update_bits(dispc->oldi_io_ctrl, OLDI_DAT1_IO_CTRL, regmap_update_bits(dispc->am65x_oldi_io_ctrl, AM65X_OLDI_DAT1_IO_CTRL,
OLDI_PWRDN_TX, val); AM65X_OLDI_PWRDN_TX, val);
regmap_update_bits(dispc->oldi_io_ctrl, OLDI_DAT2_IO_CTRL, regmap_update_bits(dispc->am65x_oldi_io_ctrl, AM65X_OLDI_DAT2_IO_CTRL,
OLDI_PWRDN_TX, val); AM65X_OLDI_PWRDN_TX, val);
regmap_update_bits(dispc->oldi_io_ctrl, OLDI_DAT3_IO_CTRL, regmap_update_bits(dispc->am65x_oldi_io_ctrl, AM65X_OLDI_DAT3_IO_CTRL,
OLDI_PWRDN_TX, val); AM65X_OLDI_PWRDN_TX, val);
regmap_update_bits(dispc->oldi_io_ctrl, OLDI_CLK_IO_CTRL, regmap_update_bits(dispc->am65x_oldi_io_ctrl, AM65X_OLDI_CLK_IO_CTRL,
OLDI_PWRDN_TX, val); AM65X_OLDI_PWRDN_TX, val);
} }
static void dispc_set_num_datalines(struct dispc_device *dispc, static void dispc_set_num_datalines(struct dispc_device *dispc,
@ -1121,8 +1142,8 @@ static void dispc_set_num_datalines(struct dispc_device *dispc,
VP_REG_FLD_MOD(dispc, hw_videoport, DISPC_VP_CONTROL, v, 10, 8); VP_REG_FLD_MOD(dispc, hw_videoport, DISPC_VP_CONTROL, v, 10, 8);
} }
static void dispc_enable_oldi(struct dispc_device *dispc, u32 hw_videoport, static void dispc_enable_am65x_oldi(struct dispc_device *dispc, u32 hw_videoport,
const struct dispc_bus_format *fmt) const struct dispc_bus_format *fmt)
{ {
u32 oldi_cfg = 0; u32 oldi_cfg = 0;
u32 oldi_reset_bit = BIT(5 + hw_videoport); u32 oldi_reset_bit = BIT(5 + hw_videoport);
@ -1141,7 +1162,7 @@ static void dispc_enable_oldi(struct dispc_device *dispc, u32 hw_videoport,
oldi_cfg |= BIT(7); /* DEPOL */ oldi_cfg |= BIT(7); /* DEPOL */
oldi_cfg = FLD_MOD(oldi_cfg, fmt->oldi_mode_reg_val, 3, 1); oldi_cfg = FLD_MOD(oldi_cfg, fmt->am65x_oldi_mode_reg_val, 3, 1);
oldi_cfg |= BIT(12); /* SOFTRST */ oldi_cfg |= BIT(12); /* SOFTRST */
@ -1170,10 +1191,10 @@ void dispc_vp_prepare(struct dispc_device *dispc, u32 hw_videoport,
if (WARN_ON(!fmt)) if (WARN_ON(!fmt))
return; return;
if (dispc->feat->vp_bus_type[hw_videoport] == DISPC_VP_OLDI) { if (dispc->feat->vp_bus_type[hw_videoport] == DISPC_VP_OLDI_AM65X) {
dispc_oldi_tx_power(dispc, true); dispc_am65x_oldi_tx_power(dispc, true);
dispc_enable_oldi(dispc, hw_videoport, fmt); dispc_enable_am65x_oldi(dispc, hw_videoport, fmt);
} }
} }
@ -1229,7 +1250,7 @@ void dispc_vp_enable(struct dispc_device *dispc, u32 hw_videoport,
align = true; align = true;
/* always use DE_HIGH for OLDI */ /* always use DE_HIGH for OLDI */
if (dispc->feat->vp_bus_type[hw_videoport] == DISPC_VP_OLDI) if (dispc->feat->vp_bus_type[hw_videoport] == DISPC_VP_OLDI_AM65X)
ieo = false; ieo = false;
dispc_vp_write(dispc, hw_videoport, DISPC_VP_POL_FREQ, dispc_vp_write(dispc, hw_videoport, DISPC_VP_POL_FREQ,
@ -1255,10 +1276,10 @@ void dispc_vp_disable(struct dispc_device *dispc, u32 hw_videoport)
void dispc_vp_unprepare(struct dispc_device *dispc, u32 hw_videoport) void dispc_vp_unprepare(struct dispc_device *dispc, u32 hw_videoport)
{ {
if (dispc->feat->vp_bus_type[hw_videoport] == DISPC_VP_OLDI) { if (dispc->feat->vp_bus_type[hw_videoport] == DISPC_VP_OLDI_AM65X) {
dispc_vp_write(dispc, hw_videoport, DISPC_VP_DSS_OLDI_CFG, 0); dispc_vp_write(dispc, hw_videoport, DISPC_VP_DSS_OLDI_CFG, 0);
dispc_oldi_tx_power(dispc, false); dispc_am65x_oldi_tx_power(dispc, false);
} }
} }
@ -1420,7 +1441,6 @@ void dispc_vp_disable_clk(struct dispc_device *dispc, u32 hw_videoport)
* Calculate the percentage difference between the requested pixel clock rate * Calculate the percentage difference between the requested pixel clock rate
* and the effective rate resulting from calculating the clock divider value. * and the effective rate resulting from calculating the clock divider value.
*/ */
static
unsigned int dispc_pclk_diff(unsigned long rate, unsigned long real_rate) unsigned int dispc_pclk_diff(unsigned long rate, unsigned long real_rate)
{ {
int r = rate / 100, rr = real_rate / 100; int r = rate / 100, rr = real_rate / 100;
@ -2852,15 +2872,15 @@ static int dispc_iomap_resource(struct platform_device *pdev, const char *name,
static int dispc_init_am65x_oldi_io_ctrl(struct device *dev, static int dispc_init_am65x_oldi_io_ctrl(struct device *dev,
struct dispc_device *dispc) struct dispc_device *dispc)
{ {
dispc->oldi_io_ctrl = dispc->am65x_oldi_io_ctrl =
syscon_regmap_lookup_by_phandle(dev->of_node, syscon_regmap_lookup_by_phandle(dev->of_node,
"ti,am65x-oldi-io-ctrl"); "ti,am65x-oldi-io-ctrl");
if (PTR_ERR(dispc->oldi_io_ctrl) == -ENODEV) { if (PTR_ERR(dispc->am65x_oldi_io_ctrl) == -ENODEV) {
dispc->oldi_io_ctrl = NULL; dispc->am65x_oldi_io_ctrl = NULL;
} else if (IS_ERR(dispc->oldi_io_ctrl)) { } else if (IS_ERR(dispc->am65x_oldi_io_ctrl)) {
dev_err(dev, "%s: syscon_regmap_lookup_by_phandle failed %ld\n", dev_err(dev, "%s: syscon_regmap_lookup_by_phandle failed %ld\n",
__func__, PTR_ERR(dispc->oldi_io_ctrl)); __func__, PTR_ERR(dispc->am65x_oldi_io_ctrl));
return PTR_ERR(dispc->oldi_io_ctrl); return PTR_ERR(dispc->am65x_oldi_io_ctrl);
} }
return 0; return 0;
} }

View File

@ -58,7 +58,7 @@ struct dispc_errata {
enum dispc_vp_bus_type { enum dispc_vp_bus_type {
DISPC_VP_DPI, /* DPI output */ DISPC_VP_DPI, /* DPI output */
DISPC_VP_OLDI, /* OLDI (LVDS) output */ DISPC_VP_OLDI_AM65X, /* OLDI (LVDS) output for AM65x DSS */
DISPC_VP_INTERNAL, /* SoC internal routing */ DISPC_VP_INTERNAL, /* SoC internal routing */
DISPC_VP_TIED_OFF, /* Tied off / Unavailable */ DISPC_VP_TIED_OFF, /* Tied off / Unavailable */
DISPC_VP_MAX_BUS_TYPE, DISPC_VP_MAX_BUS_TYPE,
@ -101,6 +101,11 @@ extern const struct dispc_features dispc_am62l_feats;
extern const struct dispc_features dispc_am65x_feats; extern const struct dispc_features dispc_am65x_feats;
extern const struct dispc_features dispc_j721e_feats; extern const struct dispc_features dispc_j721e_feats;
int tidss_configure_oldi(struct tidss_device *tidss, u32 hw_videoport,
u32 oldi_cfg);
void tidss_disable_oldi(struct tidss_device *tidss, u32 hw_videoport);
unsigned int dispc_pclk_diff(unsigned long rate, unsigned long real_rate);
void dispc_set_irqenable(struct dispc_device *dispc, dispc_irq_t mask); void dispc_set_irqenable(struct dispc_device *dispc, dispc_irq_t mask);
dispc_irq_t dispc_read_and_clear_irqstatus(struct dispc_device *dispc); dispc_irq_t dispc_read_and_clear_irqstatus(struct dispc_device *dispc);

View File

@ -226,18 +226,35 @@ enum dispc_common_regs {
#define DISPC_VP_DSS_DMA_THREADSIZE 0x170 /* J721E */ #define DISPC_VP_DSS_DMA_THREADSIZE 0x170 /* J721E */
#define DISPC_VP_DSS_DMA_THREADSIZE_STATUS 0x174 /* J721E */ #define DISPC_VP_DSS_DMA_THREADSIZE_STATUS 0x174 /* J721E */
/* OLDI Config Bits (DISPC_VP_DSS_OLDI_CFG) */
#define OLDI_ENABLE BIT(0)
#define OLDI_MAP (BIT(1) | BIT(2) | BIT(3))
#define OLDI_SRC BIT(4)
#define OLDI_CLONE_MODE BIT(5)
#define OLDI_MASTERSLAVE BIT(6)
#define OLDI_DEPOL BIT(7)
#define OLDI_MSB BIT(8)
#define OLDI_LBEN BIT(9)
#define OLDI_LBDATA BIT(10)
#define OLDI_DUALMODESYNC BIT(11)
#define OLDI_SOFTRST BIT(12)
#define OLDI_TPATCFG BIT(13)
/* LVDS Format values for OLDI_MAP field in DISPC_VP_OLDI_CFG register */
enum oldi_mode_reg_val { SPWG_18 = 0, JEIDA_24 = 1, SPWG_24 = 2 };
/* /*
* OLDI IO_CTRL register offsets. On AM654 the registers are found * OLDI IO_CTRL register offsets. On AM654 the registers are found
* from CTRL_MMR0, there the syscon regmap should map 0x14 bytes from * from CTRL_MMR0, there the syscon regmap should map 0x14 bytes from
* CTRLMMR0P1_OLDI_DAT0_IO_CTRL to CTRLMMR0P1_OLDI_CLK_IO_CTRL * CTRLMMR0P1_OLDI_DAT0_IO_CTRL to CTRLMMR0P1_OLDI_CLK_IO_CTRL
* register range. * register range.
*/ */
#define OLDI_DAT0_IO_CTRL 0x00 #define AM65X_OLDI_DAT0_IO_CTRL 0x00
#define OLDI_DAT1_IO_CTRL 0x04 #define AM65X_OLDI_DAT1_IO_CTRL 0x04
#define OLDI_DAT2_IO_CTRL 0x08 #define AM65X_OLDI_DAT2_IO_CTRL 0x08
#define OLDI_DAT3_IO_CTRL 0x0C #define AM65X_OLDI_DAT3_IO_CTRL 0x0C
#define OLDI_CLK_IO_CTRL 0x10 #define AM65X_OLDI_CLK_IO_CTRL 0x10
#define OLDI_PWRDN_TX BIT(8) #define AM65X_OLDI_PWRDN_TX BIT(8)
#endif /* __TIDSS_DISPC_REGS_H */ #endif /* __TIDSS_DISPC_REGS_H */

View File

@ -24,6 +24,7 @@
#include "tidss_drv.h" #include "tidss_drv.h"
#include "tidss_kms.h" #include "tidss_kms.h"
#include "tidss_irq.h" #include "tidss_irq.h"
#include "tidss_oldi.h"
/* Power management */ /* Power management */
@ -147,6 +148,10 @@ static int tidss_probe(struct platform_device *pdev)
return ret; return ret;
} }
ret = tidss_oldi_init(tidss);
if (ret)
return dev_err_probe(dev, ret, "failed to init OLDI\n");
pm_runtime_enable(dev); pm_runtime_enable(dev);
pm_runtime_set_autosuspend_delay(dev, 1000); pm_runtime_set_autosuspend_delay(dev, 1000);
@ -203,6 +208,8 @@ err_runtime_suspend:
pm_runtime_dont_use_autosuspend(dev); pm_runtime_dont_use_autosuspend(dev);
pm_runtime_disable(dev); pm_runtime_disable(dev);
tidss_oldi_deinit(tidss);
return ret; return ret;
} }
@ -227,6 +234,8 @@ static void tidss_remove(struct platform_device *pdev)
pm_runtime_dont_use_autosuspend(dev); pm_runtime_dont_use_autosuspend(dev);
pm_runtime_disable(dev); pm_runtime_disable(dev);
tidss_oldi_deinit(tidss);
/* devm allocated dispc goes away with the dev so mark it NULL */ /* devm allocated dispc goes away with the dev so mark it NULL */
dispc_remove(tidss); dispc_remove(tidss);

View File

@ -11,8 +11,10 @@
#define TIDSS_MAX_PORTS 4 #define TIDSS_MAX_PORTS 4
#define TIDSS_MAX_PLANES 4 #define TIDSS_MAX_PLANES 4
#define TIDSS_MAX_OLDI_TXES 2
typedef u32 dispc_irq_t; typedef u32 dispc_irq_t;
struct tidss_oldi;
struct tidss_device { struct tidss_device {
struct drm_device ddev; /* DRM device for DSS */ struct drm_device ddev; /* DRM device for DSS */
@ -27,6 +29,9 @@ struct tidss_device {
unsigned int num_planes; unsigned int num_planes;
struct drm_plane *planes[TIDSS_MAX_PLANES]; struct drm_plane *planes[TIDSS_MAX_PLANES];
unsigned int num_oldis;
struct tidss_oldi *oldis[TIDSS_MAX_OLDI_TXES];
unsigned int irq; unsigned int irq;
/* protects the irq masks field and irqenable/irqstatus registers */ /* protects the irq masks field and irqenable/irqstatus registers */

View File

@ -144,7 +144,7 @@ static int tidss_dispc_modeset_init(struct tidss_device *tidss)
dev_dbg(dev, "Setting up panel for port %d\n", i); dev_dbg(dev, "Setting up panel for port %d\n", i);
switch (feat->vp_bus_type[i]) { switch (feat->vp_bus_type[i]) {
case DISPC_VP_OLDI: case DISPC_VP_OLDI_AM65X:
enc_type = DRM_MODE_ENCODER_LVDS; enc_type = DRM_MODE_ENCODER_LVDS;
conn_type = DRM_MODE_CONNECTOR_LVDS; conn_type = DRM_MODE_CONNECTOR_LVDS;
break; break;

View File

@ -0,0 +1,598 @@
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright (C) 2025 - Texas Instruments Incorporated
*
* Aradhya Bhatia <a-bhatia1@ti.com>
*/
#include <linux/clk.h>
#include <linux/of.h>
#include <linux/of_graph.h>
#include <linux/mfd/syscon.h>
#include <linux/media-bus-format.h>
#include <linux/regmap.h>
#include <drm/drm_atomic_helper.h>
#include <drm/drm_bridge.h>
#include <drm/drm_of.h>
#include "tidss_dispc.h"
#include "tidss_dispc_regs.h"
#include "tidss_oldi.h"
struct tidss_oldi {
struct tidss_device *tidss;
struct device *dev;
struct drm_bridge bridge;
struct drm_bridge *next_bridge;
enum tidss_oldi_link_type link_type;
const struct oldi_bus_format *bus_format;
u32 oldi_instance;
int companion_instance; /* -1 when OLDI TX operates in Single-Link */
u32 parent_vp;
struct clk *serial;
struct regmap *io_ctrl;
};
struct oldi_bus_format {
u32 bus_fmt;
u32 data_width;
enum oldi_mode_reg_val oldi_mode_reg_val;
u32 input_bus_fmt;
};
static const struct oldi_bus_format oldi_bus_formats[] = {
{ MEDIA_BUS_FMT_RGB666_1X7X3_SPWG, 18, SPWG_18, MEDIA_BUS_FMT_RGB666_1X18 },
{ MEDIA_BUS_FMT_RGB888_1X7X4_SPWG, 24, SPWG_24, MEDIA_BUS_FMT_RGB888_1X24 },
{ MEDIA_BUS_FMT_RGB888_1X7X4_JEIDA, 24, JEIDA_24, MEDIA_BUS_FMT_RGB888_1X24 },
};
#define OLDI_IDLE_CLK_HZ 25000000 /*25 MHz */
static inline struct tidss_oldi *
drm_bridge_to_tidss_oldi(struct drm_bridge *bridge)
{
return container_of(bridge, struct tidss_oldi, bridge);
}
static int tidss_oldi_bridge_attach(struct drm_bridge *bridge,
struct drm_encoder *encoder,
enum drm_bridge_attach_flags flags)
{
struct tidss_oldi *oldi = drm_bridge_to_tidss_oldi(bridge);
if (!oldi->next_bridge) {
dev_err(oldi->dev,
"%s: OLDI%u Failure attach next bridge\n",
__func__, oldi->oldi_instance);
return -ENODEV;
}
if (!(flags & DRM_BRIDGE_ATTACH_NO_CONNECTOR)) {
dev_err(oldi->dev,
"%s: OLDI%u DRM_BRIDGE_ATTACH_NO_CONNECTOR is mandatory.\n",
__func__, oldi->oldi_instance);
return -EINVAL;
}
return drm_bridge_attach(encoder, oldi->next_bridge, bridge, flags);
}
static int
tidss_oldi_set_serial_clk(struct tidss_oldi *oldi, unsigned long rate)
{
unsigned long new_rate;
int ret;
ret = clk_set_rate(oldi->serial, rate);
if (ret) {
dev_err(oldi->dev,
"OLDI%u: failed to set serial clk rate to %lu Hz\n",
oldi->oldi_instance, rate);
return ret;
}
new_rate = clk_get_rate(oldi->serial);
if (dispc_pclk_diff(rate, new_rate) > 5)
dev_warn(oldi->dev,
"OLDI%u Clock rate %lu differs over 5%% from requested %lu\n",
oldi->oldi_instance, new_rate, rate);
dev_dbg(oldi->dev, "OLDI%u: new rate %lu Hz (requested %lu Hz)\n",
oldi->oldi_instance, clk_get_rate(oldi->serial), rate);
return 0;
}
static void tidss_oldi_tx_power(struct tidss_oldi *oldi, bool enable)
{
u32 mask;
/*
* The power control bits are Active Low, and remain powered off by
* default. That is, the bits are set to 1. To power on the OLDI TXes,
* the bits must be cleared to 0. Since there are cases where not all
* OLDI TXes are being used, the power logic selectively powers them
* on.
* Setting the variable 'val' to particular bit masks, makes sure that
* the undesired OLDI TXes remain powered off.
*/
if (enable) {
switch (oldi->link_type) {
case OLDI_MODE_SINGLE_LINK:
/* Power-on only the required OLDI TX's IO*/
mask = OLDI_PWRDOWN_TX(oldi->oldi_instance) | OLDI_PWRDN_BG;
break;
case OLDI_MODE_CLONE_SINGLE_LINK:
case OLDI_MODE_DUAL_LINK:
/* Power-on both the OLDI TXes' IOs */
mask = OLDI_PWRDOWN_TX(oldi->oldi_instance) |
OLDI_PWRDOWN_TX(oldi->companion_instance) |
OLDI_PWRDN_BG;
break;
default:
/*
* This code execution should never reach here as any
* OLDI with an unsupported OLDI mode would never get
* registered in the first place.
* However, power-off the OLDI in concern just in case.
*/
mask = OLDI_PWRDOWN_TX(oldi->oldi_instance);
enable = false;
break;
}
} else {
switch (oldi->link_type) {
case OLDI_MODE_CLONE_SINGLE_LINK:
case OLDI_MODE_DUAL_LINK:
mask = OLDI_PWRDOWN_TX(oldi->oldi_instance) |
OLDI_PWRDOWN_TX(oldi->companion_instance) |
OLDI_PWRDN_BG;
break;
case OLDI_MODE_SINGLE_LINK:
default:
mask = OLDI_PWRDOWN_TX(oldi->oldi_instance);
break;
}
}
regmap_update_bits(oldi->io_ctrl, OLDI_PD_CTRL, mask, enable ? 0 : mask);
}
static int tidss_oldi_config(struct tidss_oldi *oldi)
{
const struct oldi_bus_format *bus_fmt = NULL;
u32 oldi_cfg = 0;
int ret;
bus_fmt = oldi->bus_format;
/*
* MASTERSLAVE and SRC bits of OLDI Config are always set to 0.
*/
if (bus_fmt->data_width == 24)
oldi_cfg |= OLDI_MSB;
else if (bus_fmt->data_width != 18)
dev_warn(oldi->dev,
"OLDI%u: DSS port width %d not supported\n",
oldi->oldi_instance, bus_fmt->data_width);
oldi_cfg |= OLDI_DEPOL;
oldi_cfg = (oldi_cfg & (~OLDI_MAP)) | (bus_fmt->oldi_mode_reg_val << 1);
oldi_cfg |= OLDI_SOFTRST;
oldi_cfg |= OLDI_ENABLE;
switch (oldi->link_type) {
case OLDI_MODE_SINGLE_LINK:
/* All configuration is done for this mode. */
break;
case OLDI_MODE_CLONE_SINGLE_LINK:
oldi_cfg |= OLDI_CLONE_MODE;
break;
case OLDI_MODE_DUAL_LINK:
/* data-mapping field also indicates dual-link mode */
oldi_cfg |= BIT(3);
oldi_cfg |= OLDI_DUALMODESYNC;
break;
default:
dev_err(oldi->dev, "OLDI%u: Unsupported mode.\n",
oldi->oldi_instance);
return -EINVAL;
}
ret = tidss_configure_oldi(oldi->tidss, oldi->parent_vp, oldi_cfg);
if (ret == -ETIMEDOUT)
dev_warn(oldi->dev, "OLDI%u: timeout waiting for OLDI reset done.\n",
oldi->oldi_instance);
return ret;
}
static void tidss_oldi_atomic_pre_enable(struct drm_bridge *bridge,
struct drm_atomic_state *state)
{
struct tidss_oldi *oldi = drm_bridge_to_tidss_oldi(bridge);
struct drm_connector *connector;
struct drm_connector_state *conn_state;
struct drm_crtc_state *crtc_state;
struct drm_display_mode *mode;
if (oldi->link_type == OLDI_MODE_SECONDARY_CLONE_SINGLE_LINK)
return;
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;
mode = &crtc_state->adjusted_mode;
/* Configure the OLDI params*/
tidss_oldi_config(oldi);
/* Set the OLDI serial clock (7 times the pixel clock) */
tidss_oldi_set_serial_clk(oldi, mode->clock * 7 * 1000);
/* Enable OLDI IO power */
tidss_oldi_tx_power(oldi, true);
}
static void tidss_oldi_atomic_post_disable(struct drm_bridge *bridge,
struct drm_atomic_state *state)
{
struct tidss_oldi *oldi = drm_bridge_to_tidss_oldi(bridge);
if (oldi->link_type == OLDI_MODE_SECONDARY_CLONE_SINGLE_LINK)
return;
/* Disable OLDI IO power */
tidss_oldi_tx_power(oldi, false);
/* Set the OLDI serial clock to IDLE Frequency */
tidss_oldi_set_serial_clk(oldi, OLDI_IDLE_CLK_HZ);
/* Clear OLDI Config */
tidss_disable_oldi(oldi->tidss, oldi->parent_vp);
}
#define MAX_INPUT_SEL_FORMATS 1
static u32 *tidss_oldi_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 tidss_oldi *oldi = drm_bridge_to_tidss_oldi(bridge);
u32 *input_fmts;
int i;
*num_input_fmts = 0;
for (i = 0; i < ARRAY_SIZE(oldi_bus_formats); i++)
if (oldi_bus_formats[i].bus_fmt == output_fmt)
break;
if (i == ARRAY_SIZE(oldi_bus_formats))
return NULL;
input_fmts = kcalloc(MAX_INPUT_SEL_FORMATS, sizeof(*input_fmts),
GFP_KERNEL);
if (!input_fmts)
return NULL;
*num_input_fmts = 1;
input_fmts[0] = oldi_bus_formats[i].input_bus_fmt;
oldi->bus_format = &oldi_bus_formats[i];
return input_fmts;
}
static const struct drm_bridge_funcs tidss_oldi_bridge_funcs = {
.attach = tidss_oldi_bridge_attach,
.atomic_pre_enable = tidss_oldi_atomic_pre_enable,
.atomic_post_disable = tidss_oldi_atomic_post_disable,
.atomic_get_input_bus_fmts = tidss_oldi_atomic_get_input_bus_fmts,
.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,
};
static int get_oldi_mode(struct device_node *oldi_tx, int *companion_instance)
{
struct device_node *companion;
struct device_node *port0, *port1;
u32 companion_reg;
bool secondary_oldi = false;
int pixel_order;
/*
* Find if the OLDI is paired with another OLDI for combined OLDI
* operation (dual-link or clone).
*/
companion = of_parse_phandle(oldi_tx, "ti,companion-oldi", 0);
if (!companion)
/*
* The OLDI TX does not have a companion, nor is it a
* secondary OLDI. It will operate independently.
*/
return OLDI_MODE_SINGLE_LINK;
if (of_property_read_u32(companion, "reg", &companion_reg))
return OLDI_MODE_UNSUPPORTED;
if (companion_reg > (TIDSS_MAX_OLDI_TXES - 1))
/* Invalid companion OLDI reg value. */
return OLDI_MODE_UNSUPPORTED;
*companion_instance = (int)companion_reg;
if (of_property_read_bool(oldi_tx, "ti,secondary-oldi"))
secondary_oldi = true;
/*
* We need to work out if the sink is expecting us to function in
* dual-link mode. We do this by looking at the DT port nodes, the
* OLDI TX ports are connected to. If they are marked as expecting
* even pixels and odd pixels, then we need to enable dual-link.
*/
port0 = of_graph_get_port_by_id(oldi_tx, 1);
port1 = of_graph_get_port_by_id(companion, 1);
pixel_order = drm_of_lvds_get_dual_link_pixel_order(port0, port1);
of_node_put(port0);
of_node_put(port1);
of_node_put(companion);
switch (pixel_order) {
case -EINVAL:
/*
* The dual-link properties were not found in at least
* one of the sink nodes. Since 2 OLDI ports are present
* in the DT, it can be safely assumed that the required
* configuration is Clone Mode.
*/
return (secondary_oldi ? OLDI_MODE_SECONDARY_CLONE_SINGLE_LINK :
OLDI_MODE_CLONE_SINGLE_LINK);
case DRM_LVDS_DUAL_LINK_ODD_EVEN_PIXELS:
/*
* Primary OLDI can only support "ODD" pixels. So, from its
* perspective, the pixel order has to be ODD-EVEN.
*/
return (secondary_oldi ? OLDI_MODE_UNSUPPORTED :
OLDI_MODE_DUAL_LINK);
case DRM_LVDS_DUAL_LINK_EVEN_ODD_PIXELS:
/*
* Secondary OLDI can only support "EVEN" pixels. So, from its
* perspective, the pixel order has to be EVEN-ODD.
*/
return (secondary_oldi ? OLDI_MODE_SECONDARY_DUAL_LINK :
OLDI_MODE_UNSUPPORTED);
default:
return OLDI_MODE_UNSUPPORTED;
}
}
static int get_parent_dss_vp(struct device_node *oldi_tx, u32 *parent_vp)
{
struct device_node *ep, *dss_port;
int ret;
ep = of_graph_get_endpoint_by_regs(oldi_tx, OLDI_INPUT_PORT, -1);
if (ep) {
dss_port = of_graph_get_remote_port(ep);
if (!dss_port) {
ret = -ENODEV;
goto err_return_ep_port;
}
ret = of_property_read_u32(dss_port, "reg", parent_vp);
of_node_put(dss_port);
err_return_ep_port:
of_node_put(ep);
return ret;
}
return -ENODEV;
}
static const struct drm_bridge_timings default_tidss_oldi_timings = {
.input_bus_flags = DRM_BUS_FLAG_SYNC_SAMPLE_NEGEDGE
| DRM_BUS_FLAG_DE_HIGH,
};
void tidss_oldi_deinit(struct tidss_device *tidss)
{
for (int i = 0; i < tidss->num_oldis; i++) {
if (tidss->oldis[i]) {
drm_bridge_remove(&tidss->oldis[i]->bridge);
tidss->oldis[i] = NULL;
}
}
}
int tidss_oldi_init(struct tidss_device *tidss)
{
struct tidss_oldi *oldi;
struct device_node *child;
struct drm_bridge *bridge;
u32 parent_vp, oldi_instance;
int companion_instance = -1;
enum tidss_oldi_link_type link_type = OLDI_MODE_UNSUPPORTED;
struct device_node *oldi_parent;
int ret = 0;
tidss->num_oldis = 0;
oldi_parent = of_get_child_by_name(tidss->dev->of_node, "oldi-transmitters");
if (!oldi_parent)
/* Return gracefully */
return 0;
for_each_available_child_of_node(oldi_parent, child) {
ret = get_parent_dss_vp(child, &parent_vp);
if (ret) {
if (ret == -ENODEV) {
/*
* ENODEV means that this particular OLDI node
* is not connected with the DSS, which is not
* a harmful case. There could be another OLDI
* which may still be connected.
* Continue to search for that.
*/
ret = 0;
continue;
}
goto err_put_node;
}
ret = of_property_read_u32(child, "reg", &oldi_instance);
if (ret)
goto err_put_node;
/*
* Now that it's confirmed that OLDI is connected with DSS,
* let's continue getting the OLDI sinks ahead and other OLDI
* properties.
*/
bridge = devm_drm_of_get_bridge(tidss->dev, child,
OLDI_OUTPUT_PORT, 0);
if (IS_ERR(bridge)) {
/*
* Either there was no OLDI sink in the devicetree, or
* the OLDI sink has not been added yet. In any case,
* return.
* We don't want to have an OLDI node connected to DSS
* but not to any sink.
*/
ret = dev_err_probe(tidss->dev, PTR_ERR(bridge),
"no panel/bridge for OLDI%u.\n",
oldi_instance);
goto err_put_node;
}
link_type = get_oldi_mode(child, &companion_instance);
if (link_type == OLDI_MODE_UNSUPPORTED) {
ret = dev_err_probe(tidss->dev, -EINVAL,
"OLDI%u: Unsupported OLDI connection.\n",
oldi_instance);
goto err_put_node;
} else if ((link_type == OLDI_MODE_SECONDARY_CLONE_SINGLE_LINK) ||
(link_type == OLDI_MODE_CLONE_SINGLE_LINK)) {
/*
* The OLDI driver cannot support OLDI clone mode
* properly at present.
* The clone mode requires 2 working encoder-bridge
* pipelines, generating from the same crtc. The DRM
* framework does not support this at present. If
* there were to be, say, 2 OLDI sink bridges each
* connected to an OLDI TXes, they couldn't both be
* supported simultaneously.
* This driver still has some code pertaining to OLDI
* clone mode configuration in DSS hardware for future,
* when there is a better infrastructure in the DRM
* framework to support 2 encoder-bridge pipelines
* simultaneously.
* Till that time, this driver shall error out if it
* detects a clone mode configuration.
*/
ret = dev_err_probe(tidss->dev, -EOPNOTSUPP,
"The OLDI driver does not support Clone Mode at present.\n");
goto err_put_node;
} else if (link_type == OLDI_MODE_SECONDARY_DUAL_LINK) {
/*
* This is the secondary OLDI node, which serves as a
* companion to the primary OLDI, when it is configured
* for the dual-link mode. Since the primary OLDI will
* be a part of bridge chain, no need to put this one
* too. Continue onto the next OLDI node.
*/
continue;
}
oldi = devm_kzalloc(tidss->dev, sizeof(*oldi), GFP_KERNEL);
if (!oldi) {
ret = -ENOMEM;
goto err_put_node;
}
oldi->parent_vp = parent_vp;
oldi->oldi_instance = oldi_instance;
oldi->companion_instance = companion_instance;
oldi->link_type = link_type;
oldi->dev = tidss->dev;
oldi->next_bridge = bridge;
/*
* Only the primary OLDI needs to reference the io-ctrl system
* registers, and the serial clock.
* We don't require a check for secondary OLDI in dual-link mode
* because the driver will not create a drm_bridge instance.
* But the driver will need to create a drm_bridge instance,
* for secondary OLDI in clone mode (once it is supported).
*/
if (link_type != OLDI_MODE_SECONDARY_CLONE_SINGLE_LINK) {
oldi->io_ctrl = syscon_regmap_lookup_by_phandle(child,
"ti,oldi-io-ctrl");
if (IS_ERR(oldi->io_ctrl)) {
ret = dev_err_probe(oldi->dev, PTR_ERR(oldi->io_ctrl),
"OLDI%u: syscon_regmap_lookup_by_phandle failed.\n",
oldi_instance);
goto err_put_node;
}
oldi->serial = of_clk_get_by_name(child, "serial");
if (IS_ERR(oldi->serial)) {
ret = dev_err_probe(oldi->dev, PTR_ERR(oldi->serial),
"OLDI%u: Failed to get serial clock.\n",
oldi_instance);
goto err_put_node;
}
}
/* Register the bridge. */
oldi->bridge.of_node = child;
oldi->bridge.driver_private = oldi;
oldi->bridge.funcs = &tidss_oldi_bridge_funcs;
oldi->bridge.timings = &default_tidss_oldi_timings;
tidss->oldis[tidss->num_oldis++] = oldi;
oldi->tidss = tidss;
drm_bridge_add(&oldi->bridge);
}
of_node_put(child);
of_node_put(oldi_parent);
return 0;
err_put_node:
of_node_put(child);
of_node_put(oldi_parent);
return ret;
}

View File

@ -0,0 +1,43 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */
/*
* Copyright (C) 2025 - Texas Instruments Incorporated
*
* Aradhya Bhatia <a-bhatia1@ti.com>
*/
#ifndef __TIDSS_OLDI_H__
#define __TIDSS_OLDI_H__
#include "tidss_drv.h"
struct tidss_oldi;
/* OLDI PORTS */
#define OLDI_INPUT_PORT 0
#define OLDI_OUTPUT_PORT 1
/* Control MMR Registers */
/* Register offsets */
#define OLDI_PD_CTRL 0x100
#define OLDI_LB_CTRL 0x104
/* Power control bits */
#define OLDI_PWRDOWN_TX(n) BIT(n)
/* LVDS Bandgap reference Enable/Disable */
#define OLDI_PWRDN_BG BIT(8)
enum tidss_oldi_link_type {
OLDI_MODE_UNSUPPORTED,
OLDI_MODE_SINGLE_LINK,
OLDI_MODE_CLONE_SINGLE_LINK,
OLDI_MODE_SECONDARY_CLONE_SINGLE_LINK,
OLDI_MODE_DUAL_LINK,
OLDI_MODE_SECONDARY_DUAL_LINK,
};
int tidss_oldi_init(struct tidss_device *tidss);
void tidss_oldi_deinit(struct tidss_device *tidss);
#endif /* __TIDSS_OLDI_H__ */

View File

@ -526,11 +526,11 @@ static s64 ttm_bo_evict_cb(struct ttm_lru_walk *walk, struct ttm_buffer_object *
return 0; return 0;
if (bo->deleted) { if (bo->deleted) {
lret = ttm_bo_wait_ctx(bo, walk->ctx); lret = ttm_bo_wait_ctx(bo, walk->arg.ctx);
if (!lret) if (!lret)
ttm_bo_cleanup_memtype_use(bo); ttm_bo_cleanup_memtype_use(bo);
} else { } else {
lret = ttm_bo_evict(bo, walk->ctx); lret = ttm_bo_evict(bo, walk->arg.ctx);
} }
if (lret) if (lret)
@ -566,8 +566,10 @@ static int ttm_bo_evict_alloc(struct ttm_device *bdev,
struct ttm_bo_evict_walk evict_walk = { struct ttm_bo_evict_walk evict_walk = {
.walk = { .walk = {
.ops = &ttm_evict_walk_ops, .ops = &ttm_evict_walk_ops,
.ctx = ctx, .arg = {
.ticket = ticket, .ctx = ctx,
.ticket = ticket,
}
}, },
.place = place, .place = place,
.evictor = evictor, .evictor = evictor,
@ -576,7 +578,7 @@ static int ttm_bo_evict_alloc(struct ttm_device *bdev,
}; };
s64 lret; s64 lret;
evict_walk.walk.trylock_only = true; evict_walk.walk.arg.trylock_only = true;
lret = ttm_lru_walk_for_evict(&evict_walk.walk, bdev, man, 1); lret = ttm_lru_walk_for_evict(&evict_walk.walk, bdev, man, 1);
/* One more attempt if we hit low limit? */ /* One more attempt if we hit low limit? */
@ -590,12 +592,12 @@ static int ttm_bo_evict_alloc(struct ttm_device *bdev,
/* Reset low limit */ /* Reset low limit */
evict_walk.try_low = evict_walk.hit_low = false; evict_walk.try_low = evict_walk.hit_low = false;
/* If ticket-locking, repeat while making progress. */ /* If ticket-locking, repeat while making progress. */
evict_walk.walk.trylock_only = false; evict_walk.walk.arg.trylock_only = false;
retry: retry:
do { do {
/* The walk may clear the evict_walk.walk.ticket field */ /* The walk may clear the evict_walk.walk.ticket field */
evict_walk.walk.ticket = ticket; evict_walk.walk.arg.ticket = ticket;
evict_walk.evicted = 0; evict_walk.evicted = 0;
lret = ttm_lru_walk_for_evict(&evict_walk.walk, bdev, man, 1); lret = ttm_lru_walk_for_evict(&evict_walk.walk, bdev, man, 1);
} while (!lret && evict_walk.evicted); } while (!lret && evict_walk.evicted);
@ -1106,7 +1108,7 @@ ttm_bo_swapout_cb(struct ttm_lru_walk *walk, struct ttm_buffer_object *bo)
struct ttm_place place = {.mem_type = bo->resource->mem_type}; struct ttm_place place = {.mem_type = bo->resource->mem_type};
struct ttm_bo_swapout_walk *swapout_walk = struct ttm_bo_swapout_walk *swapout_walk =
container_of(walk, typeof(*swapout_walk), walk); container_of(walk, typeof(*swapout_walk), walk);
struct ttm_operation_ctx *ctx = walk->ctx; struct ttm_operation_ctx *ctx = walk->arg.ctx;
s64 ret; s64 ret;
/* /*
@ -1217,8 +1219,10 @@ s64 ttm_bo_swapout(struct ttm_device *bdev, struct ttm_operation_ctx *ctx,
struct ttm_bo_swapout_walk swapout_walk = { struct ttm_bo_swapout_walk swapout_walk = {
.walk = { .walk = {
.ops = &ttm_swap_ops, .ops = &ttm_swap_ops,
.ctx = ctx, .arg = {
.trylock_only = true, .ctx = ctx,
.trylock_only = true,
},
}, },
.gfp_flags = gfp_flags, .gfp_flags = gfp_flags,
}; };

View File

@ -773,14 +773,15 @@ error_destroy_tt:
return ret; return ret;
} }
static bool ttm_lru_walk_trylock(struct ttm_operation_ctx *ctx, static bool ttm_lru_walk_trylock(struct ttm_bo_lru_cursor *curs,
struct ttm_buffer_object *bo, struct ttm_buffer_object *bo)
bool *needs_unlock)
{ {
*needs_unlock = false; struct ttm_operation_ctx *ctx = curs->arg->ctx;
curs->needs_unlock = false;
if (dma_resv_trylock(bo->base.resv)) { if (dma_resv_trylock(bo->base.resv)) {
*needs_unlock = true; curs->needs_unlock = true;
return true; return true;
} }
@ -792,27 +793,27 @@ static bool ttm_lru_walk_trylock(struct ttm_operation_ctx *ctx,
return false; return false;
} }
static int ttm_lru_walk_ticketlock(struct ttm_lru_walk *walk, static int ttm_lru_walk_ticketlock(struct ttm_bo_lru_cursor *curs,
struct ttm_buffer_object *bo, struct ttm_buffer_object *bo)
bool *needs_unlock)
{ {
struct ttm_lru_walk_arg *arg = curs->arg;
struct dma_resv *resv = bo->base.resv; struct dma_resv *resv = bo->base.resv;
int ret; int ret;
if (walk->ctx->interruptible) if (arg->ctx->interruptible)
ret = dma_resv_lock_interruptible(resv, walk->ticket); ret = dma_resv_lock_interruptible(resv, arg->ticket);
else else
ret = dma_resv_lock(resv, walk->ticket); ret = dma_resv_lock(resv, arg->ticket);
if (!ret) { if (!ret) {
*needs_unlock = true; curs->needs_unlock = true;
/* /*
* Only a single ticketlock per loop. Ticketlocks are prone * Only a single ticketlock per loop. Ticketlocks are prone
* to return -EDEADLK causing the eviction to fail, so * to return -EDEADLK causing the eviction to fail, so
* after waiting for the ticketlock, revert back to * after waiting for the ticketlock, revert back to
* trylocking for this walk. * trylocking for this walk.
*/ */
walk->ticket = NULL; arg->ticket = NULL;
} else if (ret == -EDEADLK) { } else if (ret == -EDEADLK) {
/* Caller needs to exit the ww transaction. */ /* Caller needs to exit the ww transaction. */
ret = -ENOSPC; ret = -ENOSPC;
@ -821,12 +822,6 @@ static int ttm_lru_walk_ticketlock(struct ttm_lru_walk *walk,
return ret; return ret;
} }
static void ttm_lru_walk_unlock(struct ttm_buffer_object *bo, bool locked)
{
if (locked)
dma_resv_unlock(bo->base.resv);
}
/** /**
* ttm_lru_walk_for_evict() - Perform a LRU list walk, with actions taken on * ttm_lru_walk_for_evict() - Perform a LRU list walk, with actions taken on
* valid items. * valid items.
@ -861,64 +856,21 @@ static void ttm_lru_walk_unlock(struct ttm_buffer_object *bo, bool locked)
s64 ttm_lru_walk_for_evict(struct ttm_lru_walk *walk, struct ttm_device *bdev, s64 ttm_lru_walk_for_evict(struct ttm_lru_walk *walk, struct ttm_device *bdev,
struct ttm_resource_manager *man, s64 target) struct ttm_resource_manager *man, s64 target)
{ {
struct ttm_resource_cursor cursor; struct ttm_bo_lru_cursor cursor;
struct ttm_resource *res; struct ttm_buffer_object *bo;
s64 progress = 0; s64 progress = 0;
s64 lret; s64 lret;
spin_lock(&bdev->lru_lock); ttm_bo_lru_for_each_reserved_guarded(&cursor, man, &walk->arg, bo) {
ttm_resource_cursor_init(&cursor, man); lret = walk->ops->process_bo(walk, bo);
ttm_resource_manager_for_each_res(&cursor, res) {
struct ttm_buffer_object *bo = res->bo;
bool bo_needs_unlock = false;
bool bo_locked = false;
int mem_type;
/*
* Attempt a trylock before taking a reference on the bo,
* since if we do it the other way around, and the trylock fails,
* we need to drop the lru lock to put the bo.
*/
if (ttm_lru_walk_trylock(walk->ctx, bo, &bo_needs_unlock))
bo_locked = true;
else if (!walk->ticket || walk->ctx->no_wait_gpu ||
walk->trylock_only)
continue;
if (!ttm_bo_get_unless_zero(bo)) {
ttm_lru_walk_unlock(bo, bo_needs_unlock);
continue;
}
mem_type = res->mem_type;
spin_unlock(&bdev->lru_lock);
lret = 0;
if (!bo_locked)
lret = ttm_lru_walk_ticketlock(walk, bo, &bo_needs_unlock);
/*
* Note that in between the release of the lru lock and the
* ticketlock, the bo may have switched resource,
* and also memory type, since the resource may have been
* freed and allocated again with a different memory type.
* In that case, just skip it.
*/
if (!lret && bo->resource && bo->resource->mem_type == mem_type)
lret = walk->ops->process_bo(walk, bo);
ttm_lru_walk_unlock(bo, bo_needs_unlock);
ttm_bo_put(bo);
if (lret == -EBUSY || lret == -EALREADY) if (lret == -EBUSY || lret == -EALREADY)
lret = 0; lret = 0;
progress = (lret < 0) ? lret : progress + lret; progress = (lret < 0) ? lret : progress + lret;
spin_lock(&bdev->lru_lock);
if (progress < 0 || progress >= target) if (progress < 0 || progress >= target)
break; break;
} }
ttm_resource_cursor_fini(&cursor); if (IS_ERR(bo))
spin_unlock(&bdev->lru_lock); return PTR_ERR(bo);
return progress; return progress;
} }
@ -956,44 +908,87 @@ EXPORT_SYMBOL(ttm_bo_lru_cursor_fini);
* ttm_bo_lru_cursor_init() - Initialize a struct ttm_bo_lru_cursor * ttm_bo_lru_cursor_init() - Initialize a struct ttm_bo_lru_cursor
* @curs: The ttm_bo_lru_cursor to initialize. * @curs: The ttm_bo_lru_cursor to initialize.
* @man: The ttm resource_manager whose LRU lists to iterate over. * @man: The ttm resource_manager whose LRU lists to iterate over.
* @ctx: The ttm_operation_ctx to govern the locking. * @arg: The ttm_lru_walk_arg to govern the walk.
* *
* Initialize a struct ttm_bo_lru_cursor. Currently only trylocking * Initialize a struct ttm_bo_lru_cursor.
* or prelocked buffer objects are available as detailed by
* @ctx::resv and @ctx::allow_res_evict. Ticketlocking is not
* supported.
* *
* Return: Pointer to @curs. The function does not fail. * Return: Pointer to @curs. The function does not fail.
*/ */
struct ttm_bo_lru_cursor * struct ttm_bo_lru_cursor *
ttm_bo_lru_cursor_init(struct ttm_bo_lru_cursor *curs, ttm_bo_lru_cursor_init(struct ttm_bo_lru_cursor *curs,
struct ttm_resource_manager *man, struct ttm_resource_manager *man,
struct ttm_operation_ctx *ctx) struct ttm_lru_walk_arg *arg)
{ {
memset(curs, 0, sizeof(*curs)); memset(curs, 0, sizeof(*curs));
ttm_resource_cursor_init(&curs->res_curs, man); ttm_resource_cursor_init(&curs->res_curs, man);
curs->ctx = ctx; curs->arg = arg;
return curs; return curs;
} }
EXPORT_SYMBOL(ttm_bo_lru_cursor_init); EXPORT_SYMBOL(ttm_bo_lru_cursor_init);
static struct ttm_buffer_object * static struct ttm_buffer_object *
ttm_bo_from_res_reserved(struct ttm_resource *res, struct ttm_bo_lru_cursor *curs) __ttm_bo_lru_cursor_next(struct ttm_bo_lru_cursor *curs)
{ {
struct ttm_buffer_object *bo = res->bo; spinlock_t *lru_lock = &curs->res_curs.man->bdev->lru_lock;
struct ttm_resource *res = NULL;
struct ttm_buffer_object *bo;
struct ttm_lru_walk_arg *arg = curs->arg;
bool first = !curs->bo;
if (!ttm_lru_walk_trylock(curs->ctx, bo, &curs->needs_unlock)) ttm_bo_lru_cursor_cleanup_bo(curs);
return NULL;
if (!ttm_bo_get_unless_zero(bo)) { spin_lock(lru_lock);
if (curs->needs_unlock) for (;;) {
dma_resv_unlock(bo->base.resv); int mem_type, ret = 0;
return NULL; bool bo_locked = false;
if (first) {
res = ttm_resource_manager_first(&curs->res_curs);
first = false;
} else {
res = ttm_resource_manager_next(&curs->res_curs);
}
if (!res)
break;
bo = res->bo;
if (ttm_lru_walk_trylock(curs, bo))
bo_locked = true;
else if (!arg->ticket || arg->ctx->no_wait_gpu || arg->trylock_only)
continue;
if (!ttm_bo_get_unless_zero(bo)) {
if (curs->needs_unlock)
dma_resv_unlock(bo->base.resv);
continue;
}
mem_type = res->mem_type;
spin_unlock(lru_lock);
if (!bo_locked)
ret = ttm_lru_walk_ticketlock(curs, bo);
/*
* Note that in between the release of the lru lock and the
* ticketlock, the bo may have switched resource,
* and also memory type, since the resource may have been
* freed and allocated again with a different memory type.
* In that case, just skip it.
*/
curs->bo = bo;
if (!ret && bo->resource && bo->resource->mem_type == mem_type)
return bo;
ttm_bo_lru_cursor_cleanup_bo(curs);
if (ret && ret != -EALREADY)
return ERR_PTR(ret);
spin_lock(lru_lock);
} }
curs->bo = bo; spin_unlock(lru_lock);
return bo; return res ? bo : NULL;
} }
/** /**
@ -1007,25 +1002,7 @@ ttm_bo_from_res_reserved(struct ttm_resource *res, struct ttm_bo_lru_cursor *cur
*/ */
struct ttm_buffer_object *ttm_bo_lru_cursor_next(struct ttm_bo_lru_cursor *curs) struct ttm_buffer_object *ttm_bo_lru_cursor_next(struct ttm_bo_lru_cursor *curs)
{ {
spinlock_t *lru_lock = &curs->res_curs.man->bdev->lru_lock; return __ttm_bo_lru_cursor_next(curs);
struct ttm_resource *res = NULL;
struct ttm_buffer_object *bo;
ttm_bo_lru_cursor_cleanup_bo(curs);
spin_lock(lru_lock);
for (;;) {
res = ttm_resource_manager_next(&curs->res_curs);
if (!res)
break;
bo = ttm_bo_from_res_reserved(res, curs);
if (bo)
break;
}
spin_unlock(lru_lock);
return res ? bo : NULL;
} }
EXPORT_SYMBOL(ttm_bo_lru_cursor_next); EXPORT_SYMBOL(ttm_bo_lru_cursor_next);
@ -1039,21 +1016,8 @@ EXPORT_SYMBOL(ttm_bo_lru_cursor_next);
*/ */
struct ttm_buffer_object *ttm_bo_lru_cursor_first(struct ttm_bo_lru_cursor *curs) struct ttm_buffer_object *ttm_bo_lru_cursor_first(struct ttm_bo_lru_cursor *curs)
{ {
spinlock_t *lru_lock = &curs->res_curs.man->bdev->lru_lock; ttm_bo_lru_cursor_cleanup_bo(curs);
struct ttm_buffer_object *bo; return __ttm_bo_lru_cursor_next(curs);
struct ttm_resource *res;
spin_lock(lru_lock);
res = ttm_resource_manager_first(&curs->res_curs);
if (!res) {
spin_unlock(lru_lock);
return NULL;
}
bo = ttm_bo_from_res_reserved(res, curs);
spin_unlock(lru_lock);
return bo ? bo : ttm_bo_lru_cursor_next(curs);
} }
EXPORT_SYMBOL(ttm_bo_lru_cursor_first); EXPORT_SYMBOL(ttm_bo_lru_cursor_first);

View File

@ -284,11 +284,10 @@ static void vmw_bo_print_info(int id, struct vmw_bo *bo, struct seq_file *m)
seq_printf(m, "\t\t0x%08x: %12zu bytes %s, type = %s", seq_printf(m, "\t\t0x%08x: %12zu bytes %s, type = %s",
id, bo->tbo.base.size, placement, type); id, bo->tbo.base.size, placement, type);
seq_printf(m, ", priority = %u, pin_count = %u, GEM refs = %d, TTM refs = %d", seq_printf(m, ", priority = %u, pin_count = %u, GEM refs = %d",
bo->tbo.priority, bo->tbo.priority,
bo->tbo.pin_count, bo->tbo.pin_count,
kref_read(&bo->tbo.base.refcount), kref_read(&bo->tbo.base.refcount));
kref_read(&bo->tbo.kref));
seq_puts(m, "\n"); seq_puts(m, "\n");
} }

View File

@ -66,11 +66,15 @@ static s64 xe_shrinker_walk(struct xe_device *xe,
struct ttm_resource_manager *man = ttm_manager_type(&xe->ttm, mem_type); struct ttm_resource_manager *man = ttm_manager_type(&xe->ttm, mem_type);
struct ttm_bo_lru_cursor curs; struct ttm_bo_lru_cursor curs;
struct ttm_buffer_object *ttm_bo; struct ttm_buffer_object *ttm_bo;
struct ttm_lru_walk_arg arg = {
.ctx = ctx,
.trylock_only = true,
};
if (!man || !man->use_tt) if (!man || !man->use_tt)
continue; continue;
ttm_bo_lru_for_each_reserved_guarded(&curs, man, ctx, ttm_bo) { ttm_bo_lru_for_each_reserved_guarded(&curs, man, &arg, ttm_bo) {
if (!ttm_bo_shrink_suitable(ttm_bo, ctx)) if (!ttm_bo_shrink_suitable(ttm_bo, ctx))
continue; continue;
@ -82,6 +86,8 @@ static s64 xe_shrinker_walk(struct xe_device *xe,
if (*scanned >= to_scan) if (*scanned >= to_scan)
break; break;
} }
/* Trylocks should never error, just fail. */
xe_assert(xe, !IS_ERR(ttm_bo));
} }
return freed; return freed;

View File

@ -843,6 +843,7 @@ drm_dp_has_quirk(const struct drm_dp_desc *desc, enum drm_dp_quirk quirk)
* @lsb_reg_used: Do we also write values to the DP_EDP_BACKLIGHT_BRIGHTNESS_LSB register? * @lsb_reg_used: Do we also write values to the DP_EDP_BACKLIGHT_BRIGHTNESS_LSB register?
* @aux_enable: Does the panel support the AUX enable cap? * @aux_enable: Does the panel support the AUX enable cap?
* @aux_set: Does the panel support setting the brightness through AUX? * @aux_set: Does the panel support setting the brightness through AUX?
* @luminance_set: Does the panel support setting the brightness through AUX using luminance values?
* *
* This structure contains various data about an eDP backlight, which can be populated by using * This structure contains various data about an eDP backlight, which can be populated by using
* drm_edp_backlight_init(). * drm_edp_backlight_init().
@ -850,21 +851,23 @@ drm_dp_has_quirk(const struct drm_dp_desc *desc, enum drm_dp_quirk quirk)
struct drm_edp_backlight_info { struct drm_edp_backlight_info {
u8 pwmgen_bit_count; u8 pwmgen_bit_count;
u8 pwm_freq_pre_divider; u8 pwm_freq_pre_divider;
u16 max; u32 max;
bool lsb_reg_used : 1; bool lsb_reg_used : 1;
bool aux_enable : 1; bool aux_enable : 1;
bool aux_set : 1; bool aux_set : 1;
bool luminance_set : 1;
}; };
int int
drm_edp_backlight_init(struct drm_dp_aux *aux, struct drm_edp_backlight_info *bl, drm_edp_backlight_init(struct drm_dp_aux *aux, struct drm_edp_backlight_info *bl,
u32 max_luminance,
u16 driver_pwm_freq_hz, const u8 edp_dpcd[EDP_DISPLAY_CTL_CAP_SIZE], u16 driver_pwm_freq_hz, const u8 edp_dpcd[EDP_DISPLAY_CTL_CAP_SIZE],
u16 *current_level, u8 *current_mode); u32 *current_level, u8 *current_mode, bool need_luminance);
int drm_edp_backlight_set_level(struct drm_dp_aux *aux, const struct drm_edp_backlight_info *bl, int drm_edp_backlight_set_level(struct drm_dp_aux *aux, const struct drm_edp_backlight_info *bl,
u16 level); u32 level);
int drm_edp_backlight_enable(struct drm_dp_aux *aux, const struct drm_edp_backlight_info *bl, int drm_edp_backlight_enable(struct drm_dp_aux *aux, const struct drm_edp_backlight_info *bl,
u16 level); u32 level);
int drm_edp_backlight_disable(struct drm_dp_aux *aux, const struct drm_edp_backlight_info *bl); int drm_edp_backlight_disable(struct drm_dp_aux *aux, const struct drm_edp_backlight_info *bl);
#if IS_ENABLED(CONFIG_DRM_KMS_HELPER) && (IS_BUILTIN(CONFIG_BACKLIGHT_CLASS_DEVICE) || \ #if IS_ENABLED(CONFIG_DRM_KMS_HELPER) && (IS_BUILTIN(CONFIG_BACKLIGHT_CLASS_DEVICE) || \

View File

@ -82,8 +82,10 @@ void drm_fb_xrgb8888_to_rgb332(struct iosys_map *dst, const unsigned int *dst_pi
const struct drm_rect *clip, struct drm_format_conv_state *state); const struct drm_rect *clip, struct drm_format_conv_state *state);
void drm_fb_xrgb8888_to_rgb565(struct iosys_map *dst, const unsigned int *dst_pitch, void drm_fb_xrgb8888_to_rgb565(struct iosys_map *dst, const unsigned int *dst_pitch,
const struct iosys_map *src, const struct drm_framebuffer *fb, const struct iosys_map *src, const struct drm_framebuffer *fb,
const struct drm_rect *clip, struct drm_format_conv_state *state, const struct drm_rect *clip, struct drm_format_conv_state *state);
bool swab); void drm_fb_xrgb8888_to_rgb565be(struct iosys_map *dst, const unsigned int *dst_pitch,
const struct iosys_map *src, const struct drm_framebuffer *fb,
const struct drm_rect *clip, struct drm_format_conv_state *state);
void drm_fb_xrgb8888_to_xrgb1555(struct iosys_map *dst, const unsigned int *dst_pitch, void drm_fb_xrgb8888_to_xrgb1555(struct iosys_map *dst, const unsigned int *dst_pitch,
const struct iosys_map *src, const struct drm_framebuffer *fb, const struct iosys_map *src, const struct drm_framebuffer *fb,
const struct drm_rect *clip, struct drm_format_conv_state *state); const struct drm_rect *clip, struct drm_format_conv_state *state);

View File

@ -130,8 +130,6 @@ struct mipi_dsi_host *of_find_mipi_dsi_host_by_node(struct device_node *node);
#define MIPI_DSI_MODE_VIDEO_NO_HBP BIT(6) #define MIPI_DSI_MODE_VIDEO_NO_HBP BIT(6)
/* disable hsync-active area */ /* disable hsync-active area */
#define MIPI_DSI_MODE_VIDEO_NO_HSA BIT(7) #define MIPI_DSI_MODE_VIDEO_NO_HSA BIT(7)
/* flush display FIFO on vsync pulse */
#define MIPI_DSI_MODE_VSYNC_FLUSH BIT(8)
/* disable EoT packets in HS mode */ /* disable EoT packets in HS mode */
#define MIPI_DSI_MODE_NO_EOT_PACKET BIT(9) #define MIPI_DSI_MODE_NO_EOT_PACKET BIT(9)
/* device supports non-continuous clock behavior (DSI spec 5.6.1) */ /* device supports non-continuous clock behavior (DSI spec 5.6.1) */

View File

@ -207,11 +207,9 @@ struct ttm_lru_walk_ops {
}; };
/** /**
* struct ttm_lru_walk - Structure describing a LRU walk. * struct ttm_lru_walk_arg - Common part for the variants of BO LRU walk.
*/ */
struct ttm_lru_walk { struct ttm_lru_walk_arg {
/** @ops: Pointer to the ops structure. */
const struct ttm_lru_walk_ops *ops;
/** @ctx: Pointer to the struct ttm_operation_ctx. */ /** @ctx: Pointer to the struct ttm_operation_ctx. */
struct ttm_operation_ctx *ctx; struct ttm_operation_ctx *ctx;
/** @ticket: The struct ww_acquire_ctx if any. */ /** @ticket: The struct ww_acquire_ctx if any. */
@ -220,6 +218,16 @@ struct ttm_lru_walk {
bool trylock_only; bool trylock_only;
}; };
/**
* struct ttm_lru_walk - Structure describing a LRU walk.
*/
struct ttm_lru_walk {
/** @ops: Pointer to the ops structure. */
const struct ttm_lru_walk_ops *ops;
/** @arg: Common bo LRU walk arguments. */
struct ttm_lru_walk_arg arg;
};
s64 ttm_lru_walk_for_evict(struct ttm_lru_walk *walk, struct ttm_device *bdev, s64 ttm_lru_walk_for_evict(struct ttm_lru_walk *walk, struct ttm_device *bdev,
struct ttm_resource_manager *man, s64 target); struct ttm_resource_manager *man, s64 target);
@ -466,11 +474,6 @@ int ttm_bo_populate(struct ttm_buffer_object *bo,
struct ttm_bo_lru_cursor { struct ttm_bo_lru_cursor {
/** @res_curs: Embedded struct ttm_resource_cursor. */ /** @res_curs: Embedded struct ttm_resource_cursor. */
struct ttm_resource_cursor res_curs; struct ttm_resource_cursor res_curs;
/**
* @ctx: The struct ttm_operation_ctx used while looping.
* governs the locking mode.
*/
struct ttm_operation_ctx *ctx;
/** /**
* @bo: Buffer object pointer if a buffer object is refcounted, * @bo: Buffer object pointer if a buffer object is refcounted,
* NULL otherwise. * NULL otherwise.
@ -481,6 +484,8 @@ struct ttm_bo_lru_cursor {
* unlock before the next iteration or after loop exit. * unlock before the next iteration or after loop exit.
*/ */
bool needs_unlock; bool needs_unlock;
/** @arg: Pointer to common BO LRU walk arguments. */
struct ttm_lru_walk_arg *arg;
}; };
void ttm_bo_lru_cursor_fini(struct ttm_bo_lru_cursor *curs); void ttm_bo_lru_cursor_fini(struct ttm_bo_lru_cursor *curs);
@ -488,7 +493,7 @@ void ttm_bo_lru_cursor_fini(struct ttm_bo_lru_cursor *curs);
struct ttm_bo_lru_cursor * struct ttm_bo_lru_cursor *
ttm_bo_lru_cursor_init(struct ttm_bo_lru_cursor *curs, ttm_bo_lru_cursor_init(struct ttm_bo_lru_cursor *curs,
struct ttm_resource_manager *man, struct ttm_resource_manager *man,
struct ttm_operation_ctx *ctx); struct ttm_lru_walk_arg *arg);
struct ttm_buffer_object *ttm_bo_lru_cursor_first(struct ttm_bo_lru_cursor *curs); struct ttm_buffer_object *ttm_bo_lru_cursor_first(struct ttm_bo_lru_cursor *curs);
@ -499,9 +504,9 @@ struct ttm_buffer_object *ttm_bo_lru_cursor_next(struct ttm_bo_lru_cursor *curs)
*/ */
DEFINE_CLASS(ttm_bo_lru_cursor, struct ttm_bo_lru_cursor *, DEFINE_CLASS(ttm_bo_lru_cursor, struct ttm_bo_lru_cursor *,
if (_T) {ttm_bo_lru_cursor_fini(_T); }, if (_T) {ttm_bo_lru_cursor_fini(_T); },
ttm_bo_lru_cursor_init(curs, man, ctx), ttm_bo_lru_cursor_init(curs, man, arg),
struct ttm_bo_lru_cursor *curs, struct ttm_resource_manager *man, struct ttm_bo_lru_cursor *curs, struct ttm_resource_manager *man,
struct ttm_operation_ctx *ctx); struct ttm_lru_walk_arg *arg);
static inline void * static inline void *
class_ttm_bo_lru_cursor_lock_ptr(class_ttm_bo_lru_cursor_t *_T) class_ttm_bo_lru_cursor_lock_ptr(class_ttm_bo_lru_cursor_t *_T)
{ return *_T; } { return *_T; }
@ -512,7 +517,7 @@ class_ttm_bo_lru_cursor_lock_ptr(class_ttm_bo_lru_cursor_t *_T)
* resources on LRU lists. * resources on LRU lists.
* @_cursor: struct ttm_bo_lru_cursor to use for the iteration. * @_cursor: struct ttm_bo_lru_cursor to use for the iteration.
* @_man: The resource manager whose LRU lists to iterate over. * @_man: The resource manager whose LRU lists to iterate over.
* @_ctx: The struct ttm_operation_context to govern the @_bo locking. * @_arg: The struct ttm_lru_walk_arg to govern the LRU walk.
* @_bo: The struct ttm_buffer_object pointer pointing to the buffer object * @_bo: The struct ttm_buffer_object pointer pointing to the buffer object
* for the current iteration. * for the current iteration.
* *
@ -524,10 +529,15 @@ class_ttm_bo_lru_cursor_lock_ptr(class_ttm_bo_lru_cursor_t *_T)
* up at looping termination, even if terminated prematurely by, for * up at looping termination, even if terminated prematurely by, for
* example a return or break statement. Exiting the loop will also unlock * example a return or break statement. Exiting the loop will also unlock
* (if needed) and unreference @_bo. * (if needed) and unreference @_bo.
*
* Return: If locking of a bo returns an error, then iteration is terminated
* and @_bo is set to a corresponding error pointer. It's illegal to
* dereference @_bo after loop exit.
*/ */
#define ttm_bo_lru_for_each_reserved_guarded(_cursor, _man, _ctx, _bo) \ #define ttm_bo_lru_for_each_reserved_guarded(_cursor, _man, _arg, _bo) \
scoped_guard(ttm_bo_lru_cursor, _cursor, _man, _ctx) \ scoped_guard(ttm_bo_lru_cursor, _cursor, _man, _arg) \
for ((_bo) = ttm_bo_lru_cursor_first(_cursor); (_bo); \ for ((_bo) = ttm_bo_lru_cursor_first(_cursor); \
(_bo) = ttm_bo_lru_cursor_next(_cursor)) !IS_ERR_OR_NULL(_bo); \
(_bo) = ttm_bo_lru_cursor_next(_cursor))
#endif #endif

View File

@ -7,9 +7,13 @@
* Copyright (c) 2012-2013 David Herrmann <dh.herrmann@gmail.com> * Copyright (c) 2012-2013 David Herrmann <dh.herrmann@gmail.com>
*/ */
#include <linux/kernel.h> #include <linux/err.h>
#include <linux/types.h>
#include <linux/platform_data/simplefb.h> #include <linux/platform_data/simplefb.h>
struct device;
struct platform_device;
struct screen_info; struct screen_info;
enum { enum {

View File

@ -210,6 +210,10 @@ extern "C" {
#define DRM_FORMAT_RGBA1010102 fourcc_code('R', 'A', '3', '0') /* [31:0] R:G:B:A 10:10:10:2 little endian */ #define DRM_FORMAT_RGBA1010102 fourcc_code('R', 'A', '3', '0') /* [31:0] R:G:B:A 10:10:10:2 little endian */
#define DRM_FORMAT_BGRA1010102 fourcc_code('B', 'A', '3', '0') /* [31:0] B:G:R:A 10:10:10:2 little endian */ #define DRM_FORMAT_BGRA1010102 fourcc_code('B', 'A', '3', '0') /* [31:0] B:G:R:A 10:10:10:2 little endian */
/* 48 bpp RGB */
#define DRM_FORMAT_RGB161616 fourcc_code('R', 'G', '4', '8') /* [47:0] R:G:B 16:16:16 little endian */
#define DRM_FORMAT_BGR161616 fourcc_code('B', 'G', '4', '8') /* [47:0] B:G:R 16:16:16 little endian */
/* 64 bpp RGB */ /* 64 bpp RGB */
#define DRM_FORMAT_XRGB16161616 fourcc_code('X', 'R', '4', '8') /* [63:0] x:R:G:B 16:16:16:16 little endian */ #define DRM_FORMAT_XRGB16161616 fourcc_code('X', 'R', '4', '8') /* [63:0] x:R:G:B 16:16:16:16 little endian */
#define DRM_FORMAT_XBGR16161616 fourcc_code('X', 'B', '4', '8') /* [63:0] x:B:G:R 16:16:16:16 little endian */ #define DRM_FORMAT_XBGR16161616 fourcc_code('X', 'B', '4', '8') /* [63:0] x:B:G:R 16:16:16:16 little endian */
@ -218,7 +222,7 @@ extern "C" {
#define DRM_FORMAT_ABGR16161616 fourcc_code('A', 'B', '4', '8') /* [63:0] A:B:G:R 16:16:16:16 little endian */ #define DRM_FORMAT_ABGR16161616 fourcc_code('A', 'B', '4', '8') /* [63:0] A:B:G:R 16:16:16:16 little endian */
/* /*
* Floating point 64bpp RGB * Half-Floating point - 16b/component
* IEEE 754-2008 binary16 half-precision float * IEEE 754-2008 binary16 half-precision float
* [15:0] sign:exponent:mantissa 1:5:10 * [15:0] sign:exponent:mantissa 1:5:10
*/ */
@ -228,6 +232,20 @@ extern "C" {
#define DRM_FORMAT_ARGB16161616F fourcc_code('A', 'R', '4', 'H') /* [63:0] A:R:G:B 16:16:16:16 little endian */ #define DRM_FORMAT_ARGB16161616F fourcc_code('A', 'R', '4', 'H') /* [63:0] A:R:G:B 16:16:16:16 little endian */
#define DRM_FORMAT_ABGR16161616F fourcc_code('A', 'B', '4', 'H') /* [63:0] A:B:G:R 16:16:16:16 little endian */ #define DRM_FORMAT_ABGR16161616F fourcc_code('A', 'B', '4', 'H') /* [63:0] A:B:G:R 16:16:16:16 little endian */
#define DRM_FORMAT_R16F fourcc_code('R', ' ', ' ', 'H') /* [15:0] R 16 little endian */
#define DRM_FORMAT_GR1616F fourcc_code('G', 'R', ' ', 'H') /* [31:0] G:R 16:16 little endian */
#define DRM_FORMAT_BGR161616F fourcc_code('B', 'G', 'R', 'H') /* [47:0] B:G:R 16:16:16 little endian */
/*
* Floating point - 32b/component
* IEEE 754-2008 binary32 float
* [31:0] sign:exponent:mantissa 1:8:23
*/
#define DRM_FORMAT_R32F fourcc_code('R', ' ', ' ', 'F') /* [31:0] R 32 little endian */
#define DRM_FORMAT_GR3232F fourcc_code('G', 'R', ' ', 'F') /* [63:0] R:G 32:32 little endian */
#define DRM_FORMAT_BGR323232F fourcc_code('B', 'G', 'R', 'F') /* [95:0] R:G:B 32:32:32 little endian */
#define DRM_FORMAT_ABGR32323232F fourcc_code('A', 'B', '8', 'F') /* [127:0] R:G:B:A 32:32:32:32 little endian */
/* /*
* RGBA format with 10-bit components packed in 64-bit per pixel, with 6 bits * RGBA format with 10-bit components packed in 64-bit per pixel, with 6 bits
* of unused padding per component: * of unused padding per component: