drm-misc-next for v7.4:

Driver Changes:
 
 host1x:
 - support partitioning
 
 panel:
 - fix fallout from bridge-related changes
 
 tegra:
 - improve syncobj handling on submit
 -----BEGIN PGP SIGNATURE-----
 
 iQFPBAABCgA5FiEEchf7rIzpz2NEoWjlaA3BHVMLeiMFAmq/nSUbFIAAAAAABAAO
 bWFudTIsMi41KzEuMTIsMiwyAAoJEGgNwR1TC3ojrqwH/jHm9C9kKKYzuqGYMPXg
 6NEYXWdSWKwT3ASY6Me/dHDTPK9ceqo17W3wMY9LVJl0jWqTZpsLVGx9zwHsmVrE
 Z28z4kFbqF61h7FnQDPPPtcvLzAVz3/FzdcwQD1EjX9EJuOIaM129gHjrPSTxh4x
 ZXQXrKU7F5TBDY7mM3PKnM+43FIkdJ8ICIQJ+1n/dVtF2EBW3CqJSDOzWbtDzKge
 UPG2BLbv5ZHDdsg7ukLiZek4bb6DxDWzxL23X9TnI9BgzUPHdtPODyCPr2GvV7nG
 oDiDU0llBMbl6x/RXlrGrsjxE4RCQmw8WOmY4ILDh8RQh57q73Is5AtybdV4bET4
 /KI=
 =jTkR
 -----END PGP SIGNATURE-----

Merge tag 'drm-misc-next-2026-10-02' of https://gitlab.freedesktop.org/drm/misc/kernel into drm-next

drm-misc-next for v7.4:

Cross-subsystem Changes:

Documentation:
- Fix indention in display-related DT bindings

Core Changes:

- Provide helpers for calculating absolute and relative timeouts

clients:
- fbdev: Move helpers from drm_kms_helper.ko to drm_client_lib.ko

display:
- dp/mst: Improve DPCD compliance

sched:
- fix locking

Driver changes:
amdxdna:
- Support AIE2 rev 9
- Fix UAF access in scheduler fences
- Update workqueue interface

bridge:
- Select DRM_PANEL_BRIDGE for various drivers that require it

host1x:
- Support partitioning

imagination
- Clean up firmware interface

imx:
- Replace simple-display with regular atomic helpers

ivpu:
- Simplify NULL-pointer handling in cleanup code

mxsfb:
- Support bus formats plus DT bindings

panel:
- ebbg-ft8719: Fix MODULE_LICENSE() string; Improve initialization
- edp: NV140WU2-M70
- ilitek-ili9882t: Support Ilitek IL79600A plus DT bindings
- simple: Support Truly TFT320240-229-E plus DT bindings; Support BOE
  DV215FHM-R01 plus DT bindings
- Refactor struct drm_panel to contain struct drm_bridge plus fallout in
  various panel drivers

panthor:
- Validate user-space input against firmware limits

renesas:
- Support RZ/G3L SoC

ssd130x:
- ssd132x: Set correct hardware window when clearing screen

host1x:
- support partitioning

panel:
- fix fallout from bridge-related changes

tegra:
- improve syncobj handling on submit

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

From: Thomas Zimmermann <tzimmermann@suse.de>
Link: https://patch.msgid.link/20261002120720.GA720384@linux.fritz.box
This commit is contained in:
Dave Airlie 2026-10-03 06:12:16 +10:00
commit 1b15fa434b
176 changed files with 3351 additions and 1809 deletions

View File

@ -119,11 +119,11 @@ examples:
#size-cells = <0>;
panel@0 {
compatible = "bananapi,lhr050h41", "ilitek,ili9881c";
reg = <0>;
power-supply = <&reg_display>;
reset-gpios = <&r_pio 0 5 1>; /* PL05 */
backlight = <&pwm_bl>;
compatible = "bananapi,lhr050h41", "ilitek,ili9881c";
reg = <0>;
power-supply = <&reg_display>;
reset-gpios = <&r_pio 0 5 1>; /* PL05 */
backlight = <&pwm_bl>;
};
port {

View File

@ -84,8 +84,8 @@ examples:
};
mipi_lvds_0_pxl2dpi_dc_pixel_link1: endpoint@1 {
reg = <1>;
remote-endpoint = <&dc_pixel_link1_mipi_lvds_0_pxl2dpi>;
reg = <1>;
remote-endpoint = <&dc_pixel_link1_mipi_lvds_0_pxl2dpi>;
};
};

View File

@ -107,11 +107,11 @@ examples:
};
port@1 {
reg = <1>;
reg = <1>;
endpoint {
remote-endpoint = <&hdmi_in>;
};
endpoint {
remote-endpoint = <&hdmi_in>;
};
};
};
};

View File

@ -104,7 +104,7 @@ examples:
endpoint {
remote-endpoint = <&stdp2690_in>;
};
};
};
};
};
};

View File

@ -106,7 +106,7 @@ examples:
video-ports = <0x230145>;
#sound-dai-cells = <1>;
/* DAI-format / AP_ENA reg value */
/* DAI-format / AP_ENA reg value */
audio-ports = <TDA998x_SPDIF 0x04>,
<TDA998x_I2S 0x03>;

View File

@ -81,7 +81,7 @@ examples:
port@1 {
reg = <1>;
lvds_ep: endpoint {
remote-endpoint = <&panel_ep>;
remote-endpoint = <&panel_ep>;
};
};
};

View File

@ -161,9 +161,9 @@ examples:
dsi0_out: endpoint {
remote-endpoint = <&d2l_in_test>;
};
};
};
};
};
};
};
- |
/* For dual-link LVDS display panel */
@ -227,7 +227,7 @@ examples:
dsi0_out_dual: endpoint {
remote-endpoint = <&d2l_in_dual>;
};
};
};
};
};
};
};
...

View File

@ -54,7 +54,7 @@ examples:
display-controller@6a000000 {
compatible = "faraday,tve200";
reg = <0x6a000000 0x1000>;
interrupts = <13 IRQ_TYPE_EDGE_RISING>;
interrupts = <13 IRQ_TYPE_LEVEL_HIGH>;
resets = <&syscon GEMINI_RESET_TVC>;
clocks = <&syscon GEMINI_CLK_GATE_TVC>,
<&syscon GEMINI_CLK_TVC>;

View File

@ -68,9 +68,19 @@ properties:
maxItems: 1
port:
$ref: /schemas/graph.yaml#/properties/port
$ref: /schemas/graph.yaml#/$defs/port-base
unevaluatedProperties: false
description: The LCDIF output port
properties:
endpoint:
$ref: /schemas/media/video-interfaces.yaml
unevaluatedProperties: false
properties:
bus-width:
enum: [ 16, 18, 24 ]
display:
$ref: /schemas/types.yaml#/definitions/phandle
description: phandle to display panel

View File

@ -74,5 +74,5 @@ examples:
dc-gpios = <&gpio0 25 GPIO_ACTIVE_HIGH>;
rotation = <90>;
backlight = <&backlight>;
};
};
};

View File

@ -110,7 +110,7 @@ examples:
port {
parallel_out: endpoint {
remote-endpoint = <&panel_in>;
remote-endpoint = <&panel_in>;
};
};
};

View File

@ -150,44 +150,44 @@ examples:
/* Using an of-graph endpoint link to connect the panel */
lvds-channel@0 {
reg = <0>;
#address-cells = <1>;
#size-cells = <0>;
port@0 {
reg = <0>;
#address-cells = <1>;
#size-cells = <0>;
port@0 {
reg = <0>;
endpoint {
remote-endpoint = <&ipu_di0_lvds0>;
};
endpoint {
remote-endpoint = <&ipu_di0_lvds0>;
};
};
port@2 {
reg = <2>;
port@2 {
reg = <2>;
endpoint {
remote-endpoint = <&panel_in>;
};
};
endpoint {
remote-endpoint = <&panel_in>;
};
};
};
/* Using display-timings and fsl,data-mapping/width instead */
lvds-channel@1 {
reg = <1>;
#address-cells = <1>;
#size-cells = <0>;
fsl,data-mapping = "spwg";
fsl,data-width = <24>;
display-timings {/* ... */
};
port@1 {
reg = <1>;
#address-cells = <1>;
#size-cells = <0>;
fsl,data-mapping = "spwg";
fsl,data-width = <24>;
display-timings {/* ... */
};
port@1 {
reg = <1>;
endpoint {
remote-endpoint = <&ipu_di1_lvds1>;
};
endpoint {
remote-endpoint = <&ipu_di1_lvds1>;
};
};
};
};

View File

@ -158,51 +158,51 @@ examples:
#size-cells = <2>;
hdr-engine@1c114000 {
compatible = "mediatek,mt8195-disp-ethdr";
reg = <0 0x1c114000 0 0x1000>,
<0 0x1c115000 0 0x1000>,
<0 0x1c117000 0 0x1000>,
<0 0x1c119000 0 0x1000>,
<0 0x1c11a000 0 0x1000>,
<0 0x1c11b000 0 0x1000>,
<0 0x1c11c000 0 0x1000>;
reg-names = "mixer", "vdo_fe0", "vdo_fe1", "gfx_fe0", "gfx_fe1",
"vdo_be", "adl_ds";
mediatek,gce-client-reg = <&gce0 SUBSYS_1c11XXXX 0x4000 0x1000>,
<&gce0 SUBSYS_1c11XXXX 0x5000 0x1000>,
<&gce0 SUBSYS_1c11XXXX 0x7000 0x1000>,
<&gce0 SUBSYS_1c11XXXX 0x9000 0x1000>,
<&gce0 SUBSYS_1c11XXXX 0xa000 0x1000>,
<&gce0 SUBSYS_1c11XXXX 0xb000 0x1000>,
<&gce0 SUBSYS_1c11XXXX 0xc000 0x1000>;
clocks = <&vdosys1 CLK_VDO1_DISP_MIXER>,
<&vdosys1 CLK_VDO1_HDR_VDO_FE0>,
<&vdosys1 CLK_VDO1_HDR_VDO_FE1>,
<&vdosys1 CLK_VDO1_HDR_GFX_FE0>,
<&vdosys1 CLK_VDO1_HDR_GFX_FE1>,
<&vdosys1 CLK_VDO1_HDR_VDO_BE>,
<&vdosys1 CLK_VDO1_26M_SLOW>,
<&vdosys1 CLK_VDO1_HDR_VDO_FE0_DL_ASYNC>,
<&vdosys1 CLK_VDO1_HDR_VDO_FE1_DL_ASYNC>,
<&vdosys1 CLK_VDO1_HDR_GFX_FE0_DL_ASYNC>,
<&vdosys1 CLK_VDO1_HDR_GFX_FE1_DL_ASYNC>,
<&vdosys1 CLK_VDO1_HDR_VDO_BE_DL_ASYNC>,
<&topckgen CLK_TOP_ETHDR>;
clock-names = "mixer", "vdo_fe0", "vdo_fe1", "gfx_fe0", "gfx_fe1",
"vdo_be", "adl_ds", "vdo_fe0_async", "vdo_fe1_async",
"gfx_fe0_async", "gfx_fe1_async","vdo_be_async",
"ethdr_top";
power-domains = <&spm MT8195_POWER_DOMAIN_VDOSYS1>;
iommus = <&iommu_vpp M4U_PORT_L3_HDR_DS>,
<&iommu_vpp M4U_PORT_L3_HDR_ADL>;
interrupts = <GIC_SPI 517 IRQ_TYPE_LEVEL_HIGH 0>; /* disp mixer */
resets = <&vdosys1 MT8195_VDOSYS1_SW1_RST_B_HDR_VDO_FE0_DL_ASYNC>,
<&vdosys1 MT8195_VDOSYS1_SW1_RST_B_HDR_VDO_FE1_DL_ASYNC>,
<&vdosys1 MT8195_VDOSYS1_SW1_RST_B_HDR_GFX_FE0_DL_ASYNC>,
<&vdosys1 MT8195_VDOSYS1_SW1_RST_B_HDR_GFX_FE1_DL_ASYNC>,
<&vdosys1 MT8195_VDOSYS1_SW1_RST_B_HDR_VDO_BE_DL_ASYNC>;
reset-names = "vdo_fe0_async", "vdo_fe1_async", "gfx_fe0_async",
"gfx_fe1_async", "vdo_be_async";
compatible = "mediatek,mt8195-disp-ethdr";
reg = <0 0x1c114000 0 0x1000>,
<0 0x1c115000 0 0x1000>,
<0 0x1c117000 0 0x1000>,
<0 0x1c119000 0 0x1000>,
<0 0x1c11a000 0 0x1000>,
<0 0x1c11b000 0 0x1000>,
<0 0x1c11c000 0 0x1000>;
reg-names = "mixer", "vdo_fe0", "vdo_fe1", "gfx_fe0", "gfx_fe1",
"vdo_be", "adl_ds";
mediatek,gce-client-reg = <&gce0 SUBSYS_1c11XXXX 0x4000 0x1000>,
<&gce0 SUBSYS_1c11XXXX 0x5000 0x1000>,
<&gce0 SUBSYS_1c11XXXX 0x7000 0x1000>,
<&gce0 SUBSYS_1c11XXXX 0x9000 0x1000>,
<&gce0 SUBSYS_1c11XXXX 0xa000 0x1000>,
<&gce0 SUBSYS_1c11XXXX 0xb000 0x1000>,
<&gce0 SUBSYS_1c11XXXX 0xc000 0x1000>;
clocks = <&vdosys1 CLK_VDO1_DISP_MIXER>,
<&vdosys1 CLK_VDO1_HDR_VDO_FE0>,
<&vdosys1 CLK_VDO1_HDR_VDO_FE1>,
<&vdosys1 CLK_VDO1_HDR_GFX_FE0>,
<&vdosys1 CLK_VDO1_HDR_GFX_FE1>,
<&vdosys1 CLK_VDO1_HDR_VDO_BE>,
<&vdosys1 CLK_VDO1_26M_SLOW>,
<&vdosys1 CLK_VDO1_HDR_VDO_FE0_DL_ASYNC>,
<&vdosys1 CLK_VDO1_HDR_VDO_FE1_DL_ASYNC>,
<&vdosys1 CLK_VDO1_HDR_GFX_FE0_DL_ASYNC>,
<&vdosys1 CLK_VDO1_HDR_GFX_FE1_DL_ASYNC>,
<&vdosys1 CLK_VDO1_HDR_VDO_BE_DL_ASYNC>,
<&topckgen CLK_TOP_ETHDR>;
clock-names = "mixer", "vdo_fe0", "vdo_fe1", "gfx_fe0", "gfx_fe1",
"vdo_be", "adl_ds", "vdo_fe0_async", "vdo_fe1_async",
"gfx_fe0_async", "gfx_fe1_async","vdo_be_async",
"ethdr_top";
power-domains = <&spm MT8195_POWER_DOMAIN_VDOSYS1>;
iommus = <&iommu_vpp M4U_PORT_L3_HDR_DS>,
<&iommu_vpp M4U_PORT_L3_HDR_ADL>;
interrupts = <GIC_SPI 517 IRQ_TYPE_LEVEL_HIGH 0>; /* disp mixer */
resets = <&vdosys1 MT8195_VDOSYS1_SW1_RST_B_HDR_VDO_FE0_DL_ASYNC>,
<&vdosys1 MT8195_VDOSYS1_SW1_RST_B_HDR_VDO_FE1_DL_ASYNC>,
<&vdosys1 MT8195_VDOSYS1_SW1_RST_B_HDR_GFX_FE0_DL_ASYNC>,
<&vdosys1 MT8195_VDOSYS1_SW1_RST_B_HDR_GFX_FE1_DL_ASYNC>,
<&vdosys1 MT8195_VDOSYS1_SW1_RST_B_HDR_VDO_BE_DL_ASYNC>;
reset-names = "vdo_fe0_async", "vdo_fe1_async", "gfx_fe0_async",
"gfx_fe1_async", "vdo_be_async";
};
};
...

View File

@ -112,24 +112,24 @@ examples:
mediatek,syscon-hdmi = <&mmsys 0x900>;
ports {
#address-cells = <1>;
#size-cells = <0>;
#address-cells = <1>;
#size-cells = <0>;
port@0 {
reg = <0>;
port@0 {
reg = <0>;
hdmi0_in: endpoint {
remote-endpoint = <&dpi0_out>;
hdmi0_in: endpoint {
remote-endpoint = <&dpi0_out>;
};
};
};
port@1 {
reg = <1>;
port@1 {
reg = <1>;
hdmi0_out: endpoint {
remote-endpoint = <&hdmi_con_in>;
hdmi0_out: endpoint {
remote-endpoint = <&hdmi_con_in>;
};
};
};
};
};

View File

@ -14,10 +14,15 @@ allOf:
properties:
compatible:
items:
- enum:
- tianma,tl121bvms07-00
- const: ilitek,il79900a
oneOf:
- items:
- enum:
- raspberrypi,dsi-10inch
- const: ilitek,il79600a
- items:
- enum:
- tianma,tl121bvms07-00
- const: ilitek,il79900a
reg:
maxItems: 1

View File

@ -44,6 +44,8 @@ properties:
- auo,t215hvn01
# BOE AV123Z7M-N17 12.3" (1920x720) LVDS TFT LCD panel
- boe,av123z7m-n17
# BOE DV215FHM-R01 21.5" FHD (1920x1080) dual-channel LVDS panel
- boe,dv215fhm-r01
# InnoLux 15.6" FHD (1920x1080) TFT LCD panel
- innolux,g156hce-l01
# Kaohsiung Opto-Electronics Inc. 10.1" WUXGA (1920 x 1200) LVDS TFT LCD panel

View File

@ -338,6 +338,8 @@ properties:
- tpk,f07a-0102
# TPK U.S.A. LLC Fusion 10.1" 1024 x 600 (WSVGA) LCD panel with capacitive touch
- tpk,f10a-0102
# Truly Semiconductors Limited 3.5" QVGA (320 x 240) TFT LCD panel
- truly,tft320240-229-e
# United Radiant Technology UMSH-8596MD-xT 7.0" WVGA TFT LCD panel
# Supported are LVDS versions (-11T, -19T) and parallel ones
# (-T, -1T, -7T, -20T).

View File

@ -51,7 +51,7 @@ examples:
port {
mipi_in_panel: endpoint {
remote-endpoint = <&mipi_out_panel>;
remote-endpoint = <&mipi_out_panel>;
};
};
};

View File

@ -64,5 +64,5 @@ examples:
vfront-porch = <0>;
vsync-len = <0>;
};
};
};
};
};

View File

@ -101,35 +101,35 @@ examples:
#size-cells = <0>;
ports {
#address-cells = <1>;
#size-cells = <0>;
#address-cells = <1>;
#size-cells = <0>;
port@0 {
reg = <0>;
dsi_in: endpoint {
remote-endpoint = <&ltdc_ep1_out>;
};
};
port@0 {
reg = <0>;
dsi_in: endpoint {
remote-endpoint = <&ltdc_ep1_out>;
};
};
port@1 {
reg = <1>;
dsi_out: endpoint {
remote-endpoint = <&panel_in>;
};
};
port@1 {
reg = <1>;
dsi_out: endpoint {
remote-endpoint = <&panel_in>;
};
};
};
panel@0 {
compatible = "orisetech,otm8009a";
reg = <0>;
reset-gpios = <&gpioe 4 GPIO_ACTIVE_LOW>;
power-supply = <&v3v3>;
compatible = "orisetech,otm8009a";
reg = <0>;
reset-gpios = <&gpioe 4 GPIO_ACTIVE_LOW>;
power-supply = <&v3v3>;
port {
panel_in: endpoint {
remote-endpoint = <&dsi_out>;
};
};
port {
panel_in: endpoint {
remote-endpoint = <&dsi_out>;
};
};
};
};

View File

@ -119,9 +119,9 @@ examples:
resets = <&rcc LTDC_R>;
port {
ltdc_out_dsi: endpoint {
remote-endpoint = <&dsi_in>;
};
ltdc_out_dsi: endpoint {
remote-endpoint = <&dsi_in>;
};
};
};

View File

@ -114,14 +114,14 @@ examples:
port@0 {
reg = <0>;
lvds_in: endpoint {
remote-endpoint = <&ltdc_ep1_out>;
remote-endpoint = <&ltdc_ep1_out>;
};
};
port@1 {
reg = <1>;
lvds_out0: endpoint {
remote-endpoint = <&lvds_panel_in>;
remote-endpoint = <&lvds_panel_in>;
};
};
};

View File

@ -98,6 +98,18 @@ properties:
items:
- description: phandle to the HEG or core power domain
nvidia,channels:
$ref: /schemas/types.yaml#/definitions/uint32-array
minItems: 2
maxItems: 2
description: Describes range of usable Host1x channels as a <base count> tuple.
nvidia,syncpoints:
$ref: /schemas/types.yaml#/definitions/uint32-array
minItems: 2
maxItems: 2
description: Describes range of writable Host1x syncpoints as a <base count> tuple.
required:
- compatible
- interrupts
@ -147,14 +159,20 @@ allOf:
then:
properties:
reg-names:
items:
- const: hypervisor
- const: vm
oneOf:
- items:
- const: hypervisor
- const: vm
- items:
- const: vm
reg:
items:
- description: region used by the hypervisor
- description: region assigned to the virtual machine
oneOf:
- items:
- description: region used by the hypervisor
- description: region assigned to the virtual machine
- items:
- description: region assigned to the virtual machine
resets:
maxItems: 1
@ -198,16 +216,22 @@ allOf:
then:
properties:
reg-names:
items:
- const: common
- const: hypervisor
- const: vm
oneOf:
- items:
- const: common
- const: hypervisor
- const: vm
- items:
- const: vm
reg:
items:
- description: region used by host1x server
- description: region used by the hypervisor
- description: region assigned to the virtual machine
oneOf:
- items:
- description: region used by host1x server
- description: region used by the hypervisor
- description: region assigned to the virtual machine
- items:
- description: region assigned to the virtual machine
interrupts:
items:

View File

@ -109,13 +109,13 @@ VBLANK Helper Reference
fbdev Helper Functions Reference
================================
.. kernel-doc:: drivers/gpu/drm/drm_fb_helper.c
.. kernel-doc:: drivers/gpu/drm/clients/drm_fbdev_helper.c
:doc: fbdev helpers
.. kernel-doc:: include/drm/drm_fb_helper.h
.. kernel-doc:: include/drm/clients/drm_fbdev_helper.h
:internal:
.. kernel-doc:: drivers/gpu/drm/drm_fb_helper.c
.. kernel-doc:: drivers/gpu/drm/clients/drm_fbdev_helper.c
:export:
Draw Helper Functions Reference
@ -218,7 +218,7 @@ Bridge Connector Helper Reference
Panel-Bridge Helper Reference
-----------------------------
.. kernel-doc:: drivers/gpu/drm/bridge/panel.c
.. kernel-doc:: drivers/gpu/drm/drm_panel.c
:export:
.. _drm_panel_helper:

View File

