diff options
| author | Dave Airlie <airlied@redhat.com> | 2026-10-21 11:02:13 +1000 |
|---|---|---|
| committer | Dave Airlie <airlied@redhat.com> | 2026-10-21 11:03:26 +1000 |
| commit | fad06cfaf385787b9ffa2f73f049d42dbe1de57f (patch) | |
| tree | 669f9bc2af4bd53140bd1da30f757512f3f78bf5 /drivers/gpu | |
| parent | a56b45995b1e6b9ee671485b1fe68b0c7f754a67 (diff) | |
| parent | db1de27aca30ebaad04aea74ac21e4c4fb531a37 (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')
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 = <8713sx_bridge_funcs; lt8713sx->bridge.of_node = dev->of_node; lt8713sx->bridge.type = DRM_MODE_CONNECTOR_DisplayPort; drm_bridge_add(<8713sx->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(<9611->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[] = { |
