summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.mailmap3
-rw-r--r--Documentation/devicetree/bindings/display/panel/novatek,nt36536.yaml91
-rw-r--r--Documentation/devicetree/bindings/display/panel/panel-lvds.yaml2
-rw-r--r--Documentation/devicetree/bindings/display/tegra/nvidia,tegra124-vic.yaml1
-rw-r--r--Documentation/devicetree/bindings/display/tegra/nvidia,tegra20-host1x.yaml20
-rw-r--r--MAINTAINERS2
-rw-r--r--drivers/accel/ivpu/ivpu_fw.c2
-rw-r--r--drivers/accel/ivpu/ivpu_mmu_context.c4
-rw-r--r--drivers/gpu/drm/bridge/analogix/analogix-anx6345.c12
-rw-r--r--drivers/gpu/drm/bridge/fsl-ldb.c1
-rw-r--r--drivers/gpu/drm/bridge/lontium-lt9211.c1
-rw-r--r--drivers/gpu/drm/bridge/lvds-codec.c1
-rw-r--r--drivers/gpu/drm/bridge/panel.c20
-rw-r--r--drivers/gpu/drm/bridge/samsung-dsim.c1
-rw-r--r--drivers/gpu/drm/bridge/ssd2825.c1
-rw-r--r--drivers/gpu/drm/bridge/tc358767.c2
-rw-r--r--drivers/gpu/drm/bridge/tc358768.c1
-rw-r--r--drivers/gpu/drm/bridge/waveshare-dsi.c1
-rw-r--r--drivers/gpu/drm/drm_of.c3
-rw-r--r--drivers/gpu/drm/drm_panel.c24
-rw-r--r--drivers/gpu/drm/drm_panel.c.orig722
-rw-r--r--drivers/gpu/drm/drm_probe_helper.c53
-rw-r--r--drivers/gpu/drm/etnaviv/etnaviv_gpu.c10
-rw-r--r--drivers/gpu/drm/etnaviv/etnaviv_gpu.h6
-rw-r--r--drivers/gpu/drm/exynos/exynos_dp.c19
-rw-r--r--drivers/gpu/drm/exynos/exynos_drm_dpi.c3
-rw-r--r--drivers/gpu/drm/fsl-dcu/fsl_dcu_drm_rgb.c18
-rw-r--r--drivers/gpu/drm/gma500/cdv_intel_crt.c9
-rw-r--r--drivers/gpu/drm/gma500/cdv_intel_dp.c120
-rw-r--r--drivers/gpu/drm/gma500/cdv_intel_hdmi.c10
-rw-r--r--drivers/gpu/drm/gma500/cdv_intel_lvds.c9
-rw-r--r--drivers/gpu/drm/gma500/oaktrail_hdmi.c9
-rw-r--r--drivers/gpu/drm/gma500/oaktrail_lvds.c9
-rw-r--r--drivers/gpu/drm/gma500/psb_intel_lvds.c9
-rw-r--r--drivers/gpu/drm/hyperv/hyperv_drm_drv.c14
-rw-r--r--drivers/gpu/drm/imagination/pvr_device.c4
-rw-r--r--drivers/gpu/drm/imagination/pvr_drv.c5
-rw-r--r--drivers/gpu/drm/imagination/pvr_queue.c11
-rw-r--r--drivers/gpu/drm/imagination/pvr_rogue_fwif_check.h19
-rw-r--r--drivers/gpu/drm/imx/dcss/dcss-kms.c4
-rw-r--r--drivers/gpu/drm/ingenic/ingenic-drm-drv.c4
-rw-r--r--drivers/gpu/drm/logicvc/logicvc_interface.c12
-rw-r--r--drivers/gpu/drm/mcde/mcde_drv.c1
-rw-r--r--drivers/gpu/drm/mcde/mcde_dsi.c1
-rw-r--r--drivers/gpu/drm/mxsfb/mxsfb_drv.c2
-rw-r--r--drivers/gpu/drm/nouveau/nouveau_drm.c1
-rw-r--r--drivers/gpu/drm/omapdrm/dss/output.c1
-rw-r--r--drivers/gpu/drm/panel/Kconfig11
-rw-r--r--drivers/gpu/drm/panel/Makefile1
-rw-r--r--drivers/gpu/drm/panel/panel-edp.c4
-rw-r--r--drivers/gpu/drm/panel/panel-novatek-nt36536.c488
-rw-r--r--drivers/gpu/drm/panel/panel-osd-osd101t2587-53ts.c14
-rw-r--r--drivers/gpu/drm/panel/panel-samsung-sofef00.c4
-rw-r--r--drivers/gpu/drm/panel/panel-simple.c3
-rw-r--r--drivers/gpu/drm/panfrost/panfrost_device.c1
-rw-r--r--drivers/gpu/drm/panfrost/panfrost_device.h1
-rw-r--r--drivers/gpu/drm/pl111/pl111_drv.c1
-rw-r--r--drivers/gpu/drm/renesas/rcar-du/rcar_du_encoder.c1
-rw-r--r--drivers/gpu/drm/renesas/rcar-du/rcar_lvds.c1
-rw-r--r--drivers/gpu/drm/renesas/rz-du/rzg2l_du_encoder.c1
-rw-r--r--drivers/gpu/drm/rockchip/analogix_dp-rockchip.c11
-rw-r--r--drivers/gpu/drm/rockchip/rockchip_lvds.c4
-rw-r--r--drivers/gpu/drm/rockchip/rockchip_rgb.c3
-rw-r--r--drivers/gpu/drm/solomon/ssd130x.c9
-rw-r--r--drivers/gpu/drm/sti/sti_dvo.c3
-rw-r--r--drivers/gpu/drm/stm/ltdc.c2
-rw-r--r--drivers/gpu/drm/stm/lvds.c12
-rw-r--r--drivers/gpu/drm/sun4i/sun4i_lvds.c13
-rw-r--r--drivers/gpu/drm/sun4i/sun4i_rgb.c13
-rw-r--r--drivers/gpu/drm/sun4i/sun4i_tcon.c2
-rw-r--r--drivers/gpu/drm/sun4i/sun6i_mipi_dsi.c6
-rw-r--r--drivers/gpu/drm/tegra/drm.c1
-rw-r--r--drivers/gpu/drm/tegra/dsi.c5
-rw-r--r--drivers/gpu/drm/tegra/falcon.c66
-rw-r--r--drivers/gpu/drm/tegra/falcon.h23
-rw-r--r--drivers/gpu/drm/tegra/output.c24
-rw-r--r--drivers/gpu/drm/tegra/vic.c96
-rw-r--r--drivers/gpu/drm/tegra/vic.h9
-rw-r--r--drivers/gpu/drm/tidss/tidss_kms.c16
-rw-r--r--drivers/gpu/drm/tve200/tve200_drv.c2
-rw-r--r--drivers/gpu/drm/v3d/v3d_drv.h5
-rw-r--r--drivers/gpu/drm/v3d/v3d_submit.c92
-rw-r--r--drivers/gpu/drm/vc4/vc4_hdmi.c8
-rw-r--r--drivers/gpu/host1x/Makefile3
-rw-r--r--drivers/gpu/host1x/bus.c2
-rw-r--r--drivers/gpu/host1x/cdma.c3
-rw-r--r--drivers/gpu/host1x/context.c13
-rw-r--r--drivers/gpu/host1x/debug.c4
-rw-r--r--drivers/gpu/host1x/dev.c41
-rw-r--r--drivers/gpu/host1x/hw/cdma_hw.c14
-rw-r--r--drivers/gpu/host1x/hw/channel_hw.c15
-rw-r--r--drivers/gpu/host1x/hw/host1x10.c33
-rw-r--r--drivers/gpu/host1x/hw/host1x10.h15
-rw-r--r--drivers/gpu/host1x/hw/host1x10_hardware.h21
-rw-r--r--drivers/gpu/host1x/hw/hw_host1x10_common.h6
-rw-r--r--drivers/gpu/host1x/hw/hw_host1x10_hypervisor.h10
-rw-r--r--drivers/gpu/host1x/hw/hw_host1x10_uclass.h181
-rw-r--r--drivers/gpu/host1x/hw/hw_host1x10_vm.h36
-rw-r--r--drivers/gpu/host1x/intr.c5
-rw-r--r--drivers/gpu/host1x/job.c2
-rw-r--r--drivers/gpu/host1x/syncpt.c7
-rw-r--r--drivers/gpu/host1x/syncpt.h3
-rw-r--r--drivers/gpu/tests/gpu_buddy_test.c41
-rw-r--r--include/drm/drm_connector.h5
-rw-r--r--kernel/cgroup/dmem.c69
105 files changed, 2460 insertions, 303 deletions
diff --git a/.mailmap b/.mailmap
index f4f18b06f2f1..d8a1f19c381b 100644
--- a/.mailmap
+++ b/.mailmap
@@ -628,7 +628,8 @@ Nadav Amit <nadav.amit@gmail.com> <namit@cs.technion.ac.il>
Nadia Yvette Chambers <nyc@holomorphy.com> William Lee Irwin III <wli@holomorphy.com>
Naoya Horiguchi <nao.horiguchi@gmail.com> <n-horiguchi@ah.jp.nec.com>
Naoya Horiguchi <nao.horiguchi@gmail.com> <naoya.horiguchi@nec.com>
-Natalie Vock <natalie.vock@gmx.de> <friedrich.vock@gmx.de>
+Natalie Vock <nat@pixelcluster.dev> <friedrich.vock@gmx.de>
+Natalie Vock <nat@pixelcluster.dev> <natalie.vock@gmx.de>
Nathan Chancellor <nathan@kernel.org> <natechancellor@gmail.com>
Naveen N Rao <naveen@kernel.org> <naveen.n.rao@linux.ibm.com>
Naveen N Rao <naveen@kernel.org> <naveen.n.rao@linux.vnet.ibm.com>
diff --git a/Documentation/devicetree/bindings/display/panel/novatek,nt36536.yaml b/Documentation/devicetree/bindings/display/panel/novatek,nt36536.yaml
new file mode 100644
index 000000000000..1e86f5329b22
--- /dev/null
+++ b/Documentation/devicetree/bindings/display/panel/novatek,nt36536.yaml
@@ -0,0 +1,91 @@
+# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/display/panel/novatek,nt36536.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Novatek NT36536 based DSI display Panels
+
+maintainers:
+ - Pengyu Luo <mitltlatltl@gmail.com>
+
+description:
+ The Novatek NT36536 is a generic DSI Panel IC used to drive dsi
+ panels. Support video mode panels from China Star Optoelectronics
+ Technology (CSOT), such as PP8807HB1-1 which is a dual-link 10-bit
+ panel.
+
+allOf:
+ - $ref: panel-common-dual.yaml#
+
+properties:
+ compatible:
+ items:
+ - enum:
+ - csot,pp8807hb1-1
+ - const: novatek,nt36536
+
+ reg:
+ maxItems: 1
+
+ reset-gpios:
+ maxItems: 1
+
+ vddio-supply:
+ description: I/O source voltage rail
+
+ vsp-supply:
+ description: Positive source voltage rail
+
+ vsn-supply:
+ description: Negative source voltage rail
+
+ backlight: true
+ ports: true
+
+required:
+ - compatible
+ - reg
+ - vddio-supply
+ - reset-gpios
+ - ports
+
+additionalProperties: false
+
+examples:
+ - |
+ #include <dt-bindings/gpio/gpio.h>
+
+ dsi {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ panel@0 {
+ compatible = "csot,pp8807hb1-1", "novatek,nt36536";
+ reg = <0>;
+
+ vddio-supply = <&vreg_iovdd_1p8>;
+ reset-gpios = <&tlmm 98 GPIO_ACTIVE_LOW>;
+
+ ports {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ port@0 {
+ reg = <0>;
+ panel_in_0: endpoint {
+ remote-endpoint = <&dsi0_out>;
+ };
+ };
+
+ port@1{
+ reg = <1>;
+ panel_in_1: endpoint {
+ remote-endpoint = <&dsi1_out>;
+ };
+ };
+ };
+ };
+ };
+
+...
diff --git a/Documentation/devicetree/bindings/display/panel/panel-lvds.yaml b/Documentation/devicetree/bindings/display/panel/panel-lvds.yaml
index b89f86bc0683..8382d4920bfd 100644
--- a/Documentation/devicetree/bindings/display/panel/panel-lvds.yaml
+++ b/Documentation/devicetree/bindings/display/panel/panel-lvds.yaml
@@ -58,6 +58,8 @@ properties:
- hydis,hv070wx2-1e0
# Jenson Display BL-JT60050-01A 7" WSVGA (1024x600) color TFT LCD LVDS panel
- jenson,bl-jt60050-01a
+ # Opto Logic SCX1001511GGC49 10.1" WXGA (1280x800) TFT LCD LVDS panel
+ - optologic,scx1001511ggc49
# Riverdi RVT101HVLNWC00 10.1" WXGA (1280x800) TFT LCD LVDS panel
- riverdi,rvt101hvlnwc00
# Riverdi RVT70HSLNWCA0 7.0" WSVGA (1024x600) TFT LCD LVDS panel
diff --git a/Documentation/devicetree/bindings/display/tegra/nvidia,tegra124-vic.yaml b/Documentation/devicetree/bindings/display/tegra/nvidia,tegra124-vic.yaml
index 7200095ef19e..bdf981781bd5 100644
--- a/Documentation/devicetree/bindings/display/tegra/nvidia,tegra124-vic.yaml
+++ b/Documentation/devicetree/bindings/display/tegra/nvidia,tegra124-vic.yaml
@@ -22,6 +22,7 @@ properties:
- nvidia,tegra186-vic
- nvidia,tegra194-vic
- nvidia,tegra234-vic
+ - nvidia,tegra264-vic
- items:
- const: nvidia,tegra132-vic
diff --git a/Documentation/devicetree/bindings/display/tegra/nvidia,tegra20-host1x.yaml b/Documentation/devicetree/bindings/display/tegra/nvidia,tegra20-host1x.yaml
index 3563378a01af..8312b7699cbe 100644
--- a/Documentation/devicetree/bindings/display/tegra/nvidia,tegra20-host1x.yaml
+++ b/Documentation/devicetree/bindings/display/tegra/nvidia,tegra20-host1x.yaml
@@ -25,6 +25,7 @@ properties:
- nvidia,tegra186-host1x
- nvidia,tegra194-host1x
- nvidia,tegra234-host1x
+ - nvidia,tegra264-host1x
- items:
- const: nvidia,tegra132-host1x
@@ -57,7 +58,8 @@ properties:
enum: [1, 2]
ranges:
- maxItems: 1
+ minItems: 1
+ maxItems: 2
clocks:
description: Must contain one entry, for the module clock. See
@@ -192,6 +194,7 @@ allOf:
contains:
enum:
- nvidia,tegra234-host1x
+ - nvidia,tegra264-host1x
then:
properties:
reg-names:
@@ -239,6 +242,21 @@ allOf:
required:
- reg-names
+ - if:
+ properties:
+ compatible:
+ contains:
+ enum:
+ - nvidia,tegra264-host1x
+ then:
+ properties:
+ ranges:
+ minItems: 2
+ maxItems: 2
+ else:
+ properties:
+ ranges:
+ maxItems: 1
examples:
- |
diff --git a/MAINTAINERS b/MAINTAINERS
index 6dea93a41962..721ecafe4b2a 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -6636,7 +6636,7 @@ F: tools/testing/selftests/cgroup/test_cpuset_v1_base.sh
CONTROL GROUP - DEVICE MEMORY CONTROLLER (DMEM)
M: Maarten Lankhorst <dev@lankhorst.se>
M: Maxime Ripard <mripard@kernel.org>
-M: Natalie Vock <natalie.vock@gmx.de>
+M: Natalie Vock <nat@pixelcluster.dev>
L: cgroups@vger.kernel.org
L: dri-devel@lists.freedesktop.org
S: Maintained
diff --git a/drivers/accel/ivpu/ivpu_fw.c b/drivers/accel/ivpu/ivpu_fw.c
index 5a8f7dfb54e6..b78b7c35cd9b 100644
--- a/drivers/accel/ivpu/ivpu_fw.c
+++ b/drivers/accel/ivpu/ivpu_fw.c
@@ -318,7 +318,7 @@ static int ivpu_fw_parse(struct ivpu_device *vdev)
fw->shave_nn_size = PAGE_ALIGN(fw_hdr->shave_nn_fw_size);
fw->cold_boot_entry_point = fw_hdr->entry_point;
- fw->trace_level = min_t(u32, ivpu_fw_log_level, IVPU_FW_LOG_FATAL);
+ fw->trace_level = min(ivpu_fw_log_level, IVPU_FW_LOG_FATAL);
fw->trace_destination_mask = VPU_TRACE_DESTINATION_VERBOSE_TRACING;
fw->trace_hw_component_mask = -1;
diff --git a/drivers/accel/ivpu/ivpu_mmu_context.c b/drivers/accel/ivpu/ivpu_mmu_context.c
index c4014c83e727..bb3ca81838e3 100644
--- a/drivers/accel/ivpu/ivpu_mmu_context.c
+++ b/drivers/accel/ivpu/ivpu_mmu_context.c
@@ -588,8 +588,8 @@ void ivpu_mmu_context_init(struct ivpu_device *vdev, struct ivpu_mmu_context *ct
start = vdev->hw->ranges.runtime.start;
end = vdev->hw->ranges.shave.end;
} else {
- start = min_t(u64, vdev->hw->ranges.user.start, vdev->hw->ranges.shave.start);
- end = max_t(u64, vdev->hw->ranges.user.end, vdev->hw->ranges.dma.end);
+ start = min(vdev->hw->ranges.user.start, vdev->hw->ranges.shave.start);
+ end = max(vdev->hw->ranges.user.end, vdev->hw->ranges.dma.end);
}
drm_mm_init(&ctx->mm, start, end - start);
diff --git a/drivers/gpu/drm/bridge/analogix/analogix-anx6345.c b/drivers/gpu/drm/bridge/analogix/analogix-anx6345.c
index eba5c6dcb5ad..d152c02419f5 100644
--- a/drivers/gpu/drm/bridge/analogix/analogix-anx6345.c
+++ b/drivers/gpu/drm/bridge/analogix/analogix-anx6345.c
@@ -663,6 +663,11 @@ static bool anx6345_get_chip_id(struct anx6345 *anx6345)
return false;
}
+static void anx6345_panel_put_action(void *data)
+{
+ drm_panel_put(data);
+}
+
static int anx6345_i2c_probe(struct i2c_client *client)
{
struct anx6345 *anx6345;
@@ -691,6 +696,13 @@ static int anx6345_i2c_probe(struct i2c_client *client)
if (err)
DRM_DEBUG("No panel found\n");
+ if (anx6345->panel) {
+ err = devm_add_action_or_reset(dev, anx6345_panel_put_action,
+ anx6345->panel);
+ if (err)
+ return err;
+ }
+
/* 1.2V digital core power regulator */
anx6345->dvdd12 = devm_regulator_get(dev, "dvdd12");
if (IS_ERR(anx6345->dvdd12)) {
diff --git a/drivers/gpu/drm/bridge/fsl-ldb.c b/drivers/gpu/drm/bridge/fsl-ldb.c
index cc1f88e7873e..26cc72948f31 100644
--- a/drivers/gpu/drm/bridge/fsl-ldb.c
+++ b/drivers/gpu/drm/bridge/fsl-ldb.c
@@ -348,6 +348,7 @@ static int fsl_ldb_probe(struct platform_device *pdev)
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);
diff --git a/drivers/gpu/drm/bridge/lontium-lt9211.c b/drivers/gpu/drm/bridge/lontium-lt9211.c
index f39d83a5ae37..6d97ae5640c4 100644
--- a/drivers/gpu/drm/bridge/lontium-lt9211.c
+++ b/drivers/gpu/drm/bridge/lontium-lt9211.c
@@ -660,6 +660,7 @@ static int lt9211_parse_dt(struct lt9211 *ctx)
return ret;
if (panel) {
panel_bridge = devm_drm_panel_bridge_add(dev, panel);
+ drm_panel_put(panel);
if (IS_ERR(panel_bridge))
return PTR_ERR(panel_bridge);
}
diff --git a/drivers/gpu/drm/bridge/lvds-codec.c b/drivers/gpu/drm/bridge/lvds-codec.c
index a82ea0c944eb..f97c8586273d 100644
--- a/drivers/gpu/drm/bridge/lvds-codec.c
+++ b/drivers/gpu/drm/bridge/lvds-codec.c
@@ -156,6 +156,7 @@ static int lvds_codec_probe(struct platform_device *pdev)
lvds_codec->panel_bridge =
devm_drm_panel_bridge_add_typed(dev, panel,
lvds_codec->connector_type);
+ drm_panel_put(panel);
if (IS_ERR(lvds_codec->panel_bridge))
return PTR_ERR(lvds_codec->panel_bridge);
diff --git a/drivers/gpu/drm/bridge/panel.c b/drivers/gpu/drm/bridge/panel.c
index 62af499f1f5c..02388a3de626 100644
--- a/drivers/gpu/drm/bridge/panel.c
+++ b/drivers/gpu/drm/bridge/panel.c
@@ -294,7 +294,7 @@ struct drm_bridge *drm_panel_bridge_add_typed(struct drm_panel *panel,
return (void *)panel_bridge;
panel_bridge->connector_type = connector_type;
- panel_bridge->panel = panel;
+ panel_bridge->panel = drm_panel_get(panel);
panel_bridge->bridge.of_node = panel->dev->of_node;
panel_bridge->bridge.ops = DRM_BRIDGE_OP_MODES;
@@ -316,6 +316,7 @@ EXPORT_SYMBOL(drm_panel_bridge_add_typed);
void drm_panel_bridge_remove(struct drm_bridge *bridge)
{
struct panel_bridge *panel_bridge;
+ struct drm_panel *panel;
if (!bridge)
return;
@@ -326,10 +327,12 @@ void drm_panel_bridge_remove(struct drm_bridge *bridge)
}
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_bridge->panel->dev, bridge);
+ devm_drm_put_bridge(panel->dev, bridge);
+ drm_panel_put(panel);
}
EXPORT_SYMBOL(drm_panel_bridge_remove);
@@ -357,11 +360,16 @@ 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);
}
/**
@@ -507,8 +515,10 @@ struct drm_bridge *devm_drm_of_get_bridge(struct device *dev,
if (ret)
return ERR_PTR(ret);
- if (panel)
+ if (panel) {
bridge = devm_drm_panel_bridge_add(dev, panel);
+ drm_panel_put(panel);
+ }
return bridge;
}
@@ -541,8 +551,10 @@ struct drm_bridge *drmm_of_get_bridge(struct drm_device *drm,
if (ret)
return ERR_PTR(ret);
- if (panel)
+ if (panel) {
bridge = drmm_panel_bridge_add(drm, panel);
+ drm_panel_put(panel);
+ }
return bridge;
}
diff --git a/drivers/gpu/drm/bridge/samsung-dsim.c b/drivers/gpu/drm/bridge/samsung-dsim.c
index 25ab475309e8..e2fc69fc51b6 100644
--- a/drivers/gpu/drm/bridge/samsung-dsim.c
+++ b/drivers/gpu/drm/bridge/samsung-dsim.c
@@ -1934,6 +1934,7 @@ of_find_panel_or_bridge:
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
diff --git a/drivers/gpu/drm/bridge/ssd2825.c b/drivers/gpu/drm/bridge/ssd2825.c
index 00a4ed4a0700..b0142d6d40dc 100644
--- a/drivers/gpu/drm/bridge/ssd2825.c
+++ b/drivers/gpu/drm/bridge/ssd2825.c
@@ -301,6 +301,7 @@ static int ssd2825_dsi_host_attach(struct mipi_dsi_host *host, struct mipi_dsi_d
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);
}
diff --git a/drivers/gpu/drm/bridge/tc358767.c b/drivers/gpu/drm/bridge/tc358767.c
index ce1b73214f26..8f1648f799a7 100644
--- a/drivers/gpu/drm/bridge/tc358767.c
+++ b/drivers/gpu/drm/bridge/tc358767.c
@@ -2329,6 +2329,7 @@ static int tc_probe_dpi_bridge_endpoint(struct tc_data *tc)
if (panel) {
bridge = devm_drm_panel_bridge_add(dev, panel);
+ drm_panel_put(panel);
if (IS_ERR(bridge))
return PTR_ERR(bridge);
}
@@ -2359,6 +2360,7 @@ static int tc_probe_edp_bridge_endpoint(struct tc_data *tc)
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);
diff --git a/drivers/gpu/drm/bridge/tc358768.c b/drivers/gpu/drm/bridge/tc358768.c
index 0d85120fcc7a..112a421f99b5 100644
--- a/drivers/gpu/drm/bridge/tc358768.c
+++ b/drivers/gpu/drm/bridge/tc358768.c
@@ -479,6 +479,7 @@ static int tc358768_dsi_host_attach(struct mipi_dsi_host *host,
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);
diff --git a/drivers/gpu/drm/bridge/waveshare-dsi.c b/drivers/gpu/drm/bridge/waveshare-dsi.c
index 9c9825c9b7c5..420f3b870a74 100644
--- a/drivers/gpu/drm/bridge/waveshare-dsi.c
+++ b/drivers/gpu/drm/bridge/waveshare-dsi.c
@@ -169,6 +169,7 @@ static int ws_bridge_probe(struct i2c_client *i2c)
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);
diff --git a/drivers/gpu/drm/drm_of.c b/drivers/gpu/drm/drm_of.c
index 1f4cff6bb4a7..e495117735e1 100644
--- a/drivers/gpu/drm/drm_of.c
+++ b/drivers/gpu/drm/drm_of.c
@@ -269,7 +269,8 @@ EXPORT_SYMBOL_GPL(drm_of_get_panel_orientation);
* @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
+ * @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
diff --git a/drivers/gpu/drm/drm_panel.c b/drivers/gpu/drm/drm_panel.c
index d7c6f4824b2d..f8f6082e637f 100644
--- a/drivers/gpu/drm/drm_panel.c
+++ b/drivers/gpu/drm/drm_panel.c
@@ -82,6 +82,7 @@ static void drm_panel_init(struct drm_panel *panel, struct device *dev,
*/
void drm_panel_add(struct drm_panel *panel)
{
+ drm_panel_get(panel);
mutex_lock(&panel_lock);
list_add_tail(&panel->list, &panel_list);
mutex_unlock(&panel_lock);
@@ -99,6 +100,7 @@ void drm_panel_remove(struct drm_panel *panel)
mutex_lock(&panel_lock);
list_del_init(&panel->list);
mutex_unlock(&panel_lock);
+ drm_panel_put(panel);
}
EXPORT_SYMBOL(drm_panel_remove);
@@ -457,14 +459,17 @@ EXPORT_SYMBOL(__devm_drm_panel_alloc);
#ifdef CONFIG_OF
/**
- * of_drm_find_panel - look up a panel using a device tree node
+ * of_drm_find_panel - look up and reference a panel by device tree node
* @np: device tree node of the panel
*
* Searches the set of registered panels for one that matches the given device
- * tree node. If a matching panel is found, return a pointer to it.
+ * tree node. If a matching panel is found, the panel's reference count is
+ * incremented before returning a pointer to it. The caller must call
+ * drm_panel_put() when it no longer needs the panel pointer.
*
- * Return: A pointer to the panel registered for the specified device tree
- * node or an ERR_PTR() if no panel matching the device tree node can be found.
+ * Return: A reference-counted pointer to the panel registered for the specified
+ * device tree node or an ERR_PTR() if no panel matching the device tree node
+ * can be found.
*
* Possible error codes returned by this function:
*
@@ -483,6 +488,7 @@ struct drm_panel *of_drm_find_panel(const struct device_node *np)
list_for_each_entry(panel, &panel_list, list) {
if (panel->dev->of_node == np) {
+ drm_panel_get(panel);
mutex_unlock(&panel_lock);
return panel;
}
@@ -494,7 +500,13 @@ struct drm_panel *of_drm_find_panel(const struct device_node *np)
EXPORT_SYMBOL(of_drm_find_panel);
#endif
-/* Find panel by fwnode. This should be identical to of_drm_find_panel(). */
+/*
+ * Find panel by fwnode, returning a counted reference.
+ *
+ * Behaves identically to of_drm_find_panel(). On success the returned
+ * pointer has been passed through drm_panel_get(); the caller must call
+ * drm_panel_put() when done with it.
+ */
static struct drm_panel *find_panel_by_fwnode(const struct fwnode_handle *fwnode)
{
struct drm_panel *panel;
@@ -506,6 +518,7 @@ static struct drm_panel *find_panel_by_fwnode(const struct fwnode_handle *fwnode
list_for_each_entry(panel, &panel_list, list) {
if (dev_fwnode(panel->dev) == fwnode) {
+ drm_panel_get(panel);
mutex_unlock(&panel_lock);
return panel;
}
@@ -642,6 +655,7 @@ void drm_panel_remove_follower(struct drm_panel_follower *follower)
mutex_unlock(&panel->follower_lock);
put_device(panel->dev);
+ drm_panel_put(panel);
}
EXPORT_SYMBOL(drm_panel_remove_follower);
diff --git a/drivers/gpu/drm/drm_panel.c.orig b/drivers/gpu/drm/drm_panel.c.orig
new file mode 100644
index 000000000000..32fba1a562fb
--- /dev/null
+++ b/drivers/gpu/drm/drm_panel.c.orig
@@ -0,0 +1,722 @@
+/*
+ * 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.
+ */
+
+#include <linux/backlight.h>
+#include <linux/err.h>
+#include <linux/export.h>
+#include <linux/module.h>
+#include <linux/of.h>
+
+#include <drm/drm_crtc.h>
+#include <drm/drm_of.h>
+#include <drm/drm_panel.h>
+#include <drm/drm_print.h>
+
+static DEFINE_MUTEX(panel_lock);
+static LIST_HEAD(panel_list);
+
+/**
+ * DOC: drm panel
+ *
+ * The DRM panel helpers allow drivers to register panel objects with a
+ * central registry and provide functions to retrieve those panels in display
+ * drivers.
+ *
+ * For easy integration into drivers using the &drm_bridge infrastructure please
+ * take look at drm_panel_bridge_add() and devm_drm_panel_bridge_add().
+ */
+
+/**
+ * drm_panel_init - initialize a panel
+ * @panel: DRM panel
+ * @dev: parent device of the panel
+ * @funcs: panel operations
+ * @connector_type: the connector type (DRM_MODE_CONNECTOR_*) corresponding to
+ * the panel interface (must NOT be DRM_MODE_CONNECTOR_Unknown)
+ *
+ * Initialize the panel structure for subsequent registration with
+ * drm_panel_add().
+ */
+static void drm_panel_init(struct drm_panel *panel, struct device *dev,
+ const struct drm_panel_funcs *funcs,
+ int connector_type)
+{
+ if (connector_type == DRM_MODE_CONNECTOR_Unknown)
+ DRM_WARN("%s: %s: a valid connector type is required!\n", __func__, dev_name(dev));
+
+ INIT_LIST_HEAD(&panel->list);
+ INIT_LIST_HEAD(&panel->followers);
+ mutex_init(&panel->follower_lock);
+ panel->dev = dev;
+ panel->funcs = funcs;
+ panel->connector_type = connector_type;
+}
+
+/**
+ * drm_panel_add - add a panel to the global registry
+ * @panel: panel to add
+ *
+ * Add a panel to the global registry so that it can be looked
+ * up by display drivers. The panel to be added must have been
+ * allocated by devm_drm_panel_alloc().
+ */
+void drm_panel_add(struct drm_panel *panel)
+{
+ drm_panel_get(panel);
+ mutex_lock(&panel_lock);
+ list_add_tail(&panel->list, &panel_list);
+ mutex_unlock(&panel_lock);
+}
+EXPORT_SYMBOL(drm_panel_add);
+
+/**
+ * drm_panel_remove - remove a panel from the global registry
+ * @panel: DRM panel
+ *
+ * Removes a panel from the global registry.
+ */
+void drm_panel_remove(struct drm_panel *panel)
+{
+ mutex_lock(&panel_lock);
+ list_del_init(&panel->list);
+ mutex_unlock(&panel_lock);
+ drm_panel_put(panel);
+}
+EXPORT_SYMBOL(drm_panel_remove);
+
+static void drm_panel_add_release(void *data)
+{
+ drm_panel_remove(data);
+}
+
+/**
+ * devm_drm_panel_add - add a panel to the global registry using devres
+ * @dev: device to which the panel is attached
+ * @panel: panel to add
+ *
+ * Add a panel to the global registry so that it can be looked
+ * up by display drivers. The panel to be added must have been
+ * allocated by devm_drm_panel_alloc(). Unlike drm_panel_add() with this
+ * function there is no need to call drm_panel_remove(), it will be called
+ * automatically.
+ */
+int devm_drm_panel_add(struct device *dev, struct drm_panel *panel)
+{
+ drm_panel_add(panel);
+
+ return devm_add_action_or_reset(dev, drm_panel_add_release, panel);
+}
+EXPORT_SYMBOL(devm_drm_panel_add);
+
+/**
+ * drm_panel_prepare - power on a panel
+ * @panel: DRM panel
+ *
+ * Calling this function will enable power and deassert any reset signals to
+ * the panel. After this has completed it is possible to communicate with any
+ * integrated circuitry via a command bus. This function cannot fail (as it is
+ * called from the pre_enable call chain). There will always be a call to
+ * drm_panel_disable() afterwards.
+ */
+void drm_panel_prepare(struct drm_panel *panel)
+{
+ struct drm_panel_follower *follower;
+ int ret;
+
+ if (!panel)
+ return;
+
+ if (panel->prepared) {
+ dev_warn(panel->dev, "Skipping prepare of already prepared panel\n");
+ return;
+ }
+
+ mutex_lock(&panel->follower_lock);
+
+ if (panel->funcs && panel->funcs->prepare) {
+ ret = panel->funcs->prepare(panel);
+ if (ret < 0)
+ goto exit;
+ }
+ panel->prepared = true;
+
+ list_for_each_entry(follower, &panel->followers, list) {
+ if (!follower->funcs->panel_prepared)
+ continue;
+
+ ret = follower->funcs->panel_prepared(follower);
+ if (ret < 0)
+ dev_info(panel->dev, "%ps failed: %d\n",
+ follower->funcs->panel_prepared, ret);
+ }
+
+exit:
+ mutex_unlock(&panel->follower_lock);
+}
+EXPORT_SYMBOL(drm_panel_prepare);
+
+/**
+ * drm_panel_unprepare - power off a panel
+ * @panel: DRM panel
+ *
+ * Calling this function will completely power off a panel (assert the panel's
+ * reset, turn off power supplies, ...). After this function has completed, it
+ * is usually no longer possible to communicate with the panel until another
+ * call to drm_panel_prepare().
+ */
+void drm_panel_unprepare(struct drm_panel *panel)
+{
+ struct drm_panel_follower *follower;
+ int ret;
+
+ if (!panel)
+ return;
+
+ /*
+ * If you are seeing the warning below it likely means one of two things:
+ * - Your panel driver incorrectly calls drm_panel_unprepare() in its
+ * shutdown routine. You should delete this.
+ * - You are using panel-edp or panel-simple and your DRM modeset
+ * driver's shutdown() callback happened after the panel's shutdown().
+ * In this case the warning is harmless though ideally you should
+ * figure out how to reverse the order of the shutdown() callbacks.
+ */
+ if (!panel->prepared) {
+ dev_warn(panel->dev, "Skipping unprepare of already unprepared panel\n");
+ return;
+ }
+
+ mutex_lock(&panel->follower_lock);
+
+ list_for_each_entry(follower, &panel->followers, list) {
+ if (!follower->funcs->panel_unpreparing)
+ continue;
+
+ ret = follower->funcs->panel_unpreparing(follower);
+ if (ret < 0)
+ dev_info(panel->dev, "%ps failed: %d\n",
+ follower->funcs->panel_unpreparing, ret);
+ }
+
+ if (panel->funcs && panel->funcs->unprepare) {
+ ret = panel->funcs->unprepare(panel);
+ if (ret < 0)
+ goto exit;
+ }
+ panel->prepared = false;
+
+exit:
+ mutex_unlock(&panel->follower_lock);
+}
+EXPORT_SYMBOL(drm_panel_unprepare);
+
+/**
+ * drm_panel_enable - enable a panel
+ * @panel: DRM panel
+ *
+ * Calling this function will cause the panel display drivers to be turned on
+ * and the backlight to be enabled. Content will be visible on screen after
+ * this call completes. This function cannot fail (as it is called from the
+ * enable call chain). There will always be a call to drm_panel_disable()
+ * afterwards.
+ */
+void drm_panel_enable(struct drm_panel *panel)
+{
+ struct drm_panel_follower *follower;
+ int ret;
+
+ if (!panel)
+ return;
+
+ if (panel->enabled) {
+ dev_warn(panel->dev, "Skipping enable of already enabled panel\n");
+ return;
+ }
+
+ mutex_lock(&panel->follower_lock);
+
+ if (panel->funcs && panel->funcs->enable) {
+ ret = panel->funcs->enable(panel);
+ if (ret < 0)
+ goto exit;
+ }
+ panel->enabled = true;
+
+ ret = backlight_enable(panel->backlight);
+ if (ret < 0)
+ DRM_DEV_INFO(panel->dev, "failed to enable backlight: %d\n",
+ ret);
+
+ list_for_each_entry(follower, &panel->followers, list) {
+ if (!follower->funcs->panel_enabled)
+ continue;
+
+ ret = follower->funcs->panel_enabled(follower);
+ if (ret < 0)
+ dev_info(panel->dev, "%ps failed: %d\n",
+ follower->funcs->panel_enabled, ret);
+ }
+
+exit:
+ mutex_unlock(&panel->follower_lock);
+}
+EXPORT_SYMBOL(drm_panel_enable);
+
+/**
+ * drm_panel_disable - disable a panel
+ * @panel: DRM panel
+ *
+ * This will typically turn off the panel's backlight or disable the display
+ * drivers. For smart panels it should still be possible to communicate with
+ * the integrated circuitry via any command bus after this call.
+ */
+void drm_panel_disable(struct drm_panel *panel)
+{
+ struct drm_panel_follower *follower;
+ int ret;
+
+ if (!panel)
+ return;
+
+ /*
+ * If you are seeing the warning below it likely means one of two things:
+ * - Your panel driver incorrectly calls drm_panel_disable() in its
+ * shutdown routine. You should delete this.
+ * - You are using panel-edp or panel-simple and your DRM modeset
+ * driver's shutdown() callback happened after the panel's shutdown().
+ * In this case the warning is harmless though ideally you should
+ * figure out how to reverse the order of the shutdown() callbacks.
+ */
+ if (!panel->enabled) {
+ dev_warn(panel->dev, "Skipping disable of already disabled panel\n");
+ return;
+ }
+
+ mutex_lock(&panel->follower_lock);
+
+ list_for_each_entry(follower, &panel->followers, list) {
+ if (!follower->funcs->panel_disabling)
+ continue;
+
+ ret = follower->funcs->panel_disabling(follower);
+ if (ret < 0)
+ dev_info(panel->dev, "%ps failed: %d\n",
+ follower->funcs->panel_disabling, ret);
+ }
+
+ ret = backlight_disable(panel->backlight);
+ if (ret < 0)
+ DRM_DEV_INFO(panel->dev, "failed to disable backlight: %d\n",
+ ret);
+
+ if (panel->funcs && panel->funcs->disable) {
+ ret = panel->funcs->disable(panel);
+ if (ret < 0)
+ goto exit;
+ }
+ panel->enabled = false;
+
+exit:
+ mutex_unlock(&panel->follower_lock);
+}
+EXPORT_SYMBOL(drm_panel_disable);
+
+/**
+ * drm_panel_get_modes - probe the available display modes of a panel
+ * @panel: DRM panel
+ * @connector: DRM connector
+ *
+ * The modes probed from the panel are automatically added to the connector
+ * that the panel is attached to.
+ *
+ * Return: The number of modes available from the panel on success, or 0 on
+ * failure (no modes).
+ */
+int drm_panel_get_modes(struct drm_panel *panel,
+ struct drm_connector *connector)
+{
+ if (!panel)
+ return 0;
+
+ if (panel->funcs && panel->funcs->get_modes) {
+ int num;
+
+ num = panel->funcs->get_modes(panel, connector);
+ if (num > 0)
+ return num;
+ }
+
+ return 0;
+}
+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
+ *
+ * This function increments the panel's refcount.
+ * Returns:
+ * Pointer to @panel
+ */
+struct drm_panel *drm_panel_get(struct drm_panel *panel)
+{
+ if (!panel)
+ return panel;
+
+ kref_get(&panel->refcount);
+
+ return panel;
+}
+EXPORT_SYMBOL(drm_panel_get);
+
+/**
+ * drm_panel_put - Release a panel reference
+ * @panel: DRM panel
+ *
+ * This function decrements the panel's reference count and frees the
+ * object if the reference count drops to zero.
+ */
+void drm_panel_put(struct drm_panel *panel)
+{
+ if (panel)
+ kref_put(&panel->refcount, __drm_panel_free);
+}
+EXPORT_SYMBOL(drm_panel_put);
+
+/**
+ * drm_panel_put_void - wrapper to drm_panel_put() taking a void pointer
+ *
+ * @data: pointer to @struct drm_panel, cast to a void pointer
+ *
+ * Wrapper of drm_panel_put() to be used when a function taking a void
+ * pointer is needed, for example as a devm action.
+ */
+static void drm_panel_put_void(void *data)
+{
+ struct drm_panel *panel = (struct drm_panel *)data;
+
+ drm_panel_put(panel);
+}
+
+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 drm_panel *panel;
+ int err;
+
+ if (!funcs) {
+ dev_warn(dev, "Missing funcs pointer\n");
+ return ERR_PTR(-EINVAL);
+ }
+
+ container = kzalloc(size, GFP_KERNEL);
+ if (!container)
+ return ERR_PTR(-ENOMEM);
+
+ panel = container + offset;
+ panel->container = container;
+ panel->funcs = funcs;
+ kref_init(&panel->refcount);
+
+ err = devm_add_action_or_reset(dev, drm_panel_put_void, panel);
+ if (err)
+ return ERR_PTR(err);
+
+ drm_panel_init(panel, dev, funcs, connector_type);
+
+ return container;
+}
+EXPORT_SYMBOL(__devm_drm_panel_alloc);
+
+#ifdef CONFIG_OF
+/**
+ * of_drm_find_panel - look up and reference a panel by device tree node
+ * @np: device tree node of the panel
+ *
+ * Searches the set of registered panels for one that matches the given device
+ * tree node. If a matching panel is found, the panel's reference count is
+ * incremented before returning a pointer to it. The caller must call
+ * drm_panel_put() when it no longer needs the panel pointer.
+ *
+ * Return: A reference-counted pointer to the panel registered for the specified
+ * device tree node or an ERR_PTR() if no panel matching the device tree node
+ * can be found.
+ *
+ * Possible error codes returned by this function:
+ *
+ * - EPROBE_DEFER: the panel device has not been probed yet, and the caller
+ * should retry later
+ * - ENODEV: the device is not available (status != "okay" or "ok")
+ */
+struct drm_panel *of_drm_find_panel(const struct device_node *np)
+{
+ struct drm_panel *panel;
+
+ if (!of_device_is_available(np))
+ return ERR_PTR(-ENODEV);
+
+ mutex_lock(&panel_lock);
+
+ list_for_each_entry(panel, &panel_list, list) {
+ if (panel->dev->of_node == np) {
+ drm_panel_get(panel);
+ mutex_unlock(&panel_lock);
+ return panel;
+ }
+ }
+
+ mutex_unlock(&panel_lock);
+ return ERR_PTR(-EPROBE_DEFER);
+}
+EXPORT_SYMBOL(of_drm_find_panel);
+#endif
+
+/* Find panel by fwnode. This should be identical to of_drm_find_panel(). */
+static struct drm_panel *find_panel_by_fwnode(const struct fwnode_handle *fwnode)
+{
+ struct drm_panel *panel;
+
+ if (!fwnode_device_is_available(fwnode))
+ return ERR_PTR(-ENODEV);
+
+ mutex_lock(&panel_lock);
+
+ list_for_each_entry(panel, &panel_list, list) {
+ if (dev_fwnode(panel->dev) == fwnode) {
+ mutex_unlock(&panel_lock);
+ return panel;
+ }
+ }
+
+ mutex_unlock(&panel_lock);
+
+ return ERR_PTR(-EPROBE_DEFER);
+}
+
+/* Find panel by follower device */
+static struct drm_panel *find_panel_by_dev(struct device *follower_dev)
+{
+ struct fwnode_handle *fwnode;
+ struct drm_panel *panel;
+
+ fwnode = fwnode_find_reference(dev_fwnode(follower_dev), "panel", 0);
+ if (IS_ERR(fwnode))
+ return ERR_PTR(-ENODEV);
+
+ panel = find_panel_by_fwnode(fwnode);
+ fwnode_handle_put(fwnode);
+
+ return panel;
+}
+
+/**
+ * drm_is_panel_follower() - Check if the device is a panel follower
+ * @dev: The 'struct device' to check
+ *
+ * This checks to see if a device needs to be power sequenced together with
+ * a panel using the panel follower API.
+ *
+ * The "panel" property of the follower points to the panel to be followed.
+ *
+ * Return: true if we should be power sequenced with a panel; false otherwise.
+ */
+bool drm_is_panel_follower(struct device *dev)
+{
+ /*
+ * The "panel" property is actually a phandle, but for simplicity we
+ * don't bother trying to parse it here. We just need to know if the
+ * property is there.
+ */
+ return device_property_present(dev, "panel");
+}
+EXPORT_SYMBOL(drm_is_panel_follower);
+
+/**
+ * drm_panel_add_follower() - Register something to follow panel state.
+ * @follower_dev: The 'struct device' for the follower.
+ * @follower: The panel follower descriptor for the follower.
+ *
+ * A panel follower is called right after preparing/enabling the panel and right
+ * before unpreparing/disabling the panel. It's primary intention is to power on
+ * an associated touchscreen, though it could be used for any similar devices.
+ * Multiple devices are allowed the follow the same panel.
+ *
+ * If a follower is added to a panel that's already been prepared/enabled, the
+ * follower's prepared/enabled callback is called right away.
+ *
+ * The "panel" property of the follower points to the panel to be followed.
+ *
+ * Return: 0 or an error code. Note that -ENODEV means that we detected that
+ * follower_dev is not actually following a panel. The caller may
+ * choose to ignore this return value if following a panel is optional.
+ */
+int drm_panel_add_follower(struct device *follower_dev,
+ struct drm_panel_follower *follower)
+{
+ struct drm_panel *panel;
+ int ret;
+
+ panel = find_panel_by_dev(follower_dev);
+ if (IS_ERR(panel))
+ return PTR_ERR(panel);
+
+ get_device(panel->dev);
+ follower->panel = panel;
+
+ mutex_lock(&panel->follower_lock);
+
+ list_add_tail(&follower->list, &panel->followers);
+ if (panel->prepared && follower->funcs->panel_prepared) {
+ ret = follower->funcs->panel_prepared(follower);
+ if (ret < 0)
+ dev_info(panel->dev, "%ps failed: %d\n",
+ follower->funcs->panel_prepared, ret);
+ }
+ if (panel->enabled && follower->funcs->panel_enabled) {
+ ret = follower->funcs->panel_enabled(follower);
+ if (ret < 0)
+ dev_info(panel->dev, "%ps failed: %d\n",
+ follower->funcs->panel_enabled, ret);
+ }
+
+ mutex_unlock(&panel->follower_lock);
+
+ return 0;
+}
+EXPORT_SYMBOL(drm_panel_add_follower);
+
+/**
+ * drm_panel_remove_follower() - Reverse drm_panel_add_follower().
+ * @follower: The panel follower descriptor for the follower.
+ *
+ * Undo drm_panel_add_follower(). This includes calling the follower's
+ * unpreparing/disabling function if we're removed from a panel that's currently
+ * prepared/enabled.
+ *
+ * Return: 0 or an error code.
+ */
+void drm_panel_remove_follower(struct drm_panel_follower *follower)
+{
+ struct drm_panel *panel = follower->panel;
+ int ret;
+
+ mutex_lock(&panel->follower_lock);
+
+ if (panel->enabled && follower->funcs->panel_disabling) {
+ ret = follower->funcs->panel_disabling(follower);
+ if (ret < 0)
+ dev_info(panel->dev, "%ps failed: %d\n",
+ follower->funcs->panel_disabling, ret);
+ }
+ if (panel->prepared && follower->funcs->panel_unpreparing) {
+ ret = follower->funcs->panel_unpreparing(follower);
+ if (ret < 0)
+ dev_info(panel->dev, "%ps failed: %d\n",
+ follower->funcs->panel_unpreparing, ret);
+ }
+ list_del_init(&follower->list);
+
+ mutex_unlock(&panel->follower_lock);
+
+ put_device(panel->dev);
+}
+EXPORT_SYMBOL(drm_panel_remove_follower);
+
+static void drm_panel_remove_follower_void(void *follower)
+{
+ drm_panel_remove_follower(follower);
+}
+
+/**
+ * devm_drm_panel_add_follower() - devm version of drm_panel_add_follower()
+ * @follower_dev: The 'struct device' for the follower.
+ * @follower: The panel follower descriptor for the follower.
+ *
+ * Handles calling drm_panel_remove_follower() using devm on the follower_dev.
+ *
+ * Return: 0 or an error code.
+ */
+int devm_drm_panel_add_follower(struct device *follower_dev,
+ struct drm_panel_follower *follower)
+{
+ int ret;
+
+ ret = drm_panel_add_follower(follower_dev, follower);
+ if (ret)
+ return ret;
+
+ return devm_add_action_or_reset(follower_dev,
+ drm_panel_remove_follower_void, follower);
+}
+EXPORT_SYMBOL(devm_drm_panel_add_follower);
+
+#if IS_REACHABLE(CONFIG_BACKLIGHT_CLASS_DEVICE)
+/**
+ * drm_panel_of_backlight - use backlight device node for backlight
+ * @panel: DRM panel
+ *
+ * Use this function to enable backlight handling if your panel
+ * uses device tree and has a backlight phandle.
+ *
+ * When the panel is enabled backlight will be enabled after a
+ * successful call to &drm_panel_funcs.enable()
+ *
+ * When the panel is disabled backlight will be disabled before the
+ * call to &drm_panel_funcs.disable().
+ *
+ * A typical implementation for a panel driver supporting device tree
+ * will call this function at probe time. Backlight will then be handled
+ * transparently without requiring any intervention from the driver.
+ *
+ * Return: 0 on success or a negative error code on failure.
+ */
+int drm_panel_of_backlight(struct drm_panel *panel)
+{
+ struct backlight_device *backlight;
+
+ if (!panel || !panel->dev)
+ return -EINVAL;
+
+ backlight = devm_of_find_backlight(panel->dev);
+
+ if (IS_ERR(backlight))
+ return PTR_ERR(backlight);
+
+ panel->backlight = backlight;
+ return 0;
+}
+EXPORT_SYMBOL(drm_panel_of_backlight);
+#endif
+
+MODULE_AUTHOR("Thierry Reding <treding@nvidia.com>");
+MODULE_DESCRIPTION("DRM panel infrastructure");
+MODULE_LICENSE("GPL and additional rights");
diff --git a/drivers/gpu/drm/drm_probe_helper.c b/drivers/gpu/drm/drm_probe_helper.c
index d4dc8cb45bce..e26ead5b5e58 100644
--- a/drivers/gpu/drm/drm_probe_helper.c
+++ b/drivers/gpu/drm/drm_probe_helper.c
@@ -760,27 +760,32 @@ static void output_poll_execute(struct work_struct *work)
struct drm_connector *connector;
struct drm_connector_list_iter conn_iter;
enum drm_connector_status old_status;
- bool repoll = false, changed;
+ bool repoll = false, changed = false;
u64 old_epoch_counter;
if (!dev->mode_config.poll_enabled)
return;
- /* Pick up any changes detected by the probe functions. */
- changed = dev->mode_config.delayed_event;
- dev->mode_config.delayed_event = false;
-
if (!drm_kms_helper_poll) {
if (dev->mode_config.poll_running) {
drm_kms_helper_disable_hpd(dev);
dev->mode_config.poll_running = false;
}
- goto out;
+
+ scoped_guard(mutex, &dev->mode_config.mutex) {
+ changed = dev->mode_config.delayed_event;
+ dev->mode_config.delayed_event = false;
+ }
+
+ if (changed)
+ drm_kms_helper_hotplug_event(dev);
+
+ return;
}
if (!mutex_trylock(&dev->mode_config.mutex)) {
- repoll = true;
- goto out;
+ schedule_delayed_work(delayed_work, DRM_OUTPUT_POLL_PERIOD);
+ return;
}
drm_connector_list_iter_begin(dev, &conn_iter);
@@ -836,16 +841,23 @@ static void output_poll_execute(struct work_struct *work)
connector->base.id, connector->name,
old_epoch_counter, connector->epoch_counter);
+ drm_sysfs_connector_hotplug_event(connector);
changed = true;
}
}
drm_connector_list_iter_end(&conn_iter);
+ /* Pick up any changes detected by the probe functions. */
+ if (dev->mode_config.delayed_event) {
+ dev->mode_config.delayed_event = false;
+ changed = true;
+ drm_sysfs_hotplug_event(dev);
+ }
+
mutex_unlock(&dev->mode_config.mutex);
-out:
if (changed)
- drm_kms_helper_hotplug_event(dev);
+ drm_client_dev_hotplug(dev);
if (repoll)
schedule_delayed_work(delayed_work, DRM_OUTPUT_POLL_PERIOD);
@@ -1081,9 +1093,9 @@ EXPORT_SYMBOL(drm_connector_helper_hpd_irq_event);
*/
bool drm_helper_hpd_irq_event(struct drm_device *dev)
{
- struct drm_connector *connector, *first_changed_connector = NULL;
struct drm_connector_list_iter conn_iter;
- int changed = 0;
+ struct drm_connector *connector;
+ bool changed = false;
if (!dev->mode_config.poll_enabled)
return false;
@@ -1096,24 +1108,15 @@ bool drm_helper_hpd_irq_event(struct drm_device *dev)
continue;
if (check_connector_changed(connector)) {
- if (!first_changed_connector) {
- drm_connector_get(connector);
- first_changed_connector = connector;
- }
-
- changed++;
+ changed = true;
+ drm_sysfs_connector_hotplug_event(connector);
}
}
drm_connector_list_iter_end(&conn_iter);
mutex_unlock(&dev->mode_config.mutex);
- if (changed == 1)
- drm_kms_helper_connector_hotplug_event(first_changed_connector);
- else if (changed > 0)
- drm_kms_helper_hotplug_event(dev);
-
- if (first_changed_connector)
- drm_connector_put(first_changed_connector);
+ if (changed)
+ drm_client_dev_hotplug(dev);
return changed;
}
diff --git a/drivers/gpu/drm/etnaviv/etnaviv_gpu.c b/drivers/gpu/drm/etnaviv/etnaviv_gpu.c
index 4dbc8a7ea6af..5c23182f4d33 100644
--- a/drivers/gpu/drm/etnaviv/etnaviv_gpu.c
+++ b/drivers/gpu/drm/etnaviv/etnaviv_gpu.c
@@ -545,13 +545,13 @@ static int etnaviv_hw_reset(struct etnaviv_gpu *gpu)
u32 pulse_eater = 0x01590880;
/* disable clock gating */
- gpu_write_power(gpu, VIVS_PM_POWER_CONTROLS, 0x0);
+ gpu_write_power_sync(gpu, VIVS_PM_POWER_CONTROLS, 0x0);
/* disable pulse eater */
pulse_eater |= BIT(17);
gpu_write_power(gpu, VIVS_PM_PULSE_EATER, pulse_eater);
pulse_eater |= BIT(0);
- gpu_write_power(gpu, VIVS_PM_PULSE_EATER, pulse_eater);
+ gpu_write_power_sync(gpu, VIVS_PM_PULSE_EATER, pulse_eater);
/* enable clock */
control = VIVS_HI_CLOCK_CONTROL_FSCALE_VAL(fscale);
@@ -662,7 +662,7 @@ static void etnaviv_gpu_enable_mlcg(struct etnaviv_gpu *gpu)
gpu->identity.revision == 0x4302)
ppc |= VIVS_PM_POWER_CONTROLS_DISABLE_STALL_MODULE_CLOCK_GATING;
- gpu_write_power(gpu, VIVS_PM_POWER_CONTROLS, ppc);
+ gpu_write_power_sync(gpu, VIVS_PM_POWER_CONTROLS, ppc);
pmc = gpu_read_power(gpu, VIVS_PM_MODULE_CONTROLS);
@@ -706,7 +706,7 @@ static void etnaviv_gpu_enable_mlcg(struct etnaviv_gpu *gpu)
pmc |= VIVS_PM_MODULE_CONTROLS_DISABLE_MODULE_CLOCK_GATING_RA_HZ;
pmc |= VIVS_PM_MODULE_CONTROLS_DISABLE_MODULE_CLOCK_GATING_RA_EZ;
- gpu_write_power(gpu, VIVS_PM_MODULE_CONTROLS, pmc);
+ gpu_write_power_sync(gpu, VIVS_PM_MODULE_CONTROLS, pmc);
}
void etnaviv_gpu_start_fe(struct etnaviv_gpu *gpu, u32 address, u16 prefetch)
@@ -772,7 +772,7 @@ static void etnaviv_gpu_setup_pulse_eater(struct etnaviv_gpu *gpu)
pulse_eater |= BIT(18);
}
- gpu_write_power(gpu, VIVS_PM_PULSE_EATER, pulse_eater);
+ gpu_write_power_sync(gpu, VIVS_PM_PULSE_EATER, pulse_eater);
}
static void etnaviv_gpu_hw_init(struct etnaviv_gpu *gpu)
diff --git a/drivers/gpu/drm/etnaviv/etnaviv_gpu.h b/drivers/gpu/drm/etnaviv/etnaviv_gpu.h
index 5cb46c84e03a..e89d591d7404 100644
--- a/drivers/gpu/drm/etnaviv/etnaviv_gpu.h
+++ b/drivers/gpu/drm/etnaviv/etnaviv_gpu.h
@@ -204,6 +204,12 @@ static inline u32 gpu_read_power(struct etnaviv_gpu *gpu, u32 reg)
return readl(gpu->mmio + gpu_fix_power_address(gpu, reg));
}
+static inline void gpu_write_power_sync(struct etnaviv_gpu *gpu, u32 reg, u32 data)
+{
+ gpu_write_power(gpu, reg, data);
+ gpu_read_power(gpu, reg);
+}
+
int etnaviv_gpu_get_param(struct etnaviv_gpu *gpu, u32 param, u64 *value);
int etnaviv_gpu_init(struct etnaviv_gpu *gpu);
diff --git a/drivers/gpu/drm/exynos/exynos_dp.c b/drivers/gpu/drm/exynos/exynos_dp.c
index b80540328150..a418011f7d4d 100644
--- a/drivers/gpu/drm/exynos/exynos_dp.c
+++ b/drivers/gpu/drm/exynos/exynos_dp.c
@@ -182,8 +182,15 @@ static int exynos_dp_probe(struct platform_device *pdev)
out:
dp->adp = analogix_dp_probe(dev, &dp->plat_data);
- if (IS_ERR(dp->adp))
+ 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);
return PTR_ERR(dp->adp);
+ }
if (dp->plat_data.panel || dp->plat_data.next_bridge)
return component_add(&pdev->dev, &exynos_dp_ops);
@@ -193,6 +200,16 @@ out:
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);
}
diff --git a/drivers/gpu/drm/exynos/exynos_drm_dpi.c b/drivers/gpu/drm/exynos/exynos_drm_dpi.c
index 0dc36df6ada3..9d15a0035ea9 100644
--- a/drivers/gpu/drm/exynos/exynos_drm_dpi.c
+++ b/drivers/gpu/drm/exynos/exynos_drm_dpi.c
@@ -245,5 +245,8 @@ int exynos_dpi_remove(struct drm_encoder *encoder)
exynos_dpi_disable(&ctx->encoder);
+ if (ctx->panel)
+ drm_panel_put(ctx->panel);
+
return 0;
}
diff --git a/drivers/gpu/drm/fsl-dcu/fsl_dcu_drm_rgb.c b/drivers/gpu/drm/fsl-dcu/fsl_dcu_drm_rgb.c
index 84eff7519e32..ec71fbbb0eb8 100644
--- a/drivers/gpu/drm/fsl-dcu/fsl_dcu_drm_rgb.c
+++ b/drivers/gpu/drm/fsl-dcu/fsl_dcu_drm_rgb.c
@@ -109,6 +109,13 @@ err_cleanup:
return ret;
}
+static void fsl_dcu_panel_put_action(void *data)
+{
+ struct drm_panel *panel = data;
+
+ drm_panel_put(panel);
+}
+
int fsl_dcu_create_outputs(struct fsl_dcu_drm_device *fsl_dev)
{
struct device_node *panel_node;
@@ -124,6 +131,12 @@ int fsl_dcu_create_outputs(struct fsl_dcu_drm_device *fsl_dev)
if (IS_ERR(fsl_dev->connector.panel))
return PTR_ERR(fsl_dev->connector.panel);
+ ret = devm_add_action_or_reset(fsl_dev->dev,
+ fsl_dcu_panel_put_action,
+ fsl_dev->connector.panel);
+ if (ret)
+ return ret;
+
return fsl_dcu_attach_panel(fsl_dev, fsl_dev->connector.panel);
}
@@ -132,6 +145,11 @@ int fsl_dcu_create_outputs(struct fsl_dcu_drm_device *fsl_dev)
return ret;
if (panel) {
+ ret = devm_add_action_or_reset(fsl_dev->dev,
+ fsl_dcu_panel_put_action, panel);
+ if (ret)
+ return ret;
+
fsl_dev->connector.panel = panel;
return fsl_dcu_attach_panel(fsl_dev, panel);
}
diff --git a/drivers/gpu/drm/gma500/cdv_intel_crt.c b/drivers/gpu/drm/gma500/cdv_intel_crt.c
index 342a57c82846..e51a965ddeb8 100644
--- a/drivers/gpu/drm/gma500/cdv_intel_crt.c
+++ b/drivers/gpu/drm/gma500/cdv_intel_crt.c
@@ -29,8 +29,8 @@
#include <linux/pm_runtime.h>
#include <drm/drm_crtc_helper.h>
+#include <drm/drm_encoder.h>
#include <drm/drm_modeset_helper_vtables.h>
-#include <drm/drm_simple_kms_helper.h>
#include "cdv_device.h"
#include "intel_bios.h"
@@ -217,6 +217,10 @@ static int cdv_intel_crt_set_property(struct drm_connector *connector,
* Routines for controlling stuff on the analog port
*/
+static const struct drm_encoder_funcs cdv_intel_crt_funcs = {
+ .destroy = drm_encoder_cleanup,
+};
+
static const struct drm_encoder_helper_funcs cdv_intel_crt_helper_funcs = {
.dpms = cdv_intel_crt_dpms,
.prepare = gma_encoder_prepare,
@@ -275,7 +279,8 @@ void cdv_intel_crt_init(struct drm_device *dev,
goto err_ddc_destroy;
encoder = &gma_encoder->base;
- ret = drm_simple_encoder_init(dev, encoder, DRM_MODE_ENCODER_DAC);
+ ret = drm_encoder_init(dev, encoder, &cdv_intel_crt_funcs,
+ DRM_MODE_ENCODER_DAC, NULL);
if (ret)
goto err_connector_cleanup;
diff --git a/drivers/gpu/drm/gma500/cdv_intel_dp.c b/drivers/gpu/drm/gma500/cdv_intel_dp.c
index fbed35cf7603..8d0626ddf1c3 100644
--- a/drivers/gpu/drm/gma500/cdv_intel_dp.c
+++ b/drivers/gpu/drm/gma500/cdv_intel_dp.c
@@ -33,9 +33,9 @@
#include <drm/drm_crtc.h>
#include <drm/drm_crtc_helper.h>
#include <drm/drm_edid.h>
+#include <drm/drm_encoder.h>
#include <drm/drm_modeset_helper_vtables.h>
#include <drm/drm_print.h>
-#include <drm/drm_simple_kms_helper.h>
#include "gma_display.h"
#include "psb_drv.h"
@@ -232,16 +232,17 @@ i2c_dp_aux_add_bus(struct i2c_adapter *adapter)
}
#define _wait_for(COND, MS, W) ({ \
- unsigned long timeout__ = jiffies + msecs_to_jiffies(MS); \
- int ret__ = 0; \
- while (! (COND)) { \
- if (time_after(jiffies, timeout__)) { \
- ret__ = -ETIMEDOUT; \
- break; \
- } \
- if (W && !in_dbg_master()) msleep(W); \
- } \
- ret__; \
+ unsigned long timeout__ = jiffies + msecs_to_jiffies(MS); \
+ int ret__ = 0; \
+ while (!(COND)) { \
+ if (time_after(jiffies, timeout__)) { \
+ ret__ = -ETIMEDOUT; \
+ break; \
+ } \
+ if (W && !in_dbg_master()) \
+ msleep(W); \
+ } \
+ ret__; \
})
#define wait_for(COND, MS) _wait_for(COND, MS, 1)
@@ -296,10 +297,10 @@ static struct ddi_regoff ddi_DP_train_table[] = {
};
static uint32_t dp_vswing_premph_table[] = {
- 0x55338954, 0x4000,
- 0x554d8954, 0x2000,
- 0x55668954, 0,
- 0x559ac0d4, 0x6000,
+ 0x55338954, 0x4000,
+ 0x554d8954, 0x2000,
+ 0x55668954, 0,
+ 0x559ac0d4, 0x6000,
};
/**
* is_edp - is the given port attached to an eDP panel (either CPU or PCH)
@@ -1141,7 +1142,7 @@ static void cdv_intel_dp_prepare(struct drm_encoder *encoder)
cdv_intel_edp_backlight_off(intel_encoder);
cdv_intel_edp_panel_off(intel_encoder);
cdv_intel_edp_panel_vdd_on(intel_encoder);
- }
+ }
/* Wake up the sink first */
cdv_intel_dp_sink_dpms(intel_encoder, DRM_MODE_DPMS_ON);
cdv_intel_dp_link_down(intel_encoder);
@@ -1183,7 +1184,7 @@ cdv_intel_dp_dpms(struct drm_encoder *encoder, int mode)
cdv_intel_edp_panel_off(intel_encoder);
}
} else {
- if (edp)
+ if (edp)
cdv_intel_edp_panel_on(intel_encoder);
cdv_intel_dp_sink_dpms(intel_encoder, mode);
if (!(dp_reg & DP_PORT_EN)) {
@@ -1191,7 +1192,7 @@ cdv_intel_dp_dpms(struct drm_encoder *encoder, int mode)
cdv_intel_dp_complete_link_train(intel_encoder);
}
if (edp)
- cdv_intel_edp_backlight_on(intel_encoder);
+ cdv_intel_edp_backlight_on(intel_encoder);
}
}
@@ -1419,8 +1420,8 @@ cdv_intel_dp_set_vswing_premph(struct gma_encoder *encoder, uint8_t signal_level
DRM_DEBUG_KMS("Test2\n");
//return ;
cdv_sb_reset(dev);
- /* ;Swing voltage programming
- ;gfx_dpio_set_reg(0xc058, 0x0505313A) */
+ /* ;Swing voltage programming */
+ /* ;gfx_dpio_set_reg(0xc058, 0x0505313A) */
cdv_sb_write(dev, ddi_reg->VSwing5, 0x0505313A);
/* ;gfx_dpio_set_reg(0x8154, 0x43406055) */
@@ -1575,7 +1576,7 @@ cdv_intel_dp_complete_link_train(struct gma_encoder *encoder)
intel_dp->train_set[0],
intel_dp->link_configuration[0],
intel_dp->link_configuration[1]);
- /* channel eq pattern */
+ /* channel eq pattern */
if (!cdv_intel_dp_set_link_train(encoder, reg,
DP_TRAINING_PATTERN_2)) {
@@ -1704,7 +1705,7 @@ cdv_intel_dp_detect(struct drm_connector *connector, bool force)
if (edp)
cdv_intel_edp_panel_vdd_off(encoder);
return status;
- }
+ }
if (intel_dp->force_audio) {
intel_dp->has_audio = intel_dp->force_audio > 0;
@@ -1872,6 +1873,10 @@ cdv_intel_dp_destroy(struct drm_connector *connector)
kfree(gma_connector);
}
+static const struct drm_encoder_funcs cdv_intel_dp_funcs = {
+ .destroy = drm_encoder_cleanup,
+};
+
static const struct drm_encoder_helper_funcs cdv_intel_dp_helper_funcs = {
.dpms = cdv_intel_dp_dpms,
.mode_fixup = cdv_intel_dp_mode_fixup,
@@ -1956,12 +1961,12 @@ cdv_intel_dp_init(struct drm_device *dev, struct psb_intel_mode_device *mode_dev
gma_encoder = kzalloc_obj(struct gma_encoder);
if (!gma_encoder)
return;
- gma_connector = kzalloc_obj(struct gma_connector);
- if (!gma_connector)
- goto err_connector;
+ gma_connector = kzalloc_obj(struct gma_connector);
+ if (!gma_connector)
+ goto err_connector;
intel_dp = kzalloc_obj(struct cdv_intel_dp);
if (!intel_dp)
- goto err_priv;
+ goto err_priv;
if ((output_reg == DP_C) && cdv_intel_dpc_is_edp(dev))
type = DRM_MODE_CONNECTOR_eDP;
@@ -1970,13 +1975,14 @@ cdv_intel_dp_init(struct drm_device *dev, struct psb_intel_mode_device *mode_dev
encoder = &gma_encoder->base;
drm_connector_init(dev, connector, &cdv_intel_dp_connector_funcs, type);
- drm_simple_encoder_init(dev, encoder, DRM_MODE_ENCODER_TMDS);
+ drm_encoder_init(dev, encoder, &cdv_intel_dp_funcs,
+ DRM_MODE_ENCODER_TMDS, NULL);
gma_connector_attach_encoder(gma_connector, gma_encoder);
if (type == DRM_MODE_CONNECTOR_DisplayPort)
gma_encoder->type = INTEL_OUTPUT_DISPLAYPORT;
- else
+ else
gma_encoder->type = INTEL_OUTPUT_EDP;
@@ -2006,18 +2012,18 @@ cdv_intel_dp_init(struct drm_device *dev, struct psb_intel_mode_device *mode_dev
cdv_disable_intel_clock_gating(dev);
cdv_intel_dp_i2c_init(gma_connector, gma_encoder, name);
- /* FIXME:fail check */
+ /* FIXME:fail check */
cdv_intel_dp_add_properties(connector);
if (is_edp(gma_encoder)) {
int ret;
struct edp_power_seq cur;
- u32 pp_on, pp_off, pp_div;
+ u32 pp_on, pp_off, pp_div;
u32 pwm_ctrl;
pp_on = REG_READ(PP_CONTROL);
pp_on &= ~PANEL_UNLOCK_MASK;
- pp_on |= PANEL_UNLOCK_REGS;
+ pp_on |= PANEL_UNLOCK_REGS;
REG_WRITE(PP_CONTROL, pp_on);
@@ -2025,42 +2031,42 @@ cdv_intel_dp_init(struct drm_device *dev, struct psb_intel_mode_device *mode_dev
pwm_ctrl |= PWM_PIPE_B;
REG_WRITE(BLC_PWM_CTL2, pwm_ctrl);
- pp_on = REG_READ(PP_ON_DELAYS);
- pp_off = REG_READ(PP_OFF_DELAYS);
- pp_div = REG_READ(PP_DIVISOR);
+ pp_on = REG_READ(PP_ON_DELAYS);
+ pp_off = REG_READ(PP_OFF_DELAYS);
+ pp_div = REG_READ(PP_DIVISOR);
/* Pull timing values out of registers */
- cur.t1_t3 = (pp_on & PANEL_POWER_UP_DELAY_MASK) >>
- PANEL_POWER_UP_DELAY_SHIFT;
+ cur.t1_t3 = (pp_on & PANEL_POWER_UP_DELAY_MASK) >>
+ PANEL_POWER_UP_DELAY_SHIFT;
- cur.t8 = (pp_on & PANEL_LIGHT_ON_DELAY_MASK) >>
- PANEL_LIGHT_ON_DELAY_SHIFT;
+ cur.t8 = (pp_on & PANEL_LIGHT_ON_DELAY_MASK) >>
+ PANEL_LIGHT_ON_DELAY_SHIFT;
- cur.t9 = (pp_off & PANEL_LIGHT_OFF_DELAY_MASK) >>
- PANEL_LIGHT_OFF_DELAY_SHIFT;
+ cur.t9 = (pp_off & PANEL_LIGHT_OFF_DELAY_MASK) >>
+ PANEL_LIGHT_OFF_DELAY_SHIFT;
- cur.t10 = (pp_off & PANEL_POWER_DOWN_DELAY_MASK) >>
- PANEL_POWER_DOWN_DELAY_SHIFT;
+ cur.t10 = (pp_off & PANEL_POWER_DOWN_DELAY_MASK) >>
+ PANEL_POWER_DOWN_DELAY_SHIFT;
- cur.t11_t12 = ((pp_div & PANEL_POWER_CYCLE_DELAY_MASK) >>
- PANEL_POWER_CYCLE_DELAY_SHIFT);
+ cur.t11_t12 = ((pp_div & PANEL_POWER_CYCLE_DELAY_MASK) >>
+ PANEL_POWER_CYCLE_DELAY_SHIFT);
- DRM_DEBUG_KMS("cur t1_t3 %d t8 %d t9 %d t10 %d t11_t12 %d\n",
- cur.t1_t3, cur.t8, cur.t9, cur.t10, cur.t11_t12);
+ DRM_DEBUG_KMS("cur t1_t3 %d t8 %d t9 %d t10 %d t11_t12 %d\n",
+ cur.t1_t3, cur.t8, cur.t9, cur.t10, cur.t11_t12);
intel_dp->panel_power_up_delay = cur.t1_t3 / 10;
- intel_dp->backlight_on_delay = cur.t8 / 10;
- intel_dp->backlight_off_delay = cur.t9 / 10;
- intel_dp->panel_power_down_delay = cur.t10 / 10;
- intel_dp->panel_power_cycle_delay = (cur.t11_t12 - 1) * 100;
+ intel_dp->backlight_on_delay = cur.t8 / 10;
+ intel_dp->backlight_off_delay = cur.t9 / 10;
+ intel_dp->panel_power_down_delay = cur.t10 / 10;
+ intel_dp->panel_power_cycle_delay = (cur.t11_t12 - 1) * 100;
- DRM_DEBUG_KMS("panel power up delay %d, power down delay %d, power cycle delay %d\n",
- intel_dp->panel_power_up_delay, intel_dp->panel_power_down_delay,
- intel_dp->panel_power_cycle_delay);
+ DRM_DEBUG_KMS("panel power up delay %d, power down delay %d, power cycle delay %d\n",
+ intel_dp->panel_power_up_delay, intel_dp->panel_power_down_delay,
+ intel_dp->panel_power_cycle_delay);
- DRM_DEBUG_KMS("backlight on delay %d, off delay %d\n",
- intel_dp->backlight_on_delay, intel_dp->backlight_off_delay);
+ DRM_DEBUG_KMS("backlight on delay %d, off delay %d\n",
+ intel_dp->backlight_on_delay, intel_dp->backlight_off_delay);
cdv_intel_edp_panel_vdd_on(gma_encoder);
@@ -2075,7 +2081,7 @@ cdv_intel_dp_init(struct drm_device *dev, struct psb_intel_mode_device *mode_dev
cdv_intel_dp_destroy(connector);
goto err_connector;
} else {
- DRM_DEBUG_KMS("DPCD: Rev=%x LN_Rate=%x LN_CNT=%x LN_DOWNSP=%x\n",
+ DRM_DEBUG_KMS("DPCD: Rev=%x LN_Rate=%x LN_CNT=%x LN_DOWNSP=%x\n",
intel_dp->dpcd[0], intel_dp->dpcd[1],
intel_dp->dpcd[2], intel_dp->dpcd[3]);
@@ -2083,7 +2089,7 @@ cdv_intel_dp_init(struct drm_device *dev, struct psb_intel_mode_device *mode_dev
/* The CDV reference driver moves pnale backlight setup into the displays that
have a backlight: this is a good idea and one we should probably adopt, however
we need to migrate all the drivers before we can do that */
- /*cdv_intel_panel_setup_backlight(dev); */
+ /*cdv_intel_panel_setup_backlight(dev); */
}
return;
diff --git a/drivers/gpu/drm/gma500/cdv_intel_hdmi.c b/drivers/gpu/drm/gma500/cdv_intel_hdmi.c
index ce7850647778..757ff9408250 100644
--- a/drivers/gpu/drm/gma500/cdv_intel_hdmi.c
+++ b/drivers/gpu/drm/gma500/cdv_intel_hdmi.c
@@ -30,9 +30,9 @@
#include <drm/drm_crtc.h>
#include <drm/drm_crtc_helper.h>
#include <drm/drm_edid.h>
+#include <drm/drm_encoder.h>
#include <drm/drm_modeset_helper_vtables.h>
#include <drm/drm_print.h>
-#include <drm/drm_simple_kms_helper.h>
#include "cdv_device.h"
#include "psb_drv.h"
@@ -251,6 +251,10 @@ static void cdv_hdmi_destroy(struct drm_connector *connector)
kfree(gma_connector);
}
+static const struct drm_encoder_funcs cdv_hdmi_funcs = {
+ .destroy = drm_encoder_cleanup,
+};
+
static const struct drm_encoder_helper_funcs cdv_hdmi_helper_funcs = {
.dpms = cdv_hdmi_dpms,
.prepare = gma_encoder_prepare,
@@ -329,8 +333,8 @@ void cdv_hdmi_init(struct drm_device *dev,
if (ret)
goto err_ddc_destroy;
- ret = drm_simple_encoder_init(dev, &gma_encoder->base,
- DRM_MODE_ENCODER_TMDS);
+ ret = drm_encoder_init(dev, &gma_encoder->base, &cdv_hdmi_funcs,
+ DRM_MODE_ENCODER_TMDS, NULL);
if (ret)
goto err_connector_cleanup;
diff --git a/drivers/gpu/drm/gma500/cdv_intel_lvds.c b/drivers/gpu/drm/gma500/cdv_intel_lvds.c
index d7fd9a783cde..336ab411d699 100644
--- a/drivers/gpu/drm/gma500/cdv_intel_lvds.c
+++ b/drivers/gpu/drm/gma500/cdv_intel_lvds.c
@@ -13,9 +13,9 @@
#include <linux/pm_runtime.h>
#include <drm/drm_crtc_helper.h>
+#include <drm/drm_encoder.h>
#include <drm/drm_modeset_helper_vtables.h>
#include <drm/drm_print.h>
-#include <drm/drm_simple_kms_helper.h>
#include "cdv_device.h"
#include "intel_bios.h"
@@ -394,6 +394,10 @@ static int cdv_intel_lvds_set_property(struct drm_connector *connector,
return 0;
}
+static const struct drm_encoder_funcs cdv_intel_lvds_funcs = {
+ .destroy = drm_encoder_cleanup,
+};
+
static const struct drm_encoder_helper_funcs
cdv_intel_lvds_helper_funcs = {
.dpms = cdv_intel_lvds_encoder_dpms,
@@ -535,7 +539,8 @@ void cdv_intel_lvds_init(struct drm_device *dev,
if (ret)
goto err_destroy_ddc;
- ret = drm_simple_encoder_init(dev, encoder, DRM_MODE_ENCODER_LVDS);
+ ret = drm_encoder_init(dev, encoder, &cdv_intel_lvds_funcs,
+ DRM_MODE_ENCODER_LVDS, NULL);
if (ret)
goto err_connector_cleanup;
diff --git a/drivers/gpu/drm/gma500/oaktrail_hdmi.c b/drivers/gpu/drm/gma500/oaktrail_hdmi.c
index 403d21cbb3a2..5a6132176086 100644
--- a/drivers/gpu/drm/gma500/oaktrail_hdmi.c
+++ b/drivers/gpu/drm/gma500/oaktrail_hdmi.c
@@ -29,9 +29,9 @@
#include <drm/drm.h>
#include <drm/drm_crtc_helper.h>
#include <drm/drm_edid.h>
+#include <drm/drm_encoder.h>
#include <drm/drm_modeset_helper_vtables.h>
#include <drm/drm_print.h>
-#include <drm/drm_simple_kms_helper.h>
#include "psb_drv.h"
#include "psb_intel_drv.h"
@@ -605,6 +605,10 @@ static void oaktrail_hdmi_destroy(struct drm_connector *connector)
return;
}
+static const struct drm_encoder_funcs oaktrail_hdmi_funcs = {
+ .destroy = drm_encoder_cleanup,
+};
+
static const struct drm_encoder_helper_funcs oaktrail_hdmi_helper_funcs = {
.dpms = oaktrail_hdmi_dpms,
.prepare = gma_encoder_prepare,
@@ -648,7 +652,8 @@ void oaktrail_hdmi_init(struct drm_device *dev,
&oaktrail_hdmi_connector_funcs,
DRM_MODE_CONNECTOR_DVID);
- drm_simple_encoder_init(dev, encoder, DRM_MODE_ENCODER_TMDS);
+ drm_encoder_init(dev, encoder, &oaktrail_hdmi_funcs,
+ DRM_MODE_ENCODER_TMDS, NULL);
gma_connector_attach_encoder(gma_connector, gma_encoder);
diff --git a/drivers/gpu/drm/gma500/oaktrail_lvds.c b/drivers/gpu/drm/gma500/oaktrail_lvds.c
index e194d0cce067..425796d8d6c3 100644
--- a/drivers/gpu/drm/gma500/oaktrail_lvds.c
+++ b/drivers/gpu/drm/gma500/oaktrail_lvds.c
@@ -12,9 +12,9 @@
#include <linux/pm_runtime.h>
#include <drm/drm_edid.h>
+#include <drm/drm_encoder.h>
#include <drm/drm_modeset_helper_vtables.h>
#include <drm/drm_print.h>
-#include <drm/drm_simple_kms_helper.h>
#include "intel_bios.h"
#include "power.h"
@@ -202,6 +202,10 @@ static void oaktrail_lvds_commit(struct drm_encoder *encoder)
oaktrail_lvds_set_power(dev, gma_encoder, true);
}
+static const struct drm_encoder_funcs oaktrail_lvds_funcs = {
+ .destroy = drm_encoder_cleanup,
+};
+
static const struct drm_encoder_helper_funcs oaktrail_lvds_helper_funcs = {
.dpms = oaktrail_lvds_dpms,
.mode_fixup = psb_intel_lvds_mode_fixup,
@@ -319,7 +323,8 @@ void oaktrail_lvds_init(struct drm_device *dev,
if (ret)
goto err_free_connector;
- ret = drm_simple_encoder_init(dev, encoder, DRM_MODE_ENCODER_LVDS);
+ ret = drm_encoder_init(dev, encoder, &oaktrail_lvds_funcs,
+ DRM_MODE_ENCODER_LVDS, NULL);
if (ret)
goto err_connector_cleanup;
diff --git a/drivers/gpu/drm/gma500/psb_intel_lvds.c b/drivers/gpu/drm/gma500/psb_intel_lvds.c
index 2ca164b21293..cd882786d946 100644
--- a/drivers/gpu/drm/gma500/psb_intel_lvds.c
+++ b/drivers/gpu/drm/gma500/psb_intel_lvds.c
@@ -12,9 +12,9 @@
#include <linux/pm_runtime.h>
#include <drm/drm_crtc_helper.h>
+#include <drm/drm_encoder.h>
#include <drm/drm_modeset_helper_vtables.h>
#include <drm/drm_print.h>
-#include <drm/drm_simple_kms_helper.h>
#include "intel_bios.h"
#include "power.h"
@@ -593,6 +593,10 @@ set_prop_error:
return -1;
}
+static const struct drm_encoder_funcs psb_intel_lvds_funcs = {
+ .destroy = drm_encoder_cleanup,
+};
+
static const struct drm_encoder_helper_funcs psb_intel_lvds_helper_funcs = {
.dpms = psb_intel_lvds_encoder_dpms,
.mode_fixup = psb_intel_lvds_mode_fixup,
@@ -679,7 +683,8 @@ void psb_intel_lvds_init(struct drm_device *dev,
if (ret)
goto err_ddc_destroy;
- ret = drm_simple_encoder_init(dev, encoder, DRM_MODE_ENCODER_LVDS);
+ ret = drm_encoder_init(dev, encoder, &psb_intel_lvds_funcs,
+ DRM_MODE_ENCODER_LVDS, NULL);
if (ret)
goto err_connector_cleanup;
diff --git a/drivers/gpu/drm/hyperv/hyperv_drm_drv.c b/drivers/gpu/drm/hyperv/hyperv_drm_drv.c
index 20f35c48c0b8..0efd0593d031 100644
--- a/drivers/gpu/drm/hyperv/hyperv_drm_drv.c
+++ b/drivers/gpu/drm/hyperv/hyperv_drm_drv.c
@@ -51,11 +51,12 @@ static void hv_drm_pci_remove(struct pci_dev *pdev)
static const struct pci_device_id hv_drm_pci_tbl[] = {
{
- .vendor = PCI_VENDOR_ID_MICROSOFT,
- .device = PCI_DEVICE_ID_HYPERV_VIDEO,
+ PCI_VDEVICE_SUB(MICROSOFT, PCI_DEVICE_ID_HYPERV_VIDEO,
+ 0, 0),
},
{ /* end of list */ }
};
+MODULE_DEVICE_TABLE(pci, hv_drm_pci_tbl);
/*
* PCI stub to support gen1 VM.
@@ -224,6 +225,7 @@ static const struct hv_vmbus_device_id hv_drm_vmbus_tbl[] = {
{HV_SYNTHVID_GUID},
{}
};
+MODULE_DEVICE_TABLE(vmbus, hv_drm_vmbus_tbl);
static struct hv_driver hv_drm_hv_driver = {
.name = KBUILD_MODNAME,
@@ -249,7 +251,11 @@ static int __init hv_drm_init(void)
if (ret != 0)
return ret;
- return vmbus_driver_register(&hv_drm_hv_driver);
+ ret = vmbus_driver_register(&hv_drm_hv_driver);
+ if (ret)
+ pci_unregister_driver(&hv_drm_pci_driver);
+
+ return ret;
}
static void __exit hv_drm_exit(void)
@@ -261,8 +267,6 @@ static void __exit hv_drm_exit(void)
module_init(hv_drm_init);
module_exit(hv_drm_exit);
-MODULE_DEVICE_TABLE(pci, hv_drm_pci_tbl);
-MODULE_DEVICE_TABLE(vmbus, hv_drm_vmbus_tbl);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Deepak Rawat <drawat.floss@gmail.com>");
MODULE_DESCRIPTION("DRM driver for Hyper-V synthetic video device");
diff --git a/drivers/gpu/drm/imagination/pvr_device.c b/drivers/gpu/drm/imagination/pvr_device.c
index 2691ef9af0ca..54fe4180c73c 100644
--- a/drivers/gpu/drm/imagination/pvr_device.c
+++ b/drivers/gpu/drm/imagination/pvr_device.c
@@ -531,12 +531,10 @@ pvr_gpu_support_level(const struct pvr_gpu_id *gpu_id)
{
switch (pvr_gpu_id_to_packed_bvnc(gpu_id)) {
case PVR_PACKED_BVNC(33, 15, 11, 3):
+ case PVR_PACKED_BVNC(36, 52, 104, 182):
case PVR_PACKED_BVNC(36, 53, 104, 796):
return PVR_GPU_SUPPORTED;
- case PVR_PACKED_BVNC(36, 52, 104, 182):
- return PVR_GPU_EXPERIMENTAL;
-
default:
return PVR_GPU_UNKNOWN;
}
diff --git a/drivers/gpu/drm/imagination/pvr_drv.c b/drivers/gpu/drm/imagination/pvr_drv.c
index 8897b3bdeb4a..5c965ef0274f 100644
--- a/drivers/gpu/drm/imagination/pvr_drv.c
+++ b/drivers/gpu/drm/imagination/pvr_drv.c
@@ -43,8 +43,9 @@
*
* This driver supports the following PowerVR/IMG graphics cores from Imagination Technologies:
*
- * * AXE-1-16M (found in Texas Instruments AM62)
- * * BXS-4-64 MC1 (found in Texas Instruments J721S2/AM68)
+ * * AXE-1-16M (33.15.11.3)
+ * * BXM-4-64 MC1 (36.52.104.182)
+ * * BXS-4-64 MC1 (36.53.104.796)
*/
/**
diff --git a/drivers/gpu/drm/imagination/pvr_queue.c b/drivers/gpu/drm/imagination/pvr_queue.c
index 941c017399fc..54e88b4208d7 100644
--- a/drivers/gpu/drm/imagination/pvr_queue.c
+++ b/drivers/gpu/drm/imagination/pvr_queue.c
@@ -3,6 +3,7 @@
#include <drm/drm_managed.h>
#include <drm/gpu_scheduler.h>
+#include <linux/overflow.h>
#include "pvr_cccb.h"
#include "pvr_context.h"
@@ -36,9 +37,8 @@ static int get_xfer_ctx_state_size(struct pvr_device *pvr_dev)
return err;
}
- return sizeof(struct rogue_fwif_frag_ctx_state) +
- (num_isp_store_registers *
- sizeof(((struct rogue_fwif_frag_ctx_state *)0)->frag_reg_isp_store[0]));
+ return struct_size_t(struct rogue_fwif_frag_ctx_state,
+ frag_reg_isp_store, num_isp_store_registers);
}
static int get_frag_ctx_state_size(struct pvr_device *pvr_dev)
@@ -66,9 +66,8 @@ static int get_frag_ctx_state_size(struct pvr_device *pvr_dev)
return err;
}
- return sizeof(struct rogue_fwif_frag_ctx_state) +
- (num_isp_store_registers *
- sizeof(((struct rogue_fwif_frag_ctx_state *)0)->frag_reg_isp_store[0]));
+ return struct_size_t(struct rogue_fwif_frag_ctx_state,
+ frag_reg_isp_store, num_isp_store_registers);
}
static int get_ctx_state_size(struct pvr_device *pvr_dev, enum drm_pvr_job_type type)
diff --git a/drivers/gpu/drm/imagination/pvr_rogue_fwif_check.h b/drivers/gpu/drm/imagination/pvr_rogue_fwif_check.h
index 51dc37e78f41..e72f4064af18 100644
--- a/drivers/gpu/drm/imagination/pvr_rogue_fwif_check.h
+++ b/drivers/gpu/drm/imagination/pvr_rogue_fwif_check.h
@@ -5,6 +5,8 @@
#define PVR_ROGUE_FWIF_CHECK_H
#include <linux/build_bug.h>
+#include <linux/overflow.h>
+#include <linux/stddef.h>
#define OFFSET_CHECK(type, member, offset) \
static_assert(offsetof(type, member) == (offset), \
@@ -13,6 +15,21 @@
#define SIZE_CHECK(type, size) \
static_assert(sizeof(type) == (size), #type " is incorrect size")
+/*
+ * Where the last member of a struct is a flexible array member, using
+ * SIZE_CHECK() is pointless. If the structure is not already padded to
+ * alignment without the flexible array member, sizeof() will not match the
+ * offset of the flexible array member and the "correct" sizeof() value is
+ * completely meaningless.
+ *
+ * In those instances, use FLEX_ARRAY_CHECK() instead to assert that the final
+ * field is a flexible array member and that it behaves as expected.
+ */
+#define FLEX_ARRAY_CHECK(type, member) \
+ static_assert(flex_array_size((type *)NULL, member, 1) == \
+ sizeof_field(type, member[0]), \
+ #type "->" #member " is incorrect size")
+
OFFSET_CHECK(struct rogue_fwif_file_info_buf, path, 0);
OFFSET_CHECK(struct rogue_fwif_file_info_buf, info, 200);
OFFSET_CHECK(struct rogue_fwif_file_info_buf, line_num, 400);
@@ -157,7 +174,7 @@ OFFSET_CHECK(struct rogue_fwif_frag_ctx_state, frag_reg_pm_deallocated_mask_stat
OFFSET_CHECK(struct rogue_fwif_frag_ctx_state, frag_reg_dm_pds_mtilefree_status, 4);
OFFSET_CHECK(struct rogue_fwif_frag_ctx_state, ctx_state_flags, 8);
OFFSET_CHECK(struct rogue_fwif_frag_ctx_state, frag_reg_isp_store, 12);
-SIZE_CHECK(struct rogue_fwif_frag_ctx_state, 16);
+FLEX_ARRAY_CHECK(struct rogue_fwif_frag_ctx_state, frag_reg_isp_store);
OFFSET_CHECK(struct rogue_fwif_compute_ctx_state, ctx_state_flags, 0);
SIZE_CHECK(struct rogue_fwif_compute_ctx_state, 4);
diff --git a/drivers/gpu/drm/imx/dcss/dcss-kms.c b/drivers/gpu/drm/imx/dcss/dcss-kms.c
index 50bd7f36d36d..7c5a6e2bc89a 100644
--- a/drivers/gpu/drm/imx/dcss/dcss-kms.c
+++ b/drivers/gpu/drm/imx/dcss/dcss-kms.c
@@ -13,6 +13,7 @@
#include <drm/drm_gem_dma_helper.h>
#include <drm/drm_gem_framebuffer_helper.h>
#include <drm/drm_of.h>
+#include <drm/drm_panel.h>
#include <drm/drm_probe_helper.h>
#include <drm/drm_vblank.h>
@@ -77,6 +78,9 @@ static int dcss_kms_bridge_connector_init(struct dcss_kms_dev *kms)
if (ret)
return ret;
+ if (panel)
+ drm_panel_put(panel);
+
if (!bridge) {
dev_err(ddev->dev, "No bridge found %d.\n", ret);
return -ENODEV;
diff --git a/drivers/gpu/drm/ingenic/ingenic-drm-drv.c b/drivers/gpu/drm/ingenic/ingenic-drm-drv.c
index 7e569af22391..738a80b2550f 100644
--- a/drivers/gpu/drm/ingenic/ingenic-drm-drv.c
+++ b/drivers/gpu/drm/ingenic/ingenic-drm-drv.c
@@ -1297,9 +1297,11 @@ static int ingenic_drm_bind(struct device *dev, bool has_components)
goto err_drvdata;
}
- if (panel)
+ if (panel) {
bridge = devm_drm_panel_bridge_add_typed(dev, panel,
DRM_MODE_CONNECTOR_DPI);
+ drm_panel_put(panel);
+ }
ib = drmm_encoder_alloc(drm, struct ingenic_drm_bridge, encoder,
NULL, DRM_MODE_ENCODER_DPI, NULL);
diff --git a/drivers/gpu/drm/logicvc/logicvc_interface.c b/drivers/gpu/drm/logicvc/logicvc_interface.c
index 689049d395c0..81f760dc07f8 100644
--- a/drivers/gpu/drm/logicvc/logicvc_interface.c
+++ b/drivers/gpu/drm/logicvc/logicvc_interface.c
@@ -28,6 +28,11 @@
#define logicvc_interface_from_drm_connector(c) \
container_of(c, struct logicvc_interface, drm_connector)
+static void logicvc_panel_put_action(void *data)
+{
+ drm_panel_put(data);
+}
+
static void logicvc_encoder_enable(struct drm_encoder *drm_encoder)
{
struct logicvc_drm *logicvc = logicvc_drm(drm_encoder->dev);
@@ -160,6 +165,13 @@ int logicvc_interface_init(struct logicvc_drm *logicvc)
if (ret == -EPROBE_DEFER)
goto error_early;
+ if (interface->drm_panel) {
+ ret = devm_add_action_or_reset(dev, logicvc_panel_put_action,
+ interface->drm_panel);
+ if (ret)
+ goto error_early;
+ }
+
ret = drm_encoder_init(drm_dev, &interface->drm_encoder,
&logicvc_encoder_funcs, encoder_type, NULL);
if (ret) {
diff --git a/drivers/gpu/drm/mcde/mcde_drv.c b/drivers/gpu/drm/mcde/mcde_drv.c
index 5f2c462bad7e..53275b575f0c 100644
--- a/drivers/gpu/drm/mcde/mcde_drv.c
+++ b/drivers/gpu/drm/mcde/mcde_drv.c
@@ -153,6 +153,7 @@ static int mcde_modeset_init(struct drm_device *drm)
if (panel) {
bridge = drm_panel_bridge_add_typed(panel,
DRM_MODE_CONNECTOR_DPI);
+ drm_panel_put(panel);
if (IS_ERR(bridge)) {
dev_err(drm->dev,
"Could not connect panel bridge\n");
diff --git a/drivers/gpu/drm/mcde/mcde_dsi.c b/drivers/gpu/drm/mcde/mcde_dsi.c
index 5cf44ccb02cf..694372581840 100644
--- a/drivers/gpu/drm/mcde/mcde_dsi.c
+++ b/drivers/gpu/drm/mcde/mcde_dsi.c
@@ -1127,6 +1127,7 @@ static int mcde_dsi_bind(struct device *dev, struct device *master,
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);
diff --git a/drivers/gpu/drm/mxsfb/mxsfb_drv.c b/drivers/gpu/drm/mxsfb/mxsfb_drv.c
index 9b8fbda85d28..8b4a500347fb 100644
--- a/drivers/gpu/drm/mxsfb/mxsfb_drv.c
+++ b/drivers/gpu/drm/mxsfb/mxsfb_drv.c
@@ -29,6 +29,7 @@
#include <drm/drm_mode_config.h>
#include <drm/drm_module.h>
#include <drm/drm_of.h>
+#include <drm/drm_panel.h>
#include <drm/drm_probe_helper.h>
#include <drm/drm_vblank.h>
@@ -127,6 +128,7 @@ static int mxsfb_attach_bridge(struct mxsfb_drm_private *mxsfb)
if (panel) {
bridge = devm_drm_panel_bridge_add_typed(drm->dev, panel,
DRM_MODE_CONNECTOR_DPI);
+ drm_panel_put(panel);
if (IS_ERR(bridge))
return PTR_ERR(bridge);
}
diff --git a/drivers/gpu/drm/nouveau/nouveau_drm.c b/drivers/gpu/drm/nouveau/nouveau_drm.c
index 7ea95ab960a0..4d1ad718e09b 100644
--- a/drivers/gpu/drm/nouveau/nouveau_drm.c
+++ b/drivers/gpu/drm/nouveau/nouveau_drm.c
@@ -1209,7 +1209,6 @@ nouveau_pmops_runtime_idle(struct device *dev)
return -EBUSY;
}
- pm_runtime_mark_last_busy(dev);
pm_runtime_autosuspend(dev);
/* we don't want the main rpm_idle to call suspend - we want to autosuspend */
return 1;
diff --git a/drivers/gpu/drm/omapdrm/dss/output.c b/drivers/gpu/drm/omapdrm/dss/output.c
index ca891aba3820..6e9bc605ee22 100644
--- a/drivers/gpu/drm/omapdrm/dss/output.c
+++ b/drivers/gpu/drm/omapdrm/dss/output.c
@@ -43,6 +43,7 @@ int omapdss_device_init_output(struct omap_dss_device *out,
struct drm_bridge *bridge;
bridge = drm_panel_bridge_add(out->panel);
+ drm_panel_put(out->panel);
if (IS_ERR(bridge)) {
dev_err(out->dev,
"unable to create panel bridge (%ld)\n",
diff --git a/drivers/gpu/drm/panel/Kconfig b/drivers/gpu/drm/panel/Kconfig
index cfbfb371bc67..53c0b95c26ca 100644
--- a/drivers/gpu/drm/panel/Kconfig
+++ b/drivers/gpu/drm/panel/Kconfig
@@ -662,6 +662,17 @@ config DRM_PANEL_NOVATEK_NT36523
around the Novatek NT36523 display controller, such as some
Boe panels used in Xiaomi Mi Pad 5 and 5 Pro tablets.
+config DRM_PANEL_NOVATEK_NT36536
+ tristate "Novatek NT36536 panel driver"
+ depends on OF
+ depends on DRM_MIPI_DSI
+ depends on BACKLIGHT_CLASS_DEVICE
+ select DRM_KMS_HELPER
+ help
+ Say Y here if you want to enable support for Novatek NT36536-based
+ display panels, such as the one found in the LENOVO Legion Y700
+ Gen4.
+
config DRM_PANEL_NOVATEK_NT36672A
tristate "Novatek NT36672A DSI panel"
depends on GPIOLIB
diff --git a/drivers/gpu/drm/panel/Makefile b/drivers/gpu/drm/panel/Makefile
index 0f29f22f589e..3b523cf37833 100644
--- a/drivers/gpu/drm/panel/Makefile
+++ b/drivers/gpu/drm/panel/Makefile
@@ -64,6 +64,7 @@ obj-$(CONFIG_DRM_PANEL_NOVATEK_NT35532) += panel-novatek-nt35532.o
obj-$(CONFIG_DRM_PANEL_NOVATEK_NT35560) += panel-novatek-nt35560.o
obj-$(CONFIG_DRM_PANEL_NOVATEK_NT35950) += panel-novatek-nt35950.o
obj-$(CONFIG_DRM_PANEL_NOVATEK_NT36523) += panel-novatek-nt36523.o
+obj-$(CONFIG_DRM_PANEL_NOVATEK_NT36536) += panel-novatek-nt36536.o
obj-$(CONFIG_DRM_PANEL_NOVATEK_NT36672A) += panel-novatek-nt36672a.o
obj-$(CONFIG_DRM_PANEL_NOVATEK_NT36672E) += panel-novatek-nt36672e.o
obj-$(CONFIG_DRM_PANEL_NOVATEK_NT37700F) += panel-novatek-nt37700f.o
diff --git a/drivers/gpu/drm/panel/panel-edp.c b/drivers/gpu/drm/panel/panel-edp.c
index f66474d3ef65..df7286679446 100644
--- a/drivers/gpu/drm/panel/panel-edp.c
+++ b/drivers/gpu/drm/panel/panel-edp.c
@@ -1936,7 +1936,7 @@ static const struct edp_panel_entry edp_panels[] = {
EDP_PANEL_ENTRY('A', 'U', 'O', 0x145c, &delay_200_500_e50, "B116XAB01.4"),
EDP_PANEL_ENTRY('A', 'U', 'O', 0x1999, &delay_200_500_e50, "Unknown"),
EDP_PANEL_ENTRY('A', 'U', 'O', 0x1e9b, &delay_200_500_e50, "B133UAN02.1"),
- EDP_PANEL_ENTRY('A', 'U', 'O', 0x1ea5, &delay_200_500_e50, "B116XAK01.6"),
+ EDP_PANEL_ENTRY('A', 'U', 'O', 0x1ea5, &delay_200_500_e200, "B116XAK01.6"),
EDP_PANEL_ENTRY('A', 'U', 'O', 0x203d, &delay_200_500_e50, "B140HTN02.0"),
EDP_PANEL_ENTRY('A', 'U', 'O', 0x205c, &delay_200_500_e50, "B116XAN02.0"),
EDP_PANEL_ENTRY('A', 'U', 'O', 0x208d, &delay_200_500_e50, "B140HTN02.1"),
@@ -1958,7 +1958,7 @@ static const struct edp_panel_entry edp_panels[] = {
EDP_PANEL_ENTRY('A', 'U', 'O', 0x635c, &delay_200_500_e50, "B116XAN06.3"),
EDP_PANEL_ENTRY('A', 'U', 'O', 0x639c, &delay_200_500_e50, "B140HAK02.7"),
EDP_PANEL_ENTRY('A', 'U', 'O', 0x643d, &delay_200_500_e50, "B140HAN06.4"),
- EDP_PANEL_ENTRY('A', 'U', 'O', 0x67a8, &delay_200_500_e50, "B140XTK02.4"),
+ EDP_PANEL_ENTRY('A', 'U', 'O', 0x67a8, &delay_200_500_e200, "B140XTK02.4"),
EDP_PANEL_ENTRY('A', 'U', 'O', 0x723c, &delay_200_500_e50, "B140XTN07.2"),
EDP_PANEL_ENTRY('A', 'U', 'O', 0x73aa, &delay_200_500_e50, "B116XTN02.3"),
EDP_PANEL_ENTRY('A', 'U', 'O', 0x8594, &delay_200_500_e50, "B133UAN01.0"),
diff --git a/drivers/gpu/drm/panel/panel-novatek-nt36536.c b/drivers/gpu/drm/panel/panel-novatek-nt36536.c
new file mode 100644
index 000000000000..2a82b54880c3
--- /dev/null
+++ b/drivers/gpu/drm/panel/panel-novatek-nt36536.c
@@ -0,0 +1,488 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Novatek NT36536 DriverIC panels driver
+ * Copyright (c) 2026 Pengyu Luo <mitltlatltl@gmail.com>
+ *
+ * Based on the sample code which is generated with
+ * linux-mdss-dsi-panel-driver-generator
+ */
+
+#include <linux/backlight.h>
+#include <linux/delay.h>
+#include <linux/gpio/consumer.h>
+#include <linux/device-id/of.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_graph.h>
+#include <linux/regulator/consumer.h>
+
+#include <drm/display/drm_dsc.h>
+#include <drm/display/drm_dsc_helper.h>
+#include <drm/drm_mipi_dsi.h>
+#include <drm/drm_modes.h>
+#include <drm/drm_panel.h>
+#include <drm/drm_probe_helper.h>
+
+#include <video/mipi_display.h>
+
+struct novatek {
+ struct drm_panel panel;
+ struct mipi_dsi_device *dsi[2];
+ const struct panel_desc *desc;
+ struct drm_dsc_config dsc;
+ struct gpio_desc *reset_gpio;
+ struct regulator_bulk_data *supplies;
+ struct backlight_device *backlight;
+};
+
+struct panel_desc {
+ unsigned int width_mm;
+ unsigned int height_mm;
+ unsigned int bpc;
+ unsigned int lanes;
+ enum mipi_dsi_pixel_format format;
+ unsigned long mode_flags;
+ const struct drm_dsc_config *dsc_cfg;
+ const struct drm_display_mode *modes;
+ unsigned int num_modes;
+
+ int (*init_sequence)(struct mipi_dsi_multi_context *dsi_ctx);
+
+ bool is_dual_dsi;
+ bool has_dcs_backlight;
+};
+
+static const struct regulator_bulk_data novatek_supplies[] = {
+ { .supply = "vddio" },
+ { .supply = "vsp" },
+ { .supply = "vsn" },
+};
+
+static inline struct novatek *to_novatek(struct drm_panel *panel)
+{
+ return container_of(panel, struct novatek, panel);
+}
+
+static inline struct mipi_dsi_device *to_primary_dsi(struct novatek *ctx)
+{
+ /* Sync on DSI1 for dual dsi */
+ return ctx->desc->is_dual_dsi ? ctx->dsi[1] : ctx->dsi[0];
+}
+
+static void novatek_reset(struct novatek *ctx)
+{
+ gpiod_set_value_cansleep(ctx->reset_gpio, 1);
+ usleep_range(11000, 12000);
+ gpiod_set_value_cansleep(ctx->reset_gpio, 0);
+ usleep_range(1000, 2000);
+ gpiod_set_value_cansleep(ctx->reset_gpio, 1);
+ usleep_range(3000, 4000);
+ gpiod_set_value_cansleep(ctx->reset_gpio, 0);
+ usleep_range(10000, 11000);
+}
+
+static int novatek_prepare(struct drm_panel *panel)
+{
+ struct novatek *ctx = to_novatek(panel);
+ struct mipi_dsi_device *dsi = to_primary_dsi(ctx);
+ struct mipi_dsi_multi_context dsi_ctx = { .dsi = dsi };
+ struct drm_dsc_picture_parameter_set pps;
+ struct device *dev = &dsi->dev;
+ int ret;
+
+ ret = regulator_bulk_enable(ARRAY_SIZE(novatek_supplies),
+ ctx->supplies);
+ if (ret < 0)
+ return ret;
+
+ novatek_reset(ctx);
+
+ ret = ctx->desc->init_sequence(&dsi_ctx);
+ if (ret < 0) {
+ dev_err(dev, "Failed to initialize panel: %d\n", ret);
+ gpiod_set_value_cansleep(ctx->reset_gpio, 1);
+ regulator_bulk_disable(ARRAY_SIZE(novatek_supplies),
+ ctx->supplies);
+ return ret;
+ }
+
+ drm_dsc_pps_payload_pack(&pps, &ctx->dsc);
+ mipi_dsi_picture_parameter_set_multi(&dsi_ctx, &pps);
+ mipi_dsi_compression_mode_multi(&dsi_ctx, true);
+ mipi_dsi_msleep(&dsi_ctx, 28);
+
+ return backlight_enable(ctx->backlight);
+}
+
+static int novatek_off(struct mipi_dsi_multi_context *dsi_ctx)
+{
+ mipi_dsi_dcs_set_display_off_multi(dsi_ctx);
+ mipi_dsi_dcs_enter_sleep_mode_multi(dsi_ctx);
+
+ return dsi_ctx->accum_err;
+}
+
+static int novatek_unprepare(struct drm_panel *panel)
+{
+ struct novatek *ctx = to_novatek(panel);
+ struct mipi_dsi_device *dsi = to_primary_dsi(ctx);
+ struct mipi_dsi_multi_context dsi_ctx = { .dsi = dsi };
+ struct device *dev = &dsi->dev;
+ int ret;
+
+ backlight_disable(ctx->backlight);
+
+ ret = novatek_off(&dsi_ctx);
+ if (ret < 0)
+ dev_err(dev, "Failed to un-initialize panel: %d\n", ret);
+
+ gpiod_set_value_cansleep(ctx->reset_gpio, 1);
+ regulator_bulk_disable(ARRAY_SIZE(novatek_supplies), ctx->supplies);
+
+ return 0;
+}
+
+static int novatek_get_modes(struct drm_panel *panel,
+ struct drm_connector *connector)
+{
+ struct novatek *ctx = to_novatek(panel);
+ const struct panel_desc *desc = ctx->desc;
+ int i;
+
+ for (i = 0; i < desc->num_modes; i++) {
+ const struct drm_display_mode *m = &desc->modes[i];
+ struct drm_display_mode *mode;
+
+ mode = drm_mode_duplicate(connector->dev, m);
+ if (!mode) {
+ dev_err(panel->dev, "failed to add mode %ux%u@%u\n",
+ m->hdisplay, m->vdisplay, drm_mode_vrefresh(m));
+ return -ENOMEM;
+ }
+
+ mode->type = DRM_MODE_TYPE_DRIVER;
+ if (i == 0)
+ mode->type |= DRM_MODE_TYPE_PREFERRED;
+
+ drm_mode_set_name(mode);
+ drm_mode_probed_add(connector, mode);
+ }
+
+ connector->display_info.width_mm = desc->width_mm;
+ connector->display_info.height_mm = desc->height_mm;
+ connector->display_info.bpc = desc->bpc;
+
+ return desc->num_modes;
+}
+
+static const struct drm_panel_funcs novatek_panel_funcs = {
+ .prepare = novatek_prepare,
+ .unprepare = novatek_unprepare,
+ .get_modes = novatek_get_modes,
+};
+
+static int novatek_bl_update_status(struct backlight_device *bl)
+{
+ struct novatek *ctx = bl_get_data(bl);
+ u16 brightness = backlight_get_brightness(bl);
+
+ return mipi_dsi_dcs_set_display_brightness_large(to_primary_dsi(ctx),
+ brightness);
+}
+
+static const struct backlight_ops novatek_bl_ops = {
+ .update_status = novatek_bl_update_status,
+};
+
+static struct backlight_device *novatek_create_backlight(struct novatek *ctx)
+{
+ struct mipi_dsi_device *dsi = to_primary_dsi(ctx);
+ struct device *dev = &dsi->dev;
+ const struct backlight_properties props = {
+ .type = BACKLIGHT_RAW,
+ .brightness = 512,
+ .max_brightness = 4095,
+ .scale = BACKLIGHT_SCALE_NON_LINEAR,
+ };
+
+ return devm_backlight_device_register(dev, dev_name(dev), dev, ctx,
+ &novatek_bl_ops, &props);
+}
+
+static int csot_pp8807hb1_1_init_seq(struct mipi_dsi_multi_context *dsi_ctx)
+{
+ mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0xff, 0x20);
+ mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0xfb, 0x01);
+ mipi_dsi_dcs_write_seq_multi(dsi_ctx, MIPI_DCS_SET_PARTIAL_ROWS, 0x50);
+
+ /* cabc */
+ mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0xff, 0x23);
+ mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0xfb, 0x01);
+ mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0x89, 0xa4);
+
+ /* pen code */
+ mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0xff, 0x26);
+ mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0xfb, 0x01);
+ mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0xcd, 0x8f, 0xa9, 0x00);
+ mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0xce, 0x8c, 0xa9, 0x00);
+
+ /* esd init */
+ mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0xff, 0x27);
+ mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0xfb, 0x01);
+ mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0x79, 0x22);
+ mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0xd0, 0x31);
+ mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0xd1, 0x08, 0x08);
+ mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0xd2, 0x08);
+
+ mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0xff, 0xd0);
+ mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0xfb, 0x01);
+ mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0x00, 0x31);
+ mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0x09, 0xee);
+ mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0x1c, 0x77);
+ mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0x1d, 0x07);
+
+ mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0xff, 0xe0);
+ mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0xfb, 0x01);
+ mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0xbb, 0x00);
+
+ /* IC transfer */
+ mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0xff, 0xf0);
+ mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0xfb, 0x01);
+ mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0xe6, 0x02);
+
+ mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0xff, 0x10);
+ mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0xfb, 0x01);
+ mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0x3b,
+ 0x03, 0x4e, 0x1a, 0x04, 0x04, 0x01, 0x80,
+ 0x36, 0x36);
+ mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0x60, 0x00);
+ /* enable DSC */
+ mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0x90, 0x03, 0x00);
+ mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0x91,
+ 0xab, 0xa8, 0x00, 0x14, 0xd2, 0x00, 0x00,
+ 0x00, 0x01, 0xb9, 0x00, 0x06, 0x05, 0x7a,
+ 0x05, 0xb8);
+ mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0x92, 0x10, 0xe0);
+ /* frame ctl */
+ mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0xb2, 0x91, 0x80);
+ mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0xb3, 0x40);
+ mipi_dsi_dcs_exit_sleep_mode_multi(dsi_ctx);
+ mipi_dsi_msleep(dsi_ctx, 105);
+ mipi_dsi_dcs_set_display_on_multi(dsi_ctx);
+ mipi_dsi_msleep(dsi_ctx, 40);
+
+ return dsi_ctx->accum_err;
+}
+
+static struct drm_dsc_config csot_pp8807hb1_1_dsc_cfg = {
+ .dsc_version_major = 1,
+ .dsc_version_minor = 2,
+ .slice_height = 20,
+ .slice_width = 476,
+ .slice_count = 2,
+ .bits_per_component = 10,
+ .bits_per_pixel = 8 << 4,
+ .block_pred_enable = true,
+};
+
+static const struct drm_display_mode csot_pp8807hb1_1_modes[] = {
+ /* 915552 KHz */
+ {
+ .clock = (952 + 138 + 16 + 16) * 2 * (3040 + 26 + 4 + 330) * 120 / 1000,
+ .hdisplay = 952 * 2,
+ .hsync_start = (952 + 138) * 2,
+ .hsync_end = (952 + 138 + 16) * 2,
+ .htotal = (952 + 138 + 16 + 16) * 2,
+ .vdisplay = 3040,
+ .vsync_start = 3040 + 26,
+ .vsync_end = 3040 + 26 + 4,
+ .vtotal = 3040 + 26 + 4 + 330,
+ },
+ {
+ .clock = (952 + 138 + 16 + 16) * 2 * (3040 + 3426 + 4 + 330) * 60 / 1000,
+ .hdisplay = 952 * 2,
+ .hsync_start = (952 + 138) * 2,
+ .hsync_end = (952 + 138 + 16) * 2,
+ .htotal = (952 + 138 + 16 + 16) * 2,
+ .vdisplay = 3040,
+ .vsync_start = 3040 + 3426,
+ .vsync_end = 3040 + 3426 + 4,
+ .vtotal = 3040 + 3426 + 4 + 330,
+ },
+ {
+ .clock = (952 + 138 + 16 + 16) * 2 * (3040 + 10226 + 4 + 330) * 30 / 1000,
+ .hdisplay = 952 * 2,
+ .hsync_start = (952 + 138) * 2,
+ .hsync_end = (952 + 138 + 16) * 2,
+ .htotal = (952 + 138 + 16 + 16) * 2,
+ .vdisplay = 3040,
+ .vsync_start = 3040 + 10226,
+ .vsync_end = 3040 + 10226 + 4,
+ .vtotal = 3040 + 10226 + 4 + 330,
+ },
+ /* 1064606.4 KHz */
+ {
+ .clock = (952 + 50 + 16 + 16) * 2 * (3040 + 26 + 4 + 50) * 165 / 1000,
+ .hdisplay = 952 * 2,
+ .hsync_start = (952 + 50) * 2,
+ .hsync_end = (952 + 50 + 16) * 2,
+ .htotal = (952 + 50 + 16 + 16) * 2,
+ .vdisplay = 3040,
+ .vsync_start = 3040 + 26,
+ .vsync_end = 3040 + 26 + 4,
+ .vtotal = 3040 + 26 + 4 + 50,
+ },
+ {
+ .clock = (952 + 50 + 16 + 16) * 2 * (3040 + 481 + 4 + 50) * 144 / 1000,
+ .hdisplay = 952 * 2,
+ .hsync_start = (952 + 50) * 2,
+ .hsync_end = (952 + 50 + 16) * 2,
+ .htotal = (952 + 50 + 16 + 16) * 2,
+ .vdisplay = 3040,
+ .vsync_start = 3040 + 481,
+ .vsync_end = 3040 + 481 + 4,
+ .vtotal = 3040 + 481 + 4 + 50,
+ },
+ /* 737942.4 KHz */
+ {
+ .clock = (952 + 330 + 16 + 16) * 2 * (3040 + 26 + 4 + 50) * 90 / 1000,
+ .hdisplay = 952 * 2,
+ .hsync_start = (952 + 330) * 2,
+ .hsync_end = (952 + 330 + 16) * 2,
+ .htotal = (952 + 330 + 16 + 16) * 2,
+ .vdisplay = 3040,
+ .vsync_start = 3040 + 26,
+ .vsync_end = 3040 + 26 + 4,
+ .vtotal = 3040 + 26 + 4 + 50,
+ },
+};
+
+static int novatek_probe(struct mipi_dsi_device *dsi)
+{
+ struct mipi_dsi_device_info dsi_info = {"dsi-secondary", 0, NULL};
+ struct mipi_dsi_host *dsi1_host;
+ struct device *dev = &dsi->dev;
+ const struct panel_desc *desc;
+ struct device_node *dsi1;
+ struct novatek *ctx;
+ int num_dsi = 1;
+ int ret, i;
+
+ ctx = devm_drm_panel_alloc(dev, struct novatek, panel,
+ &novatek_panel_funcs,
+ DRM_MODE_CONNECTOR_DSI);
+ if (IS_ERR(ctx))
+ return PTR_ERR(ctx);
+
+ ret = devm_regulator_bulk_get_const(dev, ARRAY_SIZE(novatek_supplies),
+ novatek_supplies, &ctx->supplies);
+ if (ret < 0)
+ return ret;
+
+ ctx->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_HIGH);
+ if (IS_ERR(ctx->reset_gpio))
+ return dev_err_probe(dev, PTR_ERR(ctx->reset_gpio),
+ "Failed to get reset-gpios\n");
+
+ desc = of_device_get_match_data(dev);
+ if (!desc)
+ return -ENODEV;
+ ctx->desc = desc;
+ ctx->dsc = *desc->dsc_cfg;
+
+ if (desc->is_dual_dsi) {
+ num_dsi = 2;
+ dsi1 = of_graph_get_remote_node(dsi->dev.of_node, 1, -1);
+ if (!dsi1) {
+ dev_err(dev, "cannot get secondary DSI node.\n");
+ return -ENODEV;
+ }
+
+ dsi1_host = of_find_mipi_dsi_host_by_node(dsi1);
+ of_node_put(dsi1);
+ if (!dsi1_host)
+ return dev_err_probe(dev, -EPROBE_DEFER,
+ "cannot get secondary DSI host\n");
+
+ ctx->dsi[1] = devm_mipi_dsi_device_register_full(dev, dsi1_host,
+ &dsi_info);
+ if (IS_ERR(ctx->dsi[1])) {
+ dev_err(dev, "cannot get secondary DSI device\n");
+ return PTR_ERR(ctx->dsi[1]);
+ }
+
+ mipi_dsi_set_drvdata(ctx->dsi[1], ctx);
+ }
+
+ ctx->dsi[0] = dsi;
+ mipi_dsi_set_drvdata(dsi, ctx);
+
+ ctx->panel.prepare_prev_first = true;
+
+ ret = devm_drm_panel_add(dev, &ctx->panel);
+ if (ret < 0)
+ return dev_err_probe(dev, ret, "failed to add panel\n");
+
+ for (i = 0; i < num_dsi; i++) {
+ ctx->dsi[i]->lanes = desc->lanes;
+ ctx->dsi[i]->format = desc->format;
+ ctx->dsi[i]->mode_flags = desc->mode_flags;
+ ctx->dsi[i]->dsc = &ctx->dsc;
+ ret = devm_mipi_dsi_attach(dev, ctx->dsi[i]);
+ if (ret < 0) {
+ drm_panel_remove(&ctx->panel);
+ return dev_err_probe(dev, ret,
+ "Failed to attach to DSI host\n");
+ }
+ }
+
+ if (desc->has_dcs_backlight) {
+ ctx->backlight = novatek_create_backlight(ctx);
+ if (IS_ERR(ctx->backlight))
+ return dev_err_probe(dev, PTR_ERR(ctx->backlight),
+ "Failed to create backlight\n");
+ } else {
+ ret = drm_panel_of_backlight(&ctx->panel);
+ if (ret)
+ return dev_err_probe(dev, ret, "Failed to get backlight\n");
+ }
+
+ return 0;
+}
+
+/* Model name: CSOT PP8807HB1-1 */
+static const struct panel_desc csot_pp8807hb1_1_desc = {
+ .width_mm = 118,
+ .height_mm = 190,
+ .bpc = 10, /* set this to 8 with RGB888 format to support 8-bit mode */
+ .lanes = 3,
+ .format = MIPI_DSI_FMT_RGB101010,
+ .mode_flags = MIPI_DSI_MODE_VIDEO | MIPI_DSI_CLOCK_NON_CONTINUOUS |
+ MIPI_DSI_MODE_NO_EOT_PACKET | MIPI_DSI_MODE_VIDEO_BURST |
+ MIPI_DSI_MODE_LPM | MIPI_DSI_MODE_DSC_ALL_SLICES_IN_PKT,
+ .dsc_cfg = &csot_pp8807hb1_1_dsc_cfg,
+ .modes = csot_pp8807hb1_1_modes,
+ .num_modes = ARRAY_SIZE(csot_pp8807hb1_1_modes),
+ .init_sequence = csot_pp8807hb1_1_init_seq,
+ .is_dual_dsi = true,
+ .has_dcs_backlight = false,
+};
+
+static const struct of_device_id novatek_of_match[] = {
+ { .compatible = "csot,pp8807hb1-1", .data = &csot_pp8807hb1_1_desc },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, novatek_of_match);
+
+static struct mipi_dsi_driver novatek_driver = {
+ .probe = novatek_probe,
+ .driver = {
+ .name = "panel-novatek-nt36536",
+ .of_match_table = novatek_of_match,
+ },
+};
+module_mipi_dsi_driver(novatek_driver);
+
+MODULE_AUTHOR("Pengyu Luo <mitltlatltl@gmail.com>");
+MODULE_DESCRIPTION("Novatek NT36536 DriverIC panels driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/gpu/drm/panel/panel-osd-osd101t2587-53ts.c b/drivers/gpu/drm/panel/panel-osd-osd101t2587-53ts.c
index 2334b77f348c..63ae9dc5712f 100644
--- a/drivers/gpu/drm/panel/panel-osd-osd101t2587-53ts.c
+++ b/drivers/gpu/drm/panel/panel-osd-osd101t2587-53ts.c
@@ -32,11 +32,11 @@ static inline struct osd101t2587_panel *ti_osd_panel(struct drm_panel *panel)
static int osd101t2587_panel_disable(struct drm_panel *panel)
{
struct osd101t2587_panel *osd101t2587 = ti_osd_panel(panel);
- int ret;
+ struct mipi_dsi_multi_context ctx = { .dsi = osd101t2587->dsi };
- ret = mipi_dsi_shutdown_peripheral(osd101t2587->dsi);
+ mipi_dsi_shutdown_peripheral_multi(&ctx);
- return ret;
+ return ctx.accum_err;
}
static int osd101t2587_panel_unprepare(struct drm_panel *panel)
@@ -58,13 +58,11 @@ static int osd101t2587_panel_prepare(struct drm_panel *panel)
static int osd101t2587_panel_enable(struct drm_panel *panel)
{
struct osd101t2587_panel *osd101t2587 = ti_osd_panel(panel);
- int ret;
+ struct mipi_dsi_multi_context ctx = { .dsi = osd101t2587->dsi };
- ret = mipi_dsi_turn_on_peripheral(osd101t2587->dsi);
- if (ret)
- return ret;
+ mipi_dsi_turn_on_peripheral_multi(&ctx);
- return ret;
+ return ctx.accum_err;
}
static const struct drm_display_mode default_mode_osd101t2587 = {
diff --git a/drivers/gpu/drm/panel/panel-samsung-sofef00.c b/drivers/gpu/drm/panel/panel-samsung-sofef00.c
index e00a497a7c96..f07d6533ed69 100644
--- a/drivers/gpu/drm/panel/panel-samsung-sofef00.c
+++ b/drivers/gpu/drm/panel/panel-samsung-sofef00.c
@@ -175,9 +175,13 @@ static const struct drm_panel_funcs sofef00_panel_panel_funcs = {
static int sofef00_panel_bl_update_status(struct backlight_device *bl)
{
struct mipi_dsi_device *dsi = bl_get_data(bl);
+ struct sofef00_panel *ctx = mipi_dsi_get_drvdata(dsi);
int err;
u16 brightness = (u16)backlight_get_brightness(bl);
+ if (!ctx->panel.prepared)
+ return 0;
+
dsi->mode_flags &= ~MIPI_DSI_MODE_LPM;
err = mipi_dsi_dcs_set_display_brightness_large(dsi, brightness);
diff --git a/drivers/gpu/drm/panel/panel-simple.c b/drivers/gpu/drm/panel/panel-simple.c
index 5c169fdf265c..292212119fda 100644
--- a/drivers/gpu/drm/panel/panel-simple.c
+++ b/drivers/gpu/drm/panel/panel-simple.c
@@ -5910,6 +5910,9 @@ static const struct of_device_id platform_of_match[] = {
.compatible = "koe,tx31d200vm0baa",
.data = &koe_tx31d200vm0baa,
}, {
+ .compatible = "kyo,tcg070wvlq",
+ .data = &lg_lb070wv8,
+ }, {
.compatible = "kyo,tcg121xglp",
.data = &kyo_tcg121xglp,
}, {
diff --git a/drivers/gpu/drm/panfrost/panfrost_device.c b/drivers/gpu/drm/panfrost/panfrost_device.c
index 7fed22d555a5..d0a23c51be39 100644
--- a/drivers/gpu/drm/panfrost/panfrost_device.c
+++ b/drivers/gpu/drm/panfrost/panfrost_device.c
@@ -218,7 +218,6 @@ int panfrost_device_init(struct panfrost_device *pfdev)
int err;
mutex_init(&pfdev->sched_lock);
- INIT_LIST_HEAD(&pfdev->scheduled_jobs);
INIT_LIST_HEAD(&pfdev->as_lru_list);
spin_lock_init(&pfdev->as_lock);
diff --git a/drivers/gpu/drm/panfrost/panfrost_device.h b/drivers/gpu/drm/panfrost/panfrost_device.h
index ec55c136b1b6..1fe1554f2652 100644
--- a/drivers/gpu/drm/panfrost/panfrost_device.h
+++ b/drivers/gpu/drm/panfrost/panfrost_device.h
@@ -156,7 +156,6 @@ struct panfrost_device {
struct panfrost_job_slot *js;
struct panfrost_job *jobs[NUM_JOB_SLOTS][2];
- struct list_head scheduled_jobs;
struct panfrost_perfcnt *perfcnt;
bool profile_mode;
diff --git a/drivers/gpu/drm/pl111/pl111_drv.c b/drivers/gpu/drm/pl111/pl111_drv.c
index ac7b1d12a0f5..8ec659b3c08e 100644
--- a/drivers/gpu/drm/pl111/pl111_drv.c
+++ b/drivers/gpu/drm/pl111/pl111_drv.c
@@ -145,6 +145,7 @@ static int pl111_modeset_init(struct drm_device *dev)
if (panel) {
bridge = drm_panel_bridge_add_typed(panel,
DRM_MODE_CONNECTOR_Unknown);
+ drm_panel_put(panel);
if (IS_ERR(bridge)) {
ret = PTR_ERR(bridge);
goto finish;
diff --git a/drivers/gpu/drm/renesas/rcar-du/rcar_du_encoder.c b/drivers/gpu/drm/renesas/rcar-du/rcar_du_encoder.c
index db2088529b48..d8e7e9877ba8 100644
--- a/drivers/gpu/drm/renesas/rcar-du/rcar_du_encoder.c
+++ b/drivers/gpu/drm/renesas/rcar-du/rcar_du_encoder.c
@@ -69,6 +69,7 @@ int rcar_du_encoder_init(struct rcar_du_device *rcdu,
bridge = devm_drm_panel_bridge_add_typed(rcdu->dev, panel,
DRM_MODE_CONNECTOR_DPI);
+ drm_panel_put(panel);
if (IS_ERR(bridge))
return PTR_ERR(no_free_ptr(bridge));
diff --git a/drivers/gpu/drm/renesas/rcar-du/rcar_lvds.c b/drivers/gpu/drm/renesas/rcar-du/rcar_lvds.c
index e433ce61d431..9527d3637e6c 100644
--- a/drivers/gpu/drm/renesas/rcar-du/rcar_lvds.c
+++ b/drivers/gpu/drm/renesas/rcar-du/rcar_lvds.c
@@ -791,6 +791,7 @@ static int rcar_lvds_parse_dt(struct rcar_lvds *lvds)
if (lvds->panel) {
lvds->next_bridge = devm_drm_panel_bridge_add(lvds->dev,
lvds->panel);
+ drm_panel_put(lvds->panel);
if (IS_ERR_OR_NULL(lvds->next_bridge)) {
ret = -EINVAL;
goto done;
diff --git a/drivers/gpu/drm/renesas/rz-du/rzg2l_du_encoder.c b/drivers/gpu/drm/renesas/rz-du/rzg2l_du_encoder.c
index f50d166b764f..3d0999e4fcfd 100644
--- a/drivers/gpu/drm/renesas/rz-du/rzg2l_du_encoder.c
+++ b/drivers/gpu/drm/renesas/rz-du/rzg2l_du_encoder.c
@@ -90,6 +90,7 @@ int rzg2l_du_encoder_init(struct rzg2l_du_device *rcdu,
bridge = devm_drm_panel_bridge_add_typed(rcdu->dev, panel,
DRM_MODE_CONNECTOR_DPI);
+ drm_panel_put(panel);
if (IS_ERR(bridge))
return PTR_ERR(no_free_ptr(bridge));
diff --git a/drivers/gpu/drm/rockchip/analogix_dp-rockchip.c b/drivers/gpu/drm/rockchip/analogix_dp-rockchip.c
index 46c245e35d21..587e60232ec7 100644
--- a/drivers/gpu/drm/rockchip/analogix_dp-rockchip.c
+++ b/drivers/gpu/drm/rockchip/analogix_dp-rockchip.c
@@ -28,6 +28,7 @@
#include <drm/drm_bridge_connector.h>
#include <drm/bridge/analogix_dp.h>
#include <drm/drm_of.h>
+#include <drm/drm_panel.h>
#include <drm/drm_print.h>
#include <drm/drm_probe_helper.h>
@@ -485,6 +486,16 @@ static int rockchip_dp_probe(struct platform_device *pdev)
static void rockchip_dp_remove(struct platform_device *pdev)
{
+ struct rockchip_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, &rockchip_dp_component_ops);
}
diff --git a/drivers/gpu/drm/rockchip/rockchip_lvds.c b/drivers/gpu/drm/rockchip/rockchip_lvds.c
index 262f81875278..505c6c56736d 100644
--- a/drivers/gpu/drm/rockchip/rockchip_lvds.c
+++ b/drivers/gpu/drm/rockchip/rockchip_lvds.c
@@ -610,6 +610,8 @@ static int rockchip_lvds_bind(struct device *dev, struct device *master,
if (lvds->panel) {
lvds->bridge = drm_panel_bridge_add_typed(lvds->panel, DRM_MODE_CONNECTOR_LVDS);
+ drm_panel_put(lvds->panel);
+ lvds->panel = NULL;
if (IS_ERR(lvds->bridge)) {
ret = PTR_ERR(lvds->bridge);
goto err_free_encoder;
@@ -646,6 +648,8 @@ err_free_bridge:
err_free_encoder:
drm_encoder_cleanup(encoder);
err_put_remote:
+ if (lvds->panel)
+ drm_panel_put(lvds->panel);
of_node_put(remote);
err_put_port:
of_node_put(port);
diff --git a/drivers/gpu/drm/rockchip/rockchip_rgb.c b/drivers/gpu/drm/rockchip/rockchip_rgb.c
index 2ad24b914989..0e1830bc40ee 100644
--- a/drivers/gpu/drm/rockchip/rockchip_rgb.c
+++ b/drivers/gpu/drm/rockchip/rockchip_rgb.c
@@ -135,6 +135,8 @@ struct rockchip_rgb *rockchip_rgb_init(struct device *dev,
if (ret < 0) {
DRM_DEV_ERROR(drm_dev->dev,
"failed to initialize encoder: %d\n", ret);
+ if (panel)
+ drm_panel_put(panel);
return ERR_PTR(ret);
}
@@ -143,6 +145,7 @@ struct rockchip_rgb *rockchip_rgb_init(struct device *dev,
if (panel) {
bridge = drm_panel_bridge_add_typed(panel,
DRM_MODE_CONNECTOR_LVDS);
+ drm_panel_put(panel);
if (IS_ERR(bridge))
return ERR_CAST(bridge);
}
diff --git a/drivers/gpu/drm/solomon/ssd130x.c b/drivers/gpu/drm/solomon/ssd130x.c
index 4907be694aec..0940eae7a2e4 100644
--- a/drivers/gpu/drm/solomon/ssd130x.c
+++ b/drivers/gpu/drm/solomon/ssd130x.c
@@ -897,7 +897,6 @@ static int ssd130x_fb_blit_rect(struct drm_framebuffer *fb,
struct ssd130x_device *ssd130x = drm_to_ssd130x(fb->dev);
struct iosys_map dst;
unsigned int dst_pitch;
- int ret = 0;
/* Align y to display page boundaries */
rect->y1 = round_down(rect->y1, SSD130X_PAGE_HEIGHT);
@@ -910,7 +909,7 @@ static int ssd130x_fb_blit_rect(struct drm_framebuffer *fb,
ssd130x_update_rect(ssd130x, rect, buf, data_array);
- return ret;
+ return 0;
}
static int ssd132x_fb_blit_rect(struct drm_framebuffer *fb,
@@ -922,7 +921,6 @@ static int ssd132x_fb_blit_rect(struct drm_framebuffer *fb,
struct ssd130x_device *ssd130x = drm_to_ssd130x(fb->dev);
unsigned int dst_pitch;
struct iosys_map dst;
- int ret = 0;
/* Align x to display segment boundaries */
rect->x1 = round_down(rect->x1, SSD132X_SEGMENT_WIDTH);
@@ -936,7 +934,7 @@ static int ssd132x_fb_blit_rect(struct drm_framebuffer *fb,
ssd132x_update_rect(ssd130x, rect, buf, data_array);
- return ret;
+ return 0;
}
static int ssd133x_fb_blit_rect(struct drm_framebuffer *fb,
@@ -948,7 +946,6 @@ static int ssd133x_fb_blit_rect(struct drm_framebuffer *fb,
const struct drm_format_info *fi = drm_format_info(DRM_FORMAT_RGB332);
unsigned int dst_pitch;
struct iosys_map dst;
- int ret = 0;
if (!fi)
return -EINVAL;
@@ -960,7 +957,7 @@ static int ssd133x_fb_blit_rect(struct drm_framebuffer *fb,
ssd133x_update_rect(ssd130x, rect, data_array, dst_pitch);
- return ret;
+ return 0;
}
static int ssd130x_primary_plane_atomic_check(struct drm_plane *plane,
diff --git a/drivers/gpu/drm/sti/sti_dvo.c b/drivers/gpu/drm/sti/sti_dvo.c
index b48099468eb9..cdb19b75f5e1 100644
--- a/drivers/gpu/drm/sti/sti_dvo.c
+++ b/drivers/gpu/drm/sti/sti_dvo.c
@@ -498,6 +498,9 @@ static void sti_dvo_unbind(struct device *dev,
{
struct sti_dvo *dvo = dev_get_drvdata(dev);
+ if (dvo->panel)
+ drm_panel_put(dvo->panel);
+
drm_bridge_remove(&dvo->bridge);
}
diff --git a/drivers/gpu/drm/stm/ltdc.c b/drivers/gpu/drm/stm/ltdc.c
index 95fcfa48d8be..8818dcf98da7 100644
--- a/drivers/gpu/drm/stm/ltdc.c
+++ b/drivers/gpu/drm/stm/ltdc.c
@@ -34,6 +34,7 @@
#include <drm/drm_gem_atomic_helper.h>
#include <drm/drm_gem_dma_helper.h>
#include <drm/drm_of.h>
+#include <drm/drm_panel.h>
#include <drm/drm_print.h>
#include <drm/drm_probe_helper.h>
#include <drm/drm_simple_kms_helper.h>
@@ -1982,6 +1983,7 @@ int ltdc_load(struct drm_device *ddev)
if (panel) {
bridge = drmm_panel_bridge_add(ddev, panel);
+ drm_panel_put(panel);
if (IS_ERR(bridge)) {
drm_err(ddev, "panel-bridge endpoint %d\n", i);
ret = PTR_ERR(bridge);
diff --git a/drivers/gpu/drm/stm/lvds.c b/drivers/gpu/drm/stm/lvds.c
index 90a44e722057..1072dbe7bd6a 100644
--- a/drivers/gpu/drm/stm/lvds.c
+++ b/drivers/gpu/drm/stm/lvds.c
@@ -1068,20 +1068,20 @@ static int lvds_probe(struct platform_device *pdev)
if (IS_ERR(lvds->base)) {
ret = PTR_ERR(lvds->base);
dev_err(dev, "Unable to get regs %d\n", ret);
- return ret;
+ goto err_put_panel;
}
lvds->pclk = devm_clk_get(dev, "pclk");
if (IS_ERR(lvds->pclk)) {
ret = PTR_ERR(lvds->pclk);
dev_err(dev, "Unable to get peripheral clock: %d\n", ret);
- return ret;
+ goto err_put_panel;
}
ret = clk_prepare_enable(lvds->pclk);
if (ret) {
dev_err(dev, "%s: Failed to enable peripheral clk\n", __func__);
- return ret;
+ goto err_put_panel;
}
rstc = devm_reset_control_get_exclusive(dev, NULL);
@@ -1181,6 +1181,9 @@ static int lvds_probe(struct platform_device *pdev)
err_lvds_probe:
clk_disable_unprepare(lvds->pclk);
+err_put_panel:
+ if (lvds->panel)
+ drm_panel_put(lvds->panel);
return ret;
}
@@ -1189,6 +1192,9 @@ static void lvds_remove(struct platform_device *pdev)
{
struct stm_lvds *lvds = platform_get_drvdata(pdev);
+ if (lvds->panel)
+ drm_panel_put(lvds->panel);
+
lvds_pixel_clk_unregister(lvds);
drm_bridge_remove(&lvds->lvds_bridge);
diff --git a/drivers/gpu/drm/sun4i/sun4i_lvds.c b/drivers/gpu/drm/sun4i/sun4i_lvds.c
index 35a3f987c37a..6b98d20a8890 100644
--- a/drivers/gpu/drm/sun4i/sun4i_lvds.c
+++ b/drivers/gpu/drm/sun4i/sun4i_lvds.c
@@ -18,6 +18,11 @@
#include "sun4i_tcon.h"
#include "sun4i_lvds.h"
+static void sun4i_panel_put_action(void *data)
+{
+ drm_panel_put(data);
+}
+
struct sun4i_lvds {
struct drm_connector connector;
struct drm_encoder encoder;
@@ -117,6 +122,14 @@ int sun4i_lvds_init(struct drm_device *drm, struct sun4i_tcon *tcon)
return 0;
}
+ if (lvds->panel) {
+ ret = devm_add_action_or_reset(tcon->dev,
+ sun4i_panel_put_action,
+ lvds->panel);
+ if (ret)
+ return ret;
+ }
+
drm_encoder_helper_add(&lvds->encoder,
&sun4i_lvds_enc_helper_funcs);
ret = drm_encoder_init(drm, &lvds->encoder, &sun4i_lvds_enc_funcs,
diff --git a/drivers/gpu/drm/sun4i/sun4i_rgb.c b/drivers/gpu/drm/sun4i/sun4i_rgb.c
index 9c3fbf1b949e..3da9a9283c6e 100644
--- a/drivers/gpu/drm/sun4i/sun4i_rgb.c
+++ b/drivers/gpu/drm/sun4i/sun4i_rgb.c
@@ -43,6 +43,11 @@ drm_encoder_to_sun4i_rgb(struct drm_encoder *encoder)
encoder);
}
+static void sun4i_panel_put_action(void *data)
+{
+ drm_panel_put(data);
+}
+
static int sun4i_rgb_get_modes(struct drm_connector *connector)
{
struct sun4i_rgb *rgb =
@@ -209,6 +214,14 @@ int sun4i_rgb_init(struct drm_device *drm, struct sun4i_tcon *tcon)
return 0;
}
+ if (rgb->panel) {
+ ret = devm_add_action_or_reset(tcon->dev,
+ sun4i_panel_put_action,
+ rgb->panel);
+ if (ret)
+ return ret;
+ }
+
drm_encoder_helper_add(&rgb->encoder,
&sun4i_rgb_enc_helper_funcs);
ret = drm_encoder_init(drm, &rgb->encoder, &sun4i_rgb_enc_funcs,
diff --git a/drivers/gpu/drm/sun4i/sun4i_tcon.c b/drivers/gpu/drm/sun4i/sun4i_tcon.c
index 960e83c8291d..d4c1723c5e3d 100644
--- a/drivers/gpu/drm/sun4i/sun4i_tcon.c
+++ b/drivers/gpu/drm/sun4i/sun4i_tcon.c
@@ -1326,6 +1326,8 @@ static int sun4i_tcon_probe(struct platform_device *pdev)
ret = drm_of_find_panel_or_bridge(node, 1, 0, &panel, &bridge);
if (ret == -EPROBE_DEFER)
return ret;
+ if (panel)
+ drm_panel_put(panel);
}
return component_add(&pdev->dev, &sun4i_tcon_ops);
diff --git a/drivers/gpu/drm/sun4i/sun6i_mipi_dsi.c b/drivers/gpu/drm/sun4i/sun6i_mipi_dsi.c
index f0c9f0e573d2..d504ae583294 100644
--- a/drivers/gpu/drm/sun4i/sun6i_mipi_dsi.c
+++ b/drivers/gpu/drm/sun4i/sun6i_mipi_dsi.c
@@ -971,8 +971,10 @@ static int sun6i_dsi_attach(struct mipi_dsi_host *host,
if (IS_ERR(panel))
return PTR_ERR(panel);
- if (!dsi->drm || !dsi->drm->registered)
+ if (!dsi->drm || !dsi->drm->registered) {
+ drm_panel_put(panel);
return -EPROBE_DEFER;
+ }
dsi->panel = panel;
dsi->device = device;
@@ -989,6 +991,8 @@ static int sun6i_dsi_detach(struct mipi_dsi_host *host,
{
struct sun6i_dsi *dsi = host_to_sun6i_dsi(host);
+ if (dsi->panel)
+ drm_panel_put(dsi->panel);
dsi->panel = NULL;
dsi->device = NULL;
diff --git a/drivers/gpu/drm/tegra/drm.c b/drivers/gpu/drm/tegra/drm.c
index 24e8ef261836..324480c87fb1 100644
--- a/drivers/gpu/drm/tegra/drm.c
+++ b/drivers/gpu/drm/tegra/drm.c
@@ -1398,6 +1398,7 @@ static const struct of_device_id host1x_drm_subdevs[] = {
{ .compatible = "nvidia,tegra194-nvdec", },
{ .compatible = "nvidia,tegra234-vic", },
{ .compatible = "nvidia,tegra234-nvdec", },
+ { .compatible = "nvidia,tegra264-vic", },
{ /* sentinel */ }
};
diff --git a/drivers/gpu/drm/tegra/dsi.c b/drivers/gpu/drm/tegra/dsi.c
index e7fdd8c7ac12..cb88aafbd36f 100644
--- a/drivers/gpu/drm/tegra/dsi.c
+++ b/drivers/gpu/drm/tegra/dsi.c
@@ -1527,6 +1527,10 @@ static int tegra_dsi_host_attach(struct mipi_dsi_host *host,
if (!dsi->master) {
struct tegra_output *output = &dsi->output;
+ /*
+ * tegra_output_probe() never populates a panel for DSI
+ * outputs, so output->panel is always NULL here.
+ */
output->panel = of_drm_find_panel(device->dev.of_node);
if (IS_ERR(output->panel))
output->panel = NULL;
@@ -1545,6 +1549,7 @@ static int tegra_dsi_host_detach(struct mipi_dsi_host *host,
struct tegra_output *output = &dsi->output;
if (output->panel && &device->dev == output->panel->dev) {
+ drm_panel_put(output->panel);
output->panel = NULL;
if (output->connector.dev)
diff --git a/drivers/gpu/drm/tegra/falcon.c b/drivers/gpu/drm/tegra/falcon.c
index 17f616bbcb45..1172356b6af3 100644
--- a/drivers/gpu/drm/tegra/falcon.c
+++ b/drivers/gpu/drm/tegra/falcon.c
@@ -26,8 +26,12 @@ int falcon_wait_idle(struct falcon *falcon)
{
u32 value;
- return readl_poll_timeout(falcon->regs + FALCON_IDLESTATE, value,
- (value == 0), 10, 100000);
+ if (falcon->riscv)
+ return readl_poll_timeout(falcon->regs + RISCV_CPUCTL, value,
+ (value & RISCV_CPUCTL_ACTIVE_STAT_ACTIVE), 10, 100000);
+ else
+ return readl_poll_timeout(falcon->regs + FALCON_IDLESTATE, value,
+ (value == 0), 10, 100000);
}
static int falcon_dma_wait_not_full(struct falcon *falcon)
@@ -122,6 +126,17 @@ static int falcon_parse_firmware_image(struct falcon *falcon)
return 0;
}
+static void falcon_parse_firmware_desc(struct falcon *falcon)
+{
+ struct falcon_fw_riscv_desc *desc =
+ (struct falcon_fw_riscv_desc *)falcon->firmware.desc_firmware->data;
+
+ falcon->firmware.code.offset = desc->code_offset;
+ falcon->firmware.code.size = desc->code_size;
+ falcon->firmware.data.offset = desc->data_offset;
+ falcon->firmware.data.size = desc->data_size;
+}
+
int falcon_read_firmware(struct falcon *falcon, const char *name)
{
int err;
@@ -133,7 +148,23 @@ int falcon_read_firmware(struct falcon *falcon, const char *name)
falcon->firmware.size = falcon->firmware.firmware->size;
+ if (falcon->riscv) {
+ /* Load separate descriptor */
+ char desc_name[128];
+
+ scnprintf(desc_name, sizeof(desc_name), "%s.desc", name);
+ err = request_firmware(&falcon->firmware.desc_firmware, desc_name, falcon->dev);
+ if (err < 0)
+ goto release_firmware;
+ }
+
return 0;
+
+release_firmware:
+ release_firmware(falcon->firmware.firmware);
+ falcon->firmware.firmware = NULL;
+
+ return err;
}
int falcon_load_firmware(struct falcon *falcon)
@@ -144,16 +175,22 @@ int falcon_load_firmware(struct falcon *falcon)
/* copy firmware image into local area. this also ensures endianness */
falcon_copy_firmware_image(falcon, firmware);
- /* parse the image data */
- err = falcon_parse_firmware_image(falcon);
- if (err < 0) {
- dev_err(falcon->dev, "failed to parse firmware image\n");
- return err;
+ if (falcon->riscv) {
+ falcon_parse_firmware_desc(falcon);
+ } else {
+ err = falcon_parse_firmware_image(falcon);
+ if (err < 0) {
+ dev_err(falcon->dev, "failed to parse firmware image\n");
+ return err;
+ }
}
release_firmware(firmware);
falcon->firmware.firmware = NULL;
+ release_firmware(falcon->firmware.desc_firmware);
+ falcon->firmware.desc_firmware = NULL;
+
return 0;
}
@@ -168,6 +205,9 @@ void falcon_exit(struct falcon *falcon)
{
if (falcon->firmware.firmware)
release_firmware(falcon->firmware.firmware);
+
+ if (falcon->firmware.desc_firmware)
+ release_firmware(falcon->firmware.desc_firmware);
}
int falcon_boot(struct falcon *falcon)
@@ -229,9 +269,15 @@ int falcon_boot(struct falcon *falcon)
FALCON_ITFEN_CTXEN,
FALCON_ITFEN);
- /* boot falcon */
- falcon_writel(falcon, 0x00000000, FALCON_BOOTVEC);
- falcon_writel(falcon, FALCON_CPUCTL_STARTCPU, FALCON_CPUCTL);
+ if (falcon->riscv) {
+ falcon_writel(falcon, RISCV_BCR_CTRL_CORE_SELECT_RISCV, RISCV_BCR_CTRL);
+ falcon_writel(falcon, 0x0, RISCV_BOOT_VECTOR_HI);
+ falcon_writel(falcon, 0x100000, RISCV_BOOT_VECTOR_LO);
+ falcon_writel(falcon, RISCV_CPUCTL_STARTCPU, RISCV_CPUCTL);
+ } else {
+ falcon_writel(falcon, 0x00000000, FALCON_BOOTVEC);
+ falcon_writel(falcon, FALCON_CPUCTL_STARTCPU, FALCON_CPUCTL);
+ }
err = falcon_wait_idle(falcon);
if (err < 0) {
diff --git a/drivers/gpu/drm/tegra/falcon.h b/drivers/gpu/drm/tegra/falcon.h
index 902bb7e4fd0f..37a17c6136b3 100644
--- a/drivers/gpu/drm/tegra/falcon.h
+++ b/drivers/gpu/drm/tegra/falcon.h
@@ -55,6 +55,16 @@
#define FALCON_DMATRFFBOFFS 0x0000111c
+#define RISCV_BOOT_VECTOR_LO 0x00001780
+#define RISCV_BOOT_VECTOR_HI 0x00001784
+
+#define RISCV_CPUCTL 0x00001788
+#define RISCV_CPUCTL_STARTCPU (1 << 0)
+#define RISCV_CPUCTL_ACTIVE_STAT_ACTIVE (1 << 7)
+
+#define RISCV_BCR_CTRL 0x00001a68
+#define RISCV_BCR_CTRL_CORE_SELECT_RISCV (1 << 4)
+
struct falcon_fw_bin_header_v1 {
u32 magic; /* 0x10de */
u32 version; /* version of bin format (1) */
@@ -76,6 +86,14 @@ struct falcon_fw_os_header_v1 {
u32 data_size;
};
+struct falcon_fw_riscv_desc {
+ u32 reserved[74];
+ u32 data_offset;
+ u32 data_size;
+ u32 code_offset;
+ u32 code_size;
+};
+
struct falcon_firmware_section {
unsigned long offset;
size_t size;
@@ -84,6 +102,8 @@ struct falcon_firmware_section {
struct falcon_firmware {
/* Firmware after it is read but not loaded */
const struct firmware *firmware;
+ /* RISC-V firmware descriptor */
+ const struct firmware *desc_firmware;
/* Raw firmware data */
dma_addr_t iova;
@@ -102,6 +122,9 @@ struct falcon {
struct device *dev;
void __iomem *regs;
+ /* Peregrine falcon, external boot */
+ bool riscv;
+
struct falcon_firmware firmware;
};
diff --git a/drivers/gpu/drm/tegra/output.c b/drivers/gpu/drm/tegra/output.c
index 49e4f63a5550..d0ba3bd0aab8 100644
--- a/drivers/gpu/drm/tegra/output.c
+++ b/drivers/gpu/drm/tegra/output.c
@@ -117,11 +117,19 @@ int tegra_output_probe(struct tegra_output *output)
*/
WARN_ON(output->panel || output->bridge);
+ if (output->panel) {
+ drm_panel_put(output->panel);
+ output->panel = NULL;
+ }
+
output->panel = of_drm_find_panel(panel);
of_node_put(panel);
- if (IS_ERR(output->panel))
- return PTR_ERR(output->panel);
+ if (IS_ERR(output->panel)) {
+ err = PTR_ERR(output->panel);
+ output->panel = NULL;
+ return err;
+ }
}
ddc = of_parse_phandle(output->of_node, "nvidia,ddc-i2c-bus", 0);
@@ -131,7 +139,7 @@ int tegra_output_probe(struct tegra_output *output)
if (!output->ddc) {
err = -EPROBE_DEFER;
- return err;
+ goto put_i2c;
}
}
@@ -185,6 +193,11 @@ int tegra_output_probe(struct tegra_output *output)
return 0;
put_i2c:
+ if (output->panel) {
+ drm_panel_put(output->panel);
+ output->panel = NULL;
+ }
+
if (output->ddc)
i2c_put_adapter(output->ddc);
@@ -195,6 +208,11 @@ put_i2c:
void tegra_output_remove(struct tegra_output *output)
{
+ if (output->panel) {
+ drm_panel_put(output->panel);
+ output->panel = NULL;
+ }
+
if (output->hpd_gpio)
free_irq(output->hpd_irq, output);
diff --git a/drivers/gpu/drm/tegra/vic.c b/drivers/gpu/drm/tegra/vic.c
index 332c9b563d3f..fe097e967551 100644
--- a/drivers/gpu/drm/tegra/vic.c
+++ b/drivers/gpu/drm/tegra/vic.c
@@ -8,6 +8,7 @@
#include <linux/dma-mapping.h>
#include <linux/host1x.h>
#include <linux/iommu.h>
+#include <linux/iopoll.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/platform_device.h>
@@ -20,10 +21,16 @@
#include "falcon.h"
#include "vic.h"
+#define VIC_FALCON_DEBUGINFO 0x1094
+#define VIC_DEBUGINFO_DUMMY 0xabcd1234
+#define VIC_DEBUGINFO_CLEAR 0x0
+
struct vic_config {
const char *firmware;
unsigned int version;
bool supports_sid;
+ bool has_riscv;
+ unsigned int transcfg_offset;
};
struct vic {
@@ -54,8 +61,8 @@ static void vic_writel(struct vic *vic, u32 value, unsigned int offset)
static int vic_boot(struct vic *vic)
{
- u32 fce_ucode_size, fce_bin_data_offset, stream_id;
- void *hdr;
+ u32 stream_id;
+ u32 val;
int err = 0;
if (vic->config->supports_sid && tegra_dev_iommu_get_stream_id(vic->dev, &stream_id)) {
@@ -63,7 +70,7 @@ static int vic_boot(struct vic *vic)
value = TRANSCFG_ATT(1, TRANSCFG_SID_FALCON) |
TRANSCFG_ATT(0, TRANSCFG_SID_HW);
- vic_writel(vic, value, VIC_TFBIF_TRANSCFG);
+ vic_writel(vic, value, vic->config->transcfg_offset);
/*
* STREAMID0 is used for input/output buffers. Initialize it to SID_VIC in case
@@ -85,31 +92,51 @@ static int vic_boot(struct vic *vic)
CG_WAKEUP_DLY_CNT(4),
NV_PVIC_MISC_PRI_VIC_CG);
+ if (vic->config->has_riscv) {
+ /* Write a known pattern into DEBUGINFO register */
+ vic_writel(vic, VIC_DEBUGINFO_DUMMY, VIC_FALCON_DEBUGINFO);
+ }
+
err = falcon_boot(&vic->falcon);
if (err < 0)
return err;
- hdr = vic->falcon.firmware.virt;
- fce_bin_data_offset = *(u32 *)(hdr + VIC_UCODE_FCE_DATA_OFFSET);
-
- /* Old VIC firmware needs kernel help with setting up FCE microcode. */
- if (fce_bin_data_offset != 0x0 && fce_bin_data_offset != 0xa5a5a5a5) {
- hdr = vic->falcon.firmware.virt +
- *(u32 *)(hdr + VIC_UCODE_FCE_HEADER_OFFSET);
- fce_ucode_size = *(u32 *)(hdr + FCE_UCODE_SIZE_OFFSET);
-
- falcon_execute_method(&vic->falcon, VIC_SET_FCE_UCODE_SIZE,
- fce_ucode_size);
- falcon_execute_method(
- &vic->falcon, VIC_SET_FCE_UCODE_OFFSET,
- (vic->falcon.firmware.iova + fce_bin_data_offset) >> 8);
- }
+ if (vic->config->has_riscv) {
+ /* Check VIC has reached a proper initialized state */
+ err = readl_poll_timeout(vic->regs + VIC_FALCON_DEBUGINFO, val,
+ val == VIC_DEBUGINFO_CLEAR,
+ 1000, 2000000);
+ if (err) {
+ dev_err(vic->dev, "VIC not initialized, timeout, val=0x%x\n", val);
+ return err;
+ }
+ } else {
+ u32 fce_ucode_size, fce_bin_data_offset;
+ dma_addr_t iova;
+ void *hdr;
+
+ iova = vic->falcon.firmware.iova;
+ hdr = vic->falcon.firmware.virt;
+ fce_bin_data_offset = *(u32 *)(hdr + VIC_UCODE_FCE_DATA_OFFSET);
+
+ /* Old VIC firmware needs kernel help with setting up FCE microcode. */
+ if (fce_bin_data_offset != 0x0 && fce_bin_data_offset != 0xa5a5a5a5) {
+ hdr = vic->falcon.firmware.virt +
+ *(u32 *)(hdr + VIC_UCODE_FCE_HEADER_OFFSET);
+ fce_ucode_size = *(u32 *)(hdr + FCE_UCODE_SIZE_OFFSET);
+
+ falcon_execute_method(&vic->falcon, VIC_SET_FCE_UCODE_SIZE,
+ fce_ucode_size);
+ falcon_execute_method(&vic->falcon, VIC_SET_FCE_UCODE_OFFSET,
+ (iova + fce_bin_data_offset) >> 8);
+ }
- err = falcon_wait_idle(&vic->falcon);
- if (err < 0) {
- dev_err(vic->dev,
- "failed to set application ID and FCE base\n");
- return err;
+ err = falcon_wait_idle(&vic->falcon);
+ if (err < 0) {
+ dev_err(vic->dev,
+ "failed to set application ID and FCE base\n");
+ return err;
+ }
}
return 0;
@@ -277,6 +304,8 @@ static int vic_load_firmware(struct vic *vic)
if (!vic->config->supports_sid) {
vic->can_use_context = false;
+ } else if (vic->config->has_riscv) {
+ vic->can_use_context = true;
} else if (fce_bin_data_offset != 0x0 && fce_bin_data_offset != 0xa5a5a5a5) {
/*
* Firmware will access FCE through STREAMID0, so context
@@ -302,7 +331,6 @@ cleanup:
return err;
}
-
static int __maybe_unused vic_runtime_resume(struct device *dev)
{
struct vic *vic = dev_get_drvdata(dev);
@@ -417,6 +445,7 @@ static const struct vic_config vic_t186_config = {
.firmware = NVIDIA_TEGRA_186_VIC_FIRMWARE,
.version = 0x18,
.supports_sid = true,
+ .transcfg_offset = 0x2044,
};
#define NVIDIA_TEGRA_194_VIC_FIRMWARE "nvidia/tegra194/vic.bin"
@@ -425,6 +454,7 @@ static const struct vic_config vic_t194_config = {
.firmware = NVIDIA_TEGRA_194_VIC_FIRMWARE,
.version = 0x19,
.supports_sid = true,
+ .transcfg_offset = 0x2044,
};
#define NVIDIA_TEGRA_234_VIC_FIRMWARE "nvidia/tegra234/vic.bin"
@@ -433,6 +463,18 @@ static const struct vic_config vic_t234_config = {
.firmware = NVIDIA_TEGRA_234_VIC_FIRMWARE,
.version = 0x23,
.supports_sid = true,
+ .transcfg_offset = 0x2044,
+};
+
+#define NVIDIA_TEGRA_264_VIC_FIRMWARE "nvidia/tegra264/vic.bin"
+#define NVIDIA_TEGRA_264_VIC_DESC "nvidia/tegra264/vic.bin.desc"
+
+static const struct vic_config vic_t264_config = {
+ .firmware = NVIDIA_TEGRA_264_VIC_FIRMWARE,
+ .version = 0x264,
+ .supports_sid = true,
+ .has_riscv = true,
+ .transcfg_offset = 0x2244,
};
static const struct of_device_id tegra_vic_of_match[] = {
@@ -441,6 +483,7 @@ static const struct of_device_id tegra_vic_of_match[] = {
{ .compatible = "nvidia,tegra186-vic", .data = &vic_t186_config },
{ .compatible = "nvidia,tegra194-vic", .data = &vic_t194_config },
{ .compatible = "nvidia,tegra234-vic", .data = &vic_t234_config },
+ { .compatible = "nvidia,tegra264-vic", .data = &vic_t264_config },
{ },
};
MODULE_DEVICE_TABLE(of, tegra_vic_of_match);
@@ -495,6 +538,7 @@ static int vic_probe(struct platform_device *pdev)
vic->falcon.dev = dev;
vic->falcon.regs = vic->regs;
+ vic->falcon.riscv = vic->config->has_riscv;
err = falcon_init(&vic->falcon);
if (err < 0)
@@ -571,3 +615,7 @@ MODULE_FIRMWARE(NVIDIA_TEGRA_194_VIC_FIRMWARE);
#if IS_ENABLED(CONFIG_ARCH_TEGRA_234_SOC)
MODULE_FIRMWARE(NVIDIA_TEGRA_234_VIC_FIRMWARE);
#endif
+#if IS_ENABLED(CONFIG_ARCH_TEGRA_264_SOC)
+MODULE_FIRMWARE(NVIDIA_TEGRA_264_VIC_FIRMWARE);
+MODULE_FIRMWARE(NVIDIA_TEGRA_264_VIC_DESC);
+#endif
diff --git a/drivers/gpu/drm/tegra/vic.h b/drivers/gpu/drm/tegra/vic.h
index acf35aac948b..aca98a09c9fb 100644
--- a/drivers/gpu/drm/tegra/vic.h
+++ b/drivers/gpu/drm/tegra/vic.h
@@ -21,11 +21,10 @@
#define CG_IDLE_CG_EN (1 << 6)
#define CG_WAKEUP_DLY_CNT(val) ((val & 0xf) << 16)
-#define VIC_TFBIF_TRANSCFG 0x00002044
-#define TRANSCFG_ATT(i, v) (((v) & 0x3) << (i * 4))
-#define TRANSCFG_SID_HW 0
-#define TRANSCFG_SID_PHY 1
-#define TRANSCFG_SID_FALCON 2
+#define TRANSCFG_ATT(i, v) (((v) & 0x3) << ((i) * 4))
+#define TRANSCFG_SID_HW 0
+#define TRANSCFG_SID_PHY 1
+#define TRANSCFG_SID_FALCON 2
/* Firmware offsets */
diff --git a/drivers/gpu/drm/tidss/tidss_kms.c b/drivers/gpu/drm/tidss/tidss_kms.c
index 1512ee2574b6..70c14c3be10d 100644
--- a/drivers/gpu/drm/tidss/tidss_kms.c
+++ b/drivers/gpu/drm/tidss/tidss_kms.c
@@ -162,6 +162,7 @@ static int tidss_dispc_modeset_init(struct tidss_device *tidss)
if (panel) {
u32 conn_type;
+ int ret;
dev_dbg(dev, "Setting up panel for port %d\n", i);
@@ -176,7 +177,8 @@ static int tidss_dispc_modeset_init(struct tidss_device *tidss)
break;
default:
WARN_ON(1);
- return -EINVAL;
+ ret = -EINVAL;
+ goto put_panel;
}
if (panel->connector_type != conn_type) {
@@ -184,16 +186,20 @@ static int tidss_dispc_modeset_init(struct tidss_device *tidss)
"%s: Panel %s has incompatible connector type for vp%d (%d != %d)\n",
__func__, dev_name(panel->dev), i,
panel->connector_type, conn_type);
- return -EINVAL;
+ ret = -EINVAL;
+ goto put_panel;
}
bridge = devm_drm_panel_bridge_add(dev, panel);
- if (IS_ERR(bridge)) {
+ ret = PTR_ERR_OR_ZERO(bridge);
+ if (ret)
dev_err(dev,
"failed to set up panel bridge for port %d\n",
i);
- return PTR_ERR(bridge);
- }
+put_panel:
+ drm_panel_put(panel);
+ if (ret)
+ return ret;
}
pipes[num_pipes].hw_videoport = i;
diff --git a/drivers/gpu/drm/tve200/tve200_drv.c b/drivers/gpu/drm/tve200/tve200_drv.c
index 562f3f11812a..8d0e7c7c82e8 100644
--- a/drivers/gpu/drm/tve200/tve200_drv.c
+++ b/drivers/gpu/drm/tve200/tve200_drv.c
@@ -84,6 +84,7 @@ static int tve200_modeset_init(struct drm_device *dev)
if (panel) {
bridge = drm_panel_bridge_add_typed(panel,
DRM_MODE_CONNECTOR_Unknown);
+ drm_panel_put(panel);
if (IS_ERR(bridge)) {
ret = PTR_ERR(bridge);
goto out_bridge;
@@ -263,6 +264,7 @@ static const struct of_device_id tve200_of_match[] = {
},
{},
};
+MODULE_DEVICE_TABLE(of, tve200_of_match);
static struct platform_driver tve200_driver = {
.driver = {
diff --git a/drivers/gpu/drm/v3d/v3d_drv.h b/drivers/gpu/drm/v3d/v3d_drv.h
index 1b88b3dff757..8c46ed09f5c4 100644
--- a/drivers/gpu/drm/v3d/v3d_drv.h
+++ b/drivers/gpu/drm/v3d/v3d_drv.h
@@ -375,6 +375,11 @@ struct v3d_job {
void (*free)(struct kref *ref);
bool has_pm_ref;
+
+ /* Whether the job needs implicit dependencies, i.e. must wait for
+ * other contexts still writing its BOs.
+ */
+ bool has_implicit_dep;
};
struct v3d_bin_job {
diff --git a/drivers/gpu/drm/v3d/v3d_submit.c b/drivers/gpu/drm/v3d/v3d_submit.c
index 5c05f1ea24bc..623df9d5bbee 100644
--- a/drivers/gpu/drm/v3d/v3d_submit.c
+++ b/drivers/gpu/drm/v3d/v3d_submit.c
@@ -46,6 +46,9 @@ v3d_submit_lock_reservations(struct v3d_submit *submit)
for (i = 0; i < submit->job_count; i++) {
struct v3d_job *job = submit->jobs[i];
+ if (!job->has_implicit_dep)
+ continue;
+
for (j = 0; j < job->bo_count; j++) {
ret = drm_sched_job_add_implicit_dependencies(&job->base,
job->bo[j],
@@ -71,7 +74,6 @@ v3d_submit_unlock_reservations(struct v3d_submit *submit)
/**
* v3d_lookup_bos() - Sets up job->bo[] with the GEM objects
* referenced by the job.
- * @dev: DRM device
* @file_priv: DRM file for this fd
* @job: V3D job being set up
* @bo_handles: GEM handles
@@ -85,23 +87,44 @@ v3d_submit_unlock_reservations(struct v3d_submit *submit)
* failure, because that will happen at `v3d_job_free()`.
*/
static int
-v3d_lookup_bos(struct v3d_submit *submit, u64 bo_handles, u32 bo_count)
+v3d_lookup_bos(struct drm_file *file_priv, struct v3d_job *job,
+ u64 bo_handles, u32 bo_count)
{
- struct v3d_job *last_job = submit->jobs[submit->job_count - 1];
-
- last_job->bo_count = bo_count;
-
- if (!last_job->bo_count) {
- /* See comment on bo_index for why we have to check
- * this.
- */
- drm_warn(&submit->v3d->drm, "Rendering requires BOs\n");
+ if (!bo_count) {
+ drm_warn(&job->v3d->drm, "Rendering requires BOs\n");
return -EINVAL;
}
- return drm_gem_objects_lookup(submit->file_priv,
+ job->bo_count = bo_count;
+
+ return drm_gem_objects_lookup(file_priv,
(void __user *)(uintptr_t)bo_handles,
- last_job->bo_count, &last_job->bo);
+ job->bo_count, &job->bo);
+}
+
+/**
+ * v3d_job_reference_bos() - Share another job's BOs with @dst
+ * @dst: job that acquires references to the BOs
+ * @src: job whose already-resolved BO list is shared
+ *
+ * For submissions with multiple jobs that use the same BOs, a trailing job
+ * shouldn't look the handles up again, as it could cause inconsistencies.
+ * Instead, it should reference the previous job's BOs.
+ */
+static int
+v3d_job_reference_bos(struct v3d_job *dst, struct v3d_job *src)
+{
+ dst->bo = kvmalloc_objs(*dst->bo, src->bo_count);
+ if (!dst->bo)
+ return -ENOMEM;
+
+ dst->bo_count = src->bo_count;
+ for (int i = 0; i < dst->bo_count; i++) {
+ dst->bo[i] = src->bo[i];
+ drm_gem_object_get(dst->bo[i]);
+ }
+
+ return 0;
}
static void
@@ -223,13 +246,14 @@ v3d_job_add_syncobjs(struct v3d_job *job, struct drm_file *file_priv,
static const struct {
size_t size;
void (*free)(struct kref *ref);
+ bool has_implicit_dep;
} v3d_job_types[] = {
- [V3D_BIN] = { sizeof(struct v3d_bin_job), v3d_job_free },
- [V3D_RENDER] = { sizeof(struct v3d_render_job), v3d_render_job_free },
- [V3D_TFU] = { sizeof(struct v3d_tfu_job), v3d_job_free },
- [V3D_CSD] = { sizeof(struct v3d_csd_job), v3d_job_free },
- [V3D_CACHE_CLEAN] = { sizeof(struct v3d_job), v3d_job_free },
- [V3D_CPU] = { sizeof(struct v3d_cpu_job), v3d_cpu_job_free },
+ [V3D_BIN] = { sizeof(struct v3d_bin_job), v3d_job_free, false },
+ [V3D_RENDER] = { sizeof(struct v3d_render_job), v3d_render_job_free, true },
+ [V3D_TFU] = { sizeof(struct v3d_tfu_job), v3d_job_free, true },
+ [V3D_CSD] = { sizeof(struct v3d_csd_job), v3d_job_free, true },
+ [V3D_CACHE_CLEAN] = { sizeof(struct v3d_job), v3d_job_free, false },
+ [V3D_CPU] = { sizeof(struct v3d_cpu_job), v3d_cpu_job_free, true },
};
static struct v3d_job *
@@ -251,6 +275,7 @@ v3d_submit_add_job(struct v3d_submit *submit, enum v3d_queue queue)
job->queue = queue;
job->file_priv = v3d_priv;
job->free = v3d_job_types[queue].free;
+ job->has_implicit_dep = v3d_job_types[queue].has_implicit_dep;
ret = drm_sched_job_init(&job->base, &v3d_priv->sched_entity[queue],
1, v3d_priv, submit->file_priv->client_id);
@@ -518,13 +543,18 @@ v3d_setup_csd_jobs_and_bos(struct v3d_submit *submit,
if (ret)
return ret;
+ ret = v3d_lookup_bos(submit->file_priv, &job->base, args->bo_handles,
+ args->bo_handle_count);
+ if (ret)
+ return ret;
+
job->args = *args;
clean_job = v3d_submit_add_job(submit, V3D_CACHE_CLEAN);
if (IS_ERR(clean_job))
return PTR_ERR(clean_job);
- return v3d_lookup_bos(submit, args->bo_handles, args->bo_handle_count);
+ return v3d_job_reference_bos(clean_job, &job->base);
}
static void
@@ -1163,22 +1193,33 @@ v3d_submit_cl_ioctl(struct drm_device *dev, void *data,
if (ret)
goto fail;
+ /*
+ * We don't associate the BOs with the BIN job. Fences are only
+ * attached to the last job in the submission chain, and BIN jobs
+ * don't need implicit dependencies since depending on results from
+ * another context is not a realistic scenario for binning.
+ */
+ ret = v3d_lookup_bos(submit.file_priv, &render->base,
+ args->bo_handles, args->bo_handle_count);
+ if (ret)
+ goto fail;
+
if (args->flags & DRM_V3D_SUBMIT_CL_FLUSH_CACHE) {
clean_job = v3d_submit_add_job(&submit, V3D_CACHE_CLEAN);
if (IS_ERR(clean_job)) {
ret = PTR_ERR(clean_job);
goto fail;
}
+
+ ret = v3d_job_reference_bos(clean_job, &render->base);
+ if (ret)
+ goto fail;
}
ret = v3d_attach_perfmon_to_jobs(&submit, args->perfmon_id);
if (ret)
goto fail;
- ret = v3d_lookup_bos(&submit, args->bo_handles, args->bo_handle_count);
- if (ret)
- goto fail;
-
ret = v3d_submit_lock_reservations(&submit);
if (ret)
goto fail;
@@ -1557,7 +1598,8 @@ v3d_submit_cpu_ioctl(struct drm_device *dev, void *data,
* the CSD and clean jobs in the case of indirect CSD job.
*/
if (args->bo_handle_count) {
- ret = v3d_lookup_bos(&submit, args->bo_handles, args->bo_handle_count);
+ ret = v3d_lookup_bos(submit.file_priv, &cpu_job->base,
+ args->bo_handles, args->bo_handle_count);
if (ret)
goto fail;
diff --git a/drivers/gpu/drm/vc4/vc4_hdmi.c b/drivers/gpu/drm/vc4/vc4_hdmi.c
index 74dce4be0c00..17c8635c5afa 100644
--- a/drivers/gpu/drm/vc4/vc4_hdmi.c
+++ b/drivers/gpu/drm/vc4/vc4_hdmi.c
@@ -3208,11 +3208,11 @@ err_disable_clk:
return ret;
}
-static void vc4_hdmi_put_ddc_device(void *ptr)
+static void vc4_hdmi_put_ddc(void *ptr)
{
struct vc4_hdmi *vc4_hdmi = ptr;
- put_device(&vc4_hdmi->ddc->dev);
+ i2c_put_adapter(vc4_hdmi->ddc);
}
static int vc4_hdmi_bind(struct device *dev, struct device *master, void *data)
@@ -3266,14 +3266,14 @@ static int vc4_hdmi_bind(struct device *dev, struct device *master, void *data)
return -ENODEV;
}
- vc4_hdmi->ddc = of_find_i2c_adapter_by_node(ddc_node);
+ vc4_hdmi->ddc = of_get_i2c_adapter_by_node(ddc_node);
of_node_put(ddc_node);
if (!vc4_hdmi->ddc) {
drm_err(drm, "Failed to get ddc i2c adapter by node\n");
return -EPROBE_DEFER;
}
- ret = devm_add_action_or_reset(dev, vc4_hdmi_put_ddc_device, vc4_hdmi);
+ ret = devm_add_action_or_reset(dev, vc4_hdmi_put_ddc, vc4_hdmi);
if (ret)
return ret;
diff --git a/drivers/gpu/host1x/Makefile b/drivers/gpu/host1x/Makefile
index fead483af0b4..b684fbf73841 100644
--- a/drivers/gpu/host1x/Makefile
+++ b/drivers/gpu/host1x/Makefile
@@ -17,7 +17,8 @@ host1x-y = \
hw/host1x05.o \
hw/host1x06.o \
hw/host1x07.o \
- hw/host1x08.o
+ hw/host1x08.o \
+ hw/host1x10.o
host1x-$(CONFIG_IOMMU_API) += \
context.o
diff --git a/drivers/gpu/host1x/bus.c b/drivers/gpu/host1x/bus.c
index e3884096c2fe..825a943c64dd 100644
--- a/drivers/gpu/host1x/bus.c
+++ b/drivers/gpu/host1x/bus.c
@@ -508,7 +508,7 @@ static int host1x_device_add(struct host1x *host1x,
* Add device even if there are no subdevs to ensure syncpoint functionality
* is available regardless of whether any engine subdevices are present
*/
- if (list_empty(&device->subdevs)) {
+ if (list_empty(&device->subdevs) && !device->registered) {
err = device_add(&device->dev);
if (err < 0)
dev_err(&device->dev, "failed to add device: %d\n", err);
diff --git a/drivers/gpu/host1x/cdma.c b/drivers/gpu/host1x/cdma.c
index ba2e572567c0..f6d3db2c8c39 100644
--- a/drivers/gpu/host1x/cdma.c
+++ b/drivers/gpu/host1x/cdma.c
@@ -13,6 +13,7 @@
#include <linux/interrupt.h>
#include <linux/kernel.h>
#include <linux/kfifo.h>
+#include <linux/overflow.h>
#include <linux/slab.h>
#include <trace/events/host1x.h>
@@ -419,7 +420,7 @@ syncpt_incr:
/* won't need a timeout when replayed */
job->timeout = 0;
- syncpt_incrs = job->syncpt_end - syncpt_val;
+ syncpt_incrs = wrapping_sub(u32, job->syncpt_end, syncpt_val);
dev_dbg(dev, "%s: CPU incr (%d)\n", __func__, syncpt_incrs);
host1x_job_dump(dev, job);
diff --git a/drivers/gpu/host1x/context.c b/drivers/gpu/host1x/context.c
index 512f3d189ebf..94ecc8769c93 100644
--- a/drivers/gpu/host1x/context.c
+++ b/drivers/gpu/host1x/context.c
@@ -24,7 +24,7 @@ int host1x_memory_context_list_init(struct host1x *host1x)
struct host1x_memory_context_list *cdl = &host1x->context_list;
struct device_node *node = host1x->dev->of_node;
struct host1x_memory_context *ctx;
- unsigned int i;
+ unsigned int devs, i;
int err;
cdl->devs = NULL;
@@ -35,7 +35,16 @@ int host1x_memory_context_list_init(struct host1x *host1x)
if (err < 0)
return 0;
- cdl->len = err / 4;
+ devs = 0;
+
+ for (i = 0; i < err / 4; i++) {
+ u32 length;
+
+ of_property_read_u32_index(node, "iommu-map", i * 4 + 3, &length);
+ devs += length;
+ }
+
+ cdl->len = devs;
cdl->devs = kzalloc_objs(*cdl->devs, cdl->len);
if (!cdl->devs)
return -ENOMEM;
diff --git a/drivers/gpu/host1x/debug.c b/drivers/gpu/host1x/debug.c
index 6433c00d5d7e..b828f773fc06 100644
--- a/drivers/gpu/host1x/debug.c
+++ b/drivers/gpu/host1x/debug.c
@@ -31,7 +31,7 @@ void host1x_debug_output(struct output *o, const char *fmt, ...)
int len;
va_start(args, fmt);
- len = vsnprintf(o->buf, sizeof(o->buf), fmt, args);
+ len = vscnprintf(o->buf, sizeof(o->buf), fmt, args);
va_end(args);
o->fn(o->ctx, o->buf, len, false);
@@ -43,7 +43,7 @@ void host1x_debug_cont(struct output *o, const char *fmt, ...)
int len;
va_start(args, fmt);
- len = vsnprintf(o->buf, sizeof(o->buf), fmt, args);
+ len = vscnprintf(o->buf, sizeof(o->buf), fmt, args);
va_end(args);
o->fn(o->ctx, o->buf, len, true);
diff --git a/drivers/gpu/host1x/dev.c b/drivers/gpu/host1x/dev.c
index 3f475f0e6545..d2c64728f804 100644
--- a/drivers/gpu/host1x/dev.c
+++ b/drivers/gpu/host1x/dev.c
@@ -41,6 +41,7 @@
#include "hw/host1x06.h"
#include "hw/host1x07.h"
#include "hw/host1x08.h"
+#include "hw/host1x10.h"
void host1x_common_writel(struct host1x *host1x, u32 v, u32 r)
{
@@ -287,7 +288,47 @@ static const struct host1x_info host1x08_info = {
.reserve_vblank_syncpts = false,
};
+static const struct host1x_sid_entry tegra264_sid_table[] = {
+ { /* SE1 MMIO */ .base = 0x1650, .offset = 0x90, .limit = 0x90 },
+ { /* SE2 MMIO */ .base = 0x1658, .offset = 0x90, .limit = 0x90 },
+ { /* SE4 MMIO */ .base = 0x1660, .offset = 0x90, .limit = 0x90 },
+ { /* SE1 ch */ .base = 0x1738, .offset = 0x90, .limit = 0x90 },
+ { /* SE2 ch */ .base = 0x1740, .offset = 0x90, .limit = 0x90 },
+ { /* SE4 ch */ .base = 0x1748, .offset = 0x90, .limit = 0x90 },
+ { /* VIC ch */ .base = 0x1790, .offset = 0x30, .limit = 0x30 },
+ { /* VIC MMIO */ .base = 0x1688, .offset = 0x34, .limit = 0x34 },
+ { /* TSEC MMIO */ .base = 0x1690, .offset = 0x30, .limit = 0x34 },
+ { /* VI MMIO */ .base = 0x1698, .offset = 0x800, .limit = 0x800 },
+ { /* VI_THI MMIO */ .base = 0x16a0, .offset = 0x30, .limit = 0x34 },
+ { /* ISP MMIO */ .base = 0x1680, .offset = 0x800, .limit = 0x800 },
+ { /* ISP_THI MMIO */ .base = 0x16a8, .offset = 0x30, .limit = 0x34 },
+ { /* VI2 MMIO */ .base = 0x16b8, .offset = 0x800, .limit = 0x800 },
+ { /* VI2_THI MMIO */ .base = 0x16c0, .offset = 0x30, .limit = 0x34 },
+ { /* ISP1 MMIO */ .base = 0x16c8, .offset = 0x800, .limit = 0x800 },
+ { /* ISP1_THI MMIO */ .base = 0x16d0, .offset = 0x30, .limit = 0x34 },
+};
+
+static const struct host1x_info host1x10_info = {
+ .nb_channels = 63,
+ .nb_pts = 1024,
+ .nb_mlocks = 24,
+ .nb_bases = 0,
+ .init = host1x10_init,
+ .sync_offset = 0x0,
+ .dma_mask = DMA_BIT_MASK(40),
+ .has_wide_gather = true,
+ .has_hypervisor = true,
+ .has_common = true,
+ .num_sid_entries = ARRAY_SIZE(tegra264_sid_table),
+ .sid_table = tegra264_sid_table,
+ .streamid_vm_table = { 0x1004, 128 },
+ .classid_vm_table = { 0x1404, 25 },
+ .mmio_vm_table = { 0x1504, 25 },
+ .reserve_vblank_syncpts = false,
+};
+
static const struct of_device_id host1x_of_match[] = {
+ { .compatible = "nvidia,tegra264-host1x", .data = &host1x10_info, },
{ .compatible = "nvidia,tegra234-host1x", .data = &host1x08_info, },
{ .compatible = "nvidia,tegra194-host1x", .data = &host1x07_info, },
{ .compatible = "nvidia,tegra186-host1x", .data = &host1x06_info, },
diff --git a/drivers/gpu/host1x/hw/cdma_hw.c b/drivers/gpu/host1x/hw/cdma_hw.c
index 3f3f0018eee0..08a3cf2b11a9 100644
--- a/drivers/gpu/host1x/hw/cdma_hw.c
+++ b/drivers/gpu/host1x/hw/cdma_hw.c
@@ -246,23 +246,24 @@ static void timeout_release_mlock(struct host1x_cdma *cdma)
* so it turns out that if we don't /actually/ need MLOCKs, we can just
* ignore them.
*
- * As such, for now just implement this on Tegra234 where things are
- * stricter but also easy to implement.
+ * As such, for now just implement this on Tegra234 and above where things
+ * are stricter but also easy to implement.
*/
struct host1x_channel *ch = cdma_to_channel(cdma);
struct host1x *host1x = cdma_to_host1x(cdma);
u32 offset;
switch (ch->client->class) {
+ case HOST1X_CLASS_VIC:
+ offset = HOST1X_COMMON_VIC_MLOCK;
+ break;
+#if HOST1X_HW == 8
case HOST1X_CLASS_NVJPG1:
offset = HOST1X_COMMON_NVJPG1_MLOCK;
break;
case HOST1X_CLASS_NVENC:
offset = HOST1X_COMMON_NVENC_MLOCK;
break;
- case HOST1X_CLASS_VIC:
- offset = HOST1X_COMMON_VIC_MLOCK;
- break;
case HOST1X_CLASS_NVJPG:
offset = HOST1X_COMMON_NVJPG_MLOCK;
break;
@@ -272,6 +273,7 @@ static void timeout_release_mlock(struct host1x_cdma *cdma)
case HOST1X_CLASS_OFA:
offset = HOST1X_COMMON_OFA_MLOCK;
break;
+#endif
default:
WARN(1, "%s was not updated for class %u", __func__, ch->client->class);
return;
@@ -355,7 +357,7 @@ static int cdma_timeout_init(struct host1x_cdma *cdma)
static void cdma_timeout_destroy(struct host1x_cdma *cdma)
{
if (cdma->timeout.initialized)
- cancel_delayed_work(&cdma->timeout.wq);
+ cancel_delayed_work_sync(&cdma->timeout.wq);
cdma->timeout.initialized = false;
}
diff --git a/drivers/gpu/host1x/hw/channel_hw.c b/drivers/gpu/host1x/hw/channel_hw.c
index 2df6a16d484e..a8251ec0810c 100644
--- a/drivers/gpu/host1x/hw/channel_hw.c
+++ b/drivers/gpu/host1x/hw/channel_hw.c
@@ -7,6 +7,7 @@
#include <linux/host1x.h>
#include <linux/iommu.h>
+#include <linux/overflow.h>
#include <linux/slab.h>
#include <trace/events/host1x.h>
@@ -36,10 +37,9 @@ static void trace_write_gather(struct host1x_cdma *cdma, struct host1x_bo *bo,
for (i = 0; i < words; i += TRACE_MAX_LENGTH) {
u32 num_words = min(words - i, TRACE_MAX_LENGTH);
- offset += i * sizeof(u32);
-
trace_host1x_cdma_push_gather(dev_name(dev), bo,
- num_words, offset,
+ num_words,
+ offset + i * sizeof(u32),
mem);
}
@@ -121,7 +121,8 @@ static void submit_gathers(struct host1x_job *job, struct host1x_job_cmd *cmds,
if (cmd->is_wait) {
if (cmd->wait.relative)
- threshold = job_syncpt_base + cmd->wait.threshold;
+ threshold = wrapping_add(u32, job_syncpt_base,
+ cmd->wait.threshold);
else
threshold = cmd->wait.threshold;
@@ -260,7 +261,8 @@ prefences_done:
/* Submit work. */
job->syncpt_end = host1x_syncpt_incr_max(sp, job->syncpt_incrs);
- submit_gathers(job, job->cmds + i, job->num_cmds - i, job->syncpt_end - job->syncpt_incrs);
+ submit_gathers(job, job->cmds + i, job->num_cmds - i,
+ wrapping_sub(u32, job->syncpt_end, job->syncpt_incrs));
/* Before releasing MLOCK, ensure engine is idle again. */
fence = host1x_syncpt_incr_max(sp, 1);
@@ -298,7 +300,8 @@ prefences_done:
job->syncpt_end = host1x_syncpt_incr_max(sp, job->syncpt_incrs);
- submit_gathers(job, job->cmds, job->num_cmds, job->syncpt_end - job->syncpt_incrs);
+ submit_gathers(job, job->cmds, job->num_cmds,
+ wrapping_sub(u32, job->syncpt_end, job->syncpt_incrs));
#endif
}
diff --git a/drivers/gpu/host1x/hw/host1x10.c b/drivers/gpu/host1x/hw/host1x10.c
new file mode 100644
index 000000000000..2800f309bf6f
--- /dev/null
+++ b/drivers/gpu/host1x/hw/host1x10.c
@@ -0,0 +1,33 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Host1x init for Tegra264 SoCs
+ *
+ * Copyright (c) 2023 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
+ */
+
+/* include hw specification */
+#include "host1x10.h"
+#include "host1x10_hardware.h"
+
+/* include code */
+#define HOST1X_HW 10
+
+#include "cdma_hw.c"
+#include "channel_hw.c"
+#include "debug_hw.c"
+#include "intr_hw.c"
+#include "syncpt_hw.c"
+
+#include "../dev.h"
+
+int host1x10_init(struct host1x *host)
+{
+ host->channel_op = &host1x_channel_ops;
+ host->cdma_op = &host1x_cdma_ops;
+ host->cdma_pb_op = &host1x_pushbuffer_ops;
+ host->syncpt_op = &host1x_syncpt_ops;
+ host->intr_op = &host1x_intr_ops;
+ host->debug_op = &host1x_debug_ops;
+
+ return 0;
+}
diff --git a/drivers/gpu/host1x/hw/host1x10.h b/drivers/gpu/host1x/hw/host1x10.h
new file mode 100644
index 000000000000..577f6ff3dff5
--- /dev/null
+++ b/drivers/gpu/host1x/hw/host1x10.h
@@ -0,0 +1,15 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Host1x init for Tegra264 SoCs
+ *
+ * Copyright (c) 2023 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
+ */
+
+#ifndef HOST1X_HOST1X10_H
+#define HOST1X_HOST1X10_H
+
+struct host1x;
+
+int host1x10_init(struct host1x *host);
+
+#endif
diff --git a/drivers/gpu/host1x/hw/host1x10_hardware.h b/drivers/gpu/host1x/hw/host1x10_hardware.h
new file mode 100644
index 000000000000..abbead8190b1
--- /dev/null
+++ b/drivers/gpu/host1x/hw/host1x10_hardware.h
@@ -0,0 +1,21 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Tegra host1x Register Offsets for Tegra264
+ *
+ * Copyright (c) 2023 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
+ */
+
+#ifndef __HOST1X_HOST1X10_HARDWARE_H
+#define __HOST1X_HOST1X10_HARDWARE_H
+
+#include <linux/types.h>
+#include <linux/bitops.h>
+
+#include "hw_host1x10_uclass.h"
+#include "hw_host1x10_vm.h"
+#include "hw_host1x10_hypervisor.h"
+#include "hw_host1x10_common.h"
+
+#include "opcodes.h"
+
+#endif
diff --git a/drivers/gpu/host1x/hw/hw_host1x10_common.h b/drivers/gpu/host1x/hw/hw_host1x10_common.h
new file mode 100644
index 000000000000..48a632672a47
--- /dev/null
+++ b/drivers/gpu/host1x/hw/hw_host1x10_common.h
@@ -0,0 +1,6 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (c) 2023 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
+ */
+
+#define HOST1X_COMMON_VIC_MLOCK 0x4060
diff --git a/drivers/gpu/host1x/hw/hw_host1x10_hypervisor.h b/drivers/gpu/host1x/hw/hw_host1x10_hypervisor.h
new file mode 100644
index 000000000000..8c9069caffa8
--- /dev/null
+++ b/drivers/gpu/host1x/hw/hw_host1x10_hypervisor.h
@@ -0,0 +1,10 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (c) 2023 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
+ */
+
+#define HOST1X_HV_SYNCPT_PROT_EN 0x172c
+#define HOST1X_HV_SYNCPT_PROT_EN_CH_EN BIT(1)
+#define HOST1X_HV_CH_MLOCK_EN(x) (0x1708 + (x * 4))
+#define HOST1X_HV_CH_KERNEL_FILTER_GBUFFER(x) (0x1718 + (x * 4))
+#define HOST1X_HV_SYNCPT_VM(x) (0x0 + 4 * (x))
diff --git a/drivers/gpu/host1x/hw/hw_host1x10_uclass.h b/drivers/gpu/host1x/hw/hw_host1x10_uclass.h
new file mode 100644
index 000000000000..abe83e67fa83
--- /dev/null
+++ b/drivers/gpu/host1x/hw/hw_host1x10_uclass.h
@@ -0,0 +1,181 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (c) 2023 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
+ */
+
+ /*
+ * Function naming determines intended use:
+ *
+ * <x>_r(void) : Returns the offset for register <x>.
+ *
+ * <x>_w(void) : Returns the word offset for word (4 byte) element <x>.
+ *
+ * <x>_<y>_s(void) : Returns size of field <y> of register <x> in bits.
+ *
+ * <x>_<y>_f(u32 v) : Returns a value based on 'v' which has been shifted
+ * and masked to place it at field <y> of register <x>. This value
+ * can be |'d with others to produce a full register value for
+ * register <x>.
+ *
+ * <x>_<y>_m(void) : Returns a mask for field <y> of register <x>. This
+ * value can be ~'d and then &'d to clear the value of field <y> for
+ * register <x>.
+ *
+ * <x>_<y>_<z>_f(void) : Returns the constant value <z> after being shifted
+ * to place it at field <y> of register <x>. This value can be |'d
+ * with others to produce a full register value for <x>.
+ *
+ * <x>_<y>_v(u32 r) : Returns the value of field <y> from a full register
+ * <x> value 'r' after being shifted to place its LSB at bit 0.
+ * This value is suitable for direct comparison with other unshifted
+ * values appropriate for use in field <y> of register <x>.
+ *
+ * <x>_<y>_<z>_v(void) : Returns the constant value for <z> defined for
+ * field <y> of register <x>. This value is suitable for direct
+ * comparison with unshifted values appropriate for use in field <y>
+ * of register <x>.
+ */
+
+#ifndef HOST1X_HW_HOST1X10_UCLASS_H
+#define HOST1X_HW_HOST1X10_UCLASS_H
+
+static inline u32 host1x_uclass_incr_syncpt_r(void)
+{
+ return 0x0;
+}
+#define HOST1X_UCLASS_INCR_SYNCPT \
+ host1x_uclass_incr_syncpt_r()
+static inline u32 host1x_uclass_incr_syncpt_cond_f(u32 v)
+{
+ return (v & 0xff) << 10;
+}
+#define HOST1X_UCLASS_INCR_SYNCPT_COND_F(v) \
+ host1x_uclass_incr_syncpt_cond_f(v)
+static inline u32 host1x_uclass_incr_syncpt_indx_f(u32 v)
+{
+ return (v & 0x3ff) << 0;
+}
+#define HOST1X_UCLASS_INCR_SYNCPT_INDX_F(v) \
+ host1x_uclass_incr_syncpt_indx_f(v)
+static inline u32 host1x_uclass_wait_syncpt_r(void)
+{
+ return 0x8;
+}
+#define HOST1X_UCLASS_WAIT_SYNCPT \
+ host1x_uclass_wait_syncpt_r()
+static inline u32 host1x_uclass_wait_syncpt_indx_f(u32 v)
+{
+ return (v & 0xff) << 24;
+}
+#define HOST1X_UCLASS_WAIT_SYNCPT_INDX_F(v) \
+ host1x_uclass_wait_syncpt_indx_f(v)
+static inline u32 host1x_uclass_wait_syncpt_thresh_f(u32 v)
+{
+ return (v & 0xffffff) << 0;
+}
+#define HOST1X_UCLASS_WAIT_SYNCPT_THRESH_F(v) \
+ host1x_uclass_wait_syncpt_thresh_f(v)
+static inline u32 host1x_uclass_wait_syncpt_base_r(void)
+{
+ return 0x9;
+}
+#define HOST1X_UCLASS_WAIT_SYNCPT_BASE \
+ host1x_uclass_wait_syncpt_base_r()
+static inline u32 host1x_uclass_wait_syncpt_base_indx_f(u32 v)
+{
+ return (v & 0xff) << 24;
+}
+#define HOST1X_UCLASS_WAIT_SYNCPT_BASE_INDX_F(v) \
+ host1x_uclass_wait_syncpt_base_indx_f(v)
+static inline u32 host1x_uclass_wait_syncpt_base_base_indx_f(u32 v)
+{
+ return (v & 0xff) << 16;
+}
+#define HOST1X_UCLASS_WAIT_SYNCPT_BASE_BASE_INDX_F(v) \
+ host1x_uclass_wait_syncpt_base_base_indx_f(v)
+static inline u32 host1x_uclass_wait_syncpt_base_offset_f(u32 v)
+{
+ return (v & 0xffff) << 0;
+}
+#define HOST1X_UCLASS_WAIT_SYNCPT_BASE_OFFSET_F(v) \
+ host1x_uclass_wait_syncpt_base_offset_f(v)
+static inline u32 host1x_uclass_load_syncpt_base_r(void)
+{
+ return 0xb;
+}
+#define HOST1X_UCLASS_LOAD_SYNCPT_BASE \
+ host1x_uclass_load_syncpt_base_r()
+static inline u32 host1x_uclass_load_syncpt_base_base_indx_f(u32 v)
+{
+ return (v & 0xff) << 24;
+}
+#define HOST1X_UCLASS_LOAD_SYNCPT_BASE_BASE_INDX_F(v) \
+ host1x_uclass_load_syncpt_base_base_indx_f(v)
+static inline u32 host1x_uclass_load_syncpt_base_value_f(u32 v)
+{
+ return (v & 0xffffff) << 0;
+}
+#define HOST1X_UCLASS_LOAD_SYNCPT_BASE_VALUE_F(v) \
+ host1x_uclass_load_syncpt_base_value_f(v)
+static inline u32 host1x_uclass_incr_syncpt_base_base_indx_f(u32 v)
+{
+ return (v & 0xff) << 24;
+}
+#define HOST1X_UCLASS_INCR_SYNCPT_BASE_BASE_INDX_F(v) \
+ host1x_uclass_incr_syncpt_base_base_indx_f(v)
+static inline u32 host1x_uclass_incr_syncpt_base_offset_f(u32 v)
+{
+ return (v & 0xffffff) << 0;
+}
+#define HOST1X_UCLASS_INCR_SYNCPT_BASE_OFFSET_F(v) \
+ host1x_uclass_incr_syncpt_base_offset_f(v)
+static inline u32 host1x_uclass_indoff_r(void)
+{
+ return 0x2d;
+}
+#define HOST1X_UCLASS_INDOFF \
+ host1x_uclass_indoff_r()
+static inline u32 host1x_uclass_indoff_indbe_f(u32 v)
+{
+ return (v & 0xf) << 28;
+}
+#define HOST1X_UCLASS_INDOFF_INDBE_F(v) \
+ host1x_uclass_indoff_indbe_f(v)
+static inline u32 host1x_uclass_indoff_autoinc_f(u32 v)
+{
+ return (v & 0x1) << 27;
+}
+#define HOST1X_UCLASS_INDOFF_AUTOINC_F(v) \
+ host1x_uclass_indoff_autoinc_f(v)
+static inline u32 host1x_uclass_indoff_indmodid_f(u32 v)
+{
+ return (v & 0xff) << 18;
+}
+#define HOST1X_UCLASS_INDOFF_INDMODID_F(v) \
+ host1x_uclass_indoff_indmodid_f(v)
+static inline u32 host1x_uclass_indoff_indroffset_f(u32 v)
+{
+ return (v & 0xffff) << 2;
+}
+#define HOST1X_UCLASS_INDOFF_INDROFFSET_F(v) \
+ host1x_uclass_indoff_indroffset_f(v)
+static inline u32 host1x_uclass_indoff_rwn_read_v(void)
+{
+ return 1;
+}
+#define HOST1X_UCLASS_INDOFF_INDROFFSET_F(v) \
+ host1x_uclass_indoff_indroffset_f(v)
+static inline u32 host1x_uclass_load_syncpt_payload_32_r(void)
+{
+ return 0x4e;
+}
+#define HOST1X_UCLASS_LOAD_SYNCPT_PAYLOAD_32 \
+ host1x_uclass_load_syncpt_payload_32_r()
+static inline u32 host1x_uclass_wait_syncpt_32_r(void)
+{
+ return 0x50;
+}
+#define HOST1X_UCLASS_WAIT_SYNCPT_32 \
+ host1x_uclass_wait_syncpt_32_r()
+
+#endif
diff --git a/drivers/gpu/host1x/hw/hw_host1x10_vm.h b/drivers/gpu/host1x/hw/hw_host1x10_vm.h
new file mode 100644
index 000000000000..75f5b881c561
--- /dev/null
+++ b/drivers/gpu/host1x/hw/hw_host1x10_vm.h
@@ -0,0 +1,36 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (c) 2023 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
+ */
+
+#define HOST1X_CHANNEL_DMASTART 0x0000
+#define HOST1X_CHANNEL_DMASTART_HI 0x0004
+#define HOST1X_CHANNEL_DMAPUT 0x0008
+#define HOST1X_CHANNEL_DMAPUT_HI 0x000c
+#define HOST1X_CHANNEL_DMAGET 0x0010
+#define HOST1X_CHANNEL_DMAGET_HI 0x0014
+#define HOST1X_CHANNEL_DMAEND 0x0018
+#define HOST1X_CHANNEL_DMAEND_HI 0x001c
+#define HOST1X_CHANNEL_DMACTRL 0x0020
+#define HOST1X_CHANNEL_DMACTRL_DMASTOP BIT(0)
+#define HOST1X_CHANNEL_DMACTRL_DMAGETRST BIT(1)
+#define HOST1X_CHANNEL_DMACTRL_DMAINITGET BIT(2)
+#define HOST1X_CHANNEL_CMDFIFO_STAT 0x0024
+#define HOST1X_CHANNEL_CMDFIFO_STAT_EMPTY BIT(13)
+#define HOST1X_CHANNEL_CMDFIFO_RDATA 0x0028
+#define HOST1X_CHANNEL_CMDP_OFFSET 0x0030
+#define HOST1X_CHANNEL_CMDP_CLASS 0x0034
+#define HOST1X_CHANNEL_CHANNELSTAT 0x0038
+#define HOST1X_CHANNEL_CMDPROC_STOP 0x0048
+#define HOST1X_CHANNEL_TEARDOWN 0x004c
+#define HOST1X_CHANNEL_SMMU_STREAMID 0x0084
+
+#define HOST1X_SYNC_SYNCPT_CPU_INCR(x) (0x6400 + 4 * (x))
+#define HOST1X_SYNC_SYNCPT_THRESH_CPU0_INT_STATUS(x) (0x6600 + 4 * (x))
+#define HOST1X_SYNC_SYNCPT_INTR_DEST(x) (0x6684 + 4 * (x))
+#define HOST1X_SYNC_SYNCPT_THRESH_INT_ENABLE_CPU0(x) (0x770c + 4 * (x))
+#define HOST1X_SYNC_SYNCPT_THRESH_INT_DISABLE(x) (0x7790 + 4 * (x))
+#define HOST1X_SYNC_SYNCPT(x) (0x8080 + 4 * (x))
+#define HOST1X_SYNC_SYNCPT_INT_THRESH(x) (0xa088 + 4 * (x))
+#define HOST1X_SYNC_SYNCPT_CH_APP(x) (0xb090 + 4 * (x))
+#define HOST1X_SYNC_SYNCPT_CH_APP_CH(v) (((v) & 0x3f) << 8)
diff --git a/drivers/gpu/host1x/intr.c b/drivers/gpu/host1x/intr.c
index 723297250768..6a383e0c6eb9 100644
--- a/drivers/gpu/host1x/intr.c
+++ b/drivers/gpu/host1x/intr.c
@@ -7,6 +7,7 @@
#include <linux/clk.h>
#include <linux/interrupt.h>
+#include <linux/overflow.h>
#include "dev.h"
#include "fence.h"
#include "intr.h"
@@ -17,7 +18,7 @@ static void host1x_intr_add_fence_to_list(struct host1x_fence_list *list,
struct host1x_syncpt_fence *fence_in_list;
list_for_each_entry_reverse(fence_in_list, &list->list, list) {
- if ((s32)(fence_in_list->threshold - fence->threshold) <= 0) {
+ if ((s32)wrapping_sub(u32, fence_in_list->threshold, fence->threshold) <= 0) {
/* Fence in list is before us, we can insert here */
list_add(&fence->list, &fence_in_list->list);
return;
@@ -83,7 +84,7 @@ void host1x_intr_handle_interrupt(struct host1x *host, unsigned int id)
spin_lock(&sp->fences.lock);
list_for_each_entry_safe(fence, tmp, &sp->fences.list, list) {
- if (((value - fence->threshold) & 0x80000000U) != 0U) {
+ if ((wrapping_sub(u32, value, fence->threshold) & 0x80000000U) != 0U) {
/* Fence is not yet expired, we are done */
break;
}
diff --git a/drivers/gpu/host1x/job.c b/drivers/gpu/host1x/job.c
index 70bda32f1ff4..71411bc344bb 100644
--- a/drivers/gpu/host1x/job.c
+++ b/drivers/gpu/host1x/job.c
@@ -138,7 +138,7 @@ void host1x_job_add_wait(struct host1x_job *job, u32 id, u32 thresh,
}
EXPORT_SYMBOL(host1x_job_add_wait);
-static unsigned int pin_job(struct host1x *host, struct host1x_job *job)
+static int pin_job(struct host1x *host, struct host1x_job *job)
{
unsigned long mask = HOST1X_RELOC_READ | HOST1X_RELOC_WRITE;
struct host1x_client *client = job->client;
diff --git a/drivers/gpu/host1x/syncpt.c b/drivers/gpu/host1x/syncpt.c
index 807c74fc6a0a..2c111710f3bf 100644
--- a/drivers/gpu/host1x/syncpt.c
+++ b/drivers/gpu/host1x/syncpt.c
@@ -8,6 +8,7 @@
#include <linux/module.h>
#include <linux/device.h>
#include <linux/dma-fence.h>
+#include <linux/overflow.h>
#include <linux/slab.h>
#include <trace/events/host1x.h>
@@ -126,6 +127,10 @@ EXPORT_SYMBOL(host1x_syncpt_id);
*/
u32 host1x_syncpt_incr_max(struct host1x_syncpt *sp, u32 incrs)
{
+ /*
+ * Syncpoint values are intended to be modulo 2^32, so overflow
+ * here is intended.
+ */
return (u32)atomic_add_return(incrs, &sp->max_val);
}
EXPORT_SYMBOL(host1x_syncpt_incr_max);
@@ -279,7 +284,7 @@ bool host1x_syncpt_is_expired(struct host1x_syncpt *sp, u32 thresh)
current_val = (u32)atomic_read(&sp->min_val);
- return ((current_val - thresh) & 0x80000000U) == 0U;
+ return (wrapping_sub(u32, current_val, thresh) & 0x80000000U) == 0U;
}
int host1x_syncpt_init(struct host1x *host)
diff --git a/drivers/gpu/host1x/syncpt.h b/drivers/gpu/host1x/syncpt.h
index 4c3f3b2f0e9c..9eff42efc445 100644
--- a/drivers/gpu/host1x/syncpt.h
+++ b/drivers/gpu/host1x/syncpt.h
@@ -12,6 +12,7 @@
#include <linux/host1x.h>
#include <linux/kernel.h>
#include <linux/kref.h>
+#include <linux/overflow.h>
#include <linux/sched.h>
#include "fence.h"
@@ -77,7 +78,7 @@ static inline bool host1x_syncpt_check_max(struct host1x_syncpt *sp, u32 real)
if (sp->client_managed)
return true;
max = host1x_syncpt_read_max(sp);
- return (s32)(max - real) >= 0;
+ return (s32)wrapping_sub(u32, max, real) >= 0;
}
/* Return true if sync point is client managed. */
diff --git a/drivers/gpu/tests/gpu_buddy_test.c b/drivers/gpu/tests/gpu_buddy_test.c
index 7df5c2ae83bb..89698563c61b 100644
--- a/drivers/gpu/tests/gpu_buddy_test.c
+++ b/drivers/gpu/tests/gpu_buddy_test.c
@@ -1002,6 +1002,47 @@ static void gpu_test_buddy_alloc_clear(struct kunit *test)
"buddy_alloc hit an error size=%lu\n", ps);
gpu_buddy_free_list(&mm, &allocated, GPU_BUDDY_CLEARED);
gpu_buddy_fini(&mm);
+
+ /*
+ * Using a non-power-of-two mm size, allocate alternating blocks of 4KiB in an
+ * even sequence and free them as cleared. All blocks should be marked as
+ * dirty and the split blocks should be merged back to their original
+ * size when the blocks clear reset function is called.
+ */
+ KUNIT_EXPECT_FALSE(test, gpu_buddy_init(&mm, mm_size, ps));
+ KUNIT_EXPECT_EQ(test, mm.max_order, max_order);
+
+ i = 0;
+ n_pages = mm_size / ps;
+ do {
+ if (i % 2 == 0)
+ KUNIT_ASSERT_FALSE_MSG(test, gpu_buddy_alloc_blocks(&mm, 0, mm_size,
+ ps, ps, &allocated, 0),
+ "buddy_alloc hit an error size=%lu\n", ps);
+ } while (++i < n_pages);
+
+ gpu_buddy_free_list(&mm, &allocated, GPU_BUDDY_CLEARED);
+ gpu_buddy_reset_clear(&mm, false);
+ KUNIT_EXPECT_EQ(test, mm.clear_avail, 0);
+
+ /*
+ * Using a non-power-of-two mm size, allocate alternating blocks of 4KiB in an
+ * odd sequence and free them as cleared. All blocks should be marked as
+ * cleared and the split blocks should be merged back to their original
+ * size when the blocks clear reset function is called.
+ */
+ i = 0;
+ do {
+ if (i % 2 != 0)
+ KUNIT_ASSERT_FALSE_MSG(test, gpu_buddy_alloc_blocks(&mm, 0, mm_size,
+ ps, ps, &allocated, 0),
+ "buddy_alloc hit an error size=%lu\n", ps);
+ } while (++i < n_pages);
+
+ gpu_buddy_free_list(&mm, &allocated, GPU_BUDDY_CLEARED);
+ gpu_buddy_reset_clear(&mm, true);
+ KUNIT_EXPECT_EQ(test, mm.clear_avail, mm_size);
+ gpu_buddy_fini(&mm);
}
static void gpu_test_buddy_alloc_contiguous(struct kunit *test)
diff --git a/include/drm/drm_connector.h b/include/drm/drm_connector.h
index af075c38f4db..a0cf0268de48 100644
--- a/include/drm/drm_connector.h
+++ b/include/drm/drm_connector.h
@@ -2377,7 +2377,10 @@ struct drm_connector {
*/
struct mutex edid_override_mutex;
- /** @epoch_counter: used to detect any other changes in connector, besides status */
+ /**
+ * @epoch_counter: Used to detect changes in connector. Increased when
+ * the connector, including its status, is changed.
+ */
u64 epoch_counter;
/**
diff --git a/kernel/cgroup/dmem.c b/kernel/cgroup/dmem.c
index 6430c7ce1e03..39930c59cb76 100644
--- a/kernel/cgroup/dmem.c
+++ b/kernel/cgroup/dmem.c
@@ -734,57 +734,38 @@ static ssize_t dmemcg_limit_write(struct kernfs_open_file *of,
void (*apply)(struct dmem_cgroup_pool_state *, u64))
{
struct dmemcg_state *dmemcs = css_to_dmemcs(of_css(of));
- int err = 0;
-
- while (buf && !err) {
- struct dmem_cgroup_pool_state *pool = NULL;
- char *options, *region_name;
- struct dmem_cgroup_region *region;
- u64 new_limit;
-
- options = buf;
- buf = strchr(buf, '\n');
- if (buf)
- *buf++ = '\0';
-
- options = strstrip(options);
-
- /* eat empty lines */
- if (!options[0])
- continue;
-
- region_name = strsep(&options, " \t");
- if (!region_name[0])
- continue;
-
- if (!options || !*options)
- return -EINVAL;
+ struct dmem_cgroup_pool_state *pool;
+ struct dmem_cgroup_region *region;
+ char *region_name;
+ u64 new_limit;
+ int err;
- rcu_read_lock();
- region = dmemcg_get_region_by_name(region_name);
- rcu_read_unlock();
+ buf = strstrip(buf);
+ region_name = strsep(&buf, " \t");
+ if (!buf || !region_name[0])
+ return -EINVAL;
- if (!region)
- return -EINVAL;
+ rcu_read_lock();
+ region = dmemcg_get_region_by_name(region_name);
+ rcu_read_unlock();
+ if (!region)
+ return -EINVAL;
- err = dmemcg_parse_limit(options, &new_limit);
- if (err < 0)
- goto out_put;
+ err = dmemcg_parse_limit(buf, &new_limit);
+ if (err < 0)
+ goto out_put;
- pool = get_cg_pool_unlocked(dmemcs, region);
- if (IS_ERR(pool)) {
- err = PTR_ERR(pool);
- goto out_put;
- }
+ pool = get_cg_pool_unlocked(dmemcs, region);
+ if (IS_ERR(pool)) {
+ err = PTR_ERR(pool);
+ goto out_put;
+ }
- /* And commit */
- apply(pool, new_limit);
- dmemcg_pool_put(pool);
+ apply(pool, new_limit);
+ dmemcg_pool_put(pool);
out_put:
- kref_put(&region->ref, dmemcg_free_region);
- }
-
+ kref_put(&region->ref, dmemcg_free_region);
return err ?: nbytes;
}