@ -484,21 +484,30 @@ Contact: Douglas Anderson <dianders@chromium.org>
Level: Starter
Remove devm_drm_put_bridge()
----------------------------
Remove panel_bridge API functions
---------------------------------
Due to how the panel bridge handles the drm_bridge object lifetime, special
care must be taken to dispose of the drm_bridge object when the
panel_bridge is removed. This is currently managed using
devm_drm_put_bridge(), but that is an unsafe, temporary workaround. To fix
that, the DRM panel lifetime needs to be reworked. After the rework is
done, remove devm_drm_put_bridge() and the TODO in
drm_panel_bridge_remove().
Now that a drm_bridge is embedded into every drm_panel, the panel_bridge is
not needed anymore. Its APIs are just accessing the embedded drm_bridge
transparently, and the are deprecated.
Contact: Maxime Ripard <mripard@kernel.org>,
Luca Ceresoli <luca.ceresoli@bootlin.com>
The funcions to remove are:
- drm_panel_bridge_add_typed()
- devm_drm_of_get_bridge()
- drmm_of_get_bridge()
- drm_panel_bridge_remove()
- drmm_drm_panel_bridge_release()
- devm_drm_panel_bridge_release()
- their direct or indirect callers
Level: Intermediate
Convert users of those functions and eventually remove them when unused, or
make them static drm_panel.c functions when used internally by the
``drm_panel.c`` code.
Contact: Luca Ceresoli <luca.ceresoli@bootlin.com>
Maxime Ripard <mripard@kernel.org>,
Level: Easy
Convert users of of_drm_find_bridge() to of_drm_find_and_get_bridge()
---------------------------------------------------------------------

View File

