summaryrefslogtreecommitdiff
path: root/drivers/gpu
diff options
context:
space:
mode:
authorDave Airlie <airlied@redhat.com>2026-10-21 11:02:13 +1000
committerDave Airlie <airlied@redhat.com>2026-10-21 11:03:26 +1000
commitfad06cfaf385787b9ffa2f73f049d42dbe1de57f (patch)
tree669f9bc2af4bd53140bd1da30f757512f3f78bf5 /drivers/gpu
parenta56b45995b1e6b9ee671485b1fe68b0c7f754a67 (diff)
parentdb1de27aca30ebaad04aea74ac21e4c4fb531a37 (diff)
Merge tag 'drm-misc-next-2026-10-16' of https://gitlab.freedesktop.org/drm/misc/kernel into drm-next
drm-misc-next for v7.5: UAPI Changes: - Add Apple Interchange fourcc. - Expose amdxdna busy time via DRM fdinfo. Cross-subsystem Changes: - Improve DT bindings for display/ste,mcde - Add kunit test for dma_fence_dedup_array() to dma-buf/test. Core Changes: - Documentation updates to drm/scheduler, gpu/buddy. - Fail kmapping a bo without pages. Driver Changes: - Bugfixes and improvements to panel/Raystar RFF500F-AWH-DNN, accel/ivpu, vc4, accel/amdxdna, panthor, nouveau, panfrost, panthor, accel/qaic, accel/rocket, bridge/lontium-lt8713sx, appletbdrm. - Add support for BOE Novatek NT51021, Retroid Pocket 6, Raydium RM69220 DDIC panels, Sharp LQ138P1JX61 eDP, BOE NV140FHD-N43, AUO B140HAN04.0, AUO 0x0db3, panels. - Add support for Mali-G68 to panfrost. - Add Chrontel CH7218A to bridge/simple. - Support ARGB8888 for virtio primary planes. - Improve panfrost gpu reset handling and initialization. - Add HDMI 2.0 support to rockchip/dw-hdmi-qp and improve HPD handling. - Advertise HDMI 1.2 capabilities in sun4i, bridge/adv7511, it66121 & inno-hdmi. - Advertise HDMI 1.3 capabilities in bridge/ite-6263. - Advertise HDMI 1.4 capabilities in bridge/lt9611 & rockchip/rk3066_hdmi - Switch many drivers to drm_atomic_helper_create_state in preparation for readout. Signed-off-by: Dave Airlie <airlied@redhat.com> From: Maarten Lankhorst <maarten.lankhorst@linux.intel.com> Link: https://patch.msgid.link/75e7de61-048c-4fdd-9ad2-6b47b29a4863@linux.intel.com
Diffstat (limited to 'drivers/gpu')
-rw-r--r--drivers/gpu/buddy.c1
-rw-r--r--drivers/gpu/drm/arm/display/komeda/komeda_wb_connector.c2
-rw-r--r--drivers/gpu/drm/arm/malidp_mw.c18
-rw-r--r--drivers/gpu/drm/bridge/adv7511/adv7511_drv.c22
-rw-r--r--drivers/gpu/drm/bridge/inno-hdmi.c1
-rw-r--r--drivers/gpu/drm/bridge/ite-it6263.c33
-rw-r--r--drivers/gpu/drm/bridge/ite-it66121.c13
-rw-r--r--drivers/gpu/drm/bridge/lontium-lt8713sx.c1
-rw-r--r--drivers/gpu/drm/bridge/lontium-lt8912b.c2
-rw-r--r--drivers/gpu/drm/bridge/lontium-lt9611.c16
-rw-r--r--drivers/gpu/drm/bridge/megachips-stdpxxxx-ge-b850v3-fw.c2
-rw-r--r--drivers/gpu/drm/bridge/simple-bridge.c5
-rw-r--r--drivers/gpu/drm/bridge/synopsys/dw-hdmi-qp.c159
-rw-r--r--drivers/gpu/drm/hyperv/hyperv_drm_modeset.c2
-rw-r--r--drivers/gpu/drm/imx/ipuv3/imx-tve.c2
-rw-r--r--drivers/gpu/drm/nouveau/nouveau_abi16.c2
-rw-r--r--drivers/gpu/drm/panel/Kconfig26
-rw-r--r--drivers/gpu/drm/panel/Makefile2
-rw-r--r--drivers/gpu/drm/panel/panel-edp.c4
-rw-r--r--drivers/gpu/drm/panel/panel-novatek-nt51021.c875
-rw-r--r--drivers/gpu/drm/panel/panel-raydium-rm69220.c417
-rw-r--r--drivers/gpu/drm/panel/panel-ronbo-rb070d30.c14
-rw-r--r--drivers/gpu/drm/panel/panel-simple.c3
-rw-r--r--drivers/gpu/drm/panel/panel-visionox-vtdr6130.c221
-rw-r--r--drivers/gpu/drm/panfrost/panfrost_device.c247
-rw-r--r--drivers/gpu/drm/panfrost/panfrost_device.h5
-rw-r--r--drivers/gpu/drm/panfrost/panfrost_drv.c198
-rw-r--r--drivers/gpu/drm/panfrost/panfrost_drv.h9
-rw-r--r--drivers/gpu/drm/panfrost/panfrost_features.h2
-rw-r--r--drivers/gpu/drm/panfrost/panfrost_gem.c64
-rw-r--r--drivers/gpu/drm/panfrost/panfrost_gem.h10
-rw-r--r--drivers/gpu/drm/panfrost/panfrost_gem_shrinker.c9
-rw-r--r--drivers/gpu/drm/panfrost/panfrost_gpu.c5
-rw-r--r--drivers/gpu/drm/panfrost/panfrost_issues.h8
-rw-r--r--drivers/gpu/drm/panfrost/panfrost_job.c103
-rw-r--r--drivers/gpu/drm/panfrost/panfrost_job.h4
-rw-r--r--drivers/gpu/drm/panfrost/panfrost_mmu.c14
-rw-r--r--drivers/gpu/drm/panfrost/panfrost_perfcnt.c319
-rw-r--r--drivers/gpu/drm/panfrost/panfrost_perfcnt.h1
-rw-r--r--drivers/gpu/drm/panthor/panthor_drv.c6
-rw-r--r--drivers/gpu/drm/panthor/panthor_gem.c3
-rw-r--r--drivers/gpu/drm/panthor/panthor_sched.c14
-rw-r--r--drivers/gpu/drm/rockchip/dw_hdmi_qp-rockchip.c205
-rw-r--r--drivers/gpu/drm/rockchip/rk3066_hdmi.c10
-rw-r--r--drivers/gpu/drm/scheduler/sched_main.c5
-rw-r--r--drivers/gpu/drm/solomon/ssd130x.c2
-rw-r--r--drivers/gpu/drm/sun4i/sun4i_hdmi_enc.c25
-rw-r--r--drivers/gpu/drm/sysfb/drm_sysfb_helper.h2
-rw-r--r--drivers/gpu/drm/tiny/appletbdrm.c50
-rw-r--r--drivers/gpu/drm/ttm/ttm_bo_util.c11
-rw-r--r--drivers/gpu/drm/vc4/tests/vc4_mock_output.c2
-rw-r--r--drivers/gpu/drm/vc4/vc4_bo.c3
-rw-r--r--drivers/gpu/drm/vc4/vc4_drv.c28
-rw-r--r--drivers/gpu/drm/vc4/vc4_hvs.c10
-rw-r--r--drivers/gpu/drm/vc4/vc4_txp.c2
-rw-r--r--drivers/gpu/drm/vc4/vc4_v3d.c7
-rw-r--r--drivers/gpu/drm/virtio/virtgpu_plane.c1
57 files changed, 2521 insertions, 706 deletions
diff --git a/drivers/gpu/buddy.c b/drivers/gpu/buddy.c
index 2f2aaadafe35..503f7d5934e4 100644
--- a/drivers/gpu/buddy.c
+++ b/drivers/gpu/buddy.c
@@ -2148,7 +2148,6 @@ EXPORT_SYMBOL(gpu_buddy_block_print);
* gpu_buddy_print - print allocator state
*
* @mm: GPU buddy manager
- * @p: GPU printer to use
*/
void gpu_buddy_print(struct gpu_buddy *mm)
{
diff --git a/drivers/gpu/drm/arm/display/komeda/komeda_wb_connector.c b/drivers/gpu/drm/arm/display/komeda/komeda_wb_connector.c
index 4872253a2449..f394971c0edb 100644
--- a/drivers/gpu/drm/arm/display/komeda/komeda_wb_connector.c
+++ b/drivers/gpu/drm/arm/display/komeda/komeda_wb_connector.c
@@ -128,7 +128,7 @@ static void komeda_wb_connector_destroy(struct drm_connector *connector)
}
static const struct drm_connector_funcs komeda_wb_connector_funcs = {
- .reset = drm_atomic_helper_connector_reset,
+ .atomic_create_state = drm_atomic_helper_connector_create_state,
.detect = komeda_wb_connector_detect,
.fill_modes = komeda_wb_connector_fill_modes,
.destroy = komeda_wb_connector_destroy,
diff --git a/drivers/gpu/drm/arm/malidp_mw.c b/drivers/gpu/drm/arm/malidp_mw.c
index f8be65119e3b..edd924ba722d 100644
--- a/drivers/gpu/drm/arm/malidp_mw.c
+++ b/drivers/gpu/drm/arm/malidp_mw.c
@@ -64,18 +64,18 @@ static const struct drm_connector_helper_funcs malidp_mw_connector_helper_funcs
.mode_valid = malidp_mw_connector_mode_valid,
};
-static void malidp_mw_connector_reset(struct drm_connector *connector)
+static struct drm_connector_state *
+malidp_mw_connector_create_state(struct drm_connector *connector)
{
- struct malidp_mw_connector_state *mw_state = kzalloc_obj(*mw_state);
+ struct malidp_mw_connector_state *mw_state;
- if (connector->state)
- __drm_atomic_helper_connector_destroy_state(connector->state);
+ mw_state = kzalloc_obj(*mw_state);
+ if (!mw_state)
+ return ERR_PTR(-ENOMEM);
- kfree(connector->state);
- connector->state = NULL;
+ __drm_atomic_helper_connector_state_init(&mw_state->base, connector);
- if (mw_state)
- __drm_atomic_helper_connector_reset(connector, &mw_state->base);
+ return &mw_state->base;
}
static enum drm_connector_status
@@ -111,7 +111,7 @@ malidp_mw_connector_duplicate_state(struct drm_connector *connector)
}
static const struct drm_connector_funcs malidp_mw_connector_funcs = {
- .reset = malidp_mw_connector_reset,
+ .atomic_create_state = malidp_mw_connector_create_state,
.detect = malidp_mw_connector_detect,
.fill_modes = drm_helper_probe_single_connector_modes,
.destroy = malidp_mw_connector_destroy,
diff --git a/drivers/gpu/drm/bridge/adv7511/adv7511_drv.c b/drivers/gpu/drm/bridge/adv7511/adv7511_drv.c
index 038b852405b5..c3ef44074477 100644
--- a/drivers/gpu/drm/bridge/adv7511/adv7511_drv.c
+++ b/drivers/gpu/drm/bridge/adv7511/adv7511_drv.c
@@ -8,6 +8,7 @@
#include <linux/clk.h>
#include <linux/device.h>
#include <linux/gpio/consumer.h>
+#include <linux/hdmi.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/slab.h>
@@ -775,11 +776,6 @@ static int adv7511_connector_init(struct adv7511 *adv)
* DRM Bridge Operations
*/
-static const struct adv7511 *bridge_to_adv7511_const(const struct drm_bridge *bridge)
-{
- return container_of(bridge, struct adv7511, bridge);
-}
-
static void adv7511_bridge_atomic_enable(struct drm_bridge *bridge,
struct drm_atomic_commit *state)
{
@@ -817,19 +813,6 @@ static void adv7511_bridge_atomic_disable(struct drm_bridge *bridge,
adv7511_power_off(adv);
}
-static enum drm_mode_status
-adv7511_bridge_hdmi_tmds_char_rate_valid(const struct drm_bridge *bridge,
- const struct drm_display_mode *mode,
- unsigned long long tmds_rate)
-{
- const struct adv7511 *adv = bridge_to_adv7511_const(bridge);
-
- if (tmds_rate > 1000ULL * adv->info->max_mode_clock_khz)
- return MODE_CLOCK_HIGH;
-
- return MODE_OK;
-}
-
static enum drm_mode_status adv7511_bridge_mode_valid(struct drm_bridge *bridge,
const struct drm_display_info *info,
const struct drm_display_mode *mode)
@@ -1006,7 +989,6 @@ static const struct drm_bridge_funcs adv7511_bridge_funcs = {
.atomic_destroy_state = drm_atomic_helper_bridge_destroy_state,
.atomic_create_state = drm_atomic_helper_bridge_create_state,
- .hdmi_tmds_char_rate_valid = adv7511_bridge_hdmi_tmds_char_rate_valid,
.hdmi_clear_audio_infoframe = adv7511_bridge_hdmi_clear_audio_infoframe,
.hdmi_write_audio_infoframe = adv7511_bridge_hdmi_write_audio_infoframe,
.hdmi_clear_avi_infoframe = adv7511_bridge_hdmi_clear_avi_infoframe,
@@ -1359,6 +1341,8 @@ static int adv7511_probe(struct i2c_client *i2c)
adv7511->bridge.vendor = "Analog";
adv7511->bridge.product = adv7511->info->name;
+ adv7511->bridge.supported_hdmi_ver = HDMI_VERSION_1_2;
+ adv7511->bridge.max_tmds_char_rate = 1000ULL * adv7511->info->max_mode_clock_khz;
#ifdef CONFIG_DRM_I2C_ADV7511_AUDIO
adv7511->bridge.ops |= DRM_BRIDGE_OP_HDMI_AUDIO;
diff --git a/drivers/gpu/drm/bridge/inno-hdmi.c b/drivers/gpu/drm/bridge/inno-hdmi.c
index dd35f5a875d3..ea3447d4f285 100644
--- a/drivers/gpu/drm/bridge/inno-hdmi.c
+++ b/drivers/gpu/drm/bridge/inno-hdmi.c
@@ -1119,6 +1119,7 @@ struct inno_hdmi *inno_hdmi_bind(struct device *dev,
hdmi->bridge.type = DRM_MODE_CONNECTOR_HDMIA;
hdmi->bridge.vendor = "Inno";
hdmi->bridge.product = "Inno HDMI";
+ hdmi->bridge.supported_hdmi_ver = HDMI_VERSION_1_2;
hdmi->bridge.ddc = inno_hdmi_i2c_adapter(hdmi);
if (IS_ERR(hdmi->bridge.ddc))
diff --git a/drivers/gpu/drm/bridge/ite-it6263.c b/drivers/gpu/drm/bridge/ite-it6263.c
index 21ac8e58b9d2..e8e0197171e1 100644
--- a/drivers/gpu/drm/bridge/ite-it6263.c
+++ b/drivers/gpu/drm/bridge/ite-it6263.c
@@ -15,7 +15,6 @@
#include <linux/regmap.h>
#include <linux/regulator/consumer.h>
-#include <drm/display/drm_hdmi_helper.h>
#include <drm/display/drm_hdmi_state_helper.h>
#include <drm/drm_atomic.h>
#include <drm/drm_atomic_helper.h>
@@ -669,20 +668,6 @@ static void it6263_bridge_atomic_enable(struct drm_bridge *bridge,
regmap_write(regmap, HDMI_REG_PKT_GENERAL_CTRL, ENABLE_PKT | REPEAT_PKT);
}
-static enum drm_mode_status
-it6263_bridge_mode_valid(struct drm_bridge *bridge,
- const struct drm_display_info *info,
- const struct drm_display_mode *mode)
-{
- unsigned long long rate;
-
- rate = drm_hdmi_compute_mode_clock(mode, 8, DRM_OUTPUT_COLOR_FORMAT_RGB444);
- if (rate == 0)
- return MODE_NOCLOCK;
-
- return bridge->funcs->hdmi_tmds_char_rate_valid(bridge, mode, rate);
-}
-
static int it6263_bridge_attach(struct drm_bridge *bridge,
struct drm_encoder *encoder,
enum drm_bridge_attach_flags flags)
@@ -753,20 +738,6 @@ it6263_bridge_atomic_get_input_bus_fmts(struct drm_bridge *bridge,
return input_fmts;
}
-static enum drm_mode_status
-it6263_hdmi_tmds_char_rate_valid(const struct drm_bridge *bridge,
- const struct drm_display_mode *mode,
- unsigned long long tmds_rate)
-{
- if (mode->clock > MAX_PIXEL_CLOCK_KHZ)
- return MODE_CLOCK_HIGH;
-
- if (tmds_rate > MAX_HDMI_TMDS_CHAR_RATE_HZ)
- return MODE_CLOCK_HIGH;
-
- return MODE_OK;
-}
-
static int it6263_hdmi_clear_avi_infoframe(struct drm_bridge *bridge)
{
struct it6263 *it = bridge_to_it6263(bridge);
@@ -831,13 +802,11 @@ static const struct drm_bridge_funcs it6263_bridge_funcs = {
.atomic_destroy_state = drm_atomic_helper_bridge_destroy_state,
.atomic_create_state = drm_atomic_helper_bridge_create_state,
.attach = it6263_bridge_attach,
- .mode_valid = it6263_bridge_mode_valid,
.atomic_disable = it6263_bridge_atomic_disable,
.atomic_enable = it6263_bridge_atomic_enable,
.detect = it6263_bridge_detect,
.edid_read = it6263_bridge_edid_read,
.atomic_get_input_bus_fmts = it6263_bridge_atomic_get_input_bus_fmts,
- .hdmi_tmds_char_rate_valid = it6263_hdmi_tmds_char_rate_valid,
.hdmi_clear_avi_infoframe = it6263_hdmi_clear_avi_infoframe,
.hdmi_write_avi_infoframe = it6263_hdmi_write_avi_infoframe,
.hdmi_clear_hdmi_infoframe = it6263_hdmi_clear_hdmi_infoframe,
@@ -899,6 +868,8 @@ static int it6263_probe(struct i2c_client *client)
it->bridge.type = DRM_MODE_CONNECTOR_HDMIA;
it->bridge.vendor = "ITE";
it->bridge.product = "IT6263";
+ it->bridge.supported_hdmi_ver = HDMI_VERSION_1_3;
+ it->bridge.max_tmds_char_rate = 1000ULL * MAX_PIXEL_CLOCK_KHZ;
return devm_drm_bridge_add(dev, &it->bridge);
}
diff --git a/drivers/gpu/drm/bridge/ite-it66121.c b/drivers/gpu/drm/bridge/ite-it66121.c
index 51fc68ffdf8f..c8d0a26e8e90 100644
--- a/drivers/gpu/drm/bridge/ite-it66121.c
+++ b/drivers/gpu/drm/bridge/ite-it66121.c
@@ -10,6 +10,7 @@
#include <linux/media-bus-format.h>
#include <linux/module.h>
#include <linux/device.h>
+#include <linux/hdmi.h>
#include <linux/interrupt.h>
#include <linux/i2c.h>
#include <linux/bitfield.h>
@@ -867,15 +868,6 @@ it66121_bridge_hdmi_tmds_char_rate_valid(const struct drm_bridge *bridge,
const struct drm_display_mode *mode,
unsigned long long tmds_rate)
{
- const struct it66121_ctx *ctx =
- container_of(bridge, const struct it66121_ctx, bridge);
- unsigned long long max_rate;
-
- max_rate = (ctx->bus_width == 12) ? 74250000ULL : 148500000ULL;
-
- if (tmds_rate > max_rate)
- return MODE_CLOCK_HIGH;
-
if (tmds_rate < HDMI_TMDS_CHAR_RATE_MIN_HZ)
return MODE_CLOCK_LOW;
@@ -1670,6 +1662,9 @@ static int it66121_probe(struct i2c_client *client)
DRM_BRIDGE_OP_HDMI;
ctx->bridge.vendor = "ITE";
ctx->bridge.product = "IT66121";
+ ctx->bridge.supported_hdmi_ver = HDMI_VERSION_1_2;
+ ctx->bridge.max_tmds_char_rate = ctx->bus_width == 12 ? 74250000ULL : 148500000ULL;
+
if (client->irq > 0) {
ctx->bridge.ops |= DRM_BRIDGE_OP_HPD;
diff --git a/drivers/gpu/drm/bridge/lontium-lt8713sx.c b/drivers/gpu/drm/bridge/lontium-lt8713sx.c
index 926f9e1e85bf..18696b75bec1 100644
--- a/drivers/gpu/drm/bridge/lontium-lt8713sx.c
+++ b/drivers/gpu/drm/bridge/lontium-lt8713sx.c
@@ -554,7 +554,6 @@ static int lt8713sx_probe(struct i2c_client *client)
lt8713sx_reset(lt8713sx);
- lt8713sx->bridge.funcs = &lt8713sx_bridge_funcs;
lt8713sx->bridge.of_node = dev->of_node;
lt8713sx->bridge.type = DRM_MODE_CONNECTOR_DisplayPort;
drm_bridge_add(&lt8713sx->bridge);
diff --git a/drivers/gpu/drm/bridge/lontium-lt8912b.c b/drivers/gpu/drm/bridge/lontium-lt8912b.c
index e347d04b7b2d..a4d05dcba9e0 100644
--- a/drivers/gpu/drm/bridge/lontium-lt8912b.c
+++ b/drivers/gpu/drm/bridge/lontium-lt8912b.c
@@ -416,7 +416,7 @@ static const struct drm_connector_funcs lt8912_connector_funcs = {
.detect = lt8912_connector_detect,
.fill_modes = drm_helper_probe_single_connector_modes,
.destroy = drm_connector_cleanup,
- .reset = drm_atomic_helper_connector_reset,
+ .atomic_create_state = drm_atomic_helper_connector_create_state,
.atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
.atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
};
diff --git a/drivers/gpu/drm/bridge/lontium-lt9611.c b/drivers/gpu/drm/bridge/lontium-lt9611.c
index cdc2b188e2d8..2d1a28261037 100644
--- a/drivers/gpu/drm/bridge/lontium-lt9611.c
+++ b/drivers/gpu/drm/bridge/lontium-lt9611.c
@@ -5,6 +5,7 @@
*/
#include <linux/gpio/consumer.h>
+#include <linux/hdmi.h>
#include <linux/i2c.h>
#include <linux/interrupt.h>
#include <linux/media-bus-format.h>
@@ -952,18 +953,6 @@ static int lt9611_hdmi_write_hdmi_infoframe(struct drm_bridge *bridge,
return 0;
}
-static enum drm_mode_status
-lt9611_hdmi_tmds_char_rate_valid(const struct drm_bridge *bridge,
- const struct drm_display_mode *mode,
- unsigned long long tmds_rate)
-{
- /* 297 MHz for 4k@30 mode */
- if (tmds_rate > 297000000)
- return MODE_CLOCK_HIGH;
-
- return MODE_OK;
-}
-
static int lt9611_hdmi_audio_startup(struct drm_bridge *bridge,
struct drm_connector *connector)
{
@@ -1029,7 +1018,6 @@ static const struct drm_bridge_funcs lt9611_bridge_funcs = {
.atomic_create_state = drm_atomic_helper_bridge_create_state,
.atomic_get_input_bus_fmts = lt9611_atomic_get_input_bus_fmts,
- .hdmi_tmds_char_rate_valid = lt9611_hdmi_tmds_char_rate_valid,
.hdmi_write_audio_infoframe = lt9611_hdmi_write_audio_infoframe,
.hdmi_clear_audio_infoframe = lt9611_hdmi_clear_audio_infoframe,
.hdmi_write_avi_infoframe = lt9611_hdmi_write_avi_infoframe,
@@ -1175,6 +1163,8 @@ static int lt9611_probe(struct i2c_client *client)
lt9611->bridge.hdmi_audio_dev = dev;
lt9611->bridge.hdmi_audio_max_i2s_playback_channels = 8;
lt9611->bridge.hdmi_audio_dai_port = 2;
+ lt9611->bridge.supported_hdmi_ver = HDMI_VERSION_1_4;
+ lt9611->bridge.max_tmds_char_rate = 297000000; /* 297 MHz for 4k@30 mode */
drm_bridge_add(&lt9611->bridge);
diff --git a/drivers/gpu/drm/bridge/megachips-stdpxxxx-ge-b850v3-fw.c b/drivers/gpu/drm/bridge/megachips-stdpxxxx-ge-b850v3-fw.c
index 4d244e6d4ed9..9153bf629fd1 100644
--- a/drivers/gpu/drm/bridge/megachips-stdpxxxx-ge-b850v3-fw.c
+++ b/drivers/gpu/drm/bridge/megachips-stdpxxxx-ge-b850v3-fw.c
@@ -149,7 +149,7 @@ static const struct drm_connector_funcs ge_b850v3_lvds_connector_funcs = {
.fill_modes = drm_helper_probe_single_connector_modes,
.detect = ge_b850v3_lvds_detect,
.destroy = drm_connector_cleanup,
- .reset = drm_atomic_helper_connector_reset,
+ .atomic_create_state = drm_atomic_helper_connector_create_state,
.atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
.atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
};
diff --git a/drivers/gpu/drm/bridge/simple-bridge.c b/drivers/gpu/drm/bridge/simple-bridge.c
index 3fba7002322e..fab793fb12b5 100644
--- a/drivers/gpu/drm/bridge/simple-bridge.c
+++ b/drivers/gpu/drm/bridge/simple-bridge.c
@@ -276,6 +276,11 @@ static const struct of_device_id simple_bridge_match[] = {
.connector_type = DRM_MODE_CONNECTOR_HDMIA,
},
}, {
+ .compatible = "chrontel,ch7218a",
+ .data = &(const struct simple_bridge_info) {
+ .connector_type = DRM_MODE_CONNECTOR_HDMIA,
+ },
+ }, {
.compatible = "mstar,tsumu88adt3-lf-1",
.data = &(const struct simple_bridge_info) {
.connector_type = DRM_MODE_CONNECTOR_HDMIA,
diff --git a/drivers/gpu/drm/bridge/synopsys/dw-hdmi-qp.c b/drivers/gpu/drm/bridge/synopsys/dw-hdmi-qp.c
index c9568aea98dc..b268c9376087 100644
--- a/drivers/gpu/drm/bridge/synopsys/dw-hdmi-qp.c
+++ b/drivers/gpu/drm/bridge/synopsys/dw-hdmi-qp.c
@@ -2,6 +2,7 @@
/*
* Copyright (c) 2021-2022 Rockchip Electronics Co., Ltd.
* Copyright (c) 2024 Collabora Ltd.
+ * Copyright (c) 2025 Amazon.com, Inc. or its affiliates.
*
* Author: Algea Cao <algea.cao@rock-chips.com>
* Author: Cristian Ciocaltea <cristian.ciocaltea@collabora.com>
@@ -15,7 +16,6 @@
#include <linux/module.h>
#include <linux/mutex.h>
#include <linux/of.h>
-#include <linux/workqueue.h>
#include <drm/bridge/dw_hdmi_qp.h>
#include <drm/display/drm_hdmi_helper.h>
@@ -28,6 +28,7 @@
#include <drm/drm_edid.h>
#include <drm/drm_modes.h>
#include <drm/drm_print.h>
+#include <drm/drm_probe_helper.h>
#include <media/cec.h>
@@ -38,8 +39,6 @@
#define DDC_CI_ADDR 0x37
#define DDC_SEGMENT_ADDR 0x30
-#define SCRAMB_POLL_DELAY_MS 3000
-
struct dw_hdmi_qp_i2c {
struct i2c_adapter adap;
@@ -83,6 +82,9 @@ struct dw_hdmi_qp {
struct regmap *regm;
int main_irq;
+ /* Guards curr_conn between atomic hooks and HPD notify readers */
+ struct mutex mutex;
+ struct drm_connector *curr_conn;
unsigned long tmds_char_rate;
bool no_hpd;
};
@@ -350,22 +352,14 @@ static int dw_hdmi_qp_i2c_read(struct dw_hdmi_qp *hdmi,
stat = wait_for_completion_timeout(&i2c->cmp, HZ / 10);
if (!stat) {
- if (hdmi->no_hpd)
- dev_dbg_ratelimited(hdmi->dev,
- "i2c read timed out\n");
- else
- dev_err(hdmi->dev, "i2c read timed out\n");
+ dev_dbg_ratelimited(hdmi->dev, "i2c read timed out\n");
dw_hdmi_qp_write(hdmi, 0x01, I2CM_CONTROL0);
return -EAGAIN;
}
/* Check for error condition on the bus */
if (i2c->stat & I2CM_NACK_RCVD_IRQ) {
- if (hdmi->no_hpd)
- dev_dbg_ratelimited(hdmi->dev,
- "i2c read error\n");
- else
- dev_err(hdmi->dev, "i2c read error\n");
+ dev_dbg_ratelimited(hdmi->dev, "i2c read error\n");
dw_hdmi_qp_write(hdmi, 0x01, I2CM_CONTROL0);
return -EIO;
}
@@ -404,14 +398,14 @@ static int dw_hdmi_qp_i2c_write(struct dw_hdmi_qp *hdmi,
stat = wait_for_completion_timeout(&i2c->cmp, HZ / 10);
if (!stat) {
- dev_err(hdmi->dev, "i2c write time out!\n");
+ dev_dbg_ratelimited(hdmi->dev, "i2c write time out!\n");
dw_hdmi_qp_write(hdmi, 0x01, I2CM_CONTROL0);
return -EAGAIN;
}
/* Check for error condition on the bus */
if (i2c->stat & I2CM_NACK_RCVD_IRQ) {
- dev_err(hdmi->dev, "i2c write nack!\n");
+ dev_dbg_ratelimited(hdmi->dev, "i2c write nack!\n");
dw_hdmi_qp_write(hdmi, 0x01, I2CM_CONTROL0);
return -EIO;
}
@@ -547,6 +541,7 @@ static void dw_hdmi_qp_bridge_atomic_enable(struct drm_bridge *bridge,
struct drm_connector_state *conn_state;
struct drm_connector *connector;
unsigned int op_mode;
+ int ret;
connector = drm_atomic_get_new_connector_for_encoder(state, bridge->encoder);
if (WARN_ON(!connector))
@@ -557,16 +552,26 @@ static void dw_hdmi_qp_bridge_atomic_enable(struct drm_bridge *bridge,
return;
if (connector->display_info.is_hdmi) {
- dev_dbg(hdmi->dev, "%s mode=HDMI %s rate=%llu bpc=%u\n", __func__,
- drm_hdmi_connector_get_output_format_name(conn_state->hdmi.output_format),
- conn_state->hdmi.tmds_char_rate, conn_state->hdmi.output_bpc);
op_mode = 0;
hdmi->tmds_char_rate = conn_state->hdmi.tmds_char_rate;
+
+ ret = drm_connector_hdmi_enable_scrambling(connector, conn_state);
+ if (ret)
+ dev_warn(hdmi->dev, "Failed to enable scrambling: %d\n", ret);
+
+ dev_dbg(hdmi->dev, "%s mode=HDMI %s rate=%llu bpc=%u scramb=%d\n", __func__,
+ drm_hdmi_connector_get_output_format_name(conn_state->hdmi.output_format),
+ conn_state->hdmi.tmds_char_rate, conn_state->hdmi.output_bpc,
+ connector->hdmi.scrambler_enabled);
} else {
- dev_dbg(hdmi->dev, "%s mode=DVI\n", __func__);
op_mode = OPMODE_DVI;
+ dev_dbg(hdmi->dev, "%s mode=DVI\n", __func__);
}
+ drm_connector_get(connector);
+ scoped_guard(mutex, &hdmi->mutex)
+ hdmi->curr_conn = connector;
+
hdmi->phy.ops->init(hdmi, hdmi->phy.data);
dw_hdmi_qp_mod(hdmi, HDCP2_BYPASS, HDCP2_BYPASS, HDCP2LOGIC_CONFIG0);
@@ -579,9 +584,20 @@ static void dw_hdmi_qp_bridge_atomic_disable(struct drm_bridge *bridge,
struct drm_atomic_commit *state)
{
struct dw_hdmi_qp *hdmi = bridge->driver_private;
+ struct drm_connector *connector;
+
+ scoped_guard(mutex, &hdmi->mutex) {
+ if (hdmi->curr_conn) {
+ drm_connector_put(hdmi->curr_conn);
+ hdmi->curr_conn = NULL;
+ }
+ }
hdmi->tmds_char_rate = 0;
+ connector = drm_atomic_get_old_connector_for_encoder(state, bridge->encoder);
+ drm_connector_hdmi_disable_scrambling(connector);
+
hdmi->phy.ops->disable(hdmi, hdmi->phy.data);
}
@@ -616,24 +632,24 @@ dw_hdmi_qp_bridge_edid_read(struct drm_bridge *bridge,
return drm_edid;
}
-static enum drm_mode_status
-dw_hdmi_qp_bridge_tmds_char_rate_valid(const struct drm_bridge *bridge,
- const struct drm_display_mode *mode,
- unsigned long long rate)
+static int dw_hdmi_qp_bridge_scrambler_enable(struct drm_bridge *bridge)
{
struct dw_hdmi_qp *hdmi = bridge->driver_private;
- /*
- * TODO: when hdmi->no_hpd is 1 we must not support modes that
- * require scrambling, including every mode with a clock above
- * HDMI_1_3_TMDS_CHAR_RATE_MAX_HZ.
- */
- if (rate > HDMI_1_3_TMDS_CHAR_RATE_MAX_HZ) {
- dev_dbg(hdmi->dev, "Unsupported TMDS char rate: %lld\n", rate);
- return MODE_CLOCK_HIGH;
- }
+ dw_hdmi_qp_write(hdmi, 1, SCRAMB_CONFIG0);
+ dev_dbg(hdmi->dev, "scrambler enabled\n");
- return MODE_OK;
+ return 0;
+}
+
+static int dw_hdmi_qp_bridge_scrambler_disable(struct drm_bridge *bridge)
+{
+ struct dw_hdmi_qp *hdmi = bridge->driver_private;
+
+ dw_hdmi_qp_write(hdmi, 0, SCRAMB_CONFIG0);
+ dev_dbg(hdmi->dev, "scrambler disabled\n");
+
+ return 0;
}
static int dw_hdmi_qp_bridge_clear_avi_infoframe(struct drm_bridge *bridge)
@@ -982,6 +998,22 @@ static int dw_hdmi_qp_cec_transmit(struct drm_bridge *bridge, u8 attempts,
#define dw_hdmi_qp_cec_transmit NULL
#endif /* CONFIG_DRM_DW_HDMI_QP_CEC */
+static void dw_hdmi_qp_bridge_hpd_enable(struct drm_bridge *bridge)
+{
+ struct dw_hdmi_qp *hdmi = bridge->driver_private;
+
+ if (hdmi->phy.ops->enable_hpd)
+ hdmi->phy.ops->enable_hpd(hdmi, hdmi->phy.data);
+}
+
+static void dw_hdmi_qp_bridge_hpd_disable(struct drm_bridge *bridge)
+{
+ struct dw_hdmi_qp *hdmi = bridge->driver_private;
+
+ if (hdmi->phy.ops->disable_hpd)
+ hdmi->phy.ops->disable_hpd(hdmi, hdmi->phy.data);
+}
+
static const struct drm_bridge_funcs dw_hdmi_qp_bridge_funcs = {
.atomic_get_output_bus_fmts = drm_atomic_helper_bridge_get_hdmi_output_bus_fmts,
.atomic_duplicate_state = drm_atomic_helper_bridge_duplicate_state,
@@ -990,8 +1022,11 @@ static const struct drm_bridge_funcs dw_hdmi_qp_bridge_funcs = {
.atomic_enable = dw_hdmi_qp_bridge_atomic_enable,
.atomic_disable = dw_hdmi_qp_bridge_atomic_disable,
.detect = dw_hdmi_qp_bridge_detect,
+ .hpd_enable = dw_hdmi_qp_bridge_hpd_enable,
+ .hpd_disable = dw_hdmi_qp_bridge_hpd_disable,
.edid_read = dw_hdmi_qp_bridge_edid_read,
- .hdmi_tmds_char_rate_valid = dw_hdmi_qp_bridge_tmds_char_rate_valid,
+ .hdmi_scrambler_enable = dw_hdmi_qp_bridge_scrambler_enable,
+ .hdmi_scrambler_disable = dw_hdmi_qp_bridge_scrambler_disable,
.hdmi_clear_avi_infoframe = dw_hdmi_qp_bridge_clear_avi_infoframe,
.hdmi_write_avi_infoframe = dw_hdmi_qp_bridge_write_avi_infoframe,
.hdmi_clear_hdmi_infoframe = dw_hdmi_qp_bridge_clear_hdmi_infoframe,
@@ -1054,9 +1089,6 @@ static void dw_hdmi_qp_init_hw(struct dw_hdmi_qp *hdmi)
/* Clear DONE and ERROR interrupts */
dw_hdmi_qp_write(hdmi, I2CM_OP_DONE_CLEAR | I2CM_NACK_RCVD_CLEAR,
MAINUNIT_1_INT_CLEAR);
-
- if (hdmi->phy.ops->setup_hpd)
- hdmi->phy.ops->setup_hpd(hdmi, hdmi->phy.data);
}
struct dw_hdmi_qp *dw_hdmi_qp_bind(struct platform_device *pdev,
@@ -1079,6 +1111,10 @@ struct dw_hdmi_qp *dw_hdmi_qp_bind(struct platform_device *pdev,
if (IS_ERR(hdmi))
return ERR_CAST(hdmi);
+ ret = devm_mutex_init(dev, &hdmi->mutex);
+ if (ret)
+ return ERR_PTR(ret);
+
hdmi->dev = dev;
regs = devm_platform_ioremap_resource(pdev, 0);
@@ -1119,13 +1155,18 @@ struct dw_hdmi_qp *dw_hdmi_qp_bind(struct platform_device *pdev,
DRM_BRIDGE_OP_HDMI_AUDIO |
DRM_BRIDGE_OP_HDMI_HDR_DRM_INFOFRAME |
DRM_BRIDGE_OP_HDMI_SPD_INFOFRAME;
- if (!hdmi->no_hpd)
- hdmi->bridge.ops |= DRM_BRIDGE_OP_HPD;
hdmi->bridge.of_node = pdev->dev.of_node;
hdmi->bridge.type = DRM_MODE_CONNECTOR_HDMIA;
hdmi->bridge.vendor = "Synopsys";
hdmi->bridge.product = "DW HDMI QP TX";
+ if (hdmi->no_hpd) {
+ hdmi->bridge.supported_hdmi_ver = HDMI_VERSION_1_4;
+ } else {
+ hdmi->bridge.supported_hdmi_ver = HDMI_VERSION_2_0;
+ hdmi->bridge.ops |= DRM_BRIDGE_OP_HPD;
+ }
+
if (plat_data->supported_formats)
hdmi->bridge.supported_formats = plat_data->supported_formats;
@@ -1169,6 +1210,46 @@ struct dw_hdmi_qp *dw_hdmi_qp_bind(struct platform_device *pdev,
}
EXPORT_SYMBOL_GPL(dw_hdmi_qp_bind);
+/**
+ * dw_hdmi_qp_hpd_notify() - Notify a hot-plug detection event
+ * @hdmi: pointer to the DW HDMI QP controller
+ *
+ * Platform drivers should call this from their threaded IRQ handler or work
+ * function to notify the bridge of a connection status change.
+ * The bridge's .read_hpd() phy_ops callback is used to read the current
+ * connection status.
+ */
+void dw_hdmi_qp_hpd_notify(struct dw_hdmi_qp *hdmi)
+{
+ enum drm_connector_status status;
+ struct drm_connector *conn;
+
+ scoped_guard(mutex, &hdmi->mutex) {
+ conn = hdmi->curr_conn;
+ if (conn)
+ drm_connector_get(conn);
+ }
+
+ status = hdmi->phy.ops->read_hpd(hdmi, hdmi->phy.data);
+ dev_dbg(hdmi->dev, "%s status=%d\n", __func__, status);
+
+ /*
+ * When the display pipeline has been already active, switch to
+ * drm_connector_helper_hpd_irq_event() to ensure .detect_ctx()
+ * gets invoked, i.e. via drm_helper_probe_detect(), because
+ * drm_bridge_hpd_notify() defers to a delayed hotplug path in
+ * this case.
+ */
+ if (conn && status == connector_status_connected)
+ drm_connector_helper_hpd_irq_event(conn);
+ else
+ drm_bridge_hpd_notify(&hdmi->bridge, status);
+
+ if (conn)
+ drm_connector_put(conn);
+}
+EXPORT_SYMBOL_GPL(dw_hdmi_qp_hpd_notify);
+
void dw_hdmi_qp_suspend(struct device *dev, struct dw_hdmi_qp *hdmi)
{
disable_irq(hdmi->main_irq);
diff --git a/drivers/gpu/drm/hyperv/hyperv_drm_modeset.c b/drivers/gpu/drm/hyperv/hyperv_drm_modeset.c
index 523d1c7ea072..da399cb951a5 100644
--- a/drivers/gpu/drm/hyperv/hyperv_drm_modeset.c
+++ b/drivers/gpu/drm/hyperv/hyperv_drm_modeset.c
@@ -66,7 +66,7 @@ static const struct drm_connector_helper_funcs hv_drm_connector_helper_funcs = {
static const struct drm_connector_funcs hv_drm_connector_funcs = {
.fill_modes = drm_helper_probe_single_connector_modes,
.destroy = drm_connector_cleanup,
- .reset = drm_atomic_helper_connector_reset,
+ .atomic_create_state = drm_atomic_helper_connector_create_state,
.atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
.atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
};
diff --git a/drivers/gpu/drm/imx/ipuv3/imx-tve.c b/drivers/gpu/drm/imx/ipuv3/imx-tve.c
index e861b8b9d8fa..4ece79374372 100644
--- a/drivers/gpu/drm/imx/ipuv3/imx-tve.c
+++ b/drivers/gpu/drm/imx/ipuv3/imx-tve.c
@@ -314,7 +314,7 @@ static void imx_tve_connector_destroy(struct drm_connector *connector)
static const struct drm_connector_funcs imx_tve_connector_funcs = {
.fill_modes = drm_helper_probe_single_connector_modes,
.destroy = imx_tve_connector_destroy,
- .reset = drm_atomic_helper_connector_reset,
+ .atomic_create_state = drm_atomic_helper_connector_create_state,
.atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
.atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
};
diff --git a/drivers/gpu/drm/nouveau/nouveau_abi16.c b/drivers/gpu/drm/nouveau/nouveau_abi16.c
index e907c1d07887..d55d073f4804 100644
--- a/drivers/gpu/drm/nouveau/nouveau_abi16.c
+++ b/drivers/gpu/drm/nouveau/nouveau_abi16.c
@@ -306,7 +306,7 @@ nouveau_abi16_ioctl_getparam(ABI16_IOCTL_ARGS)
getparam->value = 1;
break;
case NOUVEAU_GETPARAM_GRAPH_UNITS:
- getparam->value = nvkm_gr_units(gr);
+ getparam->value = gr ? nvkm_gr_units(gr) : 0;
break;
case NOUVEAU_GETPARAM_EXEC_PUSH_MAX: {
int ib_max = getparam_dma_ib_max(device);
diff --git a/drivers/gpu/drm/panel/Kconfig b/drivers/gpu/drm/panel/Kconfig
index 53a1fc2913fe..7a520b4d60fe 100644
--- a/drivers/gpu/drm/panel/Kconfig
+++ b/drivers/gpu/drm/panel/Kconfig
@@ -695,6 +695,8 @@ config DRM_PANEL_NOVATEK_NT36536
depends on OF
depends on DRM_MIPI_DSI
depends on BACKLIGHT_CLASS_DEVICE
+ select DRM_DISPLAY_DSC_HELPER
+ select DRM_DISPLAY_HELPER
select DRM_KMS_HELPER
help
Say Y here if you want to enable support for Novatek NT36536-based
@@ -753,6 +755,15 @@ config DRM_PANEL_NOVATEK_NT39016
Say Y here if you want to enable support for the panels built
around the Novatek NT39016 display controller.
+config DRM_PANEL_NOVATEK_NT51021
+ tristate "Novatek NT51021 DSI panel"
+ depends on OF
+ depends on DRM_MIPI_DSI
+ depends on BACKLIGHT_CLASS_DEVICE
+ help
+ Say Y here if you want to enable support for the panels built
+ around the Novatek NT51021 Display controller.
+
config DRM_PANEL_OLIMEX_LCD_OLINUXINO
tristate "Olimex LCD-OLinuXino panel"
depends on OF
@@ -850,6 +861,21 @@ config DRM_PANEL_RAYDIUM_RM692E5
Say Y here if you want to enable support for Raydium RM692E5-based
display panels, such as the one found in the Fairphone 5 smartphone.
+config DRM_PANEL_RAYDIUM_RM69220
+ tristate "Raydium RM69220-based DSI panel"
+ depends on OF
+ depends on DRM_MIPI_DSI
+ depends on BACKLIGHT_CLASS_DEVICE
+ select DRM_DISPLAY_DSC_HELPER
+ select DRM_DISPLAY_HELPER
+ help
+ Say Y here if you want to enable support for Raydium RM69220-based
+ display panels, such as the BOE BF068MWM-TD0 which can be found in
+ the Nothing Phone (3a).
+
+ To compile this driver as a module, choose M here: the module
+ will be called panel-raydium-rm69220.
+
config DRM_PANEL_RAYDIUM_RM69380
tristate "Raydium RM69380-based DSI panel"
depends on GPIOLIB
diff --git a/drivers/gpu/drm/panel/Makefile b/drivers/gpu/drm/panel/Makefile
index f2c9c80a218f..da5175909d54 100644
--- a/drivers/gpu/drm/panel/Makefile
+++ b/drivers/gpu/drm/panel/Makefile
@@ -72,6 +72,7 @@ obj-$(CONFIG_DRM_PANEL_NOVATEK_NT36672E) += panel-novatek-nt36672e.o
obj-$(CONFIG_DRM_PANEL_NOVATEK_NT37700F) += panel-novatek-nt37700f.o
obj-$(CONFIG_DRM_PANEL_NOVATEK_NT37801) += panel-novatek-nt37801.o
obj-$(CONFIG_DRM_PANEL_NOVATEK_NT39016) += panel-novatek-nt39016.o
+obj-$(CONFIG_DRM_PANEL_NOVATEK_NT51021) += panel-novatek-nt51021.o
obj-$(CONFIG_DRM_PANEL_MANTIX_MLAF057WE51) += panel-mantix-mlaf057we51.o
obj-$(CONFIG_DRM_PANEL_OLIMEX_LCD_OLINUXINO) += panel-olimex-lcd-olinuxino.o
obj-$(CONFIG_DRM_PANEL_ORISETECH_OTA5601A) += panel-orisetech-ota5601a.o
@@ -83,6 +84,7 @@ obj-$(CONFIG_DRM_PANEL_RAYDIUM_RM67191) += panel-raydium-rm67191.o
obj-$(CONFIG_DRM_PANEL_RAYDIUM_RM67200) += panel-raydium-rm67200.o
obj-$(CONFIG_DRM_PANEL_RAYDIUM_RM68200) += panel-raydium-rm68200.o
obj-$(CONFIG_DRM_PANEL_RAYDIUM_RM692E5) += panel-raydium-rm692e5.o
+obj-$(CONFIG_DRM_PANEL_RAYDIUM_RM69220) += panel-raydium-rm69220.o
obj-$(CONFIG_DRM_PANEL_RAYDIUM_RM69380) += panel-raydium-rm69380.o
obj-$(CONFIG_DRM_PANEL_RENESAS_R61307) += panel-renesas-r61307.o
obj-$(CONFIG_DRM_PANEL_RENESAS_R63419) += panel-renesas-r63419.o
diff --git a/drivers/gpu/drm/panel/panel-edp.c b/drivers/gpu/drm/panel/panel-edp.c
index 5d7d696b7725..f83b93a1a414 100644
--- a/drivers/gpu/drm/panel/panel-edp.c
+++ b/drivers/gpu/drm/panel/panel-edp.c
@@ -1958,6 +1958,7 @@ static const struct edp_panel_entry edp_panels[] = {
EDP_PANEL_ENTRY('A', 'U', 'O', 0x0290, &delay_200_500_e50, "B140XTN07.5"),
EDP_PANEL_ENTRY('A', 'U', 'O', 0x04a4, &delay_200_500_e50, "B122UAN01.0"),
EDP_PANEL_ENTRY('A', 'U', 'O', 0x0ba4, &delay_200_500_e50, "B140QAX01.H"),
+ EDP_PANEL_ENTRY('A', 'U', 'O', 0x0db3, &delay_200_500_e50, "Unknown"),
EDP_PANEL_ENTRY('A', 'U', 'O', 0x105c, &delay_200_500_e50, "B116XTN01.0"),
EDP_PANEL_ENTRY('A', 'U', 'O', 0x1062, &delay_200_500_e50, "B120XAN01.0"),
EDP_PANEL_ENTRY('A', 'U', 'O', 0x125c, &delay_200_500_e50, "Unknown"),
@@ -1986,6 +1987,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', 0x65a9, &delay_200_500_e50, "B140HAN04.0"),
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"),
@@ -2003,6 +2005,7 @@ static const struct edp_panel_entry edp_panels[] = {
EDP_PANEL_ENTRY('A', 'U', 'O', 0xd497, &delay_200_500_e50, "B120XAN01.0"),
EDP_PANEL_ENTRY('A', 'U', 'O', 0xf390, &delay_200_500_e50, "B140XTN07.7"),
+ EDP_PANEL_ENTRY('B', 'O', 'E', 0x03f4, &delay_200_500_e50, "NV140FHD-N43"),
EDP_PANEL_ENTRY('B', 'O', 'E', 0x0607, &delay_200_500_e200, "Unknown"),
EDP_PANEL_ENTRY('B', 'O', 'E', 0x0608, &delay_200_500_e50, "NT116WHM-N11"),
EDP_PANEL_ENTRY('B', 'O', 'E', 0x0609, &delay_200_500_e50_po2e200, "NT116WHM-N21 V4.1"),
@@ -2198,6 +2201,7 @@ static const struct edp_panel_entry edp_panels[] = {
EDP_PANEL_ENTRY('S', 'H', 'P', 0x1523, &delay_80_500_e50, "LQ140M1JW46"),
EDP_PANEL_ENTRY('S', 'H', 'P', 0x153a, &delay_200_500_e50, "LQ140T1JH01"),
EDP_PANEL_ENTRY('S', 'H', 'P', 0x154c, &delay_200_500_p2e100, "LQ116M1JW10"),
+ EDP_PANEL_ENTRY('S', 'H', 'P', 0x1572, &delay_200_500_e200, "LQ138P1JX61"),
EDP_PANEL_ENTRY('S', 'H', 'P', 0x158f, &delay_200_500_p2e100, "LQ134Z1"),
EDP_PANEL_ENTRY('S', 'H', 'P', 0x1593, &delay_200_500_p2e100, "LQ134N1"),
EDP_PANEL_ENTRY('S', 'H', 'P', 0x15a7, &delay_200_500_e200, "LQ120P1JX51"),
diff --git a/drivers/gpu/drm/panel/panel-novatek-nt51021.c b/drivers/gpu/drm/panel/panel-novatek-nt51021.c
new file mode 100644
index 000000000000..94e7d6aaf0cb
--- /dev/null
+++ b/drivers/gpu/drm/panel/panel-novatek-nt51021.c
@@ -0,0 +1,875 @@
+// SPDX-License-Identifier: GPL-2.0-only
+// Copyright (c) 2026, Ryan Brue <ryanbrue.dev@gmail.com>
+// Copyright (c) 2025, Nickolay Goppen <setotau@mainlining.org>
+// Copyright (c) 2024, Alexey Minnekhanov <alexeymin@minlexx.ru>
+
+/*
+ * This panel IC doesn't use the set_display_on command although
+ * it uses set_display_off command. This is not an error.
+ */
+#include <linux/delay.h>
+#include <linux/gpio/consumer.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/regulator/consumer.h>
+
+#include <drm/drm_mipi_dsi.h>
+#include <drm/drm_modes.h>
+#include <drm/drm_panel.h>
+#include <drm/drm_probe_helper.h>
+
+struct novatek_nt51021_desc;
+
+struct novatek_nt51021_variant {
+ int (*init)(struct novatek_nt51021_desc *ctx);
+ int (*off)(struct novatek_nt51021_desc *ctx);
+
+ const struct drm_display_mode *display_mode;
+ unsigned long mode_flags;
+};
+
+struct novatek_nt51021_desc {
+ struct drm_panel panel;
+ struct mipi_dsi_device *dsi;
+ struct regulator_bulk_data *supplies;
+ struct gpio_desc *reset_gpio;
+
+ const struct novatek_nt51021_variant *variant;
+};
+
+static const struct regulator_bulk_data nt51021_novatek_supplies[] = {
+ { .supply = "vdd" },
+ { .supply = "avdd" },
+ { .supply = "havdd" },
+};
+
+static inline struct novatek_nt51021_desc *to_novatek_panel(struct drm_panel *panel)
+{
+ return container_of_const(panel, struct novatek_nt51021_desc, panel);
+}
+
+static void nt51021_novatek_reset(struct novatek_nt51021_desc *ctx)
+{
+ gpiod_set_value_cansleep(ctx->reset_gpio, 1);
+ msleep(20);
+ gpiod_set_value_cansleep(ctx->reset_gpio, 0);
+ usleep_range(10000, 11000);
+}
+
+static void nt51021_cabc_on(struct mipi_dsi_multi_context *dsi_ctx)
+{
+ if (dsi_ctx->accum_err)
+ return;
+
+ mipi_dsi_generic_write_seq_multi(dsi_ctx, 0x8f, 0x00);
+ mipi_dsi_usleep_range(dsi_ctx, 1000, 2000);
+ mipi_dsi_generic_write_seq_multi(dsi_ctx, 0x8f, 0xa5);
+ mipi_dsi_usleep_range(dsi_ctx, 1000, 2000);
+ mipi_dsi_generic_write_seq_multi(dsi_ctx, 0x83, 0xbb);
+ mipi_dsi_generic_write_seq_multi(dsi_ctx, 0x84, 0x22);
+ mipi_dsi_generic_write_seq_multi(dsi_ctx, 0x90, 0x40);
+ mipi_dsi_generic_write_seq_multi(dsi_ctx, 0x8f, 0x00);
+ mipi_dsi_usleep_range(dsi_ctx, 1000, 2000);
+}
+
+static void nt51021_cabc_off(struct mipi_dsi_multi_context *dsi_ctx)
+{
+ if (dsi_ctx->accum_err)
+ return;
+
+ mipi_dsi_generic_write_seq_multi(dsi_ctx, 0x8f, 0xa5);
+ mipi_dsi_usleep_range(dsi_ctx, 1000, 2000);
+ mipi_dsi_generic_write_seq_multi(dsi_ctx, 0x83, 0xbb);
+ mipi_dsi_generic_write_seq_multi(dsi_ctx, 0x84, 0x22);
+ mipi_dsi_generic_write_seq_multi(dsi_ctx, 0x90, 0xc0);
+ mipi_dsi_generic_write_seq_multi(dsi_ctx, 0x8f, 0x00);
+ mipi_dsi_usleep_range(dsi_ctx, 1000, 2000);
+}
+
+static int boe_tv080wum_nx2_init(struct novatek_nt51021_desc *ctx)
+{
+ struct mipi_dsi_multi_context dsi_ctx = { .dsi = ctx->dsi };
+
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0x8f, 0xa5);
+ mipi_dsi_usleep_range(&dsi_ctx, 1000, 2000);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0x01, 0x00);
+ mipi_dsi_msleep(&dsi_ctx, 20);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0x8f, 0xa5);
+ mipi_dsi_usleep_range(&dsi_ctx, 1000, 2000);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0x83, 0x00);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0x84, 0x00);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0x8c, 0x8e);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xcd, 0x6c);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xc0, 0x8b);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xc8, 0xf0);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0x8b, 0x10);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xa9, 0x20);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0x97, 0x00);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xfa, 0x15);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xfd, 0x1b);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0x83, 0xaa);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0x84, 0x11);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xa9, 0x4b);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0x85, 0x04);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0x86, 0x08);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0x98, 0xc1);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0x83, 0xaa);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0x84, 0x11);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xc0, 0x05);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xc1, 0x0c);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xc2, 0x22);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xc3, 0x34);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xc4, 0x44);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xc5, 0x52);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xc6, 0x5d);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xc7, 0x66);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xc8, 0x70);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xc9, 0x00);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xca, 0x11);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xcb, 0x3b);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xcc, 0x53);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xcd, 0x69);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xce, 0x6a);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xcf, 0x6c);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xd0, 0x6b);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xd1, 0x75);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xd2, 0x83);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xd3, 0x99);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xd4, 0x9a);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xd5, 0xd0);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xd6, 0xd6);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xd7, 0xdb);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xd8, 0xe1);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xd9, 0xe7);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xda, 0xed);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xdb, 0xf4);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xdc, 0xfb);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xdd, 0xff);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xde, 0xff);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xdf, 0x0f);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xe0, 0x15);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xe1, 0x1c);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xe2, 0x32);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xe3, 0x44);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xe4, 0x52);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xe5, 0x60);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xe6, 0x6b);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xe7, 0x74);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xe8, 0x7e);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xe9, 0x14);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xea, 0x25);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xeb, 0x4d);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xec, 0x63);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xed, 0x71);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xee, 0x6a);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xef, 0x6c);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xf0, 0x6b);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xf1, 0x75);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xf2, 0x83);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xf3, 0x99);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xf4, 0x9a);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xf5, 0xd0);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xf6, 0xd6);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xf7, 0xdb);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xf8, 0xe1);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xf9, 0xe7);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xfa, 0xed);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xfb, 0xf4);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xfc, 0xfb);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xfd, 0xff);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xfe, 0xff);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xff, 0x0f);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0x83, 0xbb);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0x84, 0x22);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xa1, 0xff);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xa2, 0xfe);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xa3, 0xfa);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xa4, 0xf7);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xa5, 0xf3);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xa6, 0xf1);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xa7, 0xed);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xa8, 0xeb);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xa9, 0xe9);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xaa, 0xe6);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xaf, 0x00);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xb0, 0x35);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xb1, 0x89);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xb2, 0x99);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xb3, 0x99);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xb4, 0x0d);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xb5, 0x1a);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xb6, 0x16);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0x9a, 0x10);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0x9b, 0x00);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0x96, 0xe6);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0x99, 0x06);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xc0, 0x05);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xc1, 0x0c);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xc2, 0x22);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xc3, 0x34);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xc4, 0x44);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xc5, 0x52);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xc6, 0x5d);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xc7, 0x66);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xc8, 0x70);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xc9, 0x00);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xca, 0x11);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xcb, 0x3b);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xcc, 0x53);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xcd, 0x69);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xce, 0x6a);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xcf, 0x6c);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xd0, 0x6b);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xd1, 0x75);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xd2, 0x83);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xd3, 0x99);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xd4, 0x9a);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xd5, 0xd0);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xd6, 0xd6);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xd7, 0xdb);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xd8, 0xe1);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xd9, 0xe7);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xda, 0xed);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xdb, 0xf4);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xdc, 0xfb);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xdd, 0xff);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xde, 0xff);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xdf, 0x0f);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xe0, 0x15);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xe1, 0x1c);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xe2, 0x32);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xe3, 0x44);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xe4, 0x52);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xe5, 0x60);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xe6, 0x6b);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xe7, 0x74);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xe8, 0x7e);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xe9, 0x14);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xea, 0x25);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xeb, 0x4d);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xec, 0x63);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xed, 0x71);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xee, 0x6a);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xef, 0x6c);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xf0, 0x6b);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xf1, 0x75);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xf2, 0x83);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xf3, 0x99);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xf4, 0x9a);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xf5, 0xd0);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xf6, 0xd6);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xf7, 0xdb);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xf8, 0xe1);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xf9, 0xe7);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xfa, 0xed);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xfb, 0xf4);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xfc, 0xfb);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xfd, 0xff);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xfe, 0xff);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xff, 0x0f);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0x83, 0xcc);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0x84, 0x33);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xc0, 0x05);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xc1, 0x0c);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xc2, 0x22);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xc3, 0x34);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xc4, 0x44);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xc5, 0x52);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xc6, 0x5d);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xc7, 0x66);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xc8, 0x70);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xc9, 0x00);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xca, 0x11);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xcb, 0x3b);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xcc, 0x53);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xcd, 0x69);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xce, 0x6a);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xcf, 0x6c);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xd0, 0x6b);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xd1, 0x75);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xd2, 0x83);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xd3, 0x99);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xd4, 0x9a);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xd5, 0xd0);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xd6, 0xd6);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xd7, 0xdb);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xd8, 0xe1);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xd9, 0xe7);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xda, 0xed);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xdb, 0xf4);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xdc, 0xfb);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xdd, 0xff);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xde, 0xff);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xdf, 0x0f);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xe0, 0x15);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xe1, 0x1c);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xe2, 0x32);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xe3, 0x44);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xe4, 0x52);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xe5, 0x60);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xe6, 0x6b);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xe7, 0x74);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xe8, 0x7e);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xe9, 0x14);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xea, 0x25);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xeb, 0x4d);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xec, 0x63);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xed, 0x71);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xee, 0x6a);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xef, 0x6c);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xf0, 0x6b);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xf1, 0x75);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xf2, 0x83);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xf3, 0x99);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xf4, 0x9a);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xf5, 0xd0);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xf6, 0xd6);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xf7, 0xdb);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xf8, 0xe1);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xf9, 0xe7);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xfa, 0xed);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xfb, 0xf4);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xfc, 0xfb);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xfd, 0xff);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xfe, 0xff);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xff, 0x0f);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0x83, 0x00);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0x84, 0x00);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0x9f, 0xff);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0x83, 0xbb);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0x84, 0x22);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0x94, 0xba);
+ mipi_dsi_dcs_exit_sleep_mode_multi(&dsi_ctx);
+ mipi_dsi_usleep_range(&dsi_ctx, 5000, 6000);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0x8f, 0x00);
+ mipi_dsi_usleep_range(&dsi_ctx, 1000, 2000);
+
+ nt51021_cabc_on(&dsi_ctx);
+
+ return dsi_ctx.accum_err;
+}
+
+static int boe_tv101wum_nx0_init(struct novatek_nt51021_desc *ctx)
+{
+ struct mipi_dsi_multi_context dsi_ctx = { .dsi = ctx->dsi };
+
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0x8f, 0xa5);
+ mipi_dsi_usleep_range(&dsi_ctx, 1000, 2000);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0x01, 0x00);
+ mipi_dsi_msleep(&dsi_ctx, 20);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0x8f, 0xa5);
+ mipi_dsi_usleep_range(&dsi_ctx, 1000, 2000);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0x83, 0x00);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0x84, 0x00);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0x8c, 0x8e);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xfa, 0x12);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xfd, 0x1b);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xcd, 0x6c);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xc8, 0xfc);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0x97, 0x00);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0x8b, 0x10);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xa9, 0x20);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0x83, 0xaa);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0x84, 0x11);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xa9, 0x4b);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0x85, 0x04);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0x86, 0x08);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0x98, 0xc1);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0x9c, 0x10);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0x83, 0xaa);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0x84, 0x11);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xc0, 0x0e);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xc1, 0x11);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xc2, 0x1f);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xc3, 0x30);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xc4, 0x3f);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xc5, 0x4b);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xc6, 0x55);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xc7, 0x5d);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xc8, 0x68);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xc9, 0xe3);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xca, 0xea);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xcb, 0x01);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xcc, 0x0a);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xcd, 0x0a);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xce, 0x07);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xcf, 0x08);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xd0, 0x09);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xd1, 0x1d);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xd2, 0x31);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xd3, 0x52);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xd4, 0x5b);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xd5, 0xb6);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xd6, 0xbd);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xd7, 0xc5);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xd8, 0xcd);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xd9, 0xd6);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xda, 0xe0);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xdb, 0xeb);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xdc, 0xf8);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xdd, 0xff);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xde, 0xfc);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xdf, 0x0f);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xe0, 0x0e);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xe1, 0x11);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xe2, 0x1f);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xe3, 0x30);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xe4, 0x3f);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xe5, 0x4b);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xe6, 0x55);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xe7, 0x5d);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xe8, 0x68);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xe9, 0xe3);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xea, 0xea);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xeb, 0x01);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xec, 0x0a);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xed, 0x0a);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xee, 0x07);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xef, 0x08);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xf0, 0x09);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xf1, 0x1d);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xf2, 0x31);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xf3, 0x52);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xf4, 0x5b);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xf5, 0xb6);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xf6, 0xbd);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xf7, 0xc5);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xf8, 0xcd);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xf9, 0xd6);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xfa, 0xe0);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xfb, 0xeb);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xfc, 0xf8);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xfd, 0xff);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xfe, 0xfc);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xff, 0x0f);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0x83, 0xbb);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0x84, 0x22);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xa1, 0xff);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xa2, 0xfe);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xa3, 0xfa);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xa4, 0xf7);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xa5, 0xf3);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xa6, 0xf1);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xa7, 0xed);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xa8, 0xeb);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xa9, 0xe9);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xaa, 0xe6);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xaf, 0x00);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xb0, 0x12);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xb1, 0x23);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xb2, 0x34);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xb3, 0x77);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xb4, 0x0d);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xb5, 0x1a);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xb6, 0x16);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0x9a, 0x10);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0x9b, 0x00);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0x96, 0xe6);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0x99, 0x06);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xc0, 0x0e);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xc1, 0x11);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xc2, 0x1f);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xc3, 0x30);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xc4, 0x3f);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xc5, 0x4b);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xc6, 0x55);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xc7, 0x5d);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xc8, 0x68);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xc9, 0xe3);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xca, 0xea);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xcb, 0x01);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xcc, 0x0a);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xcd, 0x0a);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xce, 0x07);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xcf, 0x08);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xd0, 0x09);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xd1, 0x1d);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xd2, 0x31);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xd3, 0x52);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xd4, 0x5b);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xd5, 0xb6);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xd6, 0xbd);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xd7, 0xc5);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xd8, 0xcd);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xd9, 0xd6);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xda, 0xe0);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xdb, 0xeb);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xdc, 0xf8);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xdd, 0xff);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xde, 0xfc);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xdf, 0x0f);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xe0, 0x0e);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xe1, 0x11);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xe2, 0x1f);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xe3, 0x30);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xe4, 0x3f);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xe5, 0x4b);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xe6, 0x55);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xe7, 0x5d);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xe8, 0x68);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xe9, 0xe3);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xea, 0xea);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xeb, 0x01);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xec, 0x0a);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xed, 0x0a);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xee, 0x07);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xef, 0x08);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xf0, 0x09);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xf1, 0x1d);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xf2, 0x31);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xf3, 0x52);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xf4, 0x5b);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xf5, 0xb6);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xf6, 0xbd);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xf7, 0xc5);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xf8, 0xcd);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xf9, 0xd6);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xfa, 0xe0);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xfb, 0xeb);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xfc, 0xf8);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xfd, 0xff);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xfe, 0xfc);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xff, 0x0f);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0x83, 0xcc);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0x84, 0x33);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xc0, 0x0e);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xc1, 0x11);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xc2, 0x1f);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xc3, 0x30);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xc4, 0x3f);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xc5, 0x4b);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xc6, 0x55);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xc7, 0x5d);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xc8, 0x68);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xc9, 0xe3);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xca, 0xea);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xcb, 0x01);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xcc, 0x0a);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xcd, 0x0a);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xce, 0x07);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xcf, 0x08);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xd0, 0x09);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xd1, 0x1d);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xd2, 0x31);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xd3, 0x52);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xd4, 0x5b);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xd5, 0xb6);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xd6, 0xbd);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xd7, 0xc5);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xd8, 0xcd);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xd9, 0xd6);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xda, 0xe0);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xdb, 0xeb);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xdc, 0xf8);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xdd, 0xff);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xde, 0xfc);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xdf, 0x0f);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xe0, 0x0e);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xe1, 0x11);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xe2, 0x1f);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xe3, 0x30);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xe4, 0x3f);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xe5, 0x4b);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xe6, 0x55);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xe7, 0x5d);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xe8, 0x68);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xe9, 0xe3);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xea, 0xea);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xeb, 0x01);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xec, 0x0a);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xed, 0x0a);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xee, 0x07);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xef, 0x08);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xf0, 0x09);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xf1, 0x1d);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xf2, 0x31);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xf3, 0x52);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xf4, 0x5b);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xf5, 0xb6);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xf6, 0xbd);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xf7, 0xc5);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xf8, 0xcd);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xf9, 0xd6);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xfa, 0xe0);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xfb, 0xeb);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xfc, 0xf8);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xfd, 0xff);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xfe, 0xfc);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xff, 0x0f);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0x83, 0x00);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0x84, 0x00);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0x9f, 0xff);
+ mipi_dsi_dcs_exit_sleep_mode_multi(&dsi_ctx);
+ mipi_dsi_usleep_range(&dsi_ctx, 5000, 6000);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0x8f, 0x00);
+ mipi_dsi_usleep_range(&dsi_ctx, 1000, 2000);
+
+ nt51021_cabc_on(&dsi_ctx);
+ mipi_dsi_usleep_range(&dsi_ctx, 1000, 2000);
+
+ return dsi_ctx.accum_err;
+}
+
+static int boe_tv101wum_nm0_init(struct novatek_nt51021_desc *ctx)
+{
+ struct mipi_dsi_multi_context dsi_ctx = { .dsi = ctx->dsi };
+
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0x8f, 0xa5);
+ mipi_dsi_usleep_range(&dsi_ctx, 1000, 2000);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0x01, 0x00);
+ mipi_dsi_msleep(&dsi_ctx, 20);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0x8f, 0xa5);
+ mipi_dsi_usleep_range(&dsi_ctx, 1000, 2000);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0x83, 0x00);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0x84, 0x00);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0x8c, 0x80);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xcd, 0x6c);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xc0, 0x8b);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xc8, 0xf0);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0x97, 0x00);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0x8b, 0x10);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xa9, 0x20);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0x83, 0xaa);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0x84, 0x11);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xa9, 0x4b);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0x85, 0x04);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0x86, 0x08);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0x9c, 0x10);
+ mipi_dsi_dcs_exit_sleep_mode_multi(&dsi_ctx);
+ mipi_dsi_msleep(&dsi_ctx, 120);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0x8f, 0x00);
+ mipi_dsi_msleep(&dsi_ctx, 5);
+
+ return dsi_ctx.accum_err;
+}
+
+static int nt51021_novatek_with_cabc_off(struct novatek_nt51021_desc *ctx)
+{
+ struct mipi_dsi_multi_context dsi_ctx = { .dsi = ctx->dsi };
+
+ nt51021_cabc_off(&dsi_ctx);
+
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0x8f, 0xa5);
+ mipi_dsi_msleep(&dsi_ctx, 20);
+ mipi_dsi_dcs_set_display_off_multi(&dsi_ctx);
+ mipi_dsi_msleep(&dsi_ctx, 130);
+ mipi_dsi_dcs_enter_sleep_mode_multi(&dsi_ctx);
+ mipi_dsi_msleep(&dsi_ctx, 70);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0x8f, 0x00);
+ mipi_dsi_usleep_range(&dsi_ctx, 4000, 5000);
+
+ return dsi_ctx.accum_err;
+}
+
+static int nt51021_novatek_off(struct novatek_nt51021_desc *ctx)
+{
+ struct mipi_dsi_multi_context dsi_ctx = { .dsi = ctx->dsi };
+
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0x8f, 0xa5);
+ mipi_dsi_msleep(&dsi_ctx, 20);
+ mipi_dsi_dcs_set_display_off_multi(&dsi_ctx);
+ mipi_dsi_msleep(&dsi_ctx, 130);
+ mipi_dsi_dcs_enter_sleep_mode_multi(&dsi_ctx);
+ mipi_dsi_msleep(&dsi_ctx, 70);
+ mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0x8f, 0x00);
+ mipi_dsi_usleep_range(&dsi_ctx, 4000, 5000);
+
+ return dsi_ctx.accum_err;
+}
+
+static int nt51021_novatek_prepare(struct drm_panel *panel)
+{
+ struct novatek_nt51021_desc *ctx = to_novatek_panel(panel);
+ struct device *dev = &ctx->dsi->dev;
+ int ret;
+
+ ret = regulator_bulk_enable(ARRAY_SIZE(nt51021_novatek_supplies), ctx->supplies);
+ if (ret < 0) {
+ dev_err(dev, "Failed to enable regulators: %d\n", ret);
+ return ret;
+ }
+ nt51021_novatek_reset(ctx);
+
+ ret = ctx->variant->init(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(nt51021_novatek_supplies), ctx->supplies);
+ return ret;
+ }
+
+ return 0;
+}
+
+static int nt51021_novatek_unprepare(struct drm_panel *panel)
+{
+ struct novatek_nt51021_desc *ctx = to_novatek_panel(panel);
+ struct device *dev = &ctx->dsi->dev;
+ int ret;
+
+ ret = ctx->variant->off(ctx);
+ if (ret < 0)
+ dev_err(dev, "Failed to un-initialize panel: %d\n", ret);
+
+ gpiod_set_value_cansleep(ctx->reset_gpio, 1);
+ regulator_bulk_disable(ARRAY_SIZE(nt51021_novatek_supplies), ctx->supplies);
+
+ return 0;
+}
+
+static int nt51021_novatek_get_modes(struct drm_panel *panel,
+ struct drm_connector *connector)
+{
+ struct novatek_nt51021_desc *ctx = to_novatek_panel(panel);
+
+ return drm_connector_helper_get_modes_fixed(connector,
+ ctx->variant->display_mode);
+}
+
+static const struct drm_panel_funcs nt51021_novatek_panel_funcs = {
+ .prepare = nt51021_novatek_prepare,
+ .unprepare = nt51021_novatek_unprepare,
+ .get_modes = nt51021_novatek_get_modes,
+};
+
+static int nt51021_novatek_probe(struct mipi_dsi_device *dsi)
+{
+ struct device *dev = &dsi->dev;
+ struct novatek_nt51021_desc *ctx;
+ int ret;
+
+ ctx = devm_drm_panel_alloc(dev, struct novatek_nt51021_desc, panel,
+ &nt51021_novatek_panel_funcs,
+ DRM_MODE_CONNECTOR_DSI);
+ if (IS_ERR(ctx))
+ return PTR_ERR(ctx);
+
+ ctx->variant = of_device_get_match_data(dev);
+ if (!ctx->variant)
+ return dev_err_probe(dev, -ENODEV, "No match data\n");
+
+ ret = devm_regulator_bulk_get_const(dev,
+ ARRAY_SIZE(nt51021_novatek_supplies),
+ nt51021_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");
+
+ ctx->dsi = dsi;
+ mipi_dsi_set_drvdata(dsi, ctx);
+
+ dsi->lanes = 4;
+ dsi->format = MIPI_DSI_FMT_RGB888;
+ dsi->mode_flags = ctx->variant->mode_flags;
+
+ ctx->panel.prepare_prev_first = true;
+
+ ret = drm_panel_of_backlight(&ctx->panel);
+ if (ret)
+ return dev_err_probe(dev, ret, "Failed to get backlight\n");
+
+ ret = devm_drm_panel_add(dev, &ctx->panel);
+ if (ret)
+ return ret;
+
+ return devm_mipi_dsi_attach(dev, dsi);
+}
+
+static const struct drm_display_mode boe_tv080wum_nx2_mode = {
+ .clock = (1200 + 84 + 4 + 60) * (1920 + 36 + 1 + 25) * 60 / 1000,
+ .hdisplay = 1200,
+ .hsync_start = 1200 + 84,
+ .hsync_end = 1200 + 84 + 4,
+ .htotal = 1200 + 84 + 4 + 60,
+ .vdisplay = 1920,
+ .vsync_start = 1920 + 36,
+ .vsync_end = 1920 + 36 + 1,
+ .vtotal = 1920 + 36 + 1 + 25,
+ .width_mm = 107,
+ .height_mm = 172,
+ .type = DRM_MODE_TYPE_DRIVER,
+};
+
+static const struct drm_display_mode boe_tv101wum_nm0_mode = {
+ .clock = 163333,
+ .hdisplay = 1200,
+ .hsync_start = 1200 + 110,
+ .hsync_end = 1200 + 110 + 1,
+ .htotal = 1200 + 110 + 1 + 32,
+ .vdisplay = 1920,
+ .vsync_start = 1920 + 11,
+ .vsync_end = 1920 + 11 + 1,
+ .vtotal = 1920 + 11 + 1 + 14,
+ .width_mm = 135,
+ .height_mm = 216,
+ .type = DRM_MODE_TYPE_DRIVER,
+};
+
+static const struct drm_display_mode boe_tv101wum_nx0_mode = {
+ .clock = (1200 + 172 + 4 + 32) * (1920 + 25 + 1 + 14) * 60 / 1000,
+ .hdisplay = 1200,
+ .hsync_start = 1200 + 172,
+ .hsync_end = 1200 + 172 + 4,
+ .htotal = 1200 + 172 + 4 + 32,
+ .vdisplay = 1920,
+ .vsync_start = 1920 + 25,
+ .vsync_end = 1920 + 25 + 1,
+ .vtotal = 1920 + 25 + 1 + 14,
+ .width_mm = 135,
+ .height_mm = 216,
+ .type = DRM_MODE_TYPE_DRIVER,
+};
+
+#define NT51021_BURST_MODE_FLAGS (MIPI_DSI_MODE_VIDEO | \
+ MIPI_DSI_MODE_VIDEO_BURST | \
+ MIPI_DSI_MODE_NO_EOT_PACKET | \
+ MIPI_DSI_MODE_LPM)
+
+static const struct novatek_nt51021_variant boe_tv080wum_nx2_data = {
+ .init = boe_tv080wum_nx2_init,
+ .off = nt51021_novatek_with_cabc_off,
+ .display_mode = &boe_tv080wum_nx2_mode,
+ .mode_flags = NT51021_BURST_MODE_FLAGS,
+};
+
+static const struct novatek_nt51021_variant boe_tv101wum_nm0_data = {
+ .init = boe_tv101wum_nm0_init,
+ .off = nt51021_novatek_off,
+ .display_mode = &boe_tv101wum_nm0_mode,
+ .mode_flags = MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_LPM,
+};
+
+static const struct novatek_nt51021_variant boe_tv101wum_nx0_data = {
+ .init = boe_tv101wum_nx0_init,
+ .off = nt51021_novatek_with_cabc_off,
+ .display_mode = &boe_tv101wum_nx0_mode,
+ .mode_flags = NT51021_BURST_MODE_FLAGS,
+};
+
+static const struct of_device_id nt51021_novatek_of_match[] = {
+ { .compatible = "boe,tv080wum-nx2", .data = &boe_tv080wum_nx2_data },
+ { .compatible = "boe,tv101wum-nm0", .data = &boe_tv101wum_nm0_data },
+ { .compatible = "boe,tv101wum-nx0", .data = &boe_tv101wum_nx0_data },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, nt51021_novatek_of_match);
+
+static struct mipi_dsi_driver nt51021_boe_driver = {
+ .probe = nt51021_novatek_probe,
+ .driver = {
+ .name = "panel-novatek-nt51021",
+ .of_match_table = nt51021_novatek_of_match,
+ },
+};
+module_mipi_dsi_driver(nt51021_boe_driver);
+
+MODULE_AUTHOR("Nickolay Goppen <setotau@mainlining.org>");
+MODULE_AUTHOR("Alexey Minnekhanov <alexeymin@minlexx.ru>");
+MODULE_AUTHOR("Ryan Brue <ryanbrue.dev@gmail.com>");
+MODULE_DESCRIPTION("DRM driver for Novatek NT51021-based DSI panels");
+MODULE_LICENSE("GPL");
diff --git a/drivers/gpu/drm/panel/panel-raydium-rm69220.c b/drivers/gpu/drm/panel/panel-raydium-rm69220.c
new file mode 100644
index 000000000000..3030f8b89760
--- /dev/null
+++ b/drivers/gpu/drm/panel/panel-raydium-rm69220.c
@@ -0,0 +1,417 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Generated with linux-mdss-dsi-panel-driver-generator from vendor device tree.
+ * Copyright (c) 2026 Alexander Koskovich <akoskovich@pm.me>
+ */
+
+#include <linux/backlight.h>
+#include <linux/delay.h>
+#include <linux/device-id/of.h>
+#include <linux/gpio/consumer.h>
+#include <linux/module.h>
+#include <linux/regulator/consumer.h>
+
+#include <video/mipi_display.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>
+
+struct raydium_rm69220 {
+ struct drm_panel panel;
+ struct mipi_dsi_device *dsi;
+ struct drm_dsc_config dsc;
+ struct regulator_bulk_data *supplies;
+ struct gpio_desc *reset_gpio;
+};
+
+static const struct regulator_bulk_data raydium_rm69220_supplies[] = {
+ { .supply = "vddio" },
+ { .supply = "dvdd" },
+ { .supply = "vci" },
+};
+
+static inline
+struct raydium_rm69220 *to_raydium_rm69220(struct drm_panel *panel)
+{
+ return container_of(panel, struct raydium_rm69220, panel);
+}
+
+static void raydium_rm69220_reset(struct raydium_rm69220 *ctx)
+{
+ gpiod_set_value_cansleep(ctx->reset_gpio, 0);
+ usleep_range(1000, 2000);
+ gpiod_set_value_cansleep(ctx->reset_gpio, 1);
+ usleep_range(1000, 2000);
+ gpiod_set_value_cansleep(ctx->reset_gpio, 0);
+ msleep(32);
+}
+
+static int raydium_rm69220_on(struct raydium_rm69220 *ctx)
+{
+ struct mipi_dsi_multi_context dsi_ctx = { .dsi = ctx->dsi };
+
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xfe, 0x76);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x9a, 0x10);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x9b, 0x00);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xfe, 0x77);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x9a, 0x10);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x9b, 0x00);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xfe, 0x78);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x9a, 0x10);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x9b, 0x00);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xfe, 0x79);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x9a, 0xf0);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x9b, 0x00);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xfe, 0x74);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x1a, 0xe0);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x1b, 0x00);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xfe, 0x40);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xa9, 0x68);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xab, 0x22);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xfe, 0x40);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xc0, 0xa6);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xbf, 0x87);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xfe, 0x82);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x00, 0x90);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xfe, 0xd4);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x61, 0x08);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xa2, 0x04);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xfe, 0x00);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xfa, 0x01);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xfe, 0xd2);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x97, 0x08);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x36, 0x11);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x39, 0xab);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x3a, 0x30);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x3b, 0x80);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x3d, 0x09);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x3f, 0x58);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x40, 0x04);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x41, 0x38);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x42, 0x00);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x43, 0x0d);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x44, 0x02);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x45, 0x1c);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x46, 0x02);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x47, 0x1c);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x48, 0x02);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x49, 0x00);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x4a, 0x02);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x4b, 0x0e);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x4d, 0x20);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x4e, 0x01);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x4f, 0x39);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x50, 0x00);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x51, 0x07);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x53, 0x0c);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x54, 0x08);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x55, 0x00);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x56, 0x07);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x58, 0xd3);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x59, 0x18);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x5a, 0x00);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x5b, 0x10);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x5c, 0xf0);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x5d, 0x07);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x5e, 0x10);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x5f, 0x20);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x60, 0x00);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x61, 0x06);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x62, 0x0f);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x63, 0x0f);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x64, 0x33);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x65, 0x0e);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x66, 0x1c);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x67, 0x2a);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x68, 0x38);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x69, 0x46);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x6a, 0x54);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x6b, 0x62);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x6c, 0x69);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x6d, 0x70);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x6e, 0x77);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x6f, 0x79);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x70, 0x7b);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x71, 0x7d);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x72, 0x7e);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x73, 0x01);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x74, 0x02);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x75, 0x22);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x76, 0x00);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x77, 0x2a);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x78, 0x40);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x79, 0x2a);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x7a, 0xbe);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x7b, 0x3a);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x7c, 0xfc);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x7d, 0x3a);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x7e, 0xfa);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x7f, 0x3a);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x80, 0xf8);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x81, 0x3b);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x82, 0x38);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x83, 0x3b);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x84, 0x78);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x85, 0x3b);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x86, 0xb6);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x87, 0x4b);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x88, 0xf6);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x89, 0x4c);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x8a, 0x34);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x8b, 0x4c);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x8c, 0x74);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x8d, 0x5c);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x8e, 0x74);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x8f, 0x8c);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x90, 0xf4);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x91, 0x00);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x92, 0x00);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x93, 0x00);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x94, 0x00);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x95, 0x00);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x96, 0x00);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xfe, 0xa0);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x66, 0x06);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xfe, 0x49);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x87, 0x00);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x3f, 0x00);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xfe, 0xa0);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x04, 0x07);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x4b, 0x40);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x4d, 0x40);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xfe, 0xa1);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x58, 0x67);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x75, 0xa7);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xfe, 0x00);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x2f, 0x00);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xfe, 0x00);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xfa, 0x01);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xc1, 0x03);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xc2, 0x03);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x35, 0x00);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x51, 0x0d, 0xbb);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xfe, 0x00);
+ mipi_dsi_dcs_exit_sleep_mode_multi(&dsi_ctx);
+ mipi_dsi_msleep(&dsi_ctx, 80);
+ mipi_dsi_dcs_set_display_on_multi(&dsi_ctx);
+ mipi_dsi_usleep_range(&dsi_ctx, 10000, 11000);
+
+ return dsi_ctx.accum_err;
+}
+
+static int raydium_rm69220_off(struct raydium_rm69220 *ctx)
+{
+ struct mipi_dsi_multi_context dsi_ctx = { .dsi = ctx->dsi };
+
+ mipi_dsi_dcs_set_display_off_multi(&dsi_ctx);
+ mipi_dsi_msleep(&dsi_ctx, 100);
+ mipi_dsi_dcs_enter_sleep_mode_multi(&dsi_ctx);
+ mipi_dsi_msleep(&dsi_ctx, 100);
+
+ return dsi_ctx.accum_err;
+}
+
+static int raydium_rm69220_prepare(struct drm_panel *panel)
+{
+ struct raydium_rm69220 *ctx = to_raydium_rm69220(panel);
+ struct device *dev = &ctx->dsi->dev;
+ struct drm_dsc_picture_parameter_set pps;
+ int ret;
+
+ ret = regulator_bulk_enable(ARRAY_SIZE(raydium_rm69220_supplies), ctx->supplies);
+ if (ret < 0) {
+ dev_err(dev, "Failed to enable regulators: %d\n", ret);
+ return ret;
+ }
+
+ raydium_rm69220_reset(ctx);
+
+ ret = raydium_rm69220_on(ctx);
+ if (ret < 0) {
+ dev_err(dev, "Failed to initialize panel: %d\n", ret);
+ goto err;
+ }
+
+ drm_dsc_pps_payload_pack(&pps, &ctx->dsc);
+
+ ret = mipi_dsi_picture_parameter_set(ctx->dsi, &pps);
+ if (ret < 0) {
+ dev_err(dev, "failed to transmit PPS: %d\n", ret);
+ goto err;
+ }
+
+ ret = mipi_dsi_compression_mode(ctx->dsi, true);
+ if (ret < 0) {
+ dev_err(dev, "failed to enable compression mode: %d\n", ret);
+ goto err;
+ }
+
+ msleep(28);
+
+ return 0;
+
+err:
+ gpiod_set_value_cansleep(ctx->reset_gpio, 1);
+ regulator_bulk_disable(ARRAY_SIZE(raydium_rm69220_supplies), ctx->supplies);
+ return ret;
+}
+
+static int raydium_rm69220_unprepare(struct drm_panel *panel)
+{
+ struct raydium_rm69220 *ctx = to_raydium_rm69220(panel);
+ struct device *dev = &ctx->dsi->dev;
+ int ret;
+
+ ret = raydium_rm69220_off(ctx);
+ if (ret < 0)
+ dev_err(dev, "Failed to un-initialize panel: %d\n", ret);
+
+ gpiod_set_value_cansleep(ctx->reset_gpio, 1);
+ regulator_bulk_disable(ARRAY_SIZE(raydium_rm69220_supplies), ctx->supplies);
+
+ return 0;
+}
+
+static const struct drm_display_mode raydium_rm69220_mode = {
+ .clock = (1080 + 36 + 4 + 36) * (2392 + 72 + 4 + 52) * 120 / 1000,
+ .hdisplay = 1080,
+ .hsync_start = 1080 + 36,
+ .hsync_end = 1080 + 36 + 4,
+ .htotal = 1080 + 36 + 4 + 36,
+ .vdisplay = 2392,
+ .vsync_start = 2392 + 72,
+ .vsync_end = 2392 + 72 + 4,
+ .vtotal = 2392 + 72 + 4 + 52,
+ .width_mm = 71,
+ .height_mm = 157,
+ .type = DRM_MODE_TYPE_DRIVER,
+};
+
+static int raydium_rm69220_get_modes(struct drm_panel *panel,
+ struct drm_connector *connector)
+{
+ return drm_connector_helper_get_modes_fixed(connector, &raydium_rm69220_mode);
+}
+
+static const struct drm_panel_funcs raydium_rm69220_panel_funcs = {
+ .prepare = raydium_rm69220_prepare,
+ .unprepare = raydium_rm69220_unprepare,
+ .get_modes = raydium_rm69220_get_modes,
+};
+
+static int raydium_rm69220_bl_update_status(struct backlight_device *bl)
+{
+ struct mipi_dsi_device *dsi = bl_get_data(bl);
+ u16 brightness = backlight_get_brightness(bl);
+ int ret;
+
+ dsi->mode_flags &= ~MIPI_DSI_MODE_LPM;
+
+ ret = mipi_dsi_dcs_set_display_brightness_large(dsi, brightness);
+ if (ret < 0)
+ return ret;
+
+ dsi->mode_flags |= MIPI_DSI_MODE_LPM;
+
+ return 0;
+}
+
+static const struct backlight_ops raydium_rm69220_bl_ops = {
+ .update_status = raydium_rm69220_bl_update_status,
+};
+
+static struct backlight_device *
+raydium_rm69220_create_backlight(struct mipi_dsi_device *dsi)
+{
+ struct device *dev = &dsi->dev;
+ const struct backlight_properties props = {
+ .type = BACKLIGHT_RAW,
+ .brightness = 2946,
+ .max_brightness = 3442, /* 4095 is HBM max */
+ };
+
+ return devm_backlight_device_register(dev, dev_name(dev), dev, dsi,
+ &raydium_rm69220_bl_ops, &props);
+}
+
+static int raydium_rm69220_probe(struct mipi_dsi_device *dsi)
+{
+ struct device *dev = &dsi->dev;
+ struct raydium_rm69220 *ctx;
+ int ret;
+
+ ctx = devm_drm_panel_alloc(dev, struct raydium_rm69220, panel,
+ &raydium_rm69220_panel_funcs,
+ DRM_MODE_CONNECTOR_DSI);
+ if (IS_ERR(ctx))
+ return PTR_ERR(ctx);
+
+ ret = devm_regulator_bulk_get_const(dev,
+ ARRAY_SIZE(raydium_rm69220_supplies),
+ raydium_rm69220_supplies,
+ &ctx->supplies);
+ if (ret < 0)
+ return ret;
+
+ ctx->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_LOW);
+ if (IS_ERR(ctx->reset_gpio))
+ return dev_err_probe(dev, PTR_ERR(ctx->reset_gpio),
+ "Failed to get reset-gpios\n");
+
+ ctx->dsi = dsi;
+
+ dsi->lanes = 4;
+ dsi->format = MIPI_DSI_FMT_RGB101010;
+ dsi->mode_flags = MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_NO_EOT_PACKET |
+ MIPI_DSI_CLOCK_NON_CONTINUOUS | MIPI_DSI_MODE_LPM |
+ MIPI_DSI_MODE_DSC_ALL_SLICES_IN_PKT;
+
+ ctx->panel.prepare_prev_first = true;
+
+ ctx->panel.backlight = raydium_rm69220_create_backlight(dsi);
+ if (IS_ERR(ctx->panel.backlight))
+ return dev_err_probe(dev, PTR_ERR(ctx->panel.backlight),
+ "Failed to create backlight\n");
+
+ ret = devm_drm_panel_add(dev, &ctx->panel);
+ if (ret < 0)
+ return ret;
+
+ /* This panel only supports DSC; unconditionally enable it */
+ dsi->dsc = &ctx->dsc;
+
+ ctx->dsc.dsc_version_major = 1;
+ ctx->dsc.dsc_version_minor = 1;
+ ctx->dsc.slice_height = 13;
+ ctx->dsc.slice_width = 540;
+
+ ctx->dsc.slice_count = 1080 / ctx->dsc.slice_width;
+ ctx->dsc.bits_per_component = 10;
+ ctx->dsc.bits_per_pixel = 8 << 4; /* 4 fractional bits */
+ ctx->dsc.block_pred_enable = true;
+
+ return devm_mipi_dsi_attach(dev, dsi);
+}
+
+static const struct of_device_id raydium_rm69220_of_match[] = {
+ { .compatible = "boe,bf068mwm-td0" },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, raydium_rm69220_of_match);
+
+static struct mipi_dsi_driver raydium_rm69220_driver = {
+ .probe = raydium_rm69220_probe,
+ .driver = {
+ .name = "panel-raydium-rm69220",
+ .of_match_table = raydium_rm69220_of_match,
+ },
+};
+module_mipi_dsi_driver(raydium_rm69220_driver);
+
+MODULE_AUTHOR("Alexander Koskovich <akoskovich@pm.me>");
+MODULE_DESCRIPTION("DRM driver for RM69220-equipped DSI panels");
+MODULE_LICENSE("GPL");
diff --git a/drivers/gpu/drm/panel/panel-ronbo-rb070d30.c b/drivers/gpu/drm/panel/panel-ronbo-rb070d30.c
index c3fbc459c7e0..9adf97aba5d3 100644
--- a/drivers/gpu/drm/panel/panel-ronbo-rb070d30.c
+++ b/drivers/gpu/drm/panel/panel-ronbo-rb070d30.c
@@ -75,15 +75,25 @@ static int rb070d30_panel_unprepare(struct drm_panel *panel)
static int rb070d30_panel_enable(struct drm_panel *panel)
{
struct rb070d30_panel *ctx = panel_to_rb070d30_panel(panel);
+ struct mipi_dsi_multi_context dsi_ctx = { .dsi = ctx->dsi };
- return mipi_dsi_dcs_exit_sleep_mode(ctx->dsi);
+ mipi_dsi_dcs_exit_sleep_mode_multi(&dsi_ctx);
+ return dsi_ctx.accum_err;
}
static int rb070d30_panel_disable(struct drm_panel *panel)
{
struct rb070d30_panel *ctx = panel_to_rb070d30_panel(panel);
+ struct mipi_dsi_multi_context dsi_ctx = { .dsi = ctx->dsi };
- return mipi_dsi_dcs_enter_sleep_mode(ctx->dsi);
+ mipi_dsi_dcs_enter_sleep_mode_multi(&dsi_ctx);
+
+ /*
+ * Returning an error would leave panel->enabled set, causing the
+ * next enable to be skipped even after unprepare powers off the panel.
+ * The helper above already logs any command failure.
+ */
+ return 0;
}
/* Default timings */
diff --git a/drivers/gpu/drm/panel/panel-simple.c b/drivers/gpu/drm/panel/panel-simple.c
index d7dbd7442919..a944c34642dc 100644
--- a/drivers/gpu/drm/panel/panel-simple.c
+++ b/drivers/gpu/drm/panel/panel-simple.c
@@ -4379,6 +4379,9 @@ static const struct panel_desc raystar_rff500f_awh_dnn = {
.width = 108,
.height = 65,
},
+ .delay = {
+ .enable = 250,
+ },
.bus_format = MEDIA_BUS_FMT_RGB888_1X7X4_SPWG,
.connector_type = DRM_MODE_CONNECTOR_LVDS,
};
diff --git a/drivers/gpu/drm/panel/panel-visionox-vtdr6130.c b/drivers/gpu/drm/panel/panel-visionox-vtdr6130.c
index 6733e0e1d061..d0db128cda81 100644
--- a/drivers/gpu/drm/panel/panel-visionox-vtdr6130.c
+++ b/drivers/gpu/drm/panel/panel-visionox-vtdr6130.c
@@ -11,6 +11,7 @@
#include <drm/display/drm_dsc.h>
#include <drm/display/drm_dsc_helper.h>
#include <drm/drm_mipi_dsi.h>
+#include <drm/drm_of.h>
#include <drm/drm_modes.h>
#include <drm/drm_panel.h>
@@ -18,10 +19,24 @@
struct visionox_vtdr6130 {
struct drm_panel panel;
- struct drm_dsc_config dsc;
struct mipi_dsi_device *dsi;
struct gpio_desc *reset_gpio;
struct regulator_bulk_data *supplies;
+ enum drm_panel_orientation orientation;
+ struct visionox_vtdr6130_desc *desc;
+};
+
+struct visionox_vtdr6130_desc {
+ unsigned int bpc;
+ unsigned int lanes;
+ unsigned long mode_flags;
+ enum mipi_dsi_pixel_format format;
+
+ const struct drm_display_mode *modes;
+ unsigned int num_modes;
+ int (*init_sequence)(struct visionox_vtdr6130 *ctx);
+
+ struct drm_dsc_config dsc;
};
static const struct regulator_bulk_data visionox_vtdr6130_supplies[] = {
@@ -45,6 +60,82 @@ static void visionox_vtdr6130_reset(struct visionox_vtdr6130 *ctx)
usleep_range(10000, 11000);
}
+static int retroidpocket_rp6_on(struct visionox_vtdr6130 *ctx)
+{
+ struct mipi_dsi_device *dsi = ctx->dsi;
+ struct mipi_dsi_multi_context dsi_ctx = { .dsi = dsi };
+ struct drm_dsc_picture_parameter_set pps;
+
+ dsi->mode_flags |= MIPI_DSI_MODE_LPM;
+
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x03, 0x01);
+ mipi_dsi_dcs_set_tear_on_multi(&dsi_ctx, MIPI_DSI_DCS_TEAR_MODE_VBLANK);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx,
+ MIPI_DCS_WRITE_CONTROL_DISPLAY, 0x20);
+ mipi_dsi_dcs_set_display_brightness_multi(&dsi_ctx, 0xff07);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x6f, 0x02);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, MIPI_DCS_SET_ADDRESS_MODE, 0x02);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x59, 0x00);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x6c, 0x00);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x6d, 0x00);
+
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x70, 0x11, 0x00, 0x00, 0x89,
+ 0x30, 0x80, 0x07, 0x80, 0x04, 0x38, 0x00,
+ 0x14, 0x02, 0x1c, 0x02, 0x1c, 0x02, 0x00,
+ 0x02, 0x25, 0x00, 0x20, 0x01, 0xd5, 0x00,
+ 0x07, 0x00, 0x0d, 0x05, 0x7a, 0x05, 0x16,
+ 0x18, 0x00, 0x10, 0xf0, 0x03, 0x0c, 0x20,
+ 0x00, 0x06, 0x0b, 0x0b, 0x33, 0x0e, 0x1c,
+ 0x2a, 0x38, 0x46, 0x54, 0x62, 0x69, 0x70,
+ 0x77, 0x79, 0x7b, 0x7d, 0x7e, 0x01, 0x02,
+ 0x01, 0x00, 0x09, 0x40, 0x09, 0xbe, 0x19,
+ 0xfc, 0x19, 0xfa, 0x19, 0xf8, 0x1a, 0x38,
+ 0x1a, 0x78, 0x1a, 0xb6, 0x2a, 0xb6, 0x2a,
+ 0xf4, 0x2a, 0xf4, 0x4b, 0x34, 0x63, 0x74,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00);
+
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xf0, 0xaa, 0x10);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xb1, 0x02, 0x15, 0x00, 0x14,
+ 0x00, 0x20, 0x00, 0x02, 0x16, 0x00, 0x14,
+ 0x07, 0xb4, 0x00, 0x02, 0x16, 0x00, 0x14,
+ 0x00, 0x10, 0x00);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xf0, 0xaa, 0x14);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xb2, 0x03, 0x33);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xb4, 0x0a, 0x72, 0x00, 0x00,
+ 0x0a, 0x72, 0x00, 0x00, 0x0a, 0x72, 0x00,
+ 0x00);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xb5, 0x00, 0x1e, 0x1e, 0x1e,
+ 0x1e, 0x1e, 0x1e, 0x06, 0x01);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xb9, 0x00, 0x00, 0x08, 0x1e,
+ 0x1e, 0x1e);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xbc, 0x10, 0x00, 0x00, 0x08,
+ 0x11, 0x1e, 0x7c, 0x1e, 0x7c, 0x1e, 0x7c);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xbe, 0x10, 0x10, 0x00, 0x0c,
+ 0x22, 0x1e, 0x62, 0x1e, 0x62, 0x1e, 0x62);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xff, 0x5a, 0x80);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x65, 0x13);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xfd, 0x01);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xff, 0x5a, 0x81);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xf9, 0x00);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xff, 0x5a, 0x83);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x65, 0x04);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xf8, 0x00);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xf0, 0xaa, 0x00);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xff, 0x5a, 0x00);
+
+ mipi_dsi_dcs_exit_sleep_mode_multi(&dsi_ctx);
+ mipi_dsi_msleep(&dsi_ctx, 150);
+
+ mipi_dsi_dcs_set_display_on_multi(&dsi_ctx);
+ mipi_dsi_msleep(&dsi_ctx, 50);
+
+ drm_dsc_pps_payload_pack(&pps, dsi->dsc);
+ mipi_dsi_picture_parameter_set_multi(&dsi_ctx, &pps);
+ mipi_dsi_compression_mode_multi(&dsi_ctx, true);
+
+ return dsi_ctx.accum_err;
+}
+
static int visionox_vtdr6130_on(struct visionox_vtdr6130 *ctx)
{
struct mipi_dsi_device *dsi = ctx->dsi;
@@ -160,7 +251,7 @@ static int visionox_vtdr6130_prepare(struct drm_panel *panel)
visionox_vtdr6130_reset(ctx);
- ret = visionox_vtdr6130_on(ctx);
+ ret = ctx->desc->init_sequence(ctx);
if (ret < 0) {
gpiod_set_value_cansleep(ctx->reset_gpio, 1);
regulator_bulk_disable(ARRAY_SIZE(visionox_vtdr6130_supplies),
@@ -185,26 +276,87 @@ static int visionox_vtdr6130_unprepare(struct drm_panel *panel)
return 0;
}
-static const struct drm_display_mode visionox_vtdr6130_mode = {
- .clock = (1080 + 20 + 2 + 20) * (2400 + 20 + 2 + 18) * 144 / 1000,
- .hdisplay = 1080,
- .hsync_start = 1080 + 20,
- .hsync_end = 1080 + 20 + 2,
- .htotal = 1080 + 20 + 2 + 20,
- .vdisplay = 2400,
- .vsync_start = 2400 + 20,
- .vsync_end = 2400 + 20 + 2,
- .vtotal = 2400 + 20 + 2 + 18,
- .width_mm = 71,
- .height_mm = 157,
+static const struct drm_display_mode retroidpocket_rp6_modes[] = {
+ {
+ .clock = (1080 + 22 + 2 + 16) * (1920 + 16 + 2 + 20) * 120 / 1000,
+ .hdisplay = 1080,
+ .hsync_start = 1080 + 22,
+ .hsync_end = 1080 + 22 + 2,
+ .htotal = 1080 + 22 + 2 + 16,
+ .vdisplay = 1920,
+ .vsync_start = 1920 + 16,
+ .vsync_end = 1920 + 16 + 2,
+ .vtotal = 1920 + 16 + 2 + 20,
+ .width_mm = 69,
+ .height_mm = 122,
+ },
+};
+
+static struct visionox_vtdr6130_desc retroidpocket_rp6_panel_desc = {
+ .modes = retroidpocket_rp6_modes,
+ .num_modes = ARRAY_SIZE(retroidpocket_rp6_modes),
+ .bpc = 8,
+ .lanes = 4,
+ .format = MIPI_DSI_FMT_RGB888,
+ .mode_flags = MIPI_DSI_MODE_NO_EOT_PACKET |
+ MIPI_DSI_CLOCK_NON_CONTINUOUS,
+ .init_sequence = retroidpocket_rp6_on,
+ .dsc = {
+ .dsc_version_major = 0x1,
+ .dsc_version_minor = 0x1,
+ .slice_height = 12,
+ .slice_width = 540,
+ .slice_count = 2,
+ .bits_per_component = 8,
+ .bits_per_pixel = 8 << 4,
+ .block_pred_enable = true,
+ },
+};
+
+static const struct drm_display_mode visionox_vtdr6130_modes[] = {
+ {
+ .clock = (1080 + 20 + 2 + 20) * (2400 + 20 + 2 + 18) * 144 / 1000,
+ .hdisplay = 1080,
+ .hsync_start = 1080 + 20,
+ .hsync_end = 1080 + 20 + 2,
+ .htotal = 1080 + 20 + 2 + 20,
+ .vdisplay = 2400,
+ .vsync_start = 2400 + 20,
+ .vsync_end = 2400 + 20 + 2,
+ .vtotal = 2400 + 20 + 2 + 18,
+ .width_mm = 71,
+ .height_mm = 157,
+ },
+};
+
+static struct visionox_vtdr6130_desc visionox_vtdr6130_panel_desc = {
+ .modes = visionox_vtdr6130_modes,
+ .num_modes = ARRAY_SIZE(visionox_vtdr6130_modes),
+ .bpc = 8,
+ .lanes = 4,
+ .format = MIPI_DSI_FMT_RGB888,
+ .mode_flags = MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_NO_EOT_PACKET |
+ MIPI_DSI_CLOCK_NON_CONTINUOUS,
+ .init_sequence = visionox_vtdr6130_on,
+ .dsc = {
+ .dsc_version_major = 0x1,
+ .dsc_version_minor = 0x2,
+ .slice_height = 40,
+ .slice_width = 540,
+ .slice_count = 2,
+ .bits_per_component = 8,
+ .bits_per_pixel = 8 << 4,
+ .block_pred_enable = true,
+ },
};
static int visionox_vtdr6130_get_modes(struct drm_panel *panel,
struct drm_connector *connector)
{
+ struct visionox_vtdr6130 *ctx = to_visionox_vtdr6130(panel);
struct drm_display_mode *mode;
- mode = drm_mode_duplicate(connector->dev, &visionox_vtdr6130_mode);
+ mode = drm_mode_duplicate(connector->dev, ctx->desc->modes);
if (!mode)
return -ENOMEM;
@@ -218,10 +370,18 @@ static int visionox_vtdr6130_get_modes(struct drm_panel *panel,
return 1;
}
+static enum drm_panel_orientation visionox_vtdr6130_get_orientation(struct drm_panel *panel)
+{
+ struct visionox_vtdr6130 *ctx = to_visionox_vtdr6130(panel);
+
+ return ctx->orientation;
+}
+
static const struct drm_panel_funcs visionox_vtdr6130_panel_funcs = {
.prepare = visionox_vtdr6130_prepare,
.unprepare = visionox_vtdr6130_unprepare,
.get_modes = visionox_vtdr6130_get_modes,
+ .get_orientation = visionox_vtdr6130_get_orientation,
};
static int visionox_vtdr6130_bl_update_status(struct backlight_device *bl)
@@ -274,25 +434,25 @@ static int visionox_vtdr6130_probe(struct mipi_dsi_device *dsi)
return dev_err_probe(dev, PTR_ERR(ctx->reset_gpio),
"Failed to get reset-gpios\n");
+ ctx->desc = (struct visionox_vtdr6130_desc *)of_device_get_match_data(dev);
+ if (!ctx->desc)
+ return -ENODEV;
+
ctx->dsi = dsi;
mipi_dsi_set_drvdata(dsi, ctx);
- ctx->dsc.dsc_version_major = 0x1;
- ctx->dsc.dsc_version_minor = 0x2;
- ctx->dsc.slice_height = 40;
- ctx->dsc.slice_width = 540;
- ctx->dsc.slice_count = 2;
- ctx->dsc.bits_per_component = 8;
- ctx->dsc.bits_per_pixel = 8 << 4;
- ctx->dsc.block_pred_enable = true;
-
- dsi->dsc = &ctx->dsc;
- dsi->lanes = 4;
- dsi->format = MIPI_DSI_FMT_RGB888;
- dsi->mode_flags = MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_NO_EOT_PACKET |
- MIPI_DSI_CLOCK_NON_CONTINUOUS;
+ dsi->dsc = &ctx->desc->dsc;
+ dsi->lanes = ctx->desc->lanes;
+ dsi->format = ctx->desc->format;
+ dsi->mode_flags = ctx->desc->mode_flags;
ctx->panel.prepare_prev_first = true;
+ ret = drm_of_get_panel_orientation(dev->of_node, &ctx->orientation);
+ if (ret < 0) {
+ dev_err(dev, "Failed to get orientation %d\n", ret);
+ return ret;
+ }
+
ctx->panel.backlight = visionox_vtdr6130_create_backlight(dsi);
if (IS_ERR(ctx->panel.backlight))
return dev_err_probe(dev, PTR_ERR(ctx->panel.backlight),
@@ -306,7 +466,8 @@ static int visionox_vtdr6130_probe(struct mipi_dsi_device *dsi)
}
static const struct of_device_id visionox_vtdr6130_of_match[] = {
- { .compatible = "visionox,vtdr6130" },
+ { .compatible = "retroidpocket,rp6-panel", .data = &retroidpocket_rp6_panel_desc },
+ { .compatible = "visionox,vtdr6130", .data = &visionox_vtdr6130_panel_desc },
{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(of, visionox_vtdr6130_of_match);
diff --git a/drivers/gpu/drm/panfrost/panfrost_device.c b/drivers/gpu/drm/panfrost/panfrost_device.c
index 485349faf251..fb0a1b45007b 100644
--- a/drivers/gpu/drm/panfrost/panfrost_device.c
+++ b/drivers/gpu/drm/panfrost/panfrost_device.c
@@ -2,12 +2,15 @@
/* Copyright 2018 Marty E. Plummer <hanetzer@startmail.com> */
/* Copyright 2019 Linaro, Ltd, Rob Herring <robh@kernel.org> */
+#include <linux/debugfs.h>
#include <linux/clk.h>
#include <linux/reset.h>
#include <linux/platform_device.h>
#include <linux/pm_domain.h>
#include <linux/pm_runtime.h>
#include <linux/regulator/consumer.h>
+#include <drm/drm_drv.h>
+#include <drm/drm_print.h>
#include "panfrost_device.h"
#include "panfrost_devfreq.h"
@@ -34,10 +37,46 @@ static void panfrost_reset_fini(struct panfrost_device *pfdev)
reset_control_assert(pfdev->rstc);
}
-static int panfrost_clk_init(struct panfrost_device *pfdev)
+static int panfrost_clks_enable(struct panfrost_device *pfdev, bool on_resume)
{
int err;
+
+ err = clk_prepare_enable(pfdev->clock);
+ if (err)
+ return err;
+
+ err = clk_prepare_enable(pfdev->bus_clock);
+ if (err)
+ goto disable_clock;
+
+ if (on_resume) {
+ err = clk_prepare_enable(pfdev->bus_ace_clock);
+ if (err)
+ goto disable_bus_clock;
+ }
+
+ return 0;
+
+disable_bus_clock:
+ clk_disable_unprepare(pfdev->bus_clock);
+disable_clock:
+ clk_disable_unprepare(pfdev->clock);
+
+ return err;
+}
+
+static void panfrost_clks_disable(struct panfrost_device *pfdev, bool on_suspend)
+{
+ if (on_suspend)
+ clk_disable_unprepare(pfdev->bus_ace_clock);
+ clk_disable_unprepare(pfdev->bus_clock);
+ clk_disable_unprepare(pfdev->clock);
+}
+
+static int panfrost_clk_init(struct panfrost_device *pfdev)
+{
unsigned long rate;
+ int err;
pfdev->clock = devm_clk_get(pfdev->base.dev, NULL);
if (IS_ERR(pfdev->clock)) {
@@ -48,53 +87,59 @@ static int panfrost_clk_init(struct panfrost_device *pfdev)
rate = clk_get_rate(pfdev->clock);
dev_info(pfdev->base.dev, "clock rate = %lu\n", rate);
- err = clk_prepare_enable(pfdev->clock);
+ err = clk_prepare(pfdev->clock);
if (err)
return err;
pfdev->bus_clock = devm_clk_get_optional(pfdev->base.dev, "bus");
if (IS_ERR(pfdev->bus_clock)) {
- dev_err(pfdev->base.dev, "get bus_clock failed %ld\n",
- PTR_ERR(pfdev->bus_clock));
err = PTR_ERR(pfdev->bus_clock);
- goto disable_clock;
+ dev_err(pfdev->base.dev, "get bus_clock failed %d\n", err);
+ goto unprepare_clock;
}
if (pfdev->bus_clock) {
rate = clk_get_rate(pfdev->bus_clock);
dev_info(pfdev->base.dev, "bus_clock rate = %lu\n", rate);
- err = clk_prepare_enable(pfdev->bus_clock);
+ err = clk_prepare(pfdev->bus_clock);
if (err)
- goto disable_clock;
+ goto unprepare_clock;
}
pfdev->bus_ace_clock = devm_clk_get_optional(pfdev->base.dev, "bus_ace");
if (IS_ERR(pfdev->bus_ace_clock)) {
err = PTR_ERR(pfdev->bus_ace_clock);
dev_err(pfdev->base.dev, "get bus_ace_clock failed %d\n", err);
- goto disable_bus_clock;
+ goto unprepare_bus_clock;
}
- err = clk_prepare_enable(pfdev->bus_ace_clock);
+ err = clk_prepare(pfdev->bus_ace_clock);
if (err)
- goto disable_bus_clock;
+ goto unprepare_bus_clock;
+
+ err = panfrost_clks_enable(pfdev, true);
+ if (err)
+ goto unprepare_bus_ace_clock;
return 0;
-disable_bus_clock:
- clk_disable_unprepare(pfdev->bus_clock);
-disable_clock:
- clk_disable_unprepare(pfdev->clock);
+unprepare_bus_ace_clock:
+ clk_unprepare(pfdev->bus_ace_clock);
+unprepare_bus_clock:
+ clk_unprepare(pfdev->bus_clock);
+unprepare_clock:
+ clk_unprepare(pfdev->clock);
return err;
}
static void panfrost_clk_fini(struct panfrost_device *pfdev)
{
- clk_disable_unprepare(pfdev->bus_ace_clock);
- clk_disable_unprepare(pfdev->bus_clock);
- clk_disable_unprepare(pfdev->clock);
+ panfrost_clks_disable(pfdev, true);
+ clk_unprepare(pfdev->bus_ace_clock);
+ clk_unprepare(pfdev->bus_clock);
+ clk_unprepare(pfdev->clock);
}
static int panfrost_regulator_init(struct panfrost_device *pfdev)
@@ -214,14 +259,14 @@ err:
int panfrost_device_init(struct panfrost_device *pfdev)
{
+ bool device_initialised = false;
int err;
- mutex_init(&pfdev->sched_lock);
- INIT_LIST_HEAD(&pfdev->as_lru_list);
-
- spin_lock_init(&pfdev->as_lock);
+ pfdev->comp = of_device_get_match_data(pfdev->base.dev);
+ if (!pfdev->comp)
+ return -ENODEV;
- spin_lock_init(&pfdev->cycle_counter.lock);
+ pfdev->coherent = device_get_dma_attr(pfdev->base.dev) == DEV_DMA_COHERENT;
#ifdef CONFIG_DEBUG_FS
mutex_init(&pfdev->debugfs.gems_lock);
@@ -280,9 +325,41 @@ int panfrost_device_init(struct panfrost_device *pfdev)
if (err)
goto out_job;
- panfrost_gem_init(pfdev);
+ err = panfrost_gem_init(pfdev);
+ if (err)
+ goto out_perfcnt;
+
+ device_initialised = true;
+
+ /* The reason we must manually set the PM status and usage counter is
+ * we have just powered the device up but did not go through the PM
+ * runtime resume callback, so we need to update these ourselves.
+ */
+ pm_runtime_get_noresume(pfdev->base.dev);
+ pm_runtime_set_active(pfdev->base.dev);
+ pm_runtime_mark_last_busy(pfdev->base.dev);
+ pm_runtime_enable(pfdev->base.dev);
+ pm_runtime_set_autosuspend_delay(pfdev->base.dev, 50); /* ~3 frames */
+ pm_runtime_use_autosuspend(pfdev->base.dev);
+
+ /*
+ * Register the DRM device with the core and the connectors with
+ * sysfs
+ */
+ err = drm_dev_register(&pfdev->base, 0);
+ if (err < 0)
+ goto err_disable_rpm;
+
+ pm_runtime_put_autosuspend(pfdev->base.dev);
return 0;
+
+err_disable_rpm:
+ pm_runtime_dont_use_autosuspend(pfdev->base.dev);
+ pm_runtime_disable(pfdev->base.dev);
+ panfrost_gem_fini(pfdev);
+out_perfcnt:
+ panfrost_perfcnt_fini(pfdev);
out_job:
panfrost_jm_fini(pfdev);
out_mmu:
@@ -299,11 +376,23 @@ out_reset:
panfrost_reset_fini(pfdev);
out_pm_domain:
panfrost_pm_domain_fini(pfdev);
+
+ if (device_initialised) {
+ pm_runtime_set_suspended(pfdev->base.dev);
+ pm_runtime_put_noidle(pfdev->base.dev);
+ }
+
return err;
}
void panfrost_device_fini(struct panfrost_device *pfdev)
{
+ drm_WARN_ON(&pfdev->base, pm_runtime_get_sync(pfdev->base.dev) < 0);
+
+ pm_runtime_dont_use_autosuspend(pfdev->base.dev);
+ pm_runtime_disable(pfdev->base.dev);
+
+ panfrost_gem_fini(pfdev);
panfrost_perfcnt_fini(pfdev);
panfrost_jm_fini(pfdev);
panfrost_mmu_fini(pfdev);
@@ -313,6 +402,9 @@ void panfrost_device_fini(struct panfrost_device *pfdev)
panfrost_clk_fini(pfdev);
panfrost_reset_fini(pfdev);
panfrost_pm_domain_fini(pfdev);
+
+ pm_runtime_set_suspended(pfdev->base.dev);
+ pm_runtime_put_noidle(pfdev->base.dev);
}
#define PANFROST_EXCEPTION(id) \
@@ -417,6 +509,8 @@ bool panfrost_exception_needs_reset(const struct panfrost_device *pfdev,
void panfrost_device_reset(struct panfrost_device *pfdev, bool enable_job_int)
{
+ guard(rwsem_write)(&pfdev->reset.lock);
+
panfrost_gpu_soft_reset(pfdev);
panfrost_gpu_power_on(pfdev);
@@ -425,6 +519,8 @@ void panfrost_device_reset(struct panfrost_device *pfdev, bool enable_job_int)
panfrost_jm_reset_interrupts(pfdev);
if (enable_job_int)
panfrost_jm_enable_interrupts(pfdev);
+
+ panfrost_perfcnt_reset(pfdev);
}
static int panfrost_device_runtime_resume(struct device *dev)
@@ -437,34 +533,17 @@ static int panfrost_device_runtime_resume(struct device *dev)
if (ret)
return ret;
- ret = clk_enable(pfdev->clock);
- if (ret)
- goto err_clk;
-
- ret = clk_enable(pfdev->bus_clock);
- if (ret)
- goto err_bus_clk;
-
- ret = clk_enable(pfdev->bus_ace_clock);
- if (ret)
- goto err_bus_ace_clk;
+ ret = panfrost_clks_enable(pfdev, true);
+ if (ret) {
+ reset_control_assert(pfdev->rstc);
+ return ret;
+ }
}
panfrost_device_reset(pfdev, true);
panfrost_devfreq_resume(pfdev);
return 0;
-
-err_bus_ace_clk:
- if (pfdev->comp->pm_features & BIT(GPU_PM_RT))
- clk_disable(pfdev->bus_clock);
-err_bus_clk:
- if (pfdev->comp->pm_features & BIT(GPU_PM_RT))
- clk_disable(pfdev->clock);
-err_clk:
- if (pfdev->comp->pm_features & BIT(GPU_PM_RT))
- reset_control_assert(pfdev->rstc);
- return ret;
}
static int panfrost_device_runtime_suspend(struct device *dev)
@@ -481,9 +560,7 @@ static int panfrost_device_runtime_suspend(struct device *dev)
panfrost_gpu_power_off(pfdev);
if (pfdev->comp->pm_features & BIT(GPU_PM_RT)) {
- clk_disable(pfdev->bus_ace_clock);
- clk_disable(pfdev->bus_clock);
- clk_disable(pfdev->clock);
+ panfrost_clks_disable(pfdev, true);
reset_control_assert(pfdev->rstc);
}
@@ -507,15 +584,9 @@ static int panfrost_device_resume(struct device *dev)
}
if (pfdev->comp->pm_features & BIT(GPU_PM_CLK_DIS)) {
- ret = clk_enable(pfdev->clock);
+ ret = panfrost_clks_enable(pfdev, false);
if (ret)
goto err_clk;
-
- if (pfdev->bus_clock) {
- ret = clk_enable(pfdev->bus_clock);
- if (ret)
- goto err_bus_clk;
- }
}
ret = pm_runtime_force_resume(dev);
@@ -525,11 +596,8 @@ static int panfrost_device_resume(struct device *dev)
return 0;
err_resume:
- if (pfdev->comp->pm_features & BIT(GPU_PM_CLK_DIS) && pfdev->bus_clock)
- clk_disable(pfdev->bus_clock);
-err_bus_clk:
if (pfdev->comp->pm_features & BIT(GPU_PM_CLK_DIS))
- clk_disable(pfdev->clock);
+ panfrost_clks_disable(pfdev, false);
err_clk:
if (pfdev->comp->pm_features & BIT(GPU_PM_VREG_OFF))
dev_pm_opp_set_opp(dev, NULL);
@@ -545,12 +613,8 @@ static int panfrost_device_suspend(struct device *dev)
if (ret)
return ret;
- if (pfdev->comp->pm_features & BIT(GPU_PM_CLK_DIS)) {
- if (pfdev->bus_clock)
- clk_disable(pfdev->bus_clock);
-
- clk_disable(pfdev->clock);
- }
+ if (pfdev->comp->pm_features & BIT(GPU_PM_CLK_DIS))
+ panfrost_clks_disable(pfdev, false);
if (pfdev->comp->pm_features & BIT(GPU_PM_VREG_OFF))
dev_pm_opp_set_opp(dev, NULL);
@@ -562,3 +626,56 @@ EXPORT_GPL_DEV_PM_OPS(panfrost_pm_ops) = {
RUNTIME_PM_OPS(panfrost_device_runtime_suspend, panfrost_device_runtime_resume, NULL)
SYSTEM_SLEEP_PM_OPS(panfrost_device_suspend, panfrost_device_resume)
};
+
+#ifdef CONFIG_DEBUG_FS
+static int reset_get(void *data, u64 *val)
+{
+ struct panfrost_device *pfdev =
+ container_of(data, struct panfrost_device, base);
+
+ *val = atomic_read(&pfdev->reset.pending);
+ return 0;
+}
+
+static int reset_set(void *data, u64 val)
+{
+ struct panfrost_device *pfdev =
+ container_of(data, struct panfrost_device, base);
+ int ret;
+
+ if (val != 1)
+ return -EINVAL;
+
+ ret = pm_runtime_get_if_active(pfdev->base.dev);
+ if (!ret)
+ return 0;
+
+ panfrost_device_schedule_reset(pfdev);
+ flush_work(&pfdev->reset.work);
+
+ /* ret < 0 means runtime PM for the device is disabled, so we
+ * only need to return the PM reference in the opposite case
+ */
+ if (ret > 0)
+ pm_runtime_put(pfdev->base.dev);
+
+ return 0;
+}
+
+DEFINE_DEBUGFS_ATTRIBUTE(panfrost_reset_debugfs_fops,
+ reset_get, reset_set,
+ "0x%08llx\n");
+
+static void panfrost_reset_debugfs_init(struct drm_minor *minor)
+{
+ debugfs_create_file("reset", 0600, minor->debugfs_root,
+ minor->dev, &panfrost_reset_debugfs_fops);
+}
+
+void panfrost_device_debugfs_init(struct drm_minor *minor)
+{
+ panfrost_gem_debugfs_init(minor);
+ panfrost_sched_debugfs_init(minor);
+ panfrost_reset_debugfs_init(minor);
+}
+#endif // CONFIG_DEBUG_FS
diff --git a/drivers/gpu/drm/panfrost/panfrost_device.h b/drivers/gpu/drm/panfrost/panfrost_device.h
index 1fe1554f2652..63452febb1cf 100644
--- a/drivers/gpu/drm/panfrost/panfrost_device.h
+++ b/drivers/gpu/drm/panfrost/panfrost_device.h
@@ -165,6 +165,7 @@ struct panfrost_device {
struct {
struct workqueue_struct *wq;
struct work_struct work;
+ struct rw_semaphore lock;
atomic_t pending;
} reset;
@@ -341,4 +342,8 @@ panfrost_device_schedule_reset(struct panfrost_device *pfdev)
queue_work(pfdev->reset.wq, &pfdev->reset.work);
}
+#ifdef CONFIG_DEBUG_FS
+void panfrost_device_debugfs_init(struct drm_minor *minor);
+#endif // CONFIG_DEBUG_FS
+
#endif
diff --git a/drivers/gpu/drm/panfrost/panfrost_drv.c b/drivers/gpu/drm/panfrost/panfrost_drv.c
index 70c05210ea84..7c348a0bf1ee 100644
--- a/drivers/gpu/drm/panfrost/panfrost_drv.c
+++ b/drivers/gpu/drm/panfrost/panfrost_drv.c
@@ -21,7 +21,6 @@
#include <drm/drm_timeout.h>
#include "panfrost_device.h"
-#include "panfrost_drv.h"
#include "panfrost_gem.h"
#include "panfrost_mmu.h"
#include "panfrost_job.h"
@@ -42,9 +41,12 @@ static int panfrost_ioctl_query_timestamp(struct panfrost_device *pfdev,
if (ret)
return ret;
- panfrost_cycle_counter_get(pfdev);
- *arg = panfrost_timestamp_read(pfdev);
- panfrost_cycle_counter_put(pfdev);
+ /* We should not read timestamp register while the GPU is being reset */
+ scoped_guard(rwsem_read, &pfdev->reset.lock) {
+ panfrost_cycle_counter_get(pfdev);
+ *arg = panfrost_timestamp_read(pfdev);
+ panfrost_cycle_counter_put(pfdev);
+ }
pm_runtime_put(pfdev->base.dev);
return 0;
@@ -793,133 +795,6 @@ static const struct file_operations panfrost_drm_driver_fops = {
.show_fdinfo = drm_show_fdinfo,
};
-#ifdef CONFIG_DEBUG_FS
-static int panthor_gems_show(struct seq_file *m, void *data)
-{
- struct drm_info_node *node = m->private;
- struct panfrost_device *pfdev = to_panfrost_device(node->minor->dev);
-
- panfrost_gem_debugfs_print_bos(pfdev, m);
-
- return 0;
-}
-
-static void show_panfrost_jm_ctx(struct panfrost_jm_ctx *jm_ctx, u32 handle,
- struct seq_file *m)
-{
- struct drm_device *ddev = ((struct drm_info_node *)m->private)->minor->dev;
- const char *prio = "UNKNOWN";
-
- static const char * const prios[] = {
- [DRM_SCHED_PRIORITY_HIGH] = "HIGH",
- [DRM_SCHED_PRIORITY_NORMAL] = "NORMAL",
- [DRM_SCHED_PRIORITY_LOW] = "LOW",
- };
-
- if (jm_ctx->slot_entity[0].priority !=
- jm_ctx->slot_entity[1].priority)
- drm_warn(ddev, "Slot priorities should be the same in a single context");
-
- if (jm_ctx->slot_entity[0].priority < ARRAY_SIZE(prios))
- prio = prios[jm_ctx->slot_entity[0].priority];
-
- seq_printf(m, " JM context %u: priority %s\n", handle, prio);
-}
-
-static int show_file_jm_ctxs(struct panfrost_file_priv *pfile,
- struct seq_file *m)
-{
- struct panfrost_jm_ctx *jm_ctx;
- unsigned long i;
-
- xa_lock(&pfile->jm_ctxs);
- xa_for_each(&pfile->jm_ctxs, i, jm_ctx) {
- jm_ctx = panfrost_jm_ctx_get(jm_ctx);
- xa_unlock(&pfile->jm_ctxs);
- show_panfrost_jm_ctx(jm_ctx, i, m);
- panfrost_jm_ctx_put(jm_ctx);
- xa_lock(&pfile->jm_ctxs);
- }
- xa_unlock(&pfile->jm_ctxs);
-
- return 0;
-}
-
-static struct drm_info_list panthor_debugfs_list[] = {
- {"gems",
- panthor_gems_show, 0, NULL},
-};
-
-static int panthor_gems_debugfs_init(struct drm_minor *minor)
-{
- drm_debugfs_create_files(panthor_debugfs_list,
- ARRAY_SIZE(panthor_debugfs_list),
- minor->debugfs_root, minor);
-
- return 0;
-}
-
-static int show_each_file(struct seq_file *m, void *arg)
-{
- struct drm_info_node *node = (struct drm_info_node *)m->private;
- struct drm_device *ddev = node->minor->dev;
- int (*show)(struct panfrost_file_priv *, struct seq_file *) =
- node->info_ent->data;
- struct drm_file *file;
- int ret;
-
- ret = mutex_lock_interruptible(&ddev->filelist_mutex);
- if (ret)
- return ret;
-
- list_for_each_entry(file, &ddev->filelist, lhead) {
- struct task_struct *task;
- struct panfrost_file_priv *pfile = file->driver_priv;
- struct pid *pid;
-
- /*
- * Although we have a valid reference on file->pid, that does
- * not guarantee that the task_struct who called get_pid() is
- * still alive (e.g. get_pid(current) => fork() => exit()).
- * Therefore, we need to protect this ->comm access using RCU.
- */
- rcu_read_lock();
- pid = rcu_dereference(file->pid);
- task = pid_task(pid, PIDTYPE_TGID);
- seq_printf(m, "client_id %8llu pid %8d command %s:\n",
- file->client_id, pid_nr(pid),
- task ? task->comm : "<unknown>");
- rcu_read_unlock();
-
- ret = show(pfile, m);
- if (ret < 0)
- break;
-
- seq_puts(m, "\n");
- }
-
- mutex_unlock(&ddev->filelist_mutex);
- return ret;
-}
-
-static struct drm_info_list panfrost_sched_debugfs_list[] = {
- { "sched_ctxs", show_each_file, 0, show_file_jm_ctxs },
-};
-
-static void panfrost_sched_debugfs_init(struct drm_minor *minor)
-{
- drm_debugfs_create_files(panfrost_sched_debugfs_list,
- ARRAY_SIZE(panfrost_sched_debugfs_list),
- minor->debugfs_root, minor);
-}
-
-static void panfrost_debugfs_init(struct drm_minor *minor)
-{
- panthor_gems_debugfs_init(minor);
- panfrost_sched_debugfs_init(minor);
-}
-#endif
-
/*
* Panfrost driver version:
* - 1.0 - initial interface
@@ -933,6 +808,7 @@ static void panfrost_debugfs_init(struct drm_minor *minor)
* - 1.6 - adds PANFROST_BO_MAP_WB, PANFROST_IOCTL_SYNC_BO,
* PANFROST_IOCTL_QUERY_BO_INFO and
* DRM_PANFROST_PARAM_SELECTED_COHERENCY
+ * - 1.7 - adds PERFCNT_DUMP state field
*/
static const struct drm_driver panfrost_drm_driver = {
.driver_features = DRIVER_RENDER | DRIVER_GEM | DRIVER_SYNCOBJ,
@@ -945,26 +821,19 @@ static const struct drm_driver panfrost_drm_driver = {
.name = "panfrost",
.desc = "panfrost DRM",
.major = 1,
- .minor = 6,
+ .minor = 7,
.gem_create_object = panfrost_gem_create_object,
.gem_prime_import = panfrost_gem_prime_import,
.gem_prime_import_sg_table = panfrost_gem_prime_import_sg_table,
#ifdef CONFIG_DEBUG_FS
- .debugfs_init = panfrost_debugfs_init,
+ .debugfs_init = panfrost_device_debugfs_init,
#endif
};
-#ifdef CONFIG_TRANSPARENT_HUGEPAGE
-bool panfrost_transparent_hugepage = true;
-module_param_named(transparent_hugepage, panfrost_transparent_hugepage, bool, 0400);
-MODULE_PARM_DESC(transparent_hugepage, "Use a dedicated tmpfs mount point with Transparent Hugepage enabled (true = default)");
-#endif
-
static int panfrost_probe(struct platform_device *pdev)
{
struct panfrost_device *pfdev;
- int err;
pfdev = devm_drm_dev_alloc(&pdev->dev, &panfrost_drm_driver,
struct panfrost_device, base);
@@ -973,50 +842,7 @@ static int panfrost_probe(struct platform_device *pdev)
platform_set_drvdata(pdev, pfdev);
- pfdev->comp = of_device_get_match_data(&pdev->dev);
- if (!pfdev->comp)
- return -ENODEV;
-
- pfdev->coherent = device_get_dma_attr(&pdev->dev) == DEV_DMA_COHERENT;
-
- mutex_init(&pfdev->shrinker_lock);
- INIT_LIST_HEAD(&pfdev->shrinker_list);
-
- err = panfrost_device_init(pfdev);
- if (err) {
- if (err != -EPROBE_DEFER)
- dev_err(&pdev->dev, "Fatal error during GPU init\n");
- goto err_out0;
- }
-
- pm_runtime_set_active(pfdev->base.dev);
- pm_runtime_mark_last_busy(pfdev->base.dev);
- pm_runtime_enable(pfdev->base.dev);
- pm_runtime_set_autosuspend_delay(pfdev->base.dev, 50); /* ~3 frames */
- pm_runtime_use_autosuspend(pfdev->base.dev);
-
- /*
- * Register the DRM device with the core and the connectors with
- * sysfs
- */
- err = drm_dev_register(&pfdev->base, 0);
- if (err < 0)
- goto err_out1;
-
- err = panfrost_gem_shrinker_init(&pfdev->base);
- if (err)
- goto err_out2;
-
- return 0;
-
-err_out2:
- drm_dev_unregister(&pfdev->base);
-err_out1:
- pm_runtime_disable(pfdev->base.dev);
- panfrost_device_fini(pfdev);
- pm_runtime_set_suspended(pfdev->base.dev);
-err_out0:
- return err;
+ return panfrost_device_init(pfdev);
}
static void panfrost_remove(struct platform_device *pdev)
@@ -1024,12 +850,8 @@ static void panfrost_remove(struct platform_device *pdev)
struct panfrost_device *pfdev = platform_get_drvdata(pdev);
drm_dev_unregister(&pfdev->base);
- panfrost_gem_shrinker_cleanup(&pfdev->base);
- pm_runtime_get_sync(pfdev->base.dev);
- pm_runtime_disable(pfdev->base.dev);
panfrost_device_fini(pfdev);
- pm_runtime_set_suspended(pfdev->base.dev);
}
static ssize_t profiling_show(struct device *dev,
diff --git a/drivers/gpu/drm/panfrost/panfrost_drv.h b/drivers/gpu/drm/panfrost/panfrost_drv.h
deleted file mode 100644
index a2277ec61aab..000000000000
--- a/drivers/gpu/drm/panfrost/panfrost_drv.h
+++ /dev/null
@@ -1,9 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0 or MIT */
-/* Copyright 2025 Amazon.com, Inc. or its affiliates */
-
-#ifndef __PANFROST_DRV_H__
-#define __PANFROST_DRV_H__
-
-extern bool panfrost_transparent_hugepage;
-
-#endif
diff --git a/drivers/gpu/drm/panfrost/panfrost_features.h b/drivers/gpu/drm/panfrost/panfrost_features.h
index 52f9d69f6db9..34c65847192f 100644
--- a/drivers/gpu/drm/panfrost/panfrost_features.h
+++ b/drivers/gpu/drm/panfrost/panfrost_features.h
@@ -121,6 +121,8 @@ enum panfrost_hw_feature {
BIT_ULL(HW_FEATURE_IDVS_GROUP_SIZE) | \
BIT_ULL(HW_FEATURE_CLEAN_ONLY_SAFE))
+#define hw_features_g68 hw_features_g57
+
static inline bool panfrost_has_hw_feature(struct panfrost_device *pfdev,
enum panfrost_hw_feature feat)
{
diff --git a/drivers/gpu/drm/panfrost/panfrost_gem.c b/drivers/gpu/drm/panfrost/panfrost_gem.c
index 3a7fce428898..518f24549b9d 100644
--- a/drivers/gpu/drm/panfrost/panfrost_gem.c
+++ b/drivers/gpu/drm/panfrost/panfrost_gem.c
@@ -9,26 +9,38 @@
#include <linux/dma-mapping.h>
#include <drm/panfrost_drm.h>
+#include <drm/drm_debugfs.h>
#include <drm/drm_print.h>
#include "panfrost_device.h"
-#include "panfrost_drv.h"
#include "panfrost_gem.h"
#include "panfrost_mmu.h"
-void panfrost_gem_init(struct panfrost_device *pfdev)
+#ifdef CONFIG_TRANSPARENT_HUGEPAGE
+static bool panfrost_transparent_hugepage = true;
+module_param_named(transparent_hugepage, panfrost_transparent_hugepage, bool, 0400);
+MODULE_PARM_DESC(transparent_hugepage, "Use a dedicated tmpfs mount point with Transparent Hugepage enabled (true = default)");
+#else
+#define panfrost_transparent_hugepage false
+#endif
+
+int panfrost_gem_init(struct panfrost_device *pfdev)
{
- int err;
+ if (panfrost_transparent_hugepage) {
+ int err = drm_gem_huge_mnt_create(&pfdev->base, "within_size");
+
+ if (err)
+ drm_warn(&pfdev->base,
+ "Can't use Transparent Hugepage (%d)\n", err);
+ else
+ drm_info(&pfdev->base, "Using Transparent Hugepage\n");
+ }
- if (IS_ENABLED(CONFIG_TRANSPARENT_HUGEPAGE) &&
- !panfrost_transparent_hugepage)
- return;
+ return panfrost_gem_shrinker_init(pfdev);
+}
- err = drm_gem_huge_mnt_create(&pfdev->base, "within_size");
- if (drm_gem_get_huge_mnt(&pfdev->base))
- drm_info(&pfdev->base, "Using Transparent Hugepage\n");
- else if (err)
- drm_warn(&pfdev->base, "Can't use Transparent Hugepage (%d)\n",
- err);
+void panfrost_gem_fini(struct panfrost_device *pfdev)
+{
+ panfrost_gem_shrinker_fini(pfdev);
}
#ifdef CONFIG_DEBUG_FS
@@ -724,8 +736,8 @@ static void panfrost_gem_debugfs_bo_print(struct panfrost_gem_object *bo,
totals->reclaimable += resident_size;
}
-void panfrost_gem_debugfs_print_bos(struct panfrost_device *pfdev,
- struct seq_file *m)
+static void panfrost_gem_debugfs_print_bos(struct panfrost_device *pfdev,
+ struct seq_file *m)
{
struct gem_size_totals totals = {0};
struct panfrost_gem_object *bo;
@@ -745,4 +757,28 @@ void panfrost_gem_debugfs_print_bos(struct panfrost_device *pfdev,
seq_printf(m, "Total size: %zd, Total resident: %zd, Total reclaimable: %zd\n",
totals.size, totals.resident, totals.reclaimable);
}
+
+static int panfrost_gems_show(struct seq_file *m, void *data)
+{
+ struct drm_info_node *node = m->private;
+ struct panfrost_device *pfdev = to_panfrost_device(node->minor->dev);
+
+ panfrost_gem_debugfs_print_bos(pfdev, m);
+
+ return 0;
+}
+
+static struct drm_info_list panfrost_debugfs_list[] = {
+ {"gems",
+ panfrost_gems_show, 0, NULL},
+};
+
+int panfrost_gem_debugfs_init(struct drm_minor *minor)
+{
+ drm_debugfs_create_files(panfrost_debugfs_list,
+ ARRAY_SIZE(panfrost_debugfs_list),
+ minor->debugfs_root, minor);
+
+ return 0;
+}
#endif
diff --git a/drivers/gpu/drm/panfrost/panfrost_gem.h b/drivers/gpu/drm/panfrost/panfrost_gem.h
index 79d4377019e9..3008afcf4bff 100644
--- a/drivers/gpu/drm/panfrost/panfrost_gem.h
+++ b/drivers/gpu/drm/panfrost/panfrost_gem.h
@@ -129,7 +129,8 @@ drm_mm_node_to_panfrost_mapping(struct drm_mm_node *node)
return container_of(node, struct panfrost_gem_mapping, mmnode);
}
-void panfrost_gem_init(struct panfrost_device *pfdev);
+int panfrost_gem_init(struct panfrost_device *pfdev);
+void panfrost_gem_fini(struct panfrost_device *pfdev);
struct drm_gem_object *panfrost_gem_create_object(struct drm_device *dev, size_t size);
@@ -154,8 +155,8 @@ panfrost_gem_mapping_get(struct panfrost_gem_object *bo,
void panfrost_gem_mapping_put(struct panfrost_gem_mapping *mapping);
void panfrost_gem_teardown_mappings_locked(struct panfrost_gem_object *bo);
-int panfrost_gem_shrinker_init(struct drm_device *dev);
-void panfrost_gem_shrinker_cleanup(struct drm_device *dev);
+int panfrost_gem_shrinker_init(struct panfrost_device *pfdev);
+void panfrost_gem_shrinker_fini(struct panfrost_device *pfdev);
void panfrost_gem_set_label(struct drm_gem_object *obj, const char *label);
int panfrost_gem_sync(struct drm_gem_object *obj, u32 type,
@@ -163,8 +164,7 @@ int panfrost_gem_sync(struct drm_gem_object *obj, u32 type,
void panfrost_gem_internal_set_label(struct drm_gem_object *obj, const char *label);
#ifdef CONFIG_DEBUG_FS
-void panfrost_gem_debugfs_print_bos(struct panfrost_device *pfdev,
- struct seq_file *m);
+int panfrost_gem_debugfs_init(struct drm_minor *minor);
#endif
#endif /* __PANFROST_GEM_H__ */
diff --git a/drivers/gpu/drm/panfrost/panfrost_gem_shrinker.c b/drivers/gpu/drm/panfrost/panfrost_gem_shrinker.c
index 2fe967a90bcb..72cd7a165e6d 100644
--- a/drivers/gpu/drm/panfrost/panfrost_gem_shrinker.c
+++ b/drivers/gpu/drm/panfrost/panfrost_gem_shrinker.c
@@ -95,9 +95,10 @@ panfrost_gem_shrinker_scan(struct shrinker *shrinker, struct shrink_control *sc)
*
* This function registers and sets up the panfrost shrinker.
*/
-int panfrost_gem_shrinker_init(struct drm_device *dev)
+int panfrost_gem_shrinker_init(struct panfrost_device *pfdev)
{
- struct panfrost_device *pfdev = to_panfrost_device(dev);
+ mutex_init(&pfdev->shrinker_lock);
+ INIT_LIST_HEAD(&pfdev->shrinker_list);
pfdev->shrinker = shrinker_alloc(0, "drm-panfrost");
if (!pfdev->shrinker)
@@ -118,10 +119,8 @@ int panfrost_gem_shrinker_init(struct drm_device *dev)
*
* This function unregisters the panfrost shrinker.
*/
-void panfrost_gem_shrinker_cleanup(struct drm_device *dev)
+void panfrost_gem_shrinker_fini(struct panfrost_device *pfdev)
{
- struct panfrost_device *pfdev = to_panfrost_device(dev);
-
if (pfdev->shrinker)
shrinker_free(pfdev->shrinker);
}
diff --git a/drivers/gpu/drm/panfrost/panfrost_gpu.c b/drivers/gpu/drm/panfrost/panfrost_gpu.c
index 7d555e63e21a..6081b27829e1 100644
--- a/drivers/gpu/drm/panfrost/panfrost_gpu.c
+++ b/drivers/gpu/drm/panfrost/panfrost_gpu.c
@@ -242,6 +242,9 @@ static const struct panfrost_model gpu_models[] = {
/* MediaTek MT8188 Mali-G57 MC3 */
GPU_MODEL(g57, 0x9093,
GPU_REV(g57, 0, 0)),
+
+ GPU_MODEL(g68, 0x9004,
+ GPU_REV(g68, 1, 0), GPU_REV(g68, 1, 1)),
{0},
};
@@ -516,6 +519,8 @@ int panfrost_gpu_init(struct panfrost_device *pfdev)
{
int err;
+ spin_lock_init(&pfdev->cycle_counter.lock);
+
err = panfrost_gpu_soft_reset(pfdev);
if (err)
return err;
diff --git a/drivers/gpu/drm/panfrost/panfrost_issues.h b/drivers/gpu/drm/panfrost/panfrost_issues.h
index eb60cb83667a..dd39946bb572 100644
--- a/drivers/gpu/drm/panfrost/panfrost_issues.h
+++ b/drivers/gpu/drm/panfrost/panfrost_issues.h
@@ -265,6 +265,14 @@ enum panfrost_hw_issue {
#define hw_issues_g57_r0p0 (\
BIT_ULL(HW_ISSUE_TTRX_3485))
+#define hw_issues_g68 (\
+ BIT_ULL(HW_ISSUE_TTRX_2968_TTRX_3162))
+
+#define hw_issues_g68_r1p0 (\
+ BIT_ULL(HW_ISSUE_TTRX_3485))
+
+#define hw_issues_g68_r1p1 0
+
static inline bool panfrost_has_hw_issue(const struct panfrost_device *pfdev,
enum panfrost_hw_issue issue)
{
diff --git a/drivers/gpu/drm/panfrost/panfrost_job.c b/drivers/gpu/drm/panfrost/panfrost_job.c
index d59b4863b8ad..4ddf4b722ddf 100644
--- a/drivers/gpu/drm/panfrost/panfrost_job.c
+++ b/drivers/gpu/drm/panfrost/panfrost_job.c
@@ -8,6 +8,8 @@
#include <linux/platform_device.h>
#include <linux/pm_runtime.h>
#include <linux/dma-resv.h>
+#include <drm/drm_debugfs.h>
+#include <drm/drm_print.h>
#include <drm/gpu_scheduler.h>
#include <drm/panfrost_drm.h>
@@ -874,6 +876,8 @@ int panfrost_jm_init(struct panfrost_device *pfdev)
INIT_WORK(&pfdev->reset.work, panfrost_reset_work);
spin_lock_init(&js->job_lock);
+ mutex_init(&pfdev->sched_lock);
+ init_rwsem(&pfdev->reset.lock);
js->irq = platform_get_irq_byname(to_platform_device(pfdev->base.dev), "job");
if (js->irq < 0)
@@ -929,7 +933,7 @@ void panfrost_jm_fini(struct panfrost_device *pfdev)
drm_sched_fini(&js->queue[j].sched);
}
- cancel_work_sync(&pfdev->reset.work);
+ disable_work_sync(&pfdev->reset.work);
destroy_workqueue(pfdev->reset.wq);
}
@@ -1137,3 +1141,100 @@ int panfrost_jm_ctx_destroy(struct drm_file *file, u32 handle)
panfrost_jm_ctx_put(jm_ctx);
return 0;
}
+
+#ifdef CONFIG_DEBUG_FS
+static void show_panfrost_jm_ctx(struct panfrost_jm_ctx *jm_ctx, u32 handle,
+ struct seq_file *m)
+{
+ struct drm_device *ddev = ((struct drm_info_node *)m->private)->minor->dev;
+ const char *prio = "UNKNOWN";
+
+ static const char * const prios[] = {
+ [DRM_SCHED_PRIORITY_HIGH] = "HIGH",
+ [DRM_SCHED_PRIORITY_NORMAL] = "NORMAL",
+ [DRM_SCHED_PRIORITY_LOW] = "LOW",
+ };
+
+ if (jm_ctx->slot_entity[0].priority !=
+ jm_ctx->slot_entity[1].priority)
+ drm_warn(ddev, "Slot priorities should be the same in a single context");
+
+ if (jm_ctx->slot_entity[0].priority < ARRAY_SIZE(prios))
+ prio = prios[jm_ctx->slot_entity[0].priority];
+
+ seq_printf(m, " JM context %u: priority %s\n", handle, prio);
+}
+
+static int show_file_jm_ctxs(struct panfrost_file_priv *pfile,
+ struct seq_file *m)
+{
+ struct panfrost_jm_ctx *jm_ctx;
+ unsigned long i;
+
+ xa_lock(&pfile->jm_ctxs);
+ xa_for_each(&pfile->jm_ctxs, i, jm_ctx) {
+ jm_ctx = panfrost_jm_ctx_get(jm_ctx);
+ xa_unlock(&pfile->jm_ctxs);
+ show_panfrost_jm_ctx(jm_ctx, i, m);
+ panfrost_jm_ctx_put(jm_ctx);
+ xa_lock(&pfile->jm_ctxs);
+ }
+ xa_unlock(&pfile->jm_ctxs);
+
+ return 0;
+}
+
+static int show_each_file(struct seq_file *m, void *arg)
+{
+ struct drm_info_node *node = (struct drm_info_node *)m->private;
+ struct drm_device *ddev = node->minor->dev;
+ int (*show)(struct panfrost_file_priv *, struct seq_file *) =
+ node->info_ent->data;
+ struct drm_file *file;
+ int ret;
+
+ ret = mutex_lock_interruptible(&ddev->filelist_mutex);
+ if (ret)
+ return ret;
+
+ list_for_each_entry(file, &ddev->filelist, lhead) {
+ struct task_struct *task;
+ struct panfrost_file_priv *pfile = file->driver_priv;
+ struct pid *pid;
+
+ /*
+ * Although we have a valid reference on file->pid, that does
+ * not guarantee that the task_struct who called get_pid() is
+ * still alive (e.g. get_pid(current) => fork() => exit()).
+ * Therefore, we need to protect this ->comm access using RCU.
+ */
+ rcu_read_lock();
+ pid = rcu_dereference(file->pid);
+ task = pid_task(pid, PIDTYPE_TGID);
+ seq_printf(m, "client_id %8llu pid %8d command %s:\n",
+ file->client_id, pid_nr(pid),
+ task ? task->comm : "<unknown>");
+ rcu_read_unlock();
+
+ ret = show(pfile, m);
+ if (ret < 0)
+ break;
+
+ seq_puts(m, "\n");
+ }
+
+ mutex_unlock(&ddev->filelist_mutex);
+ return ret;
+}
+
+static struct drm_info_list panfrost_sched_debugfs_list[] = {
+ { "sched_ctxs", show_each_file, 0, show_file_jm_ctxs },
+};
+
+void panfrost_sched_debugfs_init(struct drm_minor *minor)
+{
+ drm_debugfs_create_files(panfrost_sched_debugfs_list,
+ ARRAY_SIZE(panfrost_sched_debugfs_list),
+ minor->debugfs_root, minor);
+}
+#endif
diff --git a/drivers/gpu/drm/panfrost/panfrost_job.h b/drivers/gpu/drm/panfrost/panfrost_job.h
index c3f57e41a571..3e3b9717838f 100644
--- a/drivers/gpu/drm/panfrost/panfrost_job.h
+++ b/drivers/gpu/drm/panfrost/panfrost_job.h
@@ -74,4 +74,8 @@ int panfrost_job_get_slot(struct panfrost_job *job);
int panfrost_job_push(struct panfrost_job *job);
void panfrost_job_put(struct panfrost_job *job);
+#ifdef CONFIG_DEBUG_FS
+void panfrost_sched_debugfs_init(struct drm_minor *minor);
+#endif
+
#endif
diff --git a/drivers/gpu/drm/panfrost/panfrost_mmu.c b/drivers/gpu/drm/panfrost/panfrost_mmu.c
index 4a3162c3b659..faa62643caf1 100644
--- a/drivers/gpu/drm/panfrost/panfrost_mmu.c
+++ b/drivers/gpu/drm/panfrost/panfrost_mmu.c
@@ -336,6 +336,12 @@ void panfrost_mmu_as_put(struct panfrost_device *pfdev, struct panfrost_mmu *mmu
WARN_ON(atomic_read(&mmu->as_count) < 0);
}
+static void panfrost_mmu_enable_interrupts(struct panfrost_device *pfdev)
+{
+ mmu_write(pfdev, MMU_INT_CLEAR, ~0);
+ mmu_write(pfdev, MMU_INT_MASK, ~0);
+}
+
void panfrost_mmu_reset(struct panfrost_device *pfdev)
{
struct panfrost_mmu *mmu, *mmu_tmp;
@@ -355,8 +361,7 @@ void panfrost_mmu_reset(struct panfrost_device *pfdev)
spin_unlock(&pfdev->as_lock);
- mmu_write(pfdev, MMU_INT_CLEAR, ~0);
- mmu_write(pfdev, MMU_INT_MASK, ~0);
+ panfrost_mmu_enable_interrupts(pfdev);
}
static size_t get_pgsize(u64 addr, size_t size, size_t *count)
@@ -955,6 +960,9 @@ int panfrost_mmu_init(struct panfrost_device *pfdev)
{
int err;
+ INIT_LIST_HEAD(&pfdev->as_lru_list);
+ spin_lock_init(&pfdev->as_lock);
+
pfdev->mmu_irq = platform_get_irq_byname(to_platform_device(pfdev->base.dev), "mmu");
if (pfdev->mmu_irq < 0)
return pfdev->mmu_irq;
@@ -970,6 +978,8 @@ int panfrost_mmu_init(struct panfrost_device *pfdev)
return err;
}
+ panfrost_mmu_enable_interrupts(pfdev);
+
return 0;
}
diff --git a/drivers/gpu/drm/panfrost/panfrost_perfcnt.c b/drivers/gpu/drm/panfrost/panfrost_perfcnt.c
index 7020c0192e18..a9def452d5ef 100644
--- a/drivers/gpu/drm/panfrost/panfrost_perfcnt.c
+++ b/drivers/gpu/drm/panfrost/panfrost_perfcnt.c
@@ -1,6 +1,7 @@
// SPDX-License-Identifier: GPL-2.0
/* Copyright 2019 Collabora Ltd */
+#include "asm-generic/errno-base.h"
#include <linux/completion.h>
#include <linux/iopoll.h>
#include <linux/iosys-map.h>
@@ -11,6 +12,7 @@
#include <drm/drm_file.h>
#include <drm/drm_gem_shmem_helper.h>
#include <drm/panfrost_drm.h>
+#include <drm/drm_print.h>
#include "panfrost_device.h"
#include "panfrost_features.h"
@@ -28,13 +30,33 @@
struct panfrost_perfcnt {
struct panfrost_gem_mapping *mapping;
+ unsigned int counterset;
size_t bosize;
void *buf;
struct panfrost_file_priv *user;
struct mutex lock;
struct completion dump_comp;
+ u32 state;
+ bool owns_as_ref;
};
+static void panfrost_perfcnt_hw_disable(struct panfrost_device *pfdev)
+{
+ struct panfrost_perfcnt *perfcnt = pfdev->perfcnt;
+
+ gpu_write(pfdev, GPU_PERFCNT_CFG,
+ GPU_PERFCNT_CFG_MODE(GPU_PERFCNT_CFG_MODE_OFF));
+ gpu_write(pfdev, GPU_PRFCNT_JM_EN, 0x0);
+ gpu_write(pfdev, GPU_PRFCNT_SHADER_EN, 0x0);
+ gpu_write(pfdev, GPU_PRFCNT_MMU_L2_EN, 0x0);
+ gpu_write(pfdev, GPU_PRFCNT_TILER_EN, 0);
+
+ if (perfcnt->owns_as_ref) {
+ panfrost_mmu_as_put(pfdev, perfcnt->mapping->mmu);
+ perfcnt->owns_as_ref = false;
+ }
+}
+
void panfrost_perfcnt_clean_cache_done(struct panfrost_device *pfdev)
{
complete(&pfdev->perfcnt->dump_comp);
@@ -42,30 +64,136 @@ void panfrost_perfcnt_clean_cache_done(struct panfrost_device *pfdev)
void panfrost_perfcnt_sample_done(struct panfrost_device *pfdev)
{
- gpu_write(pfdev, GPU_CMD, GPU_CMD_CLEAN_CACHES);
+ if (pfdev->features.selected_coherency != COHERENCY_ACE)
+ gpu_write(pfdev, GPU_CMD, GPU_CMD_CLEAN_CACHES);
+ else
+ complete(&pfdev->perfcnt->dump_comp);
+}
+
+static int panfrost_perfcnt_hw_enable(struct panfrost_device *pfdev)
+{
+ struct panfrost_perfcnt *perfcnt = pfdev->perfcnt;
+ u32 cfg, as;
+ int ret;
+
+ lockdep_assert_held(&pfdev->reset.lock);
+ drm_WARN_ON(&pfdev->base, perfcnt->owns_as_ref);
+
+ ret = panfrost_mmu_as_get(pfdev, perfcnt->mapping->mmu);
+ if (ret < 0)
+ return ret;
+
+ perfcnt->owns_as_ref = true;
+
+ as = ret;
+ cfg = GPU_PERFCNT_CFG_AS(as) |
+ GPU_PERFCNT_CFG_MODE(GPU_PERFCNT_CFG_MODE_MANUAL);
+
+ /*
+ * Bifrost GPUs have 2 set of counters, but we're only interested by
+ * the first one for now.
+ */
+ if (panfrost_model_is_bifrost(pfdev))
+ cfg |= GPU_PERFCNT_CFG_SETSEL(perfcnt->counterset);
+
+ gpu_write(pfdev, GPU_PRFCNT_JM_EN, 0xffffffff);
+ gpu_write(pfdev, GPU_PRFCNT_SHADER_EN, 0xffffffff);
+ gpu_write(pfdev, GPU_PRFCNT_MMU_L2_EN, 0xffffffff);
+
+ /*
+ * Due to PRLAM-8186 we need to disable the Tiler before we enable HW
+ * counters.
+ */
+ if (panfrost_has_hw_issue(pfdev, HW_ISSUE_8186))
+ gpu_write(pfdev, GPU_PRFCNT_TILER_EN, 0);
+ else
+ gpu_write(pfdev, GPU_PRFCNT_TILER_EN, 0xffffffff);
+
+ gpu_write(pfdev, GPU_PERFCNT_CFG, cfg);
+
+ if (panfrost_has_hw_issue(pfdev, HW_ISSUE_8186))
+ gpu_write(pfdev, GPU_PRFCNT_TILER_EN, 0xffffffff);
+
+ return 0;
}
-static int panfrost_perfcnt_dump_locked(struct panfrost_device *pfdev)
+static int panfrost_perfcnt_dump_locked(struct panfrost_device *pfdev, u32 *state)
{
- u64 gpuva;
+ struct panfrost_perfcnt *perfcnt = pfdev->perfcnt;
+ u64 gpuva = perfcnt->mapping->mmnode.start << PAGE_SHIFT;
int ret;
- reinit_completion(&pfdev->perfcnt->dump_comp);
- gpuva = pfdev->perfcnt->mapping->mmnode.start << PAGE_SHIFT;
- gpu_write(pfdev, GPU_PERFCNT_BASE_LO, lower_32_bits(gpuva));
- gpu_write(pfdev, GPU_PERFCNT_BASE_HI, upper_32_bits(gpuva));
- gpu_write(pfdev, GPU_INT_CLEAR,
- GPU_IRQ_CLEAN_CACHES_COMPLETED |
- GPU_IRQ_PERFCNT_SAMPLE_COMPLETED);
- gpu_write(pfdev, GPU_CMD, GPU_CMD_PERFCNT_SAMPLE);
+ scoped_guard(rwsem_read, &pfdev->reset.lock) {
+ *state = perfcnt->state;
+ if (perfcnt->state & PANFROST_PERFCNT_SESSION_DEAD)
+ return -EIO;
+
+ perfcnt->state = 0;
+
+ reinit_completion(&pfdev->perfcnt->dump_comp);
+
+ gpu_write(pfdev, GPU_PERFCNT_BASE_LO, lower_32_bits(gpuva));
+ gpu_write(pfdev, GPU_PERFCNT_BASE_HI, upper_32_bits(gpuva));
+ gpu_write(pfdev, GPU_INT_CLEAR, GPU_IRQ_CLEAN_CACHES_COMPLETED |
+ GPU_IRQ_PERFCNT_SAMPLE_COMPLETED);
+ gpu_write(pfdev, GPU_CMD, GPU_CMD_PERFCNT_SAMPLE);
+ }
+
+ /*
+ * Here we release the reset semaphore because perfcnt should not get in the way
+ * of a HW reset. Besides, a legitimate reset might be issued during the wait.
+ */
ret = wait_for_completion_interruptible_timeout(&pfdev->perfcnt->dump_comp,
msecs_to_jiffies(1000));
+
if (!ret)
ret = -ETIMEDOUT;
- else if (ret > 0)
- ret = 0;
- return ret;
+ /* We need to restore perfcnt state so the next successful dump can relay it to UM */
+ if (ret < 0) {
+ perfcnt->state |= *state;
+ *state = 0;
+ return ret;
+ }
+
+ scoped_guard(rwsem_read, &pfdev->reset.lock) {
+ *state |= perfcnt->state;
+
+ /* UM must re-enable their session before requesting new dumps. */
+ if (perfcnt->state & PANFROST_PERFCNT_SESSION_DEAD)
+ return -EIO;
+
+ /* If we faced a reset during our SAMPLE, the user needs to try again. */
+ if (perfcnt->state & PANFROST_PERFCNT_SESSION_INTERRUPTED_BY_RESET)
+ return -EAGAIN;
+ }
+
+ return 0;
+}
+
+static int panfrost_perfcnt_disable_locked(struct panfrost_device *pfdev,
+ struct drm_file *file_priv)
+{
+ struct panfrost_file_priv *user = file_priv->driver_priv;
+ struct panfrost_perfcnt *perfcnt = pfdev->perfcnt;
+ struct iosys_map map = IOSYS_MAP_INIT_VADDR(perfcnt->buf);
+
+ if (user != perfcnt->user)
+ return -EINVAL;
+
+ scoped_guard(rwsem_read, &pfdev->reset.lock) {
+ panfrost_perfcnt_hw_disable(pfdev);
+ perfcnt->user = NULL;
+ }
+
+ drm_gem_vunmap(&perfcnt->mapping->obj->base.base, &map);
+ perfcnt->buf = NULL;
+ panfrost_gem_close(&perfcnt->mapping->obj->base.base, file_priv);
+ panfrost_gem_mapping_put(perfcnt->mapping);
+ perfcnt->mapping = NULL;
+ pm_runtime_put_autosuspend(pfdev->base.dev);
+
+ return 0;
}
static int panfrost_perfcnt_enable_locked(struct panfrost_device *pfdev,
@@ -74,14 +202,29 @@ static int panfrost_perfcnt_enable_locked(struct panfrost_device *pfdev,
{
struct panfrost_file_priv *user = file_priv->driver_priv;
struct panfrost_perfcnt *perfcnt = pfdev->perfcnt;
- struct iosys_map map;
struct drm_gem_shmem_object *bo;
- u32 cfg, as;
+ struct iosys_map map;
int ret;
- if (user == perfcnt->user)
+ if (perfcnt->user == user) {
+ scoped_guard(rwsem_read, &pfdev->reset.lock) {
+ if (perfcnt->state & PANFROST_PERFCNT_SESSION_DEAD) {
+ /* A successful reset might've come through in the time gap
+ * between the last failed one and this re-enable attempt.
+ */
+ ret = (perfcnt->owns_as_ref) ? 0 :
+ panfrost_perfcnt_hw_enable(pfdev);
+ if (!ret)
+ perfcnt->state = 0;
+
+ return ret;
+ }
+ }
+
return 0;
- else if (perfcnt->user)
+ }
+
+ if (perfcnt->user)
return -EBUSY;
ret = pm_runtime_get_sync(pfdev->base.dev);
@@ -109,65 +252,30 @@ static int panfrost_perfcnt_enable_locked(struct panfrost_device *pfdev,
ret = drm_gem_vmap(&bo->base, &map);
if (ret)
goto err_put_mapping;
+
perfcnt->buf = map.vaddr;
+ perfcnt->counterset = counterset;
panfrost_gem_internal_set_label(&bo->base, "Perfcnt sample buffer");
- /*
- * Invalidate the cache and clear the counters to start from a fresh
- * state.
- */
- reinit_completion(&pfdev->perfcnt->dump_comp);
- gpu_write(pfdev, GPU_INT_CLEAR,
- GPU_IRQ_CLEAN_CACHES_COMPLETED |
- GPU_IRQ_PERFCNT_SAMPLE_COMPLETED);
- gpu_write(pfdev, GPU_CMD, GPU_CMD_PERFCNT_CLEAR);
- gpu_write(pfdev, GPU_CMD, GPU_CMD_CLEAN_INV_CACHES);
- ret = wait_for_completion_timeout(&pfdev->perfcnt->dump_comp,
- msecs_to_jiffies(1000));
- if (!ret) {
- ret = -ETIMEDOUT;
- goto err_vunmap;
- }
-
- ret = panfrost_mmu_as_get(pfdev, perfcnt->mapping->mmu);
- if (ret < 0)
- goto err_vunmap;
-
- as = ret;
- cfg = GPU_PERFCNT_CFG_AS(as) |
- GPU_PERFCNT_CFG_MODE(GPU_PERFCNT_CFG_MODE_MANUAL);
-
- /*
- * Bifrost GPUs have 2 set of counters, but we're only interested by
- * the first one for now.
- */
- if (panfrost_model_is_bifrost(pfdev))
- cfg |= GPU_PERFCNT_CFG_SETSEL(counterset);
-
- gpu_write(pfdev, GPU_PRFCNT_JM_EN, 0xffffffff);
- gpu_write(pfdev, GPU_PRFCNT_SHADER_EN, 0xffffffff);
- gpu_write(pfdev, GPU_PRFCNT_MMU_L2_EN, 0xffffffff);
-
- /*
- * Due to PRLAM-8186 we need to disable the Tiler before we enable HW
- * counters.
- */
- if (panfrost_has_hw_issue(pfdev, HW_ISSUE_8186))
- gpu_write(pfdev, GPU_PRFCNT_TILER_EN, 0);
- else
- gpu_write(pfdev, GPU_PRFCNT_TILER_EN, 0xffffffff);
+ scoped_guard(rwsem_read, &pfdev->reset.lock) {
+ /*
+ * Clear the counters to start from a fresh state.
+ */
+ gpu_write(pfdev, GPU_INT_CLEAR, GPU_IRQ_PERFCNT_SAMPLE_COMPLETED);
+ gpu_write(pfdev, GPU_CMD, GPU_CMD_PERFCNT_CLEAR);
- gpu_write(pfdev, GPU_PERFCNT_CFG, cfg);
+ ret = panfrost_perfcnt_hw_enable(pfdev);
+ if (ret)
+ goto err_vunmap;
- if (panfrost_has_hw_issue(pfdev, HW_ISSUE_8186))
- gpu_write(pfdev, GPU_PRFCNT_TILER_EN, 0xffffffff);
+ perfcnt->user = user;
+ perfcnt->state = 0;
+ }
/* The BO ref is retained by the mapping. */
drm_gem_object_put(&bo->base);
- perfcnt->user = user;
-
return 0;
err_vunmap:
@@ -183,35 +291,6 @@ err_put_pm:
return ret;
}
-static int panfrost_perfcnt_disable_locked(struct panfrost_device *pfdev,
- struct drm_file *file_priv)
-{
- struct panfrost_file_priv *user = file_priv->driver_priv;
- struct panfrost_perfcnt *perfcnt = pfdev->perfcnt;
- struct iosys_map map = IOSYS_MAP_INIT_VADDR(perfcnt->buf);
-
- if (user != perfcnt->user)
- return -EINVAL;
-
- gpu_write(pfdev, GPU_PRFCNT_JM_EN, 0x0);
- gpu_write(pfdev, GPU_PRFCNT_SHADER_EN, 0x0);
- gpu_write(pfdev, GPU_PRFCNT_MMU_L2_EN, 0x0);
- gpu_write(pfdev, GPU_PRFCNT_TILER_EN, 0);
- gpu_write(pfdev, GPU_PERFCNT_CFG,
- GPU_PERFCNT_CFG_MODE(GPU_PERFCNT_CFG_MODE_OFF));
-
- perfcnt->user = NULL;
- drm_gem_vunmap(&perfcnt->mapping->obj->base.base, &map);
- perfcnt->buf = NULL;
- panfrost_gem_close(&perfcnt->mapping->obj->base.base, file_priv);
- panfrost_mmu_as_put(pfdev, perfcnt->mapping->mmu);
- panfrost_gem_mapping_put(perfcnt->mapping);
- perfcnt->mapping = NULL;
- pm_runtime_put_autosuspend(pfdev->base.dev);
-
- return 0;
-}
-
int panfrost_ioctl_perfcnt_enable(struct drm_device *dev, void *data,
struct drm_file *file_priv)
{
@@ -252,13 +331,16 @@ int panfrost_ioctl_perfcnt_dump(struct drm_device *dev, void *data,
if (ret)
return ret;
+ if (req->pad)
+ return -EINVAL;
+
mutex_lock(&perfcnt->lock);
if (perfcnt->user != file_priv->driver_priv) {
ret = -EINVAL;
goto out;
}
- ret = panfrost_perfcnt_dump_locked(pfdev);
+ ret = panfrost_perfcnt_dump_locked(pfdev, &req->state);
if (ret)
goto out;
@@ -325,18 +407,12 @@ int panfrost_perfcnt_init(struct panfrost_device *pfdev)
return -ENOMEM;
perfcnt->bosize = size;
+ pfdev->perfcnt = perfcnt;
+ mutex_init(&perfcnt->lock);
+ init_completion(&perfcnt->dump_comp);
/* Start with everything disabled. */
- gpu_write(pfdev, GPU_PERFCNT_CFG,
- GPU_PERFCNT_CFG_MODE(GPU_PERFCNT_CFG_MODE_OFF));
- gpu_write(pfdev, GPU_PRFCNT_JM_EN, 0);
- gpu_write(pfdev, GPU_PRFCNT_SHADER_EN, 0);
- gpu_write(pfdev, GPU_PRFCNT_MMU_L2_EN, 0);
- gpu_write(pfdev, GPU_PRFCNT_TILER_EN, 0);
-
- init_completion(&perfcnt->dump_comp);
- mutex_init(&perfcnt->lock);
- pfdev->perfcnt = perfcnt;
+ panfrost_perfcnt_hw_disable(pfdev);
return 0;
}
@@ -344,10 +420,27 @@ int panfrost_perfcnt_init(struct panfrost_device *pfdev)
void panfrost_perfcnt_fini(struct panfrost_device *pfdev)
{
/* Disable everything before leaving. */
- gpu_write(pfdev, GPU_PERFCNT_CFG,
- GPU_PERFCNT_CFG_MODE(GPU_PERFCNT_CFG_MODE_OFF));
- gpu_write(pfdev, GPU_PRFCNT_JM_EN, 0);
- gpu_write(pfdev, GPU_PRFCNT_SHADER_EN, 0);
- gpu_write(pfdev, GPU_PRFCNT_MMU_L2_EN, 0);
- gpu_write(pfdev, GPU_PRFCNT_TILER_EN, 0);
+ panfrost_perfcnt_hw_disable(pfdev);
+}
+
+void panfrost_perfcnt_reset(struct panfrost_device *pfdev)
+{
+ struct panfrost_perfcnt *perfcnt = pfdev->perfcnt;
+
+ if (drm_WARN_ON(&pfdev->base, !perfcnt))
+ return;
+
+ lockdep_assert_held(&pfdev->reset.lock);
+
+ if (!perfcnt->user)
+ return;
+
+ /* All active AS are released during the MMU post_reset. */
+ perfcnt->owns_as_ref = false;
+ perfcnt->state |= PANFROST_PERFCNT_SESSION_INTERRUPTED_BY_RESET;
+ if (panfrost_perfcnt_hw_enable(pfdev))
+ perfcnt->state |= PANFROST_PERFCNT_SESSION_DEAD;
+
+ /* Unblock pending sample requests. */
+ complete(&perfcnt->dump_comp);
}
diff --git a/drivers/gpu/drm/panfrost/panfrost_perfcnt.h b/drivers/gpu/drm/panfrost/panfrost_perfcnt.h
index 8bbcf5f5fb33..8b9bc704b634 100644
--- a/drivers/gpu/drm/panfrost/panfrost_perfcnt.h
+++ b/drivers/gpu/drm/panfrost/panfrost_perfcnt.h
@@ -14,5 +14,6 @@ int panfrost_ioctl_perfcnt_enable(struct drm_device *dev, void *data,
struct drm_file *file_priv);
int panfrost_ioctl_perfcnt_dump(struct drm_device *dev, void *data,
struct drm_file *file_priv);
+void panfrost_perfcnt_reset(struct panfrost_device *pfdev);
#endif
diff --git a/drivers/gpu/drm/panthor/panthor_drv.c b/drivers/gpu/drm/panthor/panthor_drv.c
index 1ca8aedd83f4..dc8d0191aef4 100644
--- a/drivers/gpu/drm/panthor/panthor_drv.c
+++ b/drivers/gpu/drm/panthor/panthor_drv.c
@@ -1081,7 +1081,7 @@ static int panthor_ioctl_bo_mmap_offset(struct drm_device *ddev, void *data,
struct drm_panthor_bo_mmap_offset *args = data;
struct panthor_gem_object *bo;
struct drm_gem_object *obj;
- int ret;
+ int ret = 0;
if (args->pad)
return -EINVAL;
@@ -1096,10 +1096,6 @@ static int panthor_ioctl_bo_mmap_offset(struct drm_device *ddev, void *data,
goto out;
}
- ret = drm_gem_create_mmap_offset(obj);
- if (ret)
- goto out;
-
args->offset = drm_vma_node_offset_addr(&obj->vma_node);
out:
diff --git a/drivers/gpu/drm/panthor/panthor_gem.c b/drivers/gpu/drm/panthor/panthor_gem.c
index c07a44057e42..22b57ab6f929 100644
--- a/drivers/gpu/drm/panthor/panthor_gem.c
+++ b/drivers/gpu/drm/panthor/panthor_gem.c
@@ -744,6 +744,9 @@ static int panthor_gem_mmap(struct drm_gem_object *obj, struct vm_area_struct *v
struct panthor_gem_object *bo = to_panthor_bo(obj);
int ret;
+ if (bo->flags & DRM_PANTHOR_BO_NO_MMAP)
+ return -EINVAL;
+
if (drm_gem_is_imported(obj)) {
/* Reset both vm_ops and vm_private_data, so we don't end up with
* vm_ops pointing to our implementation if the dma-buf backend
diff --git a/drivers/gpu/drm/panthor/panthor_sched.c b/drivers/gpu/drm/panthor/panthor_sched.c
index 0493858e55d7..a343c5180e01 100644
--- a/drivers/gpu/drm/panthor/panthor_sched.c
+++ b/drivers/gpu/drm/panthor/panthor_sched.c
@@ -3770,12 +3770,18 @@ int panthor_group_destroy(struct panthor_file *pfile, u32 group_handle)
struct panthor_group_pool *gpool = pfile->groups;
struct panthor_device *ptdev = pfile->ptdev;
struct panthor_scheduler *sched = ptdev->scheduler;
- struct panthor_group *group;
+ struct panthor_group *group = NULL;
- if (!xa_get_mark(&gpool->xa, group_handle, GROUP_REGISTERED))
- return -EINVAL;
+ /*
+ * Check the mark and erase the entry atomically, so a concurrent
+ * destroy + create can't make us erase a group that's still being
+ * initialized and happens to reuse the same handle.
+ */
+ xa_lock(&gpool->xa);
+ if (xa_get_mark(&gpool->xa, group_handle, GROUP_REGISTERED))
+ group = __xa_erase(&gpool->xa, group_handle);
+ xa_unlock(&gpool->xa);
- group = xa_erase(&gpool->xa, group_handle);
if (!group)
return -EINVAL;
diff --git a/drivers/gpu/drm/rockchip/dw_hdmi_qp-rockchip.c b/drivers/gpu/drm/rockchip/dw_hdmi_qp-rockchip.c
index 9c4cf68d79d5..f2a1ea8dfefa 100644
--- a/drivers/gpu/drm/rockchip/dw_hdmi_qp-rockchip.c
+++ b/drivers/gpu/drm/rockchip/dw_hdmi_qp-rockchip.c
@@ -24,7 +24,6 @@
#include <drm/drm_bridge_connector.h>
#include <drm/drm_managed.h>
#include <drm/drm_of.h>
-#include <drm/drm_probe_helper.h>
#include "rockchip_drm_drv.h"
@@ -34,6 +33,7 @@
#define RK3576_IOC_HDMI_HPD_STATUS 0xa440
#define RK3576_HDMI_LEVEL_INT BIT(3)
+#define RK3576_HDMI_OHPD_INT BIT(4)
#define RK3576_VO0_GRF_SOC_CON1 0x0004
#define RK3576_HDMI_FRL_MOD BIT(0)
@@ -64,7 +64,9 @@
#define RK3588_HPD_HDMI1_IO_EN_MASK BIT(13)
#define RK3588_GRF_SOC_STATUS1 0x0384
#define RK3588_HDMI0_LEVEL_INT BIT(16)
+#define RK3588_HDMI0_OHPD_INT BIT(17)
#define RK3588_HDMI1_LEVEL_INT BIT(24)
+#define RK3588_HDMI1_OHPD_INT BIT(25)
#define RK3588_GRF_VO1_CON3 0x000c
#define RK3588_GRF_VO1_CON6 0x0018
#define RK3588_COLOR_DEPTH_MASK GENMASK(7, 4)
@@ -96,6 +98,7 @@ struct rockchip_hdmi_qp {
struct phy *phy;
struct gpio_desc *frl_enable_gpio;
struct delayed_work hpd_work;
+ int hpd_irq;
int port_id;
const struct rockchip_hdmi_qp_ctrl_ops *ctrl_ops;
unsigned long long tmds_char_rate;
@@ -274,9 +277,8 @@ dw_hdmi_qp_rk3588_read_hpd(struct dw_hdmi_qp *dw_hdmi, void *data)
return val ? connector_status_connected : connector_status_disconnected;
}
-static void dw_hdmi_qp_rk3588_setup_hpd(struct dw_hdmi_qp *dw_hdmi, void *data)
+static void dw_hdmi_qp_rk3588_unmask_hpd(struct rockchip_hdmi_qp *hdmi)
{
- struct rockchip_hdmi_qp *hdmi = (struct rockchip_hdmi_qp *)data;
u32 val;
if (hdmi->port_id)
@@ -289,11 +291,48 @@ static void dw_hdmi_qp_rk3588_setup_hpd(struct dw_hdmi_qp *dw_hdmi, void *data)
regmap_write(hdmi->regmap, RK3588_GRF_SOC_CON2, val);
}
+static void dw_hdmi_qp_rk3588_mask_hpd(struct rockchip_hdmi_qp *hdmi)
+{
+ u32 val;
+
+ if (hdmi->port_id)
+ val = FIELD_PREP_WM16(RK3588_HDMI1_HPD_INT_MSK, 1);
+ else
+ val = FIELD_PREP_WM16(RK3588_HDMI0_HPD_INT_MSK, 1);
+
+ regmap_write(hdmi->regmap, RK3588_GRF_SOC_CON2, val);
+}
+
+static void dw_hdmi_qp_rk3588_enable_hpd(struct dw_hdmi_qp *dw_hdmi, void *data)
+{
+ struct rockchip_hdmi_qp *hdmi = (struct rockchip_hdmi_qp *)data;
+
+ dw_hdmi_qp_rk3588_unmask_hpd(hdmi);
+ enable_irq(hdmi->hpd_irq);
+}
+
+static void dw_hdmi_qp_rk3588_disable_hpd(struct dw_hdmi_qp *dw_hdmi, void *data)
+{
+ struct rockchip_hdmi_qp *hdmi = (struct rockchip_hdmi_qp *)data;
+
+ disable_irq(hdmi->hpd_irq);
+ dw_hdmi_qp_rk3588_mask_hpd(hdmi);
+
+ /*
+ * Cannot use the _sync() variant: the work takes mode_config.mutex,
+ * which the connector probe path already holds when acquiring the
+ * hpd_state_mutex this caller runs under. Requeuing is already ruled
+ * out by disable_irq().
+ */
+ cancel_delayed_work(&hdmi->hpd_work);
+}
+
static const struct dw_hdmi_qp_phy_ops rk3588_hdmi_phy_ops = {
.init = dw_hdmi_qp_rk3588_phy_init,
.disable = dw_hdmi_qp_rk3588_phy_disable,
.read_hpd = dw_hdmi_qp_rk3588_read_hpd,
- .setup_hpd = dw_hdmi_qp_rk3588_setup_hpd,
+ .enable_hpd = dw_hdmi_qp_rk3588_enable_hpd,
+ .disable_hpd = dw_hdmi_qp_rk3588_disable_hpd,
};
static enum drm_connector_status
@@ -308,23 +347,49 @@ dw_hdmi_qp_rk3576_read_hpd(struct dw_hdmi_qp *dw_hdmi, void *data)
connector_status_connected : connector_status_disconnected;
}
-static void dw_hdmi_qp_rk3576_setup_hpd(struct dw_hdmi_qp *dw_hdmi, void *data)
+static void dw_hdmi_qp_rk3576_unmask_hpd(struct rockchip_hdmi_qp *hdmi)
+{
+ regmap_write(hdmi->regmap, RK3576_IOC_MISC_CON0,
+ FIELD_PREP_WM16(RK3576_HDMI_HPD_INT_CLR, 1) |
+ FIELD_PREP_WM16(RK3576_HDMI_HPD_INT_MSK, 0));
+}
+
+static void dw_hdmi_qp_rk3576_mask_hpd(struct rockchip_hdmi_qp *hdmi)
+{
+ regmap_write(hdmi->regmap, RK3576_IOC_MISC_CON0,
+ FIELD_PREP_WM16(RK3576_HDMI_HPD_INT_MSK, 1));
+}
+
+static void dw_hdmi_qp_rk3576_enable_hpd(struct dw_hdmi_qp *dw_hdmi, void *data)
{
struct rockchip_hdmi_qp *hdmi = (struct rockchip_hdmi_qp *)data;
- u32 val;
- val = (FIELD_PREP_WM16(RK3576_HDMI_HPD_INT_CLR, 1) |
- FIELD_PREP_WM16(RK3576_HDMI_HPD_INT_MSK, 0));
+ dw_hdmi_qp_rk3576_unmask_hpd(hdmi);
+ enable_irq(hdmi->hpd_irq);
+}
- regmap_write(hdmi->regmap, RK3576_IOC_MISC_CON0, val);
- regmap_write(hdmi->regmap, 0xa404, 0xffff0102);
+static void dw_hdmi_qp_rk3576_disable_hpd(struct dw_hdmi_qp *dw_hdmi, void *data)
+{
+ struct rockchip_hdmi_qp *hdmi = (struct rockchip_hdmi_qp *)data;
+
+ disable_irq(hdmi->hpd_irq);
+ dw_hdmi_qp_rk3576_mask_hpd(hdmi);
+
+ /*
+ * Cannot use the _sync() variant: the work takes mode_config.mutex,
+ * which the connector probe path already holds when acquiring the
+ * hpd_state_mutex this caller runs under. Requeuing is already ruled
+ * out by disable_irq().
+ */
+ cancel_delayed_work(&hdmi->hpd_work);
}
static const struct dw_hdmi_qp_phy_ops rk3576_hdmi_phy_ops = {
.init = dw_hdmi_qp_rk3588_phy_init,
.disable = dw_hdmi_qp_rk3588_phy_disable,
.read_hpd = dw_hdmi_qp_rk3576_read_hpd,
- .setup_hpd = dw_hdmi_qp_rk3576_setup_hpd,
+ .enable_hpd = dw_hdmi_qp_rk3576_enable_hpd,
+ .disable_hpd = dw_hdmi_qp_rk3576_disable_hpd,
};
static void dw_hdmi_qp_rk3588_hpd_work(struct work_struct *work)
@@ -332,26 +397,19 @@ static void dw_hdmi_qp_rk3588_hpd_work(struct work_struct *work)
struct rockchip_hdmi_qp *hdmi = container_of(work,
struct rockchip_hdmi_qp,
hpd_work.work);
- struct drm_device *drm = hdmi->encoder.encoder.dev;
- bool changed;
- if (drm) {
- changed = drm_helper_hpd_irq_event(drm);
- if (changed)
- dev_dbg(hdmi->dev, "connector status changed\n");
- }
+ dw_hdmi_qp_hpd_notify(hdmi->hdmi);
}
static irqreturn_t dw_hdmi_qp_rk3576_hardirq(int irq, void *dev_id)
{
struct rockchip_hdmi_qp *hdmi = dev_id;
- u32 intr_stat, val;
+ u32 intr_stat;
regmap_read(hdmi->regmap, RK3576_IOC_HDMI_HPD_STATUS, &intr_stat);
- if (intr_stat) {
- val = FIELD_PREP_WM16(RK3576_HDMI_HPD_INT_MSK, 1);
- regmap_write(hdmi->regmap, RK3576_IOC_MISC_CON0, val);
+ if (intr_stat & RK3576_HDMI_OHPD_INT) {
+ dw_hdmi_qp_rk3576_mask_hpd(hdmi);
return IRQ_WAKE_THREAD;
}
@@ -361,15 +419,11 @@ static irqreturn_t dw_hdmi_qp_rk3576_hardirq(int irq, void *dev_id)
static irqreturn_t dw_hdmi_qp_rk3576_irq(int irq, void *dev_id)
{
struct rockchip_hdmi_qp *hdmi = dev_id;
- u32 val;
- val = FIELD_PREP_WM16(RK3576_HDMI_HPD_INT_CLR, 1);
- regmap_write(hdmi->regmap, RK3576_IOC_MISC_CON0, val);
mod_delayed_work(system_percpu_wq, &hdmi->hpd_work,
msecs_to_jiffies(HOTPLUG_DEBOUNCE_MS));
- val = FIELD_PREP_WM16(RK3576_HDMI_HPD_INT_MSK, 0);
- regmap_write(hdmi->regmap, RK3576_IOC_MISC_CON0, val);
+ dw_hdmi_qp_rk3576_unmask_hpd(hdmi);
return IRQ_HANDLED;
}
@@ -377,16 +431,14 @@ static irqreturn_t dw_hdmi_qp_rk3576_irq(int irq, void *dev_id)
static irqreturn_t dw_hdmi_qp_rk3588_hardirq(int irq, void *dev_id)
{
struct rockchip_hdmi_qp *hdmi = dev_id;
- u32 intr_stat, val;
+ u32 intr_stat;
regmap_read(hdmi->regmap, RK3588_GRF_SOC_STATUS1, &intr_stat);
+ intr_stat &= hdmi->port_id ? RK3588_HDMI1_OHPD_INT : RK3588_HDMI0_OHPD_INT;
+
if (intr_stat) {
- if (hdmi->port_id)
- val = FIELD_PREP_WM16(RK3588_HDMI1_HPD_INT_MSK, 1);
- else
- val = FIELD_PREP_WM16(RK3588_HDMI0_HPD_INT_MSK, 1);
- regmap_write(hdmi->regmap, RK3588_GRF_SOC_CON2, val);
+ dw_hdmi_qp_rk3588_mask_hpd(hdmi);
return IRQ_WAKE_THREAD;
}
@@ -396,22 +448,11 @@ static irqreturn_t dw_hdmi_qp_rk3588_hardirq(int irq, void *dev_id)
static irqreturn_t dw_hdmi_qp_rk3588_irq(int irq, void *dev_id)
{
struct rockchip_hdmi_qp *hdmi = dev_id;
- u32 val;
-
- if (hdmi->port_id)
- val = FIELD_PREP_WM16(RK3588_HDMI1_HPD_INT_CLR, 1);
- else
- val = FIELD_PREP_WM16(RK3588_HDMI0_HPD_INT_CLR, 1);
- regmap_write(hdmi->regmap, RK3588_GRF_SOC_CON2, val);
mod_delayed_work(system_percpu_wq, &hdmi->hpd_work,
msecs_to_jiffies(HOTPLUG_DEBOUNCE_MS));
- if (hdmi->port_id)
- val |= FIELD_PREP_WM16(RK3588_HDMI1_HPD_INT_MSK, 0);
- else
- val |= FIELD_PREP_WM16(RK3588_HDMI0_HPD_INT_MSK, 0);
- regmap_write(hdmi->regmap, RK3588_GRF_SOC_CON2, val);
+ dw_hdmi_qp_rk3588_unmask_hpd(hdmi);
return IRQ_HANDLED;
}
@@ -424,11 +465,11 @@ static void dw_hdmi_qp_rk3576_io_init(struct rockchip_hdmi_qp *hdmi)
FIELD_PREP_WM16(RK3576_SDAIN_MASK, 1) |
FIELD_PREP_WM16(RK3576_HDMI_GRANT_SEL, 1) |
FIELD_PREP_WM16(RK3576_I2S_SEL_MASK, 1);
-
regmap_write(hdmi->vo_regmap, RK3576_VO0_GRF_SOC_CON14, val);
- val = FIELD_PREP_WM16(RK3576_HDMI_HPD_INT_MSK, 0);
- regmap_write(hdmi->regmap, RK3576_IOC_MISC_CON0, val);
+ regmap_write(hdmi->regmap, 0xa404, 0xffff0102);
+
+ dw_hdmi_qp_rk3576_mask_hpd(hdmi);
}
static void dw_hdmi_qp_rk3588_io_init(struct rockchip_hdmi_qp *hdmi)
@@ -453,11 +494,7 @@ static void dw_hdmi_qp_rk3588_io_init(struct rockchip_hdmi_qp *hdmi)
val = FIELD_PREP_WM16(RK3588_HDMI0_GRANT_SEL, 1);
regmap_write(hdmi->vo_regmap, RK3588_GRF_VO1_CON9, val);
- if (hdmi->port_id)
- val = FIELD_PREP_WM16(RK3588_HDMI1_HPD_INT_MSK, 1);
- else
- val = FIELD_PREP_WM16(RK3588_HDMI0_HPD_INT_MSK, 1);
- regmap_write(hdmi->regmap, RK3588_GRF_SOC_CON2, val);
+ dw_hdmi_qp_rk3588_mask_hpd(hdmi);
}
static void dw_hdmi_qp_rk3576_enc_init(struct rockchip_hdmi_qp *hdmi,
@@ -561,9 +598,9 @@ static int dw_hdmi_qp_rockchip_bind(struct device *dev, struct device *master,
struct resource *res;
struct clk_bulk_data *clks;
struct clk *ref_clk;
- int ret, irq, i;
+ int ret, i;
- if (!pdev->dev.of_node)
+ if (!dev->of_node)
return -ENODEV;
hdmi = drmm_kzalloc(drm, sizeof(*hdmi), GFP_KERNEL);
@@ -583,7 +620,7 @@ static int dw_hdmi_qp_rockchip_bind(struct device *dev, struct device *master,
return dev_err_probe(dev, -ENODEV, "Missing platform ctrl ops\n");
hdmi->ctrl_ops = cfg->ctrl_ops;
- hdmi->dev = &pdev->dev;
+ hdmi->dev = dev;
hdmi->port_id = -ENODEV;
/* Identify port ID by matching base IO address */
@@ -594,7 +631,7 @@ static int dw_hdmi_qp_rockchip_bind(struct device *dev, struct device *master,
}
}
if (hdmi->port_id < 0)
- return dev_err_probe(hdmi->dev, hdmi->port_id,
+ return dev_err_probe(dev, hdmi->port_id,
"Failed to match HDMI port ID\n");
plat_data.phy_ops = cfg->phy_ops;
@@ -622,37 +659,36 @@ static int dw_hdmi_qp_rockchip_bind(struct device *dev, struct device *master,
hdmi->regmap = syscon_regmap_lookup_by_phandle(dev->of_node,
"rockchip,grf");
if (IS_ERR(hdmi->regmap))
- return dev_err_probe(hdmi->dev, PTR_ERR(hdmi->regmap),
+ return dev_err_probe(dev, PTR_ERR(hdmi->regmap),
"Unable to get rockchip,grf\n");
hdmi->vo_regmap = syscon_regmap_lookup_by_phandle(dev->of_node,
"rockchip,vo-grf");
if (IS_ERR(hdmi->vo_regmap))
- return dev_err_probe(hdmi->dev, PTR_ERR(hdmi->vo_regmap),
+ return dev_err_probe(dev, PTR_ERR(hdmi->vo_regmap),
"Unable to get rockchip,vo-grf\n");
- ret = devm_clk_bulk_get_all_enabled(hdmi->dev, &clks);
+ ret = devm_clk_bulk_get_all_enabled(dev, &clks);
if (ret < 0)
- return dev_err_probe(hdmi->dev, ret, "Failed to get clocks\n");
+ return dev_err_probe(dev, ret, "Failed to get clocks\n");
- ref_clk = clk_get(hdmi->dev, "ref");
+ ref_clk = clk_get(dev, "ref");
if (IS_ERR(ref_clk))
- return dev_err_probe(hdmi->dev, PTR_ERR(ref_clk),
+ return dev_err_probe(dev, PTR_ERR(ref_clk),
"Failed to get ref clock\n");
plat_data.ref_clk_rate = clk_get_rate(ref_clk);
clk_put(ref_clk);
- hdmi->frl_enable_gpio = devm_gpiod_get_optional(hdmi->dev, "frl-enable",
+ hdmi->frl_enable_gpio = devm_gpiod_get_optional(dev, "frl-enable",
GPIOD_OUT_LOW);
if (IS_ERR(hdmi->frl_enable_gpio))
- return dev_err_probe(hdmi->dev, PTR_ERR(hdmi->frl_enable_gpio),
+ return dev_err_probe(dev, PTR_ERR(hdmi->frl_enable_gpio),
"Failed to request FRL enable GPIO\n");
hdmi->phy = devm_of_phy_get_by_index(dev, dev->of_node, 0);
if (IS_ERR(hdmi->phy))
- return dev_err_probe(hdmi->dev, PTR_ERR(hdmi->phy),
- "Failed to get phy\n");
+ return dev_err_probe(dev, PTR_ERR(hdmi->phy), "Failed to get phy\n");
cfg->ctrl_ops->io_init(hdmi);
@@ -666,14 +702,14 @@ static int dw_hdmi_qp_rockchip_bind(struct device *dev, struct device *master,
if (plat_data.cec_irq < 0)
return plat_data.cec_irq;
- irq = platform_get_irq_byname(pdev, "hpd");
- if (irq < 0)
- return irq;
+ hdmi->hpd_irq = platform_get_irq_byname(pdev, "hpd");
+ if (hdmi->hpd_irq < 0)
+ return hdmi->hpd_irq;
- ret = devm_request_threaded_irq(hdmi->dev, irq,
+ ret = devm_request_threaded_irq(dev, hdmi->hpd_irq,
cfg->ctrl_ops->hardirq_callback,
cfg->ctrl_ops->irq_callback,
- IRQF_SHARED, "dw-hdmi-qp-hpd",
+ IRQF_NO_AUTOEN, "dw-hdmi-qp-hpd",
hdmi);
if (ret)
return ret;
@@ -681,20 +717,20 @@ static int dw_hdmi_qp_rockchip_bind(struct device *dev, struct device *master,
drm_encoder_helper_add(encoder, &dw_hdmi_qp_rockchip_encoder_helper_funcs);
ret = drmm_encoder_init(drm, encoder, NULL, DRM_MODE_ENCODER_TMDS, NULL);
if (ret)
- return dev_err_probe(hdmi->dev, ret, "Failed to init encoder");
-
- platform_set_drvdata(pdev, hdmi);
+ return dev_err_probe(dev, ret, "Failed to init encoder\n");
hdmi->hdmi = dw_hdmi_qp_bind(pdev, encoder, &plat_data);
if (IS_ERR(hdmi->hdmi))
- return dev_err_probe(hdmi->dev, PTR_ERR(hdmi->hdmi),
- "Failed to bind dw-hdmi-qp");
+ return dev_err_probe(dev, PTR_ERR(hdmi->hdmi),
+ "Failed to bind dw-hdmi-qp\n");
connector = drm_bridge_connector_init(drm, encoder);
if (IS_ERR(connector))
- return dev_err_probe(hdmi->dev, PTR_ERR(connector),
+ return dev_err_probe(dev, PTR_ERR(connector),
"Failed to init bridge connector\n");
+ platform_set_drvdata(pdev, hdmi);
+
return 0;
}
@@ -704,6 +740,8 @@ static void dw_hdmi_qp_rockchip_unbind(struct device *dev,
{
struct rockchip_hdmi_qp *hdmi = dev_get_drvdata(dev);
+ dev_set_drvdata(dev, NULL);
+
cancel_delayed_work_sync(&hdmi->hpd_work);
}
@@ -726,6 +764,11 @@ static int __maybe_unused dw_hdmi_qp_rockchip_suspend(struct device *dev)
{
struct rockchip_hdmi_qp *hdmi = dev_get_drvdata(dev);
+ if (!hdmi)
+ return 0;
+
+ cancel_delayed_work_sync(&hdmi->hpd_work);
+
dw_hdmi_qp_suspend(dev, hdmi->hdmi);
return 0;
@@ -735,12 +778,14 @@ static int __maybe_unused dw_hdmi_qp_rockchip_resume(struct device *dev)
{
struct rockchip_hdmi_qp *hdmi = dev_get_drvdata(dev);
+ if (!hdmi)
+ return 0;
+
hdmi->ctrl_ops->io_init(hdmi);
dw_hdmi_qp_resume(dev, hdmi->hdmi);
- if (hdmi->encoder.encoder.dev)
- drm_helper_hpd_irq_event(hdmi->encoder.encoder.dev);
+ dw_hdmi_qp_hpd_notify(hdmi->hdmi);
return 0;
}
diff --git a/drivers/gpu/drm/rockchip/rk3066_hdmi.c b/drivers/gpu/drm/rockchip/rk3066_hdmi.c
index f28d6a883e09..273f61758b10 100644
--- a/drivers/gpu/drm/rockchip/rk3066_hdmi.c
+++ b/drivers/gpu/drm/rockchip/rk3066_hdmi.c
@@ -14,6 +14,7 @@
#include <drm/drm_probe_helper.h>
#include <linux/clk.h>
+#include <linux/hdmi.h>
#include <linux/mfd/syscon.h>
#include <linux/platform_device.h>
#include <linux/regmap.h>
@@ -703,7 +704,6 @@ rk3066_hdmi_register(struct drm_device *drm, struct rk3066_hdmi *hdmi)
DRM_MODE_ENCODER_TMDS, NULL);
hdmi->bridge.driver_private = hdmi;
- hdmi->bridge.funcs = &rk3066_hdmi_bridge_funcs;
hdmi->bridge.ops = DRM_BRIDGE_OP_DETECT |
DRM_BRIDGE_OP_EDID |
DRM_BRIDGE_OP_HDMI |
@@ -712,6 +712,7 @@ rk3066_hdmi_register(struct drm_device *drm, struct rk3066_hdmi *hdmi)
hdmi->bridge.type = DRM_MODE_CONNECTOR_HDMIA;
hdmi->bridge.vendor = "Rockchip";
hdmi->bridge.product = "RK3066 HDMI";
+ hdmi->bridge.supported_hdmi_ver = HDMI_VERSION_1_4;
hdmi->bridge.ddc = rk3066_hdmi_i2c_adapter(hdmi);
if (IS_ERR(hdmi->bridge.ddc))
@@ -747,9 +748,10 @@ static int rk3066_hdmi_bind(struct device *dev, struct device *master,
int irq;
int ret;
- hdmi = devm_kzalloc(dev, sizeof(*hdmi), GFP_KERNEL);
- if (!hdmi)
- return -ENOMEM;
+ hdmi = devm_drm_bridge_alloc(dev, struct rk3066_hdmi, bridge,
+ &rk3066_hdmi_bridge_funcs);
+ if (IS_ERR(hdmi))
+ return PTR_ERR(hdmi);
hdmi->dev = dev;
hdmi->drm_dev = drm;
diff --git a/drivers/gpu/drm/scheduler/sched_main.c b/drivers/gpu/drm/scheduler/sched_main.c
index b9580ce1ddec..99252674e266 100644
--- a/drivers/gpu/drm/scheduler/sched_main.c
+++ b/drivers/gpu/drm/scheduler/sched_main.c
@@ -1202,6 +1202,11 @@ static void drm_sched_cancel_remaining_jobs(struct drm_gpu_scheduler *sched)
* is implemented, all jobs will be canceled through it and afterwards cleaned
* up through &struct drm_sched_backend_ops.free_job. If cancel_job is not
* implemented, memory could leak.
+ *
+ * The user must wait one RCU grace period between signaling the last hardware-
+ * fence and calling this function because the timeline name returned by
+ * scheduler fences must stay valid until all readers have exited their RCU read
+ * side critical section.
*/
void drm_sched_fini(struct drm_gpu_scheduler *sched)
{
diff --git a/drivers/gpu/drm/solomon/ssd130x.c b/drivers/gpu/drm/solomon/ssd130x.c
index 8cbf18754487..ee43cf0da879 100644
--- a/drivers/gpu/drm/solomon/ssd130x.c
+++ b/drivers/gpu/drm/solomon/ssd130x.c
@@ -1985,7 +1985,7 @@ static const struct drm_connector_helper_funcs ssd130x_connector_helper_funcs =
};
static const struct drm_connector_funcs ssd130x_connector_funcs = {
- .reset = drm_atomic_helper_connector_reset,
+ .atomic_create_state = drm_atomic_helper_connector_create_state,
.fill_modes = drm_helper_probe_single_connector_modes,
.destroy = drm_connector_cleanup,
.atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
diff --git a/drivers/gpu/drm/sun4i/sun4i_hdmi_enc.c b/drivers/gpu/drm/sun4i/sun4i_hdmi_enc.c
index d98b6f12a7bc..6f4fe0c8efae 100644
--- a/drivers/gpu/drm/sun4i/sun4i_hdmi_enc.c
+++ b/drivers/gpu/drm/sun4i/sun4i_hdmi_enc.c
@@ -192,10 +192,6 @@ sun4i_hdmi_connector_clock_valid(const struct drm_connector *connector,
if (mode->flags & DRM_MODE_FLAG_DBLCLK)
return MODE_BAD;
- /* HDMI 1.0 max TMDS character rate */
- if (clock > HDMI_1_0_TMDS_CHAR_RATE_MAX_HZ)
- return MODE_CLOCK_HIGH;
-
rounded_rate = clk_round_rate(hdmi->tmds_clk, clock);
if (rounded_rate > 0 &&
max_t(unsigned long, rounded_rate, clock) -
@@ -253,6 +249,15 @@ static struct i2c_adapter *sun4i_hdmi_get_ddc(struct device *dev)
}
static const struct drm_connector_hdmi_funcs sun4i_hdmi_hdmi_connector_funcs = {
+ /*
+ * NOTE: Those are likely to be wrong, but I couldn't find
+ * the actual ones in the BSP.
+ */
+ .vendor = "AW",
+ .product = "HDMI",
+ .supported_hdmi_ver = HDMI_VERSION_1_2,
+ .supported_formats = BIT(DRM_OUTPUT_COLOR_FORMAT_RGB444),
+ .max_bpc = 8,
.tmds_char_rate_valid = sun4i_hdmi_connector_clock_valid,
.avi = {
.clear_infoframe = sun4i_hdmi_clear_avi_infoframe,
@@ -654,19 +659,11 @@ static int sun4i_hdmi_bind(struct device *dev, struct device *master,
drm_connector_helper_add(&hdmi->connector,
&sun4i_hdmi_connector_helper_funcs);
- ret = drmm_connector_hdmi_ini2(drm, &hdmi->connector,
- /*
- * NOTE: Those are likely to be
- * wrong, but I couldn't find the
- * actual ones in the BSP.
- */
- "AW", "HDMI",
+ ret = drmm_connector_hdmi_init(drm, &hdmi->connector,
&sun4i_hdmi_connector_funcs,
&sun4i_hdmi_hdmi_connector_funcs,
DRM_MODE_CONNECTOR_HDMIA,
- hdmi->ddc_i2c,
- BIT(DRM_OUTPUT_COLOR_FORMAT_RGB444),
- 8);
+ hdmi->ddc_i2c);
if (ret) {
dev_err(dev,
"Couldn't initialise the HDMI connector\n");
diff --git a/drivers/gpu/drm/sysfb/drm_sysfb_helper.h b/drivers/gpu/drm/sysfb/drm_sysfb_helper.h
index c043428e02fd..6dab27b41c08 100644
--- a/drivers/gpu/drm/sysfb/drm_sysfb_helper.h
+++ b/drivers/gpu/drm/sysfb/drm_sysfb_helper.h
@@ -194,7 +194,7 @@ int drm_sysfb_connector_helper_get_modes(struct drm_connector *connector);
.get_modes = drm_sysfb_connector_helper_get_modes
#define DRM_SYSFB_CONNECTOR_FUNCS \
- .reset = drm_atomic_helper_connector_reset, \
+ .atomic_create_state = drm_atomic_helper_connector_create_state, \
.fill_modes = drm_helper_probe_single_connector_modes, \
.atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state, \
.atomic_destroy_state = drm_atomic_helper_connector_destroy_state
diff --git a/drivers/gpu/drm/tiny/appletbdrm.c b/drivers/gpu/drm/tiny/appletbdrm.c
index 4297d9c1125e..ebfe37557bfe 100644
--- a/drivers/gpu/drm/tiny/appletbdrm.c
+++ b/drivers/gpu/drm/tiny/appletbdrm.c
@@ -30,6 +30,7 @@
#include <drm/drm_gem_atomic_helper.h>
#include <drm/drm_gem_framebuffer_helper.h>
#include <drm/drm_gem_shmem_helper.h>
+#include <drm/drm_modeset_helper.h>
#include <drm/drm_plane.h>
#include <drm/drm_print.h>
#include <drm/drm_probe_helper.h>
@@ -589,7 +590,7 @@ static const struct drm_mode_config_funcs appletbdrm_mode_config_funcs = {
};
static const struct drm_connector_funcs appletbdrm_connector_funcs = {
- .reset = drm_atomic_helper_connector_reset,
+ .atomic_create_state = drm_atomic_helper_connector_create_state,
.destroy = drm_connector_cleanup,
.fill_modes = drm_helper_probe_single_connector_modes,
.atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
@@ -724,7 +725,7 @@ static int appletbdrm_setup_mode_config(struct appletbdrm_device *adev)
ret = drm_connector_attach_encoder(connector, encoder);
if (ret) {
- drm_err(drm, "Failed to initialize simple display pipe\n");
+ drm_err(drm, "Failed to attach connector to encoder\n");
return ret;
}
@@ -821,6 +822,48 @@ static void appletbdrm_shutdown(struct usb_interface *intf)
drm_atomic_helper_shutdown(&adev->drm);
}
+static int appletbdrm_suspend(struct usb_interface *intf, pm_message_t message)
+{
+ struct appletbdrm_device *adev = usb_get_intfdata(intf);
+
+ return drm_mode_config_helper_suspend(&adev->drm);
+}
+
+static int appletbdrm_resume(struct usb_interface *intf)
+{
+ struct appletbdrm_device *adev = usb_get_intfdata(intf);
+
+ return drm_mode_config_helper_resume(&adev->drm);
+}
+
+static int appletbdrm_reset_resume(struct usb_interface *intf)
+{
+ struct appletbdrm_device *adev = usb_get_intfdata(intf);
+ struct drm_device *drm = &adev->drm;
+ int ret, resume_ret;
+
+ /*
+ * A reset puts the device back into its initial state, where it
+ * expects the same handshake as during probe. Resume the DRM side
+ * even if the handshake fails, so that the state saved on suspend
+ * is released.
+ */
+ ret = appletbdrm_get_information(adev);
+ if (ret) {
+ drm_err(drm, "Failed to get display information\n");
+ goto resume;
+ }
+
+ ret = appletbdrm_signal_readiness(adev);
+ if (ret)
+ drm_err(drm, "Failed to signal readiness\n");
+
+resume:
+ resume_ret = drm_mode_config_helper_resume(drm);
+
+ return ret ? ret : resume_ret;
+}
+
static const struct usb_device_id appletbdrm_usb_id_table[] = {
{ USB_DEVICE_INTERFACE_CLASS(0x05ac, 0x8302, USB_CLASS_AUDIO_VIDEO) },
{}
@@ -832,6 +875,9 @@ static struct usb_driver appletbdrm_usb_driver = {
.probe = appletbdrm_probe,
.disconnect = appletbdrm_disconnect,
.shutdown = appletbdrm_shutdown,
+ .suspend = appletbdrm_suspend,
+ .resume = appletbdrm_resume,
+ .reset_resume = appletbdrm_reset_resume,
.id_table = appletbdrm_usb_id_table,
};
module_usb_driver(appletbdrm_usb_driver);
diff --git a/drivers/gpu/drm/ttm/ttm_bo_util.c b/drivers/gpu/drm/ttm/ttm_bo_util.c
index bd0b23ac2cc4..f9458a4d5a09 100644
--- a/drivers/gpu/drm/ttm/ttm_bo_util.c
+++ b/drivers/gpu/drm/ttm/ttm_bo_util.c
@@ -356,6 +356,10 @@ static int ttm_bo_kmap_ttm(struct ttm_buffer_object *bo,
if (ret)
return ret;
+ /* Imported sg tables only come with DMA addresses, no pages to map. */
+ if (!ttm->pages)
+ return -EINVAL;
+
if (num_pages == 1 && ttm->caching == ttm_cached &&
!(man->use_tt && (ttm->page_flags & TTM_TT_FLAG_DECRYPTED))) {
/*
@@ -419,7 +423,7 @@ EXPORT_SYMBOL(ttm_bo_kmap_try_from_panic);
*
* Returns
* -ENOMEM: Out of memory.
- * -EINVAL: Invalid range.
+ * -EINVAL: Invalid range or the BO has no pages to map.
*/
int ttm_bo_kmap(struct ttm_buffer_object *bo,
unsigned long start_page, unsigned long num_pages,
@@ -493,7 +497,7 @@ EXPORT_SYMBOL(ttm_bo_kunmap);
*
* Returns
* -ENOMEM: Out of memory.
- * -EINVAL: Invalid range.
+ * -EINVAL: Invalid range or the BO has no pages to map.
*/
int ttm_bo_vmap(struct ttm_buffer_object *bo, struct iosys_map *map)
{
@@ -537,6 +541,9 @@ int ttm_bo_vmap(struct ttm_buffer_object *bo, struct iosys_map *map)
if (ret)
return ret;
+ if (!ttm->pages)
+ return -EINVAL;
+
/*
* We need to use vmap to get the desired page protection
* or to make the buffer object look contiguous.
diff --git a/drivers/gpu/drm/vc4/tests/vc4_mock_output.c b/drivers/gpu/drm/vc4/tests/vc4_mock_output.c
index b53c3635ce67..54f952e4b1ac 100644
--- a/drivers/gpu/drm/vc4/tests/vc4_mock_output.c
+++ b/drivers/gpu/drm/vc4/tests/vc4_mock_output.c
@@ -17,7 +17,7 @@ static const struct drm_connector_helper_funcs vc4_dummy_connector_helper_funcs
static const struct drm_connector_funcs vc4_dummy_connector_funcs = {
.atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
.atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
- .reset = drm_atomic_helper_connector_reset,
+ .atomic_create_state = drm_atomic_helper_connector_create_state,
};
struct vc4_dummy_output *vc4_dummy_output(struct kunit *test,
diff --git a/drivers/gpu/drm/vc4/vc4_bo.c b/drivers/gpu/drm/vc4/vc4_bo.c
index 49ea2ed0996b..320c5a0c71ed 100644
--- a/drivers/gpu/drm/vc4/vc4_bo.c
+++ b/drivers/gpu/drm/vc4/vc4_bo.c
@@ -303,7 +303,8 @@ static void vc4_bo_purge(struct drm_gem_object *obj)
drm_vma_node_unmap(&obj->vma_node, dev->anon_inode->i_mapping);
- dma_free_wc(dev->dev, obj->size, bo->base.vaddr, bo->base.dma_addr);
+ dma_free_wc(drm_dev_dma_dev(dev), obj->size, bo->base.vaddr,
+ bo->base.dma_addr);
bo->base.vaddr = NULL;
bo->madv = __VC4_MADV_PURGED;
}
diff --git a/drivers/gpu/drm/vc4/vc4_drv.c b/drivers/gpu/drm/vc4/vc4_drv.c
index eb859c65a453..83a9443db98f 100644
--- a/drivers/gpu/drm/vc4/vc4_drv.c
+++ b/drivers/gpu/drm/vc4/vc4_drv.c
@@ -24,10 +24,8 @@
#include <linux/clk.h>
#include <linux/component.h>
#include <linux/device.h>
-#include <linux/dma-mapping.h>
#include <linux/io.h>
#include <linux/module.h>
-#include <linux/of_device.h>
#include <linux/platform_device.h>
#include <linux/pm_runtime.h>
@@ -273,15 +271,6 @@ static void vc4_component_unbind_all(void *ptr)
component_unbind_all(vc4->dev, &vc4->base);
}
-static const struct of_device_id vc4_dma_range_matches[] = {
- { .compatible = "brcm,bcm2711-hvs" },
- { .compatible = "brcm,bcm2712-hvs" },
- { .compatible = "brcm,bcm2835-hvs" },
- { .compatible = "brcm,bcm2835-v3d" },
- { .compatible = "brcm,cygnus-v3d" },
- {}
-};
-
static int vc4_drm_bind(struct device *dev)
{
struct platform_device *pdev = to_platform_device(dev);
@@ -294,8 +283,6 @@ static int vc4_drm_bind(struct device *dev)
enum vc4_gen gen;
int ret = 0;
- dev->coherent_dma_mask = DMA_BIT_MASK(32);
-
gen = (enum vc4_gen)of_device_get_match_data(dev);
if (gen > VC4_GEN_4)
@@ -303,21 +290,6 @@ static int vc4_drm_bind(struct device *dev)
else
driver = &vc4_drm_driver;
- if (gen >= VC4_GEN_6_C)
- dma_set_mask_and_coherent(dev, DMA_BIT_MASK(36));
- else
- dma_set_mask_and_coherent(dev, DMA_BIT_MASK(32));
-
- node = of_find_matching_node_and_match(NULL, vc4_dma_range_matches,
- NULL);
- if (node) {
- ret = of_dma_configure(dev, node, true);
- of_node_put(node);
-
- if (ret)
- return ret;
- }
-
vc4 = devm_drm_dev_alloc(dev, driver, struct vc4_dev, base);
if (IS_ERR(vc4))
return PTR_ERR(vc4);
diff --git a/drivers/gpu/drm/vc4/vc4_hvs.c b/drivers/gpu/drm/vc4/vc4_hvs.c
index e715147d091c..b1fb096858cc 100644
--- a/drivers/gpu/drm/vc4/vc4_hvs.c
+++ b/drivers/gpu/drm/vc4/vc4_hvs.c
@@ -22,6 +22,7 @@
#include <linux/bitfield.h>
#include <linux/clk.h>
#include <linux/component.h>
+#include <linux/dma-mapping.h>
#include <linux/platform_device.h>
#include <drm/drm_atomic_helper.h>
@@ -1662,6 +1663,15 @@ static int vc4_hvs_bind(struct device *dev, struct device *master, void *data)
hvs->regset.nregs = ARRAY_SIZE(vc4_hvs_regs);
}
+ if (vc4->gen >= VC4_GEN_6_C) {
+ ret = dma_set_mask_and_coherent(dev, DMA_BIT_MASK(36));
+ if (ret)
+ return ret;
+ }
+
+ if (drm_dev_dma_dev(drm) == drm->dev)
+ drm_dev_set_dma_dev(drm, dev);
+
if (vc4->gen >= VC4_GEN_5) {
struct rpi_firmware *firmware;
struct device_node *node;
diff --git a/drivers/gpu/drm/vc4/vc4_txp.c b/drivers/gpu/drm/vc4/vc4_txp.c
index c469f8b1068b..642953cba25f 100644
--- a/drivers/gpu/drm/vc4/vc4_txp.c
+++ b/drivers/gpu/drm/vc4/vc4_txp.c
@@ -378,7 +378,7 @@ static const struct drm_connector_funcs vc4_txp_connector_funcs = {
.detect = vc4_txp_connector_detect,
.fill_modes = drm_helper_probe_single_connector_modes,
.destroy = drm_connector_cleanup,
- .reset = drm_atomic_helper_connector_reset,
+ .atomic_create_state = drm_atomic_helper_connector_create_state,
.atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
.atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
};
diff --git a/drivers/gpu/drm/vc4/vc4_v3d.c b/drivers/gpu/drm/vc4/vc4_v3d.c
index b40d98c9d1d2..6b85053c77d6 100644
--- a/drivers/gpu/drm/vc4/vc4_v3d.c
+++ b/drivers/gpu/drm/vc4/vc4_v3d.c
@@ -423,6 +423,13 @@ static int vc4_v3d_bind(struct device *dev, struct device *master, void *data)
vc4->v3d = v3d;
v3d->vc4 = vc4;
+ /*
+ * In headless setups where HVS is absent, use V3D as the fallback
+ * DMA controller for buffer allocations.
+ */
+ if (drm_dev_dma_dev(drm) == drm->dev)
+ drm_dev_set_dma_dev(drm, dev);
+
v3d->clk = devm_clk_get_optional(dev, NULL);
if (IS_ERR(v3d->clk))
return dev_err_probe(dev, PTR_ERR(v3d->clk), "Failed to get V3D clock\n");
diff --git a/drivers/gpu/drm/virtio/virtgpu_plane.c b/drivers/gpu/drm/virtio/virtgpu_plane.c
index 10718bc73693..eef9ef42dffe 100644
--- a/drivers/gpu/drm/virtio/virtgpu_plane.c
+++ b/drivers/gpu/drm/virtio/virtgpu_plane.c
@@ -38,6 +38,7 @@
static const uint32_t virtio_gpu_formats[] = {
DRM_FORMAT_HOST_XRGB8888,
+ DRM_FORMAT_HOST_ARGB8888,
};
static const uint32_t virtio_gpu_cursor_formats[] = {