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:
commit
17d081ef84
|
@ -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>;
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
...
|
|
@ -42,7 +42,6 @@ required:
|
|||
- compatible
|
||||
- port
|
||||
- reg
|
||||
- reset-gpios
|
||||
|
||||
additionalProperties: false
|
||||
|
||||
|
|
|
@ -64,10 +64,10 @@ properties:
|
|||
- description: Pixel clock for video port 0.
|
||||
- description: Pixel clock for video port 1.
|
||||
- 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:
|
||||
minItems: 5
|
||||
|
@ -77,10 +77,10 @@ properties:
|
|||
- const: dclk_vp0
|
||||
- const: dclk_vp1
|
||||
- const: dclk_vp2
|
||||
- const: dclk_vp3
|
||||
- const: pclk_vop
|
||||
- const: pll_hdmiphy0
|
||||
- const: pll_hdmiphy1
|
||||
- {}
|
||||
- {}
|
||||
- {}
|
||||
- {}
|
||||
|
||||
rockchip,grf:
|
||||
$ref: /schemas/types.yaml#/definitions/phandle
|
||||
|
@ -175,10 +175,24 @@ allOf:
|
|||
then:
|
||||
properties:
|
||||
clocks:
|
||||
maxItems: 5
|
||||
minItems: 5
|
||||
items:
|
||||
- {}
|
||||
- {}
|
||||
- {}
|
||||
- {}
|
||||
- {}
|
||||
- description: Alternative pixel clock provided by HDMI PHY PLL.
|
||||
|
||||
clock-names:
|
||||
maxItems: 5
|
||||
minItems: 5
|
||||
items:
|
||||
- {}
|
||||
- {}
|
||||
- {}
|
||||
- {}
|
||||
- {}
|
||||
- const: pll_hdmiphy0
|
||||
|
||||
interrupts:
|
||||
minItems: 4
|
||||
|
@ -208,11 +222,29 @@ allOf:
|
|||
properties:
|
||||
clocks:
|
||||
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:
|
||||
minItems: 7
|
||||
maxItems: 9
|
||||
items:
|
||||
- {}
|
||||
- {}
|
||||
- {}
|
||||
- {}
|
||||
- {}
|
||||
- const: dclk_vp3
|
||||
- const: pclk_vop
|
||||
- const: pll_hdmiphy0
|
||||
- const: pll_hdmiphy1
|
||||
|
||||
interrupts:
|
||||
maxItems: 1
|
||||
|
|
|
@ -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
|
||||
|
||||
...
|
|
@ -100,6 +100,24 @@ properties:
|
|||
For AM62A7 DSS, the port is tied off inside the SoC.
|
||||
For AM62L DSS, the DSS DPI output port node from video port 1
|
||||
or DSI Tx controller node connected to video port 1.
|
||||
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:
|
||||
$ref: /schemas/graph.yaml#/properties/port
|
||||
|
@ -121,6 +139,25 @@ properties:
|
|||
Input memory (from main memory to dispc) bandwidth limit in
|
||||
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:
|
||||
- if:
|
||||
properties:
|
||||
|
@ -129,6 +166,7 @@ allOf:
|
|||
const: ti,am62a7-dss
|
||||
then:
|
||||
properties:
|
||||
oldi-transmitters: false
|
||||
ports:
|
||||
properties:
|
||||
port@0: false
|
||||
|
@ -143,6 +181,22 @@ allOf:
|
|||
properties:
|
||||
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:
|
||||
- compatible
|
||||
- reg
|
||||
|
@ -190,3 +244,106 @@ examples:
|
|||
};
|
||||
};
|
||||
};
|
||||
|
||||
- |
|
||||
#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",
|
||||
"ovr1", "ovr2", "vp1", "vp2", "common1";
|
||||
power-domains = <&k3_pds 186 TI_SCI_PD_EXCLUSIVE>;
|
||||
clocks = <&k3_clks 186 6>,
|
||||
<&vp1_clock>,
|
||||
<&k3_clks 186 2>;
|
||||
clock-names = "fck", "vp1", "vp2";
|
||||
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 {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
port@0 {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
reg = <0>;
|
||||
dpi0_out0: endpoint@0 {
|
||||
reg = <0>;
|
||||
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>;
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
|
|
|
@ -398,6 +398,8 @@ patternProperties:
|
|||
description: Diodes, Inc.
|
||||
"^dioo,.*":
|
||||
description: Dioo Microcircuit Co., Ltd
|
||||
"^djn,.*":
|
||||
description: Shenzhen DJN Optronics Technology Co., Ltd
|
||||
"^dlc,.*":
|
||||
description: DLC Display Co., Ltd.
|
||||
"^dlg,.*":
|
||||
|
|
|
@ -7499,10 +7499,12 @@ M: Javier Martinez Canillas <javierm@redhat.com>
|
|||
L: dri-devel@lists.freedesktop.org
|
||||
S: Maintained
|
||||
T: git https://gitlab.freedesktop.org/drm/misc/kernel.git
|
||||
F: drivers/firmware/sysfb*.c
|
||||
F: drivers/gpu/drm/sysfb/
|
||||
F: drivers/video/aperture.c
|
||||
F: drivers/video/nomodeset.c
|
||||
F: include/linux/aperture.h
|
||||
F: include/linux/sysfb.h
|
||||
F: include/video/nomodeset.h
|
||||
|
||||
DRM DRIVER FOR GENERIC EDP PANELS
|
||||
|
@ -8232,6 +8234,7 @@ M: Tomi Valkeinen <tomi.valkeinen@ideasonboard.com>
|
|||
L: dri-devel@lists.freedesktop.org
|
||||
S: Maintained
|
||||
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,j721e-dss.yaml
|
||||
F: Documentation/devicetree/bindings/display/ti/ti,k2g-dss.yaml
|
||||
|
|
|
@ -20,6 +20,7 @@
|
|||
#include <linux/of.h>
|
||||
#include <linux/phy/phy.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/units.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);
|
||||
}
|
||||
|
||||
#ifndef MHZ
|
||||
#define MHZ (1000 * 1000)
|
||||
#endif
|
||||
|
||||
static unsigned long samsung_dsim_pll_find_pms(struct samsung_dsim *dsi,
|
||||
unsigned long fin,
|
||||
unsigned long fout,
|
||||
|
@ -575,8 +572,8 @@ static unsigned long samsung_dsim_pll_find_pms(struct samsung_dsim *dsi,
|
|||
u16 _m, best_m;
|
||||
u8 _s, best_s;
|
||||
|
||||
p_min = DIV_ROUND_UP(fin, (driver_data->pll_fin_max * MHZ));
|
||||
p_max = fin / (driver_data->pll_fin_min * MHZ);
|
||||
p_min = DIV_ROUND_UP(fin, (driver_data->pll_fin_max * HZ_PER_MHZ));
|
||||
p_max = fin / (driver_data->pll_fin_min * HZ_PER_MHZ);
|
||||
|
||||
for (_p = p_min; _p <= p_max; ++_p) {
|
||||
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;
|
||||
do_div(tmp, _p);
|
||||
if (tmp < driver_data->min_freq * MHZ ||
|
||||
tmp > driver_data->max_freq * MHZ)
|
||||
if (tmp < driver_data->min_freq * HZ_PER_MHZ ||
|
||||
tmp > driver_data->max_freq * HZ_PER_MHZ)
|
||||
continue;
|
||||
|
||||
tmp = (u64)_m * fin;
|
||||
|
@ -635,7 +632,7 @@ static unsigned long samsung_dsim_set_pll(struct samsung_dsim *dsi,
|
|||
* limit.
|
||||
*/
|
||||
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;
|
||||
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) {
|
||||
static const unsigned long freq_bands[] = {
|
||||
100 * MHZ, 120 * MHZ, 160 * MHZ, 200 * MHZ,
|
||||
270 * MHZ, 320 * MHZ, 390 * MHZ, 450 * MHZ,
|
||||
510 * MHZ, 560 * MHZ, 640 * MHZ, 690 * MHZ,
|
||||
770 * MHZ, 870 * MHZ, 950 * MHZ,
|
||||
100 * HZ_PER_MHZ, 120 * HZ_PER_MHZ, 160 * HZ_PER_MHZ,
|
||||
200 * HZ_PER_MHZ, 270 * HZ_PER_MHZ, 320 * HZ_PER_MHZ,
|
||||
390 * HZ_PER_MHZ, 450 * HZ_PER_MHZ, 510 * HZ_PER_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;
|
||||
|
||||
|
@ -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_clk = byte_clk / esc_div;
|
||||
|
||||
if (esc_clk > 20 * MHZ) {
|
||||
if (esc_clk > 20 * HZ_PER_MHZ) {
|
||||
++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
|
||||
* 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)
|
||||
reg |= DSIM_SYNC_INFORM;
|
||||
if (dsi->mode_flags & MIPI_DSI_MODE_VIDEO_BURST)
|
||||
|
@ -1236,16 +1232,10 @@ static void samsung_dsim_transfer_start(struct samsung_dsim *dsi)
|
|||
{
|
||||
unsigned long flags;
|
||||
struct samsung_dsim_transfer *xfer;
|
||||
bool start = false;
|
||||
|
||||
again:
|
||||
spin_lock_irqsave(&dsi->transfer_lock, flags);
|
||||
|
||||
if (list_empty(&dsi->transfer_list)) {
|
||||
spin_unlock_irqrestore(&dsi->transfer_lock, flags);
|
||||
return;
|
||||
}
|
||||
|
||||
while (!list_empty(&dsi->transfer_list)) {
|
||||
xfer = list_first_entry(&dsi->transfer_list,
|
||||
struct samsung_dsim_transfer, list);
|
||||
|
||||
|
@ -1267,12 +1257,9 @@ again:
|
|||
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)
|
||||
|
|
|
@ -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);
|
||||
|
||||
if (!test_bit(offset, pdata->gchip_output)) {
|
||||
dev_err(pdata->dev, "Ignoring GPIO set while input\n");
|
||||
return -EPERM;
|
||||
}
|
||||
|
||||
val &= 1;
|
||||
return regmap_update_bits(pdata->regmap, SN_GPIO_IO_REG,
|
||||
BIT(SN_GPIO_OUTPUT_SHIFT + offset),
|
||||
|
|
|
@ -3957,23 +3957,31 @@ EXPORT_SYMBOL(drm_dp_pcon_convert_rgb_to_ycbcr);
|
|||
* 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,
|
||||
u16 level)
|
||||
u32 level)
|
||||
{
|
||||
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 */
|
||||
if (!bl->aux_set)
|
||||
if (!(bl->aux_set || bl->luminance_set))
|
||||
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[1] = (level & 0x00ff);
|
||||
} else {
|
||||
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) {
|
||||
drm_err(aux->drm_dev,
|
||||
"%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.
|
||||
*/
|
||||
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;
|
||||
u8 dpcd_buf;
|
||||
|
@ -4046,6 +4054,9 @@ int drm_edp_backlight_enable(struct drm_dp_aux *aux, const struct drm_edp_backli
|
|||
else
|
||||
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) {
|
||||
ret = drm_dp_dpcd_write_byte(aux, DP_EDP_PWMGEN_BIT_COUNT, bl->pwmgen_bit_count);
|
||||
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)
|
||||
{
|
||||
int ret;
|
||||
u8 buf[2];
|
||||
u8 buf[3];
|
||||
u8 mode_reg;
|
||||
|
||||
ret = drm_dp_dpcd_read_byte(aux, DP_EDP_BACKLIGHT_MODE_SET_REGISTER, &mode_reg);
|
||||
|
@ -4226,9 +4237,28 @@ drm_edp_backlight_probe_state(struct drm_dp_aux *aux, struct drm_edp_backlight_i
|
|||
if (*current_mode == DP_EDP_BACKLIGHT_CONTROL_MODE_DPCD) {
|
||||
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) {
|
||||
ret = drm_dp_dpcd_read_data(aux, DP_EDP_PANEL_TARGET_LUMINANCE_VALUE,
|
||||
buf, sizeof(buf));
|
||||
if (ret < 0) {
|
||||
drm_dbg_kms(aux->drm_dev, "%s: Failed to read backlight level: %d\n",
|
||||
drm_dbg_kms(aux->drm_dev,
|
||||
"%s: Failed to read backlight level: %d\n",
|
||||
aux->name, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* Incase luminance is set we want to send the value back in nits but since
|
||||
* DP_EDP_PANEL_TARGET_LUMINANCE stores values in millinits we need to divide
|
||||
* 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;
|
||||
}
|
||||
|
@ -4238,6 +4268,7 @@ drm_edp_backlight_probe_state(struct drm_dp_aux *aux, struct drm_edp_backlight_i
|
|||
else
|
||||
return buf[0];
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* If we're not in DPCD control mode yet, the programmed brightness value is meaningless and
|
||||
|
@ -4251,10 +4282,12 @@ drm_edp_backlight_probe_state(struct drm_dp_aux *aux, struct drm_edp_backlight_i
|
|||
* interface.
|
||||
* @aux: The DP aux device to use for probing
|
||||
* @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
|
||||
* @edp_dpcd: A cached copy of the eDP DPCD
|
||||
* @current_level: Where to store the probed brightness level, if any
|
||||
* @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,
|
||||
* 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
|
||||
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 *current_level, u8 *current_mode)
|
||||
u32 *current_level, u8 *current_mode, bool need_luminance)
|
||||
{
|
||||
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;
|
||||
if (edp_dpcd[2] & DP_EDP_BACKLIGHT_BRIGHTNESS_BYTE_COUNT)
|
||||
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 */
|
||||
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,
|
||||
"%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);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (bl->luminance_set) {
|
||||
bl->max = max_luminance;
|
||||
} 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);
|
||||
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 backlight_properties props = { 0 };
|
||||
u16 current_level;
|
||||
u32 current_level;
|
||||
u8 current_mode;
|
||||
u8 edp_dpcd[EDP_DISPLAY_CTL_CAP_SIZE];
|
||||
int ret;
|
||||
|
@ -4391,8 +4433,8 @@ int drm_panel_dp_aux_backlight(struct drm_panel *panel, struct drm_dp_aux *aux)
|
|||
|
||||
bl->aux = aux;
|
||||
|
||||
ret = drm_edp_backlight_init(aux, &bl->info, 0, edp_dpcd,
|
||||
¤t_level, ¤t_mode);
|
||||
ret = drm_edp_backlight_init(aux, &bl->info, 0, 0, edp_dpcd,
|
||||
¤t_level, ¤t_mode, false);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
|
|
|
@ -295,6 +295,11 @@ EXPORT_SYMBOL(__devm_drm_bridge_alloc);
|
|||
*/
|
||||
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);
|
||||
|
||||
if (bridge->ops & DRM_BRIDGE_OP_HDMI)
|
||||
|
@ -342,6 +347,8 @@ void drm_bridge_remove(struct drm_bridge *bridge)
|
|||
mutex_unlock(&bridge_lock);
|
||||
|
||||
mutex_destroy(&bridge->hpd_mutex);
|
||||
|
||||
drm_bridge_put(bridge);
|
||||
}
|
||||
EXPORT_SYMBOL(drm_bridge_remove);
|
||||
|
||||
|
@ -407,11 +414,17 @@ int drm_bridge_attach(struct drm_encoder *encoder, struct drm_bridge *bridge,
|
|||
if (!encoder || !bridge)
|
||||
return -EINVAL;
|
||||
|
||||
if (previous && (!previous->dev || previous->encoder != encoder))
|
||||
return -EINVAL;
|
||||
drm_bridge_get(bridge);
|
||||
|
||||
if (bridge->dev)
|
||||
return -EBUSY;
|
||||
if (previous && (!previous->dev || previous->encoder != encoder)) {
|
||||
ret = -EINVAL;
|
||||
goto err_put_bridge;
|
||||
}
|
||||
|
||||
if (bridge->dev) {
|
||||
ret = -EBUSY;
|
||||
goto err_put_bridge;
|
||||
}
|
||||
|
||||
bridge->dev = encoder->dev;
|
||||
bridge->encoder = encoder;
|
||||
|
@ -460,6 +473,8 @@ err_reset_bridge:
|
|||
"failed to attach bridge %pOF to encoder %s\n",
|
||||
bridge->of_node, encoder->name);
|
||||
|
||||
err_put_bridge:
|
||||
drm_bridge_put(bridge);
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL(drm_bridge_attach);
|
||||
|
@ -480,6 +495,7 @@ void drm_bridge_detach(struct drm_bridge *bridge)
|
|||
|
||||
list_del(&bridge->chain_node);
|
||||
bridge->dev = NULL;
|
||||
drm_bridge_put(bridge);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
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
|
||||
* @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
|
||||
* @clip: Clip rectangle area to copy
|
||||
* @state: Transform and conversion state
|
||||
* @swab: Swap bytes
|
||||
*
|
||||
* 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
|
||||
|
@ -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,
|
||||
const struct iosys_map *src, const struct drm_framebuffer *fb,
|
||||
const struct drm_rect *clip, struct drm_format_conv_state *state,
|
||||
bool swab)
|
||||
const struct drm_rect *clip, struct drm_format_conv_state *state)
|
||||
{
|
||||
static const u8 dst_pixsize[DRM_FORMAT_MAX_PLANES] = {
|
||||
2,
|
||||
};
|
||||
|
||||
void (*xfrm_line)(void *dbuf, const void *sbuf, unsigned int npixels);
|
||||
|
||||
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);
|
||||
drm_fb_xfrm(dst, dst_pitch, dst_pixsize, src, fb, clip, false, state,
|
||||
drm_fb_xrgb8888_to_rgb565_line);
|
||||
}
|
||||
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)
|
||||
{
|
||||
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;
|
||||
} else if (fb_format == DRM_FORMAT_XRGB8888) {
|
||||
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;
|
||||
} else if (dst_format == DRM_FORMAT_XRGB1555) {
|
||||
drm_fb_xrgb8888_to_xrgb1555(dst, dst_pitch, src, fb, clip, state);
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
|
||||
#include <linux/bits.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/swab.h>
|
||||
|
||||
/*
|
||||
* 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);
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
return ((pix & 0x00f80000) >> 8) |
|
||||
|
|
|
@ -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_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_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_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 },
|
||||
|
|
|
@ -230,7 +230,13 @@ int mipi_dbi_buf_copy(void *dst, struct iosys_map *src, struct drm_framebuffer *
|
|||
case DRM_FORMAT_XRGB8888:
|
||||
switch (dbidev->pixel_format) {
|
||||
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;
|
||||
case DRM_FORMAT_RGB888:
|
||||
drm_fb_xrgb8888_to_rgb888(&dst_map, NULL, src, fb, clip, fmtcnv_state);
|
||||
|
|
|
@ -188,8 +188,13 @@ retry:
|
|||
} else if (format->format == DRM_FORMAT_RGB332) {
|
||||
drm_fb_xrgb8888_to_rgb332(&dst, NULL, src, fb, rect, fmtcnv_state);
|
||||
} else if (format->format == DRM_FORMAT_RGB565) {
|
||||
drm_fb_xrgb8888_to_rgb565(&dst, NULL, src, fb, rect, fmtcnv_state,
|
||||
gud_is_big_endian());
|
||||
if (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) {
|
||||
drm_fb_xrgb8888_to_rgb888(&dst, NULL, src, fb, rect, fmtcnv_state);
|
||||
} else {
|
||||
|
|
|
@ -475,31 +475,6 @@ static u32 intel_dp_aux_vesa_get_backlight(struct intel_connector *connector, en
|
|||
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
|
||||
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_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) {
|
||||
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_panel *panel = &connector->panel;
|
||||
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) {
|
||||
u32 pwm_level;
|
||||
|
@ -580,27 +538,15 @@ static int intel_dp_aux_vesa_setup_backlight(struct intel_connector *connector,
|
|||
&connector->base.display_info.luminance_range;
|
||||
struct intel_dp *intel_dp = intel_attached_dp(connector);
|
||||
struct intel_panel *panel = &connector->panel;
|
||||
u16 current_level;
|
||||
u32 current_level;
|
||||
u8 current_mode;
|
||||
int ret;
|
||||
|
||||
if (panel->backlight.edp.vesa.luminance_control_support) {
|
||||
if (luminance_range->max_luminance) {
|
||||
panel->backlight.max = luminance_range->max_luminance;
|
||||
panel->backlight.min = luminance_range->min_luminance;
|
||||
} else {
|
||||
panel->backlight.max = 512;
|
||||
panel->backlight.min = 0;
|
||||
}
|
||||
panel->backlight.level = intel_dp_aux_vesa_get_backlight(connector, 0);
|
||||
panel->backlight.enabled = panel->backlight.level != 0;
|
||||
drm_dbg_kms(display->drm,
|
||||
"[CONNECTOR:%d:%s] AUX VESA Nits backlight level is controlled through DPCD\n",
|
||||
connector->base.base.id, connector->base.name);
|
||||
} else {
|
||||
ret = drm_edp_backlight_init(&intel_dp->aux, &panel->backlight.edp.vesa.info,
|
||||
panel->vbt.backlight.pwm_freq_hz, intel_dp->edp_dpcd,
|
||||
¤t_level, ¤t_mode);
|
||||
luminance_range->max_luminance,
|
||||
panel->vbt.backlight.pwm_freq_hz,
|
||||
intel_dp->edp_dpcd, ¤t_level, ¤t_mode,
|
||||
false);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
|
@ -624,7 +570,20 @@ static int intel_dp_aux_vesa_setup_backlight(struct intel_connector *connector,
|
|||
}
|
||||
}
|
||||
|
||||
if (panel->backlight.edp.vesa.info.aux_set) {
|
||||
if (panel->backlight.edp.vesa.info.luminance_set) {
|
||||
if (luminance_range->max_luminance) {
|
||||
panel->backlight.max = panel->backlight.edp.vesa.info.max;
|
||||
panel->backlight.min = luminance_range->min_luminance;
|
||||
} else {
|
||||
panel->backlight.max = 512;
|
||||
panel->backlight.min = 0;
|
||||
}
|
||||
panel->backlight.level = intel_dp_aux_vesa_get_backlight(connector, 0);
|
||||
panel->backlight.enabled = panel->backlight.level != 0;
|
||||
drm_dbg_kms(display->drm,
|
||||
"[CONNECTOR:%d:%s] AUX VESA Nits backlight level is controlled through DPCD\n",
|
||||
connector->base.base.id, connector->base.name);
|
||||
} else 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) {
|
||||
|
@ -646,7 +605,6 @@ static int intel_dp_aux_vesa_setup_backlight(struct intel_connector *connector,
|
|||
panel->backlight.enabled = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
drm_dbg_kms(display->drm,
|
||||
"[CONNECTOR:%d:%s] Using AUX VESA interface for backlight control\n",
|
||||
|
|
|
@ -1839,7 +1839,7 @@ nv50_sor_atomic_enable(struct drm_encoder *encoder, struct drm_atomic_state *sta
|
|||
backlight = nv_connector->backlight;
|
||||
if (backlight && backlight->uses_dpcd)
|
||||
drm_edp_backlight_enable(&nv_connector->aux, &backlight->edp_info,
|
||||
(u16)backlight->dev->props.brightness);
|
||||
backlight->dev->props.brightness);
|
||||
#endif
|
||||
|
||||
break;
|
||||
|
|
|
@ -245,7 +245,7 @@ nv50_backlight_init(struct nouveau_backlight *bl,
|
|||
|
||||
if (nv_conn->type == DCB_CONNECTOR_eDP) {
|
||||
int ret;
|
||||
u16 current_level;
|
||||
u32 current_level;
|
||||
u8 edp_dpcd[EDP_DISPLAY_CTL_CAP_SIZE];
|
||||
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_conn->base.name);
|
||||
|
||||
ret = drm_edp_backlight_init(&nv_conn->aux, &bl->edp_info, 0, edp_dpcd,
|
||||
¤t_level, ¤t_mode);
|
||||
ret = drm_edp_backlight_init(&nv_conn->aux, &bl->edp_info,
|
||||
0, 0, edp_dpcd,
|
||||
¤t_level, ¤t_mode, false);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
|
|
|
@ -193,6 +193,16 @@ config DRM_PANEL_HIMAX_HX83112A
|
|||
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.
|
||||
|
||||
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
|
||||
tristate "HIMAX HX8394 MIPI-DSI LCD panels"
|
||||
depends on OF
|
||||
|
|
|
@ -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_HX83102) += panel-himax-hx83102.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_ILITEK_IL9322) += panel-ilitek-ili9322.o
|
||||
obj-$(CONFIG_DRM_PANEL_ILITEK_ILI9341) += panel-ilitek-ili9341.o
|
||||
|
|
|
@ -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', 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', 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', 0x142b, &delay_200_500_e80_d50, "N140HCA-EAC"),
|
||||
EDP_PANEL_ENTRY('C', 'M', 'N', 0x142e, &delay_200_500_e80_d50, "N140BGA-EA4"),
|
||||
|
|
|
@ -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");
|
|
@ -36,6 +36,7 @@ static inline struct raydium_rm67200 *to_raydium_rm67200(struct drm_panel *panel
|
|||
|
||||
static void raydium_rm67200_reset(struct raydium_rm67200 *ctx)
|
||||
{
|
||||
if (ctx->reset_gpio) {
|
||||
gpiod_set_value_cansleep(ctx->reset_gpio, 0);
|
||||
msleep(60);
|
||||
gpiod_set_value_cansleep(ctx->reset_gpio, 1);
|
||||
|
@ -43,6 +44,7 @@ static void raydium_rm67200_reset(struct raydium_rm67200 *ctx)
|
|||
gpiod_set_value_cansleep(ctx->reset_gpio, 0);
|
||||
msleep(60);
|
||||
}
|
||||
}
|
||||
|
||||
static void raydium_rm67200_write(struct mipi_dsi_multi_context *ctx,
|
||||
u8 arg1, u8 arg2)
|
||||
|
@ -318,6 +320,7 @@ static void w552793baa_setup(struct mipi_dsi_multi_context *ctx)
|
|||
static int raydium_rm67200_prepare(struct drm_panel *panel)
|
||||
{
|
||||
struct raydium_rm67200 *ctx = to_raydium_rm67200(panel);
|
||||
struct mipi_dsi_multi_context mctx = { .dsi = ctx->dsi };
|
||||
int ret;
|
||||
|
||||
ret = regulator_bulk_enable(ctx->num_supplies, ctx->supplies);
|
||||
|
@ -328,6 +331,12 @@ static int raydium_rm67200_prepare(struct drm_panel *panel)
|
|||
|
||||
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;
|
||||
}
|
||||
|
||||
|
@ -343,20 +352,6 @@ static int raydium_rm67200_unprepare(struct drm_panel *panel)
|
|||
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)
|
||||
{
|
||||
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,
|
||||
.unprepare = raydium_rm67200_unprepare,
|
||||
.get_modes = raydium_rm67200_get_modes,
|
||||
.enable = raydium_rm67200_enable,
|
||||
.disable = raydium_rm67200_disable,
|
||||
};
|
||||
|
||||
|
@ -409,7 +403,7 @@ static int raydium_rm67200_probe(struct mipi_dsi_device *dsi)
|
|||
if (ret < 0)
|
||||
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))
|
||||
return dev_err_probe(dev, PTR_ERR(ctx->reset_gpio),
|
||||
"Failed to get reset-gpios\n");
|
||||
|
@ -470,6 +464,7 @@ static const struct raydium_rm67200_panel_info w552793baa_info = {
|
|||
.vtotal = 1952,
|
||||
.width_mm = 68, /* 68.04mm */
|
||||
.height_mm = 121, /* 120.96mm */
|
||||
.flags = DRM_MODE_FLAG_NVSYNC | DRM_MODE_FLAG_NHSYNC,
|
||||
.type = DRM_MODE_TYPE_DRIVER,
|
||||
},
|
||||
.regulators = w552793baa_regulators,
|
||||
|
|
|
@ -244,7 +244,7 @@ static const struct s6d7aa0_panel_desc s6d7aa0_lsl080al02_desc = {
|
|||
.init_func = s6d7aa0_lsl080al02_init,
|
||||
.off_func = s6d7aa0_lsl080al02_off,
|
||||
.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,
|
||||
|
||||
.has_backlight = false,
|
||||
|
|
|
@ -992,7 +992,7 @@ static int s6e8aa0_probe(struct mipi_dsi_device *dsi)
|
|||
dsi->lanes = 4;
|
||||
dsi->format = MIPI_DSI_FMT_RGB888;
|
||||
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);
|
||||
if (ret < 0)
|
||||
|
|
|
@ -297,8 +297,9 @@ int panthor_gpu_block_power_on(struct panthor_device *ptdev,
|
|||
|
||||
gpu_write64(ptdev, pwron_reg, mask);
|
||||
|
||||
ret = gpu_read64_relaxed_poll_timeout(ptdev, pwrtrans_reg, val,
|
||||
!(mask & val), 100, timeout_us);
|
||||
ret = gpu_read64_relaxed_poll_timeout(ptdev, rdy_reg, val,
|
||||
(mask & val) == val,
|
||||
100, timeout_us);
|
||||
if (ret) {
|
||||
drm_err(&ptdev->base, "timeout waiting on %s:%llx readiness",
|
||||
blk_name, mask);
|
||||
|
|
|
@ -200,12 +200,36 @@ static struct dma_fence *mock_sched_run_job(struct drm_sched_job *sched_job)
|
|||
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
|
||||
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);
|
||||
unsigned long flags;
|
||||
|
||||
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;
|
||||
}
|
||||
|
|
|
@ -735,13 +735,13 @@ static void drm_test_fb_xrgb8888_to_rgb565(struct kunit *test)
|
|||
NULL : &result->dst_pitch;
|
||||
|
||||
drm_fb_xrgb8888_to_rgb565(&dst, dst_pitch, &src, &fb, ¶ms->clip,
|
||||
&fmtcnv_state, false);
|
||||
&fmtcnv_state);
|
||||
buf = le16buf_to_cpu(test, (__force const __le16 *)buf, dst_size / sizeof(__le16));
|
||||
KUNIT_EXPECT_MEMEQ(test, buf, result->expected, dst_size);
|
||||
|
||||
buf = dst.vaddr; /* restore original value of buf */
|
||||
drm_fb_xrgb8888_to_rgb565(&dst, &result->dst_pitch, &src, &fb, ¶ms->clip,
|
||||
&fmtcnv_state, true);
|
||||
drm_fb_xrgb8888_to_rgb565be(&dst, &result->dst_pitch, &src, &fb, ¶ms->clip,
|
||||
&fmtcnv_state);
|
||||
buf = le16buf_to_cpu(test, (__force const __le16 *)buf, dst_size / sizeof(__le16));
|
||||
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);
|
||||
|
||||
drm_fb_xrgb8888_to_rgb565(&dst, dst_pitch, &src, &fb, ¶ms->clip,
|
||||
&fmtcnv_state, false);
|
||||
&fmtcnv_state);
|
||||
buf = le16buf_to_cpu(test, (__force const __le16 *)buf, dst_size / sizeof(__le16));
|
||||
KUNIT_EXPECT_MEMEQ(test, buf, result->expected, dst_size);
|
||||
}
|
||||
|
|
|
@ -7,6 +7,7 @@ tidss-y := tidss_crtc.o \
|
|||
tidss_irq.o \
|
||||
tidss_plane.o \
|
||||
tidss_scale_coefs.o \
|
||||
tidss_dispc.o
|
||||
tidss_dispc.o \
|
||||
tidss_oldi.o
|
||||
|
||||
obj-$(CONFIG_DRM_TIDSS) += tidss.o
|
||||
|
|
|
@ -146,7 +146,7 @@ static const u16 tidss_am65x_common_regs[DISPC_COMMON_REG_TABLE_LEN] = {
|
|||
const struct dispc_features dispc_am65x_feats = {
|
||||
.max_pclk_khz = {
|
||||
[DISPC_VP_DPI] = 165000,
|
||||
[DISPC_VP_OLDI] = 165000,
|
||||
[DISPC_VP_OLDI_AM65X] = 165000,
|
||||
},
|
||||
|
||||
.scaling = {
|
||||
|
@ -176,7 +176,7 @@ const struct dispc_features dispc_am65x_feats = {
|
|||
.vp_name = { "vp1", "vp2" },
|
||||
.ovr_name = { "ovr1", "ovr2" },
|
||||
.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 = {
|
||||
.has_ctm = true,
|
||||
|
@ -491,7 +491,7 @@ struct dispc_device {
|
|||
void __iomem *base_ovr[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];
|
||||
|
||||
|
@ -566,6 +566,29 @@ static u32 dispc_vp_read(struct dispc_device *dispc, u32 hw_videoport, u16 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
|
||||
* 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 {
|
||||
u32 bus_fmt;
|
||||
u32 data_width;
|
||||
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[] = {
|
||||
|
@ -1066,7 +1087,7 @@ int dispc_vp_bus_check(struct dispc_device *dispc, u32 hw_videoport,
|
|||
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) {
|
||||
dev_dbg(dispc->dev, "%s: %s is not OLDI-port\n",
|
||||
__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;
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
regmap_update_bits(dispc->oldi_io_ctrl, OLDI_DAT0_IO_CTRL,
|
||||
OLDI_PWRDN_TX, val);
|
||||
regmap_update_bits(dispc->oldi_io_ctrl, OLDI_DAT1_IO_CTRL,
|
||||
OLDI_PWRDN_TX, val);
|
||||
regmap_update_bits(dispc->oldi_io_ctrl, OLDI_DAT2_IO_CTRL,
|
||||
OLDI_PWRDN_TX, val);
|
||||
regmap_update_bits(dispc->oldi_io_ctrl, OLDI_DAT3_IO_CTRL,
|
||||
OLDI_PWRDN_TX, val);
|
||||
regmap_update_bits(dispc->oldi_io_ctrl, OLDI_CLK_IO_CTRL,
|
||||
OLDI_PWRDN_TX, val);
|
||||
regmap_update_bits(dispc->am65x_oldi_io_ctrl, AM65X_OLDI_DAT0_IO_CTRL,
|
||||
AM65X_OLDI_PWRDN_TX, val);
|
||||
regmap_update_bits(dispc->am65x_oldi_io_ctrl, AM65X_OLDI_DAT1_IO_CTRL,
|
||||
AM65X_OLDI_PWRDN_TX, val);
|
||||
regmap_update_bits(dispc->am65x_oldi_io_ctrl, AM65X_OLDI_DAT2_IO_CTRL,
|
||||
AM65X_OLDI_PWRDN_TX, val);
|
||||
regmap_update_bits(dispc->am65x_oldi_io_ctrl, AM65X_OLDI_DAT3_IO_CTRL,
|
||||
AM65X_OLDI_PWRDN_TX, val);
|
||||
regmap_update_bits(dispc->am65x_oldi_io_ctrl, AM65X_OLDI_CLK_IO_CTRL,
|
||||
AM65X_OLDI_PWRDN_TX, val);
|
||||
}
|
||||
|
||||
static void dispc_set_num_datalines(struct dispc_device *dispc,
|
||||
|
@ -1121,7 +1142,7 @@ static void dispc_set_num_datalines(struct dispc_device *dispc,
|
|||
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)
|
||||
{
|
||||
u32 oldi_cfg = 0;
|
||||
|
@ -1141,7 +1162,7 @@ static void dispc_enable_oldi(struct dispc_device *dispc, u32 hw_videoport,
|
|||
|
||||
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 */
|
||||
|
||||
|
@ -1170,10 +1191,10 @@ void dispc_vp_prepare(struct dispc_device *dispc, u32 hw_videoport,
|
|||
if (WARN_ON(!fmt))
|
||||
return;
|
||||
|
||||
if (dispc->feat->vp_bus_type[hw_videoport] == DISPC_VP_OLDI) {
|
||||
dispc_oldi_tx_power(dispc, true);
|
||||
if (dispc->feat->vp_bus_type[hw_videoport] == DISPC_VP_OLDI_AM65X) {
|
||||
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;
|
||||
|
||||
/* 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;
|
||||
|
||||
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)
|
||||
{
|
||||
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_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
|
||||
* and the effective rate resulting from calculating the clock divider value.
|
||||
*/
|
||||
static
|
||||
unsigned int dispc_pclk_diff(unsigned long rate, unsigned long real_rate)
|
||||
{
|
||||
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,
|
||||
struct dispc_device *dispc)
|
||||
{
|
||||
dispc->oldi_io_ctrl =
|
||||
dispc->am65x_oldi_io_ctrl =
|
||||
syscon_regmap_lookup_by_phandle(dev->of_node,
|
||||
"ti,am65x-oldi-io-ctrl");
|
||||
if (PTR_ERR(dispc->oldi_io_ctrl) == -ENODEV) {
|
||||
dispc->oldi_io_ctrl = NULL;
|
||||
} else if (IS_ERR(dispc->oldi_io_ctrl)) {
|
||||
if (PTR_ERR(dispc->am65x_oldi_io_ctrl) == -ENODEV) {
|
||||
dispc->am65x_oldi_io_ctrl = NULL;
|
||||
} else if (IS_ERR(dispc->am65x_oldi_io_ctrl)) {
|
||||
dev_err(dev, "%s: syscon_regmap_lookup_by_phandle failed %ld\n",
|
||||
__func__, PTR_ERR(dispc->oldi_io_ctrl));
|
||||
return PTR_ERR(dispc->oldi_io_ctrl);
|
||||
__func__, PTR_ERR(dispc->am65x_oldi_io_ctrl));
|
||||
return PTR_ERR(dispc->am65x_oldi_io_ctrl);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -58,7 +58,7 @@ struct dispc_errata {
|
|||
|
||||
enum dispc_vp_bus_type {
|
||||
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_TIED_OFF, /* Tied off / Unavailable */
|
||||
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_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);
|
||||
dispc_irq_t dispc_read_and_clear_irqstatus(struct dispc_device *dispc);
|
||||
|
||||
|
|
|
@ -226,18 +226,35 @@ enum dispc_common_regs {
|
|||
#define DISPC_VP_DSS_DMA_THREADSIZE 0x170 /* 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
|
||||
* from CTRL_MMR0, there the syscon regmap should map 0x14 bytes from
|
||||
* CTRLMMR0P1_OLDI_DAT0_IO_CTRL to CTRLMMR0P1_OLDI_CLK_IO_CTRL
|
||||
* register range.
|
||||
*/
|
||||
#define OLDI_DAT0_IO_CTRL 0x00
|
||||
#define OLDI_DAT1_IO_CTRL 0x04
|
||||
#define OLDI_DAT2_IO_CTRL 0x08
|
||||
#define OLDI_DAT3_IO_CTRL 0x0C
|
||||
#define OLDI_CLK_IO_CTRL 0x10
|
||||
#define AM65X_OLDI_DAT0_IO_CTRL 0x00
|
||||
#define AM65X_OLDI_DAT1_IO_CTRL 0x04
|
||||
#define AM65X_OLDI_DAT2_IO_CTRL 0x08
|
||||
#define AM65X_OLDI_DAT3_IO_CTRL 0x0C
|
||||
#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 */
|
||||
|
|
|
@ -24,6 +24,7 @@
|
|||
#include "tidss_drv.h"
|
||||
#include "tidss_kms.h"
|
||||
#include "tidss_irq.h"
|
||||
#include "tidss_oldi.h"
|
||||
|
||||
/* Power management */
|
||||
|
||||
|
@ -147,6 +148,10 @@ static int tidss_probe(struct platform_device *pdev)
|
|||
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_set_autosuspend_delay(dev, 1000);
|
||||
|
@ -203,6 +208,8 @@ err_runtime_suspend:
|
|||
pm_runtime_dont_use_autosuspend(dev);
|
||||
pm_runtime_disable(dev);
|
||||
|
||||
tidss_oldi_deinit(tidss);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
@ -227,6 +234,8 @@ static void tidss_remove(struct platform_device *pdev)
|
|||
pm_runtime_dont_use_autosuspend(dev);
|
||||
pm_runtime_disable(dev);
|
||||
|
||||
tidss_oldi_deinit(tidss);
|
||||
|
||||
/* devm allocated dispc goes away with the dev so mark it NULL */
|
||||
dispc_remove(tidss);
|
||||
|
||||
|
|
|
@ -11,8 +11,10 @@
|
|||
|
||||
#define TIDSS_MAX_PORTS 4
|
||||
#define TIDSS_MAX_PLANES 4
|
||||
#define TIDSS_MAX_OLDI_TXES 2
|
||||
|
||||
typedef u32 dispc_irq_t;
|
||||
struct tidss_oldi;
|
||||
|
||||
struct tidss_device {
|
||||
struct drm_device ddev; /* DRM device for DSS */
|
||||
|
@ -27,6 +29,9 @@ struct tidss_device {
|
|||
unsigned int num_planes;
|
||||
struct drm_plane *planes[TIDSS_MAX_PLANES];
|
||||
|
||||
unsigned int num_oldis;
|
||||
struct tidss_oldi *oldis[TIDSS_MAX_OLDI_TXES];
|
||||
|
||||
unsigned int irq;
|
||||
|
||||
/* protects the irq masks field and irqenable/irqstatus registers */
|
||||
|
|
|
@ -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);
|
||||
|
||||
switch (feat->vp_bus_type[i]) {
|
||||
case DISPC_VP_OLDI:
|
||||
case DISPC_VP_OLDI_AM65X:
|
||||
enc_type = DRM_MODE_ENCODER_LVDS;
|
||||
conn_type = DRM_MODE_CONNECTOR_LVDS;
|
||||
break;
|
||||
|
|
|
@ -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;
|
||||
}
|
|
@ -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__ */
|
|
@ -526,11 +526,11 @@ static s64 ttm_bo_evict_cb(struct ttm_lru_walk *walk, struct ttm_buffer_object *
|
|||
return 0;
|
||||
|
||||
if (bo->deleted) {
|
||||
lret = ttm_bo_wait_ctx(bo, walk->ctx);
|
||||
lret = ttm_bo_wait_ctx(bo, walk->arg.ctx);
|
||||
if (!lret)
|
||||
ttm_bo_cleanup_memtype_use(bo);
|
||||
} else {
|
||||
lret = ttm_bo_evict(bo, walk->ctx);
|
||||
lret = ttm_bo_evict(bo, walk->arg.ctx);
|
||||
}
|
||||
|
||||
if (lret)
|
||||
|
@ -566,8 +566,10 @@ static int ttm_bo_evict_alloc(struct ttm_device *bdev,
|
|||
struct ttm_bo_evict_walk evict_walk = {
|
||||
.walk = {
|
||||
.ops = &ttm_evict_walk_ops,
|
||||
.arg = {
|
||||
.ctx = ctx,
|
||||
.ticket = ticket,
|
||||
}
|
||||
},
|
||||
.place = place,
|
||||
.evictor = evictor,
|
||||
|
@ -576,7 +578,7 @@ static int ttm_bo_evict_alloc(struct ttm_device *bdev,
|
|||
};
|
||||
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);
|
||||
|
||||
/* 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 */
|
||||
evict_walk.try_low = evict_walk.hit_low = false;
|
||||
/* If ticket-locking, repeat while making progress. */
|
||||
evict_walk.walk.trylock_only = false;
|
||||
evict_walk.walk.arg.trylock_only = false;
|
||||
|
||||
retry:
|
||||
do {
|
||||
/* 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;
|
||||
lret = ttm_lru_walk_for_evict(&evict_walk.walk, bdev, man, 1);
|
||||
} 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_bo_swapout_walk *swapout_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;
|
||||
|
||||
/*
|
||||
|
@ -1217,9 +1219,11 @@ s64 ttm_bo_swapout(struct ttm_device *bdev, struct ttm_operation_ctx *ctx,
|
|||
struct ttm_bo_swapout_walk swapout_walk = {
|
||||
.walk = {
|
||||
.ops = &ttm_swap_ops,
|
||||
.arg = {
|
||||
.ctx = ctx,
|
||||
.trylock_only = true,
|
||||
},
|
||||
},
|
||||
.gfp_flags = gfp_flags,
|
||||
};
|
||||
|
||||
|
|
|
@ -773,14 +773,15 @@ error_destroy_tt:
|
|||
return ret;
|
||||
}
|
||||
|
||||
static bool ttm_lru_walk_trylock(struct ttm_operation_ctx *ctx,
|
||||
struct ttm_buffer_object *bo,
|
||||
bool *needs_unlock)
|
||||
static bool ttm_lru_walk_trylock(struct ttm_bo_lru_cursor *curs,
|
||||
struct ttm_buffer_object *bo)
|
||||
{
|
||||
*needs_unlock = false;
|
||||
struct ttm_operation_ctx *ctx = curs->arg->ctx;
|
||||
|
||||
curs->needs_unlock = false;
|
||||
|
||||
if (dma_resv_trylock(bo->base.resv)) {
|
||||
*needs_unlock = true;
|
||||
curs->needs_unlock = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -792,27 +793,27 @@ static bool ttm_lru_walk_trylock(struct ttm_operation_ctx *ctx,
|
|||
return false;
|
||||
}
|
||||
|
||||
static int ttm_lru_walk_ticketlock(struct ttm_lru_walk *walk,
|
||||
struct ttm_buffer_object *bo,
|
||||
bool *needs_unlock)
|
||||
static int ttm_lru_walk_ticketlock(struct ttm_bo_lru_cursor *curs,
|
||||
struct ttm_buffer_object *bo)
|
||||
{
|
||||
struct ttm_lru_walk_arg *arg = curs->arg;
|
||||
struct dma_resv *resv = bo->base.resv;
|
||||
int ret;
|
||||
|
||||
if (walk->ctx->interruptible)
|
||||
ret = dma_resv_lock_interruptible(resv, walk->ticket);
|
||||
if (arg->ctx->interruptible)
|
||||
ret = dma_resv_lock_interruptible(resv, arg->ticket);
|
||||
else
|
||||
ret = dma_resv_lock(resv, walk->ticket);
|
||||
ret = dma_resv_lock(resv, arg->ticket);
|
||||
|
||||
if (!ret) {
|
||||
*needs_unlock = true;
|
||||
curs->needs_unlock = true;
|
||||
/*
|
||||
* Only a single ticketlock per loop. Ticketlocks are prone
|
||||
* to return -EDEADLK causing the eviction to fail, so
|
||||
* after waiting for the ticketlock, revert back to
|
||||
* trylocking for this walk.
|
||||
*/
|
||||
walk->ticket = NULL;
|
||||
arg->ticket = NULL;
|
||||
} else if (ret == -EDEADLK) {
|
||||
/* Caller needs to exit the ww transaction. */
|
||||
ret = -ENOSPC;
|
||||
|
@ -821,12 +822,6 @@ static int ttm_lru_walk_ticketlock(struct ttm_lru_walk *walk,
|
|||
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
|
||||
* 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,
|
||||
struct ttm_resource_manager *man, s64 target)
|
||||
{
|
||||
struct ttm_resource_cursor cursor;
|
||||
struct ttm_resource *res;
|
||||
struct ttm_bo_lru_cursor cursor;
|
||||
struct ttm_buffer_object *bo;
|
||||
s64 progress = 0;
|
||||
s64 lret;
|
||||
|
||||
spin_lock(&bdev->lru_lock);
|
||||
ttm_resource_cursor_init(&cursor, man);
|
||||
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)
|
||||
ttm_bo_lru_for_each_reserved_guarded(&cursor, man, &walk->arg, bo) {
|
||||
lret = walk->ops->process_bo(walk, bo);
|
||||
|
||||
ttm_lru_walk_unlock(bo, bo_needs_unlock);
|
||||
ttm_bo_put(bo);
|
||||
if (lret == -EBUSY || lret == -EALREADY)
|
||||
lret = 0;
|
||||
progress = (lret < 0) ? lret : progress + lret;
|
||||
|
||||
spin_lock(&bdev->lru_lock);
|
||||
if (progress < 0 || progress >= target)
|
||||
break;
|
||||
}
|
||||
ttm_resource_cursor_fini(&cursor);
|
||||
spin_unlock(&bdev->lru_lock);
|
||||
if (IS_ERR(bo))
|
||||
return PTR_ERR(bo);
|
||||
|
||||
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
|
||||
* @curs: The ttm_bo_lru_cursor to initialize.
|
||||
* @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
|
||||
* or prelocked buffer objects are available as detailed by
|
||||
* @ctx::resv and @ctx::allow_res_evict. Ticketlocking is not
|
||||
* supported.
|
||||
* Initialize a struct ttm_bo_lru_cursor.
|
||||
*
|
||||
* Return: Pointer to @curs. The function does not fail.
|
||||
*/
|
||||
struct ttm_bo_lru_cursor *
|
||||
ttm_bo_lru_cursor_init(struct ttm_bo_lru_cursor *curs,
|
||||
struct ttm_resource_manager *man,
|
||||
struct ttm_operation_ctx *ctx)
|
||||
struct ttm_lru_walk_arg *arg)
|
||||
{
|
||||
memset(curs, 0, sizeof(*curs));
|
||||
ttm_resource_cursor_init(&curs->res_curs, man);
|
||||
curs->ctx = ctx;
|
||||
curs->arg = arg;
|
||||
|
||||
return curs;
|
||||
}
|
||||
EXPORT_SYMBOL(ttm_bo_lru_cursor_init);
|
||||
|
||||
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))
|
||||
return NULL;
|
||||
ttm_bo_lru_cursor_cleanup_bo(curs);
|
||||
|
||||
spin_lock(lru_lock);
|
||||
for (;;) {
|
||||
int mem_type, ret = 0;
|
||||
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);
|
||||
return NULL;
|
||||
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);
|
||||
}
|
||||
|
||||
spin_unlock(lru_lock);
|
||||
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)
|
||||
{
|
||||
spinlock_t *lru_lock = &curs->res_curs.man->bdev->lru_lock;
|
||||
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;
|
||||
return __ttm_bo_lru_cursor_next(curs);
|
||||
}
|
||||
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)
|
||||
{
|
||||
spinlock_t *lru_lock = &curs->res_curs.man->bdev->lru_lock;
|
||||
struct ttm_buffer_object *bo;
|
||||
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);
|
||||
ttm_bo_lru_cursor_cleanup_bo(curs);
|
||||
return __ttm_bo_lru_cursor_next(curs);
|
||||
}
|
||||
EXPORT_SYMBOL(ttm_bo_lru_cursor_first);
|
||||
|
||||
|
|
|
@ -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",
|
||||
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.pin_count,
|
||||
kref_read(&bo->tbo.base.refcount),
|
||||
kref_read(&bo->tbo.kref));
|
||||
kref_read(&bo->tbo.base.refcount));
|
||||
seq_puts(m, "\n");
|
||||
}
|
||||
|
||||
|
|
|
@ -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_bo_lru_cursor curs;
|
||||
struct ttm_buffer_object *ttm_bo;
|
||||
struct ttm_lru_walk_arg arg = {
|
||||
.ctx = ctx,
|
||||
.trylock_only = true,
|
||||
};
|
||||
|
||||
if (!man || !man->use_tt)
|
||||
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))
|
||||
continue;
|
||||
|
||||
|
@ -82,6 +86,8 @@ static s64 xe_shrinker_walk(struct xe_device *xe,
|
|||
if (*scanned >= to_scan)
|
||||
break;
|
||||
}
|
||||
/* Trylocks should never error, just fail. */
|
||||
xe_assert(xe, !IS_ERR(ttm_bo));
|
||||
}
|
||||
|
||||
return freed;
|
||||
|
|
|
@ -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?
|
||||
* @aux_enable: Does the panel support the AUX enable cap?
|
||||
* @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
|
||||
* 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 {
|
||||
u8 pwmgen_bit_count;
|
||||
u8 pwm_freq_pre_divider;
|
||||
u16 max;
|
||||
u32 max;
|
||||
|
||||
bool lsb_reg_used : 1;
|
||||
bool aux_enable : 1;
|
||||
bool aux_set : 1;
|
||||
bool luminance_set : 1;
|
||||
};
|
||||
|
||||
int
|
||||
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 *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,
|
||||
u16 level);
|
||||
u32 level);
|
||||
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);
|
||||
|
||||
#if IS_ENABLED(CONFIG_DRM_KMS_HELPER) && (IS_BUILTIN(CONFIG_BACKLIGHT_CLASS_DEVICE) || \
|
||||
|
|
|
@ -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);
|
||||
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 drm_rect *clip, struct drm_format_conv_state *state,
|
||||
bool swab);
|
||||
const struct drm_rect *clip, struct drm_format_conv_state *state);
|
||||
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,
|
||||
const struct iosys_map *src, const struct drm_framebuffer *fb,
|
||||
const struct drm_rect *clip, struct drm_format_conv_state *state);
|
||||
|
|
|
@ -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)
|
||||
/* disable hsync-active area */
|
||||
#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 */
|
||||
#define MIPI_DSI_MODE_NO_EOT_PACKET BIT(9)
|
||||
/* device supports non-continuous clock behavior (DSI spec 5.6.1) */
|
||||
|
|
|
@ -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 {
|
||||
/** @ops: Pointer to the ops structure. */
|
||||
const struct ttm_lru_walk_ops *ops;
|
||||
struct ttm_lru_walk_arg {
|
||||
/** @ctx: Pointer to the struct ttm_operation_ctx. */
|
||||
struct ttm_operation_ctx *ctx;
|
||||
/** @ticket: The struct ww_acquire_ctx if any. */
|
||||
|
@ -220,6 +218,16 @@ struct ttm_lru_walk {
|
|||
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,
|
||||
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 {
|
||||
/** @res_curs: Embedded struct ttm_resource_cursor. */
|
||||
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,
|
||||
* NULL otherwise.
|
||||
|
@ -481,6 +484,8 @@ struct ttm_bo_lru_cursor {
|
|||
* unlock before the next iteration or after loop exit.
|
||||
*/
|
||||
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);
|
||||
|
@ -488,7 +493,7 @@ void ttm_bo_lru_cursor_fini(struct ttm_bo_lru_cursor *curs);
|
|||
struct ttm_bo_lru_cursor *
|
||||
ttm_bo_lru_cursor_init(struct ttm_bo_lru_cursor *curs,
|
||||
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);
|
||||
|
||||
|
@ -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 *,
|
||||
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_operation_ctx *ctx);
|
||||
struct ttm_lru_walk_arg *arg);
|
||||
static inline void *
|
||||
class_ttm_bo_lru_cursor_lock_ptr(class_ttm_bo_lru_cursor_t *_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.
|
||||
* @_cursor: struct ttm_bo_lru_cursor to use for the iteration.
|
||||
* @_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
|
||||
* 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
|
||||
* example a return or break statement. Exiting the loop will also unlock
|
||||
* (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) \
|
||||
scoped_guard(ttm_bo_lru_cursor, _cursor, _man, _ctx) \
|
||||
for ((_bo) = ttm_bo_lru_cursor_first(_cursor); (_bo); \
|
||||
#define ttm_bo_lru_for_each_reserved_guarded(_cursor, _man, _arg, _bo) \
|
||||
scoped_guard(ttm_bo_lru_cursor, _cursor, _man, _arg) \
|
||||
for ((_bo) = ttm_bo_lru_cursor_first(_cursor); \
|
||||
!IS_ERR_OR_NULL(_bo); \
|
||||
(_bo) = ttm_bo_lru_cursor_next(_cursor))
|
||||
|
||||
#endif
|
||||
|
|
|
@ -7,9 +7,13 @@
|
|||
* 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>
|
||||
|
||||
struct device;
|
||||
struct platform_device;
|
||||
struct screen_info;
|
||||
|
||||
enum {
|
||||
|
|
|
@ -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_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 */
|
||||
#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 */
|
||||
|
@ -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 */
|
||||
|
||||
/*
|
||||
* Floating point 64bpp RGB
|
||||
* Half-Floating point - 16b/component
|
||||
* IEEE 754-2008 binary16 half-precision float
|
||||
* [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_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
|
||||
* of unused padding per component:
|
||||
|
|
Loading…
Reference in New Issue