@ -7,6 +7,7 @@
#include <drm/drm_device.h>
#include <drm/drm_gem.h>
#include <drm/drm_gem_shmem_helper.h>
#include <drm/drm_managed.h>
#include <drm/drm_print.h>
#include <drm/drm_syncobj.h>
#include <linux/hmm.h>
@ -99,9 +100,9 @@ static void aie2_job_put(struct amdxdna_sched_job *job)
static void aie2_hwctx_stop(struct amdxdna_dev *xdna, struct amdxdna_hwctx *hwctx,
struct drm_sched_job *bad_job)
{
drm_sched_stop(&hwctx->priv->sched, bad_job);
drm_sched_stop(hwctx->priv->sched, bad_job);
aie2_destroy_context(xdna->dev_handle, hwctx);
drm_sched_start(&hwctx->priv->sched, 0);
drm_sched_start(hwctx->priv->sched, 0);
}
static int aie2_hwctx_restart(struct amdxdna_dev *xdna, struct amdxdna_hwctx *hwctx)
@ -659,10 +660,19 @@ static void aie2_ctx_syncobj_destroy(struct amdxdna_hwctx *hwctx)
drm_syncobj_put(hwctx->priv->syncobj);
}
int aie2_hwctx_init(struct amdxdna_hwctx *hwctx)
void aie2_hwctx_sched_fini(struct amdxdna_dev_hdl *ndev)
{
struct amdxdna_client *client = hwctx->client;
struct amdxdna_dev *xdna = client->xdna;
int i;
for (i = 0; i < ndev->priv->hwctx_limit; i++)
drm_sched_fini(&ndev->hwctx_sched[i]);
ida_destroy(&ndev->hwctx_sched_ida);
}
int aie2_hwctx_sched_init(struct amdxdna_dev_hdl *ndev)
{
struct amdxdna_dev *xdna = ndev->aie.xdna;
const struct drm_sched_init_args args = {
.ops = &sched_ops,
.num_rqs = DRM_SCHED_PRIORITY_COUNT,
@ -673,7 +683,55 @@ int aie2_hwctx_init(struct amdxdna_hwctx *hwctx)
.name = "amdxdna_js",
.dev = xdna->ddev.dev,
};
struct drm_gpu_scheduler *sched;
int i, ret;
ndev->hwctx_sched = drmm_kcalloc(&xdna->ddev, ndev->priv->hwctx_limit,
sizeof(*ndev->hwctx_sched), GFP_KERNEL);
if (!ndev->hwctx_sched)
return -ENOMEM;
for (i = 0; i < ndev->priv->hwctx_limit; i++) {
ret = drm_sched_init(&ndev->hwctx_sched[i], &args);
if (ret) {
XDNA_ERR(xdna, "Failed to init DRM scheduler. ret %d", ret);
goto fini_sched;
}
}
ida_init(&ndev->hwctx_sched_ida);
return 0;
fini_sched:
while (--i >= 0)
drm_sched_fini(&ndev->hwctx_sched[i]);
return ret;
}
static struct drm_gpu_scheduler *aie2_hwctx_sched_alloc(struct amdxdna_dev_hdl *ndev)
{
int ret;
ret = ida_alloc_range(&ndev->hwctx_sched_ida, 0,
ndev->priv->hwctx_limit - 1, GFP_KERNEL);
if (ret < 0)
return ERR_PTR(ret);
return &ndev->hwctx_sched[ret];
}
static void aie2_hwctx_sched_free(struct amdxdna_dev_hdl *ndev,
struct drm_gpu_scheduler *sched)
{
ida_free(&ndev->hwctx_sched_ida, sched - ndev->hwctx_sched);
}
int aie2_hwctx_init(struct amdxdna_hwctx *hwctx)
{
struct amdxdna_client *client = hwctx->client;
struct amdxdna_dev *xdna = client->xdna;
struct amdxdna_dev_hdl *ndev = xdna->dev_handle;
struct amdxdna_hwctx_priv *priv;
struct amdxdna_gem_obj *heap;
int i, ret;
@ -683,13 +741,26 @@ int aie2_hwctx_init(struct amdxdna_hwctx *hwctx)
return -ENOMEM;
hwctx->priv = priv;
priv->sched = aie2_hwctx_sched_alloc(ndev);
if (IS_ERR(priv->sched)) {
ret = PTR_ERR(priv->sched);
goto free_priv;
}
ret = drm_sched_entity_init(&priv->entity, DRM_SCHED_PRIORITY_NORMAL,
&priv->sched, 1, NULL);
if (ret) {
XDNA_ERR(xdna, "Failed to initial sched entity. ret %d", ret);
goto free_sched;
}
mutex_lock(&client->mm_lock);
heap = xa_load(&client->dev_heap_xa, 0);
if (!heap) {
XDNA_ERR(xdna, "The client dev heap object not exist");
mutex_unlock(&client->mm_lock);
ret = -ENOENT;
goto free_priv;
goto free_entity;
}
drm_gem_object_get(to_gobj(heap));
mutex_unlock(&client->mm_lock);
@ -722,30 +793,16 @@ int aie2_hwctx_init(struct amdxdna_hwctx *hwctx)
priv->cmd_buf[i] = abo;
}
sched = &priv->sched;
mutex_init(&priv->io_lock);
fs_reclaim_acquire(GFP_KERNEL);
might_lock(&priv->io_lock);
fs_reclaim_release(GFP_KERNEL);
ret = drm_sched_init(sched, &args);
if (ret) {
XDNA_ERR(xdna, "Failed to init DRM scheduler. ret %d", ret);
goto free_cmd_bufs;
}
ret = drm_sched_entity_init(&priv->entity, DRM_SCHED_PRIORITY_NORMAL,
&sched, 1, NULL);
if (ret) {
XDNA_ERR(xdna, "Failed to initial sched entiry. ret %d", ret);
goto free_sched;
}
ret = aie2_hwctx_col_list(hwctx);
if (ret) {
XDNA_ERR(xdna, "Create col list failed, ret %d", ret);
goto free_entity;
goto free_cmd_bufs;
}
ret = amdxdna_pm_resume_get_locked(xdna);
@ -791,10 +848,6 @@ int aie2_hwctx_init(struct amdxdna_hwctx *hwctx)
amdxdna_pm_suspend_put(xdna);
free_col_list:
kfree(hwctx->col_list);
free_entity:
drm_sched_entity_destroy(&priv->entity);
free_sched:
drm_sched_fini(&priv->sched);
free_cmd_bufs:
for (i = 0; i < ARRAY_SIZE(priv->cmd_buf); i++) {
if (!priv->cmd_buf[i])
@ -805,6 +858,10 @@ int aie2_hwctx_init(struct amdxdna_hwctx *hwctx)
amdxdna_gem_unpin(heap);
put_heap:
drm_gem_object_put(to_gobj(heap));
free_entity:
drm_sched_entity_destroy(&priv->entity);
free_sched:
aie2_hwctx_sched_free(ndev, priv->sched);
free_priv:
kfree(priv);
return ret;
@ -812,18 +869,20 @@ int aie2_hwctx_init(struct amdxdna_hwctx *hwctx)
void aie2_hwctx_fini(struct amdxdna_hwctx *hwctx)
{
struct amdxdna_dev_hdl *ndev;
struct amdxdna_dev *xdna;
int idx;
xdna = hwctx->client->xdna;
ndev = xdna->dev_handle;
XDNA_DBG(xdna, "%s sequence number %lld", hwctx->name, hwctx->priv->seq);
aie2_hwctx_wait_for_idle(hwctx);
/* Request fw to destroy hwctx and cancel the rest pending requests */
drm_sched_stop(&hwctx->priv->sched, NULL);
drm_sched_stop(hwctx->priv->sched, NULL);
aie2_release_resource(hwctx);
drm_sched_start(&hwctx->priv->sched, 0);
drm_sched_start(hwctx->priv->sched, 0);
mutex_unlock(&xdna->dev_lock);
drm_sched_entity_destroy(&hwctx->priv->entity);
@ -834,7 +893,7 @@ void aie2_hwctx_fini(struct amdxdna_hwctx *hwctx)
atomic64_read(&hwctx->job_free_cnt));
mutex_lock(&xdna->dev_lock);
drm_sched_fini(&hwctx->priv->sched);
aie2_hwctx_sched_free(ndev, hwctx->priv->sched);
aie2_ctx_syncobj_destroy(hwctx);
for (idx = 0; idx < ARRAY_SIZE(hwctx->priv->cmd_buf); idx++) {

View File

@ -530,6 +530,7 @@ enum aie2_dev_revision {
AIE2_DEV_REVISION_GPT1,
AIE2_DEV_REVISION_GPT2,
AIE2_DEV_REVISION_GPT3,
AIE2_DEV_REVISION_GRGH,
AIE2_DEV_REVISION_UNKN,
};

View File

@ -503,10 +503,16 @@ static int aie2_init(struct amdxdna_dev *xdna)
ndev->priv = xdna->dev_info->dev_priv;
ndev->aie.xdna = xdna;
ret = aie2_hwctx_sched_init(ndev);
if (ret)
return ret;
for (i = 0; i < ARRAY_SIZE(npu_fw); i++) {
fw_full_path = kasprintf(GFP_KERNEL, "%s%s", ndev->priv->fw_path, npu_fw[i]);
if (!fw_full_path)
return -ENOMEM;
if (!fw_full_path) {
ret = -ENOMEM;
goto free_hwctx_sched;
}
ret = firmware_request_nowarn(&fw, fw_full_path, &pdev->dev);
kfree(fw_full_path);
@ -519,7 +525,7 @@ static int aie2_init(struct amdxdna_dev *xdna)
if (ret) {
XDNA_ERR(xdna, "failed to request_firmware %s, ret %d",
ndev->priv->fw_path, ret);
return ret;
goto free_hwctx_sched;
}
ret = pcim_enable_device(pdev);
@ -623,6 +629,8 @@ static int aie2_init(struct amdxdna_dev *xdna)
aie2_hw_stop(xdna);
release_fw:
release_firmware(fw);
free_hwctx_sched:
aie2_hwctx_sched_fini(ndev);
return ret;
}
@ -631,6 +639,7 @@ static void aie2_fini(struct amdxdna_dev *xdna)
{
amdxdna_pm_fini(xdna);
aie2_hw_stop(xdna);
aie2_hwctx_sched_fini(xdna->dev_handle);
}
static int aie2_get_aie_status(struct amdxdna_client *client,

View File

@ -107,7 +107,7 @@ struct amdxdna_hwctx_priv {
struct amdxdna_gem_obj *heap;
void *mbox_chann;
struct drm_gpu_scheduler sched;
struct drm_gpu_scheduler *sched;
struct drm_sched_entity entity;
struct mutex io_lock; /* protect seq and cmd order */
@ -152,6 +152,8 @@ struct amdxdna_dev_hdl {
u32 total_col;
struct amdxdna_drm_query_aie_version version;
struct aie2_exec_msg_ops *exec_msg_ops;
struct drm_gpu_scheduler *hwctx_sched;
struct ida hwctx_sched_ida;
/* power management and clock*/
enum amdxdna_power_mode_type pw_mode;
@ -294,6 +296,8 @@ int aie2_update_prop_time_quota(struct amdxdna_dev_hdl *ndev, u32 us);
/* aie2_hwctx.c */
int aie2_hwctx_init(struct amdxdna_hwctx *hwctx);
void aie2_hwctx_fini(struct amdxdna_hwctx *hwctx);
int aie2_hwctx_sched_init(struct amdxdna_dev_hdl *ndev);
void aie2_hwctx_sched_fini(struct amdxdna_dev_hdl *ndev);
int aie2_hwctx_config(struct amdxdna_hwctx *hwctx, u32 type, u64 value, void *buf, u32 size);
int aie2_hwctx_sync_debug_bo(struct amdxdna_hwctx *hwctx, u32 debug_bo_hdl);
void aie2_hwctx_suspend(struct amdxdna_client *client);

View File

@ -481,7 +481,7 @@ struct mailbox_channel *xdna_mailbox_alloc_channel(struct mailbox *mb)
return NULL;
INIT_WORK(&mb_chann->rx_work, mailbox_rx_worker);
mb_chann->work_q = create_singlethread_workqueue(MAILBOX_NAME);
mb_chann->work_q = alloc_ordered_workqueue(MAILBOX_NAME, WQ_MEM_RECLAIM);
if (!mb_chann->work_q) {
MB_ERR(mb_chann, "Create workqueue failed");
goto free_chann;

View File

@ -153,6 +153,7 @@ const struct amdxdna_rev_vbnv npu4_rev_vbnv_tbl[] = {
{ AIE2_DEV_REVISION_GPT1, "NPU Gorgon Point 1" },
{ AIE2_DEV_REVISION_GPT2, "NPU Gorgon Point 2" },
{ AIE2_DEV_REVISION_GPT3, "NPU Gorgon Point 3" },
{ AIE2_DEV_REVISION_GRGH, "NPU Gorgon Halo" },
{ 0 }
};

View File

@ -12,10 +12,10 @@
#include <drm/drm_drv.h>
#include <drm/drm_ioctl.h>
#include <drm/drm_utils.h>
#include <drm/drm_gem.h>
#include <drm/drm_accel.h>
#include <drm/drm_managed.h>
#include <drm/drm_timeout.h>
#include <drm/ethosu_accel.h>
#include "ethosu_drv.h"

View File

@ -364,6 +364,25 @@ static void ivpu_fw_release(struct ivpu_device *vdev)
}
static void ivpu_fw_mem_fini(struct ivpu_device *vdev)
{
struct ivpu_fw_info *fw = vdev->fw;
ivpu_bo_free(fw->mem_shave_nn);
ivpu_bo_free(fw->mem_log_verb);
ivpu_bo_free(fw->mem_log_crit);
ivpu_bo_free(fw->mem);
ivpu_bo_free(fw->mem_fw_ver);
ivpu_bo_free(fw->mem_bp);
fw->mem_shave_nn = NULL;
fw->mem_log_verb = NULL;
fw->mem_log_crit = NULL;
fw->mem = NULL;
fw->mem_fw_ver = NULL;
fw->mem_bp = NULL;
}
static int ivpu_fw_mem_init(struct ivpu_device *vdev)
{
struct ivpu_fw_info *fw = vdev->fw;
@ -382,7 +401,7 @@ static int ivpu_fw_mem_init(struct ivpu_device *vdev)
if (!fw->mem_fw_ver) {
ivpu_err(vdev, "Failed to create firmware version memory buffer\n");
ret = -ENOMEM;
goto err_free_bp;
goto err_free;
}
fw->mem = ivpu_bo_create_runtime(vdev, fw->runtime_addr, fw->runtime_size,
@ -390,14 +409,14 @@ static int ivpu_fw_mem_init(struct ivpu_device *vdev)
if (!fw->mem) {
ivpu_err(vdev, "Failed to create firmware runtime memory buffer\n");
ret = -ENOMEM;
goto err_free_fw_ver;
goto err_free;
}
ret = ivpu_mmu_context_set_pages_ro(vdev, &vdev->gctx, fw->read_only_addr,
fw->read_only_size);
if (ret) {
ivpu_err(vdev, "Failed to set firmware image read-only\n");
goto err_free_fw_mem;
goto err_free;
}
fw->mem_log_crit = ivpu_bo_create_global(vdev, IVPU_FW_CRITICAL_BUFFER_SIZE,
@ -405,7 +424,7 @@ static int ivpu_fw_mem_init(struct ivpu_device *vdev)
if (!fw->mem_log_crit) {
ivpu_err(vdev, "Failed to create critical log buffer\n");
ret = -ENOMEM;
goto err_free_fw_mem;
goto err_free;
}
if (ivpu_fw_log_level <= IVPU_FW_LOG_INFO)
@ -418,7 +437,7 @@ static int ivpu_fw_mem_init(struct ivpu_device *vdev)
if (!fw->mem_log_verb) {
ivpu_err(vdev, "Failed to create verbose log buffer\n");
ret = -ENOMEM;
goto err_free_log_crit;
goto err_free;
}
if (fw->shave_nn_size) {
@ -427,47 +446,17 @@ static int ivpu_fw_mem_init(struct ivpu_device *vdev)
if (!fw->mem_shave_nn) {
ivpu_err(vdev, "Failed to create shavenn buffer\n");
ret = -ENOMEM;
goto err_free_log_verb;
goto err_free;
}
}
return 0;
err_free_log_verb:
ivpu_bo_free(fw->mem_log_verb);
err_free_log_crit:
ivpu_bo_free(fw->mem_log_crit);
err_free_fw_mem:
ivpu_bo_free(fw->mem);
err_free_fw_ver:
ivpu_bo_free(fw->mem_fw_ver);
err_free_bp:
ivpu_bo_free(fw->mem_bp);
err_free:
ivpu_fw_mem_fini(vdev);
return ret;
}
static void ivpu_fw_mem_fini(struct ivpu_device *vdev)
{
struct ivpu_fw_info *fw = vdev->fw;
if (fw->mem_shave_nn) {
ivpu_bo_free(fw->mem_shave_nn);
fw->mem_shave_nn = NULL;
}
ivpu_bo_free(fw->mem_log_verb);
ivpu_bo_free(fw->mem_log_crit);
ivpu_bo_free(fw->mem);
ivpu_bo_free(fw->mem_fw_ver);
ivpu_bo_free(fw->mem_bp);
fw->mem_log_verb = NULL;
fw->mem_log_crit = NULL;
fw->mem = NULL;
fw->mem_fw_ver = NULL;
fw->mem_bp = NULL;
}
int ivpu_fw_init(struct ivpu_device *vdev)
{
int ret;

View File

@ -12,7 +12,7 @@
#include <drm/drm_cache.h>
#include <drm/drm_debugfs.h>
#include <drm/drm_file.h>
#include <drm/drm_utils.h>
#include <drm/drm_timeout.h>
#include "ivpu_drv.h"
#include "ivpu_fw.h"
@ -471,6 +471,9 @@ struct ivpu_bo *ivpu_bo_create_global(struct ivpu_device *vdev, u64 size, u32 fl
void ivpu_bo_free(struct ivpu_bo *bo)
{
if (!bo)
return;
struct iosys_map map = IOSYS_MAP_INIT_VADDR(bo->base.vaddr);
if (bo->flags & DRM_IVPU_BO_MAPPABLE) {

View File

@ -509,7 +509,7 @@ int ivpu_ipc_init(struct ivpu_device *vdev)
if (!ipc->mem_rx) {
ivpu_err(vdev, "Failed to allocate mem_rx\n");
ret = -ENOMEM;
goto err_free_tx;
goto err_free_bo;
}
ipc->mm_tx = devm_gen_pool_create(vdev->drm.dev, __ffs(IVPU_IPC_ALIGNMENT),
@ -517,13 +517,13 @@ int ivpu_ipc_init(struct ivpu_device *vdev)
if (IS_ERR(ipc->mm_tx)) {
ret = PTR_ERR(ipc->mm_tx);
ivpu_err(vdev, "Failed to create gen pool, %pe\n", ipc->mm_tx);
goto err_free_rx;
goto err_free_bo;
}
ret = gen_pool_add(ipc->mm_tx, ipc->mem_tx->vpu_addr, ivpu_bo_size(ipc->mem_tx), -1);
if (ret) {
ivpu_err(vdev, "gen_pool_add failed, ret %d\n", ret);
goto err_free_rx;
goto err_free_bo;
}
spin_lock_init(&ipc->cons_lock);
@ -532,14 +532,13 @@ int ivpu_ipc_init(struct ivpu_device *vdev)
ret = drmm_mutex_init(&vdev->drm, &ipc->lock);
if (ret) {
ivpu_err(vdev, "Failed to initialize ipc->lock, ret %d\n", ret);
goto err_free_rx;
goto err_free_bo;
}
ivpu_ipc_reset(vdev);
return 0;
err_free_rx:
err_free_bo:
ivpu_bo_free(ipc->mem_rx);
err_free_tx:
ivpu_bo_free(ipc->mem_tx);
err_destroy_cache:
kmem_cache_destroy(ipc->rx_msg_cache);

View File

@ -64,10 +64,8 @@ static int ivpu_preemption_buffers_create(struct ivpu_device *vdev,
static void ivpu_preemption_buffers_free(struct ivpu_device *vdev,
struct ivpu_file_priv *file_priv, struct ivpu_cmdq *cmdq)
{
if (cmdq->primary_preempt_buf)
ivpu_bo_free(cmdq->primary_preempt_buf);
if (cmdq->secondary_preempt_buf)
ivpu_bo_free(cmdq->secondary_preempt_buf);
ivpu_bo_free(cmdq->primary_preempt_buf);
ivpu_bo_free(cmdq->secondary_preempt_buf);
}
static int ivpu_preemption_job_init(struct ivpu_device *vdev, struct ivpu_file_priv *file_priv,

View File

@ -69,7 +69,7 @@ int ivpu_ms_start_ioctl(struct drm_device *dev, void *data, struct drm_file *fil
ret = ivpu_jsm_metric_streamer_info(vdev, ms->mask, 0, 0, &sample_size, NULL);
if (ret)
goto err_free_ms;
goto err_free;
buf_size = PAGE_ALIGN((u64)args->read_period_samples * sample_size *
MS_READ_PERIOD_MULTIPLIER * MS_NUM_BUFFERS);
@ -77,14 +77,14 @@ int ivpu_ms_start_ioctl(struct drm_device *dev, void *data, struct drm_file *fil
ivpu_dbg(vdev, IOCTL, "Requested MS buffer size %llu exceeds range size %llu\n",
buf_size, ivpu_hw_range_size(&vdev->hw->ranges.global));
ret = -EINVAL;
goto err_free_ms;
goto err_free;
}
ms->bo = ivpu_bo_create_global(vdev, buf_size, DRM_IVPU_BO_CACHED | DRM_IVPU_BO_MAPPABLE);
if (!ms->bo) {
ivpu_dbg(vdev, IOCTL, "Failed to allocate MS buffer (size %llu)\n", buf_size);
ret = -ENOMEM;
goto err_free_ms;
goto err_free;
}
ms->buff_size = ivpu_bo_size(ms->bo) / MS_NUM_BUFFERS;
@ -96,16 +96,15 @@ int ivpu_ms_start_ioctl(struct drm_device *dev, void *data, struct drm_file *fil
ret = ivpu_jsm_metric_streamer_start(vdev, ms->mask, args->sampling_period_ns,
ms->active_buff_vpu_addr, ms->buff_size);
if (ret)
goto err_free_bo;
goto err_free;
args->sample_size = sample_size;
args->max_data_size = ivpu_bo_size(ms->bo);
list_add_tail(&ms->ms_instance_node, &file_priv->ms_instance_list);
goto unlock;
err_free_bo:
err_free:
ivpu_bo_free(ms->bo);
err_free_ms:
kfree(ms);
unlock:
mutex_unlock(&file_priv->ms_lock);
@ -322,10 +321,8 @@ void ivpu_ms_cleanup(struct ivpu_file_priv *file_priv)
mutex_lock(&file_priv->ms_lock);
if (file_priv->ms_info_bo) {
ivpu_bo_free(file_priv->ms_info_bo);
file_priv->ms_info_bo = NULL;
}
ivpu_bo_free(file_priv->ms_info_bo);
file_priv->ms_info_bo = NULL;
list_for_each_entry_safe(ms, tmp, &file_priv->ms_instance_list, ms_instance_node)
free_instance(file_priv, ms);

View File

@ -3,7 +3,7 @@
#include <drm/drm_device.h>
#include <drm/drm_print.h>
#include <drm/drm_utils.h>
#include <drm/drm_timeout.h>
#include <drm/rocket_accel.h>
#include <linux/dma-mapping.h>
#include <linux/iommu.h>

View File

@ -46,7 +46,6 @@ config DRM_KMS_HELPER
tristate
depends on DRM
select DRM_PANIC_HELPER if DRM_PANIC
select FB_CORE if DRM_FBDEV_EMULATION
help
CRTC helpers for KMS drivers.
@ -256,7 +255,7 @@ config DRM_TTM_HELPER
tristate
depends on DRM
select DRM_TTM
select DRM_KMS_HELPER if DRM_FBDEV_EMULATION
select DRM_CLIENT_LIB if DRM_FBDEV_EMULATION
select FB_CORE if DRM_FBDEV_EMULATION
select FB_SYSMEM_HELPERS_DEFERRED if DRM_FBDEV_EMULATION
help
@ -265,7 +264,7 @@ config DRM_TTM_HELPER
config DRM_GEM_DMA_HELPER
tristate
depends on DRM
select DRM_KMS_HELPER if DRM_FBDEV_EMULATION
select DRM_CLIENT_LIB if DRM_FBDEV_EMULATION
select FB_CORE if DRM_FBDEV_EMULATION
select FB_DMAMEM_HELPERS_DEFERRED if DRM_FBDEV_EMULATION
help
@ -274,7 +273,7 @@ config DRM_GEM_DMA_HELPER
config DRM_GEM_SHMEM_HELPER
tristate
depends on DRM && MMU
select DRM_KMS_HELPER if DRM_FBDEV_EMULATION
select DRM_CLIENT_LIB if DRM_FBDEV_EMULATION
select FB_CORE if DRM_FBDEV_EMULATION
select FB_SYSMEM_HELPERS_DEFERRED if DRM_FBDEV_EMULATION
help

View File

@ -68,6 +68,7 @@ drm-y := \
drm_rect.o \
drm_syncobj.o \
drm_sysfs.o \
drm_timeout.o \
drm_trace_points.o \
drm_vblank.o \
drm_vblank_work.o \
@ -79,7 +80,6 @@ drm-$(CONFIG_DRM_CLIENT) += \
drm_client_modeset.o \
drm_client_sysrq.o
drm-$(CONFIG_COMPAT) += drm_ioc32.o
drm-$(CONFIG_DRM_PANEL) += drm_panel.o
drm-$(CONFIG_OF) += drm_of.o
drm-$(CONFIG_PCI) += drm_pci.o
drm-$(CONFIG_DEBUG_FS) += \
@ -95,6 +95,7 @@ drm-$(CONFIG_DRM_DRAW) += drm_draw.o
drm-$(CONFIG_DRM_RAS) += drm_ras.o drm_ras_nl.o drm_ras_genl_family.o
obj-$(CONFIG_DRM) += drm.o
obj-$(CONFIG_DRM_PANEL) += drm_panel.o
obj-$(CONFIG_DRM_PANEL_ORIENTATION_QUIRKS) += drm_panel_orientation_quirks.o
obj-$(CONFIG_DRM_PANEL_BACKLIGHT_QUIRKS) += drm_panel_backlight_quirks.o
@ -155,10 +156,8 @@ drm_kms_helper-y := \
drm_self_refresh_helper.o \
drm_simple_kms_helper.o \
drm_vblank_helper.o
drm_kms_helper-$(CONFIG_DRM_PANEL_BRIDGE) += bridge/panel.o
drm_kms_helper-$(CONFIG_DRM_PANIC_HELPER) += drm_panic_helper.o
drm_kms_helper-$(CONFIG_DRM_PANIC_SCREEN_QR_CODE) += drm_panic_helper_qr.o
drm_kms_helper-$(CONFIG_DRM_FBDEV_EMULATION) += drm_fb_helper.o
obj-$(CONFIG_DRM_KMS_HELPER) += drm_kms_helper.o
#

View File

@ -8,6 +8,7 @@
#include <drm/drm_atomic_state_helper.h>
#include <drm/drm_bridge.h>
#include <drm/drm_mipi_dsi.h>
#include <drm/drm_panel.h>
#define DSI_GEN_HDR 0x6c
#define DSI_GEN_PLD_DATA 0x70

View File

@ -38,11 +38,11 @@
#include <linux/pci.h>
#include <linux/pm_runtime.h>
#include <drm/clients/drm_fbdev_helper.h>
#include <drm/drm_crtc_helper.h>
#include <drm/drm_damage_helper.h>
#include <drm/drm_drv.h>
#include <drm/drm_edid.h>
#include <drm/drm_fb_helper.h>
#include <drm/drm_gem_framebuffer_helper.h>
#include <drm/drm_fourcc.h>
#include <drm/drm_modeset_helper.h>

View File

@ -25,7 +25,6 @@
* Alex Deucher
* Jerome Glisse
*/
#include <linux/ktime.h>
#include <linux/module.h>
#include <linux/overflow.h>
#include <linux/pagemap.h>
@ -40,6 +39,7 @@
#include <drm/drm_gem_ttm_helper.h>
#include <drm/ttm/ttm_tt.h>
#include <drm/drm_syncobj.h>
#include <drm/drm_timeout.h>
#include "amdgpu.h"
#include "amdgpu_display.h"
@ -652,23 +652,11 @@ int amdgpu_gem_mmap_ioctl(struct drm_device *dev, void *data,
*/
unsigned long amdgpu_gem_timeout(uint64_t timeout_ns)
{
unsigned long timeout_jiffies;
ktime_t timeout;
/* clamp timeout if it's to large */
if (((int64_t)timeout_ns) < 0)
/* Map anything that doesn't fit in a s64 to an infinite wait */
if (timeout_ns > S64_MAX)
return MAX_SCHEDULE_TIMEOUT;
timeout = ktime_sub(ns_to_ktime(timeout_ns), ktime_get());
if (ktime_to_ns(timeout) < 0)
return 0;
timeout_jiffies = nsecs_to_jiffies(ktime_to_ns(timeout));
/* clamp timeout to avoid unsigned-> signed overflow */
if (timeout_jiffies > MAX_SCHEDULE_TIMEOUT)
return MAX_SCHEDULE_TIMEOUT - 1;
return timeout_jiffies;
return drm_timeout_abs_to_jiffies(timeout_ns);
}
int amdgpu_gem_wait_idle_ioctl(struct drm_device *dev, void *data,

View File

@ -28,8 +28,8 @@
#include "amdgpu.h"
#include <drm/amdgpu_drm.h>
#include <drm/clients/drm_fbdev_helper.h>
#include <drm/drm_drv.h>
#include <drm/drm_fb_helper.h>
#include "amdgpu_uvd.h"
#include "amdgpu_vce.h"
#include "atom.h"

View File

@ -98,7 +98,6 @@
#include <drm/drm_edid.h>
#include <drm/drm_eld.h>
#include <drm/drm_mode.h>
#include <drm/drm_utils.h>
#include <drm/drm_vblank.h>
#include <drm/drm_colorop.h>
#include <drm/drm_gem_atomic_helper.h>

View File

@ -41,7 +41,7 @@
#include <linux/backlight.h>
#include <linux/power_supply.h>
#include <drm/drm_edid.h>
#include <drm/drm_utils.h>
#include <drm/drm_panel_quirks.h>
#include <acpi/video.h>

View File

@ -62,7 +62,6 @@
#include <drm/drm_fixed.h>
#include <drm/drm_mode.h>
#include <drm/drm_probe_helper.h>
#include <drm/drm_utils.h>
#include <drm/display/drm_dp_mst_helper.h>
#include <drm/display/drm_hdmi_helper.h>
#include <drm/drm_privacy_screen_consumer.h>

View File

@ -15,6 +15,7 @@
#include <drm/drm_print.h>
#include <drm/drm_vblank.h>
#include <drm/drm_bridge.h>
#include <drm/drm_panel.h>
#include "komeda_dev.h"
#include "komeda_kms.h"

View File

@ -9,9 +9,9 @@
#include <linux/kernel.h>
#include <linux/module.h>
#include <drm/clients/drm_fbdev_helper.h>
#include <drm/drm_crtc_helper.h>
#include <drm/drm_drv.h>
#include <drm/drm_fb_helper.h>
#include <drm/drm_fourcc.h>
#include <drm/drm_print.h>

View File

@ -87,7 +87,6 @@ config DRM_FSL_LDB
depends on OF
depends on ARCH_MXC || COMPILE_TEST
select DRM_KMS_HELPER
select DRM_PANEL_BRIDGE
help
Support for i.MX8MP DPI-to-LVDS on-SoC encoder.
@ -151,6 +150,7 @@ config DRM_LONTIUM_LT8912B
config DRM_LONTIUM_LT9211
tristate "Lontium LT9211 DSI/LVDS/DPI bridge"
depends on OF
select DRM_PANEL
select DRM_PANEL_BRIDGE
select DRM_KMS_HELPER
select DRM_MIPI_DSI
@ -259,7 +259,7 @@ config DRM_NXP_PTN3460
tristate "NXP PTN3460 DP/LVDS bridge"
depends on OF
select DRM_KMS_HELPER
select DRM_PANEL
select DRM_PANEL_BRIDGE
help
NXP PTN3460 eDP-LVDS bridge chip driver.
@ -276,7 +276,7 @@ config DRM_OF_DISPLAY_MODE_BRIDGE
config DRM_PARADE_PS8622
tristate "Parade eDP/LVDS bridge"
depends on OF
select DRM_PANEL
select DRM_PANEL_BRIDGE
select DRM_KMS_HELPER
select BACKLIGHT_CLASS_DEVICE
help
@ -302,7 +302,6 @@ config DRM_SAMSUNG_DSIM
depends on OF && HAS_IOMEM
select DRM_KMS_HELPER
select DRM_MIPI_DSI
select DRM_PANEL_BRIDGE
select GENERIC_PHY
select GENERIC_PHY_MIPI_DPHY
help
@ -350,7 +349,6 @@ config DRM_SOLOMON_SSD2825
depends on SPI_MASTER && OF
select DRM_MIPI_DSI
select DRM_KMS_HELPER
select DRM_PANEL
help
Say Y here if you want support for the Solomon SSD2825 RGB/DSI
SPI bridge driver.
@ -388,7 +386,7 @@ config DRM_TOSHIBA_TC358764
depends on OF
select DRM_MIPI_DSI
select DRM_KMS_HELPER
select DRM_PANEL
select DRM_PANEL_BRIDGE
help
Toshiba TC358764 DSI/LVDS bridge driver.
@ -400,7 +398,6 @@ config DRM_TOSHIBA_TC358767
select DRM_KMS_HELPER
select REGMAP_I2C
select DRM_MIPI_DSI
select DRM_PANEL
help
Toshiba TC358767 eDP bridge chip driver.
@ -409,7 +406,6 @@ config DRM_TOSHIBA_TC358768
depends on OF
select DRM_KMS_HELPER
select REGMAP_I2C
select DRM_PANEL
select DRM_MIPI_DSI
select VIDEOMODE_HELPERS
help
@ -422,16 +418,17 @@ config DRM_TOSHIBA_TC358775
select DRM_DISPLAY_HELPER
select DRM_KMS_HELPER
select REGMAP_I2C
select DRM_PANEL
select DRM_PANEL_BRIDGE
select DRM_MIPI_DSI
help
Toshiba TC358775 DSI/LVDS bridge chip driver.
config DRM_TI_DLPC3433
tristate "TI DLPC3433 Display controller"
depends on DRM && DRM_PANEL
depends on DRM
depends on OF
select DRM_MIPI_DSI
select DRM_PANEL_BRIDGE
help
TI DLPC3433 is a MIPI DSI based display controller bridge
for processing high resolution DMD based projectors.
@ -494,6 +491,7 @@ config DRM_WAVESHARE_BRIDGE
tristate "Waveshare DSI bridge"
depends on OF
depends on BACKLIGHT_CLASS_DEVICE
select DRM_PANEL
select DRM_PANEL_BRIDGE
select DRM_KMS_HELPER
select DRM_MIPI_DSI

View File

@ -6,6 +6,7 @@ config DRM_ANALOGIX_ANX6345
select DRM_DISPLAY_DP_HELPER
select DRM_DISPLAY_HELPER
select DRM_KMS_HELPER
select DRM_PANEL
select REGMAP_I2C
help
ANX6345 is an ultra-low power Full-HD DisplayPort/eDP

View File

@ -29,7 +29,6 @@
#include <drm/drm_device.h>
#include <drm/drm_edid.h>
#include <drm/drm_of.h>
#include <drm/drm_panel.h>
#include <drm/drm_print.h>
#include <drm/drm_probe_helper.h>
@ -1567,15 +1566,6 @@ int analogix_dp_bind(struct analogix_dp_device *dp, struct drm_device *drm_dev)
if (ret)
goto err_unregister_aux;
if (dp->plat_data->panel) {
dp->plat_data->next_bridge = devm_drm_panel_bridge_add(dp->dev,
dp->plat_data->panel);
if (IS_ERR(dp->plat_data->next_bridge)) {
ret = PTR_ERR(dp->plat_data->next_bridge);
goto err_unregister_aux;
}
}
ret = drm_bridge_attach(dp->encoder, bridge, NULL, DRM_BRIDGE_ATTACH_NO_CONNECTOR);
if (ret) {
DRM_ERROR("failed to create bridge (%d)\n", ret);
@ -1647,6 +1637,11 @@ struct drm_dp_aux *analogix_dp_get_aux(struct analogix_dp_device *dp)
}
EXPORT_SYMBOL_GPL(analogix_dp_get_aux);
static void analogix_dp_put_bridge(void *data)
{
drm_bridge_put(data);
}
static int analogix_dp_aux_done_probing(struct drm_dp_aux *aux)
{
struct analogix_dp_device *dp = to_dp(aux);
@ -1655,14 +1650,25 @@ static int analogix_dp_aux_done_probing(struct drm_dp_aux *aux)
int ret;
/*
* If drm_of_find_panel_or_bridge() returns -ENODEV, there may be no valid panel
* or bridge nodes. The driver should go on for the driver-free bridge or the DP
* mode applications.
* If of_drm_get_bridge_by_endpoint() returns -ENODEV, there may be no
* valid panel or bridge nodes. The driver should go on for the
* driver-free bridge or the DP mode applications.
*/
ret = drm_of_find_panel_or_bridge(dp->dev->of_node, port, 0,
&plat_data->panel, &plat_data->next_bridge);
if (ret && ret != -ENODEV)
return ret;
plat_data->next_bridge = of_drm_get_bridge_by_endpoint(dp->dev->of_node,
port, 0);
if (IS_ERR(plat_data->next_bridge)) {
if (PTR_ERR(plat_data->next_bridge) == -ENODEV)
plat_data->next_bridge = NULL;
else
return PTR_ERR(plat_data->next_bridge);
}
if (plat_data->next_bridge) {
ret = devm_add_action_or_reset(dp->dev, analogix_dp_put_bridge,
plat_data->next_bridge);
if (ret)
return ret;
}
return component_add(dp->dev, plat_data->ops);
}

View File

@ -12,6 +12,7 @@
#include <drm/drm_atomic_helper.h>
#include <drm/drm_bridge.h>
#include <drm/bridge/aux-bridge.h>
#include <drm/drm_panel.h>
static DEFINE_IDA(drm_aux_bridge_ida);

View File

@ -7,6 +7,7 @@
#include <drm/drm_atomic_helper.h>
#include <drm/drm_drv.h>
#include <drm/drm_panel.h>
#include <drm/drm_probe_helper.h>
#include <video/mipi_display.h>
#include <video/videomode.h>

View File

@ -15,7 +15,6 @@
#include <drm/drm_atomic_helper.h>
#include <drm/drm_bridge.h>
#include <drm/drm_of.h>
#include <drm/drm_panel.h>
#define LDB_CTRL_CH0_ENABLE BIT(0)
#define LDB_CTRL_CH0_DI_SELECT BIT(1)
@ -86,7 +85,6 @@ static const struct fsl_ldb_devdata fsl_ldb_devdata[] = {
struct fsl_ldb {
struct device *dev;
struct drm_bridge bridge;
struct drm_bridge *panel_bridge;
struct clk *clk;
struct regmap *regmap;
const struct fsl_ldb_devdata *devdata;
@ -119,7 +117,7 @@ static int fsl_ldb_attach(struct drm_bridge *bridge,
{
struct fsl_ldb *fsl_ldb = to_fsl_ldb(bridge);
return drm_bridge_attach(encoder, fsl_ldb->panel_bridge,
return drm_bridge_attach(encoder, fsl_ldb->bridge.next_bridge,
bridge, flags);
}
@ -298,7 +296,6 @@ static int fsl_ldb_probe(struct platform_device *pdev)
struct device *dev = &pdev->dev;
struct device_node *panel_node;
struct device_node *remote1, *remote2;
struct drm_panel *panel;
struct fsl_ldb *fsl_ldb;
int dual_link;
@ -339,19 +336,13 @@ static int fsl_ldb_probe(struct platform_device *pdev)
fsl_ldb_is_dual(fsl_ldb) ? "dual-link mode" :
fsl_ldb->ch0_enabled ? "channel 0" : "channel 1");
panel = of_drm_find_panel(panel_node);
of_node_put(panel_node);
if (IS_ERR(panel))
return PTR_ERR(panel);
if (of_property_present(dev->of_node, "nxp,enable-termination-resistor"))
fsl_ldb->use_termination_resistor = true;
fsl_ldb->panel_bridge = devm_drm_panel_bridge_add(dev, panel);
drm_panel_put(panel);
if (IS_ERR(fsl_ldb->panel_bridge))
return PTR_ERR(fsl_ldb->panel_bridge);
fsl_ldb->bridge.next_bridge = of_drm_find_and_get_bridge(panel_node);
of_node_put(panel_node);
if (!fsl_ldb->bridge.next_bridge)
return -ENODEV;
if (fsl_ldb_is_dual(fsl_ldb)) {
struct device_node *port1, *port2;

View File

@ -16,6 +16,7 @@
#include <drm/drm_atomic_state_helper.h>
#include <drm/drm_bridge.h>
#include <drm/drm_panel.h>
#define IMX93_DISPLAY_MUX_REG 0x60
#define PARALLEL_DISP_FORMAT GENMASK(10, 8)

View File

@ -1,563 +0,0 @@
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright (C) 2016 Laurent Pinchart <laurent.pinchart@ideasonboard.com>
* Copyright (C) 2017 Broadcom
*/
#include <linux/debugfs.h>
#include <linux/export.h>
#include <drm/drm_atomic_helper.h>
#include <drm/drm_bridge.h>
#include <drm/drm_connector.h>
#include <drm/drm_encoder.h>
#include <drm/drm_managed.h>
#include <drm/drm_modeset_helper_vtables.h>
#include <drm/drm_of.h>
#include <drm/drm_panel.h>
#include <drm/drm_print.h>
#include <drm/drm_probe_helper.h>
struct panel_bridge {
struct drm_bridge bridge;
struct drm_connector connector;
struct drm_panel *panel;
u32 connector_type;
};
static inline struct panel_bridge *
drm_bridge_to_panel_bridge(struct drm_bridge *bridge)
{
return container_of(bridge, struct panel_bridge, bridge);
}
static inline struct panel_bridge *
drm_connector_to_panel_bridge(struct drm_connector *connector)
{
return container_of(connector, struct panel_bridge, connector);
}
static int panel_bridge_connector_get_modes(struct drm_connector *connector)
{
struct panel_bridge *panel_bridge =
drm_connector_to_panel_bridge(connector);
return drm_panel_get_modes(panel_bridge->panel, connector);
}
static const struct drm_connector_helper_funcs
panel_bridge_connector_helper_funcs = {
.get_modes = panel_bridge_connector_get_modes,
};
static const struct drm_connector_funcs panel_bridge_connector_funcs = {
.reset = drm_atomic_helper_connector_reset,
.fill_modes = drm_helper_probe_single_connector_modes,
.destroy = drm_connector_cleanup,
.atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
.atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
};
static int panel_bridge_attach(struct drm_bridge *bridge,
struct drm_encoder *encoder,
enum drm_bridge_attach_flags flags)
{
struct panel_bridge *panel_bridge = drm_bridge_to_panel_bridge(bridge);
struct drm_connector *connector = &panel_bridge->connector;
int ret;
if (flags & DRM_BRIDGE_ATTACH_NO_CONNECTOR)
return 0;
drm_connector_helper_add(connector,
&panel_bridge_connector_helper_funcs);
ret = drm_connector_init(bridge->dev, connector,
&panel_bridge_connector_funcs,
panel_bridge->connector_type);
if (ret) {
DRM_ERROR("Failed to initialize connector\n");
return ret;
}
drm_panel_bridge_set_orientation(connector, bridge);
drm_connector_attach_encoder(&panel_bridge->connector,
encoder);
if (bridge->dev->registered) {
if (connector->funcs->reset)
connector->funcs->reset(connector);
drm_connector_register(connector);
}
return 0;
}
static void panel_bridge_detach(struct drm_bridge *bridge)
{
struct panel_bridge *panel_bridge = drm_bridge_to_panel_bridge(bridge);
struct drm_connector *connector = &panel_bridge->connector;
/*
* Cleanup the connector if we know it was initialized.
*
* FIXME: This wouldn't be needed if the panel_bridge structure was
* allocated with drmm_kzalloc(). This might be tricky since the
* drm_device pointer can only be retrieved when the bridge is attached.
*/
if (connector->dev)
drm_connector_cleanup(connector);
}
static void panel_bridge_atomic_pre_enable(struct drm_bridge *bridge,
struct drm_atomic_commit *atomic_state)
{
struct panel_bridge *panel_bridge = drm_bridge_to_panel_bridge(bridge);
struct drm_encoder *encoder = bridge->encoder;
struct drm_crtc *crtc;
struct drm_crtc_state *old_crtc_state;
crtc = drm_atomic_get_new_crtc_for_encoder(atomic_state, encoder);
if (!crtc)
return;
old_crtc_state = drm_atomic_get_old_crtc_state(atomic_state, crtc);
if (old_crtc_state && old_crtc_state->self_refresh_active)
return;
drm_panel_prepare(panel_bridge->panel);
}
static void panel_bridge_atomic_enable(struct drm_bridge *bridge,
struct drm_atomic_commit *atomic_state)
{
struct panel_bridge *panel_bridge = drm_bridge_to_panel_bridge(bridge);
struct drm_encoder *encoder = bridge->encoder;
struct drm_crtc *crtc;
struct drm_crtc_state *old_crtc_state;
crtc = drm_atomic_get_new_crtc_for_encoder(atomic_state, encoder);
if (!crtc)
return;
old_crtc_state = drm_atomic_get_old_crtc_state(atomic_state, crtc);
if (old_crtc_state && old_crtc_state->self_refresh_active)
return;
drm_panel_enable(panel_bridge->panel);
}
static void panel_bridge_atomic_disable(struct drm_bridge *bridge,
struct drm_atomic_commit *atomic_state)
{
struct panel_bridge *panel_bridge = drm_bridge_to_panel_bridge(bridge);
struct drm_encoder *encoder = bridge->encoder;
struct drm_crtc *crtc;
struct drm_crtc_state *new_crtc_state;
crtc = drm_atomic_get_old_crtc_for_encoder(atomic_state, encoder);
if (!crtc)
return;
new_crtc_state = drm_atomic_get_new_crtc_state(atomic_state, crtc);
if (new_crtc_state && new_crtc_state->self_refresh_active)
return;
drm_panel_disable(panel_bridge->panel);
}
static void panel_bridge_atomic_post_disable(struct drm_bridge *bridge,
struct drm_atomic_commit *atomic_state)
{
struct panel_bridge *panel_bridge = drm_bridge_to_panel_bridge(bridge);
struct drm_encoder *encoder = bridge->encoder;
struct drm_crtc *crtc;
struct drm_crtc_state *new_crtc_state;
crtc = drm_atomic_get_old_crtc_for_encoder(atomic_state, encoder);
if (!crtc)
return;
new_crtc_state = drm_atomic_get_new_crtc_state(atomic_state, crtc);
if (new_crtc_state && new_crtc_state->self_refresh_active)
return;
drm_panel_unprepare(panel_bridge->panel);
}
static int panel_bridge_get_modes(struct drm_bridge *bridge,
struct drm_connector *connector)
{
struct panel_bridge *panel_bridge = drm_bridge_to_panel_bridge(bridge);
return drm_panel_get_modes(panel_bridge->panel, connector);
}
static void panel_bridge_debugfs_init(struct drm_bridge *bridge,
struct dentry *root)
{
struct panel_bridge *panel_bridge = drm_bridge_to_panel_bridge(bridge);
struct drm_panel *panel = panel_bridge->panel;
root = debugfs_create_dir("panel", root);
if (panel->funcs->debugfs_init)
panel->funcs->debugfs_init(panel, root);
}
static const struct drm_bridge_funcs panel_bridge_bridge_funcs = {
.attach = panel_bridge_attach,
.detach = panel_bridge_detach,
.atomic_pre_enable = panel_bridge_atomic_pre_enable,
.atomic_enable = panel_bridge_atomic_enable,
.atomic_disable = panel_bridge_atomic_disable,
.atomic_post_disable = panel_bridge_atomic_post_disable,
.get_modes = panel_bridge_get_modes,
.atomic_create_state = drm_atomic_helper_bridge_create_state,
.atomic_duplicate_state = drm_atomic_helper_bridge_duplicate_state,
.atomic_destroy_state = drm_atomic_helper_bridge_destroy_state,
.atomic_get_input_bus_fmts = drm_atomic_helper_bridge_propagate_bus_fmt,
.debugfs_init = panel_bridge_debugfs_init,
};
/**
* drm_bridge_is_panel - Checks if a drm_bridge is a panel_bridge.
*
* @bridge: The drm_bridge to be checked.
*
* Returns true if the bridge is a panel bridge, or false otherwise.
*/
bool drm_bridge_is_panel(const struct drm_bridge *bridge)
{
return bridge->funcs == &panel_bridge_bridge_funcs;
}
EXPORT_SYMBOL(drm_bridge_is_panel);
/**
* drm_panel_bridge_add - Creates a &drm_bridge and &drm_connector that
* just calls the appropriate functions from &drm_panel.
*
* @panel: The drm_panel being wrapped. Must be non-NULL.
*
* For drivers converting from directly using drm_panel: The expected
* usage pattern is that during either encoder module probe or DSI
* host attach, a drm_panel will be looked up through
* drm_of_find_panel_or_bridge(). drm_panel_bridge_add() is used to
* wrap that panel in the new bridge, and the result can then be
* passed to drm_bridge_attach(). The drm_panel_prepare() and related
* functions can be dropped from the encoder driver (they're now
* called by the KMS helpers before calling into the encoder), along
* with connector creation. When done with the bridge (after
* drm_mode_config_cleanup() if the bridge has already been attached), then
* drm_panel_bridge_remove() to free it.
*
* The connector type is set to @panel->connector_type, which must be set to a
* known type. Calling this function with a panel whose connector type is
* DRM_MODE_CONNECTOR_Unknown will return ERR_PTR(-EINVAL).
*
* See devm_drm_panel_bridge_add() for an automatically managed version of this
* function.
*/
struct drm_bridge *drm_panel_bridge_add(struct drm_panel *panel)
{
if (WARN_ON(panel->connector_type == DRM_MODE_CONNECTOR_Unknown))
return ERR_PTR(-EINVAL);
return drm_panel_bridge_add_typed(panel, panel->connector_type);
}
EXPORT_SYMBOL(drm_panel_bridge_add);
/**
* drm_panel_bridge_add_typed - Creates a &drm_bridge and &drm_connector with
* an explicit connector type.
* @panel: The drm_panel being wrapped. Must be non-NULL.
* @connector_type: The connector type (DRM_MODE_CONNECTOR_*)
*
* This is just like drm_panel_bridge_add(), but forces the connector type to
* @connector_type instead of infering it from the panel.
*
* This function is deprecated and should not be used in new drivers. Use
* drm_panel_bridge_add() instead, and fix panel drivers as necessary if they
* don't report a connector type.
*/
struct drm_bridge *drm_panel_bridge_add_typed(struct drm_panel *panel,
u32 connector_type)
{
struct panel_bridge *panel_bridge;
if (!panel)
return ERR_PTR(-EINVAL);
panel_bridge = devm_drm_bridge_alloc(panel->dev, struct panel_bridge, bridge,
&panel_bridge_bridge_funcs);
if (IS_ERR(panel_bridge))
return (void *)panel_bridge;
panel_bridge->connector_type = connector_type;
panel_bridge->panel = drm_panel_get(panel);
panel_bridge->bridge.of_node = panel->dev->of_node;
panel_bridge->bridge.ops = DRM_BRIDGE_OP_MODES;
panel_bridge->bridge.type = connector_type;
panel_bridge->bridge.pre_enable_prev_first = panel->prepare_prev_first;
drm_bridge_add(&panel_bridge->bridge);
return &panel_bridge->bridge;
}
EXPORT_SYMBOL(drm_panel_bridge_add_typed);
/**
* drm_panel_bridge_remove - Unregisters and frees a drm_bridge
* created by drm_panel_bridge_add().
*
* @bridge: The drm_bridge being freed.
*/
void drm_panel_bridge_remove(struct drm_bridge *bridge)
{
struct panel_bridge *panel_bridge;
struct drm_panel *panel;
if (!bridge)
return;
if (!drm_bridge_is_panel(bridge)) {
drm_warn(bridge->dev, "%s: called on non-panel bridge!\n", __func__);
return;
}
panel_bridge = drm_bridge_to_panel_bridge(bridge);
panel = panel_bridge->panel;
drm_bridge_remove(bridge);
/* TODO remove this after reworking panel_bridge lifetime */
devm_drm_put_bridge(panel->dev, bridge);
drm_panel_put(panel);
}
EXPORT_SYMBOL(drm_panel_bridge_remove);
/**
* drm_panel_bridge_set_orientation - Set the connector's panel orientation
* from the bridge that can be transformed to panel bridge.
*
* @connector: The connector to be set panel orientation.
* @bridge: The drm_bridge to be transformed to panel bridge.
*
* Returns 0 on success, negative errno on failure.
*/
int drm_panel_bridge_set_orientation(struct drm_connector *connector,
struct drm_bridge *bridge)
{
struct panel_bridge *panel_bridge;
panel_bridge = drm_bridge_to_panel_bridge(bridge);
return drm_connector_set_orientation_from_panel(connector,
panel_bridge->panel);
}
EXPORT_SYMBOL(drm_panel_bridge_set_orientation);
static void devm_drm_panel_bridge_release(struct device *dev, void *res)
{
struct drm_bridge *bridge = *(struct drm_bridge **)res;
struct panel_bridge *panel_bridge;
struct drm_panel *panel;
if (!bridge)
return;
panel_bridge = drm_bridge_to_panel_bridge(bridge);
panel = panel_bridge->panel;
drm_bridge_remove(bridge);
drm_panel_put(panel);
}
/**
* devm_drm_panel_bridge_add - Creates a managed &drm_bridge and &drm_connector
* that just calls the appropriate functions from &drm_panel.
* @dev: device to tie the bridge lifetime to
* @panel: The drm_panel being wrapped. Must be non-NULL.
*
* This is the managed version of drm_panel_bridge_add() which automatically
* calls drm_panel_bridge_remove() when @dev is unbound.
*/
struct drm_bridge *devm_drm_panel_bridge_add(struct device *dev,
struct drm_panel *panel)
{
if (WARN_ON(panel->connector_type == DRM_MODE_CONNECTOR_Unknown))
return ERR_PTR(-EINVAL);
return devm_drm_panel_bridge_add_typed(dev, panel,
panel->connector_type);
}
EXPORT_SYMBOL(devm_drm_panel_bridge_add);
/**
* devm_drm_panel_bridge_add_typed - Creates a managed &drm_bridge and
* &drm_connector with an explicit connector type.
* @dev: device to tie the bridge lifetime to
* @panel: The drm_panel being wrapped. Must be non-NULL.
* @connector_type: The connector type (DRM_MODE_CONNECTOR_*)
*
* This is just like devm_drm_panel_bridge_add(), but forces the connector type
* to @connector_type instead of infering it from the panel.
*
* This function is deprecated and should not be used in new drivers. Use
* devm_drm_panel_bridge_add() instead, and fix panel drivers as necessary if
* they don't report a connector type.
*/
struct drm_bridge *devm_drm_panel_bridge_add_typed(struct device *dev,
struct drm_panel *panel,
u32 connector_type)
{
struct drm_bridge **ptr, *bridge;
ptr = devres_alloc(devm_drm_panel_bridge_release, sizeof(*ptr),
GFP_KERNEL);
if (!ptr)
return ERR_PTR(-ENOMEM);
bridge = drm_panel_bridge_add_typed(panel, connector_type);
if (IS_ERR(bridge)) {
devres_free(ptr);
return bridge;
}
*ptr = bridge;
devres_add(dev, ptr);
return bridge;
}
EXPORT_SYMBOL(devm_drm_panel_bridge_add_typed);
static void drmm_drm_panel_bridge_release(struct drm_device *drm, void *ptr)
{
struct drm_bridge *bridge = ptr;
drm_panel_bridge_remove(bridge);
}
/**
* drmm_panel_bridge_add - Creates a DRM-managed &drm_bridge and
* &drm_connector that just calls the
* appropriate functions from &drm_panel.
*
* @drm: DRM device to tie the bridge lifetime to
* @panel: The drm_panel being wrapped. Must be non-NULL.
*
* This is the DRM-managed version of drm_panel_bridge_add() which
* automatically calls drm_panel_bridge_remove() when @dev is cleaned
* up.
*/
struct drm_bridge *drmm_panel_bridge_add(struct drm_device *drm,
struct drm_panel *panel)
{
struct drm_bridge *bridge;
int ret;
bridge = drm_panel_bridge_add_typed(panel, panel->connector_type);
if (IS_ERR(bridge))
return bridge;
ret = drmm_add_action_or_reset(drm, drmm_drm_panel_bridge_release,
bridge);
if (ret)
return ERR_PTR(ret);
return bridge;
}
EXPORT_SYMBOL(drmm_panel_bridge_add);
/**
* drm_panel_bridge_connector - return the connector for the panel bridge
* @bridge: The drm_bridge.
*
* drm_panel_bridge creates the connector.
* This function gives external access to the connector.
*
* Returns: Pointer to drm_connector
*/
struct drm_connector *drm_panel_bridge_connector(struct drm_bridge *bridge)
{
struct panel_bridge *panel_bridge;
panel_bridge = drm_bridge_to_panel_bridge(bridge);
return &panel_bridge->connector;
}
EXPORT_SYMBOL(drm_panel_bridge_connector);
#ifdef CONFIG_OF
/**
* devm_drm_of_get_bridge - Return next bridge in the chain
* @dev: device to tie the bridge lifetime to
* @np: device tree node containing encoder output ports
* @port: port in the device tree node
* @endpoint: endpoint in the device tree node
*
* Given a DT node's port and endpoint number, finds the connected node
* and returns the associated bridge if any, or creates and returns a
* drm panel bridge instance if a panel is connected.
*
* Returns a pointer to the bridge if successful, or an error pointer
* otherwise.
*/
struct drm_bridge *devm_drm_of_get_bridge(struct device *dev,
struct device_node *np,
u32 port, u32 endpoint)
{
struct drm_bridge *bridge;
struct drm_panel *panel;
int ret;
ret = drm_of_find_panel_or_bridge(np, port, endpoint,
&panel, &bridge);
if (ret)
return ERR_PTR(ret);
if (panel) {
bridge = devm_drm_panel_bridge_add(dev, panel);
drm_panel_put(panel);
}
return bridge;
}
EXPORT_SYMBOL(devm_drm_of_get_bridge);
/**
* drmm_of_get_bridge - Return next bridge in the chain
* @drm: device to tie the bridge lifetime to
* @np: device tree node containing encoder output ports
* @port: port in the device tree node
* @endpoint: endpoint in the device tree node
*
* Given a DT node's port and endpoint number, finds the connected node
* and returns the associated bridge if any, or creates and returns a
* drm panel bridge instance if a panel is connected.
*
* Returns a drmm managed pointer to the bridge if successful, or an error
* pointer otherwise.
*/
struct drm_bridge *drmm_of_get_bridge(struct drm_device *drm,
struct device_node *np,
u32 port, u32 endpoint)
{
struct drm_bridge *bridge;
struct drm_panel *panel;
int ret;
ret = drm_of_find_panel_or_bridge(np, port, endpoint,
&panel, &bridge);
if (ret)
return ERR_PTR(ret);
if (panel) {
bridge = drmm_panel_bridge_add(drm, panel);
drm_panel_put(panel);
}
return bridge;
}
EXPORT_SYMBOL(drmm_of_get_bridge);
#endif

View File

@ -25,7 +25,6 @@
#include <video/mipi_display.h>
#include <drm/bridge/samsung-dsim.h>
#include <drm/drm_panel.h>
#include <drm/drm_print.h>
/* returns true iff both arguments logically differs */
@ -1898,7 +1897,6 @@ static int samsung_dsim_host_attach(struct mipi_dsi_host *host,
struct device *dev = dsi->dev;
struct device_node *np = dev->of_node;
struct device_node *remote;
struct drm_panel *panel;
int ret = 0;
/*
@ -1913,7 +1911,7 @@ static int samsung_dsim_host_attach(struct mipi_dsi_host *host,
of_node_name_eq(remote, "ports"))
continue;
goto of_find_panel_or_bridge;
goto of_find_bridge;
}
/*
@ -1927,25 +1925,13 @@ static int samsung_dsim_host_attach(struct mipi_dsi_host *host,
remote = of_graph_get_remote_node(np, 1, 0);
of_find_panel_or_bridge:
of_find_bridge:
if (!remote)
return -ENODEV;
panel = of_drm_find_panel(remote);
if (!IS_ERR(panel)) {
next_bridge = devm_drm_panel_bridge_add(dev, panel);
drm_panel_put(panel);
if (IS_ERR(next_bridge)) {
ret = PTR_ERR(next_bridge);
next_bridge = NULL; // Inhibit the cleanup action on an ERR_PTR
} else {
drm_bridge_get(next_bridge);
}
} else {
next_bridge = of_drm_find_and_get_bridge(remote);
if (!next_bridge)
ret = -EINVAL;
}
next_bridge = of_drm_find_and_get_bridge(remote);
if (!next_bridge)
ret = -EINVAL;
of_node_put(remote);

View File

@ -17,7 +17,6 @@
#include <drm/drm_drv.h>
#include <drm/drm_mipi_dsi.h>
#include <drm/drm_of.h>
#include <drm/drm_panel.h>
#include <video/mipi_display.h>
#define SSD2825_DEVICE_ID_REG 0xb0
@ -114,7 +113,6 @@ static const struct regulator_bulk_data ssd2825_supplies[] = {
struct ssd2825_dsi_output {
struct mipi_dsi_device *dev;
struct drm_panel *panel;
struct drm_bridge *bridge;
};
@ -277,7 +275,6 @@ static int ssd2825_dsi_host_attach(struct mipi_dsi_host *host, struct mipi_dsi_d
{
struct ssd2825_priv *priv = dsi_host_to_ssd2825(host);
struct drm_bridge *bridge;
struct drm_panel *panel;
struct device_node *ep;
int ret;
@ -295,20 +292,14 @@ static int ssd2825_dsi_host_attach(struct mipi_dsi_host *host, struct mipi_dsi_d
return -EOPNOTSUPP;
}
ret = drm_of_find_panel_or_bridge(host->dev->of_node, 1, 0, &panel, &bridge);
if (ret)
return ret;
bridge = of_drm_get_bridge_by_endpoint(host->dev->of_node, 1, 0);
if (IS_ERR(bridge))
return PTR_ERR(bridge);
if (panel) {
bridge = drm_panel_bridge_add_typed(panel, DRM_MODE_CONNECTOR_DSI);
drm_panel_put(panel);
if (IS_ERR(bridge))
return PTR_ERR(bridge);
}
bridge->type = DRM_MODE_CONNECTOR_DSI;
priv->output.dev = dev;
priv->output.bridge = bridge;
priv->output.panel = panel;
priv->dsi_lanes = dev->lanes;
@ -333,8 +324,7 @@ static int ssd2825_dsi_host_detach(struct mipi_dsi_host *host, struct mipi_dsi_d
struct ssd2825_priv *priv = dsi_host_to_ssd2825(host);
drm_bridge_remove(&priv->bridge);
if (priv->output.panel)
drm_panel_bridge_remove(priv->output.bridge);
drm_bridge_put(priv->output.bridge);
return 0;
}
@ -579,9 +569,6 @@ static void ssd2825_bridge_atomic_pre_enable(struct drm_bridge *bridge,
/* Initial DSI configuration register set */
ssd2825_write_reg(priv, SSD2825_CONFIGURATION_REG, config);
ssd2825_write_reg(priv, SSD2825_VC_CTRL_REG, 0);
if (priv->output.panel)
drm_panel_enable(priv->output.panel);
}
static void ssd2825_bridge_atomic_enable(struct drm_bridge *bridge,

View File

@ -244,7 +244,6 @@ struct debugfs_entries {
struct dw_mipi_dsi {
struct drm_bridge bridge;
struct mipi_dsi_host dsi_host;
struct drm_bridge *panel_bridge;
struct device *dev;
void __iomem *base;
@ -333,12 +332,12 @@ static int dw_mipi_dsi_host_attach(struct mipi_dsi_host *host,
dsi->format = device->format;
dsi->mode_flags = device->mode_flags;
bridge = devm_drm_of_get_bridge(dsi->dev, dsi->dev->of_node, 1, 0);
bridge = of_drm_get_bridge_by_endpoint(dsi->dev->of_node, 1, 0);
if (IS_ERR(bridge))
return PTR_ERR(bridge);
bridge->pre_enable_prev_first = true;
dsi->panel_bridge = bridge;
dsi->bridge.next_bridge = bridge;
drm_bridge_add(&dsi->bridge);
@ -352,6 +351,7 @@ static int dw_mipi_dsi_host_attach(struct mipi_dsi_host *host,
err_remove_bridge:
drm_bridge_remove(&dsi->bridge);
drm_bridge_clear_and_put(&dsi->bridge.next_bridge);
return ret;
}
@ -368,9 +368,8 @@ static int dw_mipi_dsi_host_detach(struct mipi_dsi_host *host,
return ret;
}
drm_of_panel_bridge_remove(host->dev->of_node, 1, 0);
drm_bridge_remove(&dsi->bridge);
drm_bridge_clear_and_put(&dsi->bridge.next_bridge);
return 0;
}
@ -1086,7 +1085,7 @@ static int dw_mipi_dsi_bridge_attach(struct drm_bridge *bridge,
encoder->encoder_type = DRM_MODE_ENCODER_DSI;
/* Attach the panel-bridge to the dsi bridge */
return drm_bridge_attach(encoder, dsi->panel_bridge, bridge,
return drm_bridge_attach(encoder, dsi->bridge.next_bridge, bridge,
flags);
}

View File

@ -192,7 +192,6 @@ struct cmd_header {
struct dw_mipi_dsi2 {
struct drm_bridge bridge;
struct mipi_dsi_host dsi_host;
struct drm_bridge *panel_bridge;
struct device *dev;
struct regmap *regmap;
struct clk *pclk;
@ -528,12 +527,12 @@ static int dw_mipi_dsi2_host_attach(struct mipi_dsi_host *host,
dsi2->format = device->format;
dsi2->mode_flags = device->mode_flags;
bridge = devm_drm_of_get_bridge(dsi2->dev, dsi2->dev->of_node, 1, 0);
bridge = of_drm_get_bridge_by_endpoint(dsi2->dev->of_node, 1, 0);
if (IS_ERR(bridge))
return PTR_ERR(bridge);
bridge->pre_enable_prev_first = true;
dsi2->panel_bridge = bridge;
dsi2->bridge.next_bridge = bridge;
drm_bridge_add(&dsi2->bridge);
@ -547,6 +546,7 @@ static int dw_mipi_dsi2_host_attach(struct mipi_dsi_host *host,
err_remove_bridge:
drm_bridge_remove(&dsi2->bridge);
drm_bridge_clear_and_put(&dsi2->bridge.next_bridge);
return ret;
}
@ -564,8 +564,7 @@ static int dw_mipi_dsi2_host_detach(struct mipi_dsi_host *host,
}
drm_bridge_remove(&dsi2->bridge);
drm_of_panel_bridge_remove(host->dev->of_node, 1, 0);
drm_bridge_clear_and_put(&dsi2->bridge.next_bridge);
return 0;
}
@ -884,7 +883,7 @@ static int dw_mipi_dsi2_bridge_attach(struct drm_bridge *bridge,
encoder->encoder_type = DRM_MODE_ENCODER_DSI;
/* Attach the panel-bridge to the dsi bridge */
return drm_bridge_attach(encoder, dsi2->panel_bridge, bridge,
return drm_bridge_attach(encoder, dsi2->bridge.next_bridge, bridge,
flags);
}

View File

@ -35,7 +35,6 @@
#include <drm/drm_edid.h>
#include <drm/drm_mipi_dsi.h>
#include <drm/drm_of.h>
#include <drm/drm_panel.h>
#include <drm/drm_print.h>
#include <drm/drm_probe_helper.h>
@ -370,7 +369,6 @@ struct tc_data {
struct drm_dp_aux aux;
struct drm_bridge bridge;
struct drm_bridge *panel_bridge;
struct drm_connector connector;
struct mipi_dsi_device *dsi;
@ -1742,8 +1740,8 @@ static int tc_connector_get_modes(struct drm_connector *connector)
return 0;
}
if (tc->panel_bridge) {
num_modes = drm_bridge_get_modes(tc->panel_bridge, connector);
if (tc->bridge.next_bridge) {
num_modes = drm_bridge_get_modes(tc->bridge.next_bridge, connector);
if (num_modes > 0)
return num_modes;
}
@ -1788,7 +1786,7 @@ tc_connector_detect(struct drm_connector *connector, bool force)
if (tc->hpd_pin >= 0)
return tc_bridge_detect(&tc->bridge, connector);
if (tc->panel_bridge)
if (tc->bridge.next_bridge)
return connector_status_connected;
else
return connector_status_unknown;
@ -1809,10 +1807,10 @@ static int tc_dpi_bridge_attach(struct drm_bridge *bridge,
{
struct tc_data *tc = bridge_to_tc(bridge);
if (!tc->panel_bridge)
if (!tc->bridge.next_bridge)
return 0;
return drm_bridge_attach(tc->bridge.encoder, tc->panel_bridge,
return drm_bridge_attach(tc->bridge.encoder, tc->bridge.next_bridge,
&tc->bridge, flags);
}
@ -1825,9 +1823,9 @@ static int tc_edp_bridge_attach(struct drm_bridge *bridge,
struct drm_device *drm = bridge->dev;
int ret;
if (tc->panel_bridge) {
if (tc->bridge.next_bridge) {
/* If a connector is required then this driver shall create it */
ret = drm_bridge_attach(tc->bridge.encoder, tc->panel_bridge,
ret = drm_bridge_attach(tc->bridge.encoder, tc->bridge.next_bridge,
&tc->bridge, flags | DRM_BRIDGE_ATTACH_NO_CONNECTOR);
if (ret)
return ret;
@ -2318,53 +2316,31 @@ static int tc_probe_dpi_bridge_endpoint(struct tc_data *tc)
{
struct device *dev = tc->dev;
struct drm_bridge *bridge;
struct drm_panel *panel;
int ret;
/* port@1 is the DPI input/output port */
ret = drm_of_find_panel_or_bridge(dev->of_node, 1, 0, &panel, &bridge);
if (ret && ret != -ENODEV)
return dev_err_probe(dev, ret,
"Could not find DPI panel or bridge\n");
bridge = of_drm_get_bridge_by_endpoint(dev->of_node, 1, 0);
if (IS_ERR(bridge))
return dev_err_probe(dev, PTR_ERR(bridge),
"Could not find DPI bridge\n");
if (panel) {
bridge = devm_drm_panel_bridge_add(dev, panel);
drm_panel_put(panel);
if (IS_ERR(bridge))
return PTR_ERR(bridge);
}
tc->bridge.next_bridge = bridge;
tc->bridge.type = DRM_MODE_CONNECTOR_DPI;
if (bridge) {
tc->panel_bridge = bridge;
tc->bridge.type = DRM_MODE_CONNECTOR_DPI;
return 0;
}
return ret;
return 0;
}
static int tc_probe_edp_bridge_endpoint(struct tc_data *tc)
{
struct device *dev = tc->dev;
struct drm_panel *panel;
int ret;
struct drm_bridge *bridge;
/* port@2 is the output port */
ret = drm_of_find_panel_or_bridge(dev->of_node, 2, 0, &panel, NULL);
if (ret && ret != -ENODEV)
return dev_err_probe(dev, ret,
"Could not find DSI panel or bridge\n");
bridge = of_drm_get_bridge_by_endpoint(dev->of_node, 2, 0);
if (IS_ERR(bridge) && PTR_ERR(bridge) != -ENODEV)
return dev_err_probe(dev, PTR_ERR(bridge), "Could not find DSI bridge\n");
if (panel) {
struct drm_bridge *panel_bridge;
panel_bridge = devm_drm_panel_bridge_add(dev, panel);
drm_panel_put(panel);
if (IS_ERR(panel_bridge))
return PTR_ERR(panel_bridge);
tc->panel_bridge = panel_bridge;
if (!IS_ERR(bridge)) {
tc->bridge.next_bridge = bridge;
tc->bridge.type = DRM_MODE_CONNECTOR_eDP;
} else {
tc->bridge.type = DRM_MODE_CONNECTOR_DisplayPort;

View File

@ -22,7 +22,6 @@
#include <drm/drm_drv.h>
#include <drm/drm_mipi_dsi.h>
#include <drm/drm_of.h>
#include <drm/drm_panel.h>
#include <video/mipi_display.h>
#include <video/videomode.h>
@ -137,7 +136,6 @@ static const char * const tc358768_supplies[] = {
struct tc358768_dsi_output {
struct mipi_dsi_device *dev;
struct drm_panel *panel;
struct drm_bridge *bridge;
};
@ -443,7 +441,6 @@ static int tc358768_dsi_host_attach(struct mipi_dsi_host *host,
{
struct tc358768_priv *priv = dsi_host_to_tc358768(host);
struct drm_bridge *bridge;
struct drm_panel *panel;
struct device_node *ep;
int ret;
@ -471,24 +468,15 @@ static int tc358768_dsi_host_attach(struct mipi_dsi_host *host,
return -ENOTSUPP;
}
ret = drm_of_find_panel_or_bridge(host->dev->of_node, 1, 0, &panel,
&bridge);
if (ret)
return ret;
bridge = of_drm_get_bridge_by_endpoint(host->dev->of_node, 1, 0);
if (IS_ERR(bridge))
return PTR_ERR(bridge);
if (panel) {
bridge = drm_panel_bridge_add_typed(panel,
DRM_MODE_CONNECTOR_DSI);
drm_panel_put(panel);
if (IS_ERR(bridge))
return PTR_ERR(bridge);
bridge->pre_enable_prev_first = true;
}
bridge->type = DRM_MODE_CONNECTOR_DSI;
bridge->pre_enable_prev_first = true;
priv->output.dev = dev;
priv->output.bridge = bridge;
priv->output.panel = panel;
priv->dsi_lanes = dev->lanes;
priv->dsi_bpp = mipi_dsi_pixel_format_to_bpp(dev->format);
@ -518,8 +506,7 @@ static int tc358768_dsi_host_detach(struct mipi_dsi_host *host,
struct tc358768_priv *priv = dsi_host_to_tc358768(host);
drm_bridge_remove(&priv->bridge);
if (priv->output.panel)
drm_panel_bridge_remove(priv->output.bridge);
drm_bridge_put(priv->output.bridge);
return 0;
}

View File

@ -8,6 +8,7 @@
#include <drm/drm_atomic_helper.h>
#include <drm/drm_bridge.h>
#include <drm/drm_panel.h>
struct tdp158 {
struct drm_bridge bridge;

View File

@ -16,11 +16,9 @@
#include <drm/drm_bridge.h>
#include <drm/drm_mipi_dsi.h>
#include <drm/drm_of.h>
#include <drm/drm_panel.h>
struct ws_bridge {
struct drm_bridge bridge;
struct drm_bridge *next_bridge;
struct backlight_device *backlight;
struct device *dev;
struct regmap *reg_map;
@ -89,7 +87,7 @@ static int ws_bridge_bridge_attach(struct drm_bridge *bridge,
{
struct ws_bridge *ws = bridge_to_ws_bridge(bridge);
return drm_bridge_attach(encoder, ws->next_bridge,
return drm_bridge_attach(encoder, ws->bridge.next_bridge,
&ws->bridge, flags);
}
@ -150,7 +148,6 @@ static struct backlight_device *ws_bridge_create_backlight(struct ws_bridge *ws)
static int ws_bridge_probe(struct i2c_client *i2c)
{
struct device *dev = &i2c->dev;
struct drm_panel *panel;
struct ws_bridge *ws;
int ret;
@ -164,14 +161,10 @@ static int ws_bridge_probe(struct i2c_client *i2c)
if (IS_ERR(ws->reg_map))
return dev_err_probe(dev, PTR_ERR(ws->reg_map), "Failed to allocate regmap\n");
ret = drm_of_find_panel_or_bridge(dev->of_node, 1, -1, &panel, NULL);
if (ret)
return dev_err_probe(dev, ret, "Failed to find remote panel\n");
ws->next_bridge = devm_drm_panel_bridge_add(dev, panel);
drm_panel_put(panel);
if (IS_ERR(ws->next_bridge))
return PTR_ERR(ws->next_bridge);
ws->bridge.next_bridge = of_drm_get_bridge_by_endpoint(dev->of_node, 1, -1);
if (IS_ERR(ws->bridge.next_bridge))
return dev_err_probe(dev, PTR_ERR(ws->bridge.next_bridge),
"Failed to find remote bridge\n");
ws->backlight = ws_bridge_create_backlight(ws);
if (IS_ERR(ws->backlight)) {

View File

@ -4,5 +4,6 @@ subdir-ccflags-y += -I$(src)/..
drm_client_lib-y := drm_client_setup.o
drm_client_lib-$(CONFIG_DRM_CLIENT_LOG) += drm_log.o
drm_client_lib-$(CONFIG_DRM_FBDEV_EMULATION) += drm_fbdev_client.o
drm_client_lib-$(CONFIG_DRM_FBDEV_EMULATION) += drm_fbdev_client.o \
drm_fbdev_helper.o
obj-$(CONFIG_DRM_CLIENT_LIB) += drm_client_lib.o

View File

@ -1,9 +1,9 @@
// SPDX-License-Identifier: MIT
#include <drm/clients/drm_fbdev_helper.h>
#include <drm/drm_client.h>
#include <drm/drm_crtc_helper.h>
#include <drm/drm_drv.h>
#include <drm/drm_fb_helper.h>
#include <drm/drm_fourcc.h>
#include <drm/drm_print.h>

View File

@ -32,9 +32,9 @@
#include <linux/console.h>
#include <linux/export.h>
#include <drm/clients/drm_fbdev_helper.h>
#include <drm/drm_atomic.h>
#include <drm/drm_drv.h>
#include <drm/drm_fb_helper.h>
#include <drm/drm_fourcc.h>
#include <drm/drm_framebuffer.h>
#include <drm/drm_gem_framebuffer_helper.h>
@ -43,39 +43,9 @@
#include <drm/drm_vblank.h>
#include "drm_internal.h"
#include "drm_crtc_helper_internal.h"
#include "drm_crtc_internal.h"
static bool drm_fbdev_emulation = true;
module_param_named(fbdev_emulation, drm_fbdev_emulation, bool, 0600);
MODULE_PARM_DESC(fbdev_emulation,
"Enable legacy fbdev emulation [default=true]");
static int drm_fbdev_overalloc = CONFIG_DRM_FBDEV_OVERALLOC;
module_param(drm_fbdev_overalloc, int, 0444);
MODULE_PARM_DESC(drm_fbdev_overalloc,
"Overallocation of the fbdev buffer (%) [default="
__MODULE_STRING(CONFIG_DRM_FBDEV_OVERALLOC) "]");
/*
* In order to keep user-space compatibility, we want in certain use-cases
* to keep leaking the fbdev physical address to the user-space program
* handling the fbdev buffer.
*
* This is a bad habit, essentially kept to support closed-source OpenGL
* drivers that should really be moved into open-source upstream projects
* instead of using legacy physical addresses in user space to communicate
* with other out-of-tree kernel modules.
*
* This module_param *should* be removed as soon as possible and be
* considered as a broken and legacy behaviour from a modern fbdev device.
*/
static bool drm_leak_fbdev_smem;
#if IS_ENABLED(CONFIG_DRM_FBDEV_LEAK_PHYS_SMEM)
module_param_unsafe(drm_leak_fbdev_smem, bool, 0600);
MODULE_PARM_DESC(drm_leak_fbdev_smem,
"Allow unsafe leaking fbdev physical smem address [default=false]");
#endif
/**
* DOC: fbdev helpers
*

View File

@ -18,6 +18,7 @@
#include <drm/drm_edid.h>
#include <drm/drm_managed.h>
#include <drm/drm_modeset_helper_vtables.h>
#include <drm/drm_panel.h>
#include <drm/drm_print.h>
#include <drm/drm_probe_helper.h>
#include <drm/display/drm_hdcp_helper.h>

View File

@ -2161,6 +2161,9 @@ ssize_t drm_dp_mst_dpcd_read(struct drm_dp_aux *aux,
struct drm_dp_mst_port *port = container_of(aux, struct drm_dp_mst_port,
aux);
if (!port->ddps)
return -EIO;
return drm_dp_send_dpcd_read(port->mgr, port,
offset, size, buffer);
}
@ -2184,6 +2187,9 @@ ssize_t drm_dp_mst_dpcd_write(struct drm_dp_aux *aux,
struct drm_dp_mst_port *port = container_of(aux, struct drm_dp_mst_port,
aux);
if (!port->ddps)
return -EIO;
return drm_dp_send_dpcd_write(port->mgr, port,
offset, size, buffer);
}

View File

@ -1741,23 +1741,6 @@ struct drm_bridge *of_drm_get_bridge_by_endpoint(const struct device_node *np,
EXPORT_SYMBOL_GPL(of_drm_get_bridge_by_endpoint);
#endif
/**
* devm_drm_put_bridge - Release a bridge reference obtained via devm
* @dev: device that got the bridge via devm
* @bridge: pointer to a struct drm_bridge obtained via devm
*
* Same as drm_bridge_put() for bridge pointers obtained via devm functions
* such as devm_drm_bridge_alloc().
*
* This function is a temporary workaround and MUST NOT be used. Manual
* handling of bridge lifetime is inherently unsafe.
*/
void devm_drm_put_bridge(struct device *dev, struct drm_bridge *bridge)
{
devm_release_action(dev, drm_bridge_put_void, bridge);
}
EXPORT_SYMBOL(devm_drm_put_bridge);
static void drm_bridge_debugfs_show_bridge(struct drm_printer *p,
struct drm_bridge *bridge,
unsigned int idx,

View File

@ -28,10 +28,10 @@
#include <drm/drm_file.h>
#include <drm/drm_managed.h>
#include <drm/drm_panel.h>
#include <drm/drm_panel_quirks.h>
#include <drm/drm_print.h>
#include <drm/drm_privacy_screen_consumer.h>
#include <drm/drm_sysfs.h>
#include <drm/drm_utils.h>
#include <linux/export.h>
#include <linux/platform_device.h>

View File

@ -29,6 +29,8 @@
#ifndef __DRM_CRTC_HELPER_INTERNAL_H__
#define __DRM_CRTC_HELPER_INTERNAL_H__
#include <linux/types.h>
enum drm_mode_status;
struct drm_connector;
struct drm_crtc;
@ -36,6 +38,13 @@ struct drm_display_mode;
struct drm_encoder;
struct drm_modeset_acquire_ctx;
/* drm_kms_helper_common.c */
#if IS_ENABLED(CONFIG_DRM_FBDEV_EMULATION)
extern bool drm_fbdev_emulation;
extern int drm_fbdev_overalloc;
extern bool drm_leak_fbdev_smem;
#endif
/* drm_probe_helper.c */
enum drm_mode_status drm_crtc_mode_valid(struct drm_crtc *crtc,
const struct drm_display_mode *mode);

View File

@ -4,10 +4,10 @@
#include <linux/fb.h>
#include <linux/vmalloc.h>
#include <drm/clients/drm_fbdev_helper.h>
#include <drm/drm_drv.h>
#include <drm/drm_fbdev_dma.h>
#include <drm/drm_fb_dma_helper.h>
#include <drm/drm_fb_helper.h>
#include <drm/drm_framebuffer.h>
#include <drm/drm_gem_dma_helper.h>
#include <drm/drm_print.h>

View File

@ -3,9 +3,9 @@
#include <linux/export.h>
#include <linux/fb.h>
#include <drm/clients/drm_fbdev_helper.h>
#include <drm/drm_drv.h>
#include <drm/drm_fbdev_shmem.h>
#include <drm/drm_fb_helper.h>
#include <drm/drm_framebuffer.h>
#include <drm/drm_gem_framebuffer_helper.h>
#include <drm/drm_gem_shmem_helper.h>

View File

@ -4,9 +4,9 @@
#include <linux/moduleparam.h>
#include <linux/vmalloc.h>
#include <drm/clients/drm_fbdev_helper.h>
#include <drm/drm_crtc_helper.h>
#include <drm/drm_drv.h>
#include <drm/drm_fb_helper.h>
#include <drm/drm_framebuffer.h>
#include <drm/drm_gem.h>
#include <drm/drm_print.h>

View File

@ -25,10 +25,49 @@
*
*/
#include <linux/export.h>
#include <linux/module.h>
#include <linux/moduleparam.h>
#include "drm_crtc_helper_internal.h"
#include "drm_panic_internal.h"
#if IS_ENABLED(CONFIG_DRM_FBDEV_EMULATION)
bool drm_fbdev_emulation = true;
EXPORT_SYMBOL(drm_fbdev_emulation);
module_param_named(fbdev_emulation, drm_fbdev_emulation, bool, 0600);
MODULE_PARM_DESC(fbdev_emulation, "Enable legacy fbdev emulation [default=true]");
int drm_fbdev_overalloc = CONFIG_DRM_FBDEV_OVERALLOC;
EXPORT_SYMBOL(drm_fbdev_overalloc);
module_param(drm_fbdev_overalloc, int, 0444);
MODULE_PARM_DESC(drm_fbdev_overalloc,
"Overallocation of the fbdev buffer (%) [default="
__MODULE_STRING(CONFIG_DRM_FBDEV_OVERALLOC) "]");
/*
* In order to keep user-space compatibility, we want in certain use-cases
* to keep leaking the fbdev physical address to the user-space program
* handling the fbdev buffer.
*
* This is a bad habit, essentially kept to support closed-source OpenGL
* drivers that should really be moved into open-source upstream projects
* instead of using legacy physical addresses in user space to communicate
* with other out-of-tree kernel modules.
*
* This module_param *should* be removed as soon as possible and be
* considered as a broken and legacy behaviour from a modern fbdev device.
*/
bool drm_leak_fbdev_smem;
EXPORT_SYMBOL(drm_leak_fbdev_smem);
#if IS_ENABLED(CONFIG_DRM_FBDEV_LEAK_PHYS_SMEM)
module_param_unsafe(drm_leak_fbdev_smem, bool, 0600);
MODULE_PARM_DESC(drm_leak_fbdev_smem,
"Allow unsafe leaking fbdev physical smem address [default=false]");
#endif
#endif
static int __init drm_kms_helper_init(void)
{
return drm_panic_helper_init();

View File

@ -264,69 +264,6 @@ int drm_of_get_panel_orientation(const struct device_node *np,
}
EXPORT_SYMBOL_GPL(drm_of_get_panel_orientation);
/**
* drm_of_find_panel_or_bridge - return connected panel or bridge device
* @np: device tree node containing encoder output ports
* @port: port in the device tree node
* @endpoint: endpoint in the device tree node
* @panel: pointer to hold returned drm_panel, must not be NULL. On success
* the caller must call drm_panel_put() when done with the panel
* @bridge: pointer to hold returned drm_bridge
*
* Given a DT node's port and endpoint number, find the connected node and
* return either the associated struct drm_panel or drm_bridge device.
*
* This function is deprecated and should not be used in new drivers. Use
* of_drm_get_bridge_by_endpoint() instead when not looking for a panel, or
* devm_drm_of_get_bridge() otherwise.
*
* Returns zero if successful, or one of the standard error codes if it fails.
*/
int drm_of_find_panel_or_bridge(const struct device_node *np,
int port, int endpoint,
struct drm_panel **panel,
struct drm_bridge **bridge)
{
if (WARN_ON(!panel))
return -EINVAL;
*panel = NULL;
if (bridge)
*bridge = NULL;
/*
* of_graph_get_remote_node() produces a noisy error message if port
* node isn't found and the absence of the port is a legit case here,
* so at first we silently check whether a graph is present in the
* device-tree node.
*/
if (!of_graph_is_present(np))
return -ENODEV;
struct device_node *remote __free(device_node) =
of_graph_get_remote_node(np, port, endpoint);
if (!remote)
return -ENODEV;
*panel = of_drm_find_panel(remote);
if (!IS_ERR(*panel))
return 0;
*panel = NULL;
if (bridge) {
/* No panel found yet, check for a bridge next. */
*bridge = of_drm_find_bridge(remote);
if (*bridge)
return 0;
*bridge = NULL;
}
return -EPROBE_DEFER;
}
EXPORT_SYMBOL_GPL(drm_of_find_panel_or_bridge);
enum drm_of_lvds_pixels {
DRM_OF_LVDS_EVEN = BIT(0),
DRM_OF_LVDS_ODD = BIT(1),

View File

@ -1,36 +1,28 @@
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright (C) 2013, NVIDIA Corporation. All rights reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sub license,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice (including the
* next paragraph) shall be included in all copies or substantial portions
* of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
* Copyright (C) 2016 Laurent Pinchart <laurent.pinchart@ideasonboard.com>
* Copyright (C) 2017 Broadcom
*/
#include <linux/backlight.h>
#include <linux/debugfs.h>
#include <linux/err.h>
#include <linux/export.h>
#include <linux/module.h>
#include <linux/of.h>
#include <drm/drm_atomic_helper.h>
#include <drm/drm_bridge.h>
#include <drm/drm_connector.h>
#include <drm/drm_crtc.h>
#include <drm/drm_encoder.h>
#include <drm/drm_managed.h>
#include <drm/drm_modeset_helper_vtables.h>
#include <drm/drm_of.h>
#include <drm/drm_panel.h>
#include <drm/drm_print.h>
#include <drm/drm_probe_helper.h>
static DEFINE_MUTEX(panel_lock);
static LIST_HEAD(panel_list);
@ -46,6 +38,526 @@ static LIST_HEAD(panel_list);
* take look at drm_panel_bridge_add() and devm_drm_panel_bridge_add().
*/
static inline struct drm_panel *
drm_bridge_to_panel(const struct drm_bridge *bridge)
{
return container_of(bridge, struct drm_panel, bridge);
}
static inline struct drm_panel *
drm_connector_to_panel(const struct drm_connector *connector)
{
return container_of(connector, struct drm_panel, connector);
}
struct panel_bridge {
struct drm_bridge bridge;
struct drm_connector connector;
struct drm_panel *panel;
u32 connector_type;
};
static int panel_bridge_connector_get_modes(struct drm_connector *connector)
{
struct drm_panel *panel = drm_connector_to_panel(connector);
return drm_panel_get_modes(panel, connector);
}
static const struct drm_connector_helper_funcs
panel_bridge_connector_helper_funcs = {
.get_modes = panel_bridge_connector_get_modes,
};
static const struct drm_connector_funcs panel_bridge_connector_funcs = {
.reset = drm_atomic_helper_connector_reset,
.fill_modes = drm_helper_probe_single_connector_modes,
.destroy = drm_connector_cleanup,
.atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
.atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
};
static int panel_bridge_attach(struct drm_bridge *bridge,
struct drm_encoder *encoder,
enum drm_bridge_attach_flags flags)
{
struct drm_panel *panel = drm_bridge_to_panel(bridge);
struct drm_connector *connector = &panel->connector;
int ret;
if (flags & DRM_BRIDGE_ATTACH_NO_CONNECTOR)
return 0;
drm_connector_helper_add(connector,
&panel_bridge_connector_helper_funcs);
ret = drm_connector_init(bridge->dev, connector,
&panel_bridge_connector_funcs,
panel->connector_type);
if (ret) {
DRM_ERROR("Failed to initialize connector\n");
return ret;
}
drm_panel_bridge_set_orientation(connector, bridge);
drm_connector_attach_encoder(connector, encoder);
if (bridge->dev->registered) {
if (connector->funcs->reset)
connector->funcs->reset(connector);
drm_connector_register(connector);
}
return 0;
}
static void panel_bridge_detach(struct drm_bridge *bridge)
{
struct drm_panel *panel = drm_bridge_to_panel(bridge);
struct drm_connector *connector = &panel->connector;
/* Cleanup the connector if we know it was initialized */
if (connector->dev)
drm_connector_cleanup(connector);
}
static void panel_bridge_atomic_pre_enable(struct drm_bridge *bridge,
struct drm_atomic_commit *atomic_state)
{
struct drm_panel *panel = drm_bridge_to_panel(bridge);
struct drm_encoder *encoder = bridge->encoder;
struct drm_crtc *crtc;
struct drm_crtc_state *old_crtc_state;
crtc = drm_atomic_get_new_crtc_for_encoder(atomic_state, encoder);
if (!crtc)
return;
old_crtc_state = drm_atomic_get_old_crtc_state(atomic_state, crtc);
if (old_crtc_state && old_crtc_state->self_refresh_active)
return;
drm_panel_prepare(panel);
}
static void panel_bridge_atomic_enable(struct drm_bridge *bridge,
struct drm_atomic_commit *atomic_state)
{
struct drm_panel *panel = drm_bridge_to_panel(bridge);
struct drm_encoder *encoder = bridge->encoder;
struct drm_crtc *crtc;
struct drm_crtc_state *old_crtc_state;
crtc = drm_atomic_get_new_crtc_for_encoder(atomic_state, encoder);
if (!crtc)
return;
old_crtc_state = drm_atomic_get_old_crtc_state(atomic_state, crtc);
if (old_crtc_state && old_crtc_state->self_refresh_active)
return;
drm_panel_enable(panel);
}
static void panel_bridge_atomic_disable(struct drm_bridge *bridge,
struct drm_atomic_commit *atomic_state)
{
struct drm_panel *panel = drm_bridge_to_panel(bridge);
struct drm_encoder *encoder = bridge->encoder;
struct drm_crtc *crtc;
struct drm_crtc_state *new_crtc_state;
crtc = drm_atomic_get_old_crtc_for_encoder(atomic_state, encoder);
if (!crtc)
return;
new_crtc_state = drm_atomic_get_new_crtc_state(atomic_state, crtc);
if (new_crtc_state && new_crtc_state->self_refresh_active)
return;
drm_panel_disable(panel);
}
static void panel_bridge_atomic_post_disable(struct drm_bridge *bridge,
struct drm_atomic_commit *atomic_state)
{
struct drm_panel *panel = drm_bridge_to_panel(bridge);
struct drm_encoder *encoder = bridge->encoder;
struct drm_crtc *crtc;
struct drm_crtc_state *new_crtc_state;
crtc = drm_atomic_get_old_crtc_for_encoder(atomic_state, encoder);
if (!crtc)
return;
new_crtc_state = drm_atomic_get_new_crtc_state(atomic_state, crtc);
if (new_crtc_state && new_crtc_state->self_refresh_active)
return;
drm_panel_unprepare(panel);
}
static int panel_bridge_get_modes(struct drm_bridge *bridge,
struct drm_connector *connector)
{
struct drm_panel *panel = drm_bridge_to_panel(bridge);
return drm_panel_get_modes(panel, connector);
}
static void panel_bridge_debugfs_init(struct drm_bridge *bridge,
struct dentry *root)
{
struct drm_panel *panel = drm_bridge_to_panel(bridge);
root = debugfs_create_dir("panel", root);
if (panel->funcs->debugfs_init)
panel->funcs->debugfs_init(panel, root);
}
static const struct drm_bridge_funcs panel_bridge_bridge_funcs = {
.attach = panel_bridge_attach,
.detach = panel_bridge_detach,
.atomic_pre_enable = panel_bridge_atomic_pre_enable,
.atomic_enable = panel_bridge_atomic_enable,
.atomic_disable = panel_bridge_atomic_disable,
.atomic_post_disable = panel_bridge_atomic_post_disable,
.get_modes = panel_bridge_get_modes,
.atomic_create_state = drm_atomic_helper_bridge_create_state,
.atomic_duplicate_state = drm_atomic_helper_bridge_duplicate_state,
.atomic_destroy_state = drm_atomic_helper_bridge_destroy_state,
.atomic_get_input_bus_fmts = drm_atomic_helper_bridge_propagate_bus_fmt,
.debugfs_init = panel_bridge_debugfs_init,
};
/**
* drm_bridge_is_panel - Checks if a drm_bridge is a panel_bridge.
*
* @bridge: The drm_bridge to be checked.
*
* Returns true if the bridge is a panel bridge, or false otherwise.
*/
bool drm_bridge_is_panel(const struct drm_bridge *bridge)
{
return bridge->funcs == &panel_bridge_bridge_funcs;
}
EXPORT_SYMBOL(drm_bridge_is_panel);
/**
* drm_panel_bridge_add - Creates a &drm_bridge and &drm_connector that
* just calls the appropriate functions from &drm_panel.
*
* @panel: The drm_panel being wrapped. Must be non-NULL.
*
* This function is deprecated. A drm_bridge is embedded into every panel.
* Don't use it in new drivers and update your driver to stop using it.
*
* For drivers converting from directly using drm_panel: The expected
* usage pattern is that during either encoder module probe or DSI
* host attach, a drm_panel will be looked up through
* drm_of_find_panel_or_bridge(). drm_panel_bridge_add() is used to
* wrap that panel in the new bridge, and the result can then be
* passed to drm_bridge_attach(). The drm_panel_prepare() and related
* functions can be dropped from the encoder driver (they're now
* called by the KMS helpers before calling into the encoder), along
* with connector creation. When done with the bridge (after
* drm_mode_config_cleanup() if the bridge has already been attached), then
* drm_panel_bridge_remove() to free it.
*
* The connector type is set to @panel->connector_type, which must be set to a
* known type. Calling this function with a panel whose connector type is
* DRM_MODE_CONNECTOR_Unknown will return ERR_PTR(-EINVAL).
*
* See devm_drm_panel_bridge_add() for an automatically managed version of this
* function.
*/
struct drm_bridge *drm_panel_bridge_add(struct drm_panel *panel)
{
if (WARN_ON(panel->connector_type == DRM_MODE_CONNECTOR_Unknown))
return ERR_PTR(-EINVAL);
return drm_panel_bridge_add_typed(panel, panel->connector_type);
}
EXPORT_SYMBOL(drm_panel_bridge_add);
/**
* drm_panel_bridge_add_typed - Pretend to create a &drm_bridge and &drm_connector with
* an explicit connector type.
* @panel: The drm_panel being wrapped. Must be non-NULL.
* @connector_type: The connector type (DRM_MODE_CONNECTOR_*)
*
* This is just like drm_panel_bridge_add(), but forces the connector type to
* @connector_type instead of infering it from the panel.
*
* This function is deprecated. A drm_bridge is embedded into every panel.
* Don't use it in new drivers and update your driver to stop using it.
*/
struct drm_bridge *drm_panel_bridge_add_typed(struct drm_panel *panel,
u32 connector_type)
{
if (!panel)
return ERR_PTR(-EINVAL);
return drm_bridge_get(&panel->bridge);
}
EXPORT_SYMBOL(drm_panel_bridge_add_typed);
/**
* drm_panel_bridge_remove - Unregisters and frees a drm_bridge
* created by drm_panel_bridge_add().
*
* @bridge: The drm_bridge being freed.
*
* This function is deprecated. A drm_bridge is embedded into every panel
* and removed when the panel is removed. Don't use it in new drivers and
* update your driver to stop using it.
*/
void drm_panel_bridge_remove(struct drm_bridge *bridge)
{
if (!bridge)
return;
if (!drm_bridge_is_panel(bridge)) {
drm_warn(bridge->dev, "%s: called on non-panel bridge!\n", __func__);
return;
}
drm_bridge_put(bridge);
}
EXPORT_SYMBOL(drm_panel_bridge_remove);
/**
* drm_panel_bridge_set_orientation - Set the connector's panel orientation
* from the bridge that can be transformed to panel bridge.
*
* @connector: The connector to be set panel orientation.
* @bridge: The drm_bridge whose orientation should be set.
*
* Returns 0 on success, negative errno on failure.
*/
int drm_panel_bridge_set_orientation(struct drm_connector *connector,
struct drm_bridge *bridge)
{
struct drm_panel *panel = drm_bridge_to_panel(bridge);
return drm_connector_set_orientation_from_panel(connector, panel);
}
EXPORT_SYMBOL(drm_panel_bridge_set_orientation);
static void devm_drm_panel_bridge_release(struct device *dev, void *res)
{
struct drm_bridge *bridge = *(struct drm_bridge **)res;
if (!bridge)
return;
drm_bridge_put(bridge);
}
/**
* devm_drm_panel_bridge_add - Creates a managed &drm_bridge and &drm_connector
* that just calls the appropriate functions from &drm_panel.
* @dev: device to tie the bridge lifetime to
* @panel: The drm_panel being wrapped. Must be non-NULL.
*
* This function is deprecated. A drm_bridge is embedded into every panel.
* Don't use it in new drivers and update your driver to stop using it.
*
* This is the managed version of drm_panel_bridge_add() which automatically
* calls drm_panel_bridge_remove() when @dev is unbound.
*/
struct drm_bridge *devm_drm_panel_bridge_add(struct device *dev,
struct drm_panel *panel)
{
if (WARN_ON(panel->connector_type == DRM_MODE_CONNECTOR_Unknown))
return ERR_PTR(-EINVAL);
return devm_drm_panel_bridge_add_typed(dev, panel,
panel->connector_type);
}
EXPORT_SYMBOL(devm_drm_panel_bridge_add);
/**
* devm_drm_panel_bridge_add_typed - Creates a managed &drm_bridge and
* &drm_connector with an explicit connector type.
* @dev: device to tie the bridge lifetime to
* @panel: The drm_panel being wrapped. Must be non-NULL.
* @connector_type: The connector type (DRM_MODE_CONNECTOR_*)
*
* This is just like devm_drm_panel_bridge_add(), but forces the connector type
* to @connector_type instead of infering it from the panel.
*
* This function is deprecated. A drm_bridge is embedded into every panel.
* Don't use it in new drivers and update your driver to stop using it.
*/
struct drm_bridge *devm_drm_panel_bridge_add_typed(struct device *dev,
struct drm_panel *panel,
u32 connector_type)
{
struct drm_bridge **ptr, *bridge;
ptr = devres_alloc(devm_drm_panel_bridge_release, sizeof(*ptr),
GFP_KERNEL);
if (!ptr)
return ERR_PTR(-ENOMEM);
bridge = drm_panel_bridge_add_typed(panel, connector_type);
if (IS_ERR(bridge)) {
devres_free(ptr);
return bridge;
}
*ptr = bridge;
devres_add(dev, ptr);
return &panel->bridge;
}
EXPORT_SYMBOL(devm_drm_panel_bridge_add_typed);
static void drmm_drm_panel_bridge_release(struct drm_device *drm, void *ptr)
{
struct drm_bridge *bridge = ptr;
drm_panel_bridge_remove(bridge);
}
/**
* drmm_panel_bridge_add - Creates a DRM-managed &drm_bridge and
* &drm_connector that just calls the
* appropriate functions from &drm_panel.
*
* @drm: DRM device to tie the bridge lifetime to
* @panel: The drm_panel being wrapped. Must be non-NULL.
*
* This function is deprecated. A drm_bridge is embedded into every panel.
* Don't use it in new drivers and update your driver to stop using it.
*
* This is the DRM-managed version of drm_panel_bridge_add() which
* automatically calls drm_panel_bridge_remove() when @dev is cleaned
* up.
*/
struct drm_bridge *drmm_panel_bridge_add(struct drm_device *drm,
struct drm_panel *panel)
{
struct drm_bridge *bridge;
int ret;
bridge = drm_panel_bridge_add_typed(panel, panel->connector_type);
if (IS_ERR(bridge))
return bridge;
ret = drmm_add_action_or_reset(drm, drmm_drm_panel_bridge_release,
bridge);
if (ret)
return ERR_PTR(ret);
return bridge;
}
EXPORT_SYMBOL(drmm_panel_bridge_add);
/**
* drm_panel_bridge_connector - return the connector for the panel (for
* legacy drivers not using DRM_BRIDGE_ATTACH_NO_CONNECTOR)
* @bridge: The drm_bridge.
*
* This function gives external access to the connector.
*
* Returns: Pointer to drm_connector
*/
struct drm_connector *drm_panel_bridge_connector(struct drm_bridge *bridge)
{
struct drm_panel *panel;
panel = drm_bridge_to_panel(bridge);
return &panel->connector;
}
EXPORT_SYMBOL(drm_panel_bridge_connector);
#ifdef CONFIG_OF
/**
* devm_drm_of_get_bridge - Return next bridge in the chain
* @dev: device to tie the bridge lifetime to
* @np: device tree node containing encoder output ports
* @port: port in the device tree node
* @endpoint: endpoint in the device tree node
*
* This function is deprecated. A drm_bridge is embedded into every panel.
* Don't use it in new drivers and convert your driver to
* of_drm_get_bridge_by_endpoint().
*
* Given a DT node's port and endpoint number, finds the connected node
* and returns the associated bridge if any, or creates and returns a
* drm panel bridge instance if a panel is connected.
*
* Returns a pointer to the bridge if successful, or an error pointer
* otherwise.
*/
struct drm_bridge *devm_drm_of_get_bridge(struct device *dev,
struct device_node *np,
u32 port, u32 endpoint)
{
struct drm_bridge *bridge;
struct drm_panel *panel;
int ret;
ret = drm_of_find_panel_or_bridge(np, port, endpoint,
&panel, &bridge);
if (ret)
return ERR_PTR(ret);
if (panel) {
bridge = devm_drm_panel_bridge_add(dev, panel);
drm_panel_put(panel);
}
return bridge;
}
EXPORT_SYMBOL(devm_drm_of_get_bridge);
/**
* drmm_of_get_bridge - Return next bridge in the chain
* @drm: device to tie the bridge lifetime to
* @np: device tree node containing encoder output ports
* @port: port in the device tree node
* @endpoint: endpoint in the device tree node
*
* This function is deprecated. A drm_bridge is embedded into every panel.
* Don't use it in new drivers and convert your driver to
* of_drm_get_bridge_by_endpoint().
*
* Given a DT node's port and endpoint number, finds the connected node
* and returns the associated bridge if any, or creates and returns a
* drm panel bridge instance if a panel is connected.
*
* Returns a drmm managed pointer to the bridge if successful, or an error
* pointer otherwise.
*/
struct drm_bridge *drmm_of_get_bridge(struct drm_device *drm,
struct device_node *np,
u32 port, u32 endpoint)
{
struct drm_bridge *bridge;
struct drm_panel *panel;
int ret;
ret = drm_of_find_panel_or_bridge(np, port, endpoint,
&panel, &bridge);
if (ret)
return ERR_PTR(ret);
if (panel) {
bridge = drmm_panel_bridge_add(drm, panel);
drm_panel_put(panel);
}
return bridge;
}
EXPORT_SYMBOL(drmm_of_get_bridge);
#endif
/**
* drm_panel_init - initialize a panel
* @panel: DRM panel
@ -86,6 +598,13 @@ void drm_panel_add(struct drm_panel *panel)
mutex_lock(&panel_lock);
list_add_tail(&panel->list, &panel_list);
mutex_unlock(&panel_lock);
panel->bridge.of_node = panel->dev->of_node;
panel->bridge.ops = DRM_BRIDGE_OP_MODES;
panel->bridge.type = panel->connector_type;
panel->bridge.pre_enable_prev_first = panel->prepare_prev_first;
drm_bridge_add(&panel->bridge);
}
EXPORT_SYMBOL(drm_panel_add);
@ -97,6 +616,7 @@ EXPORT_SYMBOL(drm_panel_add);
*/
void drm_panel_remove(struct drm_panel *panel)
{
drm_bridge_remove(&panel->bridge);
mutex_lock(&panel_lock);
list_del_init(&panel->list);
mutex_unlock(&panel_lock);
@ -370,13 +890,6 @@ int drm_panel_get_modes(struct drm_panel *panel,
}
EXPORT_SYMBOL(drm_panel_get_modes);
static void __drm_panel_free(struct kref *kref)
{
struct drm_panel *panel = container_of(kref, struct drm_panel, refcount);
kfree(panel->container);
}
/**
* drm_panel_get - Acquire a panel reference
* @panel: DRM panel
@ -387,10 +900,8 @@ static void __drm_panel_free(struct kref *kref)
*/
struct drm_panel *drm_panel_get(struct drm_panel *panel)
{
if (!panel)
return panel;
kref_get(&panel->refcount);
if (panel)
drm_bridge_get(&panel->bridge);
return panel;
}
@ -406,7 +917,7 @@ EXPORT_SYMBOL(drm_panel_get);
void drm_panel_put(struct drm_panel *panel)
{
if (panel)
kref_put(&panel->refcount, __drm_panel_free);
drm_bridge_put(&panel->bridge);
}
EXPORT_SYMBOL(drm_panel_put);
@ -429,8 +940,22 @@ void *__devm_drm_panel_alloc(struct device *dev, size_t size, size_t offset,
const struct drm_panel_funcs *funcs,
int connector_type)
{
void *container;
/*
* Struct embedding and offsets:
*
* |--------------- user container struct ------------|
* : |---------- struct drm_panel ------------|
* : : |----- struct drm_bridge ------|
* A B C
*
* B - A = offset (passed as argument)
* C - B = panel_bridge_offset
* C - A = alloc_bridge_offset
*/
const size_t panel_bridge_offset = offsetof(struct drm_panel, bridge);
const size_t alloc_bridge_offset = offset + panel_bridge_offset;
struct drm_panel *panel;
void *container;
int err;
if (!funcs) {
@ -438,14 +963,16 @@ void *__devm_drm_panel_alloc(struct device *dev, size_t size, size_t offset,
return ERR_PTR(-EINVAL);
}
container = kzalloc(size, GFP_KERNEL);
if (!container)
return ERR_PTR(-ENOMEM);
container = __devm_drm_bridge_alloc(dev, size, alloc_bridge_offset,
&panel_bridge_bridge_funcs);
if (IS_ERR(container))
return container;
panel = container + offset;
panel->container = container;
panel->funcs = funcs;
kref_init(&panel->refcount);
panel->bridge.of_node = dev->of_node;
drm_panel_get(panel);
err = devm_add_action_or_reset(dev, drm_panel_put_void, panel);
if (err)
@ -498,6 +1025,69 @@ struct drm_panel *of_drm_find_panel(const struct device_node *np)
return ERR_PTR(-EPROBE_DEFER);
}
EXPORT_SYMBOL(of_drm_find_panel);
/**
* drm_of_find_panel_or_bridge - return connected panel or bridge device
* @np: device tree node containing encoder output ports
* @port: port in the device tree node
* @endpoint: endpoint in the device tree node
* @panel: pointer to hold returned drm_panel, must not be NULL. On success
* the caller must call drm_panel_put() when done with the panel
* @bridge: pointer to hold returned drm_bridge
*
* Given a DT node's port and endpoint number, find the connected node and
* return either the associated struct drm_panel or drm_bridge device.
*
* This function is deprecated and should not be used in new drivers. Use
* of_drm_get_bridge_by_endpoint() instead when not looking for a panel, or
* devm_drm_of_get_bridge() otherwise.
*
* Returns zero if successful, or one of the standard error codes if it fails.
*/
int drm_of_find_panel_or_bridge(const struct device_node *np,
int port, int endpoint,
struct drm_panel **panel,
struct drm_bridge **bridge)
{
if (WARN_ON(!panel))
return -EINVAL;
*panel = NULL;
if (bridge)
*bridge = NULL;
/*
* of_graph_get_remote_node() produces a noisy error message if port
* node isn't found and the absence of the port is a legit case here,
* so at first we silently check whether a graph is present in the
* device-tree node.
*/
if (!of_graph_is_present(np))
return -ENODEV;
struct device_node *remote __free(device_node) =
of_graph_get_remote_node(np, port, endpoint);
if (!remote)
return -ENODEV;
*panel = of_drm_find_panel(remote);
if (!IS_ERR(*panel))
return 0;
*panel = NULL;
if (bridge) {
/* No panel found yet, check for a bridge next. */
*bridge = of_drm_find_bridge(remote);
if (*bridge)
return 0;
*bridge = NULL;
}
return -EPROBE_DEFER;
}
EXPORT_SYMBOL_GPL(drm_of_find_panel_or_bridge);
#endif
/*

View File

@ -5,7 +5,7 @@
#include <linux/export.h>
#include <linux/module.h>
#include <drm/drm_edid.h>
#include <drm/drm_utils.h>
#include <drm/drm_panel_quirks.h>
struct drm_panel_match {
enum dmi_field field;

View File

@ -12,7 +12,7 @@
#include <linux/export.h>
#include <linux/module.h>
#include <drm/drm_connector.h>
#include <drm/drm_utils.h>
#include <drm/drm_panel_quirks.h>
#ifdef CONFIG_DMI

View File

@ -208,7 +208,7 @@
#include <drm/drm_gem.h>
#include <drm/drm_print.h>
#include <drm/drm_syncobj.h>
#include <drm/drm_utils.h>
#include <drm/drm_timeout.h>
#include "drm_internal.h"
@ -1193,39 +1193,6 @@ static signed long drm_syncobj_array_wait_timeout(struct drm_syncobj **syncobjs,
return timeout;
}
/**
* drm_timeout_abs_to_jiffies - calculate jiffies timeout from absolute value
*
* @timeout_nsec: timeout nsec component in ns, 0 for poll
*
* Calculate the timeout in jiffies from an absolute time in sec/nsec.
*/
signed long drm_timeout_abs_to_jiffies(int64_t timeout_nsec)
{
ktime_t abs_timeout, now;
u64 timeout_ns, timeout_jiffies64;
/* make 0 timeout means poll - absolute 0 doesn't seem valid */
if (timeout_nsec == 0)
return 0;
abs_timeout = ns_to_ktime(timeout_nsec);
now = ktime_get();
if (!ktime_after(abs_timeout, now))
return 0;
timeout_ns = ktime_to_ns(ktime_sub(abs_timeout, now));
timeout_jiffies64 = nsecs_to_jiffies64(timeout_ns);
/* clamp timeout to avoid infinite timeout */
if (timeout_jiffies64 >= MAX_SCHEDULE_TIMEOUT - 1)
return MAX_SCHEDULE_TIMEOUT - 1;
return timeout_jiffies64 + 1;
}
EXPORT_SYMBOL(drm_timeout_abs_to_jiffies);
static int drm_syncobj_array_wait(struct drm_device *dev,
struct drm_file *file_private,
struct drm_syncobj_wait *wait,

View File

@ -0,0 +1,95 @@
// SPDX-License-Identifier: MIT
/*
* Timeout conversion helpers for wait ioctls.
*
* Copyright 2017 Red Hat
* Copyright 2016 Advanced Micro Devices, Inc.
*/
#include <linux/export.h>
#include <linux/jiffies.h>
#include <linux/ktime.h>
#include <linux/math64.h>
#include <linux/sched.h>
#include <drm/drm_timeout.h>
/**
* drm_timeout_abs_to_jiffies - calculate jiffies timeout from absolute value
* @timeout_nsec: timeout nsec component in ns, 0 for poll
*
* Calculate the timeout in jiffies from an absolute time in ns. The result is
* clamped to MAX_SCHEDULE_TIMEOUT - 1 to avoid infinite timeouts.
*
* Returns:
* The number of jiffies until the given timeout, or the upper limit.
*/
signed long drm_timeout_abs_to_jiffies(s64 timeout_nsec)
{
ktime_t abs_timeout, now;
u64 timeout_ns, timeout_jiffies64;
/* make 0 timeout means poll - absolute 0 doesn't seem valid */
if (timeout_nsec == 0)
return 0;
abs_timeout = ns_to_ktime(timeout_nsec);
now = ktime_get();
if (!ktime_after(abs_timeout, now))
return 0;
timeout_ns = ktime_to_ns(ktime_sub(abs_timeout, now));
timeout_jiffies64 = nsecs_to_jiffies64(timeout_ns);
/* clamp timeout to avoid infinite timeout */
if (timeout_jiffies64 >= MAX_SCHEDULE_TIMEOUT - 1)
return MAX_SCHEDULE_TIMEOUT - 1;
return timeout_jiffies64 + 1;
}
EXPORT_SYMBOL(drm_timeout_abs_to_jiffies);
/**
* drm_timeout_rel_to_jiffies - calculate jiffies timeout from relative value
* @timeout_nsec: relative timeout in ns, 0 for poll
*
* Calculate the timeout in jiffies from a relative timeout in ns, for drivers
* whose UAPI expresses a wait as a duration rather than as a deadline.
*
* The result is clamped to MAX_JIFFY_OFFSET. That keeps it positive once it is
* converted to the signed long taken by dma_fence_wait_timeout() and friends,
* which matters on 32-bit, and keeps it distinct from MAX_SCHEDULE_TIMEOUT so
* that a finite wait is never understood as an infinite one.
*
* It's strongly discouraged to use relative timeouts in uAPIs, as they do not
* survive a restarted ioctl. A signal-interrupted ioctl is re-entered with the
* same arguments, so the duration starts counting from zero again. New uAPIs
* should take an absolute deadline and use drm_timeout_abs_to_jiffies().
*
* Returns:
* 0 if @timeout_nsec is 0. Otherwise the equivalent number of jiffies and
* clamped to MAX_JIFFY_OFFSET.
*/
unsigned long drm_timeout_rel_to_jiffies(u64 timeout_nsec)
{
u64 secs;
u32 rem;
/* Make 0 timeout means poll, as for the absolute variant. */
if (timeout_nsec == 0)
return 0;
/*
* As nsecs_to_jiffies64() does not guard against overflow, split
* the timeout into whole seconds and nanoseconds. This way
* nsecs_to_jiffies64() is always handed a value below a second.
*/
secs = div_u64_rem(timeout_nsec, NSEC_PER_SEC, &rem);
if (secs >= MAX_JIFFY_OFFSET / HZ)
return MAX_JIFFY_OFFSET;
return min_t(u64, MAX_JIFFY_OFFSET,
secs_to_jiffies(secs) + nsecs_to_jiffies64(rem) + 1);
}
EXPORT_SYMBOL(drm_timeout_rel_to_jiffies);

View File

@ -26,7 +26,6 @@
#include <drm/drm_crtc.h>
#include <drm/drm_encoder.h>
#include <drm/drm_of.h>
#include <drm/drm_panel.h>
#include <drm/drm_print.h>
#include <drm/drm_probe_helper.h>
#include <drm/exynos_drm.h>
@ -147,7 +146,6 @@ static const struct component_ops exynos_dp_ops = {
static int exynos_dp_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct device_node *np;
struct exynos_dp_device *dp;
dp = devm_kzalloc(&pdev->dev, sizeof(struct exynos_dp_device),
@ -163,18 +161,6 @@ static int exynos_dp_probe(struct platform_device *pdev)
*/
platform_set_drvdata(pdev, dp);
/* This is for the backward compatibility. */
np = of_parse_phandle(dev->of_node, "panel", 0);
if (np) {
dp->plat_data.panel = of_drm_find_panel(np);
of_node_put(np);
if (IS_ERR(dp->plat_data.panel))
return PTR_ERR(dp->plat_data.panel);
goto out;
}
if (of_get_display_timings(dev->of_node)) {
dp->plat_data.next_bridge = devm_drm_of_display_mode_bridge(dp->dev,
dp->dev->of_node,
@ -189,19 +175,11 @@ static int exynos_dp_probe(struct platform_device *pdev)
dp->plat_data.power_off = exynos_dp_poweroff;
dp->plat_data.ops = &exynos_dp_ops;
out:
dp->adp = analogix_dp_probe(dev, &dp->plat_data);
if (IS_ERR(dp->adp)) {
/*
* The driver core does not invoke remove() for failed probes,
* so release the probe-time panel reference here.
*/
if (dp->plat_data.panel)
drm_panel_put(dp->plat_data.panel);
if (IS_ERR(dp->adp))
return PTR_ERR(dp->adp);
}
if (dp->plat_data.panel || dp->plat_data.next_bridge)
if (dp->plat_data.next_bridge)
return component_add(&pdev->dev, &exynos_dp_ops);
else
return analogix_dp_finish_probe(dp->adp);
@ -209,16 +187,6 @@ static int exynos_dp_probe(struct platform_device *pdev)
static void exynos_dp_remove(struct platform_device *pdev)
{
struct exynos_dp_device *dp = platform_get_drvdata(pdev);
/*
* Release the probe-time reference from of_drm_find_panel(). If bind
* ran, the panel_bridge holds a second reference that devm cleanup
* will release when the bridge is destroyed after remove() returns.
*/
if (dp->plat_data.panel)
drm_panel_put(dp->plat_data.panel);
component_del(&pdev->dev, &exynos_dp_ops);
}

View File

@ -10,9 +10,9 @@
#include <linux/fb.h>
#include <drm/clients/drm_fbdev_helper.h>
#include <drm/drm_crtc_helper.h>
#include <drm/drm_drv.h>
#include <drm/drm_fb_helper.h>
#include <drm/drm_framebuffer.h>
#include <drm/drm_gem_framebuffer_helper.h>
#include <drm/drm_prime.h>

View File

@ -7,9 +7,9 @@
#include <linux/fb.h>
#include <drm/clients/drm_fbdev_helper.h>
#include <drm/drm_crtc_helper.h>
#include <drm/drm_drv.h>
#include <drm/drm_fb_helper.h>
#include <drm/drm_framebuffer.h>
#include "gem.h"

View File

@ -38,10 +38,10 @@
#include <linux/vga_switcheroo.h>
#include <drm/clients/drm_client_setup.h>
#include <drm/clients/drm_fbdev_helper.h>
#include <drm/intel/display_parent_interface.h>
#include <drm/drm_crtc.h>
#include <drm/drm_crtc_helper.h>
#include <drm/drm_fb_helper.h>
#include <drm/drm_fourcc.h>
#include <drm/drm_gem.h>
#include <drm/drm_gem_framebuffer_helper.h>

View File

@ -7,6 +7,8 @@
#include <linux/dma-fence-chain.h>
#include <linux/jiffies.h>
#include <drm/drm_timeout.h>
#include "gt/intel_engine.h"
#include "gt/intel_rps.h"
@ -184,25 +186,12 @@ i915_gem_object_wait(struct drm_i915_gem_object *obj,
return !timeout ? -ETIME : 0;
}
static inline unsigned long nsecs_to_jiffies_timeout(const u64 n)
{
/* nsecs_to_jiffies64() does not guard against overflow */
if ((NSEC_PER_SEC % HZ) != 0 &&
div_u64(n, NSEC_PER_SEC) >= MAX_JIFFY_OFFSET / HZ)
return MAX_JIFFY_OFFSET;
return min_t(u64, MAX_JIFFY_OFFSET, nsecs_to_jiffies64(n) + 1);
}
static unsigned long to_wait_timeout(s64 timeout_ns)
{
if (timeout_ns < 0)
return MAX_SCHEDULE_TIMEOUT;
if (timeout_ns == 0)
return 0;
return nsecs_to_jiffies_timeout(timeout_ns);
return drm_timeout_rel_to_jiffies(timeout_ns);
}
/**

View File

@ -278,37 +278,6 @@ pvr_fw_find_mmu_segment(struct pvr_device *pvr_dev, u32 addr, u32 size, void *fw
return -EINVAL;
}
static int
pvr_fw_create_fwif_connection_ctl(struct pvr_device *pvr_dev)
{
struct drm_device *drm_dev = from_pvr_device(pvr_dev);
struct pvr_fw_device *fw_dev = &pvr_dev->fw_dev;
fw_dev->fwif_connection_ctl =
pvr_fw_object_create_and_map_offset(pvr_dev,
fw_dev->fw_heap_info.config_offset +
PVR_ROGUE_FWIF_CONNECTION_CTL_OFFSET,
sizeof(*fw_dev->fwif_connection_ctl),
PVR_BO_FW_FLAGS_DEVICE_UNCACHED,
NULL, NULL,
&fw_dev->mem.fwif_connection_ctl_obj);
if (IS_ERR(fw_dev->fwif_connection_ctl)) {
drm_err(drm_dev,
"Unable to allocate FWIF connection control memory\n");
return PTR_ERR(fw_dev->fwif_connection_ctl);
}
return 0;
}
static void
pvr_fw_fini_fwif_connection_ctl(struct pvr_device *pvr_dev)
{
struct pvr_fw_device *fw_dev = &pvr_dev->fw_dev;
pvr_fw_object_unmap_and_destroy(fw_dev->mem.fwif_connection_ctl_obj);
}
static void
fw_osinit_init(void *cpu_ptr, void *priv)
{
@ -459,21 +428,34 @@ pvr_fw_create_structures(struct pvr_device *pvr_dev)
struct pvr_fw_mem *fw_mem = &fw_dev->mem;
int err;
err = pvr_fw_object_create_offset(pvr_dev,
fw_dev->fw_heap_info.config_offset +
PVR_ROGUE_FWIF_CONNECTION_CTL_OFFSET,
sizeof(struct rogue_fwif_connection_ctl),
PVR_BO_FW_FLAGS_DEVICE_UNCACHED,
NULL, NULL,
&fw_mem->fwif_connection_ctl_obj);
if (err) {
drm_err(drm_dev,
"Unable to allocate FWIF connection control memory\n");
return err;
}
fw_dev->power_sync = pvr_fw_object_create_and_map(pvr_dev, sizeof(*fw_dev->power_sync),
PVR_BO_FW_FLAGS_DEVICE_UNCACHED,
NULL, NULL, &fw_mem->power_sync_obj);
if (IS_ERR(fw_dev->power_sync)) {
drm_err(drm_dev, "Unable to allocate FW power_sync structure\n");
return PTR_ERR(fw_dev->power_sync);
err = PTR_ERR(fw_dev->power_sync);
goto err_release_connection_ctl;
}
fw_dev->hwrinfobuf = pvr_fw_object_create_and_map(pvr_dev, sizeof(*fw_dev->hwrinfobuf),
PVR_BO_FW_FLAGS_DEVICE_UNCACHED,
NULL, NULL, &fw_mem->hwrinfobuf_obj);
if (IS_ERR(fw_dev->hwrinfobuf)) {
err = pvr_fw_object_create(pvr_dev, sizeof(struct rogue_fwif_hwrinfobuf),
PVR_BO_FW_FLAGS_DEVICE_UNCACHED,
NULL, NULL, &fw_mem->hwrinfobuf_obj);
if (err) {
drm_err(drm_dev,
"Unable to allocate FW hwrinfobuf structure\n");
err = PTR_ERR(fw_dev->hwrinfobuf);
goto err_release_power_sync;
}
@ -536,16 +518,14 @@ pvr_fw_create_structures(struct pvr_device *pvr_dev)
goto err_fw_trace_fini;
}
fw_dev->fwif_osinit =
pvr_fw_object_create_and_map_offset(pvr_dev,
fw_dev->fw_heap_info.config_offset +
PVR_ROGUE_FWIF_OSINIT_OFFSET,
sizeof(*fw_dev->fwif_osinit),
PVR_BO_FW_FLAGS_DEVICE_UNCACHED,
fw_osinit_init, pvr_dev, &fw_mem->osinit_obj);
if (IS_ERR(fw_dev->fwif_osinit)) {
err = pvr_fw_object_create_offset(pvr_dev,
fw_dev->fw_heap_info.config_offset +
PVR_ROGUE_FWIF_OSINIT_OFFSET,
sizeof(struct rogue_fwif_osinit),
PVR_BO_FW_FLAGS_DEVICE_UNCACHED,
fw_osinit_init, pvr_dev, &fw_mem->osinit_obj);
if (err) {
drm_err(drm_dev, "Unable to allocate FW OSINIT structure\n");
err = PTR_ERR(fw_dev->fwif_osinit);
goto err_release_osdata;
}
@ -565,7 +545,7 @@ pvr_fw_create_structures(struct pvr_device *pvr_dev)
return 0;
err_release_osinit:
pvr_fw_object_unmap_and_destroy(fw_mem->osinit_obj);
pvr_fw_object_destroy(fw_mem->osinit_obj);
err_release_osdata:
pvr_fw_object_unmap_and_destroy(fw_mem->osdata_obj);
@ -589,11 +569,14 @@ pvr_fw_create_structures(struct pvr_device *pvr_dev)
pvr_fw_object_destroy(fw_mem->mmucache_sync_obj);
err_release_hwrinfobuf:
pvr_fw_object_unmap_and_destroy(fw_mem->hwrinfobuf_obj);
pvr_fw_object_destroy(fw_mem->hwrinfobuf_obj);
err_release_power_sync:
pvr_fw_object_unmap_and_destroy(fw_mem->power_sync_obj);
err_release_connection_ctl:
pvr_fw_object_destroy(fw_mem->fwif_connection_ctl_obj);
return err;
}
@ -611,10 +594,11 @@ pvr_fw_destroy_structures(struct pvr_device *pvr_dev)
pvr_fw_object_unmap_and_destroy(fw_mem->sysinit_obj);
pvr_fw_object_destroy(fw_mem->mmucache_sync_obj);
pvr_fw_object_unmap_and_destroy(fw_mem->hwrinfobuf_obj);
pvr_fw_object_destroy(fw_mem->hwrinfobuf_obj);
pvr_fw_object_unmap_and_destroy(fw_mem->power_sync_obj);
pvr_fw_object_unmap_and_destroy(fw_mem->osdata_obj);
pvr_fw_object_unmap_and_destroy(fw_mem->osinit_obj);
pvr_fw_object_destroy(fw_mem->osinit_obj);
pvr_fw_object_destroy(fw_mem->fwif_connection_ctl_obj);
}
/**
@ -755,10 +739,6 @@ pvr_fw_process(struct pvr_device *pvr_dev)
pvr_fw_object_vunmap(fw_mem->code_obj);
fw_code_ptr = NULL;
err = pvr_fw_create_fwif_connection_ctl(pvr_dev);
if (err)
goto err_free_kdata;
return 0;
err_free_kdata:
@ -842,8 +822,6 @@ pvr_fw_cleanup(struct pvr_device *pvr_dev)
{
struct pvr_fw_mem *fw_mem = &pvr_dev->fw_dev.mem;
pvr_fw_fini_fwif_connection_ctl(pvr_dev);
kfree(fw_mem->core_data);
kfree(fw_mem->core_code);
kfree(fw_mem->data);
@ -1402,6 +1380,46 @@ pvr_fw_object_create_and_map_offset(struct pvr_device *pvr_dev,
fw_obj_out);
}
/**
* pvr_fw_object_create_offset() - Create a FW object and map to firmware
* at the provided offset.
* @pvr_dev: PowerVR device pointer.
* @fw_addr: Base address of desired FW mapping, offset from start of FW heap.
* If a specific FW address is not required, pass %U32_MAX.
* @size: Size of object, in bytes.
* @flags: Options which affect both this operation and future mapping
* operations performed on the returned object. Must be a combination of
* DRM_PVR_BO_* and/or PVR_BO_* flags.
* @init: Initialisation callback.
* @init_priv: Private pointer to pass to initialisation callback.
* @fw_obj_out: Pointer to location to store created object pointer.
*
* %DRM_PVR_BO_DEVICE_PM_FW_PROTECT is implied for all FW objects. Consequently,
* this function will fail if @flags has %DRM_PVR_BO_CPU_ALLOW_USERSPACE_ACCESS
* set.
*
* Returns:
* * 0 on success, or
* * Any error returned by pvr_fw_object_create_and_map().
*/
int
pvr_fw_object_create_offset(struct pvr_device *pvr_dev, u32 fw_addr,
size_t size, u64 flags,
void (*init)(void *cpu_ptr, void *priv),
void *init_priv, struct pvr_fw_object **fw_obj_out)
{
void *cpu_ptr;
cpu_ptr = pvr_fw_object_create_and_map_offset(pvr_dev, fw_addr, size, flags,
init, init_priv, fw_obj_out);
if (IS_ERR(cpu_ptr))
return PTR_ERR(cpu_ptr);
pvr_fw_object_vunmap(*fw_obj_out);
return 0;
}
/**
* pvr_fw_object_destroy() - Destroy a pvr_fw_object
* @fw_obj: Pointer to object to destroy.

View File

@ -349,30 +349,18 @@ struct pvr_fw_device {
/** @fw_mm_base: Base address of address space managed by @fw_mm. */
u64 fw_mm_base;
/**
* @fwif_connection_ctl: Pointer to CPU mapping of FWIF connection
* control structure.
*/
struct rogue_fwif_connection_ctl *fwif_connection_ctl;
/** @fwif_sysinit: Pointer to CPU mapping of FW SYSINIT structure. */
struct rogue_fwif_sysinit *fwif_sysinit;
/** @fwif_sysdata: Pointer to CPU mapping of FW SYSDATA structure. */
struct rogue_fwif_sysdata *fwif_sysdata;
/** @fwif_osinit: Pointer to CPU mapping of FW OSINIT structure. */
struct rogue_fwif_osinit *fwif_osinit;
/** @fwif_osdata: Pointer to CPU mapping of FW OSDATA structure. */
struct rogue_fwif_osdata *fwif_osdata;
/** @power_sync: Pointer to CPU mapping of power sync state. */
u32 *power_sync;
/** @hwrinfobuf: Pointer to CPU mapping of FW HWR info buffer. */
struct rogue_fwif_hwrinfobuf *hwrinfobuf;
/** @fw_trace: Device firmware trace buffer state. */
struct pvr_fw_trace fw_trace;
@ -430,6 +418,12 @@ void *pvr_fw_object_create_and_map(struct pvr_device *pvr_dev, size_t size, u64
void (*init)(void *cpu_ptr, void *priv),
void *init_priv, struct pvr_fw_object **pvr_obj_out);
int
pvr_fw_object_create_offset(struct pvr_device *pvr_dev, u32 fw_addr,
size_t size, u64 flags,
void (*init)(void *cpu_ptr, void *priv),
void *init_priv, struct pvr_fw_object **pvr_obj_out);
void *
pvr_fw_object_create_and_map_offset(struct pvr_device *pvr_dev, u32 dev_offset, size_t size,
u64 flags, void (*init)(void *cpu_ptr, void *priv),

View File

@ -15,6 +15,7 @@
#include <drm/drm_encoder.h>
#include <drm/drm_gem_framebuffer_helper.h>
#include <drm/drm_mode_config.h>
#include <drm/drm_panel.h>
#include <drm/drm_print.h>
#include <drm/drm_probe_helper.h>
#include <drm/drm_vblank.h>

View File

@ -6,6 +6,7 @@ config DRM_IMX_DCSS
select DRM_DISPLAY_HELPER
select DRM_BRIDGE_CONNECTOR
select DRM_GEM_DMA_HELPER
select DRM_PANEL
select VIDEOMODE_HELPERS
depends on DRM && ((ARCH_MXC && ARM64) || COMPILE_TEST)
help

View File

@ -2,6 +2,7 @@
// SPDX-FileCopyrightText: 2020 Marian Cichy <M.Cichy@pengutronix.de>
#include <drm/clients/drm_client_setup.h>
#include <drm/drm_atomic.h>
#include <drm/drm_bridge.h>
#include <drm/drm_bridge_connector.h>
#include <drm/drm_damage_helper.h>
@ -14,10 +15,11 @@
#include <drm/drm_gem_dma_helper.h>
#include <drm/drm_gem_framebuffer_helper.h>
#include <drm/drm_of.h>
#include <drm/drm_plane_helper.h>
#include <drm/drm_print.h>
#include <drm/drm_probe_helper.h>
#include <drm/drm_simple_kms_helper.h>
#include <drm/drm_vblank.h>
#include <drm/drm_vblank_helper.h>
#include <linux/bitfield.h>
#include <linux/clk.h>
#include <linux/dma-mapping.h>
@ -101,7 +103,9 @@
struct imx_lcdc {
struct drm_device drm;
struct drm_simple_display_pipe pipe;
struct drm_plane plane;
struct drm_crtc crtc;
struct drm_encoder encoder;
struct drm_connector *connector;
void __iomem *base;
@ -134,14 +138,15 @@ static unsigned int imx_lcdc_get_format(unsigned int drm_format)
}
}
static void imx_lcdc_update_hw_registers(struct drm_simple_display_pipe *pipe,
struct drm_plane_state *old_state,
static void imx_lcdc_update_hw_registers(struct drm_crtc *crtc,
struct drm_crtc_state *old_crtc_state,
struct drm_crtc_state *new_crtc_state,
struct drm_plane_state *new_state,
bool mode_set)
{
struct drm_crtc *crtc = &pipe->crtc;
struct drm_plane_state *new_state = pipe->plane.state;
struct imx_lcdc *lcdc = imx_lcdc_from_drmdev(crtc->dev);
const struct drm_display_mode *mode = &new_crtc_state->mode;
struct drm_framebuffer *fb = new_state->fb;
struct imx_lcdc *lcdc = imx_lcdc_from_drmdev(pipe->crtc.dev);
u32 lpcr, lvcr, lhcr;
u32 framesize;
dma_addr_t addr;
@ -154,24 +159,24 @@ static void imx_lcdc_update_hw_registers(struct drm_simple_display_pipe *pipe,
return;
/* Disable PER clock to make register write possible */
if (old_state && old_state->crtc && old_state->crtc->enabled)
if (old_crtc_state && old_crtc_state->enable)
clk_disable_unprepare(lcdc->clk_per);
/* Framesize */
framesize = FIELD_PREP(IMX21LCDC_LSR_XMAX, crtc->mode.hdisplay >> 4) |
FIELD_PREP(IMX21LCDC_LSR_YMAX, crtc->mode.vdisplay);
framesize = FIELD_PREP(IMX21LCDC_LSR_XMAX, mode->hdisplay >> 4) |
FIELD_PREP(IMX21LCDC_LSR_YMAX, mode->vdisplay);
writel(framesize, lcdc->base + IMX21LCDC_LSR);
/* HSYNC */
lhcr = FIELD_PREP(IMX21LCDC_LHCR_HFPORCH, crtc->mode.hsync_start - crtc->mode.hdisplay - 1) |
FIELD_PREP(IMX21LCDC_LHCR_HWIDTH, crtc->mode.hsync_end - crtc->mode.hsync_start - 1) |
FIELD_PREP(IMX21LCDC_LHCR_HBPORCH, crtc->mode.htotal - crtc->mode.hsync_end - 3);
lhcr = FIELD_PREP(IMX21LCDC_LHCR_HFPORCH, mode->hsync_start - mode->hdisplay - 1) |
FIELD_PREP(IMX21LCDC_LHCR_HWIDTH, mode->hsync_end - mode->hsync_start - 1) |
FIELD_PREP(IMX21LCDC_LHCR_HBPORCH, mode->htotal - mode->hsync_end - 3);
writel(lhcr, lcdc->base + IMX21LCDC_LHCR);
/* VSYNC */
lvcr = FIELD_PREP(IMX21LCDC_LVCR_VFPORCH, crtc->mode.vsync_start - crtc->mode.vdisplay) |
FIELD_PREP(IMX21LCDC_LVCR_VWIDTH, crtc->mode.vsync_end - crtc->mode.vsync_start) |
FIELD_PREP(IMX21LCDC_LVCR_VBPORCH, crtc->mode.vtotal - crtc->mode.vsync_end);
lvcr = FIELD_PREP(IMX21LCDC_LVCR_VFPORCH, mode->vsync_start - mode->vdisplay) |
FIELD_PREP(IMX21LCDC_LVCR_VWIDTH, mode->vsync_end - mode->vsync_start) |
FIELD_PREP(IMX21LCDC_LVCR_VBPORCH, mode->vtotal - mode->vsync_end);
writel(lvcr, lcdc->base + IMX21LCDC_LVCR);
lpcr = readl(lcdc->base + IMX21LCDC_LPCR);
@ -183,19 +188,20 @@ static void imx_lcdc_update_hw_registers(struct drm_simple_display_pipe *pipe,
writel(new_state->fb->pitches[0] / 4, lcdc->base + IMX21LCDC_LVPWR);
/* Enable PER clock */
if (new_state->crtc->enabled)
if (new_crtc_state->enable)
clk_prepare_enable(lcdc->clk_per);
}
static void imx_lcdc_pipe_enable(struct drm_simple_display_pipe *pipe,
struct drm_crtc_state *crtc_state,
struct drm_plane_state *plane_state)
static void imx_lcdc_crtc_helper_atomic_enable(struct drm_crtc *crtc,
struct drm_atomic_commit *commit)
{
int ret;
int clk_div;
int bpp;
struct imx_lcdc *lcdc = imx_lcdc_from_drmdev(pipe->crtc.dev);
struct drm_display_mode *mode = &pipe->crtc.mode;
struct imx_lcdc *lcdc = imx_lcdc_from_drmdev(crtc->dev);
struct drm_crtc_state *cstate = drm_atomic_get_new_crtc_state(commit, crtc);
struct drm_plane_state *pstate = drm_atomic_get_new_plane_state(commit, &lcdc->plane);
struct drm_display_mode *mode = &cstate->mode;
struct drm_display_info *disp_info = &lcdc->connector->display_info;
const int hsync_pol = (mode->flags & DRM_MODE_FLAG_PHSYNC) ? 0 : 1;
const int vsync_pol = (mode->flags & DRM_MODE_FLAG_PVSYNC) ? 0 : 1;
@ -206,7 +212,7 @@ static void imx_lcdc_pipe_enable(struct drm_simple_display_pipe *pipe,
clk_div = DIV_ROUND_CLOSEST_ULL(clk_get_rate(lcdc->clk_per),
mode->clock * 1000);
bpp = imx_lcdc_get_format(plane_state->fb->format->format);
bpp = imx_lcdc_get_format(pstate->fb->format->format);
writel(FIELD_PREP(IMX21LCDC_LPCR_PCD, clk_div - 1) |
FIELD_PREP(IMX21LCDC_LPCR_LPPOL, hsync_pol) |
@ -230,40 +236,46 @@ static void imx_lcdc_pipe_enable(struct drm_simple_display_pipe *pipe,
ret = clk_prepare_enable(lcdc->clk_ipg);
if (ret) {
dev_err(pipe->crtc.dev->dev, "Cannot enable ipg clock: %pe\n", ERR_PTR(ret));
dev_err(crtc->dev->dev, "Cannot enable ipg clock: %pe\n", ERR_PTR(ret));
return;
}
ret = clk_prepare_enable(lcdc->clk_ahb);
if (ret) {
dev_err(pipe->crtc.dev->dev, "Cannot enable ahb clock: %pe\n", ERR_PTR(ret));
dev_err(crtc->dev->dev, "Cannot enable ahb clock: %pe\n", ERR_PTR(ret));
clk_disable_unprepare(lcdc->clk_ipg);
return;
}
imx_lcdc_update_hw_registers(pipe, NULL, true);
imx_lcdc_update_hw_registers(crtc, NULL, cstate, pstate, true);
/* Enable VBLANK Interrupt */
writel(INTR_EOF, lcdc->base + IMX21LCDC_LIER);
drm_crtc_vblank_on(crtc);
}
static void imx_lcdc_pipe_disable(struct drm_simple_display_pipe *pipe)
static void imx_lcdc_crtc_helper_atomic_disable(struct drm_crtc *crtc,
struct drm_atomic_commit *commit)
{
struct imx_lcdc *lcdc = imx_lcdc_from_drmdev(pipe->crtc.dev);
struct drm_crtc *crtc = &lcdc->pipe.crtc;
struct drm_crtc_state *old_crtc_state = drm_atomic_get_old_crtc_state(commit, crtc);
struct drm_crtc_state *new_crtc_state = drm_atomic_get_new_crtc_state(commit, crtc);
struct imx_lcdc *lcdc = imx_lcdc_from_drmdev(crtc->dev);
struct drm_pending_vblank_event *event;
drm_crtc_vblank_off(crtc);
clk_disable_unprepare(lcdc->clk_ahb);
clk_disable_unprepare(lcdc->clk_ipg);
if (pipe->crtc.enabled)
if (old_crtc_state->enable)
clk_disable_unprepare(lcdc->clk_per);
spin_lock_irq(&lcdc->drm.event_lock);
event = crtc->state->event;
event = new_crtc_state->event;
if (event) {
crtc->state->event = NULL;
new_crtc_state->event = NULL;
drm_crtc_send_vblank_event(crtc, event);
}
spin_unlock_irq(&lcdc->drm.event_lock);
@ -272,65 +284,132 @@ static void imx_lcdc_pipe_disable(struct drm_simple_display_pipe *pipe)
writel(0, lcdc->base + IMX21LCDC_LIER);
}
static int imx_lcdc_pipe_check(struct drm_simple_display_pipe *pipe,
struct drm_plane_state *plane_state,
struct drm_crtc_state *crtc_state)
static int imx_lcdc_crtc_helper_atomic_check(struct drm_crtc *crtc,
struct drm_atomic_commit *commit)
{
struct drm_crtc_state *crtc_state = drm_atomic_get_new_crtc_state(commit, crtc);
struct drm_crtc_state *old_crtc_state = drm_atomic_get_old_crtc_state(commit, crtc);
const struct drm_display_mode *mode = &crtc_state->mode;
const struct drm_display_mode *old_mode = &pipe->crtc.state->mode;
const struct drm_display_mode *old_mode = &old_crtc_state->mode;
int ret;
if (mode->hdisplay < LCDC_MIN_XRES || mode->hdisplay > LCDC_MAX_XRES ||
mode->vdisplay < LCDC_MIN_YRES || mode->vdisplay > LCDC_MAX_YRES ||
mode->hdisplay % 0x10) { /* must be multiple of 16 */
drm_err(pipe->crtc.dev, "unsupported display mode (%u x %u)\n",
if (crtc_state->enable) {
ret = drm_atomic_helper_check_crtc_primary_plane(crtc_state);
if (ret)
return ret;
}
if (crtc_state->enable &&
(mode->hdisplay < LCDC_MIN_XRES || mode->hdisplay > LCDC_MAX_XRES ||
mode->vdisplay < LCDC_MIN_YRES || mode->vdisplay > LCDC_MAX_YRES ||
mode->hdisplay % 0x10)) { /* must be multiple of 16 */
drm_err(crtc->dev, "unsupported display mode (%u x %u)\n",
mode->hdisplay, mode->vdisplay);
return -EINVAL;
}
crtc_state->mode_changed =
old_mode->hdisplay != mode->hdisplay ||
old_mode->vdisplay != mode->vdisplay;
if (old_mode->hdisplay != mode->hdisplay ||
old_mode->vdisplay != mode->vdisplay)
crtc_state->mode_changed = true;
return 0;
return drm_atomic_add_affected_planes(commit, crtc);
}
static void imx_lcdc_pipe_update(struct drm_simple_display_pipe *pipe,
struct drm_plane_state *old_state)
static void imx_lcdc_plane_helper_atomic_update(struct drm_plane *plane,
struct drm_atomic_commit *commit)
{
struct drm_crtc *crtc = &pipe->crtc;
struct drm_pending_vblank_event *event = crtc->state->event;
struct drm_plane_state *new_state = pipe->plane.state;
struct drm_plane_state *old_state = drm_atomic_get_old_plane_state(commit, plane);
struct drm_plane_state *new_state = drm_atomic_get_new_plane_state(commit, plane);
struct drm_crtc *crtc = new_state->crtc;
struct drm_crtc_state *old_crtc_state = NULL;
struct drm_crtc_state *new_crtc_state;
struct drm_framebuffer *fb = new_state->fb;
struct drm_framebuffer *old_fb = old_state->fb;
struct drm_crtc *old_crtc = old_state->crtc;
bool mode_changed = false;
if (!fb || !crtc)
return;
if (old_crtc)
old_crtc_state = drm_atomic_get_old_crtc_state(commit, old_crtc);
new_crtc_state = drm_atomic_get_new_crtc_state(commit, crtc);
if (!new_crtc_state)
return;
if (old_fb && old_fb->format != fb->format)
mode_changed = true;
else if (old_crtc != crtc)
mode_changed = true;
imx_lcdc_update_hw_registers(pipe, old_state, mode_changed);
if (event) {
crtc->state->event = NULL;
spin_lock_irq(&crtc->dev->event_lock);
if (crtc->state->active && drm_crtc_vblank_get(crtc) == 0)
drm_crtc_arm_vblank_event(crtc, event);
else
drm_crtc_send_vblank_event(crtc, event);
spin_unlock_irq(&crtc->dev->event_lock);
}
if (!drm_atomic_crtc_needs_modeset(new_crtc_state))
imx_lcdc_update_hw_registers(crtc, old_crtc_state, new_crtc_state,
new_state, mode_changed);
}
static const struct drm_simple_display_pipe_funcs imx_lcdc_pipe_funcs = {
.enable = imx_lcdc_pipe_enable,
.disable = imx_lcdc_pipe_disable,
.check = imx_lcdc_pipe_check,
.update = imx_lcdc_pipe_update,
static int imx_lcdc_plane_helper_atomic_check(struct drm_plane *plane,
struct drm_atomic_commit *commit)
{
struct drm_plane_state *plane_state = drm_atomic_get_new_plane_state(commit, plane);
struct drm_crtc_state *crtc_state = NULL;
if (plane_state->crtc) {
crtc_state = drm_atomic_get_crtc_state(commit, plane_state->crtc);
if (IS_ERR(crtc_state))
return PTR_ERR(crtc_state);
}
return drm_atomic_helper_check_plane_state(plane_state, crtc_state,
DRM_PLANE_NO_SCALING,
DRM_PLANE_NO_SCALING,
false, false);
}
static const struct drm_plane_helper_funcs imx_lcdc_plane_helper_funcs = {
.prepare_fb = drm_gem_plane_helper_prepare_fb,
.atomic_check = imx_lcdc_plane_helper_atomic_check,
.atomic_update = imx_lcdc_plane_helper_atomic_update,
};
static const struct drm_plane_funcs imx_lcdc_plane_funcs = {
.update_plane = drm_atomic_helper_update_plane,
.disable_plane = drm_atomic_helper_disable_plane,
.destroy = drm_plane_cleanup,
.atomic_create_state = drm_atomic_helper_plane_create_state,
.atomic_duplicate_state = drm_atomic_helper_plane_duplicate_state,
.atomic_destroy_state = drm_atomic_helper_plane_destroy_state,
};
static const struct drm_crtc_helper_funcs imx_lcdc_crtc_helper_funcs = {
.atomic_check = imx_lcdc_crtc_helper_atomic_check,
.atomic_enable = imx_lcdc_crtc_helper_atomic_enable,
.atomic_disable = imx_lcdc_crtc_helper_atomic_disable,
.atomic_flush = drm_crtc_vblank_atomic_flush,
};
static int imx_lcdc_crtc_enable_vblank(struct drm_crtc *crtc)
{
return 0;
}
static void imx_lcdc_crtc_disable_vblank(struct drm_crtc *crtc)
{
}
static const struct drm_crtc_funcs imx_lcdc_crtc_funcs = {
.atomic_create_state = drm_atomic_helper_crtc_create_state,
.destroy = drm_crtc_cleanup,
.set_config = drm_atomic_helper_set_config,
.page_flip = drm_atomic_helper_page_flip,
.atomic_duplicate_state = drm_atomic_helper_crtc_duplicate_state,
.atomic_destroy_state = drm_atomic_helper_crtc_destroy_state,
.enable_vblank = imx_lcdc_crtc_enable_vblank,
.disable_vblank = imx_lcdc_crtc_disable_vblank,
};
static const struct drm_encoder_funcs imx_lcdc_encoder_funcs = {
.destroy = drm_encoder_cleanup,
};
static const struct drm_mode_config_funcs imx_lcdc_mode_config_funcs = {
@ -368,7 +447,7 @@ MODULE_DEVICE_TABLE(of, imx_lcdc_of_dev_id);
static irqreturn_t imx_lcdc_irq_handler(int irq, void *arg)
{
struct imx_lcdc *lcdc = arg;
struct drm_crtc *crtc = &lcdc->pipe.crtc;
struct drm_crtc *crtc = &lcdc->crtc;
unsigned int status;
status = readl(lcdc->base + IMX21LCDC_LISR);
@ -385,7 +464,9 @@ static int imx_lcdc_probe(struct platform_device *pdev)
{
struct imx_lcdc *lcdc;
struct drm_device *drm;
struct drm_bridge *bridge;
struct drm_plane *plane;
struct drm_crtc *crtc;
struct drm_encoder *encoder;
int irq;
int ret;
struct device *dev = &pdev->dev;
@ -401,7 +482,8 @@ static int imx_lcdc_probe(struct platform_device *pdev)
if (IS_ERR(lcdc->base))
return dev_err_probe(dev, PTR_ERR(lcdc->base), "Cannot get IO memory\n");
bridge = devm_drm_of_get_bridge(dev, dev->of_node, 0, 0);
struct drm_bridge *bridge __free(drm_bridge_put) =
of_drm_get_bridge_by_endpoint(dev->of_node, 0, 0);
if (IS_ERR(bridge))
return dev_err_probe(dev, PTR_ERR(bridge), "Failed to find bridge\n");
@ -427,23 +509,40 @@ static int imx_lcdc_probe(struct platform_device *pdev)
if (ret)
return dev_err_probe(dev, ret, "Cannot initialize mode configuration structure\n");
/* CRTC, Plane, Encoder */
ret = drm_simple_display_pipe_init(drm, &lcdc->pipe,
&imx_lcdc_pipe_funcs,
imx_lcdc_formats,
ARRAY_SIZE(imx_lcdc_formats), NULL, NULL);
plane = &lcdc->plane;
ret = drm_universal_plane_init(drm, plane, 0,
&imx_lcdc_plane_funcs,
imx_lcdc_formats,
ARRAY_SIZE(imx_lcdc_formats),
NULL,
DRM_PLANE_TYPE_PRIMARY, NULL);
if (ret < 0)
return dev_err_probe(drm->dev, ret, "Cannot setup simple display pipe\n");
return dev_err_probe(drm->dev, ret, "Cannot initialize primary plane\n");
drm_plane_helper_add(plane, &imx_lcdc_plane_helper_funcs);
crtc = &lcdc->crtc;
ret = drm_crtc_init_with_planes(drm, crtc, plane, NULL,
&imx_lcdc_crtc_funcs, NULL);
if (ret < 0)
return dev_err_probe(drm->dev, ret, "Cannot initialize CRTC\n");
drm_crtc_helper_add(crtc, &imx_lcdc_crtc_helper_funcs);
encoder = &lcdc->encoder;
ret = drm_encoder_init(drm, encoder, &imx_lcdc_encoder_funcs,
DRM_MODE_ENCODER_NONE, NULL);
if (ret < 0)
return dev_err_probe(drm->dev, ret, "Cannot initialize encoder\n");
encoder->possible_crtcs = drm_crtc_mask(crtc);
ret = drm_vblank_init(drm, drm->mode_config.num_crtc);
if (ret < 0)
return dev_err_probe(drm->dev, ret, "Failed to initialize vblank\n");
ret = drm_bridge_attach(&lcdc->pipe.encoder, bridge, NULL, DRM_BRIDGE_ATTACH_NO_CONNECTOR);
ret = drm_bridge_attach(encoder, bridge, NULL, DRM_BRIDGE_ATTACH_NO_CONNECTOR);
if (ret)
return dev_err_probe(drm->dev, ret, "Cannot attach bridge\n");
lcdc->connector = drm_bridge_connector_init(drm, &lcdc->pipe.encoder);
lcdc->connector = drm_bridge_connector_init(drm, encoder);
if (IS_ERR(lcdc->connector))
return dev_err_probe(drm->dev, PTR_ERR(lcdc->connector), "Cannot init bridge connector\n");
@ -482,7 +581,9 @@ static int imx_lcdc_probe(struct platform_device *pdev)
drm->mode_config.funcs = &imx_lcdc_mode_config_funcs;
drm->mode_config.helper_private = &imx_lcdc_mode_config_helpers;
drm_mode_config_reset(drm);
ret = drm_mode_config_create_initial_state(drm);
if (ret)
return dev_err_probe(dev, ret, "Cannot create initial state\n");
irq = platform_get_irq(pdev, 0);
if (irq < 0) {

View File

@ -7,6 +7,7 @@ config DRM_INGENIC
depends on COMMON_CLK
select DRM_BRIDGE
select DRM_CLIENT_SELECTION
select DRM_PANEL
select DRM_PANEL_BRIDGE
select DRM_KMS_HELPER
select DRM_DISPLAY_HELPER

View File

@ -10,7 +10,7 @@
#include <drm/drm_file.h>
#include <drm/drm_syncobj.h>
#include <drm/drm_utils.h>
#include <drm/drm_timeout.h>
#include <drm/lima_drm.h>

View File

@ -5,6 +5,7 @@ config DRM_LOGICVC
select DRM_CLIENT_SELECTION
select DRM_KMS_HELPER
select DRM_GEM_DMA_HELPER
select DRM_PANEL
select REGMAP
select REGMAP_MMIO
help

View File

@ -9,7 +9,7 @@ config DRM_MCDE
select DRM_CLIENT_SELECTION
select DRM_MIPI_DSI
select DRM_BRIDGE
select DRM_PANEL_BRIDGE
select DRM_PANEL
select DRM_KMS_HELPER
select DRM_GEM_DMA_HELPER
help

View File

@ -18,6 +18,7 @@
#include <drm/drm_gem_atomic_helper.h>
#include <drm/drm_gem_dma_helper.h>
#include <drm/drm_mipi_dsi.h>
#include <drm/drm_panel.h>
#include <drm/drm_print.h>
#include <drm/drm_bridge.h>
#include <drm/drm_vblank.h>

View File

@ -11,17 +11,15 @@
#include <linux/regulator/consumer.h>
#include <video/mipi_display.h>
#include <drm/drm_atomic_helper.h>
#include <drm/drm_atomic_state_helper.h>
#include <drm/drm_bridge.h>
#include <drm/drm_device.h>
#include <drm/drm_drv.h>
#include <drm/drm_encoder.h>
#include <drm/drm_mipi_dsi.h>
#include <drm/drm_modeset_helper_vtables.h>
#include <drm/drm_of.h>
#include <drm/drm_panel.h>
#include <drm/drm_print.h>
#include <drm/drm_probe_helper.h>
#include "mcde_drm.h"
#include "mcde_dsi_regs.h"
@ -39,7 +37,6 @@ struct mcde_dsi {
struct device *dev;
struct mcde *mcde;
struct drm_bridge bridge;
struct drm_panel *panel;
struct mipi_dsi_host dsi_host;
struct mipi_dsi_device *mdsi;
const struct drm_display_mode *mode;
@ -1076,7 +1073,6 @@ static int mcde_dsi_bind(struct device *dev, struct device *master,
struct drm_device *drm = data;
struct mcde *mcde = to_mcde(drm);
struct mcde_dsi *d = dev_get_drvdata(dev);
struct drm_panel *panel = NULL;
struct drm_bridge *bridge __free(drm_bridge_put) = NULL;
if (!of_get_available_child_count(dev->of_node)) {
@ -1104,40 +1100,21 @@ static int mcde_dsi_bind(struct device *dev, struct device *master,
/* Look for a panel as a child to this node */
for_each_available_child_of_node_scoped(dev->of_node, child) {
panel = of_drm_find_panel(child);
if (IS_ERR(panel)) {
dev_err(dev, "failed to find panel try bridge (%ld)\n",
PTR_ERR(panel));
panel = NULL;
bridge = of_drm_find_and_get_bridge(child);
if (!bridge) {
dev_err(dev, "failed to find bridge\n");
return -EINVAL;
}
bridge = of_drm_find_and_get_bridge(child);
if (!bridge) {
dev_err(dev, "failed to find bridge\n");
return -EINVAL;
}
if (panel || bridge)
break;
break;
}
if (panel) {
bridge = drm_panel_bridge_add_typed(panel,
DRM_MODE_CONNECTOR_DSI);
drm_panel_put(panel);
if (IS_ERR(bridge)) {
dev_err(dev, "error adding panel bridge\n");
return PTR_ERR(bridge);
}
drm_bridge_get(bridge);
dev_info(dev, "connected to panel\n");
d->panel = panel;
} else if (bridge) {
if (!bridge) {
dev_err(dev, "no bridge\n");
return -ENODEV;
} else if (!drm_bridge_is_panel(bridge)) {
/* TODO: AV8100 HDMI encoder goes here for example */
dev_info(dev, "connected to non-panel bridge (unsupported)\n");
return -ENODEV;
} else {
dev_err(dev, "no panel or bridge\n");
return -ENODEV;
}
d->bridge.next_bridge = drm_bridge_get(bridge);
@ -1159,8 +1136,6 @@ static void mcde_dsi_unbind(struct device *dev, struct device *master,
{
struct mcde_dsi *d = dev_get_drvdata(dev);
if (d->panel)
drm_panel_bridge_remove(d->bridge.next_bridge);
regmap_update_bits(d->prcmu, PRCM_DSI_SW_RESET,
PRCM_DSI_SW_RESET_DSI0_SW_RESETN, 0);
}

View File

@ -15,6 +15,7 @@
#include <drm/display/drm_dp_aux_bus.h>
#include <drm/display/drm_hdmi_audio_helper.h>
#include <drm/drm_edid.h>
#include <drm/drm_panel.h>
#include "msm_drv.h"
#include "msm_kms.h"

Some files were not shown because too many files have changed in this diff Show More